Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 5f85cb8fdecc
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: afe3b28de09e
Choose a head ref
  • 2 commits
  • 3 files changed
  • 1 contributor

Commits on Jan 19, 2017

  1. Don't use reflection to define classes, since Java 9 hates it.

    Java 9 errors when we try to make defineClass accessible on
    ClassLoader, due to accessibility of the java.lang module. It
    turns out we don't need to do this, anyway, because the only
    ClassLoader passed in is a JRubyClassLoader, which implements
    ClassDefiningClassLoader and exposes defineClass publicly.
    headius committed Jan 19, 2017
    Copy the full SHA
    6926296 View commit details
  2. Disable setAccessible in JI when on Java 9.

    Java 9's module system requires us to do a bit more work to
    set methods accessible within java.lang and other modules. If we
    fail to do this, the method raises InaccessibleObjectException,
    a Java 9-specific exception we can't catch in Java 8 code. For the
    moment we will disable setAccessible use on Java 9 so JRuby can
    start up and run correctly.
    headius committed Jan 19, 2017
    Copy the full SHA
    afe3b28 View commit details
Original file line number Diff line number Diff line change
@@ -63,6 +63,7 @@
import org.jruby.javasupport.*;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ClassDefiningClassLoader;

import static org.jruby.javasupport.JavaClass.EMPTY_CLASS_ARRAY;
import static org.jruby.javasupport.JavaCallable.inspectParameterTypes;
@@ -155,7 +156,7 @@ public static JavaProxyClass newProxyClass(final Ruby runtime, Class superClass,
if ( proxyClass != null ) return proxyClass;

final ClassLoader loader = runtime.getJRubyClassLoader();
proxyClass = runtime.getJavaProxyClassFactory().genProxyClass(runtime, loader, null, superClass, interfaces, names);
proxyClass = runtime.getJavaProxyClassFactory().genProxyClass(runtime, (ClassDefiningClassLoader) loader, null, superClass, interfaces, names);
return JavaSupportImpl.saveJavaProxyClass(runtime, classKey, proxyClass);
}

