/* * The MIT License (MIT) * * Copyright (c) 2016. Diorite (by Bartłomiej Mazur (aka GotoFinal)) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package org.diorite.inject.impl.controller; import java.lang.instrument.ClassFileTransformer; import java.util.function.Predicate; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.MethodNode; import org.diorite.inject.Injection; import org.diorite.inject.impl.controller.TransformerInjectTracker.PlaceholderType; import org.diorite.inject.impl.data.InjectValueData; import org.diorite.inject.impl.utils.AsmUtils; import org.diorite.inject.impl.utils.Constants; import net.bytebuddy.ClassFileVersion; import net.bytebuddy.asm.AsmVisitorWrapper; import net.bytebuddy.description.field.FieldDescription; import net.bytebuddy.description.field.FieldList; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.method.MethodDescription.InDefinedShape; import net.bytebuddy.description.method.MethodList; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription.Generic; import net.bytebuddy.implementation.Implementation.Context; import net.bytebuddy.pool.TypePool; final class TransformerInvokerGenerator implements ClassFileTransformer, Opcodes { public static final String INJECTOR_CLASS = Constants.INJECTOR.getInternalName(); public static final String INJECTOR_FIELD = "injectField"; public static final String INJECTOR_FIELD_DESC = "(Ljava/lang/Object;IIZ)Ljava/lang/Object;"; public static final String INJECTOR_METHOD = "injectMethod"; public static final String INJECTOR_METHOD_DESC = "(Ljava/lang/Object;IIIZ)Ljava/lang/Object;"; public static final String GENERATED_PREFIX = Injection.class.getPackage().getName() + ".generated.invokers"; public static final Object[] STACK = {}; public static final int HASHCODE_MULTI = 127; private static final class AnnotationImplementationVisitor implements AsmVisitorWrapper { private final TypeDescription clazz; private AnnotationImplementationVisitor(TypeDescription clazz) { this.clazz = clazz; } @Override public int mergeWriter(int flags) { return flags | ClassWriter.COMPUTE_MAXS; } @Override public int mergeReader(int flags) { return flags; } @Override public ClassVisitor wrap(TypeDescription typeDescription, ClassVisitor cv, Context context, TypePool typePool, FieldList<FieldDescription.InDefinedShape> fieldList, MethodList<?> methodList, int i, int i1) { // public void visit(int version, int modifiers, String name, String signature, String superName, String[] interfaces) { cv.visit(ClassFileVersion.JAVA_V9.getMinorMajorVersion(), typeDescription.getModifiers(), typeDescription.getInternalName(), null, typeDescription.getSuperClass().asErasure().getInternalName(), typeDescription.getInterfaces().asErasures().toInternalNames()); TypeDescription clazz = this.clazz; String internalName = clazz.getInternalName(); String descriptor = clazz.getDescriptor(); MethodList<InDefinedShape> declaredMethods = clazz.getDeclaredMethods(); int methodsSize = declaredMethods.size(); String implName = GENERATED_PREFIX + "." + clazz.getName(); String internalImplName = GENERATED_PREFIX.replace('.', '/') + "/" + internalName; String descriptorImplName = "L" + GENERATED_PREFIX.replace('.', '/') + "/" + internalName + ";"; FieldVisitor fv; MethodVisitor mv; AnnotationVisitor av0; cv.visitEnd(); return cv; } } // public static void test(ClassData classData, byte[] bytecode) // { // ClassReader cr = new ClassReader(bytecode); // ClassNode node = new ClassNode(Opcodes.ASM6); // cr.accept(node, 0); // // classData.getMethods() // List<MethodNode> methods = node.methods; // for (MethodNode method : methods) // { // if (method.desc) // } // } public static int generateFieldInjection(ControllerClassData classData, ControllerFieldData<?> fieldData, MethodNode mv, int lineNumber, PlaceholderType placeholderType) { AbstractInsnNode[] result = new AbstractInsnNode[2]; FieldDescription.InDefinedShape member = fieldData.getMember(); TypeDescription fieldType = member.getType().asErasure(); boolean isStatic = member.isStatic(); lineNumber = AsmUtils.printLineNumber(mv, lineNumber); if (isStatic) { mv.visitInsn(ACONST_NULL); } else { mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0); } AsmUtils.storeInt(mv, classData.getIndex()); AsmUtils.storeInt(mv, fieldData.getIndex()); switch (placeholderType) { case INVALID: case UNKNOWN: default: throw new IllegalStateException("Can't generate injection for invalid placeholders."); case NONNULL: mv.visitInsn(ICONST_1); break; case NULLABLE: mv.visitInsn(ICONST_0); break; } mv.visitMethodInsn(INVOKESTATIC, INJECTOR_CLASS, INJECTOR_FIELD, INJECTOR_FIELD_DESC, false); return lineNumber; } public static int printMethods(MethodNode mv, String clazz, Iterable<String> methods, Predicate<String> isStatic, int lineNumber) { for (String method : methods) { lineNumber = printMethod(mv, clazz, method, isStatic.test(method), lineNumber); } return lineNumber; } public static int printMethods(MethodNode mv, String clazz, Iterable<String> methods, boolean isStatic, int lineNumber) { for (String method : methods) { lineNumber = printMethod(mv, clazz, method, isStatic, lineNumber); } return lineNumber; } public static int printMethod(MethodNode mv, String clazz, String method, boolean isStatic, int lineNumber) { lineNumber = AsmUtils.printLineNumber(mv, lineNumber); if (isStatic) { mv.visitMethodInsn(INVOKESTATIC, clazz, method, "()V", false); } else { mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, clazz, method, "()V", false); } return lineNumber; } public static void generateMethodInjection(ControllerClassData classData, ControllerMethodData methodData, MethodNode mv, boolean printMethods, int lineNumber) { MethodDescription.InDefinedShape member = methodData.getMember(); boolean isStatic = member.isStatic(); if (printMethods) { lineNumber = printMethods(mv, classData.getType().getInternalName(), methodData.getBefore(), isStatic, lineNumber); } lineNumber = AsmUtils.printLineNumber(mv, lineNumber); if (! isStatic) { mv.visitVarInsn(ALOAD, 0); } for (InjectValueData<?, Generic> valueData : methodData.getInjectValues()) { if (isStatic) { mv.visitInsn(ACONST_NULL); } else { mv.visitVarInsn(ALOAD, 0); } AsmUtils.storeInt(mv, classData.getIndex()); AsmUtils.storeInt(mv, methodData.getIndex()); AsmUtils.storeInt(mv, valueData.getIndex()); mv.visitInsn(ICONST_0); // skip null checks in methods. lineNumber = AsmUtils.printLineNumber(mv, lineNumber); mv.visitMethodInsn(INVOKESTATIC, INJECTOR_CLASS, INJECTOR_METHOD, INJECTOR_METHOD_DESC, false); TypeDescription paramType = valueData.getType().asErasure(); mv.visitTypeInsn(CHECKCAST, paramType.getInternalName()); // skip cast check? } lineNumber = AsmUtils.printLineNumber(mv, lineNumber); if (isStatic) { mv.visitMethodInsn(INVOKESTATIC, classData.getType().getInternalName(), member.getName(), member.getDescriptor(), false); } else { mv.visitMethodInsn(INVOKESPECIAL, classData.getType().getInternalName(), member.getName(), member.getDescriptor(), false); } if (printMethods) { printMethods(mv, classData.getType().getInternalName(), methodData.getAfter(), isStatic, lineNumber); } } }