1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dx.cf.code;
18
19import com.android.dex.util.ExceptionWithContext;
20import com.android.dx.rop.code.RegisterSpec;
21import com.android.dx.rop.type.Type;
22import com.android.dx.rop.type.TypeBearer;
23import com.android.dx.util.Hex;
24
25/**
26 * Representation of an array of local variables, with Java semantics.
27 *
28 * <p><b>Note:</b> For the most part, the documentation for this class
29 * ignores the distinction between {@link com.android.dx.rop.type.Type} and {@link
30 * com.android.dx.rop.type.TypeBearer}.</p>
31 */
32public class OneLocalsArray extends LocalsArray {
33    /** {@code non-null;} actual array */
34    private final TypeBearer[] locals;
35
36    /**
37     * Constructs an instance. The locals array initially consists of
38     * all-uninitialized values (represented as {@code null}s).
39     *
40     * @param maxLocals {@code >= 0;} the maximum number of locals this instance
41     * can refer to
42     */
43    public OneLocalsArray(int maxLocals) {
44        super(maxLocals != 0);
45        locals = new TypeBearer[maxLocals];
46    }
47
48    /** @inheritDoc */
49    public OneLocalsArray copy() {
50        OneLocalsArray result = new OneLocalsArray(locals.length);
51
52        System.arraycopy(locals, 0, result.locals, 0, locals.length);
53
54        return result;
55    }
56
57    /** @inheritDoc */
58    public void annotate(ExceptionWithContext ex) {
59        for (int i = 0; i < locals.length; i++) {
60            TypeBearer type = locals[i];
61            String s = (type == null) ? "<invalid>" : type.toString();
62            ex.addContext("locals[" + Hex.u2(i) + "]: " + s);
63        }
64    }
65
66    /** {@inheritDoc*/
67    public String toHuman() {
68        StringBuilder sb = new StringBuilder();
69
70        for (int i = 0; i < locals.length; i++) {
71            TypeBearer type = locals[i];
72            String s = (type == null) ? "<invalid>" : type.toString();
73            sb.append("locals[" + Hex.u2(i) + "]: " + s + "\n");
74        }
75
76        return sb.toString();
77    }
78
79    /** @inheritDoc */
80    public void makeInitialized(Type type) {
81        int len = locals.length;
82
83        if (len == 0) {
84            // We have to check for this before checking for immutability.
85            return;
86        }
87
88        throwIfImmutable();
89
90        Type initializedType = type.getInitializedType();
91
92        for (int i = 0; i < len; i++) {
93            if (locals[i] == type) {
94                locals[i] = initializedType;
95            }
96        }
97    }
98
99    /** @inheritDoc */
100    public int getMaxLocals() {
101        return locals.length;
102    }
103
104    /** @inheritDoc */
105    public void set(int idx, TypeBearer type) {
106        throwIfImmutable();
107
108        try {
109            type = type.getFrameType();
110        } catch (NullPointerException ex) {
111            // Elucidate the exception
112            throw new NullPointerException("type == null");
113        }
114
115        if (idx < 0) {
116            throw new IndexOutOfBoundsException("idx < 0");
117        }
118
119        // Make highest possible out-of-bounds check happen first.
120        if (type.getType().isCategory2()) {
121            locals[idx + 1] = null;
122        }
123
124        locals[idx] = type;
125
126        if (idx != 0) {
127            TypeBearer prev = locals[idx - 1];
128            if ((prev != null) && prev.getType().isCategory2()) {
129                locals[idx - 1] = null;
130            }
131        }
132    }
133
134    /** @inheritDoc */
135    public void set(RegisterSpec spec) {
136        set(spec.getReg(), spec);
137    }
138
139    /** @inheritDoc */
140    public void invalidate(int idx) {
141        throwIfImmutable();
142        locals[idx] = null;
143    }
144
145    /** @inheritDoc */
146    public TypeBearer getOrNull(int idx) {
147        return locals[idx];
148    }
149
150    /** @inheritDoc */
151    public TypeBearer get(int idx) {
152        TypeBearer result = locals[idx];
153
154        if (result == null) {
155            return throwSimException(idx, "invalid");
156        }
157
158        return result;
159    }
160
161    /** @inheritDoc */
162    public TypeBearer getCategory1(int idx) {
163        TypeBearer result = get(idx);
164        Type type = result.getType();
165
166        if (type.isUninitialized()) {
167            return throwSimException(idx, "uninitialized instance");
168        }
169
170        if (type.isCategory2()) {
171            return throwSimException(idx, "category-2");
172        }
173
174        return result;
175    }
176
177    /** @inheritDoc */
178    public TypeBearer getCategory2(int idx) {
179        TypeBearer result = get(idx);
180
181        if (result.getType().isCategory1()) {
182            return throwSimException(idx, "category-1");
183        }
184
185        return result;
186    }
187
188    /** @inheritDoc */
189    @Override
190    public LocalsArray merge(LocalsArray other) {
191        if (other instanceof OneLocalsArray) {
192            return merge((OneLocalsArray)other);
193        } else { //LocalsArraySet
194            // LocalsArraySet knows how to merge me.
195            return other.merge(this);
196        }
197    }
198
199    /**
200     * Merges this OneLocalsArray instance with another OneLocalsArray
201     * instance. A more-refined version of {@link #merge(LocalsArray) merge}
202     * which is called by that method when appropriate.
203     *
204     * @param other locals array with which to merge
205     * @return this instance if merge was a no-op, or a new instance if
206     * the merge resulted in a change.
207     */
208    public OneLocalsArray merge(OneLocalsArray other) {
209        try {
210            return Merger.mergeLocals(this, other);
211        } catch (SimException ex) {
212            ex.addContext("underlay locals:");
213            annotate(ex);
214            ex.addContext("overlay locals:");
215            other.annotate(ex);
216            throw ex;
217        }
218    }
219
220    /** @inheritDoc */
221    @Override
222    public LocalsArraySet mergeWithSubroutineCaller
223            (LocalsArray other, int predLabel) {
224
225        LocalsArraySet result = new LocalsArraySet(getMaxLocals());
226        return result.mergeWithSubroutineCaller(other, predLabel);
227    }
228
229    /**{@inheritDoc}*/
230    @Override
231    protected OneLocalsArray getPrimary() {
232        return this;
233    }
234
235    /**
236     * Throws a properly-formatted exception.
237     *
238     * @param idx the salient local index
239     * @param msg {@code non-null;} useful message
240     * @return never (keeps compiler happy)
241     */
242    private static TypeBearer throwSimException(int idx, String msg) {
243        throw new SimException("local " + Hex.u2(idx) + ": " + msg);
244    }
245}
246