3535import org .jruby .runtime .Helpers ;
3636import org .jruby .runtime .ObjectAllocator ;
3737import org .jruby .runtime .builtin .IRubyObject ;
38- import org .jruby .util .ClassDefiningClassLoader ;
3938import org .jruby .util .cli .Options ;
4039import org .jruby .util .collections .NonBlockingHashMapLong ;
4140import org .objectweb .asm .Label ;
4241import org .objectweb .asm .tree .LabelNode ;
4342
4443import java .lang .invoke .MethodHandles ;
44+ import java .nio .file .Files ;
45+ import java .nio .file .Paths ;
4546import java .util .Set ;
4647
4748import static org .jruby .util .CodegenUtils .ci ;
5455public class RubyObjectSpecializer {
5556
5657 public static final MethodHandles .Lookup LOOKUP = MethodHandles .lookup ();
58+ private static final String GENERATED_PACKAGE = "org/jruby/gen/" ;
5759
5860 private final Ruby runtime ;
5961
@@ -78,56 +80,20 @@ static class ClassAndAllocator {
7880 }
7981
8082 public ObjectAllocator specializeForVariables (RubyClass klass , Set <String > foundVariables ) {
81- int size = foundVariables .size ();
83+ // clamp to max object width
84+ int size = Math .min (foundVariables .size (), Options .REIFY_VARIABLES_MAX .load ());
8285
83- // clamp to max object width (jruby/jruby#
84- size = Math .min (size , Options .REIFY_VARIABLES_MAX .load ());
85-
86- ClassAndAllocator cna = null ;
87- String className = null ;
86+ ClassAndAllocator cna ;
8887
8988 if (Options .REIFY_VARIABLES_NAME .load ()) {
90- className = klass .getName ();
91-
92- if (className .startsWith ("#" )) {
93- className = "Anonymous" + Integer .toHexString (System .identityHashCode (klass ));
94- } else {
95- className = className .replace ("::" , "/" );
96- }
89+ // use Ruby class name for debugging, profiling
90+ cna = generateSpecializedRubyObject (uniqueClassName (klass ), size , false );
9791 } else {
98- // Generate class for specified size
92+ // Generic class for specified size
9993 cna = getClassForSize (size );
10094
10195 if (cna == null ) {
102- className = "RubyObject" + size ;
103- }
104- }
105-
106- // if we have a className, proceed to generate
107- if (className != null ) {
108- final String clsPath = "org/jruby/gen/" + className ;
109-
110- synchronized (this ) {
111- Class specialized ;
112- try {
113- // try loading class without generating
114- specialized = runtime .getJRubyClassLoader ().loadClass (clsPath .replace ('/' , '.' ));
115- } catch (ClassNotFoundException cnfe ) {
116- // generate specialized class
117- specialized = generateInternal (size , clsPath );
118- }
119-
120- try {
121- ObjectAllocator allocator = (ObjectAllocator ) specialized .getDeclaredClasses ()[0 ].getConstructor ().newInstance ();
122-
123- cna = new ClassAndAllocator (specialized , allocator );
124-
125- if (!Options .REIFY_VARIABLES_NAME .load ()) {
126- specializedClasses .put (size , cna );
127- }
128- } catch (Throwable t ) {
129- throw new RuntimeException (t );
130- }
96+ cna = generateSpecializedRubyObject (genericClassName (size ), size , true );
13197 }
13298 }
13399
@@ -154,7 +120,78 @@ public ObjectAllocator specializeForVariables(RubyClass klass, Set<String> found
154120 return cna .allocator ;
155121 }
156122
157- private 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 ) {
158195 // ensure only one thread will attempt to generate and define the new class
159196 final String baseName = p (RubyObject .class );
160197
@@ -221,11 +258,7 @@ private Class generateInternal(int size, final String clsPath) {
221258 }});
222259 }});
223260 }};
224-
225- Class specializedClass = defineClass (jiteClass );
226- defineClass (jiteClass .getChildClasses ().get (0 ));
227-
228- return specializedClass ;
261+ return jiteClass ;
229262 }
230263
231264 private static void genGetSwitch (String clsPath , int size , CodeBlock block , int offsetVar ) {
0 commit comments