diff --git a/core/pom.rb b/core/pom.rb
index 7ada71f0b37..9fd2625b6f4 100644
--- a/core/pom.rb
+++ b/core/pom.rb
@@ -166,6 +166,16 @@
'${project.build.outputDirectory}' ],
'executable' => 'java',
'classpathScope' => 'compile' )
+
+ execute_goals( 'exec',
+ :id => 'specialized-object-generator',
+ 'arguments' => [ '-Djruby.bytecode.version=${base.java.version}',
+ '-classpath',
+ xml( '' ),
+ 'org.jruby.specialized.RubyObjectSpecializer',
+ '${project.build.outputDirectory}' ],
+ 'executable' => 'java',
+ 'classpathScope' => 'compile' )
end
end
diff --git a/core/pom.xml b/core/pom.xml
index c93c228a0e7..113027cc6bd 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -417,6 +417,24 @@ DO NOT MODIFY - GENERATED CODE
compile
+
+ specialized-object-generator
+ process-classes
+
+ exec
+
+
+
+ -Djruby.bytecode.version=${base.java.version}
+ -classpath
+
+ org.jruby.specialized.RubyObjectSpecializer
+ ${project.build.outputDirectory}
+
+ java
+ compile
+
+
diff --git a/core/src/main/java/org/jruby/specialized/RubyObjectSpecializer.java b/core/src/main/java/org/jruby/specialized/RubyObjectSpecializer.java
index 3599062c324..362ebafd57d 100644
--- a/core/src/main/java/org/jruby/specialized/RubyObjectSpecializer.java
+++ b/core/src/main/java/org/jruby/specialized/RubyObjectSpecializer.java
@@ -35,13 +35,14 @@
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.builtin.IRubyObject;
-import org.jruby.util.ClassDefiningClassLoader;
import org.jruby.util.cli.Options;
import org.jruby.util.collections.NonBlockingHashMapLong;
import org.objectweb.asm.Label;
import org.objectweb.asm.tree.LabelNode;
import java.lang.invoke.MethodHandles;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.Set;
import static org.jruby.util.CodegenUtils.ci;
@@ -54,6 +55,7 @@
public class RubyObjectSpecializer {
public static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+ private static final String GENERATED_PACKAGE = "org/jruby/gen/";
private final Ruby runtime;
@@ -78,56 +80,20 @@ static class ClassAndAllocator {
}
public ObjectAllocator specializeForVariables(RubyClass klass, Set foundVariables) {
- int size = foundVariables.size();
+ // clamp to max object width
+ int size = Math.min(foundVariables.size(), Options.REIFY_VARIABLES_MAX.load());
- // clamp to max object width (jruby/jruby#
- size = Math.min(size, Options.REIFY_VARIABLES_MAX.load());
-
- ClassAndAllocator cna = null;
- String className = null;
+ ClassAndAllocator cna;
if (Options.REIFY_VARIABLES_NAME.load()) {
- className = klass.getName();
-
- if (className.startsWith("#")) {
- className = "Anonymous" + Integer.toHexString(System.identityHashCode(klass));
- } else {
- className = className.replace("::", "/");
- }
+ // use Ruby class name for debugging, profiling
+ cna = generateSpecializedRubyObject(uniqueClassName(klass), size, false);
} else {
- // Generate class for specified size
+ // Generic class for specified size
cna = getClassForSize(size);
if (cna == null) {
- className = "RubyObject" + size;
- }
- }
-
- // if we have a className, proceed to generate
- if (className != null) {
- final String clsPath = "org/jruby/gen/" + className;
-
- synchronized (this) {
- Class specialized;
- try {
- // try loading class without generating
- specialized = runtime.getJRubyClassLoader().loadClass(clsPath.replace('/', '.'));
- } catch (ClassNotFoundException cnfe) {
- // generate specialized class
- specialized = generateInternal(size, clsPath);
- }
-
- try {
- ObjectAllocator allocator = (ObjectAllocator) specialized.getDeclaredClasses()[0].getConstructor().newInstance();
-
- cna = new ClassAndAllocator(specialized, allocator);
-
- if (!Options.REIFY_VARIABLES_NAME.load()) {
- specializedClasses.put(size, cna);
- }
- } catch (Throwable t) {
- throw new RuntimeException(t);
- }
+ cna = generateSpecializedRubyObject(genericClassName(size), size, true);
}
}
@@ -154,7 +120,78 @@ public ObjectAllocator specializeForVariables(RubyClass klass, Set found
return cna.allocator;
}
- private Class generateInternal(int size, final String clsPath) {
+ private ClassAndAllocator generateSpecializedRubyObject(String className, int size, boolean cache) {
+ ClassAndAllocator cna;
+
+ synchronized (this) {
+ Class specialized;
+ try {
+ // try loading class without generating
+ specialized = runtime.getJRubyClassLoader().loadClass(className.replace('/', '.'));
+ } catch (ClassNotFoundException cnfe) {
+ // generate specialized class
+ specialized = generateInternal(className, size);
+ }
+
+ try {
+ ObjectAllocator allocator = (ObjectAllocator) specialized.getDeclaredClasses()[0].getConstructor().newInstance();
+
+ cna = new ClassAndAllocator(specialized, allocator);
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ if (cache) {
+ specializedClasses.put(size, cna);
+ }
+
+ return cna;
+ }
+
+ private static String genericClassName(int size) {
+ return GENERATED_PACKAGE + "RubyObject" + size;
+ }
+
+ private static String uniqueClassName(RubyClass klass) {
+ String className = klass.getName();
+
+ if (className.startsWith("#")) {
+ className = "Anonymous" + Integer.toHexString(System.identityHashCode(klass));
+ } else {
+ className = className.replace("::", "/");
+ }
+
+ return GENERATED_PACKAGE + className;
+ }
+
+ /**
+ * Emit all generic RubyObject specializations to disk, so they do not need to generate at runtime.
+ */
+ public static void main(String[] args) throws Throwable {
+ String targetPath = args[0];
+
+ Files.createDirectories(Paths.get(targetPath, GENERATED_PACKAGE));
+
+ int maxVars = Options.REIFY_VARIABLES_MAX.load();
+ for (int i = 0; i <= maxVars; i++) {
+ String clsPath = genericClassName(i);
+ JiteClass jcls = generateJiteClass(clsPath, i);
+ Files.write(Paths.get(targetPath, clsPath + ".class"), jcls.toBytes(JDKVersion.V1_8));
+ Files.write(Paths.get(targetPath, clsPath + "Allocator.class"), jcls.getChildClasses().get(0).toBytes(JDKVersion.V1_8));
+ }
+ }
+
+ private Class generateInternal(final String clsPath, int size) {
+ final JiteClass jiteClass = generateJiteClass(clsPath, size);
+
+ Class specializedClass = defineClass(jiteClass);
+ defineClass(jiteClass.getChildClasses().get(0));
+
+ return specializedClass;
+ }
+
+ private static JiteClass generateJiteClass(String clsPath, int size) {
// ensure only one thread will attempt to generate and define the new class
final String baseName = p(RubyObject.class);
@@ -221,11 +258,7 @@ private Class generateInternal(int size, final String clsPath) {
}});
}});
}};
-
- Class specializedClass = defineClass(jiteClass);
- defineClass(jiteClass.getChildClasses().get(0));
-
- return specializedClass;
+ return jiteClass;
}
private static void genGetSwitch(String clsPath, int size, CodeBlock block, int offsetVar) {