Skip to content

Commit

Permalink
[shadowloader] Fix for lombok v1.16.0 no longer working in eclipses t…
Browse files Browse the repository at this point in the history
…hat use -target 1.4 style class files (such as eclipse indigo). It’s a doozy.
  • Loading branch information
rzwitserloot committed Feb 8, 2015
1 parent fd12196 commit 65de469
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 67 deletions.
18 changes: 18 additions & 0 deletions build.xml
Expand Up @@ -173,13 +173,31 @@ the common tasks and can be called on to run the main aspects of all the sub-scr
<fileset dir="build/lombok-utils" />
</copy>

<mkdir dir="build/transformedSources" />
<copy todir="build/transformedSources">
<fileset dir="src/eclipseAgent">
<include name="**/*Transplants.java" />
</fileset>
<filterchain>
<lineContainsRegExp negate="true">
<regexp pattern="^\s*@SuppressWarnings.*$" />
</lineContainsRegExp>
</filterchain>
</copy>

<ivy:compile destdir="build/lombok" source="1.4" target="1.4" includeantruntime="false">
<compilerarg value="-Xbootclasspath/p:build/stubs${path.separator}lib/openJDK6Environment/rt-openjdk6.jar" />
<src path="build/transformedSources" />
</ivy:compile>

<ivy:compile destdir="build/lombok" source="1.5" target="1.5" includeantruntime="false">
<compilerarg value="-Xbootclasspath/p:build/stubs${path.separator}lib/openJDK6Environment/rt-openjdk6.jar" />
<src path="src/launch" />
<src path="src/core" />
<src path="src/installer" />
<src path="src/eclipseAgent" />
<exclude name="lombok/javac/**" />
<exclude name="**/*Transplants.java" />
<classpath location="build/lombok" />
<classpath refid="build.path" />
</ivy:compile>
Expand Down
1 change: 1 addition & 0 deletions doc/changelog.markdown
Expand Up @@ -7,6 +7,7 @@ Lombok Changelog
* BUGFIX: The ant `delombok` task was broken starting with v1.16.0. Note that the task def class has been changed; taskdef `lombok.delombok.ant.Tasks$Delombok` instead of the old `lombok.delombok.ant.DelombokTask`. [Issue #775](https://code.google.com/p/projectlombok/issues/detail?id=775).
* BUGFIX: `val` in javac would occasionally fail if used inside inner classes. This is (probably) fixed. [Issue #694](https://code.google.com/p/projectlombok/issues/detail?id=694).
* BUGFIX: Starting with v1.16.0, lombok would fail to execute as an executable jar if it was in a path was spaced in it. [Issue #777](https://code.google.com/p/projectlombok/issues/detail?id=777).
* BUGFIX: v1.16.0 did not work in old eclipse versions (such as eclipse indigo).

### v1.16.0 "Candid Duck" (January 26th, 2015)
* BUGFIX: `@ExtensionMethod` was broken in Eclipse using java 8. [Issue #742](https://code.google.com/p/projectlombok/issues/detail?id=742), [Issue #747](https://code.google.com/p/projectlombok/issues/detail?id=747)
Expand Down
90 changes: 24 additions & 66 deletions src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java
@@ -1,13 +1,26 @@
/*
* Copyright (C) 2015 The Project Lombok Authors.
*
* 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 lombok.eclipse.agent;

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

import lombok.patcher.ClassRootFinder;
import lombok.patcher.Hook;
import lombok.patcher.MethodTarget;
Expand All @@ -16,62 +29,7 @@
import lombok.patcher.scripts.ScriptBuilder;

public class EclipseLoaderPatcher {
public static boolean overrideLoadDecide(ClassLoader original, String name, boolean resolve) {
return name.startsWith("lombok.");
}

public static Class<?> overrideLoadResult(ClassLoader original, String name, boolean resolve) throws ClassNotFoundException {
try {
Field shadowLoaderField = original.getClass().getField("lombok$shadowLoader");
ClassLoader shadowLoader = (ClassLoader) shadowLoaderField.get(original);
if (shadowLoader == null) {
String jarLoc = (String) original.getClass().getField("lombok$location").get(null);
JarFile jf = new JarFile(jarLoc);
InputStream in = null;
try {
ZipEntry entry = jf.getEntry("lombok/launch/ShadowClassLoader.class");
in = jf.getInputStream(entry);
byte[] bytes = new byte[65536];
int len = 0;
while (true) {
int r = in.read(bytes, len, bytes.length - len);
if (r == -1) break;
len += r;
if (len == bytes.length) throw new IllegalStateException("lombok.launch.ShadowClassLoader too large.");
}
in.close();
Class<?> shadowClassLoaderClass; {
Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);
shadowClassLoaderClass = (Class<?>) defineClassMethod.invoke(original, "lombok.launch.ShadowClassLoader", bytes, 0, len);
}
Constructor<?> constructor = shadowClassLoaderClass.getDeclaredConstructor(ClassLoader.class, String.class, String.class, String[].class);
constructor.setAccessible(true);
shadowLoader = (ClassLoader) constructor.newInstance(original, "lombok", jarLoc, new String[] {"lombok."});
shadowLoaderField.set(original, shadowLoader);
} finally {
if (in != null) in.close();
jf.close();
}
}

if (resolve) {
Method m = shadowLoader.getClass().getDeclaredMethod("loadClass", String.class, boolean.class);
m.setAccessible(true);
return (Class<?>) m.invoke(shadowLoader, name, true);
} else {
return shadowLoader.loadClass(name);
}
} catch (Exception ex) {
Throwable t = ex;
if (t instanceof InvocationTargetException) t = t.getCause();
if (t instanceof RuntimeException) throw (RuntimeException) t;
if (t instanceof Error) throw (Error) t;
throw new RuntimeException(t);
}
}

private static final String SELF_NAME = "lombok.eclipse.agent.EclipseLoaderPatcher";
private static final String TRANSPLANTS_CLASS_NAME = "lombok.eclipse.agent.EclipseLoaderPatcherTransplants";

public static void patchEquinoxLoaders(ScriptManager sm, Class<?> launchingContext) {
sm.addScript(ScriptBuilder.exitEarly()
Expand All @@ -81,8 +39,8 @@ public static void patchEquinoxLoaders(ScriptManager sm, Class<?> launchingConte
"java.lang.Class", "java.lang.String", "boolean"))
.target(new MethodTarget("org.eclipse.osgi.internal.loader.ModuleClassLoader", "loadClass",
"java.lang.Class", "java.lang.String", "boolean"))
.decisionMethod(new Hook(SELF_NAME, "overrideLoadDecide", "boolean", "java.lang.ClassLoader", "java.lang.String", "boolean"))
.valueMethod(new Hook(SELF_NAME, "overrideLoadResult", "java.lang.Class", "java.lang.ClassLoader", "java.lang.String", "boolean"))
.decisionMethod(new Hook(TRANSPLANTS_CLASS_NAME, "overrideLoadDecide", "boolean", "java.lang.ClassLoader", "java.lang.String", "boolean"))
.valueMethod(new Hook(TRANSPLANTS_CLASS_NAME, "overrideLoadResult", "java.lang.Class", "java.lang.ClassLoader", "java.lang.String", "boolean"))
.transplant()
.request(StackRequest.THIS, StackRequest.PARAM1, StackRequest.PARAM2).build());

Expand Down
@@ -0,0 +1,117 @@
/*
* Copyright (C) 2015 The Project Lombok Authors.
*
* 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 lombok.eclipse.agent;

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

/**
* Contains all the code to be transplanted into eclipse.
*
* Do not use:
*
* * Annotations
* * Generics
* * Varargs
* * Auto (un)boxing
* * class literals
*
* The above because this code is compiled with -source 1.4, and is transplanted.
*
* NB: The suppress warnings will be stripped out before compilation.
*/
@SuppressWarnings("all")
public class EclipseLoaderPatcherTransplants {
public static boolean overrideLoadDecide(ClassLoader original, String name, boolean resolve) {
return name.startsWith("lombok.");
}

public static Class overrideLoadResult(ClassLoader original, String name, boolean resolve) throws ClassNotFoundException {
try {
Field shadowLoaderField = original.getClass().getField("lombok$shadowLoader");
ClassLoader shadowLoader = (ClassLoader) shadowLoaderField.get(original);
if (shadowLoader == null) {
String jarLoc = (String) original.getClass().getField("lombok$location").get(null);
JarFile jf = new JarFile(jarLoc);
InputStream in = null;
try {
ZipEntry entry = jf.getEntry("lombok/launch/ShadowClassLoader.class");
in = jf.getInputStream(entry);
byte[] bytes = new byte[65536];
int len = 0;
while (true) {
int r = in.read(bytes, len, bytes.length - len);
if (r == -1) break;
len += r;
if (len == bytes.length) throw new IllegalStateException("lombok.launch.ShadowClassLoader too large.");
}
in.close();
Class classLoaderClass = Class.forName("java.lang.ClassLoader");
Class shadowClassLoaderClass; {
Class[] paramTypes = new Class[4];
paramTypes[0] = "".getClass();
paramTypes[1] = new byte[0].getClass();
paramTypes[2] = Integer.TYPE;
paramTypes[3] = paramTypes[2];
Method defineClassMethod = classLoaderClass.getDeclaredMethod("defineClass", paramTypes);
defineClassMethod.setAccessible(true);
shadowClassLoaderClass = (Class) defineClassMethod.invoke(original, new Object[] {"lombok.launch.ShadowClassLoader", bytes, new Integer(0), new Integer(len)});
}
Class[] paramTypes = new Class[4];
paramTypes[0] = classLoaderClass;
paramTypes[1] = "".getClass();
paramTypes[2] = paramTypes[1];
paramTypes[3] = new String[0].getClass();
Constructor constructor = shadowClassLoaderClass.getDeclaredConstructor(paramTypes);
constructor.setAccessible(true);
shadowLoader = (ClassLoader) constructor.newInstance(new Object[] {original, "lombok", jarLoc, new String[] {"lombok."}});
shadowLoaderField.set(original, shadowLoader);
} finally {
if (in != null) in.close();
jf.close();
}
}

if (resolve) {
Class[] paramTypes = new Class[2];
paramTypes[0] = "".getClass();
paramTypes[1] = Boolean.TYPE;
Method m = shadowLoader.getClass().getDeclaredMethod("loadClass", new Class[] {String.class, boolean.class});
m.setAccessible(true);
return (Class) m.invoke(shadowLoader, new Object[] {name, Boolean.TRUE});
} else {
return shadowLoader.loadClass(name);
}
} catch (Exception ex) {
Throwable t = ex;
if (t instanceof InvocationTargetException) t = t.getCause();
if (t instanceof RuntimeException) throw (RuntimeException) t;
if (t instanceof Error) throw (Error) t;
throw new RuntimeException(t);
}
}
}
1 change: 0 additions & 1 deletion src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
Expand Up @@ -29,7 +29,6 @@
import java.util.List;

import lombok.core.AgentLauncher;

import lombok.patcher.Hook;
import lombok.patcher.MethodTarget;
import lombok.patcher.ScriptManager;
Expand Down

0 comments on commit 65de469

Please sign in to comment.