aboutsummaryrefslogtreecommitdiff
path: root/src_eclipseagent
diff options
context:
space:
mode:
Diffstat (limited to 'src_eclipseagent')
-rw-r--r--src_eclipseagent/java/lombok/ClassLoaderWorkaround.java104
-rw-r--r--src_eclipseagent/lombok/agent/eclipse/EclipseParserPatcher.java47
-rw-r--r--src_eclipseagent/lombok/agent/eclipse/EclipseParserTransformer.java157
3 files changed, 308 insertions, 0 deletions
diff --git a/src_eclipseagent/java/lombok/ClassLoaderWorkaround.java b/src_eclipseagent/java/lombok/ClassLoaderWorkaround.java
new file mode 100644
index 00000000..0238b592
--- /dev/null
+++ b/src_eclipseagent/java/lombok/ClassLoaderWorkaround.java
@@ -0,0 +1,104 @@
+package java.lombok;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+
+/**
+ * Allows you to inject the lombok classes into any classloader, even if that classloader does not
+ * know how to find the lombok classes.
+ *
+ * Example: Injecting lombok's Eclipse Parser patching code into eclipse's OSGi BundleLoader.
+ *
+ * @author rzwitserloot
+ */
+public class ClassLoaderWorkaround {
+ private static boolean initialized;
+ private static Method transformCompilationUnitDeclaration;
+ private static Method transformMethodDeclaration;
+ private static Method transformConstructorDeclaration;
+ private static Method transformInitializer;
+
+ public static void transformCompilationUnitDeclaration(Object parser, Object cud) throws Exception {
+ initialize(cud);
+ transformCompilationUnitDeclaration.invoke(null, parser, cud);
+ }
+
+ public static void transformMethodDeclaration(Object parser, Object methodDeclaration) throws Exception {
+ initialize(methodDeclaration);
+ transformMethodDeclaration.invoke(null, parser, methodDeclaration);
+ }
+
+ public static void transformConstructorDeclaration(Object parser, Object constructorDeclaration) throws Exception {
+ initialize(constructorDeclaration);
+ transformConstructorDeclaration.invoke(null, parser, constructorDeclaration);
+ }
+
+ public static void transformInitializer(Object parser, Object initializer) throws Exception {
+ initialize(initializer);
+ transformInitializer.invoke(null, parser, initializer);
+ }
+
+ private static void initialize(Object cud) throws ClassNotFoundException {
+ if ( initialized ) {
+ if ( transformInitializer == null ) throw new ClassNotFoundException("lombok.eclipse.TransformEclipseAST");
+ return;
+ }
+
+ final ClassLoader parent = cud.getClass().getClassLoader();
+ ClassLoader loader = new ClassLoader() {
+ @Override public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if ( name.startsWith("lombok.") ) {
+ InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(name.replace(".", "/") + ".class");
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ byte[] b = new byte[65536];
+ try {
+ while ( true ) {
+ int r = in.read(b);
+ if ( r == -1 ) break;
+ if ( r > 0 ) out.write(b, 0, r);
+ }
+
+ in.close();
+ byte[] data = out.toByteArray();
+ Class<?> result = defineClass(name, data, 0, data.length);
+ if ( resolve ) resolveClass(result);
+ return result;
+ } catch ( IOException e ) {
+ throw new ClassNotFoundException();
+ }
+ } else {
+ try {
+ Class<?> result = ClassLoader.getSystemClassLoader().loadClass(name);
+ if ( resolve ) resolveClass(result);
+ return result;
+ } catch ( ClassNotFoundException e ) {
+ Class<?> result = parent.loadClass(name);
+ if ( resolve ) resolveClass(result);
+ return result;
+ }
+ }
+ }
+ };
+
+ try {
+ Class<?> c = loader.loadClass("lombok.eclipse.TransformEclipseAST");
+ for ( Method m : c.getMethods() ) {
+ if ( m.getName().equals("transform") ) {
+ if ( m.getParameterTypes().length >= 2 ) {
+ Class<?> astType = m.getParameterTypes()[1];
+ String astName = astType.getName();
+ astName = astName.substring(astName.lastIndexOf('.') + 1);
+ if ( astName.equals("CompilationUnitDeclaration") ) transformCompilationUnitDeclaration = m;
+ else if ( astName.equals("MethodDeclaration") ) transformMethodDeclaration = m;
+ else if ( astName.equals("ConstructorDeclaration") ) transformConstructorDeclaration = m;
+ else if ( astName.equals("Initializer") ) transformInitializer = m;
+ }
+ }
+ }
+ } catch ( ClassNotFoundException ignore ) {}
+ initialized = true;
+ }
+}
diff --git a/src_eclipseagent/lombok/agent/eclipse/EclipseParserPatcher.java b/src_eclipseagent/lombok/agent/eclipse/EclipseParserPatcher.java
new file mode 100644
index 00000000..abe26683
--- /dev/null
+++ b/src_eclipseagent/lombok/agent/eclipse/EclipseParserPatcher.java
@@ -0,0 +1,47 @@
+package lombok.agent.eclipse;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.lang.instrument.Instrumentation;
+import java.lang.instrument.UnmodifiableClassException;
+import java.security.ProtectionDomain;
+
+public class EclipseParserPatcher {
+ private static class Patcher implements ClassFileTransformer {
+ @Override public byte[] transform(ClassLoader loader, String className,
+ Class<?> classBeingRedefined,
+ ProtectionDomain protectionDomain, byte[] classfileBuffer)
+ throws IllegalClassFormatException {
+
+ if ( !ECLIPSE_PARSER_CLASS_NAME.equals(className) ) return null;
+ EclipseParserTransformer transformer = new EclipseParserTransformer(classfileBuffer);
+ return transformer.transform();
+ }
+ }
+
+ static final String ECLIPSE_PARSER_CLASS_NAME = "org/eclipse/jdt/internal/compiler/parser/Parser";
+
+ public static void agentmain(String agentArgs, Instrumentation instrumentation) {
+ registerPatcher(instrumentation, true);
+ }
+
+ public static void premain(String agentArgs, Instrumentation instrumentation) {
+ registerPatcher(instrumentation, false);
+ }
+
+ private static void registerPatcher(Instrumentation instrumentation, boolean transformExisting) {
+ instrumentation.addTransformer(new Patcher(), true);
+
+ if ( transformExisting ) for ( Class<?> c : instrumentation.getAllLoadedClasses() ) {
+ if ( c.getName().equals(ECLIPSE_PARSER_CLASS_NAME) ) {
+ try {
+ instrumentation.retransformClasses(c);
+ } catch ( UnmodifiableClassException ex ) {
+ throw new UnsupportedOperationException(
+ "The eclipse parser class is already loaded and cannot be modified. " +
+ "You'll have to restart eclipse in order to use Lombok in eclipse.");
+ }
+ }
+ }
+ }
+}
diff --git a/src_eclipseagent/lombok/agent/eclipse/EclipseParserTransformer.java b/src_eclipseagent/lombok/agent/eclipse/EclipseParserTransformer.java
new file mode 100644
index 00000000..803d5d66
--- /dev/null
+++ b/src_eclipseagent/lombok/agent/eclipse/EclipseParserTransformer.java
@@ -0,0 +1,157 @@
+package lombok.agent.eclipse;
+
+import java.lang.reflect.Constructor;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+class EclipseParserTransformer {
+ private static final String COMPILER_PKG =
+ "Lorg/eclipse/jdt/internal/compiler/ast/";
+ private static final String TARGET_STATIC_CLASS = "java/lombok/ClassLoaderWorkaround";
+ private static final String TARGET_STATIC_METHOD_DESC = "(Ljava/lang/Object;Ljava/lang/Object;)V";
+
+ private static final Map<String, Class<? extends MethodVisitor>> rewriters;
+
+ static {
+ Map<String, Class<? extends MethodVisitor>> map = new HashMap<String, Class<? extends MethodVisitor>>();
+ map.put(String.format("endParse(I)%sCompilationUnitDeclaration;", COMPILER_PKG), EndParsePatcher.class);
+ map.put(String.format("parse(%1$sMethodDeclaration;%1$sCompilationUnitDeclaration;)V", COMPILER_PKG), ParseMethodPatcher.class);
+ map.put(String.format("parse(%1$sConstructorDeclaration;%1$sCompilationUnitDeclaration;Z)V", COMPILER_PKG), ParseConstructorPatcher.class);
+ map.put(String.format("parse(%1$sInitializer;%1$sTypeDeclaration;%1$sCompilationUnitDeclaration;)V", COMPILER_PKG), ParseInitializerPatcher.class);
+ rewriters = Collections.unmodifiableMap(map);
+ }
+
+ private final byte[] in;
+
+ EclipseParserTransformer(byte[] classfileBuffer) {
+ in = classfileBuffer;
+ }
+
+ byte[] transform() {
+ ClassReader reader = new ClassReader(in);
+ ClassWriter writer = new ClassWriter(reader, 0);
+ ClassAdapter adapter = new ParserPatcherAdapter(writer);
+ reader.accept(adapter, 0);
+ return writer.toByteArray();
+ }
+
+ public static RuntimeException sneakyThrow(Throwable t) {
+ if ( t == null ) throw new NullPointerException("t");
+ EclipseParserTransformer.<RuntimeException>sneakyThrow0(t);
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T {
+ throw (T)t;
+ }
+
+ private static class ParserPatcherAdapter extends ClassAdapter {
+ public ParserPatcherAdapter(ClassVisitor cv) {
+ super(cv);
+ }
+
+ @Override public MethodVisitor visitMethod(int access, String name, String desc,
+ String signature, String[] exceptions) {
+ MethodVisitor writerVisitor = super.visitMethod(access, name, desc, signature, exceptions);
+ Class<? extends MethodVisitor> targetVisitorClass = rewriters.get(name+desc);
+ if ( targetVisitorClass == null ) return writerVisitor;
+
+ try {
+ Constructor<? extends MethodVisitor> c = targetVisitorClass.getDeclaredConstructor(MethodVisitor.class);
+ c.setAccessible(true);
+ return c.newInstance(writerVisitor);
+ } catch ( Exception e ) {
+ //NoSuchMethodException: We know they exist.
+ //InvocationTargetException: No checked exceptions thrown by any compilers.
+ //IllegalAccessException: We called setAccessible.
+ //InstantiationException: None of these classes are abstract.
+ throw sneakyThrow(e);
+ }
+ }
+ }
+
+ private static final int BIT24 = 0x800000;
+
+ static class ParseBlockContainerPatcher extends MethodAdapter {
+ private final String staticMethodName;
+
+ ParseBlockContainerPatcher(MethodVisitor mv, String staticMethodName) {
+ super(mv);
+ this.staticMethodName = staticMethodName;
+ }
+
+ @Override public void visitCode() {
+ //injects: if ( constructorDeclaration.bits & BIT24 > 0 ) return;
+ mv.visitVarInsn(Opcodes.ALOAD, 1);
+ mv.visitFieldInsn(Opcodes.GETFIELD, "org/eclipse/jdt/internal/compiler/ast/ASTNode", "bits", "I");
+ mv.visitLdcInsn(Integer.valueOf(BIT24));
+ mv.visitInsn(Opcodes.IAND);
+ Label l0 = new Label();
+ mv.visitJumpInsn(Opcodes.IFLE, l0);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitLabel(l0);
+ mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ super.visitCode();
+ }
+
+ @Override public void visitInsn(int opcode) {
+ if ( opcode == Opcodes.RETURN ) {
+ //injects: ClassLoaderWorkaround.transformConstructorDeclaration(parser, constructorDeclaration);
+ super.visitVarInsn(Opcodes.ALOAD, 0);
+ super.visitVarInsn(Opcodes.ALOAD, 1);
+ super.visitMethodInsn(Opcodes.INVOKESTATIC, TARGET_STATIC_CLASS,
+ staticMethodName, TARGET_STATIC_METHOD_DESC);
+ }
+ }
+ }
+
+ static class ParseConstructorPatcher extends ParseBlockContainerPatcher {
+ public ParseConstructorPatcher(MethodVisitor mv) {
+ super(mv, "transformConstructorDeclaration");
+ }
+ }
+
+ static class ParseMethodPatcher extends ParseBlockContainerPatcher {
+ public ParseMethodPatcher(MethodVisitor mv) {
+ super(mv, "transformMethodDeclaration");
+ }
+ }
+
+ static class ParseInitializerPatcher extends ParseBlockContainerPatcher {
+ public ParseInitializerPatcher(MethodVisitor mv) {
+ super(mv, "transformInitializer");
+ }
+ }
+
+ static class EndParsePatcher extends MethodAdapter {
+ private static final String TARGET_STATIC_METHOD_NAME = "transformCompilationUnitDeclaration";
+
+ EndParsePatcher(MethodVisitor mv) {
+ super(mv);
+ }
+
+ @Override public void visitInsn(int opcode) {
+ if ( opcode == Opcodes.ARETURN ) {
+ //injects: ClassLoaderWorkaround.transformCUD(parser, compilationUnitDeclaration);
+ super.visitInsn(Opcodes.DUP);
+ super.visitVarInsn(Opcodes.ALOAD, 0);
+ super.visitInsn(Opcodes.SWAP);
+ super.visitMethodInsn(Opcodes.INVOKESTATIC, TARGET_STATIC_CLASS,
+ TARGET_STATIC_METHOD_NAME, TARGET_STATIC_METHOD_DESC);
+ }
+
+ super.visitInsn(opcode);
+ }
+ }
+}