| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.dx.cf.code; |
| |
| import com.android.dx.rop.code.RegisterSpec; |
| import com.android.dx.rop.type.Type; |
| import com.android.dx.rop.type.TypeBearer; |
| import com.android.dx.util.ExceptionWithContext; |
| import com.android.dx.util.Hex; |
| import com.android.dx.util.MutabilityControl; |
| |
| /** |
| * Representation of an array of local variables, with Java semantics. |
| * |
| * <p><b>Note:</b> For the most part, the documentation for this class |
| * ignores the distinction between {@link com.android.dx.rop.type.Type} and {@link |
| * com.android.dx.rop.type.TypeBearer}.</p> |
| */ |
| public class OneLocalsArray extends LocalsArray { |
| /** {@code non-null;} actual array */ |
| private final TypeBearer[] locals; |
| |
| /** |
| * Constructs an instance. The locals array initially consists of |
| * all-uninitialized values (represented as {@code null}s). |
| * |
| * @param maxLocals {@code >= 0;} the maximum number of locals this instance |
| * can refer to |
| */ |
| public OneLocalsArray(int maxLocals) { |
| super(maxLocals != 0); |
| locals = new TypeBearer[maxLocals]; |
| } |
| |
| /** @inheritDoc */ |
| public OneLocalsArray copy() { |
| OneLocalsArray result = new OneLocalsArray(locals.length); |
| |
| System.arraycopy(locals, 0, result.locals, 0, locals.length); |
| |
| return result; |
| } |
| |
| /** @inheritDoc */ |
| public void annotate(ExceptionWithContext ex) { |
| for (int i = 0; i < locals.length; i++) { |
| TypeBearer type = locals[i]; |
| String s = (type == null) ? "<invalid>" : type.toString(); |
| ex.addContext("locals[" + Hex.u2(i) + "]: " + s); |
| } |
| } |
| |
| /** {@inheritDoc*/ |
| public String toHuman() { |
| StringBuilder sb = new StringBuilder(); |
| |
| for (int i = 0; i < locals.length; i++) { |
| TypeBearer type = locals[i]; |
| String s = (type == null) ? "<invalid>" : type.toString(); |
| sb.append("locals[" + Hex.u2(i) + "]: " + s + "\n"); |
| } |
| |
| return sb.toString(); |
| } |
| |
| /** @inheritDoc */ |
| public void makeInitialized(Type type) { |
| int len = locals.length; |
| |
| if (len == 0) { |
| // We have to check for this before checking for immutability. |
| return; |
| } |
| |
| throwIfImmutable(); |
| |
| Type initializedType = type.getInitializedType(); |
| |
| for (int i = 0; i < len; i++) { |
| if (locals[i] == type) { |
| locals[i] = initializedType; |
| } |
| } |
| } |
| |
| /** @inheritDoc */ |
| public int getMaxLocals() { |
| return locals.length; |
| } |
| |
| /** @inheritDoc */ |
| public void set(int idx, TypeBearer type) { |
| throwIfImmutable(); |
| |
| try { |
| type = type.getFrameType(); |
| } catch (NullPointerException ex) { |
| // Elucidate the exception |
| throw new NullPointerException("type == null"); |
| } |
| |
| if (idx < 0) { |
| throw new IndexOutOfBoundsException("idx < 0"); |
| } |
| |
| // Make highest possible out-of-bounds check happen first. |
| if (type.getType().isCategory2()) { |
| locals[idx + 1] = null; |
| } |
| |
| locals[idx] = type; |
| |
| if (idx != 0) { |
| TypeBearer prev = locals[idx - 1]; |
| if ((prev != null) && prev.getType().isCategory2()) { |
| locals[idx - 1] = null; |
| } |
| } |
| } |
| |
| /** @inheritDoc */ |
| public void set(RegisterSpec spec) { |
| set(spec.getReg(), spec); |
| } |
| |
| /** @inheritDoc */ |
| public void invalidate(int idx) { |
| throwIfImmutable(); |
| locals[idx] = null; |
| } |
| |
| /** @inheritDoc */ |
| public TypeBearer getOrNull(int idx) { |
| return locals[idx]; |
| } |
| |
| /** @inheritDoc */ |
| public TypeBearer get(int idx) { |
| TypeBearer result = locals[idx]; |
| |
| if (result == null) { |
| return throwSimException(idx, "invalid"); |
| } |
| |
| return result; |
| } |
| |
| /** @inheritDoc */ |
| public TypeBearer getCategory1(int idx) { |
| TypeBearer result = get(idx); |
| Type type = result.getType(); |
| |
| if (type.isUninitialized()) { |
| return throwSimException(idx, "uninitialized instance"); |
| } |
| |
| if (type.isCategory2()) { |
| return throwSimException(idx, "category-2"); |
| } |
| |
| return result; |
| } |
| |
| /** @inheritDoc */ |
| public TypeBearer getCategory2(int idx) { |
| TypeBearer result = get(idx); |
| |
| if (result.getType().isCategory1()) { |
| return throwSimException(idx, "category-1"); |
| } |
| |
| return result; |
| } |
| |
| /** @inheritDoc */ |
| @Override |
| public LocalsArray merge(LocalsArray other) { |
| if (other instanceof OneLocalsArray) { |
| return merge((OneLocalsArray)other); |
| } else { //LocalsArraySet |
| // LocalsArraySet knows how to merge me. |
| return other.merge(this); |
| } |
| } |
| |
| /** |
| * Merges this OneLocalsArray instance with another OneLocalsArray |
| * instance. A more-refined version of {@link #merge(LocalsArray) merge} |
| * which is called by that method when appropriate. |
| * |
| * @param other locals array with which to merge |
| * @return this instance if merge was a no-op, or a new instance if |
| * the merge resulted in a change. |
| */ |
| public OneLocalsArray merge(OneLocalsArray other) { |
| try { |
| return Merger.mergeLocals(this, other); |
| } catch (SimException ex) { |
| ex.addContext("underlay locals:"); |
| annotate(ex); |
| ex.addContext("overlay locals:"); |
| other.annotate(ex); |
| throw ex; |
| } |
| } |
| |
| /** @inheritDoc */ |
| @Override |
| public LocalsArraySet mergeWithSubroutineCaller |
| (LocalsArray other, int predLabel) { |
| |
| LocalsArraySet result = new LocalsArraySet(getMaxLocals()); |
| return result.mergeWithSubroutineCaller(other, predLabel); |
| } |
| |
| /**{@inheritDoc}*/ |
| @Override |
| protected OneLocalsArray getPrimary() { |
| return this; |
| } |
| |
| /** |
| * Throws a properly-formatted exception. |
| * |
| * @param idx the salient local index |
| * @param msg {@code non-null;} useful message |
| * @return never (keeps compiler happy) |
| */ |
| private static TypeBearer throwSimException(int idx, String msg) { |
| throw new SimException("local " + Hex.u2(idx) + ": " + msg); |
| } |
| } |