Original file line number Diff line number Diff line change
@@ -49,6 +49,8 @@
import org.jruby.javasupport.JavaSupportImpl;
import org.jruby.runtime.Helpers;
import org.jruby.util.ArraySupport;
import org.jruby.util.ClassDefiningClassLoader;
import org.jruby.util.OneShotClassLoader;
import org.jruby.util.cli.Options;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;
@@ -135,7 +137,7 @@ public static JavaProxyClassFactory createFactory() {

static ThreadLocal<Ruby> runtimeTLS = new ThreadLocal<>();

public final JavaProxyClass genProxyClass(final Ruby runtime, ClassLoader loader,
public final JavaProxyClass genProxyClass(final Ruby runtime, ClassDefiningClassLoader loader,
String targetClassName, Class superClass, Class[] interfaces, Set<String> names)
throws InvocationTargetException {
final Ruby prev = runtimeTLS.get();
@@ -146,24 +148,24 @@ public final JavaProxyClass genProxyClass(final Ruby runtime, ClassLoader loader
finally { runtimeTLS.set(prev); }
}

public JavaProxyClass newProxyClass(final Ruby runtime, ClassLoader loader,
public JavaProxyClass newProxyClass(final Ruby runtime, ClassDefiningClassLoader loader,
String targetClassName, Class superClass, Class[] interfaces, Set<String> names)
throws InvocationTargetException {

if (loader == null) loader = JavaProxyClassFactory.class.getClassLoader();
if (targetClassName == null) {
targetClassName = targetClassName(superClass);
}
validateArgs(runtime, targetClassName, superClass);

Type selfType = Type.getType('L' + toInternalClassName(targetClassName) + ';');
Map<MethodKey, MethodData> methods = collectMethods(superClass, interfaces, names);

return generate(loader, targetClassName, superClass, interfaces, methods, selfType);
}

private JavaProxyClass generate(ClassLoader loader, String targetClassName,
Class superClass, Class[] interfaces,
Map<MethodKey, MethodData> methods, Type selfType) {
private JavaProxyClass generate(ClassDefiningClassLoader loader, String targetClassName,
Class superClass, Class[] interfaces,
Map<MethodKey, MethodData> methods, Type selfType) {
ClassWriter cw = beginProxyClass(targetClassName, superClass, interfaces);

GeneratorAdapter clazzInit = createClassInitializer(selfType, cw);
@@ -180,7 +182,7 @@ private JavaProxyClass generate(ClassLoader loader, String targetClassName,
// end class
cw.visitEnd();

Class clazz = invokeDefineClass(loader, selfType.getClassName(), cw.toByteArray());
Class clazz = loader.defineClass(selfType.getClassName(), cw.toByteArray());

// trigger class initialization for the class
try {
@@ -206,42 +208,6 @@ private static String targetClassName(final Class<?> clazz) {
.append("$Proxy").append(nextId()).toString();
}

private static final Method defineClassMethod;

static {
defineClassMethod = AccessController.doPrivileged(new PrivilegedAction<Method>() {
public Method run() {
try {
final Class[] parameterTypes = { String.class,
byte[].class, int.class, int.class, ProtectionDomain.class
};
final Method method = ClassLoader.class.getDeclaredMethod("defineClass", parameterTypes);
method.setAccessible(true);
return method;
}
catch (Exception e) {
LOG.error("could not use ClassLoader.defineClass method", e);
return null; // should not happen!
}
}
});
}

protected Class invokeDefineClass(ClassLoader loader, String className, final byte[] data) {
try {
final Object[] parameters = { className, data, 0, data.length, JavaProxyClassFactory.class.getProtectionDomain() };
return (Class) defineClassMethod.invoke(loader, parameters);
}
catch (IllegalArgumentException|IllegalAccessException e) {
LOG.warn("defining class with name " + className + " failed", e);
return null;
}
catch (InvocationTargetException e) {
LOG.warn("defining class with name " + className + " failed", e.getTargetException());
return null;
}
}

private static ClassWriter beginProxyClass(final String className,
final Class superClass, final Class[] interfaces) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
@@ -829,4 +795,26 @@ private static StringBuilder proxyPackageName(final String className) {
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public static @interface VarArgs {}

@Deprecated
public final JavaProxyClass genProxyClass(final Ruby runtime, ClassLoader loader,
String targetClassName, Class superClass, Class[] interfaces, Set<String> names)
throws InvocationTargetException {
if (loader instanceof ClassDefiningClassLoader) {
return genProxyClass(runtime, (ClassDefiningClassLoader) loader, targetClassName, superClass, interfaces, names);
}

return genProxyClass(runtime, (ClassDefiningClassLoader) new OneShotClassLoader(loader), targetClassName, superClass, interfaces, names);
}

@Deprecated
public JavaProxyClass newProxyClass(final Ruby runtime, ClassLoader loader,
String targetClassName, Class superClass, Class[] interfaces, Set<String> names)
throws InvocationTargetException {
if (loader instanceof ClassDefiningClassLoader) {
return newProxyClass(runtime, (ClassDefiningClassLoader) loader, targetClassName, superClass, interfaces, names);
}

return newProxyClass(runtime, (ClassDefiningClassLoader) new OneShotClassLoader(loader), targetClassName, superClass, interfaces, names);
}

}
12 changes: 11 additions & 1 deletion core/src/main/java/org/jruby/util/cli/Options.java
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@
***** END LICENSE BLOCK *****/
package org.jruby.util.cli;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -187,7 +188,7 @@ public class Options {
public static final Option<Boolean> DUMP_INSTANCE_VARS = bool(DEBUG, "dump.variables", false, "Dump class + instance var names on first new of Object subclasses.");
public static final Option<Boolean> REWRITE_JAVA_TRACE = bool(DEBUG, "rewrite.java.trace", true, "Rewrite stack traces from exceptions raised in Java calls.");

public static final Option<Boolean> JI_SETACCESSIBLE = bool(JAVA_INTEGRATION, "ji.setAccessible", true, "Try to set inaccessible Java methods to be accessible.");
public static final Option<Boolean> JI_SETACCESSIBLE = bool(JAVA_INTEGRATION, "ji.setAccessible", calculateSetAccessibleDefault(), "Try to set inaccessible Java methods to be accessible.");
public static final Option<Boolean> JI_LOGCANSETACCESSIBLE = bool(JAVA_INTEGRATION, "ji.logCanSetAccessible", false, "Log whether setAccessible is working.");
public static final Option<Boolean> JI_UPPER_CASE_PACKAGE_NAME_ALLOWED = bool(JAVA_INTEGRATION, "ji.upper.case.package.name.allowed", false, "Allow Capitalized Java package names.");
public static final Option<Boolean> INTERFACES_USEPROXY = bool(JAVA_INTEGRATION, "interfaces.useProxy", false, "Use java.lang.reflect.Proxy for interface impl.");
@@ -285,6 +286,15 @@ private static boolean calculateInvokedynamicDefault() {
return false;
}

/**
* Java 9 has much more restrictive reflection access to e.g. java.lang classes, and raises a Java 9-specific
* error. For now we default ji.setAccessible to false so we don't attempt it.
*/
private static boolean calculateSetAccessibleDefault() {
String version = SafePropertyAccessor.getProperty("java.specification.version", "1.7");
return new BigDecimal(version).compareTo(new BigDecimal("1.9")) < 0;
}

private enum SearchMode { PREFIX, CONTAINS }

public static void listPrefix(String prefix) {