/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.osr; import org.jikesrvm.VM; import org.jikesrvm.classloader.BytecodeConstants; import org.jikesrvm.classloader.BytecodeStream; import org.jikesrvm.classloader.ClassLoaderConstants; import org.jikesrvm.classloader.RVMClass; import org.jikesrvm.classloader.ExceptionHandlerMap; import org.jikesrvm.classloader.FieldReference; import org.jikesrvm.classloader.RVMMethod; import org.jikesrvm.classloader.MethodReference; import org.jikesrvm.classloader.NormalMethod; import org.jikesrvm.classloader.TypeReference; import org.jikesrvm.compilers.common.CompiledMethods; import org.jikesrvm.osr.bytecodes.InvokeStatic; /** * BytecodeTraverser does depth first search on a bytecode * array, determines the type information of locals and stacks at * insteresting point. * * This class only intends to provide type information for on-stack * replacement, which needs to know the type of a value. This class * can only tells basic type information such as : REFERENCE, LONG, * DOUBLE, FLOAT, INT, and ReturnAddress. Not like GCMap which tells * GC a value is REFERENCE or NON-REFERENCE we also want to know it * is INT or DOUBLE, and takes two words value or one word. * * The produced type information has to be adjusted by consulting * GC maps because two different types may merge at one program point * (REF and non-REF types). Bytecode verifier will make the type info * undefined in that case. But this class won't know. So the caller * should check the GC map to validate a REF type variable. * * More or less, this class needs to do the same work as a bytecode * verifier, which tells the type and size of each locals and stacks. * The JSR/RET instructions pose the difficulty to our case. However, * we can assume the bytecode is verified. We use following assumptions: * 1. After JSR, the stack was not changed, only local variable * type needs to merge with FINALLY clause. * 2. We need program-point specific stack type, but only need * the summary of local types. Thus, after analysis, local * types are same for all PCs. */ public class BytecodeTraverser implements BytecodeConstants, ClassLoaderConstants, OSRConstants { /////// COMMON /* to handle ret address which is not produced by JSR, we need a * separate array to track that. */ private int[] retaddr; private int addr; private byte[] visitedpc; private boolean TRACE = false; // when computing infor for partial bytecodes // donot following bytecodes out of range private boolean ignoreGotos = false; private BytecodeStream bytecodes; /////// COMPUTING_TYPE_INFO /* type information of local variables and stack slots */ private byte[] ltypes; private byte[] stypes; /////////////////////////// // COMPUTE TYPE INFORMATION ////////////////////////// /** * Computes types of local variable and stack slots at an interesting point * for future querying. Computing type info and retrieval should not be * reentered. The type info of local variable is not accurate about reference * types, see JVM SPEC (2nd edition) p 146. The caller can consult GC map * to verify if a local is a reference or not. * * @param method whose bytecode to be queried * @param bcpoint the bytecode index which is the interesting point * at the mean time, we only support one PC. * @return whether the pc is a valid program point of the method */ public boolean computeLocalStackTypes(NormalMethod method, int bcpoint) { if (VM.TraceOnStackReplacement) { VM.sysWrite("computing local and stack types of " + method + "\n"); } int localsize = method.getLocalWords(); ltypes = new byte[localsize]; if (VM.TraceOnStackReplacement) { VM.sysWrite("local size : "); VM.sysWrite(localsize); VM.sysWrite("\n"); } retaddr = new int[localsize]; for (int i = 0; i < localsize; i++) { retaddr[i] = -1; } addr = -1; int stacksize = method.getOperandWords(); stypes = new byte[stacksize]; /* set marks for each byte code. */ // always operate on original method this.bytecodes = method.getBytecodes(); visitedpc = new byte[bytecodes.length()]; /* then we initialize all stack and local type as void. */ for (int i = 0, n = ltypes.length; i < n; i++) { ltypes[i] = VoidTypeCode; } TypeStack simstacks = new TypeStack(stacksize, VoidTypeCode); /* initialize local types from method signature.*/ { TypeReference[] ptypes = method.getParameterTypes(); int lidx = 0; if (!method.isStatic()) { ltypes[lidx++] = ClassTypeCode; } for (int i = 0, n = ptypes.length; i < n; i++) { byte tcode = ptypes[i].getName().parseForTypeCode(); ltypes[lidx++] = tcode; if ((tcode == LongTypeCode) || (tcode == DoubleTypeCode)) { ltypes[lidx++] = VoidTypeCode; } } } /* scan start from method entry */ boolean found = scanBlocks(method, bytecodes, true, bcpoint, ltypes, stypes, 0, simstacks, null); /* scan for exception handler. */ if (!found) { ExceptionHandlerMap ehmap = method.getExceptionHandlerMap(); if (ehmap != null) { int[] handlerPCs = ehmap.getHandlerPC(); for (int i = 0, n = handlerPCs.length; i < n; i++) { simstacks.clear(); simstacks.push(ClassTypeCode); int startpc = handlerPCs[i]; found = scanBlocks(method, bytecodes, true, bcpoint, ltypes, stypes, startpc, simstacks, null); if (found) { break; } } } } visitedpc = null; return true; } /** * Returns an array of type information of locals at the registered * program point. The size of array is fixed by MAX_LOCALS, the type * descriptor can be found in "ClassLoadConstants.java". * * @return an array of type information, or null */ public byte[] getLocalTypes() { return ltypes; } /** * Returns an array of type information of stacks at a program * point. The size of array is fixed by MAX_STACKS, the type * descriptor can be found in "ClassLoadConstants.java". * * @return an array of type information, or null */ public byte[] getStackTypes() { return stypes; } ////////////////////////// // COMPUTE STACK HEIGHTS ////////////////////////// public void computeStackHeights(NormalMethod method, BytecodeStream bcodes, int[] stackHeights, boolean adjustExptable) { if (VM.TraceOnStackReplacement) { VM.sysWriteln("computing stack heights of method " + method.toString()); } /* set marks for each byte code. */ // this may be the specialized method bytecodes = bcodes; visitedpc = new byte[bytecodes.length()]; int localsize = method.getLocalWords(); retaddr = new int[localsize]; for (int i = 0; i < localsize; i++) { retaddr[i] = -1; } addr = -1; int stacksize = method.getOperandWords(); TypeStack simstacks = new TypeStack(stacksize, VoidTypeCode); /* scan start from method entry */ { int startpc = 0; scanBlocks(method, bytecodes, false, -1, null, null, startpc, simstacks, stackHeights); } /* scan for exception handler. */ { ExceptionHandlerMap ehmap = method.getExceptionHandlerMap(); if (ehmap != null) { int[] handlerPCs = ehmap.getHandlerPC(); for (int i = 0, n = handlerPCs.length; i < n; i++) { int startpc = handlerPCs[i]; /* for baseline compilation, the SpecialCompiler * didnot adjust exception table, we has to adjust it * here. */ if (adjustExptable && method.isForOsrSpecialization()) { startpc += method.getOsrPrologueLength(); } simstacks.clear(); simstacks.push(ClassTypeCode); scanBlocks(method, bytecodes, false, -1, null, null, startpc, simstacks, stackHeights); } } } visitedpc = null; } /** * Compute stack heights of bytecode stream (used for osr prologue) */ public void prologueStackHeights(NormalMethod method, BytecodeStream bcodes, int[] stackHeights) { if (VM.TraceOnStackReplacement) { VM.sysWriteln("computing stack heights of method " + method.toString()); } /* set marks for each byte code. */ // this may be the specialized method bytecodes = bcodes; visitedpc = new byte[bytecodes.length()]; ignoreGotos = true; int localsize = method.getLocalWords(); retaddr = new int[localsize]; for (int i = 0; i < localsize; i++) { retaddr[i] = -1; } addr = -1; int stacksize = method.getOperandWords(); TypeStack simstacks = new TypeStack(stacksize, VoidTypeCode); /* scan start from method entry */ { int startpc = 0; scanBlocks(method, bytecodes, false, -1, null, null, startpc, simstacks, stackHeights); } visitedpc = null; } /* returns type code of the return type from the signature. * SEE also : Atom.parseForReturnType */ @SuppressWarnings("unused") private byte getReturnCodeFromSignature(String sig) { byte[] val = sig.getBytes(); int i = 0; while (val[i++] != ')') ; return (val[i]); } //////////////////////////// // IMPLEMENTATION /////////////////////////// /* return true --> hit the bytecode pointed by PC */ private boolean scanBlocks(NormalMethod method, // which method BytecodeStream bytecodes, // the bytecodes boolean doDFS, // do a DFS or one-pass scan int pcs, // the target pcs, if doDFS byte[] ltypes, // the local types if doDFS byte[] stypes, // the stack types if doDFS int startpc, // start pc TypeStack S, // stack int[] stackHeights) { // the stack height if not doDFS int localsize = method.getLocalWords() - 1; RVMClass declaringClass = method.getDeclaringClass(); bytecodes.reset(startpc); boolean found = false; while (bytecodes.hasMoreBytecodes()) { int pc = bytecodes.index(); // get current pc if (visitedpc[pc] == 1) { return false; } else { visitedpc[pc] = 1; } if (doDFS && (pc == pcs)) { /* make a copy of stack frame and put into stypes. */ byte[] stack = S.snapshot(); System.arraycopy(stack, 0, stypes, 0, stack.length); return true; } if (!doDFS) { // record stack heights stackHeights[pc] = localsize + S.depth(); } /* let's continue */ int bcode = bytecodes.nextInstruction(); if (TRACE) { if (bcode <= JBC_jsr_w) { VM.sysWriteln(pc + " : " + S.depth() + " : " + JBC_name[bcode]); } else { VM.sysWriteln(pc + " : " + S.depth() + " : impdep1"); } } switch (bcode) { case JBC_nop: break; case JBC_aconst_null: S.push(ClassTypeCode); break; case JBC_iconst_m1: case JBC_iconst_0: case JBC_iconst_1: case JBC_iconst_2: case JBC_iconst_3: case JBC_iconst_4: case JBC_iconst_5: S.push(IntTypeCode); break; case JBC_lconst_0: case JBC_lconst_1: /* we should do the save order as opt compiler */ S.push(VoidTypeCode); S.push(LongTypeCode); break; case JBC_fconst_0: case JBC_fconst_1: case JBC_fconst_2: S.push(FloatTypeCode); break; case JBC_dconst_0: case JBC_dconst_1: S.push(VoidTypeCode); S.push(DoubleTypeCode); break; case JBC_bipush: bytecodes.getByteValue(); S.push(IntTypeCode); break; case JBC_sipush: bytecodes.getShortValue(); S.push(IntTypeCode); break; case JBC_ldc: case JBC_ldc_w: { int cpoolidx = (bcode == JBC_ldc) ? bytecodes.getConstantIndex() : bytecodes.getWideConstantIndex(); byte tdesc = declaringClass.getLiteralDescription(cpoolidx); switch (tdesc) { case CP_INT: S.push(IntTypeCode); break; case CP_FLOAT: S.push(FloatTypeCode); break; case CP_STRING: S.push(ClassTypeCode); break; case CP_CLASS: S.push(ClassTypeCode); break; default: if (VM.TraceOnStackReplacement) VM.sysWriteln("ldc unknown type " + tdesc); if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); break; } // end of switch } break; case JBC_ldc2_w: { int cpoolidx = bytecodes.getWideConstantIndex(); byte tdesc = declaringClass.getLiteralDescription(cpoolidx); S.push(VoidTypeCode); switch (tdesc) { case CP_LONG: S.push(LongTypeCode); break; case CP_DOUBLE: S.push(DoubleTypeCode); break; default: if (VM.TraceOnStackReplacement) VM.sysWriteln("ldc2_w unknown type " + tdesc); if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); break; } // end of switch } break; case JBC_iload: bytecodes.getLocalNumber(); // skip local S.push(IntTypeCode); break; case JBC_lload: bytecodes.getLocalNumber(); // skip local S.push(VoidTypeCode); S.push(LongTypeCode); break; case JBC_fload: bytecodes.getLocalNumber(); // skip local S.push(FloatTypeCode); break; case JBC_dload: bytecodes.getLocalNumber(); S.push(VoidTypeCode); S.push(DoubleTypeCode); break; case JBC_aload: bytecodes.getLocalNumber(); S.push(ClassTypeCode); break; case JBC_iload_0: case JBC_iload_1: case JBC_iload_2: case JBC_iload_3: S.push(IntTypeCode); break; case JBC_lload_0: case JBC_lload_1: case JBC_lload_2: case JBC_lload_3: S.push(VoidTypeCode); S.push(LongTypeCode); break; case JBC_fload_0: case JBC_fload_1: case JBC_fload_2: case JBC_fload_3: S.push(FloatTypeCode); break; case JBC_dload_0: case JBC_dload_1: case JBC_dload_2: case JBC_dload_3: S.push(VoidTypeCode); S.push(DoubleTypeCode); break; case JBC_aload_0: case JBC_aload_1: case JBC_aload_2: case JBC_aload_3: S.push(ClassTypeCode); break; case JBC_iaload: case JBC_baload: case JBC_caload: case JBC_saload: S.pop(); S.pop(); S.push(IntTypeCode); break; case JBC_laload: S.pop(); S.pop(); S.push(VoidTypeCode); S.push(LongTypeCode); break; case JBC_faload: S.pop(); S.pop(); S.push(FloatTypeCode); break; case JBC_daload: S.pop(); S.pop(); S.push(VoidTypeCode); S.push(DoubleTypeCode); break; case JBC_aaload: S.pop(); S.pop(); S.push(ClassTypeCode); break; case JBC_istore: { S.pop(); int index = bytecodes.getLocalNumber(); if (doDFS) ltypes[index] = IntTypeCode; } break; case JBC_istore_0: case JBC_istore_1: case JBC_istore_2: case JBC_istore_3: { S.pop(); int index = bcode - JBC_istore_0; if (doDFS) ltypes[index] = IntTypeCode; } break; case JBC_lstore: { S.pop(); S.pop(); int index = bytecodes.getLocalNumber(); if (doDFS) { ltypes[index] = LongTypeCode; ltypes[index + 1] = VoidTypeCode; } } break; case JBC_lstore_0: case JBC_lstore_1: case JBC_lstore_2: case JBC_lstore_3: { S.pop(); S.pop(); int index = bcode - JBC_lstore_0; if (doDFS) { ltypes[index] = LongTypeCode; ltypes[index + 1] = VoidTypeCode; } } break; case JBC_fstore: { S.pop(); int index = bytecodes.getLocalNumber(); if (doDFS) ltypes[index] = FloatTypeCode; } break; case JBC_fstore_0: case JBC_fstore_1: case JBC_fstore_2: case JBC_fstore_3: { S.pop(); int index = bcode - JBC_fstore_0; if (doDFS) ltypes[index] = FloatTypeCode; } break; case JBC_dstore: { S.pop(); S.pop(); int index = bytecodes.getLocalNumber(); if (doDFS) { ltypes[index] = DoubleTypeCode; ltypes[index + 1] = VoidTypeCode; } } break; case JBC_dstore_0: case JBC_dstore_1: case JBC_dstore_2: case JBC_dstore_3: { S.pop(); S.pop(); int index = bcode - JBC_dstore_0; if (doDFS) { ltypes[index] = DoubleTypeCode; ltypes[index + 1] = VoidTypeCode; } } break; case JBC_astore: { // caution: astore may save return address type int index = bytecodes.getLocalNumber(); byte tcode = S.pop(); if (doDFS) ltypes[index] = tcode; // for ret address. if (tcode == ReturnAddressTypeCode) { retaddr[index] = addr; addr = -1; } } break; case JBC_astore_0: case JBC_astore_1: case JBC_astore_2: case JBC_astore_3: { // caution: astore may save return address type int index = bcode - JBC_astore_0; byte tcode = S.pop(); if (doDFS) ltypes[index] = tcode; // for ret address. if (tcode == ReturnAddressTypeCode) { retaddr[index] = addr; addr = -1; } } break; case JBC_iastore: case JBC_bastore: case JBC_castore: case JBC_sastore: S.pop(3); break; case JBC_lastore: S.pop(4); break; case JBC_fastore: S.pop(3); break; case JBC_dastore: S.pop(4); break; case JBC_aastore: S.pop(3); break; case JBC_pop: S.pop(); break; case JBC_pop2: S.pop(2); break; case JBC_dup: { byte v1 = S.peek(); S.push(v1); } break; case JBC_dup_x1: { byte v1 = S.peek(); S.pop(); byte v2 = S.peek(); S.pop(); S.push(v1); S.push(v2); S.push(v1); } break; case JBC_dup_x2: { byte v1 = S.peek(); S.pop(); byte v2 = S.peek(); S.pop(); byte v3 = S.peek(); S.pop(); S.push(v1); S.push(v3); S.push(v2); S.push(v1); } break; case JBC_dup2: { byte v1 = S.peek(); S.pop(); byte v2 = S.peek(); S.push(v1); S.push(v2); S.push(v1); } break; case JBC_dup2_x1: { byte v1 = S.peek(); S.pop(); byte v2 = S.peek(); S.pop(); byte v3 = S.peek(); S.pop(); S.push(v2); S.push(v1); S.push(v3); S.push(v2); S.push(v1); } break; case JBC_dup2_x2: { byte v1 = S.peek(); S.pop(); byte v2 = S.peek(); S.pop(); byte v3 = S.peek(); S.pop(); byte v4 = S.peek(); S.pop(); S.push(v2); S.push(v1); S.push(v4); S.push(v3); S.push(v2); S.push(v1); } break; case JBC_swap: { byte v1 = S.peek(); S.pop(); byte v2 = S.peek(); S.pop(); S.push(v1); S.push(v2); } break; case JBC_iadd: case JBC_isub: case JBC_imul: case JBC_idiv: case JBC_irem: case JBC_iand: case JBC_ior: case JBC_ixor: case JBC_ishl: case JBC_ishr: case JBC_iushr: S.pop(); break; case JBC_ladd: case JBC_lsub: case JBC_lmul: case JBC_ldiv: case JBC_lrem: case JBC_land: case JBC_lor: case JBC_lxor: S.pop(2); break; case JBC_lshl: case JBC_lshr: case JBC_lushr: S.pop(); break; case JBC_fadd: case JBC_fsub: case JBC_fmul: case JBC_fdiv: case JBC_frem: S.pop(); break; case JBC_dadd: case JBC_dsub: case JBC_dmul: case JBC_ddiv: case JBC_drem: S.pop(2); break; case JBC_ineg: case JBC_lneg: case JBC_fneg: case JBC_dneg: break; case JBC_iinc: { int index = bytecodes.getLocalNumber(); /* int value = */ bytecodes.getIncrement(); if (doDFS) ltypes[index] = IntTypeCode; } break; case JBC_i2l: S.pop(); S.push(VoidTypeCode); S.push(LongTypeCode); break; case JBC_i2f: S.pop(); S.push(FloatTypeCode); break; case JBC_i2d: S.pop(); S.push(VoidTypeCode); S.push(DoubleTypeCode); break; case JBC_l2i: S.pop(2); S.push(IntTypeCode); break; case JBC_l2f: S.pop(2); S.push(FloatTypeCode); break; case JBC_l2d: S.pop(2); S.push(VoidTypeCode); S.push(DoubleTypeCode); break; case JBC_f2i: S.pop(); S.push(IntTypeCode); break; case JBC_f2l: S.pop(); S.push(VoidTypeCode); S.push(LongTypeCode); break; case JBC_f2d: S.pop(); S.push(VoidTypeCode); S.push(DoubleTypeCode); break; case JBC_d2i: S.pop(2); S.push(IntTypeCode); break; case JBC_d2l: S.pop(2); S.push(VoidTypeCode); S.push(LongTypeCode); break; case JBC_d2f: S.pop(2); S.push(FloatTypeCode); break; case JBC_int2byte: case JBC_int2char: case JBC_int2short: break; case JBC_lcmp: S.pop(4); S.push(IntTypeCode); break; case JBC_fcmpl: case JBC_fcmpg: S.pop(2); S.push(IntTypeCode); break; case JBC_dcmpl: case JBC_dcmpg: S.pop(4); S.push(IntTypeCode); break; case JBC_ifeq: case JBC_ifne: case JBC_iflt: case JBC_ifge: case JBC_ifgt: case JBC_ifle: case JBC_ifnull: case JBC_ifnonnull: { S.pop(); // flowthrough first int nextpc = pc + 3; int target = pc + bytecodes.getBranchOffset(); if (doDFS) { // make a copy of ltypes, stypes to pass in byte[] newltypes = new byte[ltypes.length]; byte[] newstypes = new byte[stypes.length]; System.arraycopy(ltypes, 0, newltypes, 0, ltypes.length); System.arraycopy(stypes, 0, newstypes, 0, stypes.length); found = scanBlocks(method, bytecodes, true, pcs, newltypes, newstypes, nextpc, new TypeStack(S), null); if (found) { // copy back the ltypes and stypes System.arraycopy(newltypes, 0, ltypes, 0, ltypes.length); System.arraycopy(newstypes, 0, stypes, 0, stypes.length); return true; } } else { found = scanBlocks(method, bytecodes, false, -1, null, null, nextpc, new TypeStack(S), stackHeights); } bytecodes.reset(target); } break; case JBC_if_icmpeq: case JBC_if_icmpne: case JBC_if_icmplt: case JBC_if_icmpge: case JBC_if_icmpgt: case JBC_if_icmple: case JBC_if_acmpeq: case JBC_if_acmpne: { S.pop(2); // flowthrough first int nextpc = pc + 3; int target = pc + bytecodes.getBranchOffset(); if (doDFS) { // make a copy of ltypes, stypes to pass in byte[] newltypes = new byte[ltypes.length]; byte[] newstypes = new byte[stypes.length]; System.arraycopy(ltypes, 0, newltypes, 0, ltypes.length); System.arraycopy(stypes, 0, newstypes, 0, stypes.length); found = scanBlocks(method, bytecodes, true, pcs, newltypes, newstypes, nextpc, new TypeStack(S), null); if (found) { // copy back the ltypes and stypes System.arraycopy(newltypes, 0, ltypes, 0, ltypes.length); System.arraycopy(newstypes, 0, stypes, 0, stypes.length); return true; } } else { found = scanBlocks(method, bytecodes, false, -1, null, null, nextpc, new TypeStack(S), stackHeights); } bytecodes.reset(target); } break; case JBC_goto: { int offset = bytecodes.getBranchOffset(); if (!ignoreGotos) { bytecodes.reset(pc + offset); } } break; case JBC_goto_w: { int offset = bytecodes.getWideBranchOffset(); if (!ignoreGotos) { bytecodes.reset(pc + offset); } } break; case JBC_jsr: case JBC_jsr_w: { // flow through firs int nextpc = pc + ((bcode == JBC_jsr) ? 3 : 5); int target = pc + ((bcode == JBC_jsr) ? bytecodes.getBranchOffset() : bytecodes.getWideBranchOffset()); if (doDFS) { // make a copy of ltypes, stypes to pass in byte[] newltypes = new byte[ltypes.length]; byte[] newstypes = new byte[stypes.length]; System.arraycopy(ltypes, 0, newltypes, 0, ltypes.length); System.arraycopy(stypes, 0, newstypes, 0, stypes.length); found = scanBlocks(method, bytecodes, true, pcs, newltypes, newstypes, nextpc, new TypeStack(S), null); if (found) { // copy back the ltypes and stypes System.arraycopy(newltypes, 0, ltypes, 0, ltypes.length); System.arraycopy(newstypes, 0, stypes, 0, stypes.length); return true; } } else { found = scanBlocks(method, bytecodes, false, -1, null, null, nextpc, new TypeStack(S), stackHeights); } // branch to jsr subroutine // remember return address for ret. addr = pc + ((bcode == JBC_jsr) ? 3 : 5); S.push(ReturnAddressTypeCode); bytecodes.reset(target); } break; case JBC_ret: { // the OPT compiler set local to null after _ret instruction, // then we should clean it here also, otherwise it will // throw a null pointer exception. // HOWEVER, it is not part of JVM spec. int index = bytecodes.getLocalNumber(); if (doDFS) ltypes[index] = VoidTypeCode; /* the ret address may be saved by a PSEUDO_LoadRetAddrConstant */ if (retaddr[index] != -1) { bytecodes.reset(retaddr[index]); retaddr[index] = -1; } else { // now we hit ret, return out return false; } } break; case JBC_tableswitch: { S.pop(); bytecodes.alignSwitch(); int defaultval = bytecodes.getDefaultSwitchOffset(); int low = bytecodes.getLowSwitchValue(); int high = bytecodes.getHighSwitchValue(); // write down a list of targets int npairs = high - low + 1; int[] offsets = new int[npairs]; for (int i = 0; i < npairs; i++) { offsets[i] = bytecodes.getTableSwitchOffset(i); } for (int i = 0; i < npairs; i++) { int tgtpc = pc + offsets[i]; if (doDFS) { // make a copy of ltypes, stypes to pass in byte[] newltypes = new byte[ltypes.length]; byte[] newstypes = new byte[stypes.length]; System.arraycopy(ltypes, 0, newltypes, 0, ltypes.length); System.arraycopy(stypes, 0, newstypes, 0, stypes.length); found = scanBlocks(method, bytecodes, true, pcs, newltypes, newstypes, tgtpc, new TypeStack(S), null); if (found) { // copy back the ltypes and stypes System.arraycopy(newltypes, 0, ltypes, 0, ltypes.length); System.arraycopy(newstypes, 0, stypes, 0, stypes.length); return true; } } else { found = scanBlocks(method, bytecodes, false, -1, null, null, tgtpc, new TypeStack(S), stackHeights); } } // default { int tgtpc = pc + defaultval; if (doDFS) { // make a copy of ltypes, stypes to pass in byte[] newltypes = new byte[ltypes.length]; byte[] newstypes = new byte[stypes.length]; System.arraycopy(ltypes, 0, newltypes, 0, ltypes.length); System.arraycopy(stypes, 0, newstypes, 0, stypes.length); found = scanBlocks(method, bytecodes, true, pcs, newltypes, newstypes, tgtpc, new TypeStack(S), null); if (found) { // copy back the ltypes and stypes System.arraycopy(newltypes, 0, ltypes, 0, ltypes.length); System.arraycopy(newstypes, 0, stypes, 0, stypes.length); } } else { found = scanBlocks(method, bytecodes, false, -1, null, null, tgtpc, new TypeStack(S), stackHeights); } return found; } } case JBC_lookupswitch: { S.pop(); // pop the key bytecodes.alignSwitch(); int defaultval = bytecodes.getDefaultSwitchOffset(); int npairs = bytecodes.getSwitchLength(); int[] matches = new int[npairs]; int[] offsets = new int[npairs]; for (int i = 0; i < npairs; i++) { matches[i] = bytecodes.getLookupSwitchValue(i); offsets[i] = bytecodes.getLookupSwitchOffset(i); } for (int i = 0; i < npairs; i++) { //int match = matches[i]; int offset = offsets[i]; int tgtpc = pc + offset; if (doDFS) { // make a copy of ltypes, stypes to pass in byte[] newltypes = new byte[ltypes.length]; byte[] newstypes = new byte[stypes.length]; System.arraycopy(ltypes, 0, newltypes, 0, ltypes.length); System.arraycopy(stypes, 0, newstypes, 0, stypes.length); found = scanBlocks(method, bytecodes, true, pcs, newltypes, newstypes, tgtpc, new TypeStack(S), null); if (found) { // copy back the ltypes and stypes System.arraycopy(newltypes, 0, ltypes, 0, ltypes.length); System.arraycopy(newstypes, 0, stypes, 0, stypes.length); return true; } } else { found = scanBlocks(method, bytecodes, false, -1, null, null, tgtpc, new TypeStack(S), stackHeights); } } // default { int tgtpc = pc + defaultval; if (doDFS) { // make a copy of ltypes, stypes to pass in byte[] newltypes = new byte[ltypes.length]; byte[] newstypes = new byte[stypes.length]; System.arraycopy(ltypes, 0, newltypes, 0, ltypes.length); System.arraycopy(stypes, 0, newstypes, 0, stypes.length); found = scanBlocks(method, bytecodes, true, pcs, newltypes, newstypes, tgtpc, new TypeStack(S), null); if (found) { // copy back the ltypes and stypes System.arraycopy(newltypes, 0, ltypes, 0, ltypes.length); System.arraycopy(newstypes, 0, stypes, 0, stypes.length); } } else { found = scanBlocks(method, bytecodes, false, -1, null, null, tgtpc, new TypeStack(S), stackHeights); } } } return found; case JBC_ireturn: S.pop(); return false; case JBC_lreturn: S.pop(2); return false; case JBC_freturn: S.pop(); return false; case JBC_dreturn: S.pop(2); return false; case JBC_areturn: S.pop(); return false; case JBC_return: return false; case JBC_getfield: S.pop(); case JBC_getstatic: { FieldReference fieldRef = bytecodes.getFieldReference(); TypeReference ftype = fieldRef.getFieldContentsType(); byte tcode = ftype.getName().parseForTypeCode(); if ((tcode == LongTypeCode) || (tcode == DoubleTypeCode)) { S.push(VoidTypeCode); } S.push(tcode); } break; case JBC_putstatic: { FieldReference fieldRef = bytecodes.getFieldReference(); TypeReference ftype = fieldRef.getFieldContentsType(); byte tcode = ftype.getName().parseForTypeCode(); if ((tcode == LongTypeCode) || (tcode == DoubleTypeCode)) { S.pop(2); } else { S.pop(); } } break; case JBC_putfield: { FieldReference fieldRef = bytecodes.getFieldReference(); TypeReference ftype = fieldRef.getFieldContentsType(); byte tcode = ftype.getName().parseForTypeCode(); if ((tcode == LongTypeCode) || (tcode == DoubleTypeCode)) { S.pop(2); } else { S.pop(); } } S.pop(); break; case JBC_invokevirtual: case JBC_invokespecial: case JBC_invokestatic: case JBC_invokeinterface: { MethodReference callee = bytecodes.getMethodReference(); int psize = callee.getParameterWords(); S.pop(psize); if (bcode != JBC_invokestatic) { S.pop(); // pop the object reference } TypeReference rtype = callee.getReturnType(); byte tcode = rtype.getName().parseForTypeCode(); if (tcode == VoidTypeCode) { // nothing to do with void return type } else { if ((tcode == LongTypeCode) || (tcode == DoubleTypeCode)) { S.push(VoidTypeCode); } S.push(tcode); } if (bcode == JBC_invokeinterface) { bytecodes.alignInvokeInterface(); } } break; case JBC_xxxunusedxxx: break; case JBC_new: bytecodes.getTypeReference(); // skip cpi of type S.push(ClassTypeCode); break; case JBC_newarray: S.pop(); S.push(ArrayTypeCode); bytecodes.getArrayElementType(); // skip cpi of element type break; case JBC_anewarray: S.pop(); S.push(ArrayTypeCode); bytecodes.getTypeReference(); // skip cpi of reference type break; case JBC_arraylength: S.pop(); S.push(IntTypeCode); break; case JBC_athrow: S.clear(); S.push(ClassTypeCode); return false; case JBC_checkcast: bytecodes.getTypeReference(); // skip cpi of reference type break; case JBC_instanceof: S.pop(); S.push(IntTypeCode); bytecodes.getTypeReference(); // skip cpi of reference type break; case JBC_monitorenter: case JBC_monitorexit: S.pop(); break; case JBC_wide: { int widecode = bytecodes.getWideOpcode(); int index = bytecodes.getWideLocalNumber(); switch (widecode) { case JBC_iload: S.push(IntTypeCode); break; case JBC_lload: S.push(LongTypeCode); break; case JBC_fload: S.push(FloatTypeCode); break; case JBC_dload: S.push(DoubleTypeCode); break; case JBC_aload: S.push(ClassTypeCode); break; case JBC_istore: S.pop(); if (doDFS) ltypes[index] = IntTypeCode; break; case JBC_lstore: S.pop(); if (doDFS) ltypes[index] = LongTypeCode; break; case JBC_fstore: S.pop(); if (doDFS) ltypes[index] = FloatTypeCode; break; case JBC_dstore: S.pop(); if (doDFS) ltypes[index] = DoubleTypeCode; break; case JBC_astore: { byte tcode = S.pop(); if (doDFS) ltypes[index] = tcode; // for ret address. if (tcode == ReturnAddressTypeCode) { retaddr[index] = addr; addr = -1; } } break; case JBC_iinc: { bytecodes.getWideIncrement(); // skip increment if (doDFS) ltypes[index] = IntTypeCode; } break; case JBC_ret: if (doDFS) ltypes[index] = VoidTypeCode; if (retaddr[index] != -1) { bytecodes.reset(retaddr[index]); retaddr[index] = -1; } else { // now we hit ret, return out return false; } break; default: if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); break; } break; } case JBC_multianewarray: { bytecodes.getTypeReference(); // skip type reference int dims = bytecodes.getArrayDimension(); S.pop(dims); S.push(ArrayTypeCode); } break; case JBC_impdep1: { int pseudo_opcode = bytecodes.nextPseudoInstruction(); switch (pseudo_opcode) { case PSEUDO_LoadIntConst: bytecodes.readIntConst(); // skip value S.push(IntTypeCode); break; case PSEUDO_LoadLongConst: bytecodes.readLongConst(); // skip value S.push(VoidTypeCode); S.push(LongTypeCode); break; case PSEUDO_LoadWordConst: if (VM.BuildFor32Addr) { bytecodes.readIntConst(); } else { bytecodes.readLongConst(); // skip value } S.push(WordTypeCode); break; case PSEUDO_LoadFloatConst: bytecodes.readIntConst(); // skip value S.push(FloatTypeCode); break; case PSEUDO_LoadDoubleConst: bytecodes.readLongConst(); // skip value S.push(VoidTypeCode); S.push(DoubleTypeCode); break; case PSEUDO_LoadRetAddrConst: // remember the address for ret. addr = bytecodes.readIntConst(); // get address S.push(ReturnAddressTypeCode); break; case PSEUDO_InvokeStatic: { int mid = bytecodes.readIntConst(); // get METHIDX RVMMethod callee = InvokeStatic.targetMethod(mid); int psize = callee.getParameterWords(); S.pop(psize); TypeReference rtype = callee.getReturnType(); byte tcode = rtype.getName().parseForTypeCode(); if (tcode == VoidTypeCode) { // nothing to do with void return type } else { if ((tcode == LongTypeCode) || (tcode == DoubleTypeCode)) { S.push(VoidTypeCode); } S.push(tcode); } break; } /* case PSEUDO_CheckCast: bytecodes.readIntConst(); // skip type id break; */ case PSEUDO_InvokeCompiledMethod: int cmid = bytecodes.readIntConst(); // cmid bytecodes.readIntConst(); // skip bcindex RVMMethod callee = CompiledMethods.getCompiledMethod(cmid).getMethod(); int psize = callee.getParameterWords(); S.pop(psize); if (!callee.isStatic()) { S.pop(); // pop receiver } TypeReference rtype = callee.getReturnType(); byte tcode = rtype.getName().parseForTypeCode(); if (tcode == VoidTypeCode) { // nothing to do with void return type } else { if ((tcode == LongTypeCode) || (tcode == DoubleTypeCode)) { S.push(VoidTypeCode); } S.push(tcode); } break; case PSEUDO_ParamInitEnd: break; default: if (VM.VerifyAssertions) { VM.sysWrite(" Error, no such pseudo code : " + pseudo_opcode + "\n"); VM._assert(VM.NOT_REACHED); } return false; } break; } default: VM.sysWrite("Unknown bytecode : " + bcode + "\n"); return false; } } /* did not found the PC. */ return false; } }