aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/lombok/core/Main.java1
-rw-r--r--src/delombok/lombok/delombok/DelombokApp.java16
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java4
-rw-r--r--src/launch/lombok/launch/Agent.java (renamed from src/core/lombok/core/Agent.java)18
-rw-r--r--src/launch/lombok/launch/Main.java52
-rw-r--r--src/launch/lombok/launch/ShadowClassLoader.java225
6 files changed, 301 insertions, 15 deletions
diff --git a/src/core/lombok/core/Main.java b/src/core/lombok/core/Main.java
index d62fe3e4..0856d3b3 100644
--- a/src/core/lombok/core/Main.java
+++ b/src/core/lombok/core/Main.java
@@ -38,6 +38,7 @@ public class Main {
));
public static void main(String[] args) throws IOException {
+ Thread.currentThread().setContextClassLoader(Main.class.getClassLoader());
int err = new Main(SpiLoadUtil.readAllFromIterator(
SpiLoadUtil.findServices(LombokApp.class)), Arrays.asList(args)).go();
System.exit(err);
diff --git a/src/delombok/lombok/delombok/DelombokApp.java b/src/delombok/lombok/delombok/DelombokApp.java
index 276bd7de..aa753fc8 100644
--- a/src/delombok/lombok/delombok/DelombokApp.java
+++ b/src/delombok/lombok/delombok/DelombokApp.java
@@ -88,7 +88,7 @@ public class DelombokApp extends LombokApp {
// Since we only read from it, not closing it should not be a problem.
@SuppressWarnings({"resource", "all"}) final JarFile toolsJarFile = new JarFile(toolsJar);
- ClassLoader loader = new ClassLoader() {
+ ClassLoader loader = new ClassLoader(DelombokApp.class.getClassLoader()) {
private Class<?> loadStreamAsClass(String name, boolean resolve, InputStream in) throws ClassNotFoundException {
try {
try {
@@ -107,16 +107,24 @@ public class DelombokApp extends LombokApp {
} finally {
in.close();
}
- } catch (IOException e2) {
+ } catch (Exception e2) {
throw new ClassNotFoundException(name, e2);
}
}
@Override protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
- String rawName = name.replace(".", "/") + ".class";
+ String rawName, altName; {
+ String binName = name.replace(".", "/");
+ rawName = binName + ".class";
+ altName = binName + ".SCL.lombok";
+ }
JarEntry entry = toolsJarFile.getJarEntry(rawName);
if (entry == null) {
- if (name.startsWith("lombok.")) return loadStreamAsClass(name, resolve, super.getResourceAsStream(rawName));
+ if (name.startsWith("lombok.")) {
+ InputStream res = getParent().getResourceAsStream(rawName);
+ if (res == null) res = getParent().getResourceAsStream(altName);
+ return loadStreamAsClass(name, resolve, res);
+ }
return super.loadClass(name, resolve);
}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
index e14d1367..3fce9626 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
@@ -28,7 +28,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import lombok.core.Agent;
import lombok.patcher.Hook;
import lombok.patcher.MethodTarget;
import lombok.patcher.ScriptManager;
@@ -44,12 +43,11 @@ import lombok.patcher.scripts.ScriptBuilder;
* classes in this package for more information about which classes are transformed and how they are
* transformed.
*/
-public class EclipsePatcher extends Agent {
+public class EclipsePatcher {
// At some point I'd like the agent to be capable of auto-detecting if its on eclipse or on ecj. This class is a sure sign we're not in ecj but in eclipse. -ReinierZ
@SuppressWarnings("unused")
private static final String ECLIPSE_SIGNATURE_CLASS = "org/eclipse/core/runtime/adaptor/EclipseStarter";
- @Override
public void runAgent(String agentArgs, Instrumentation instrumentation, boolean injected) throws Exception {
String[] args = agentArgs == null ? new String[0] : agentArgs.split(":");
boolean forceEcj = false;
diff --git a/src/core/lombok/core/Agent.java b/src/launch/lombok/launch/Agent.java
index 49c03bf3..fe64e1b6 100644
--- a/src/core/lombok/core/Agent.java
+++ b/src/launch/lombok/launch/Agent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Project Lombok Authors.
+ * Copyright (C) 2009-2014 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
@@ -19,11 +19,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package lombok.core;
+package lombok.launch;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
+import java.lang.reflect.InvocationTargetException;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.Collections;
@@ -32,9 +33,7 @@ import java.util.List;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
-public abstract class Agent {
- protected abstract void runAgent(String agentArgs, Instrumentation instrumentation, boolean injected) throws Exception;
-
+final class Agent {
public static void agentmain(String agentArgs, Instrumentation instrumentation) throws Throwable {
runAgents(agentArgs, instrumentation, true);
}
@@ -49,12 +48,15 @@ public abstract class Agent {
));
private static void runAgents(String agentArgs, Instrumentation instrumentation, boolean injected) throws Throwable {
+ ClassLoader cl = Main.createShadowClassLoader();
+
for (AgentInfo info : AGENTS) {
try {
- Class<?> agentClass = Class.forName(info.className());
- Agent agent = (Agent) agentClass.newInstance();
- agent.runAgent(agentArgs, instrumentation, injected);
+ Class<?> agentClass = cl.loadClass(info.className());
+ Object agent = agentClass.newInstance();
+ agentClass.getMethod("runAgent", String.class, Instrumentation.class, boolean.class).invoke(agent, agentArgs, instrumentation, injected);
} catch (Throwable t) {
+ if (t instanceof InvocationTargetException) t = t.getCause();
info.problem(t, instrumentation);
}
}
diff --git a/src/launch/lombok/launch/Main.java b/src/launch/lombok/launch/Main.java
new file mode 100644
index 00000000..f4b6a788
--- /dev/null
+++ b/src/launch/lombok/launch/Main.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 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.launch;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+
+class Main {
+ public static ClassLoader createShadowClassLoader() {
+ ShadowClassLoader cl = new ShadowClassLoader(Main.class.getClassLoader());
+ String scl = System.getProperty("shadow.classpath");
+ if (scl == null || scl.isEmpty()) return cl;
+ for (String part : scl.split("\\s*" + (File.pathSeparatorChar == ';' ? ";" : ":") + "\\s*")) {
+ if (part.endsWith("/*") || part.endsWith(File.separator + "*")) {
+ cl.addPriorityJarDir(part.substring(0, part.length() - 2));
+ } else {
+ cl.addPriorityClasspathEntry(part);
+ }
+ }
+
+ return cl;
+ }
+
+ public static void main(String[] args) throws Throwable {
+ ClassLoader cl = createShadowClassLoader();
+ Class<?> mc = cl.loadClass("lombok.core.Main");
+ try {
+ mc.getMethod("main", String[].class).invoke(null, new Object[] {args});
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ }
+}
diff --git a/src/launch/lombok/launch/ShadowClassLoader.java b/src/launch/lombok/launch/ShadowClassLoader.java
new file mode 100644
index 00000000..66749b94
--- /dev/null
+++ b/src/launch/lombok/launch/ShadowClassLoader.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2014 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.launch;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * The shadow classloader serves to completely hide almost all classes in a given jar file by using a different file ending.
+ *
+ * Classes loaded by the shadowloader use ".SCL.lombok" instead of ".class".
+ *
+ * In addition, the shadowloader will pick up an alternate (priority) classpath, using normal class files, from the system property {@code shadow.classpath}.
+ *
+ * This classloader accomplishes a number of things:<ul>
+ * <li>Avoid contaminating the namespace of any project using lombok. Autocompleters in IDEs will NOT suggest anything other than actual public API.
+ * <li>Like jarjar, allows folding in dependencies such as ASM without foisting these dependencies on projects that use lombok. shadowloader obviates the need for jarjar.
+ * <li>Allows an agent (which MUST be in jar form) to still load everything except this loader infrastructure from class files generated by the IDE, which should
+ * considerably help debugging, as you can now rely on the IDE's built-in auto-recompile features instead of having to run a full build everytime, and it should help
+ * with hot code replace and the like.
+ * </ul>
+ */
+class ShadowClassLoader extends ClassLoader {
+ private final ClassLoader source;
+ private final List<File> priority = new ArrayList<File>();
+
+ private static final int INITIAL_BUFFER_SIZE = 65536;
+ private static final int MAX_BUFFER_SIZE = 1048576;
+
+ public ShadowClassLoader(ClassLoader source) {
+ super(source);
+ this.source = source == null ? ClassLoader.getSystemClassLoader() : source;
+ }
+
+ private static final ThreadLocal<byte[]> BUFFERS = new ThreadLocal<byte[]>() {
+ @Override protected byte[] initialValue() {
+ return new byte[INITIAL_BUFFER_SIZE];
+ }
+ };
+
+ public Enumeration<URL> getResources(String name) throws IOException {
+ List<URL> prioritized = null;
+ for (File ce : priority) {
+ if (ce.isDirectory()) {
+ File f = new File(ce, name);
+ if (f.isFile() && f.canRead()) {
+ if (prioritized == null) prioritized = new ArrayList<URL>();
+ prioritized.add(f.toURI().toURL());
+ }
+ } else if (ce.isFile() && ce.canRead()) {
+ JarFile jf;
+ JarEntry entry;
+
+ try {
+ jf = new JarFile(ce);
+ entry = jf.getJarEntry(name);
+ } catch (IOException ignore) {
+ continue;
+ }
+
+ if (entry != null) try {
+ // TODO: This needs work, this feels a bit hacky. Is there an API way to create these URLs?
+ URL url = new URI("jar:" + ce.toURI().toString() + "!/" + name).toURL();
+ if (prioritized == null) prioritized = new ArrayList<URL>();
+ prioritized.add(url);
+ } catch (URISyntaxException ignore) {}
+ }
+ }
+
+ if (prioritized == null) return super.getResources(name);
+ final Iterator<URL> prim = prioritized.iterator();
+ final Enumeration<URL> sec = super.getResources(name);
+ return new Enumeration<URL>() {
+ @Override public boolean hasMoreElements() {
+ if (prim.hasNext()) return true;
+ return sec.hasMoreElements();
+ }
+
+ @Override public URL nextElement() {
+ if (prim.hasNext()) return prim.next();
+ return sec.nextElement();
+ }
+ };
+ }
+
+ @Override public URL getResource(String name) {
+ for (File ce : priority) {
+ if (ce.isDirectory()) {
+ File f = new File(ce, name);
+ if (f.isFile() && f.canRead()) try {
+ return f.toURI().toURL();
+ } catch (MalformedURLException ignore) {}
+ } else if (ce.isFile() && ce.canRead()) {
+ JarFile jf;
+ JarEntry entry;
+
+ try {
+ jf = new JarFile(ce);
+ entry = jf.getJarEntry(name);
+ } catch (IOException ignore) {
+ continue;
+ }
+
+ if (entry != null) try {
+ // TODO: This needs work, this feels a bit hacky. Is there an API way to create these URLs?
+ URL url = new URI("jar:" + ce.toURI().toString() + "!/" + name).toURL();
+ return url;
+ } catch (URISyntaxException ignore) {
+ } catch (MalformedURLException ignore) {
+ }
+ }
+ }
+
+ return super.getResource(name);
+ }
+
+ @Override protected Class<?> findClass(String name) throws ClassNotFoundException {
+ String rawName, sclName; {
+ String binName = name.replace(".", "/");
+ rawName = binName.concat(".class");
+ sclName = binName.concat(".SCL.lombok");
+ }
+
+ InputStream in = null;
+ byte[] b;
+ int p = 0;
+
+ try { try {
+ for (File ce : priority) {
+ if (ce.isDirectory()) {
+ File f = new File(ce, rawName);
+ if (f.isFile() && f.canRead()) {
+ in = new FileInputStream(f);
+ break;
+ }
+ } else if (ce.isFile() && ce.canRead()) {
+ JarFile jf;
+ JarEntry entry;
+
+ try {
+ jf = new JarFile(ce);
+ entry = jf.getJarEntry(rawName);
+ } catch (IOException ignore) {
+ continue;
+ }
+
+ if (entry != null) {
+ in = jf.getInputStream(entry);
+ break;
+ }
+ }
+ }
+
+ if (in == null) in = source.getResourceAsStream(sclName);
+ if (in == null) in = source.getResourceAsStream(rawName);
+ if (in == null) throw new ClassNotFoundException(name);
+
+ b = BUFFERS.get();
+ while (true) {
+ int r = in.read(b, p, b.length - p);
+ if (r == -1) break;
+ p += r;
+ if (p == b.length) {
+ byte[] nb = new byte[b.length * 2];
+ System.arraycopy(b, 0, nb, 0, p);
+ b = nb;
+ BUFFERS.set(nb);
+ }
+ }
+ } finally {
+ if (in != null) in.close();
+ }} catch (IOException e) {
+ throw new ClassNotFoundException("I/O exception reading class " + name, e);
+ }
+
+ Class<?> c = defineClass(name, b, 0, p);
+ if (b.length > MAX_BUFFER_SIZE) BUFFERS.set(new byte[INITIAL_BUFFER_SIZE]);
+ return c;
+ }
+
+ public void addPriorityJarDir(String dir) {
+ File f = new File(dir);
+ for (File j : f.listFiles(new FilenameFilter() {
+ @Override public boolean accept(File dir, String name) {
+ return name.toLowerCase().endsWith(".jar");
+ }
+ })) if (j.canRead() && j.isFile()) priority.add(j);
+ }
+
+ public void addPriorityClasspathEntry(String entry) {
+ priority.add(new File(entry));
+ }
+}