3535import org .jruby .runtime .Helpers ;
3636import org .jruby .runtime .ObjectAllocator ;
3737import org .jruby .runtime .builtin .IRubyObject ;
38- import org .jruby .util .ClassDefiningClassLoader ;
39- import org .jruby .util .OneShotClassLoader ;
4038import org .jruby .util .cli .Options ;
4139import org .jruby .util .collections .NonBlockingHashMapLong ;
4240import org .objectweb .asm .Label ;
4341import org .objectweb .asm .tree .LabelNode ;
4442
45- import java .lang .invoke .CallSite ;
46- import java .lang .invoke .LambdaMetafactory ;
47- import java .lang .invoke .MethodHandle ;
4843import java .lang .invoke .MethodHandles ;
49- import java .lang .invoke .MethodType ;
44+ import java .nio .file .Files ;
45+ import java .nio .file .Paths ;
5046import java .util .Set ;
5147
5248import static org .jruby .util .CodegenUtils .ci ;
5955public class RubyObjectSpecializer {
6056
6157 public static final MethodHandles .Lookup LOOKUP = MethodHandles .lookup ();
58+ private static final String GENERATED_PACKAGE = "org/jruby/gen/" ;
6259
63- private static ClassAndAllocator getClassForSize (int size ) {
64- return SPECIALIZED_CLASSES .get (size );
60+ private final Ruby runtime ;
61+
62+ private ClassAndAllocator getClassForSize (int size ) {
63+ return specializedClasses .get (size );
6564 }
6665
67- private static final NonBlockingHashMapLong <ClassAndAllocator > SPECIALIZED_CLASSES = new NonBlockingHashMapLong <>();
66+ private final NonBlockingHashMapLong <ClassAndAllocator > specializedClasses = new NonBlockingHashMapLong <>();
6867
69- private static final ClassDefiningClassLoader LOADER = new OneShotClassLoader (Ruby .getClassLoader ());
68+ public RubyObjectSpecializer (Ruby runtime ) {
69+ this .runtime = runtime ;
70+ }
7071
7172 static class ClassAndAllocator {
7273 final Class cls ;
@@ -78,57 +79,21 @@ static class ClassAndAllocator {
7879 }
7980 }
8081
81- public static ObjectAllocator specializeForVariables (RubyClass klass , Set <String > foundVariables ) {
82- int size = foundVariables .size ();
83-
84- // clamp to max object width (jruby/jruby#
85- size = Math .min (size , Options .REIFY_VARIABLES_MAX .load ());
82+ public ObjectAllocator specializeForVariables (RubyClass klass , Set <String > foundVariables ) {
83+ // clamp to max object width
84+ int size = Math .min (foundVariables .size (), Options .REIFY_VARIABLES_MAX .load ());
8685
87- ClassAndAllocator cna = null ;
88- String className = null ;
86+ ClassAndAllocator cna ;
8987
9088 if (Options .REIFY_VARIABLES_NAME .load ()) {
91- className = klass .getName ();
92-
93- if (className .startsWith ("#" )) {
94- className = "Anonymous" + Integer .toHexString (System .identityHashCode (klass ));
95- } else {
96- className = className .replace ("::" , "/" );
97- }
89+ // use Ruby class name for debugging, profiling
90+ cna = generateSpecializedRubyObject (uniqueClassName (klass ), size , false );
9891 } else {
99- // Generate class for specified size
92+ // Generic class for specified size
10093 cna = getClassForSize (size );
10194
10295 if (cna == null ) {
103- className = "RubyObject" + size ;
104- }
105- }
106-
107- // if we have a className, proceed to generate
108- if (className != null ) {
109- final String clsPath = "org/jruby/gen/" + className ;
110-
111- synchronized (LOADER ) {
112- Class specialized ;
113- try {
114- // try loading class without generating
115- specialized = LOADER .loadClass (clsPath .replace ('/' , '.' ));
116- } catch (ClassNotFoundException cnfe ) {
117- // generate specialized class
118- specialized = generateInternal (size , clsPath );
119- }
120-
121- try {
122- ObjectAllocator allocator = (ObjectAllocator ) specialized .getDeclaredClasses ()[0 ].getConstructor ().newInstance ();
123-
124- cna = new ClassAndAllocator (specialized , allocator );
125-
126- if (!Options .REIFY_VARIABLES_NAME .load ()) {
127- SPECIALIZED_CLASSES .put (size , cna );
128- }
129- } catch (Throwable t ) {
130- throw new RuntimeException (t );
131- }
96+ cna = generateSpecializedRubyObject (genericClassName (size ), size , true );
13297 }
13398 }
13499
@@ -155,7 +120,78 @@ public static ObjectAllocator specializeForVariables(RubyClass klass, Set<String
155120 return cna .allocator ;
156121 }
157122
158- private static Class generateInternal (int size , final String clsPath ) {
123+ private ClassAndAllocator generateSpecializedRubyObject (String className , int size , boolean cache ) {
124+ ClassAndAllocator cna ;
125+
126+ synchronized (this ) {
127+ Class specialized ;
128+ try {
129+ // try loading class without generating
130+ specialized = runtime .getJRubyClassLoader ().loadClass (className .replace ('/' , '.' ));
131+ } catch (ClassNotFoundException cnfe ) {
132+ // generate specialized class
133+ specialized = generateInternal (className , size );
134+ }
135+
136+ try {
137+ ObjectAllocator allocator = (ObjectAllocator ) specialized .getDeclaredClasses ()[0 ].getConstructor ().newInstance ();
138+
139+ cna = new ClassAndAllocator (specialized , allocator );
140+ } catch (Throwable t ) {
141+ throw new RuntimeException (t );
142+ }
143+ }
144+
145+ if (cache ) {
146+ specializedClasses .put (size , cna );
147+ }
148+
149+ return cna ;
150+ }
151+
152+ private static String genericClassName (int size ) {
153+ return GENERATED_PACKAGE + "RubyObject" + size ;
154+ }
155+
156+ private static String uniqueClassName (RubyClass klass ) {
157+ String className = klass .getName ();
158+
159+ if (className .startsWith ("#" )) {
160+ className = "Anonymous" + Integer .toHexString (System .identityHashCode (klass ));
161+ } else {
162+ className = className .replace ("::" , "/" );
163+ }
164+
165+ return GENERATED_PACKAGE + className ;
166+ }
167+
168+ /**
169+ * Emit all generic RubyObject specializations to disk, so they do not need to generate at runtime.
170+ */
171+ public static void main (String [] args ) throws Throwable {
172+ String targetPath = args [0 ];
173+
174+ Files .createDirectories (Paths .get (targetPath , GENERATED_PACKAGE ));
175+
176+ int maxVars = Options .REIFY_VARIABLES_MAX .load ();
177+ for (int i = 0 ; i <= maxVars ; i ++) {
178+ String clsPath = genericClassName (i );
179+ JiteClass jcls = generateJiteClass (clsPath , i );
180+ Files .write (Paths .get (targetPath , clsPath + ".class" ), jcls .toBytes (JDKVersion .V1_8 ));
181+ Files .write (Paths .get (targetPath , clsPath + "Allocator.class" ), jcls .getChildClasses ().get (0 ).toBytes (JDKVersion .V1_8 ));
182+ }
183+ }
184+
185+ private Class generateInternal (final String clsPath , int size ) {
186+ final JiteClass jiteClass = generateJiteClass (clsPath , size );
187+
188+ Class specializedClass = defineClass (jiteClass );
189+ defineClass (jiteClass .getChildClasses ().get (0 ));
190+
191+ return specializedClass ;
192+ }
193+
194+ private static JiteClass generateJiteClass (String clsPath , int size ) {
159195 // ensure only one thread will attempt to generate and define the new class
160196 final String baseName = p (RubyObject .class );
161197
@@ -222,11 +258,7 @@ private static Class generateInternal(int size, final String clsPath) {
222258 }});
223259 }});
224260 }};
225-
226- Class specializedClass = defineClass (jiteClass );
227- defineClass (jiteClass .getChildClasses ().get (0 ));
228-
229- return specializedClass ;
261+ return jiteClass ;
230262 }
231263
232264 private static void genGetSwitch (String clsPath , int size , CodeBlock block , int offsetVar ) {
@@ -264,8 +296,8 @@ private static void genPutSwitch(String clsPath, int size, CodeBlock block, int
264296 block .label (defaultError );
265297 }
266298
267- private static Class defineClass (JiteClass jiteClass ) {
268- return LOADER .defineClass (classNameFromJiteClass (jiteClass ), jiteClass .toBytes (JDKVersion .V1_8 ));
299+ private Class defineClass (JiteClass jiteClass ) {
300+ return runtime . getJRubyClassLoader () .defineClass (classNameFromJiteClass (jiteClass ), jiteClass .toBytes (JDKVersion .V1_8 ));
269301 }
270302
271303 private static String classNameFromJiteClass (JiteClass jiteClass ) {
0 commit comments