private static byte[] insertGapCore1( byte[] code, int where, int gapLength, boolean exclusive, ExceptionTable etable, CodeAttribute ca) throws BadBytecode, AlignmentException { int codeLength = code.length; byte[] newcode = new byte[codeLength + gapLength]; insertGap2(code, where, gapLength, codeLength, newcode, exclusive); etable.shiftPc(where, gapLength, exclusive); LineNumberAttribute na = (LineNumberAttribute) ca.getAttribute(LineNumberAttribute.tag); if (na != null) na.shiftPc(where, gapLength, exclusive); LocalVariableAttribute va = (LocalVariableAttribute) ca.getAttribute(LocalVariableAttribute.tag); if (va != null) va.shiftPc(where, gapLength, exclusive); LocalVariableAttribute vta = (LocalVariableAttribute) ca.getAttribute(LocalVariableAttribute.typeTag); if (vta != null) vta.shiftPc(where, gapLength, exclusive); StackMapTable smt = (StackMapTable) ca.getAttribute(StackMapTable.tag); if (smt != null) smt.shiftPc(where, gapLength, exclusive); StackMap sm = (StackMap) ca.getAttribute(StackMap.tag); if (sm != null) sm.shiftPc(where, gapLength, exclusive); return newcode; }
@SuppressWarnings("unchecked") public void visit(ClassFile file) throws Exception { List<MethodInfo> methods = file.getMethods(); for (MethodInfo mi : methods) { try { CodeAttribute codeAttribute = mi.getCodeAttribute(); // ignore abstract methods if (codeAttribute == null) { continue; } final int maxStack = codeAttribute.getMaxStack(); int modified = 0; for (MethodRewriter mr : rewriters) { modified += mr.visit(mi); } if (modified != 0) { codeAttribute.setMaxStack(maxStack + modified); } } catch (Exception e) { throw new IllegalStateException("Cannot rewrite method: " + mi, e); } } }
/* skipSuper 1: this(), 0: super(), -1: both. */ private int skipSuperConstructor0(int skipThis) throws BadBytecode { begin(); ConstPool cp = codeAttr.getConstPool(); String thisClassName = codeAttr.getDeclaringClass(); int nested = 0; while (hasNext()) { int index = next(); int c = byteAt(index); if (c == NEW) ++nested; else if (c == INVOKESPECIAL) { int mref = ByteArray.readU16bit(bytecode, index + 1); if (cp.getMethodrefName(mref).equals(MethodInfo.nameInit)) if (--nested < 0) { if (skipThis < 0) return index; String cname = cp.getMethodrefClassName(mref); if (cname.equals(thisClassName) == (skipThis > 0)) return index; else break; } } } begin(); return -1; }
/** * Rebuilds a stack map table for J2ME (CLDC). If no stack map table is included, a new one is * created. If this <code>MethodInfo</code> does not include a code attribute, nothing happens. * * @param pool used for making type hierarchy. * @see StackMap * @since 3.12 */ public void rebuildStackMapForME(ClassPool pool) throws BadBytecode { CodeAttribute ca = getCodeAttribute(); if (ca != null) { StackMap sm = MapMaker.make2(pool, this); ca.setAttribute(sm); } }
/** * Rebuilds a stack map table. If no stack map table is included, a new one is created. If this * <code>MethodInfo</code> does not include a code attribute, nothing happens. * * @param pool used for making type hierarchy. * @see StackMapTable * @since 3.6 */ public void rebuildStackMap(ClassPool pool) throws BadBytecode { CodeAttribute ca = getCodeAttribute(); if (ca != null) { StackMapTable smt = MapMaker.make(pool, this); ca.setAttribute(smt); } }
/** * Returns the line number of the source line corresponding to the specified bytecode contained in * this method. * * @param pos the position of the bytecode (>= 0). an index into the code array. * @return -1 if this information is not available. */ public int getLineNumber(int pos) { CodeAttribute ca = getCodeAttribute(); if (ca == null) return -1; LineNumberAttribute ainfo = (LineNumberAttribute) ca.getAttribute(LineNumberAttribute.tag); if (ainfo == null) return -1; return ainfo.toLineNumber(pos); }
private void initExtraHarvest() { try { CtClass terraForming = HookManager.getInstance() .getClassPool() .get("com.wurmonline.server.behaviours.Terraforming"); CtClass[] paramTypes = { HookManager.getInstance().getClassPool().get("com.wurmonline.server.creatures.Creature"), CtPrimitiveType.intType, CtPrimitiveType.intType, CtPrimitiveType.booleanType, CtPrimitiveType.intType, CtPrimitiveType.floatType, HookManager.getInstance().getClassPool().get("com.wurmonline.server.items.Item") }; CtMethod method = terraForming.getMethod( "harvest", Descriptor.ofMethod(CtPrimitiveType.booleanType, paramTypes)); MethodInfo methodInfo = method.getMethodInfo(); CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); CodeIterator codeIterator = codeAttribute.iterator(); LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); int quantityIndex = -1; for (int i = 0; i < attr.tableLength(); i++) { if ("quantity".equals(attr.variableName(i))) { quantityIndex = attr.index(i); } } if (quantityIndex == -1) { throw new HookException("Quantity variable can not be resolved"); } while (codeIterator.hasNext()) { int pos = codeIterator.next(); int op = codeIterator.byteAt(pos); if (op == CodeIterator.ISTORE) { int fieldRefIdx = codeIterator.byteAt(pos + 1); if (quantityIndex == fieldRefIdx) { Bytecode bytecode = new Bytecode(codeIterator.get().getConstPool()); bytecode.addIconst(extraHarvest); bytecode.add(Bytecode.IADD); codeIterator.insertAt(pos, bytecode.get()); break; } } } } catch (NotFoundException | BadBytecode e) { throw new HookException(e); } }
Pointers(int cur, int m, int m0, ExceptionTable et, CodeAttribute ca) { cursor = cur; mark = m; mark0 = m0; etable = et; // non null line = (LineNumberAttribute) ca.getAttribute(LineNumberAttribute.tag); vars = (LocalVariableAttribute) ca.getAttribute(LocalVariableAttribute.tag); types = (LocalVariableAttribute) ca.getAttribute(LocalVariableAttribute.typeTag); stack = (StackMapTable) ca.getAttribute(StackMapTable.tag); stack2 = (StackMap) ca.getAttribute(StackMap.tag); }
/** * Return javaassist source snippet which lists all the parameters and their values. If available * the source names are extracted from the debug information and used, otherwise just a number is * shown. * * @param method * @return * @throws NotFoundException */ public static String getSignature(CtBehavior method) throws NotFoundException { CtClass[] parameterTypes = method.getParameterTypes(); CodeAttribute codeAttribute = method.getMethodInfo().getCodeAttribute(); LocalVariableAttribute locals = null; if (codeAttribute != null) { AttributeInfo attribute; attribute = codeAttribute.getAttribute("LocalVariableTable"); locals = (LocalVariableAttribute) attribute; } String methodName = method.getName(); StringBuilder sb = new StringBuilder(methodName).append("(\" "); for (int i = 0; i < parameterTypes.length; i++) { if (i > 0) { // add a comma and a space between printed values sb.append(" + \", \" "); } CtClass parameterType = parameterTypes[i]; boolean isArray = parameterType.isArray(); CtClass arrayType = parameterType.getComponentType(); if (isArray) { while (arrayType.isArray()) { arrayType = arrayType.getComponentType(); } } sb.append(" + \""); try { sb.append(parameterNameFor(method, locals, i)); } catch (Exception e) { sb.append(i + 1); } sb.append("\" + \"="); if (parameterType.isPrimitive()) { // let the compiler handle primitive -> string sb.append("\"+ $").append(i + 1); } else { String s = "org.slf4j.instrumentation.ToStringHelper.render"; sb.append("\"+ ").append(s).append("($").append(i + 1).append(')'); } } sb.append("+\")"); String signature = sb.toString(); return signature; }
/** * Changes a super constructor called by this constructor. * * <p>This method modifies a call to <code>super()</code>, which should be at the head of a * constructor body, so that a constructor in a different super class is called. This method does * not change actural parameters. Hence the new super class must have a constructor with the same * signature as the original one. * * <p>This method should be called when the super class of the class declaring this method is * changed. * * <p>This method does not perform anything unless this <code>MethodInfo</code> represents a * constructor. * * @param superclass the new super class */ public void setSuperclass(String superclass) throws BadBytecode { if (!isConstructor()) return; CodeAttribute ca = getCodeAttribute(); byte[] code = ca.getCode(); CodeIterator iterator = ca.iterator(); int pos = iterator.skipSuperConstructor(); if (pos >= 0) { // not this() ConstPool cp = constPool; int mref = ByteArray.readU16bit(code, pos + 1); int nt = cp.getMethodrefNameAndType(mref); int sc = cp.addClassInfo(superclass); int mref2 = cp.addMethodrefInfo(sc, nt); ByteArray.write16bit(mref2, code, pos + 1); } }
/** * Constructs a copy of <code>Code_attribute</code>. Specified class names are replaced during the * copy. * * @param cp constant pool table. * @param src source Code attribute. * @param classnames pairs of replaced and substituted class names. */ private CodeAttribute(ConstPool cp, CodeAttribute src, Map classnames) throws BadBytecode { super(cp, tag); maxStack = src.getMaxStack(); maxLocals = src.getMaxLocals(); exceptions = src.getExceptionTable().copy(cp, classnames); attributes = new ArrayList(); List src_attr = src.getAttributes(); int num = src_attr.size(); for (int i = 0; i < num; ++i) { AttributeInfo ai = (AttributeInfo) src_attr.get(i); attributes.add(ai.copy(cp, classnames)); } info = src.copyCode(cp, classnames, exceptions, this); }
private void read(MethodInfo src, String methodname, Map classnames) throws BadBytecode { ConstPool destCp = constPool; accessFlags = src.accessFlags; name = destCp.addUtf8Info(methodname); cachedName = methodname; ConstPool srcCp = src.constPool; String desc = srcCp.getUtf8Info(src.descriptor); String desc2 = Descriptor.rename(desc, classnames); descriptor = destCp.addUtf8Info(desc2); attribute = new LinkedList(); ExceptionsAttribute eattr = src.getExceptionsAttribute(); if (eattr != null) attribute.add(eattr.copy(destCp, classnames)); CodeAttribute cattr = src.getCodeAttribute(); if (cattr != null) attribute.add(cattr.copy(destCp, classnames)); }
/** Prints the bytecode instructions of a given method. */ public void print(CtMethod method) { MethodInfo info = method.getMethodInfo2(); ConstPool pool = info.getConstPool(); CodeAttribute code = info.getCodeAttribute(); if (code == null) return; CodeIterator iterator = code.iterator(); while (iterator.hasNext()) { int pos; try { pos = iterator.next(); } catch (BadBytecode e) { throw new RuntimeException(e); } stream.println(pos + ": " + instructionString(iterator, pos, pool)); } }
/** Copies code. */ private byte[] copyCode( ConstPool destCp, Map classnames, ExceptionTable etable, CodeAttribute destCa) throws BadBytecode { int len = getCodeLength(); byte[] newCode = new byte[len]; destCa.info = newCode; LdcEntry ldc = copyCode(this.info, 0, len, this.getConstPool(), newCode, destCp, classnames); return LdcEntry.doit(newCode, ldc, etable, destCa); }
/** * Appends a gap at the end of the bytecode sequence. * * @param gapLength gap length */ public void appendGap(int gapLength) { byte[] code = bytecode; int codeLength = code.length; byte[] newcode = new byte[codeLength + gapLength]; int i; for (i = 0; i < codeLength; ++i) newcode[i] = code[i]; for (i = codeLength; i < codeLength + gapLength; ++i) newcode[i] = NOP; codeAttr.setCode(newcode); bytecode = newcode; endPos = getCodeLength(); }
/** * Inserts an inclusive or exclusive gap in front of the instruction at the given index <code>pos * </code>. Branch offsets and the exception table in the method body are also updated. The * inserted gap is filled with NOP. The gap length may be extended to a multiple of 4. * * <p>Suppose that the instruction at the given index is at the beginning of a block statement. If * the gap is inclusive, then it is included within that block. If the gap is exclusive, then it * is excluded from that block. * * <p>The index at which the gap is actually inserted might be different from pos since some other * bytes might be inserted at other positions (e.g. to change <code>GOTO</code> to <code>GOTO_W * </code>). The index is available from the <code>Gap</code> object returned by this method. * * <p>Suppose that the gap is inserted at the position of the next instruction that would be * returned by <code>next()</code> (not the last instruction returned by the last call to <code> * next()</code>). The next instruction returned by <code>next()</code> after the gap is inserted * is still the same instruction. It is not <code>NOP</code> at the first byte of the inserted * gap. * * @param pos the index at which a gap is inserted. * @param length gap length. * @param exclusive true if exclusive, otherwise false. * @return the position and the length of the inserted gap. * @since 3.11 */ public Gap insertGapAt(int pos, int length, boolean exclusive) throws BadBytecode { /** cursorPos indicates the next bytecode whichever exclusive is true or false. */ Gap gap = new Gap(); if (length <= 0) { gap.position = pos; gap.length = 0; return gap; } byte[] c; int length2; if (bytecode.length + length > Short.MAX_VALUE) { // currentPos might change after calling insertGapCore0w(). c = insertGapCore0w( bytecode, pos, length, exclusive, get().getExceptionTable(), codeAttr, gap); pos = gap.position; length2 = length; // == gap.length } else { int cur = currentPos; c = insertGapCore0(bytecode, pos, length, exclusive, get().getExceptionTable(), codeAttr); // insertGapCore0() never changes pos. length2 = c.length - bytecode.length; gap.position = pos; gap.length = length2; if (cur >= pos) currentPos = cur + length2; if (mark > pos || (mark == pos && exclusive)) mark += length2; } codeAttr.setCode(c); bytecode = c; endPos = getCodeLength(); updateCursors(pos, length2); return gap; }
public boolean match(String filename, ClassFile classFile, ClassMap tempClassMap) { for (Object o : classFile.getMethods()) { MethodInfo methodInfo = (MethodInfo) o; classMod.methodInfo = methodInfo; CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); if (codeAttribute == null) { continue; } if (getClassMap().hasMap(deobfMethod)) { MethodRef obfTarget = (MethodRef) getClassMap().map(deobfMethod); if (!methodInfo.getName().equals(obfTarget.getName())) { continue; } } ArrayList<String> deobfTypes = null; ArrayList<String> obfTypes = null; if (deobfMethod != null && deobfMethod.getType() != null) { deobfTypes = ConstPoolUtils.parseDescriptor(deobfMethod.getType()); obfTypes = ConstPoolUtils.parseDescriptor(methodInfo.getDescriptor()); if (!isPotentialTypeMatch(deobfTypes, obfTypes)) { continue; } } ConstPool constPool = methodInfo.getConstPool(); CodeIterator codeIterator = codeAttribute.iterator(); initMatcher(); ArrayList<JavaRef> tempMappings = new ArrayList<JavaRef>(); try { match: for (int offset = 0; offset < codeIterator.getCodeLength() && matcher.match(methodInfo, offset); offset = codeIterator.next()) { tempMappings.clear(); for (Map.Entry<Integer, JavaRef> entry : xrefs.entrySet()) { int captureGroup = entry.getKey(); JavaRef xref = entry.getValue(); byte[] code = matcher.getCaptureGroup(captureGroup); int index = Util.demarshal(code, 1, 2); ConstPoolUtils.matchOpcodeToRefType(code[0], xref); ConstPoolUtils.matchConstPoolTagToRefType(constPool.getTag(index), xref); JavaRef newRef = ConstPoolUtils.getRefForIndex(constPool, index); if (!isPotentialTypeMatch(xref.getType(), newRef.getType())) { if (deobfMethod != null) { Logger.log( Logger.LOG_METHOD, "method %s %s matches %s %s, but", methodInfo.getName(), methodInfo.getDescriptor(), deobfMethod.getName(), deobfMethod.getType()); } Logger.log( Logger.LOG_METHOD, "method %s %s failed xref #%d %s %s -> %s %s", methodInfo.getName(), methodInfo.getDescriptor(), captureGroup, xref.getName(), xref.getType(), newRef.getName(), newRef.getType()); continue match; } tempMappings.add(xref); tempMappings.add(newRef); } for (int i = 0; i + 1 < tempMappings.size(); i += 2) { tempClassMap.addMap(tempMappings.get(i), tempMappings.get(i + 1)); } if (deobfMethod != null) { String deobfName = classMod.getDeobfClass(); tempClassMap.addClassMap(deobfName, ClassMap.filenameToClassName(filename)); tempClassMap.addMethodMap( deobfName, deobfMethod.getName(), methodInfo.getName(), methodInfo.getDescriptor()); if (deobfTypes != null && obfTypes != null) { for (int i = 0; i < deobfTypes.size(); i++) { String desc = ClassMap.descriptorToClassName(deobfTypes.get(i)); String obf = ClassMap.descriptorToClassName(obfTypes.get(i)); if (!obf.equals(desc)) { tempClassMap.addClassMap(desc, obf); } } } } afterMatch(classFile, methodInfo); classMod.methodInfo = null; return true; } } catch (BadBytecode e) { Logger.log(e); } } classMod.methodInfo = null; return false; }
protected CodeIterator(CodeAttribute ca) { codeAttr = ca; bytecode = ca.getCode(); begin(); }
/** Add a method to a class that simply delegates to the parent implementation of the method */ public static void addDelegatingMethod(ClassFile file, MethodData mData) throws BadBytecode, DuplicateMemberException { MethodInfo m = new MethodInfo(file.getConstPool(), mData.getMethodName(), mData.getDescriptor()); m.setAccessFlags(mData.getAccessFlags()); Bytecode code = new Bytecode(file.getConstPool()); String[] params = DescriptorUtils.descriptorStringToParameterArray(mData.getDescriptor()); code.add(Opcode.ALOAD_0); // push this int count = 1; // zero is the this pointer int maxLocals = 1; for (String p : params) { // int char short boolean byte if (p.equals("I") || p.equals("C") || p.equals("S") || p.equals("Z") || p.equals("B")) { // push integer 0 code.addIload(count); maxLocals++; } // long else if (p.equals("J")) { code.addLload(count); maxLocals += 2; count++; } // double else if (p.equals("D")) { code.addDload(count); maxLocals += 2; count++; } // float else if (p.equals("F")) { code.addFload(count); maxLocals++; } // arrays and reference types else { code.addAload(count); maxLocals++; } count++; } code.addInvokespecial(file.getSuperclass(), mData.getMethodName(), mData.getDescriptor()); String p = DescriptorUtils.getReturnTypeInJvmFormat(mData.getDescriptor()); // int char short boolean byte if (p.equals("I") || p.equals("C") || p.equals("S") || p.equals("Z") || p.equals("B")) { code.add(Opcode.IRETURN); } // long else if (p.equals("J")) { code.add(Opcode.LRETURN); } // double else if (p.equals("D")) { code.add(Opcode.DRETURN); } // float else if (p.equals("F")) { code.add(Opcode.FRETURN); } // void else if (p.equals("V")) { code.add(Opcode.RETURN); } // arrays and reference types else { code.add(Opcode.ARETURN); } CodeAttribute ca = code.toCodeAttribute(); ca.computeMaxStack(); ca.setMaxLocals(maxLocals); m.setCodeAttribute(ca); file.addMethod(m); }
/** * Copies and inserts the entries in the given exception table at the beginning of the exception * table in the code attribute edited by this object. * * @param offset the value added to the code positions included in the entries. */ public void insert(ExceptionTable et, int offset) { codeAttr.getExceptionTable().add(0, et, offset); }
/** * Copies and appends the entries in the given exception table at the end of the exception table * in the code attribute edited by this object. * * @param offset the value added to the code positions included in the entries. */ public void append(ExceptionTable et, int offset) { ExceptionTable table = codeAttr.getExceptionTable(); table.add(table.size(), et, offset); }