/* 94: */ /* 95: */ protected int match( int c, int pos, CodeIterator iterator, int typedesc, ConstPool cp) /* 96: */ throws BadBytecode /* 97: */ { /* 98:110 */ if (this.newIndex == 0) /* 99: */ { /* 100:111 */ int nt = cp.addNameAndTypeInfo(cp.addUtf8Info(this.newMethodname), typedesc); /* 101: */ /* 102:113 */ int ci = cp.addClassInfo(this.newClassname); /* 103:114 */ if (c == 185) /* 104: */ { /* 105:115 */ this.newIndex = cp.addInterfaceMethodrefInfo(ci, nt); /* 106: */ } /* 107: */ else /* 108: */ { /* 109:117 */ if ((this.newMethodIsPrivate) && (c == 182)) { /* 110:118 */ iterator.writeByte(183, pos); /* 111: */ } /* 112:120 */ this.newIndex = cp.addMethodrefInfo(ci, nt); /* 113: */ } /* 114:123 */ this.constPool = cp; /* 115: */ } /* 116:126 */ iterator.write16bit(this.newIndex, pos + 1); /* 117:127 */ return pos; /* 118: */ }
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); } } }
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); } } }
/* 49: */ /* 50: */ public int transform(CtClass clazz, int pos, CodeIterator iterator, ConstPool cp) /* 51: */ throws BadBytecode /* 52: */ { /* 53: 68 */ int c = iterator.byteAt(pos); /* 54: 69 */ if ((c == 185) || (c == 183) || (c == 184) || (c == 182)) /* 55: */ { /* 56: 71 */ int index = iterator.u16bitAt(pos + 1); /* 57: 72 */ String cname = cp.eqMember(this.methodname, this.methodDescriptor, index); /* 58: 73 */ if ((cname != null) && (matchClass(cname, clazz.getClassPool()))) /* 59: */ { /* 60: 74 */ int ntinfo = cp.getMemberNameAndType(index); /* 61: 75 */ pos = match(c, pos, iterator, cp.getNameAndTypeDescriptor(ntinfo), cp); /* 62: */ } /* 63: */ } /* 64: 80 */ return pos; /* 65: */ }
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; } }
/** * 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 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; }
/** * Add Method to ConstPool. If method was not in the ConstPool will add and return index, * otherwise will return index of already existing entry of constpool */ private static int addMethod(ConstPool cPool, CtMethod method) { // addMethodrefInfo is a safe add, if constant already present it return the existing value // without adding. return cPool.addMethodrefInfo( cPool.getThisClassInfo(), method.getName(), method.getSignature()); }
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(); } } }