byte[] nullAdaptClass(final InputStream is, final String name) throws Exception { JavaClass jc = new ClassParser(is, name + ".class").parse(); ClassGen cg = new ClassGen(jc); String cName = cg.getClassName(); ConstantPoolGen cp = cg.getConstantPool(); Method[] ms = cg.getMethods(); for (int j = 0; j < ms.length; ++j) { MethodGen mg = new MethodGen(ms[j], cg.getClassName(), cp); boolean lv = ms[j].getLocalVariableTable() == null; boolean ln = ms[j].getLineNumberTable() == null; if (lv) { mg.removeLocalVariables(); } if (ln) { mg.removeLineNumbers(); } mg.stripAttributes(skipDebug); InstructionList il = mg.getInstructionList(); if (il != null) { InstructionHandle ih = il.getStart(); while (ih != null) { ih = ih.getNext(); } if (compute) { mg.setMaxStack(); mg.setMaxLocals(); } } cg.replaceMethod(ms[j], mg.getMethod()); } return cg.getJavaClass().getBytes(); }
/** * @param verbose toggle output format * @return String containing all instructions in this list. */ public String toString(boolean verbose) { StringBuilder buf = new StringBuilder(); for (InstructionHandle ih = start; ih != null; ih = ih.next) { buf.append(ih.toString(verbose)).append("\n"); } return buf.toString(); }
/** * Sets the leaving RET instruction. Must be invoked after all instructions are added. Must not * be invoked for top-level 'subroutine'. */ void setLeavingRET() { if (localVariable == UNSET) { throw new AssertionViolatedException( "setLeavingRET() called for top-level 'subroutine' or forgot to set local variable first."); } Iterator iter = instructions.iterator(); InstructionHandle ret = null; while (iter.hasNext()) { InstructionHandle actual = (InstructionHandle) iter.next(); if (actual.getInstruction() instanceof RET) { if (ret != null) { throw new StructuralCodeConstraintException( "Subroutine with more then one RET detected: '" + ret + "' and '" + actual + "'."); } else { ret = actual; } } } if (ret == null) { throw new StructuralCodeConstraintException("Subroutine without a RET detected."); } if (((RET) ret.getInstruction()).getIndex() != localVariable) { throw new StructuralCodeConstraintException( "Subroutine uses '" + ret + "' which does not match the correct local variable '" + localVariable + "'."); } theRET = ret; }
/** * Get CodeException object.<br> * This relies on that the instruction list has already been dumped to byte code or or that the * `setPositions' methods has been called for the instruction list. * * @param cp constant pool */ public CodeException getCodeException(ConstantPoolGen cp) { return new CodeException( start_pc.getPosition(), end_pc.getPosition() + end_pc.getInstruction().getLength(), handler_pc.getPosition(), (catch_type == null) ? 0 : cp.addClass(catch_type)); }
/** Instrument the specified method to replace mapped calls. */ public void instrument_method(Method m, MethodGen mg) { // Loop through each instruction, making substitutions InstructionList il = mg.getInstructionList(); for (InstructionHandle ih = il.getStart(); ih != null; ) { if (debug_instrument_inst.enabled()) { debug_instrument_inst.log("instrumenting instruction %s%n", ih); // ih.getInstruction().toString(pool.getConstantPool())); } InstructionList new_il = null; // Remember the next instruction to process InstructionHandle next_ih = ih.getNext(); // Get the translation for this instruction (if any) new_il = xform_inst(mg, ih.getInstruction()); if (debug_instrument_inst.enabled()) debug_instrument_inst.log(" new inst: %s%n", new_il); // If this instruction was modified, replace it with the new // instruction list. If this instruction was the target of any // jumps or line numbers , replace them with the first // instruction in the new list replace_instructions(il, ih, new_il); ih = next_ih; } }
CFG createCFG(String className) throws ClassNotFoundException { CFG cfg = new CFG(); JavaClass jc = Repository.lookupClass(className); ClassGen cg = new ClassGen(jc); ConstantPoolGen cpg = cg.getConstantPool(); for (Method m : cg.getMethods()) { MethodGen mg = new MethodGen(m, cg.getClassName(), cpg); InstructionList il = mg.getInstructionList(); InstructionHandle[] handles = il.getInstructionHandles(); int prev = 0; for (InstructionHandle ih : handles) { int position = ih.getPosition(); cfg.addNode(position, m, jc); Instruction inst = ih.getInstruction(); boolean br = inst.getName().contains("if") || inst.getName().contains("goto"); boolean ret = inst.getName().contains("return"); boolean stat = inst.getName().contains("invokestatic"); int len = inst.getLength(); if (stat) { int index = inst.toString(true).indexOf(" "); String name = inst.toString(true).substring(index + 1); int tar = Integer.valueOf(name); INVOKESTATIC inv = new INVOKESTATIC(tar); name = inv.getMethodName(cpg); Method m2 = null; Method[] tm = cg.getMethods(); for (int i = 0; i < tm.length; i++) { if (tm[i].getName().equals(name)) { m2 = tm[i]; } } cfg.addEdge(position, m, jc, 0, m2, jc); cfg.addEdge(-1, m2, jc, position + len, m, jc); } if (!ret && !stat) { cfg.addEdge(position, position + len, m, jc); } if (br) { cfg.addEdge(position, position + len, m, jc); IF_ICMPGE comp = new IF_ICMPGE(ih); String name = comp.getTarget().toString(false); int index = name.indexOf(">"); name = name.substring(index + 2); int tar = Integer.valueOf(name); cfg.addEdge(position, tar, m, jc); } if (ret) { cfg.addEdge(position, -1, m, jc); } prev = position; } System.out.println(cfg.toString()); } return cfg; }
/** Build the array of line number information for the optimized instruction sequence. */ private LineNumberInfo[] buildLineNumberInfo() { Vector lineNumbers = new Vector(); for (InstructionHandle handle = this.codeStart; handle != null; handle = handle.getNext()) { handle.addLineNumberInfo(lineNumbers); } return (LineNumberInfo[]) Utils.toArray(lineNumbers, LineNumberInfo.class); }
/** * Delete contents of list. Provides besser memory utilization, because the system then may reuse * the instruction handles. This method is typically called right after * <href="MethodGen.html#getMethod()">MethodGen.getMethod()</a>. */ public void dispose() { // Traverse in reverse order, because ih.next is overwritten for (InstructionHandle ih = end; ih != null; ih = ih.prev) { /* Causes BranchInstructions to release target and targeters, because it * calls dispose() on the contained instruction. */ ih.dispose(); } clear(); }
private void dumpCode() { int i = 0; for (InstructionHandle handle = this.codeStart; handle != null; handle = handle.getNext()) { System.err.println( i++ + ":\t" + org.caesarj.classfile.OpcodeNames.getName(handle.getInstruction().getOpcode())); } System.err.flush(); }
/** Start the method's visit. */ public void start() { if (!mg.isAbstract() && !mg.isNative()) { for (InstructionHandle ih = mg.getInstructionList().getStart(); ih != null; ih = ih.getNext()) { Instruction i = ih.getInstruction(); if (!visitInstruction(i)) i.accept(this); } updateExceptionHandlers(); } }
/** * Get LocalVariable object. * * <p>This relies on that the instruction list has already been dumped to byte code or or that the * `setPositions' methods has been called for the instruction list. * * <p>Note that for local variables whose scope end at the last instruction of the method's code, * the JVM specification is ambiguous: both a start_pc+length ending at the last instruction and * start_pc+length ending at first index beyond the end of the code are valid. * * @param il instruction list (byte code) which this variable belongs to * @param cp constant pool */ public LocalVariable getLocalVariable(ConstantPoolGen cp) { int start_pc = start.getPosition(); int length = end.getPosition() - start_pc; if (length > 0) length += end.getInstruction().getLength(); int name_index = cp.addUtf8(name); int signature_index = cp.addUtf8(type.getSignature()); return new LocalVariable( start_pc, length, name_index, signature_index, index, cp.getConstantPool()); }
/** * Insert an instruction at start of this list. * * @param ih instruction to insert */ private void insert(InstructionHandle ih) { if (isEmpty()) { start = end = ih; ih.next = ih.prev = null; } else { start.prev = ih; ih.next = start; ih.prev = null; start = ih; } length++; }
/** * A utility method that calculates the successors of a given InstructionHandle <B>in the same * subroutine</B>. That means, a RET does not have any successors as defined here. A * JsrInstruction has its physical successor as its successor (opposed to its target) as defined * here. */ private static InstructionHandle[] getSuccessors(InstructionHandle instruction) { final InstructionHandle[] empty = new InstructionHandle[0]; final InstructionHandle[] single = new InstructionHandle[1]; final InstructionHandle[] pair = new InstructionHandle[2]; Instruction inst = instruction.getInstruction(); if (inst instanceof RET) { return empty; } // Terminates method normally. if (inst instanceof ReturnInstruction) { return empty; } // Terminates method abnormally, because JustIce mandates // subroutines not to be protected by exception handlers. if (inst instanceof ATHROW) { return empty; } // See method comment. if (inst instanceof JsrInstruction) { single[0] = instruction.getNext(); return single; } if (inst instanceof GotoInstruction) { single[0] = ((GotoInstruction) inst).getTarget(); return single; } if (inst instanceof BranchInstruction) { if (inst instanceof Select) { // BCEL's getTargets() returns only the non-default targets, // thanks to Eli Tilevich for reporting. InstructionHandle[] matchTargets = ((Select) inst).getTargets(); InstructionHandle[] ret = new InstructionHandle[matchTargets.length + 1]; ret[0] = ((Select) inst).getTarget(); System.arraycopy(matchTargets, 0, ret, 1, matchTargets.length); return ret; } else { pair[0] = instruction.getNext(); pair[1] = ((BranchInstruction) inst).getTarget(); return pair; } } // default case: Fall through. single[0] = instruction.getNext(); return single; }
/** * Append an instruction to the end of this list. * * @param ih instruction to append */ private void append(InstructionHandle ih) { if (isEmpty()) { start = end = ih; ih.next = ih.prev = null; } else { end.next = ih; ih.prev = end; ih.next = null; end = ih; } length++; // Update length }
private boolean optimizeCodeSequence() { boolean codeChanged = false; buildBasicBlocks(codeStart); for (InstructionHandle handle = codeStart; handle != null; handle = handle.getNext()) { codeChanged |= Patterns.optimize(handle); } codeChanged |= cleanCode(); return codeChanged; }
/** * Replace instruction ih in list il with the instructions in new_il. If new_il is null, do * nothing */ protected static void replace_instructions( InstructionList il, InstructionHandle ih, InstructionList new_il) { if ((new_il == null) || new_il.isEmpty()) return; // If there is only one new instruction, just replace it in the handle if (new_il.getLength() == 1) { ih.setInstruction(new_il.getEnd().getInstruction()); return; } // Get the start and end instruction of the new instructions InstructionHandle new_end = new_il.getEnd(); InstructionHandle new_start = il.insert(ih, new_il); // Move all of the branches from the old instruction to the new start il.redirectBranches(ih, new_start); // Move other targets to the new instuctions. if (ih.hasTargeters()) { for (InstructionTargeter it : ih.getTargeters()) { if (it instanceof LineNumberGen) { it.updateTarget(ih, new_start); } else if (it instanceof LocalVariableGen) { it.updateTarget(ih, new_end); } else if (it instanceof CodeExceptionGen) { CodeExceptionGen exc = (CodeExceptionGen) it; if (exc.getStartPC() == ih) exc.updateTarget(ih, new_start); else if (exc.getEndPC() == ih) exc.updateTarget(ih, new_end); else if (exc.getHandlerPC() == ih) exc.setHandlerPC(new_start); else System.out.printf("Malformed CodeException: %s%n", exc); } else { System.out.printf("unexpected target %s%n", it); } } } // Remove the old handle. There should be no targeters left to it. try { il.delete(ih); } catch (Exception e) { throw new Error("Can't delete instruction", e); } }
/** * Build the array of the instructions resulting from the optimization process. * * @return the array of instructions */ private Instruction[] buildInstructionArray() { int length; // count size of instruction array length = 0; for (InstructionHandle handle = this.codeStart; handle != null; handle = handle.getNext()) { length += 1; } Instruction[] insns = new Instruction[length]; length = 0; for (InstructionHandle handle = this.codeStart; handle != null; handle = handle.getNext()) { insns[length] = handle.getInstruction(); length += 1; } return insns; }
/** * Long output format: * * <p><position in byte code> <name of opcode> "["<opcode number>"]" * "("<length of instruction>")" "<"<target instruction>">" "@"<branch target * offset> * * @param verbose long/short format switch * @return mnemonic for instruction */ public String toString(boolean verbose) { String s = super.toString(verbose); String t = "null"; if (verbose) { if (target != null) { if (target.getInstruction() == this) t = "<points to itself>"; else if (target.getInstruction() == null) t = "<null instruction!!!?>"; else t = target.getInstruction().toString(false); // Avoid circles } } else { if (target != null) { index = getTargetOffset(); t = "" + (index + position); } } return s + " -> " + t; }
/** Adds a new JSR or JSR_W that has this subroutine as its target. */ public void addEnteringJsrInstruction(InstructionHandle jsrInst) { if ((jsrInst == null) || (!(jsrInst.getInstruction() instanceof JsrInstruction))) { throw new AssertionViolatedException("Expecting JsrInstruction InstructionHandle."); } if (localVariable == UNSET) { throw new AssertionViolatedException("Set the localVariable first!"); } else { // Something is wrong when an ASTORE is targeted that does not operate on the same local // variable than the rest of the // JsrInstruction-targets and the RET. // (We don't know out leader here so we cannot check if we're really targeted!) if (localVariable != ((ASTORE) (((JsrInstruction) jsrInst.getInstruction()).getTarget().getInstruction())) .getIndex()) { throw new AssertionViolatedException("Setting a wrong JsrInstruction."); } } theJSRs.add(jsrInst); }
/** * Append another list after instruction (handle) ih contained in this list. Consumes argument * list, i.e., it becomes empty. * * @param ih where to append the instruction list * @param il Instruction list to append to this one * @return instruction handle pointing to the <B>first</B> appended instruction */ public InstructionHandle append(InstructionHandle ih, InstructionList il) { if (il == null) { throw new ClassGenException("Appending null InstructionList"); } if (il.isEmpty()) { return ih; } InstructionHandle next = ih.next, ret = il.start; ih.next = il.start; il.start.prev = ih; il.end.next = next; if (next != null) { next.prev = il.end; } else { end = il.end; // Update end ... } length += il.length; // Update length il.clear(); return ret; }
/** * Redirect all references from old_target to new_target, i.e., update targets of branch * instructions. * * @param old_target the old target instruction handle * @param new_target the new target instruction handle */ public void redirectBranches(InstructionHandle old_target, InstructionHandle new_target) { for (InstructionHandle ih = start; ih != null; ih = ih.next) { Instruction i = ih.getInstruction(); if (i instanceof BranchInstruction) { BranchInstruction b = (BranchInstruction) i; InstructionHandle target = b.getTarget(); if (target == old_target) { b.setTarget(new_target); } if (b instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH InstructionHandle[] targets = ((Select) b).getTargets(); for (int j = 0; j < targets.length; j++) { if (targets[j] == old_target) { ((Select) b).setTarget(j, new_target); } } } } } }
/** * Insert another list before Instruction handle ih contained in this list. Consumes argument * list, i.e., it becomes empty. * * @param ih where to append the instruction list * @param il Instruction list to insert * @return instruction handle of the first inserted instruction */ public InstructionHandle insert(InstructionHandle ih, InstructionList il) { if (il == null) { throw new ClassGenException("Inserting null InstructionList"); } if (il.isEmpty()) { return ih; } InstructionHandle prev = ih.prev, ret = il.start; ih.prev = il.end; il.end.next = ih; il.start.prev = prev; if (prev != null) { prev.next = il.start; } else { start = il.start; // Update start ... } length += il.length; // Update length il.clear(); return ret; }
private void buildBasicBlocks(InstructionHandle start) { for (InstructionHandle handle = this.codeStart; handle != null; handle = handle.getNext()) { handle.reset(); } for (int i = 0; i < handlers.length; i++) { // !!! if (start.isReachable()) ((InstructionHandle) handlers[i].getHandler()).addAccessor(handlers[i]); ((InstructionHandle) handlers[i].getStart()).addAccessor(handlers[i]); // !!! WHY ??? graf 010111 // ((InstructionHandle)handlers[i].getEnd()).addAccessor(handlers[i]); // !!! WHY ??? graf 010111 } // add local variable infos as accessors to their start and end instructions in order // to be notified by changes. for (int j = 0; localVariables != null && j < localVariables.length; j++) { ((InstructionHandle) localVariables[j].getEnd()).addAccessor(localVariables[j]); ((InstructionHandle) localVariables[j].getStart()).addAccessor(localVariables[j]); } }
/** * @param target branch target * @return the offset to `target' relative to this instruction */ protected int getTargetOffset(InstructionHandle target) { if (target == null) throw new ClassGenException("Target of " + super.toString(true) + " is invalid null handle"); int t = target.getPosition(); if (t < 0) throw new ClassGenException( "Invalid branch target position offset for " + super.toString(true) + ":" + t + ":" + target); return t - position; }
private boolean cleanCode() { boolean codeRemoved = false; // Test if all end-instructions are reached... (copied from exception"handlers" see below) for (int j = 0; localVariables != null && j < localVariables.length; j++) { while (!((InstructionHandle) localVariables[j].getEnd()).isReached()) { localVariables[j].setEnd(((InstructionHandle) localVariables[j].getEnd()).getPrevious()); } } for (int i = 0; i < handlers.length; i++) { while (!((InstructionHandle) handlers[i].getEnd()).isReached()) { handlers[i].setEnd(((InstructionHandle) handlers[i].getEnd()).getPrevious()); } // !!!if (start > end) remove handler } InstructionHandle current = codeStart; for (InstructionHandle handle = current.getNext(); handle != null; handle = handle.getNext()) { if (handle.isReached()) { current.setNext(handle); current = handle; } else { current.setNext(null); codeRemoved = true; } /* Andreas start handle.clean(); */ handle._clean(); // Andreas end } if (current == codeStart) { codeStart.setNext(null); } return codeRemoved; }
/* * Satisfies Subroutine.getAccessedLocalIndices(). */ public int[] getAccessedLocalsIndices() { // TODO: Implement caching. HashSet<Integer> acc = new HashSet<Integer>(); if (theRET == null && this != TOPLEVEL) { throw new AssertionViolatedException( "This subroutine object must be built up completely before calculating accessed locals."); } Iterator i = instructions.iterator(); while (i.hasNext()) { InstructionHandle ih = (InstructionHandle) i.next(); // RET is not a LocalVariableInstruction in the current version of BCEL. if (ih.getInstruction() instanceof LocalVariableInstruction || ih.getInstruction() instanceof RET) { int idx = ((IndexedInstruction) (ih.getInstruction())).getIndex(); acc.add(new Integer(idx)); // LONG? DOUBLE?. try { // LocalVariableInstruction instances are typed without the need to look into // the constant pool. if (ih.getInstruction() instanceof LocalVariableInstruction) { int s = ((LocalVariableInstruction) ih.getInstruction()).getType(null).getSize(); if (s == 2) acc.add(new Integer(idx + 1)); } } catch (RuntimeException re) { throw new AssertionViolatedException( "Oops. BCEL did not like NULL as a ConstantPoolGen object."); } } } int[] ret = new int[acc.size()]; i = acc.iterator(); int j = -1; while (i.hasNext()) { j++; ret[j] = ((Integer) i.next()).intValue(); } return ret; }
/** * Get LineNumber attribute . * * <p>This relies on that the instruction list has already been dumped to byte code or or that the * `setPositions' methods has been called for the instruction list. */ public LineNumber getLineNumber() { return new LineNumber(ih.getPosition(), src_line); }
/** * Take all instructions (handles) from "start" to "end" and append them after the new location * "target". Of course, "end" must be after "start" and target must not be located withing this * range. If you want to move something to the start of the list use null as value for target.<br> * Any instruction targeters pointing to handles within the block, keep their targets. * * @param start of moved block * @param end of moved block * @param target of moved block */ public void move(InstructionHandle start, InstructionHandle end, InstructionHandle target) { // Step 1: Check constraints if ((start == null) || (end == null)) { throw new ClassGenException("Invalid null handle: From " + start + " to " + end); } if ((target == start) || (target == end)) { throw new ClassGenException( "Invalid range: From " + start + " to " + end + " contains target " + target); } for (InstructionHandle ih = start; ih != end.next; ih = ih.next) { if (ih == null) { throw new ClassGenException("Invalid range: From " + start + " to " + end); } else if (ih == target) { throw new ClassGenException( "Invalid range: From " + start + " to " + end + " contains target " + target); } } // Step 2: Temporarily remove the given instructions from the list InstructionHandle prev = start.prev, next = end.next; if (prev != null) { prev.next = next; } else { this.start = next; } if (next != null) { next.prev = prev; } else { this.end = prev; } start.prev = end.next = null; // Step 3: append after target if (target == null) { // append to start of list if (this.start != null) { this.start.prev = end; } end.next = this.start; this.start = start; } else { next = target.next; target.next = start; start.prev = target; end.next = next; if (next != null) { next.prev = end; } else { this.end = end; } } }
/** * Remove from instruction `prev' to instruction `next' both contained in this list. Throws * TargetLostException when one of the removed instruction handles is still being targeted. * * @param prev where to start deleting (predecessor, exclusive) * @param next where to end deleting (successor, exclusive) */ private void remove(InstructionHandle prev, InstructionHandle next) throws TargetLostException { InstructionHandle first, last; // First and last deleted instruction if ((prev == null) && (next == null)) { first = start; last = end; start = end = null; } else { if (prev == null) { // At start of list first = start; start = next; } else { first = prev.next; prev.next = next; } if (next == null) { // At end of list last = end; end = prev; } else { last = next.prev; next.prev = prev; } } first.prev = null; // Completely separated from rest of list last.next = null; List<InstructionHandle> target_vec = new ArrayList<InstructionHandle>(); for (InstructionHandle ih = first; ih != null; ih = ih.next) { ih.getInstruction().dispose(); // e.g. BranchInstructions release their targets } StringBuilder buf = new StringBuilder("{ "); for (InstructionHandle ih = first; ih != null; ih = next) { next = ih.next; length--; if (ih.hasTargeters()) { // Still got targeters? target_vec.add(ih); buf.append(ih.toString(true) + " "); ih.next = ih.prev = null; } else { ih.dispose(); } } buf.append("}"); if (!target_vec.isEmpty()) { InstructionHandle[] targeted = new InstructionHandle[target_vec.size()]; target_vec.toArray(targeted); throw new TargetLostException(targeted, buf.toString()); } }
/** * Give all instructions their position number (offset in byte stream), i.e., make the list ready * to be dumped. * * @param check Perform sanity checks, e.g. if all targeted instructions really belong to this * list */ public void setPositions(boolean check) { int max_additional_bytes = 0, additional_bytes = 0; int index = 0, count = 0; int[] pos = new int[length]; /* Pass 0: Sanity checks */ if (check) { for (InstructionHandle ih = start; ih != null; ih = ih.next) { Instruction i = ih.instruction; if (i instanceof BranchInstruction) { // target instruction within list? Instruction inst = ((BranchInstruction) i).getTarget().instruction; if (!contains(inst)) { throw new ClassGenException( "Branch target of " + Constants.OPCODE_NAMES[i.opcode] + ":" + inst + " not in instruction list"); } if (i instanceof Select) { InstructionHandle[] targets = ((Select) i).getTargets(); for (InstructionHandle target : targets) { inst = target.instruction; if (!contains(inst)) { throw new ClassGenException( "Branch target of " + Constants.OPCODE_NAMES[i.opcode] + ":" + inst + " not in instruction list"); } } } if (!(ih instanceof BranchHandle)) { throw new ClassGenException( "Branch instruction " + Constants.OPCODE_NAMES[i.opcode] + ":" + inst + " not contained in BranchHandle."); } } } } /* Pass 1: Set position numbers and sum up the maximum number of bytes an * instruction may be shifted. */ for (InstructionHandle ih = start; ih != null; ih = ih.next) { Instruction i = ih.instruction; ih.setPosition(index); pos[count++] = index; /* Get an estimate about how many additional bytes may be added, because * BranchInstructions may have variable length depending on the target * offset (short vs. int) or alignment issues (TABLESWITCH and * LOOKUPSWITCH). */ switch (i.getOpcode()) { case Constants.JSR: case Constants.GOTO: max_additional_bytes += 2; break; case Constants.TABLESWITCH: case Constants.LOOKUPSWITCH: max_additional_bytes += 3; break; } index += i.getLength(); } /* Pass 2: Expand the variable-length (Branch)Instructions depending on * the target offset (short or int) and ensure that branch targets are * within this list. */ for (InstructionHandle ih = start; ih != null; ih = ih.next) { additional_bytes += ih.updatePosition(additional_bytes, max_additional_bytes); } /* Pass 3: Update position numbers (which may have changed due to the * preceding expansions), like pass 1. */ index = count = 0; for (InstructionHandle ih = start; ih != null; ih = ih.next) { Instruction i = ih.instruction; ih.setPosition(index); pos[count++] = index; index += i.getLength(); } byte_positions = new int[count]; // Trim to proper size System.arraycopy(pos, 0, byte_positions, 0, count); }