/* * 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.code.LocalItem; import com.android.dx.rop.cst.Constant; import com.android.dx.rop.type.Prototype; import com.android.dx.rop.type.StdTypeList; import com.android.dx.rop.type.Type; import com.android.dx.rop.type.TypeBearer; import java.util.ArrayList; /** * Base implementation of {@link Machine}. * * <p><b>Note:</b> For the most part, the documentation for this class * ignores the distinction between {@link Type} and {@link * TypeBearer}.</p> */ public abstract class BaseMachine implements Machine { /* {@code non-null;} the prototype for the associated method */ private final Prototype prototype; /** {@code non-null;} primary arguments */ private TypeBearer[] args; /** {@code >= 0;} number of primary arguments */ private int argCount; /** {@code null-ok;} type of the operation, if salient */ private Type auxType; /** auxiliary {@code int} argument */ private int auxInt; /** {@code null-ok;} auxiliary constant argument */ private Constant auxCst; /** auxiliary branch target argument */ private int auxTarget; /** {@code null-ok;} auxiliary switch cases argument */ private SwitchList auxCases; /** {@code null-ok;} auxiliary initial value list for newarray */ private ArrayList<Constant> auxInitValues; /** {@code >= -1;} last local accessed */ private int localIndex; /** {@code null-ok;} local target spec, if salient and calculated */ private RegisterSpec localTarget; /** {@code non-null;} results */ private TypeBearer[] results; /** * {@code >= -1;} count of the results, or {@code -1} if no results * have been set */ private int resultCount; /** * Constructs an instance. * * @param prototype {@code non-null;} the prototype for the associated method */ public BaseMachine(Prototype prototype) { if (prototype == null) { throw new NullPointerException("prototype == null"); } this.prototype = prototype; args = new TypeBearer[10]; results = new TypeBearer[6]; clearArgs(); } /** {@inheritDoc} */ public Prototype getPrototype() { return prototype; } /** {@inheritDoc} */ public final void clearArgs() { argCount = 0; auxType = null; auxInt = 0; auxCst = null; auxTarget = 0; auxCases = null; auxInitValues = null; localIndex = -1; localTarget = null; resultCount = -1; } /** {@inheritDoc} */ public final void popArgs(Frame frame, int count) { ExecutionStack stack = frame.getStack(); clearArgs(); if (count > args.length) { // Grow args, and add a little extra room to grow even more. args = new TypeBearer[count + 10]; } for (int i = count - 1; i >= 0; i--) { args[i] = stack.pop(); } argCount = count; } /** {@inheritDoc} */ public void popArgs(Frame frame, Prototype prototype) { StdTypeList types = prototype.getParameterTypes(); int size = types.size(); // Use the above method to do the actual popping... popArgs(frame, size); // ...and then verify the popped types. for (int i = 0; i < size; i++) { if (! Merger.isPossiblyAssignableFrom(types.getType(i), args[i])) { throw new SimException("at stack depth " + (size - 1 - i) + ", expected type " + types.getType(i).toHuman() + " but found " + args[i].getType().toHuman()); } } } public final void popArgs(Frame frame, Type type) { // Use the above method to do the actual popping... popArgs(frame, 1); // ...and then verify the popped type. if (! Merger.isPossiblyAssignableFrom(type, args[0])) { throw new SimException("expected type " + type.toHuman() + " but found " + args[0].getType().toHuman()); } } /** {@inheritDoc} */ public final void popArgs(Frame frame, Type type1, Type type2) { // Use the above method to do the actual popping... popArgs(frame, 2); // ...and then verify the popped types. if (! Merger.isPossiblyAssignableFrom(type1, args[0])) { throw new SimException("expected type " + type1.toHuman() + " but found " + args[0].getType().toHuman()); } if (! Merger.isPossiblyAssignableFrom(type2, args[1])) { throw new SimException("expected type " + type2.toHuman() + " but found " + args[1].getType().toHuman()); } } /** {@inheritDoc} */ public final void popArgs(Frame frame, Type type1, Type type2, Type type3) { // Use the above method to do the actual popping... popArgs(frame, 3); // ...and then verify the popped types. if (! Merger.isPossiblyAssignableFrom(type1, args[0])) { throw new SimException("expected type " + type1.toHuman() + " but found " + args[0].getType().toHuman()); } if (! Merger.isPossiblyAssignableFrom(type2, args[1])) { throw new SimException("expected type " + type2.toHuman() + " but found " + args[1].getType().toHuman()); } if (! Merger.isPossiblyAssignableFrom(type3, args[2])) { throw new SimException("expected type " + type2.toHuman() + " but found " + args[2].getType().toHuman()); } } /** {@inheritDoc} */ public final void localArg(Frame frame, int idx) { clearArgs(); args[0] = frame.getLocals().get(idx); argCount = 1; localIndex = idx; } /** {@inheritDoc} */ public final void auxType(Type type) { auxType = type; } /** {@inheritDoc} */ public final void auxIntArg(int value) { auxInt = value; } /** {@inheritDoc} */ public final void auxCstArg(Constant cst) { if (cst == null) { throw new NullPointerException("cst == null"); } auxCst = cst; } /** {@inheritDoc} */ public final void auxTargetArg(int target) { auxTarget = target; } /** {@inheritDoc} */ public final void auxSwitchArg(SwitchList cases) { if (cases == null) { throw new NullPointerException("cases == null"); } auxCases = cases; } /** {@inheritDoc} */ public final void auxInitValues(ArrayList<Constant> initValues) { auxInitValues = initValues; } /** {@inheritDoc} */ public final void localTarget(int idx, Type type, LocalItem local) { localTarget = RegisterSpec.makeLocalOptional(idx, type, local); } /** * Gets the number of primary arguments. * * @return {@code >= 0;} the number of primary arguments */ protected final int argCount() { return argCount; } /** * Gets the width of the arguments (where a category-2 value counts as * two). * * @return {@code >= 0;} the argument width */ protected final int argWidth() { int result = 0; for (int i = 0; i < argCount; i++) { result += args[i].getType().getCategory(); } return result; } /** * Gets the {@code n}th primary argument. * * @param n {@code >= 0, < argCount();} which argument * @return {@code non-null;} the indicated argument */ protected final TypeBearer arg(int n) { if (n >= argCount) { throw new IllegalArgumentException("n >= argCount"); } try { return args[n]; } catch (ArrayIndexOutOfBoundsException ex) { // Translate the exception. throw new IllegalArgumentException("n < 0"); } } /** * Gets the type auxiliary argument. * * @return {@code null-ok;} the salient type */ protected final Type getAuxType() { return auxType; } /** * Gets the {@code int} auxiliary argument. * * @return the argument value */ protected final int getAuxInt() { return auxInt; } /** * Gets the constant auxiliary argument. * * @return {@code null-ok;} the argument value */ protected final Constant getAuxCst() { return auxCst; } /** * Gets the branch target auxiliary argument. * * @return the argument value */ protected final int getAuxTarget() { return auxTarget; } /** * Gets the switch cases auxiliary argument. * * @return {@code null-ok;} the argument value */ protected final SwitchList getAuxCases() { return auxCases; } /** * Gets the init values auxiliary argument. * * @return {@code null-ok;} the argument value */ protected final ArrayList<Constant> getInitValues() { return auxInitValues; } /** * Gets the last local index accessed. * * @return {@code >= -1;} the salient local index or {@code -1} if none * was set since the last time {@link #clearArgs} was called */ protected final int getLocalIndex() { return localIndex; } /** * Gets the target local register spec of the current operation, if any. * The local target spec is the combination of the values indicated * by a previous call to {@link #localTarget} with the type of what * should be the sole result set by a call to {@link #setResult} (or * the combination {@link #clearResult} then {@link #addResult}. * * @return {@code null-ok;} the salient register spec or {@code null} if no * local target was set since the last time {@link #clearArgs} was * called */ protected final RegisterSpec getLocalTarget() { if (localTarget == null) { return null; } if (resultCount != 1) { throw new SimException("local target with " + ((resultCount == 0) ? "no" : "multiple") + " results"); } TypeBearer result = results[0]; Type resultType = result.getType(); Type localType = localTarget.getType(); if (resultType == localType) { return localTarget; } if (! Merger.isPossiblyAssignableFrom(localType, resultType)) { // The result and local types are inconsistent. Complain! throwLocalMismatch(resultType, localType); return null; } if (localType == Type.OBJECT) { /* * The result type is more specific than the local type, * so use that instead. */ localTarget = localTarget.withType(result); } return localTarget; } /** * Clears the results. */ protected final void clearResult() { resultCount = 0; } /** * Sets the results list to be the given single value. * * <p><b>Note:</b> If there is more than one result value, the * others may be added by using {@link #addResult}.</p> * * @param result {@code non-null;} result value */ protected final void setResult(TypeBearer result) { if (result == null) { throw new NullPointerException("result == null"); } results[0] = result; resultCount = 1; } /** * Adds an additional element to the list of results. * * @see #setResult * * @param result {@code non-null;} result value */ protected final void addResult(TypeBearer result) { if (result == null) { throw new NullPointerException("result == null"); } results[resultCount] = result; resultCount++; } /** * Gets the count of results. This throws an exception if results were * never set. (Explicitly clearing the results counts as setting them.) * * @return {@code >= 0;} the count */ protected final int resultCount() { if (resultCount < 0) { throw new SimException("results never set"); } return resultCount; } /** * Gets the width of the results (where a category-2 value counts as * two). * * @return {@code >= 0;} the result width */ protected final int resultWidth() { int width = 0; for (int i = 0; i < resultCount; i++) { width += results[i].getType().getCategory(); } return width; } /** * Gets the {@code n}th result value. * * @param n {@code >= 0, < resultCount();} which result * @return {@code non-null;} the indicated result value */ protected final TypeBearer result(int n) { if (n >= resultCount) { throw new IllegalArgumentException("n >= resultCount"); } try { return results[n]; } catch (ArrayIndexOutOfBoundsException ex) { // Translate the exception. throw new IllegalArgumentException("n < 0"); } } /** * Stores the results of the latest operation into the given frame. If * there is a local target (see {@link #localTarget}), then the sole * result is stored to that target; otherwise any results are pushed * onto the stack. * * @param frame {@code non-null;} frame to operate on */ protected final void storeResults(Frame frame) { if (resultCount < 0) { throw new SimException("results never set"); } if (resultCount == 0) { // Nothing to do. return; } if (localTarget != null) { /* * Note: getLocalTarget() doesn't necessarily return * localTarget directly. */ frame.getLocals().set(getLocalTarget()); } else { ExecutionStack stack = frame.getStack(); for (int i = 0; i < resultCount; i++) { stack.push(results[i]); } } } /** * Throws an exception that indicates a mismatch in local variable * types. * * @param found {@code non-null;} the encountered type * @param local {@code non-null;} the local variable's claimed type */ public static void throwLocalMismatch(TypeBearer found, TypeBearer local) { throw new SimException("local variable type mismatch: " + "attempt to set or access a value of type " + found.toHuman() + " using a local variable of type " + local.toHuman() + ". This is symptomatic of .class transformation tools " + "that ignore local variable information."); } }