private Bytecode createAbstractMethodCode(ClassFile file, MethodInformation method) throws NotFoundException { if ((delegateField != null) && (!Modifier.isPrivate(delegateField.getModifiers()))) { // Call the corresponding method directly on the delegate Bytecode b = new Bytecode(file.getConstPool()); int localVariables = MethodUtils.calculateMaxLocals(method.getMethod()); b.setMaxLocals(localVariables); // load the delegate field b.addAload(0); b.addGetfield( file.getName(), delegateField.getName(), DescriptorUtils.classToStringRepresentation(delegateField.getType())); // load the parameters BytecodeUtils.loadParameters(b, method.getDescriptor()); // invoke the delegate method b.addInvokeinterface( delegateField.getType().getName(), method.getName(), method.getDescriptor(), localVariables); // return the value if applicable BytecodeUtils.addReturnInstruction(b, method.getReturnType()); return b; } else { if (!Modifier.isPrivate(method.getMethod().getModifiers())) { // if it is a parameter injection point we need to initalize the // injection point then handle the method with the method handler return createAbstractMethodHandler(file, method); } else { // if the delegate is private we need to use the method handler return createInterceptorBody(file, method); } } }
@Override public void describeTo(StringBuilder stringBuilder) { ClosureUtil.SourceInfo sourceInfo = ClosureUtil.getSourceInfo(invoker.getClosure()); if (sourceInfo == null) { ClassPool pool = ClassPool.getDefault(); try { CtClass ctClass = pool.get(invoker.getClosure().getClass().getName()); CtMethod ctMethod = ctClass.getDeclaredMethod("doCall"); int lineNumber = ctMethod.getMethodInfo().getLineNumber(0); ClassFile classFile = ctClass.getClassFile(); String sourceFile = classFile.getSourceFile(); if (lineNumber != -1 && sourceFile != null) { stringBuilder .append("closure at line ") .append(lineNumber) .append(" of ") .append(sourceFile); } else { stringBuilder.append("closure ").append(invoker.getClosure().getClass().getName()); } } catch (NotFoundException e) { stringBuilder.append(invoker.getClosure().getClass().getName()); } } else { stringBuilder .append("closure at line ") .append(sourceInfo.getLineNumber()) .append(" of ") .append(sourceInfo.getUri()); } }
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); } } }
public void testJIRA256() throws Exception { // CtClass ec = sloader.get("test5.Entity"); CtClass cc = sloader.makeClass("test5.JIRA256"); ClassFile ccFile = cc.getClassFile(); ConstPool constpool = ccFile.getConstPool(); AnnotationsAttribute attr = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag); javassist.bytecode.annotation.Annotation entityAnno = new javassist.bytecode.annotation.Annotation("test5.Entity", constpool); // = new javassist.bytecode.annotation.Annotation(constpool, ec); entityAnno.addMemberValue( "value", new javassist.bytecode.annotation.ArrayMemberValue(constpool)); attr.addAnnotation(entityAnno); ccFile.addAttribute(attr); cc.writeFile(); Object o = make(cc.getName()); assertTrue(o.getClass().getName().equals("test5.JIRA256")); java.lang.annotation.Annotation[] annotations = o.getClass().getDeclaredAnnotations(); assertEquals(1, annotations.length); }
@Override public JElement allocate(File e) { JElement element; if (e.isDirectory()) { // Attempt to construct package element = assignPackage(e); } else { try { ClassFile f = JavassistClassFileHelper.constructClassFileForPath( e.getPath(), new TFileInputStream(e)); if (f.isInterface()) { return JInterface.getJInterface(f, ClasspathResolver.this); } else { return JClass.getJClass(f, ClasspathResolver.this); } } catch (FileNotFoundException e1) { throw new ClasspathAccessException("Couldnt find file", e1); } catch (IOException e1) { throw new ClasspathAccessException("Couldnt access file", e1); } } return element; }
protected void buildClassLevelAnnotations( ClassFile classFile, ClassFile templateClassFile, ConstPool constantPool) throws NotFoundException { List<?> templateAttributes = templateClassFile.getAttributes(); Iterator<?> templateItr = templateAttributes.iterator(); Annotation templateEntityListeners = null; while (templateItr.hasNext()) { Object object = templateItr.next(); if (AnnotationsAttribute.class.isAssignableFrom(object.getClass())) { AnnotationsAttribute attr = (AnnotationsAttribute) object; Annotation[] items = attr.getAnnotations(); for (Annotation annotation : items) { String typeName = annotation.getTypeName(); if (typeName.equals(EntityListeners.class.getName())) { templateEntityListeners = annotation; } } } } if (templateEntityListeners != null) { AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constantPool, AnnotationsAttribute.visibleTag); List<?> attributes = classFile.getAttributes(); Iterator<?> itr = attributes.iterator(); Annotation existingEntityListeners = null; while (itr.hasNext()) { Object object = itr.next(); if (AnnotationsAttribute.class.isAssignableFrom(object.getClass())) { AnnotationsAttribute attr = (AnnotationsAttribute) object; Annotation[] items = attr.getAnnotations(); for (Annotation annotation : items) { String typeName = annotation.getTypeName(); if (typeName.equals(EntityListeners.class.getName())) { logger.debug( "Stripping out previous EntityListeners annotation at the class level - will merge into new EntityListeners"); existingEntityListeners = annotation; continue; } annotationsAttribute.addAnnotation(annotation); } itr.remove(); } } Annotation entityListeners = getEntityListeners(constantPool, existingEntityListeners, templateEntityListeners); annotationsAttribute.addAnnotation(entityListeners); classFile.addAttribute(annotationsAttribute); } }
@Override public byte[] transform( ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { // Lambdas and anonymous methods in Java 8 do not have a class name defined and so no // transformation should be done if (className == null) { return null; } String convertedClassName = className.replace('/', '.'); if (isIgnored(convertedClassName)) { return null; } try { ClassFile classFile = new ClassFile(new DataInputStream(new ByteArrayInputStream(classfileBuffer))); List<?> attributes = classFile.getAttributes(); Iterator<?> itr = attributes.iterator(); while (itr.hasNext()) { Object object = itr.next(); if (AnnotationsAttribute.class.isAssignableFrom(object.getClass())) { boolean containsTypeLevelAnnotation = containsTypeLevelPersistenceAnnotation( ((AnnotationsAttribute) object).getAnnotations()); if (containsTypeLevelAnnotation) { LOG.debug("Marking " + convertedClassName + " as transformed"); transformedEntityClassNames.add(convertedClassName); } else { LOG.debug( "Marking " + convertedClassName + " as picked up by the transformer but not detected as an entity"); transformedNonEntityClassNames.add(convertedClassName); } } } } catch (Exception e) { LOG.error(e); throw new IllegalClassFormatException( "Unable to mark " + convertedClassName + " as transformed."); } // We don't need to transform anything, so we'll return null return null; }
/** * 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; }
@Override protected void addMethodsFromClass(ClassFile proxyClassType) { Method initializerMethod = null; int delegateParameterPosition = -1; if (delegateInjectionPoint instanceof ParameterInjectionPoint<?, ?>) { ParameterInjectionPoint<?, ?> parameterIP = (ParameterInjectionPoint<?, ?>) delegateInjectionPoint; if (parameterIP.getMember() instanceof Method) { initializerMethod = ((Method) parameterIP.getMember()); delegateParameterPosition = parameterIP.getPosition(); } } try { if (delegateParameterPosition >= 0) { addHandlerInitializerMethod(proxyClassType); } Class<?> cls = getBeanType(); while (cls != null) { for (Method method : cls.getDeclaredMethods()) { MethodInformation methodInfo = new RuntimeMethodInformation(method); if (!method.getDeclaringClass().getName().equals("java.lang.Object") || method.getName().equals("toString")) { Bytecode methodBody = null; if ((delegateParameterPosition >= 0) && (initializerMethod.equals(method))) { methodBody = createDelegateInitializerCode( proxyClassType, methodInfo, delegateParameterPosition); } if (Modifier.isAbstract(method.getModifiers())) { methodBody = createAbstractMethodCode(proxyClassType, methodInfo); } if (methodBody != null) { log.trace("Adding method " + method); proxyClassType.addMethod( MethodUtils.makeMethod( methodInfo, method.getExceptionTypes(), methodBody, proxyClassType.getConstPool())); } } } cls = cls.getSuperclass(); } } catch (Exception e) { throw new WeldException(e); } }
private void addHandlerInitializerMethod(ClassFile proxyClassType) throws Exception { StaticMethodInformation methodInfo = new StaticMethodInformation( "_initMH", new Class[] {Object.class}, void.class, proxyClassType.getName(), Modifier.PRIVATE); proxyClassType.addMethod( MethodUtils.makeMethod( methodInfo, new Class[] {}, createMethodHandlerInitializerBody(proxyClassType), proxyClassType.getConstPool())); }
protected void fillSupers(ClassFile clazz) { String name = clazz.getName(); List<String> list = supers.get(name); if (list == null) { list = new ArrayList<>(); supers.put(name, list); } if (clazz.isInterface()) { Collections.addAll(list, clazz.getInterfaces()); } else { String superclass = clazz.getSuperclass(); if (superclass != null) { list.add(superclass); } } }
@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); } } }
/** * calls _initMH on the method handler and then stores the result in the methodHandler field as * then new methodHandler */ private Bytecode createMethodHandlerInitializerBody(ClassFile proxyClassType) { Bytecode b = new Bytecode(proxyClassType.getConstPool(), 1, 2); b.add(Opcode.ALOAD_0); StaticMethodInformation methodInfo = new StaticMethodInformation( "_initMH", new Class[] {Object.class}, void.class, proxyClassType.getName()); invokeMethodHandler(proxyClassType, b, methodInfo, false, DEFAULT_METHOD_RESOLVER); b.addCheckcast("javassist/util/proxy/MethodHandler"); b.addPutfield( proxyClassType.getName(), "methodHandler", DescriptorUtils.classToStringRepresentation(MethodHandler.class)); b.add(Opcode.RETURN); log.trace("Created MH initializer body for decorator proxy: " + getBeanType()); return b; }
/** 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; }
/** * 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; }
private Bytecode createAbstractMethodHandler(ClassFile file, MethodInformation methodInfo) { // this is slightly different to a normal method handler call, as we pass // in a TargetInstanceBytecodeMethodResolver. This resolver uses the // method handler to call getTargetClass to get the correct class type to // resolve the method with, and then resolves this method Bytecode b = new Bytecode(file.getConstPool()); invokeMethodHandler(file, b, methodInfo, true, TargetInstanceBytecodeMethodResolver.INSTANCE); return b; }
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); }
public void testInnerClassAttributeRemove() throws Exception { CtClass cc = sloader.get("test5.InnerClassRemove"); ClassFile cf = cc.getClassFile(); InnerClassesAttribute ica = (InnerClassesAttribute) cf.getAttribute(InnerClassesAttribute.tag); String second = ica.innerClass(1); String secondName = ica.innerName(1); String third = ica.innerClass(2); String thirdName = ica.innerName(2); assertEquals(3, ica.remove(3)); assertEquals(2, ica.remove(0)); assertEquals(second, ica.innerClass(0)); assertEquals(secondName, ica.innerName(0)); assertEquals(third, ica.innerClass(1)); assertEquals(thirdName, ica.innerName(1)); assertEquals(1, ica.remove(1)); assertEquals(second, ica.innerClass(0)); assertEquals(secondName, ica.innerName(0)); cc.writeFile(); Object obj = make(cc.getName()); assertEquals(1, invoke(obj, "run")); }
public String apply(StaticFieldDescriptor from) { // this is quite simple. First we create a proxy String proxyName = ProxyDefinitionStore.getProxyName(); ClassFile proxy = new ClassFile(false, proxyName, "java.lang.Object"); ClassDataStore.instance().registerProxyName(from.getClazz(), proxyName); proxy.setAccessFlags(AccessFlag.PUBLIC); FieldInfo newField = new FieldInfo(proxy.getConstPool(), from.getName(), from.getDescriptor()); newField.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); if (from.getSigniture() != null) { SignatureAttribute sig = new SignatureAttribute(proxy.getConstPool(), from.getSigniture()); newField.addAttribute(sig); } try { proxy.addField(newField); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bytes); try { proxy.write(dos); } catch (IOException e) { throw new RuntimeException(e); } ProxyDefinitionStore.saveProxyDefinition( from.getClazz().getClassLoader(), proxyName, bytes.toByteArray()); } catch (DuplicateMemberException e) { // can't happen } return proxyName; }
/** * When creates the delegate initializer code when the delegate is injected into a method. * * <p>super initializer method is called first, and then _initMH is called * * @param file * @param intializerMethodInfo * @param delegateParameterPosition * @return */ private Bytecode createDelegateInitializerCode( ClassFile file, MethodInformation intializerMethodInfo, int delegateParameterPosition) { Bytecode b = new Bytecode(file.getConstPool()); // we need to push all the pareters on the stack to call the corresponding // superclass arguments b.addAload(0); // load this int localVariables = 1; int actualDelegateParamterPosition = 0; for (int i = 0; i < intializerMethodInfo.getMethod().getParameterTypes().length; ++i) { if (i == delegateParameterPosition) { // figure out the actual position of the delegate in the local // variables actualDelegateParamterPosition = localVariables; } Class<?> type = intializerMethodInfo.getMethod().getParameterTypes()[i]; BytecodeUtils.addLoadInstruction( b, DescriptorUtils.classToStringRepresentation(type), localVariables); if (type == long.class || type == double.class) { localVariables = localVariables + 2; } else { localVariables++; } } b.addInvokespecial( file.getSuperclass(), intializerMethodInfo.getName(), intializerMethodInfo.getDescriptor()); // if this method returns a value it is now sitting on top of the stack // we will leave it there are return it later // now we need to call _initMH b.addAload(0); // load this b.addAload(actualDelegateParamterPosition); // load the delegate b.addInvokevirtual(file.getName(), "_initMH", "(Ljava/lang/Object;)V"); // return the object from the top of the stack that we got from calling // the superclass method earlier BytecodeUtils.addReturnInstruction(b, intializerMethodInfo.getReturnType()); b.setMaxLocals(localVariables); return b; }
protected void writeOutChanges() { log.info("writing injection changes back [" + classFileLocation.getAbsolutePath() + "]"); long timeStamp = classFileLocation.lastModified(); ClassFile classFile = ctClass.getClassFile(); classFile.compact(); try { DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(classFileLocation))); try { classFile.write(out); out.flush(); if (!classFileLocation.setLastModified(System.currentTimeMillis())) { log.info("Unable to manually update class file timestamp"); } } finally { out.close(); classFileLocation.setLastModified(timeStamp); } } catch (IOException e) { throw new InjectionException("Unable to write out modified class file", e); } }
@Override public void handleChanges(Collection<WatchServiceFileSystemWatcher.FileChangeEvent> changes) { try { List<AddedClass> addedClasses = new ArrayList<>(); List<ClassDefinition> changedClasses = new ArrayList<>(); for (WatchServiceFileSystemWatcher.FileChangeEvent change : changes) { if (change.getType() == WatchServiceFileSystemWatcher.FileChangeEvent.Type.ADDED) { try (FileInputStream in = new FileInputStream(change.getFile())) { byte[] bytes = FileReader.readFileBytes(in); ClassFile file = new ClassFile(new DataInputStream(new ByteArrayInputStream(bytes))); addedClasses.add(new AddedClass(file.getName(), bytes, classLoader)); } } else if (change.getType() == WatchServiceFileSystemWatcher.FileChangeEvent.Type.MODIFIED) { String hash = hashes.get(change.getFile().getCanonicalPath()); if (hash == null) { // class is not loaded yet continue; } try (FileInputStream in = new FileInputStream(change.getFile())) { byte[] bytes = FileReader.readFileBytes(in); if (!hash.equals(MD5.md5(bytes))) { ClassFile file = new ClassFile(new DataInputStream(new ByteArrayInputStream(bytes))); changedClasses.add( new ClassDefinition(classLoader.loadClass(file.getName()), bytes)); } } } } Agent.redefine( changedClasses.toArray(new ClassDefinition[changedClasses.size()]), addedClasses.toArray(new AddedClass[addedClasses.size()])); } catch (Exception e) { e.printStackTrace(); } }
private JPackage assignPackage(File e) { JPackage retVal; final File[] classes = e.listFiles(CLASSFILE_FILTER); for (int i = 0; i < classes.length; i++) { ClassFile classFile = FileUtils.doWithFile( classes[i], new FileInputStreamOperation<ClassFile>() { @Override public ClassFile execute(String path, InputStream fileInputStream) { try { return JavassistClassFileHelper.constructClassFile(path, fileInputStream); } catch (IOException e) { throw new FileAccessException( "Cannot access class file: " + e.getMessage(), e); } } }); try { Package pkg = Class.forName(classFile.getName()).getPackage(); retVal = JPackage.getJPackage(pkg, ClasspathResolver.this); } catch (ClassNotFoundException ex) { throw new FileAccessException("Cannot access class file: " + ex.getMessage(), ex); } if (retVal != null) { return retVal; } } return null; }
@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); }
@Override public byte[] transform( ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { // Be careful with Apache library usage in this class (e.g. ArrayUtils). Usage will likely cause // a ClassCircularityError // under JRebel. Favor not including outside libraries and unnecessary classes. CtClass clazz = null; try { boolean mySkipOverlaps = skipOverlaps; boolean myRenameMethodOverlaps = renameMethodOverlaps; String convertedClassName = className.replace('/', '.'); ClassPool classPool = null; String xformKey = convertedClassName; String[] xformVals = null; Boolean[] xformSkipOverlaps = null; Boolean[] xformRenameMethodOverlaps = null; if (!xformTemplates.isEmpty()) { if (xformTemplates.containsKey(xformKey)) { xformVals = xformTemplates.get(xformKey).split(","); classPool = ClassPool.getDefault(); clazz = classPool.makeClass(new ByteArrayInputStream(classfileBuffer), false); } } else { if (annotationTransformedClasses.contains(convertedClassName)) { logger.warn( convertedClassName + " has already been transformed by a previous instance of DirectCopyTransfomer. " + "Skipping this annotation based transformation. Generally, annotation-based transformation is handled " + "by bean id blAnnotationDirectCopyClassTransformer with template tokens being added to " + "blDirectCopyTransformTokenMap via EarlyStageMergeBeanPostProcessor."); } boolean isValidPattern = true; List<DirectCopyIgnorePattern> matchedPatterns = new ArrayList<DirectCopyIgnorePattern>(); for (DirectCopyIgnorePattern pattern : ignorePatterns) { boolean isPatternMatch = false; for (String patternString : pattern.getPatterns()) { isPatternMatch = convertedClassName.matches(patternString); if (isPatternMatch) { break; } } if (isPatternMatch) { matchedPatterns.add(pattern); } isValidPattern = !(isPatternMatch && pattern.getTemplateTokenPatterns() == null); if (!isValidPattern) { return null; } } if (isValidPattern) { classPool = ClassPool.getDefault(); clazz = classPool.makeClass(new ByteArrayInputStream(classfileBuffer), false); List<?> attributes = clazz.getClassFile().getAttributes(); Iterator<?> itr = attributes.iterator(); List<String> templates = new ArrayList<String>(); List<Boolean> skips = new ArrayList<Boolean>(); List<Boolean> renames = new ArrayList<Boolean>(); check: { while (itr.hasNext()) { Object object = itr.next(); if (AnnotationsAttribute.class.isAssignableFrom(object.getClass())) { AnnotationsAttribute attr = (AnnotationsAttribute) object; Annotation[] items = attr.getAnnotations(); for (Annotation annotation : items) { String typeName = annotation.getTypeName(); if (typeName.equals(DirectCopyTransform.class.getName())) { ArrayMemberValue arrayMember = (ArrayMemberValue) annotation.getMemberValue("value"); for (MemberValue arrayMemberValue : arrayMember.getValue()) { AnnotationMemberValue member = (AnnotationMemberValue) arrayMemberValue; Annotation memberAnnot = member.getValue(); ArrayMemberValue annot = (ArrayMemberValue) memberAnnot.getMemberValue("templateTokens"); for (MemberValue memberValue : annot.getValue()) { String val = ((StringMemberValue) memberValue).getValue(); if (val != null && templateTokens.containsKey(val)) { templateCheck: { for (DirectCopyIgnorePattern matchedPattern : matchedPatterns) { for (String ignoreToken : matchedPattern.getTemplateTokenPatterns()) { if (val.matches(ignoreToken)) { break templateCheck; } } } templates.add(templateTokens.get(val)); } } } BooleanMemberValue skipAnnot = (BooleanMemberValue) memberAnnot.getMemberValue("skipOverlaps"); if (skipAnnot != null) { skips.add(skipAnnot.getValue()); } else { skips.add(mySkipOverlaps); } BooleanMemberValue renameAnnot = (BooleanMemberValue) memberAnnot.getMemberValue("renameMethodOverlaps"); if (renameAnnot != null) { renames.add(renameAnnot.getValue()); } else { renames.add(myRenameMethodOverlaps); } } xformVals = templates.toArray(new String[templates.size()]); xformSkipOverlaps = skips.toArray(new Boolean[skips.size()]); xformRenameMethodOverlaps = renames.toArray(new Boolean[renames.size()]); break check; } } } } } } } if (xformVals != null && xformVals.length > 0) { logger.lifecycle( LifeCycleEvent.START, String.format( "Transform - Copying into [%s] from [%s]", xformKey, StringUtils.join(xformVals, ","))); // Load the destination class and defrost it so it is eligible for modifications clazz.defrost(); int index = 0; for (String xformVal : xformVals) { // Load the source class String trimmed = xformVal.trim(); classPool.appendClassPath(new LoaderClassPath(Class.forName(trimmed).getClassLoader())); CtClass template = classPool.get(trimmed); // Add in extra interfaces CtClass[] interfacesToCopy = template.getInterfaces(); for (CtClass i : interfacesToCopy) { checkInterfaces: { CtClass[] myInterfaces = clazz.getInterfaces(); for (CtClass myInterface : myInterfaces) { if (myInterface.getName().equals(i.getName())) { if (xformSkipOverlaps[index]) { break checkInterfaces; } else { throw new RuntimeException( "Duplicate interface detected " + myInterface.getName()); } } } logger.debug(String.format("Adding interface [%s]", i.getName())); clazz.addInterface(i); } } // copy over any EntityListeners ClassFile classFile = clazz.getClassFile(); ClassFile templateFile = template.getClassFile(); ConstPool constantPool = classFile.getConstPool(); buildClassLevelAnnotations(classFile, templateFile, constantPool); // Copy over all declared fields from the template class // Note that we do not copy over fields with the @NonCopiedField annotation CtField[] fieldsToCopy = template.getDeclaredFields(); for (CtField field : fieldsToCopy) { if (field.hasAnnotation(NonCopied.class)) { logger.debug(String.format("Not adding field [%s]", field.getName())); } else { try { CtField ctField = clazz.getDeclaredField(field.getName()); String originalSignature = ctField.getSignature(); String mySignature = field.getSignature(); if (!originalSignature.equals(mySignature)) { throw new IllegalArgumentException( "Field with name (" + field.getName() + ") and signature " + "(" + field.getSignature() + ") is targeted for weaving into (" + clazz.getName() + "). " + "An incompatible field of the same name and signature of (" + ctField.getSignature() + ") " + "already exists. The field in the target class should be updated to a different name, " + "or made to have a matching type."); } if (xformSkipOverlaps[index]) { logger.debug(String.format("Skipping overlapped field [%s]", field.getName())); continue; } } catch (NotFoundException e) { // do nothing -- field does not exist } logger.debug(String.format("Adding field [%s]", field.getName())); CtField copiedField = new CtField(field, clazz); boolean defaultConstructorFound = false; String implClass = getImplementationType(field.getType().getName()); // Look through all of the constructors in the implClass to see // if there is one that takes zero parameters try { CtConstructor[] implConstructors = classPool.get(implClass).getConstructors(); if (implConstructors != null) { for (CtConstructor cons : implConstructors) { if (cons.getParameterTypes().length == 0) { defaultConstructorFound = true; break; } } } } catch (NotFoundException e) { // Do nothing -- if we don't find this implementation, it's probably because it's // an array. In this case, we will not initialize the field. } if (defaultConstructorFound) { clazz.addField(copiedField, "new " + implClass + "()"); } else { clazz.addField(copiedField); } } } // Copy over all declared methods from the template class CtMethod[] methodsToCopy = template.getDeclaredMethods(); for (CtMethod method : methodsToCopy) { if (method.hasAnnotation(NonCopied.class)) { logger.debug(String.format("Not adding method [%s]", method.getName())); } else { try { CtClass[] paramTypes = method.getParameterTypes(); CtMethod originalMethod = clazz.getDeclaredMethod(method.getName(), paramTypes); if (xformSkipOverlaps[index]) { logger.debug( String.format( "Skipping overlapped method [%s]", methodDescription(originalMethod))); continue; } if (transformedMethods.contains(methodDescription(originalMethod))) { throw new RuntimeException( "Method already replaced " + methodDescription(originalMethod)); } else { logger.debug( String.format("Marking as replaced [%s]", methodDescription(originalMethod))); transformedMethods.add(methodDescription(originalMethod)); } logger.debug(String.format("Removing method [%s]", method.getName())); if (xformRenameMethodOverlaps[index]) { originalMethod.setName(renameMethodPrefix + method.getName()); } else { clazz.removeMethod(originalMethod); } } catch (NotFoundException e) { // Do nothing -- we don't need to remove a method because it doesn't exist } logger.debug(String.format("Adding method [%s]", method.getName())); CtMethod copiedMethod = new CtMethod(method, clazz, null); clazz.addMethod(copiedMethod); } } index++; } if (xformTemplates.isEmpty()) { annotationTransformedClasses.add(convertedClassName); } logger.lifecycle( LifeCycleEvent.END, String.format( "Transform - Copying into [%s] from [%s]", xformKey, StringUtils.join(xformVals, ","))); return clazz.toBytecode(); } } catch (ClassCircularityError error) { error.printStackTrace(); throw error; } catch (Exception e) { throw new RuntimeException("Unable to transform class", e); } finally { if (clazz != null) { clazz.detach(); } } return null; }
/** * Calls methodHandler.invoke with a null method parameter in order to get the underlying * instance. The invocation is then forwarded to this instance with generated bytecode. */ protected Bytecode createForwardingMethodBody(ClassFile file, MethodInformation methodInfo) throws NotFoundException { Method method = methodInfo.getMethod(); // we can only use bytecode based invocation for some methods // at the moment we restrict it solely to public methods with public // return and parameter types boolean bytecodeInvocationAllowed = Modifier.isPublic(method.getModifiers()) && Modifier.isPublic(method.getReturnType().getModifiers()); for (Class<?> paramType : method.getParameterTypes()) { if (!Modifier.isPublic(paramType.getModifiers())) { bytecodeInvocationAllowed = false; break; } } if (!bytecodeInvocationAllowed) { return createInterceptorBody(file, methodInfo); } Bytecode b = new Bytecode(file.getConstPool()); int localCount = MethodUtils.calculateMaxLocals(method) + 1; // create a new interceptor invocation context whenever we invoke a method on a client proxy // we use a try-catch block in order to make sure that endInterceptorContext() is invoked // regardless whether // the method has succeeded or not int start = b.currentPc(); b.addInvokestatic( "org.jboss.weld.bean.proxy.InterceptionDecorationContext", "startInterceptorContext", "()V"); b.add(Opcode.ALOAD_0); b.addGetfield( file.getName(), "methodHandler", DescriptorUtils.classToStringRepresentation(MethodHandler.class)); // pass null arguments to methodHandler.invoke b.add(Opcode.ALOAD_0); b.add(Opcode.ACONST_NULL); b.add(Opcode.ACONST_NULL); b.add(Opcode.ACONST_NULL); // now we have all our arguments on the stack // lets invoke the method b.addInvokeinterface( MethodHandler.class.getName(), "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", 5); b.addCheckcast(methodInfo.getDeclaringClass()); // now we should have the target bean instance on top of the stack // we need to dup it so we still have it to compare to the return value b.add(Opcode.DUP); // lets create the method invocation String methodDescriptor = methodInfo.getDescriptor(); BytecodeUtils.loadParameters(b, methodDescriptor); if (method.getDeclaringClass().isInterface()) { b.addInvokeinterface( methodInfo.getDeclaringClass(), methodInfo.getName(), methodDescriptor, method.getParameterTypes().length + 1); } else { b.addInvokevirtual(methodInfo.getDeclaringClass(), methodInfo.getName(), methodDescriptor); } // end the interceptor context, everything was fine b.addInvokestatic( "org.jboss.weld.bean.proxy.InterceptionDecorationContext", "endInterceptorContext", "()V"); // jump over the catch block b.addOpcode(Opcode.GOTO); JumpMarker gotoEnd = JumpUtils.addJumpInstruction(b); // create catch block b.addExceptionHandler(start, b.currentPc(), b.currentPc(), 0); b.addInvokestatic( "org.jboss.weld.bean.proxy.InterceptionDecorationContext", "endInterceptorContext", "()V"); b.add(Opcode.ATHROW); // update the correct address to jump over the catch block gotoEnd.mark(); // if this method returns a primitive we just return if (method.getReturnType().isPrimitive()) { BytecodeUtils.addReturnInstruction(b, methodInfo.getReturnType()); } else { // otherwise we have to check that the proxy is not returning 'this; // now we need to check if the proxy has return 'this' and if so return // an // instance of the proxy. // currently we have result, beanInstance on the stack. b.add(Opcode.DUP_X1); // now we have result, beanInstance, result // we need to compare result and beanInstance // first we need to build up the inner conditional that just returns // the // result b.add(Opcode.IF_ACMPEQ); JumpMarker returnInstruction = JumpUtils.addJumpInstruction(b); BytecodeUtils.addReturnInstruction(b, methodInfo.getReturnType()); returnInstruction.mark(); // now add the case where the proxy returns 'this'; b.add(Opcode.ALOAD_0); b.addCheckcast(methodInfo.getMethod().getReturnType().getName()); BytecodeUtils.addReturnInstruction(b, methodInfo.getReturnType()); } if (b.getMaxLocals() < localCount) { b.setMaxLocals(localCount); } return b; }
@Override protected AnnotationsAttribute getAnnotationsAttribute() { return (AnnotationsAttribute) classFile.getAttribute(VISIBLE); }
public byte[] transform( ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if (infos.isEmpty()) { return null; } String convertedClassName = className.replace('/', '.'); SingleTableInheritanceInfo key = new SingleTableInheritanceInfo(); key.setClassName(convertedClassName); int pos = infos.indexOf(key); if (pos >= 0) { try { if (LOG.isDebugEnabled()) { LOG.debug("Converting " + convertedClassName + " to a SingleTable inheritance strategy."); } SingleTableInheritanceInfo myInfo = infos.get(pos); ClassFile classFile = new ClassFile(new DataInputStream(new ByteArrayInputStream(classfileBuffer))); ConstPool constantPool = classFile.getConstPool(); AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constantPool, AnnotationsAttribute.visibleTag); List<?> attributes = classFile.getAttributes(); Iterator<?> itr = attributes.iterator(); while (itr.hasNext()) { Object object = itr.next(); if (AnnotationsAttribute.class.isAssignableFrom(object.getClass())) { AnnotationsAttribute attr = (AnnotationsAttribute) object; Annotation[] items = attr.getAnnotations(); for (Annotation annotation : items) { String typeName = annotation.getTypeName(); if (!typeName.equals(Inheritance.class.getName()) && ((myInfo.getDiscriminatorName() == null && !typeName.equals(Table.class.getName())) || myInfo.getDiscriminatorName() != null)) { annotationsAttribute.addAnnotation(annotation); } } itr.remove(); } } Annotation inheritance = new Annotation(Inheritance.class.getName(), constantPool); ClassPool pool = ClassPool.getDefault(); pool.importPackage("javax.persistence"); pool.importPackage("java.lang"); EnumMemberValue strategy = (EnumMemberValue) Annotation.createMemberValue(constantPool, pool.makeClass("InheritanceType")); strategy.setType(InheritanceType.class.getName()); strategy.setValue(InheritanceType.SINGLE_TABLE.name()); inheritance.addMemberValue("strategy", strategy); annotationsAttribute.addAnnotation(inheritance); if (myInfo.getDiscriminatorName() != null) { Annotation discriminator = new Annotation(DiscriminatorColumn.class.getName(), constantPool); StringMemberValue name = new StringMemberValue(constantPool); name.setValue(myInfo.getDiscriminatorName()); discriminator.addMemberValue("name", name); EnumMemberValue discriminatorType = (EnumMemberValue) Annotation.createMemberValue(constantPool, pool.makeClass("DiscriminatorType")); discriminatorType.setType(DiscriminatorType.class.getName()); discriminatorType.setValue(myInfo.getDiscriminatorType().name()); discriminator.addMemberValue("discriminatorType", discriminatorType); IntegerMemberValue length = new IntegerMemberValue(constantPool); length.setValue(myInfo.getDiscriminatorLength()); discriminator.addMemberValue("length", length); annotationsAttribute.addAnnotation(discriminator); } classFile.addAttribute(annotationsAttribute); ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream os = new DataOutputStream(bos); classFile.write(os); os.close(); return bos.toByteArray(); } catch (Exception ex) { ex.printStackTrace(); throw new IllegalClassFormatException( "Unable to convert " + convertedClassName + " to a SingleTable inheritance strategy: " + ex.getMessage()); } } else { return null; } }
@Override public byte[] transform( final ClassLoader loader, final String className, final Class<?> classBeingRedefined, final ProtectionDomain protectionDomain, final byte[] classfileBuffer) throws IllegalClassFormatException { if (integrationClassTriggers.containsKey(className)) { integrationClassloader.put(loader, new Object()); // we need to load the class in another thread // otherwise it will not go through the javaagent ThreadLoader.loadAsync( integrationClassTriggers.get(className).getClassChangeAwareName(), loader, true); } boolean changed = false; if (UnmodifiedFileIndex.isClassUnmodified(className)) { // TODO: enable this return null; } final ClassFile file; try { file = new ClassFile(new DataInputStream(new ByteArrayInputStream(classfileBuffer))); for (final FakereplaceTransformer transformer : transformers) { if (transformer.transform(loader, className, classBeingRedefined, protectionDomain, file)) { changed = true; } } if (!changed) { UnmodifiedFileIndex.markClassUnmodified(className); return null; } else { ByteArrayOutputStream bs = new ByteArrayOutputStream(); file.write(new DataOutputStream(bs)); // dump the class for debugging purposes if (DefaultEnvironment.getEnvironment().getDumpDirectory() != null && classBeingRedefined != null) { FileOutputStream s = new FileOutputStream( DefaultEnvironment.getEnvironment().getDumpDirectory() + '/' + file.getName() + ".class"); DataOutputStream dos = new DataOutputStream(s); file.write(dos); s.close(); } return bs.toByteArray(); } } catch (IOException e) { e.printStackTrace(); throw new IllegalClassFormatException(e.getMessage()); } catch (Throwable e) { e.printStackTrace(); throw new RuntimeException(e); } }