Skip to content

Commit

Permalink
Make dispatchers robust for case where java.instrument module is miss…
Browse files Browse the repository at this point in the history
…ing.
  • Loading branch information
raphw committed Feb 3, 2020
1 parent a6be443 commit d9a5b3a
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1327,9 +1327,12 @@ enum CreationAction implements PrivilegedAction<Dispatcher> {
*/
public Dispatcher run() {
try {
return new ForJava6CapableVm(Instrumentation.class.getMethod("isRetransformClassesSupported"),
Instrumentation.class.getMethod("addTransformer", ClassFileTransformer.class, boolean.class),
Instrumentation.class.getMethod("retransformClasses", Class[].class));
Class<?> instrumentation = Class.forName("java.lang.instrument.Instrumentation");
return new ForJava6CapableVm(instrumentation.getMethod("isRetransformClassesSupported"),
instrumentation.getMethod("addTransformer", ClassFileTransformer.class, boolean.class),
instrumentation.getMethod("retransformClasses", Class[].class));
} catch (ClassNotFoundException ignored) {
return ForLegacyVm.INSTANCE;
} catch (NoSuchMethodException ignored) {
return ForLegacyVm.INSTANCE;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2379,16 +2379,19 @@ enum CreationAction implements PrivilegedAction<Dispatcher> {
*/
public Dispatcher run() {
try {
return new ForJava6CapableVm(Instrumentation.class.getMethod("appendToBootstrapClassLoaderSearch", JarFile.class),
Instrumentation.class.getMethod("appendToSystemClassLoaderSearch", JarFile.class));
Class<?> instrumentation = Class.forName("java.lang.instrument.Instrumentation");
return new ForJava6CapableVm(instrumentation.getMethod("appendToBootstrapClassLoaderSearch", JarFile.class),
instrumentation.getMethod("appendToSystemClassLoaderSearch", JarFile.class));
} catch (ClassNotFoundException ignored) {
return ForLegacyVm.INSTANCE;
} catch (NoSuchMethodException ignored) {
return ForLegacyVm.INSTANCE;
}
}
}

/**
* A dispatcher for a legacy VM that is not capable of appending jar files.
* A dispatcher for a legacy VM that is not capable of appending jar files using instrumentation.
*/
enum ForLegacyVm implements Dispatcher {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,10 +349,13 @@ enum CreationAction implements PrivilegedAction<Dispatcher> {
*/
public Dispatcher run() {
try {
return new ForJava6CapableVm(Instrumentation.class.getMethod("isModifiableClass", Class.class),
Instrumentation.class.getMethod("isRetransformClassesSupported"),
Instrumentation.class.getMethod("addTransformer", ClassFileTransformer.class, boolean.class),
Instrumentation.class.getMethod("retransformClasses", Class[].class));
Class<?> instrumentation = Class.forName("java.lang.instrument.Instrumentation");
return new ForJava6CapableVm(instrumentation.getMethod("isModifiableClass", Class.class),
instrumentation.getMethod("isRetransformClassesSupported"),
instrumentation.getMethod("addTransformer", ClassFileTransformer.class, boolean.class),
instrumentation.getMethod("retransformClasses", Class[].class));
} catch (ClassNotFoundException ignored) {
return ForLegacyVm.INSTANCE;
} catch (NoSuchMethodException ignored) {
return ForLegacyVm.INSTANCE;
}
Expand Down
203 changes: 151 additions & 52 deletions byte-buddy-dep/src/main/java/net/bytebuddy/utility/JavaModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -342,17 +342,31 @@ enum CreationAction implements PrivilegedAction<Dispatcher> {
public Dispatcher run() {
try {
Class<?> module = Class.forName("java.lang.Module");
return new Dispatcher.Enabled(Class.class.getMethod("getModule"),
module.getMethod("getClassLoader"),
module.getMethod("isNamed"),
module.getMethod("getName"),
module.getMethod("getResourceAsStream", String.class),
module.getMethod("isExported", String.class, module),
module.getMethod("isOpen", String.class, module),
module.getMethod("canRead", module),
Instrumentation.class.getMethod("isModifiableModule", module),
Instrumentation.class.getMethod("redefineModule", module, Set.class, Map.class, Map.class, Set.class, Map.class));
} catch (Exception ignored) {
try {
Class<?> instrumentation = Class.forName("java.lang.instrument.Instrumentation");
return new Dispatcher.Enabled.WithInstrumentationSupport(Class.class.getMethod("getModule"),
module.getMethod("getClassLoader"),
module.getMethod("isNamed"),
module.getMethod("getName"),
module.getMethod("getResourceAsStream", String.class),
module.getMethod("isExported", String.class, module),
module.getMethod("isOpen", String.class, module),
module.getMethod("canRead", module),
instrumentation.getMethod("isModifiableModule", module),
instrumentation.getMethod("redefineModule", module, Set.class, Map.class, Map.class, Set.class, Map.class));
} catch (ClassNotFoundException ignored) {
return new Dispatcher.Enabled.WithoutInstrumentationSupport(Class.class.getMethod("getModule"),
module.getMethod("getClassLoader"),
module.getMethod("isNamed"),
module.getMethod("getName"),
module.getMethod("getResourceAsStream", String.class),
module.getMethod("isExported", String.class, module),
module.getMethod("isOpen", String.class, module),
module.getMethod("canRead", module));
}
} catch (ClassNotFoundException ignored) {
return Dispatcher.Disabled.INSTANCE;
} catch (NoSuchMethodException ignored) {
return Dispatcher.Disabled.INSTANCE;
}
}
Expand All @@ -362,7 +376,7 @@ public Dispatcher run() {
* A dispatcher for a VM that does support the {@code java.lang.Module} API.
*/
@HashCodeAndEqualsPlugin.Enhance
class Enabled implements Dispatcher {
abstract class Enabled implements Dispatcher {

/**
* An empty array that can be used to indicate no arguments to avoid an allocation on a reflective call.
Expand Down Expand Up @@ -409,16 +423,6 @@ class Enabled implements Dispatcher {
*/
private final Method canRead;

/**
* The {@code java.lang.instrument.Instrumentation#isModifiableModule} method.
*/
private final Method isModifiableModule;

/**
* The {@code java.lang.instrument.Instrumentation#redefineModule} method.
*/
private final Method redefineModule;

/**
* Creates an enabled dispatcher.
*
Expand All @@ -430,8 +434,6 @@ class Enabled implements Dispatcher {
* @param isExported The {@code java.lang.Module#isExported(String,Module)} method.
* @param isOpened The {@code java.lang.Module#isOpened(String,Module)} method.
* @param canRead The {@code java.lang.Module#canRead(Module)} method.
* @param isModifiableModule The {@code java.lang.instrument.Instrumentation#isModifiableModule} method.
* @param redefineModule The {@code java.lang.instrument.Instrumentation#redefineModule} method.
*/
protected Enabled(Method getModule,
Method getClassLoader,
Expand All @@ -440,9 +442,7 @@ protected Enabled(Method getModule,
Method getResourceAsStream,
Method isExported,
Method isOpened,
Method canRead,
Method isModifiableModule,
Method redefineModule) {
Method canRead) {
this.getModule = getModule;
this.getClassLoader = getClassLoader;
this.isNamed = isNamed;
Expand All @@ -451,8 +451,6 @@ protected Enabled(Method getModule,
this.isExported = isExported;
this.isOpened = isOpened;
this.canRead = canRead;
this.isModifiableModule = isModifiableModule;
this.redefineModule = redefineModule;
}

/**
Expand Down Expand Up @@ -567,30 +565,131 @@ public boolean canRead(Object source, Object target) {
}

/**
* {@inheritDoc}
*/
public void modify(Instrumentation instrumentation,
Object source,
Set<Object> reads,
Map<String, Set<Object>> exports,
Map<String, Set<Object>> opens,
Set<Class<?>> uses,
Map<Class<?>, List<Class<?>>> provides) {
try {
if (!(Boolean) isModifiableModule.invoke(instrumentation, source)) {
throw new IllegalStateException(source + " is not modifiable");
}
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access " + redefineModule, exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Cannot invoke " + redefineModule, exception.getCause());
* A dispatcher for a VM that does support the {@code java.lang.Module} API and that does not support {@link Instrumentation}.
*/
protected static class WithoutInstrumentationSupport extends Enabled {

/**
* Creates an enabled dispatcher without support for {@link Instrumentation}.
*
* @param getModule The {@code java.lang.Class#getModule()} method.
* @param getClassLoader The {@code java.lang.Module#getClassLoader()} method.
* @param isNamed The {@code java.lang.Module#isNamed()} method.
* @param getName The {@code java.lang.Module#getName()} method.
* @param getResourceAsStream The {@code java.lang.Module#getResourceAsStream(String)} method.
* @param isExported The {@code java.lang.Module#isExported(String,Module)} method.
* @param isOpened The {@code java.lang.Module#isOpened(String,Module)} method.
* @param canRead The {@code java.lang.Module#canRead(Module)} method.
*/
protected WithoutInstrumentationSupport(Method getModule,
Method getClassLoader,
Method isNamed,
Method getName,
Method getResourceAsStream,
Method isExported,
Method isOpened,
Method canRead) {
super(getModule,
getClassLoader,
isNamed,
getName,
getResourceAsStream,
isExported,
isOpened,
canRead);
}
try {
redefineModule.invoke(instrumentation, source, reads, exports, opens, uses, provides);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access " + redefineModule, exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Cannot invoke " + redefineModule, exception.getCause());

/**
* {@inheritDoc}
*/
public void modify(Instrumentation instrumentation,
Object source,
Set<Object> reads,
Map<String, Set<Object>> exports,
Map<String, Set<Object>> opens,
Set<Class<?>> uses,
Map<Class<?>, List<Class<?>>> provides) {
throw new IllegalStateException("Did not expect use of instrumentation");
}
}

/**
* A dispatcher for a VM that does support the {@code java.lang.Module} API and that supports {@link Instrumentation}.
*/
protected static class WithInstrumentationSupport extends Enabled {

/**
* The {@code java.lang.instrument.Instrumentation#isModifiableModule} method.
*/
private final Method isModifiableModule;

/**
* The {@code java.lang.instrument.Instrumentation#redefineModule} method.
*/
private final Method redefineModule;

/**
* Creates an enabled dispatcher.
*
* @param getModule The {@code java.lang.Class#getModule()} method.
* @param getClassLoader The {@code java.lang.Module#getClassLoader()} method.
* @param isNamed The {@code java.lang.Module#isNamed()} method.
* @param getName The {@code java.lang.Module#getName()} method.
* @param getResourceAsStream The {@code java.lang.Module#getResourceAsStream(String)} method.
* @param isExported The {@code java.lang.Module#isExported(String,Module)} method.
* @param isOpened The {@code java.lang.Module#isOpened(String,Module)} method.
* @param canRead The {@code java.lang.Module#canRead(Module)} method.
* @param isModifiableModule The {@code java.lang.instrument.Instrumentation#isModifiableModule} method.
* @param redefineModule The {@code java.lang.instrument.Instrumentation#redefineModule} method.
*/
protected WithInstrumentationSupport(Method getModule,
Method getClassLoader,
Method isNamed,
Method getName,
Method getResourceAsStream,
Method isExported,
Method isOpened,
Method canRead,
Method isModifiableModule,
Method redefineModule) {
super(getModule,
getClassLoader,
isNamed,
getName,
getResourceAsStream,
isExported,
isOpened,
canRead);
this.isModifiableModule = isModifiableModule;
this.redefineModule = redefineModule;
}

/**
* {@inheritDoc}
*/
public void modify(Instrumentation instrumentation,
Object source,
Set<Object> reads,
Map<String, Set<Object>> exports,
Map<String, Set<Object>> opens,
Set<Class<?>> uses,
Map<Class<?>, List<Class<?>>> provides) {
try {
if (!(Boolean) isModifiableModule.invoke(instrumentation, source)) {
throw new IllegalStateException(source + " is not modifiable");
}
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access " + redefineModule, exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Cannot invoke " + redefineModule, exception.getCause());
}
try {
redefineModule.invoke(instrumentation, source, reads, exports, opens, uses, provides);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access " + redefineModule, exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Cannot invoke " + redefineModule, exception.getCause());
}
}
}
}
Expand Down

1 comment on commit d9a5b3a

@sormuras
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.