Skip to content

Commit 97ae862

Browse files
committed
Root specialized object classloader at JRuby classloader
The classes generated here need to be visible to other classes loaded and generated by JRuby, such as Ruby classes that implement Java interfaces. If the specialized class and a Java interface to implement come from different classloader hierarchies, the JVM classloading subsystem will be unable to resolve the impl class. The fix here makes the RubyObjectSpecializer per-runtime, so the specialized classes live within the standard JRubyClassLoader hierarchy, visible to other classes loaded and generated by Ruby code at runtime. An additional fix will pre-generate the first 50 specialized object classes, which will also fix the same issue (they will load into the same classloader as JRuby itself) and avoid re-generating these classes for each runtime instance. Fixes jruby#8412.
1 parent fe763ca commit 97ae862

File tree

3 files changed

+27
-17
lines changed

3 files changed

+27
-17
lines changed

core/src/main/java/org/jruby/Ruby.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
import org.jruby.runtime.MethodIndex;
7070
import org.jruby.runtime.TraceEventManager;
7171
import org.jruby.runtime.invokedynamic.InvokeDynamicSupport;
72+
import org.jruby.specialized.RubyObjectSpecializer;
7273
import org.jruby.util.JavaNameMangler;
7374
import org.jruby.util.MRIRecursionGuard;
7475
import org.jruby.util.StringSupport;
@@ -343,6 +344,9 @@ private Ruby(RubyInstanceConfig config) {
343344
objectClass.setConstant("Module", moduleClass);
344345
objectClass.setConstant("Refinement", refinementClass);
345346

347+
// specializer for RubyObject subclasses
348+
objectSpecializer = new RubyObjectSpecializer(this);
349+
346350
// Initialize Kernel and include into Object
347351
RubyModule kernel = kernelModule = RubyKernel.createKernelModule(this);
348352
objectClass.includeModule(kernelModule);
@@ -2619,6 +2623,10 @@ public JavaSupport getJavaSupport() {
26192623
return javaSupport;
26202624
}
26212625

2626+
public RubyObjectSpecializer getObjectSpecializer() {
2627+
return objectSpecializer;
2628+
}
2629+
26222630
public static ClassLoader getClassLoader() {
26232631
// we try to getService the classloader that loaded JRuby, falling back on System
26242632
ClassLoader loader = Ruby.class.getClassLoader();
@@ -5547,6 +5555,9 @@ public RubyClass getData() {
55475555
private final JavaSupport javaSupport;
55485556
private final JRubyClassLoader jrubyClassLoader;
55495557

5558+
// Object Specializer
5559+
private final RubyObjectSpecializer objectSpecializer;
5560+
55505561
// Management/monitoring
55515562
private final BeanManager beanManager;
55525563

core/src/main/java/org/jruby/RubyObject.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
176176
System.err.println(klass + ";" + foundVariables);
177177
}
178178

179-
allocator = RubyObjectSpecializer.specializeForVariables(klass, foundVariables);
179+
allocator = runtime.getObjectSpecializer().specializeForVariables(klass, foundVariables);
180180

181181
// invalidate metaclass so new allocator is picked up for specialized .new
182182
klass.metaClass.invalidateCacheDescendants();

core/src/main/java/org/jruby/specialized/RubyObjectSpecializer.java

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,12 @@
3636
import org.jruby.runtime.ObjectAllocator;
3737
import org.jruby.runtime.builtin.IRubyObject;
3838
import org.jruby.util.ClassDefiningClassLoader;
39-
import org.jruby.util.OneShotClassLoader;
4039
import org.jruby.util.cli.Options;
4140
import org.jruby.util.collections.NonBlockingHashMapLong;
4241
import org.objectweb.asm.Label;
4342
import org.objectweb.asm.tree.LabelNode;
4443

45-
import java.lang.invoke.CallSite;
46-
import java.lang.invoke.LambdaMetafactory;
47-
import java.lang.invoke.MethodHandle;
4844
import java.lang.invoke.MethodHandles;
49-
import java.lang.invoke.MethodType;
5045
import java.util.Set;
5146

5247
import static org.jruby.util.CodegenUtils.ci;
@@ -60,13 +55,17 @@ public class RubyObjectSpecializer {
6055

6156
public static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
6257

63-
private static ClassAndAllocator getClassForSize(int size) {
64-
return SPECIALIZED_CLASSES.get(size);
58+
private final Ruby runtime;
59+
60+
private ClassAndAllocator getClassForSize(int size) {
61+
return specializedClasses.get(size);
6562
}
6663

67-
private static final NonBlockingHashMapLong<ClassAndAllocator> SPECIALIZED_CLASSES = new NonBlockingHashMapLong<>();
64+
private final NonBlockingHashMapLong<ClassAndAllocator> specializedClasses = new NonBlockingHashMapLong<>();
6865

69-
private static final ClassDefiningClassLoader LOADER = new OneShotClassLoader(Ruby.getClassLoader());
66+
public RubyObjectSpecializer(Ruby runtime) {
67+
this.runtime = runtime;
68+
}
7069

7170
static class ClassAndAllocator {
7271
final Class cls;
@@ -78,7 +77,7 @@ static class ClassAndAllocator {
7877
}
7978
}
8079

81-
public static ObjectAllocator specializeForVariables(RubyClass klass, Set<String> foundVariables) {
80+
public ObjectAllocator specializeForVariables(RubyClass klass, Set<String> foundVariables) {
8281
int size = foundVariables.size();
8382

8483
// clamp to max object width (jruby/jruby#
@@ -108,11 +107,11 @@ public static ObjectAllocator specializeForVariables(RubyClass klass, Set<String
108107
if (className != null) {
109108
final String clsPath = "org/jruby/gen/" + className;
110109

111-
synchronized (LOADER) {
110+
synchronized (this) {
112111
Class specialized;
113112
try {
114113
// try loading class without generating
115-
specialized = LOADER.loadClass(clsPath.replace('/', '.'));
114+
specialized = runtime.getJRubyClassLoader().loadClass(clsPath.replace('/', '.'));
116115
} catch (ClassNotFoundException cnfe) {
117116
// generate specialized class
118117
specialized = generateInternal(size, clsPath);
@@ -124,7 +123,7 @@ public static ObjectAllocator specializeForVariables(RubyClass klass, Set<String
124123
cna = new ClassAndAllocator(specialized, allocator);
125124

126125
if (!Options.REIFY_VARIABLES_NAME.load()) {
127-
SPECIALIZED_CLASSES.put(size, cna);
126+
specializedClasses.put(size, cna);
128127
}
129128
} catch (Throwable t) {
130129
throw new RuntimeException(t);
@@ -155,7 +154,7 @@ public static ObjectAllocator specializeForVariables(RubyClass klass, Set<String
155154
return cna.allocator;
156155
}
157156

158-
private static Class generateInternal(int size, final String clsPath) {
157+
private Class generateInternal(int size, final String clsPath) {
159158
// ensure only one thread will attempt to generate and define the new class
160159
final String baseName = p(RubyObject.class);
161160

@@ -264,8 +263,8 @@ private static void genPutSwitch(String clsPath, int size, CodeBlock block, int
264263
block.label(defaultError);
265264
}
266265

267-
private static Class defineClass(JiteClass jiteClass) {
268-
return LOADER.defineClass(classNameFromJiteClass(jiteClass), jiteClass.toBytes(JDKVersion.V1_8));
266+
private Class defineClass(JiteClass jiteClass) {
267+
return runtime.getJRubyClassLoader().defineClass(classNameFromJiteClass(jiteClass), jiteClass.toBytes(JDKVersion.V1_8));
269268
}
270269

271270
private static String classNameFromJiteClass(JiteClass jiteClass) {

0 commit comments

Comments
 (0)