/** * Creates a public setter method. The setter method assigns the value of the first parameter to * the specified field in the class to which this method is added. The created method is not * static even if the field is static. You may not change it to be static by <code>setModifiers() * </code> in <code>CtBehavior</code>. * * @param methodName the name of the setter * @param field the field accessed. */ public static CtMethod setter(String methodName, CtField field) throws CannotCompileException { FieldInfo finfo = field.getFieldInfo2(); String fieldType = finfo.getDescriptor(); String desc = "(" + fieldType + ")V"; ConstPool cp = finfo.getConstPool(); MethodInfo minfo = new MethodInfo(cp, methodName, desc); minfo.setAccessFlags(AccessFlag.PUBLIC); Bytecode code = new Bytecode(cp, 3, 3); try { String fieldName = finfo.getName(); if ((finfo.getAccessFlags() & AccessFlag.STATIC) == 0) { code.addAload(0); code.addLoad(1, field.getType()); code.addPutfield(Bytecode.THIS, fieldName, fieldType); } else { code.addLoad(1, field.getType()); code.addPutstatic(Bytecode.THIS, fieldName, fieldType); } code.addReturn(null); } catch (NotFoundException e) { throw new CannotCompileException(e); } minfo.setCodeAttribute(code.toCodeAttribute()); return new CtMethod(minfo, field.getDeclaringClass()); }
@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); } } }
private CodeCoverage(ClassLoader classLoader, MethodExclusion exclusion, String... classes) throws Exception { for (String clazz : classes) { Map<Tuple, Set<CodeLine>> map = new TreeMap<>(); report.put(clazz, map); List<Tuple> mds = new ArrayList<>(); descriptors.put(clazz, mds); InputStream is = classLoader.getResourceAsStream(clazz.replace(".", "/") + ".class"); ClassFile classFile = getClassFile(clazz, is); List<MethodInfo> methods = classFile.getMethods(); for (MethodInfo m : methods) { if (exclusion.exclude(classFile, m) == false) { String descriptor = m.getDescriptor(); Tuple tuple = new Tuple(m.getName(), descriptor); map.put(tuple, new TreeSet<CodeLine>()); mds.add(tuple); } } if (isAccessFlagSet(classFile.getAccessFlags(), AccessFlag.ANNOTATION)) { boolean hasAttributes = methods.size() > 0; annotations.put(clazz, hasAttributes); if (hasAttributes == false) { map.put(TYPE_USAGE, new TreeSet<CodeLine>()); mds.add(TYPE_USAGE); } } else { fillSupers(classFile); } } }
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); } }
private static CtMethod delegator0(CtMethod delegate, CtClass declaring) throws CannotCompileException, NotFoundException { MethodInfo deleInfo = delegate.getMethodInfo2(); String methodName = deleInfo.getName(); String desc = deleInfo.getDescriptor(); ConstPool cp = declaring.getClassFile2().getConstPool(); MethodInfo minfo = new MethodInfo(cp, methodName, desc); minfo.setAccessFlags(deleInfo.getAccessFlags()); ExceptionsAttribute eattr = deleInfo.getExceptionsAttribute(); if (eattr != null) minfo.setExceptionsAttribute((ExceptionsAttribute) eattr.copy(cp, null)); Bytecode code = new Bytecode(cp, 0, 0); boolean isStatic = Modifier.isStatic(delegate.getModifiers()); CtClass deleClass = delegate.getDeclaringClass(); CtClass[] params = delegate.getParameterTypes(); int s; if (isStatic) { s = code.addLoadParameters(params, 0); code.addInvokestatic(deleClass, methodName, desc); } else { code.addLoad(0, deleClass); s = code.addLoadParameters(params, 1); code.addInvokespecial(deleClass, methodName, desc); } code.addReturn(delegate.getReturnType()); code.setMaxLocals(++s); code.setMaxStack(s < 2 ? 2 : s); // for a 2-word return value minfo.setCodeAttribute(code.toCodeAttribute()); return new CtMethod(minfo, declaring); }
protected void enhanceAttributesAccess( CtClass managedCtClass, IdentityHashMap<String, PersistentAttributeAccessMethods> attributeDescriptorMap) { final ConstPool constPool = managedCtClass.getClassFile().getConstPool(); for (Object oMethod : managedCtClass.getClassFile().getMethods()) { final MethodInfo methodInfo = (MethodInfo) oMethod; final String methodName = methodInfo.getName(); // skip methods added by enhancement and abstract methods (methods without any code) if (methodName.startsWith("$$_hibernate_") || methodInfo.getCodeAttribute() == null) { continue; } try { final CodeIterator itr = methodInfo.getCodeAttribute().iterator(); while (itr.hasNext()) { final int index = itr.next(); final int op = itr.byteAt(index); if (op != Opcode.PUTFIELD && op != Opcode.GETFIELD) { continue; } final String fieldName = constPool.getFieldrefName(itr.u16bitAt(index + 1)); final PersistentAttributeAccessMethods attributeMethods = attributeDescriptorMap.get(fieldName); // its not a field we have enhanced for interception, so skip it if (attributeMethods == null) { continue; } // System.out.printf( "Transforming access to field [%s] from method [%s]%n", fieldName, // methodName ); log.debugf("Transforming access to field [%s] from method [%s]", fieldName, methodName); if (op == Opcode.GETFIELD) { final int methodIndex = MethodWriter.addMethod(constPool, attributeMethods.getReader()); itr.writeByte(Opcode.INVOKESPECIAL, index); itr.write16bit(methodIndex, index + 1); } else { final int methodIndex = MethodWriter.addMethod(constPool, attributeMethods.getWriter()); itr.writeByte(Opcode.INVOKESPECIAL, index); itr.write16bit(methodIndex, index + 1); } } methodInfo.getCodeAttribute().setAttribute(MapMaker.make(classPool, methodInfo)); } catch (BadBytecode bb) { final String msg = String.format( "Unable to perform field access transformation in method [%s]", methodName); throw new EnhancementException(msg, bb); } } }
/** * Client proxies are equal to other client proxies for the same bean. * * <p>The corresponding java code: <code> * return other instanceof MyProxyClassType.class * </code> */ @Override protected MethodInfo generateEqualsMethod(ClassFile proxyClassType) { MethodInfo method = new MethodInfo(proxyClassType.getConstPool(), "equals", "(Ljava/lang/Object;)Z"); method.setAccessFlags(AccessFlag.PUBLIC); Bytecode b = new Bytecode(proxyClassType.getConstPool()); b.addAload(1); b.addInstanceof(proxyClassType.getName()); b.add(Opcode.IRETURN); b.setMaxLocals(2); b.setMaxStack(1); method.setCodeAttribute(b.toCodeAttribute()); return method; }
/** Client proxies use the following hashCode: <code>MyProxyName.class.hashCode()</code> */ @Override protected MethodInfo generateHashCodeMethod(ClassFile proxyClassType) { MethodInfo method = new MethodInfo(proxyClassType.getConstPool(), "hashCode", "()I"); method.setAccessFlags(AccessFlag.PUBLIC); Bytecode b = new Bytecode(proxyClassType.getConstPool()); // MyProxyName.class.hashCode() int classLocation = proxyClassType.getConstPool().addClassInfo(proxyClassType.getName()); b.addLdc(classLocation); // now we have the class object on top of the stack b.addInvokevirtual("java.lang.Object", "hashCode", "()I"); // now we have the hashCode b.add(Opcode.IRETURN); b.setMaxLocals(1); b.setMaxStack(1); method.setCodeAttribute(b.toCodeAttribute()); return method; }
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)); }
/** * add a bogus constructor call to a bytecode sequence so a constructor can pass bytecode * validation * * @param bytecode */ public static boolean addBogusConstructorCall(ClassFile file, Bytecode code) { MethodInfo constructorToCall = null; for (Object meth : file.getMethods()) { MethodInfo m = (MethodInfo) meth; if (m.getName().equals("<init>")) { constructorToCall = m; break; } } if (constructorToCall == null) { return false; } // push this onto the stack code.add(Bytecode.ALOAD_0); String[] params = DescriptorUtils.descriptorStringToParameterArray(constructorToCall.getDescriptor()); 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.add(Opcode.ICONST_0); } // long else if (p.equals("J")) { code.add(Opcode.LCONST_0); } // double else if (p.equals("D")) { code.add(Opcode.DCONST_0); } // float else if (p.equals("F")) { code.add(Opcode.FCONST_0); } // arrays and reference types else { code.add(Opcode.ACONST_NULL); } } // all our args should be pushed onto the stack, call the constructor code.addInvokespecial(file.getName(), "<init>", constructorToCall.getDescriptor()); code.add(Opcode.RETURN); return true; }
/** 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)); } }
public BaseClassData(ClassFile file, ClassLoader loader, boolean replaceable) { className = file.getName(); this.replaceable = replaceable; internalName = Descriptor.toJvmName(file.getName()); this.loader = loader; superClassName = file.getSuperclass(); boolean finalMethod = false; Set<MethodData> meths = new HashSet<MethodData>(); for (Object o : file.getMethods()) { String methodClassName = className; MethodInfo m = (MethodInfo) o; MemberType type = MemberType.NORMAL; if ((m.getDescriptor().equals(Constants.ADDED_METHOD_DESCRIPTOR) && m.getName().equals(Constants.ADDED_METHOD_NAME)) || (m.getDescriptor().equals(Constants.ADDED_STATIC_METHOD_DESCRIPTOR) && m.getName().equals(Constants.ADDED_STATIC_METHOD_NAME)) || (m.getDescriptor().equals(Constants.ADDED_CONSTRUCTOR_DESCRIPTOR))) { type = MemberType.ADDED_SYSTEM; } else if (m.getAttribute(Constants.FINAL_METHOD_ATTRIBUTE) != null) { finalMethod = true; } MethodData md = new MethodData( m.getName(), m.getDescriptor(), methodClassName, type, m.getAccessFlags(), finalMethod); meths.add(md); } this.methods = Collections.unmodifiableSet(meths); Set<FieldData> fieldData = new HashSet<FieldData>(); for (Object o : file.getFields()) { FieldInfo m = (FieldInfo) o; MemberType mt = MemberType.NORMAL; fieldData.add(new FieldData(m, mt, className, m.getAccessFlags())); } this.fields = Collections.unmodifiableSet(fieldData); }
protected void enhanceAttributesAccess(Map<String, CtField> fieldsMap, CtClass managedCtClass) throws Exception { final ConstPool constPool = managedCtClass.getClassFile().getConstPool(); final ClassPool classPool = managedCtClass.getClassPool(); for (Object oMethod : managedCtClass.getClassFile().getMethods()) { final MethodInfo methodInfo = (MethodInfo) oMethod; final String methodName = methodInfo.getName(); // skip methods added by enhancement, and abstract methods (methods without any code) if (methodName.startsWith(DROOLS_PREFIX) || methodInfo.getCodeAttribute() == null) { continue; } try { final CodeIterator itr = methodInfo.getCodeAttribute().iterator(); while (itr.hasNext()) { final int index = itr.next(); final int op = itr.byteAt(index); if (op != Opcode.PUTFIELD && op != Opcode.GETFIELD) { continue; } final String fieldName = constPool.getFieldrefName(itr.u16bitAt(index + 1)); CtField ctField = fieldsMap.get(fieldName); if (ctField == null) { continue; } // if we are in constructors, only need to intercept assignment statement for Reactive // Collection/List/... (regardless they may be final) if (methodInfo.isConstructor() && !(isCtFieldACollection(ctField))) { continue; } if (op == Opcode.PUTFIELD) { // addMethod is a safe add, if constant already present it return the existing value // without adding. final int methodIndex = addMethod(constPool, writeMethods.get(fieldName)); itr.writeByte(Opcode.INVOKEVIRTUAL, index); itr.write16bit(methodIndex, index + 1); } } methodInfo.getCodeAttribute().setAttribute(MapMaker.make(classPool, methodInfo)); } catch (BadBytecode bb) { final String msg = String.format( "Unable to perform field access transformation in method [%s]", methodName); throw new Exception(msg, bb); } } }
protected void checkClassFile(ClassFile file) throws Exception { Map<Integer, Triple> calls = new HashMap<>(); ConstPool pool = file.getConstPool(); for (int i = 1; i < pool.getSize(); ++i) { // we have a method call BytecodeUtils.Ref ref = BytecodeUtils.getRef(pool, i); String className = ref.getClassName(pool, i); if (className != null) { String methodName = ref.getName(pool, i); String methodDesc = ref.getDesc(pool, i); fillCalls(i, className, methodName, methodDesc, calls); } } if (calls.isEmpty() && annotations.isEmpty()) { return; } String className = file.getName(); AnnotationsAttribute faa = (AnnotationsAttribute) file.getAttribute(AnnotationsAttribute.visibleTag); checkAnnotations(className, TYPE_USAGE.getMethodName(), faa, -1); List<MethodInfo> methods = file.getMethods(); for (MethodInfo m : methods) { try { // ignore abstract methods if (m.getCodeAttribute() == null) { continue; } AnnotationsAttribute maa = (AnnotationsAttribute) m.getAttribute(AnnotationsAttribute.visibleTag); boolean annotationsChecked = false; int firstLine = -1; CodeIterator it = m.getCodeAttribute().iterator(); while (it.hasNext()) { // loop through the bytecode final int index = it.next(); final int line = m.getLineNumber(index); if (annotationsChecked == false) { annotationsChecked = true; firstLine = line; checkAnnotations( className, m.getName(), maa, line - 2); // -2 to get the line above the method } int op = it.byteAt(index); // if the bytecode is a method invocation if (op == CodeIterator.INVOKEVIRTUAL || op == CodeIterator.INVOKESTATIC || op == CodeIterator.INVOKEINTERFACE || op == CodeIterator.INVOKESPECIAL) { int val = it.s16bitAt(index + 1); Triple triple = calls.get(val); if (triple != null) { Map<Tuple, Set<CodeLine>> map = report.get(triple.className); Set<CodeLine> set = map.get(triple.tuple); CodeLine cl = new CodeLine(className, m.getName(), line); set.add(cl.modify()); // check for .jsp, etc } } } if (BaseMethodExclusion.isBridge(m) == false) { SignatureAttribute.MethodSignature signature = SignatureAttribute.toMethodSignature(m.getDescriptor()); handleMethodSignature(className, m.getName(), firstLine - 1, signature.getReturnType()); handleMethodSignature( className, m.getName(), firstLine - 1, signature.getParameterTypes()); handleMethodSignature( className, m.getName(), firstLine - 1, signature.getExceptionTypes()); } ParameterAnnotationsAttribute paa = (ParameterAnnotationsAttribute) m.getAttribute(ParameterAnnotationsAttribute.visibleTag); if (paa != null) { Annotation[][] paas = paa.getAnnotations(); if (paas != null) { for (Annotation[] params : paas) { for (Annotation a : params) { for (Map.Entry<String, Boolean> entry : annotations.entrySet()) { if (entry.getKey().equals(a.getTypeName())) { checkAnnotation( className, m.getName(), firstLine - 1, entry.getValue(), entry.getKey(), a); } } } } } } m.getCodeAttribute().computeMaxStack(); } catch (Exception e) { e.printStackTrace(); } } }
@Override public boolean transform( ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, ClassFile file) throws IllegalClassFormatException, BadBytecode { /** * Hack up the proxy factory so it stores the proxy ClassFile. We need this to regenerate * proxies. */ if (file.getName().equals("org.jboss.weld.bean.proxy.ProxyFactory")) { for (final MethodInfo method : (List<MethodInfo>) file.getMethods()) { if (method.getName().equals("createProxyClass")) { final MethodInvokationManipulator methodInvokationManipulator = new MethodInvokationManipulator(); methodInvokationManipulator.replaceVirtualMethodInvokationWithStatic( ClassLoader.class.getName(), WeldProxyClassLoadingDelegate.class.getName(), "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", "(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;", loader); methodInvokationManipulator.replaceVirtualMethodInvokationWithStatic( "org.jboss.weld.util.bytecode.ClassFileUtils", WeldProxyClassLoadingDelegate.class.getName(), "toClass", "(Ljavassist/bytecode/ClassFile;Ljava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;", "(Ljavassist/bytecode/ClassFile;Ljava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;", loader); HashSet<MethodInfo> modifiedMethods = new HashSet<MethodInfo>(); methodInvokationManipulator.transformClass(file, loader, true, modifiedMethods); for (MethodInfo m : modifiedMethods) { m.rebuildStackMap(ClassPool.getDefault()); } return true; } else if (method.getName().equals("<init>")) { Integer beanArgument = null; int count = 0; for (final String paramType : DescriptorUtils.descriptorStringToParameterArray(method.getDescriptor())) { if (paramType.equals("javax/enterprise/inject/spi/Bean")) { beanArgument = count; break; } else if (paramType.equals("D") || paramType.equals("J")) { count += 2; } else { count++; } } if (beanArgument == null) { log.error( "Constructor org.jboss.weld.bean.proxy.ProxyFactory.<init>" + method.getDescriptor() + " does not have a bean parameter, proxies produced by this factory will not be reloadable"); continue; } // similar to other tracked instances // but we need a strong ref Bytecode code = new Bytecode(file.getConstPool()); code.addAload(0); code.addAload(beanArgument); code.addInvokestatic( WeldClassChangeAware.class.getName(), "addProxyFactory", "(Lorg/jboss/weld/bean/proxy/ProxyFactory;)V"); CodeIterator it = method.getCodeAttribute().iterator(); it.skipConstructor(); it.insert(code.get()); } } } return false; }
/** 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); }
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; }
/** * Replace access to fields of entities (for example, entity.field) with a call to the enhanced * getter / setter (in this example, entity.$$_hibernate_read_field()). It's assumed that the * target entity is enhanced as well. * * @param managedCtClass Class to enhance */ public void enhanceFieldAccess(CtClass managedCtClass) { final ConstPool constPool = managedCtClass.getClassFile().getConstPool(); for (Object oMethod : managedCtClass.getClassFile().getMethods()) { final MethodInfo methodInfo = (MethodInfo) oMethod; final String methodName = methodInfo.getName(); // skip methods added by enhancement and abstract methods (methods without any code) if (methodName.startsWith("$$_hibernate_") || methodInfo.getCodeAttribute() == null) { continue; } try { final CodeIterator itr = methodInfo.getCodeAttribute().iterator(); while (itr.hasNext()) { int index = itr.next(); int op = itr.byteAt(index); if (op != Opcode.PUTFIELD && op != Opcode.GETFIELD) { continue; } String fieldName = constPool.getFieldrefName(itr.u16bitAt(index + 1)); String fieldClassName = constPool.getClassInfo(constPool.getFieldrefClass(itr.u16bitAt(index + 1))); CtClass targetCtClass = this.classPool.getCtClass(fieldClassName); if (!enhancementContext.isEntityClass(targetCtClass) && !enhancementContext.isCompositeClass(targetCtClass)) { continue; } if (targetCtClass == managedCtClass || !enhancementContext.isPersistentField(targetCtClass.getField(fieldName)) || "this$0".equals(fieldName)) { continue; } log.debugf("Transforming access to field [%s] from method [%s]", fieldName, methodName); if (op == Opcode.GETFIELD) { int fieldReaderMethodIndex = constPool.addMethodrefInfo( constPool.addClassInfo(fieldClassName), EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName, "()" + constPool.getFieldrefType(itr.u16bitAt(index + 1))); itr.writeByte(Opcode.INVOKEVIRTUAL, index); itr.write16bit(fieldReaderMethodIndex, index + 1); } else { int fieldWriterMethodIndex = constPool.addMethodrefInfo( constPool.addClassInfo(fieldClassName), EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName, "(" + constPool.getFieldrefType(itr.u16bitAt(index + 1)) + ")V"); itr.writeByte(Opcode.INVOKEVIRTUAL, index); itr.write16bit(fieldWriterMethodIndex, index + 1); } } methodInfo.getCodeAttribute().setAttribute(MapMaker.make(classPool, methodInfo)); } catch (BadBytecode bb) { final String msg = String.format( "Unable to perform field access transformation in method [%s]", methodName); throw new EnhancementException(msg, bb); } catch (NotFoundException nfe) { final String msg = String.format( "Unable to perform field access transformation in method [%s]", methodName); throw new EnhancementException(msg, nfe); } } }
public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass) { Set<Integer> methodCallLocations = new HashSet<Integer>(); Integer newCallLocation = null; Integer methodReflectionLocation = null; Integer fakeCallRequiredLocation = null; // first we need to scan the constant pool looking for // CONSTANT_method_info_ref structures ConstPool pool = file.getConstPool(); for (int i = 1; i < pool.getSize(); ++i) { // we have a method call if (pool.getTag(i) == ConstPool.CONST_Methodref) { String className = pool.getMethodrefClassName(i); String methodName = pool.getMethodrefName(i); if (className.equals(Method.class.getName())) { if (methodName.equals("invoke")) { // store the location in the const pool of the method ref methodCallLocations.add(i); // we have found a method call // if we have not already stored a reference to our new // method in the const pool if (newCallLocation == null) { methodReflectionLocation = pool.addClassInfo("org.fakereplace.reflection.MethodReflection"); int nt = pool.addNameAndTypeInfo("fakeCallRequired", "(Ljava/lang/reflect/Method;)Z"); fakeCallRequiredLocation = pool.addMethodrefInfo(methodReflectionLocation, nt); newCallLocation = pool.addNameAndTypeInfo(METHOD_NAME, REPLACED_METHOD_DESCRIPTOR); } } } } } // this means we found an instance of the call, now we have to iterate // through the methods and replace instances of the call if (newCallLocation != null) { List<MethodInfo> methods = file.getMethods(); for (MethodInfo m : methods) { try { // ignore abstract methods if (m.getCodeAttribute() == null) { continue; } CodeIterator it = m.getCodeAttribute().iterator(); while (it.hasNext()) { // loop through the bytecode int index = it.next(); int op = it.byteAt(index); // if the bytecode is a method invocation if (op == CodeIterator.INVOKEVIRTUAL) { int val = it.s16bitAt(index + 1); // if the method call is one of the methods we are // replacing if (methodCallLocations.contains(val)) { Bytecode b = new Bytecode(file.getConstPool()); // our stack looks like Method, instance,params // we need Method, instance, params , Method b.add(Opcode.DUP_X2); b.add(Opcode.POP); b.add(Opcode.DUP_X2); b.add(Opcode.POP); b.add(Opcode.DUP_X2); b.addInvokestatic( methodReflectionLocation, "fakeCallRequired", "(Ljava/lang/reflect/Method;)Z"); b.add(Opcode.IFEQ); JumpMarker performRealCall = JumpUtils.addJumpInstruction(b); // now perform the fake call b.addInvokestatic(methodReflectionLocation, "invoke", REPLACED_METHOD_DESCRIPTOR); b.add(Opcode.GOTO); JumpMarker finish = JumpUtils.addJumpInstruction(b); performRealCall.mark(); b.addInvokevirtual(Method.class.getName(), METHOD_NAME, METHOD_DESCRIPTOR); finish.mark(); it.writeByte(CodeIterator.NOP, index); it.writeByte(CodeIterator.NOP, index + 1); it.writeByte(CodeIterator.NOP, index + 2); it.insert(b.get()); } } } m.getCodeAttribute().computeMaxStack(); } catch (Exception e) { log.error("Bad byte code transforming " + file.getName()); e.printStackTrace(); } } return true; } else { return false; } }
private URL computeResourceURL(Method method) throws NotFoundException, URISyntaxException { List<ResourceFileEntry> filesSimpleNames = new ArrayList<ResourceFileEntry>(); boolean computeExtensions = false; CtMethod m = ctClass.getMethod(method.getName(), getDescriptor(method)); MethodInfo minfo = m.getMethodInfo2(); AnnotationsAttribute attr = (AnnotationsAttribute) minfo.getAttribute(AnnotationsAttribute.invisibleTag); if (attr != null) { Annotation an = attr.getAnnotation(Source.class.getName()); if (an != null) { MemberValue[] mvArray = ((ArrayMemberValue) an.getMemberValue("value")).getValue(); if (mvArray != null) { for (MemberValue mv : mvArray) { StringMemberValue smv = (StringMemberValue) mv; filesSimpleNames.add(new ResourceFileEntry(smv.getValue(), m)); } } } } if (filesSimpleNames.isEmpty()) { // no @Source annotation detected filesSimpleNames.add(new ResourceFileEntry(method.getName(), m)); computeExtensions = true; } List<URL> existingFiles = new ArrayList<URL>(); for (ResourceFileEntry resourceEntry : filesSimpleNames) { String resourceName = resourceEntry.resourceName; CtClass declaringClass = resourceEntry.resourceMethod.getDeclaringClass(); String baseDir = declaringClass.getPackageName().replaceAll("\\.", "/") + "/"; String fileName = (resourceName.startsWith(baseDir)) ? resourceName : baseDir + resourceName; if (computeExtensions) { String[] extensions = getResourceDefaultExtensions(method); for (String extension : extensions) { String possibleFile = fileName + extension; URL url = GwtPatcher.class.getClassLoader().getResource(possibleFile); if (url != null) { existingFiles.add(url); } } } else { URL url = GwtPatcher.class.getClassLoader().getResource(fileName); if (url != null) { existingFiles.add(url); } } } if (existingFiles.isEmpty()) { throw new RuntimeException( "No resource file found for method " + ctClass.getSimpleName() + "." + method.getName() + "()"); } else if (existingFiles.size() > 1) { throw new RuntimeException( "Too many resource files found for method " + ctClass.getSimpleName() + "." + method.getName() + "()"); } return existingFiles.get(0); }