@@ -192,7 +192,9 @@ private static void appendMethod(StringBuilder out, ByteCodeClass cls, BytecodeM
192192 List <Instruction > instructions = method .getInstructions ();
193193 Map <Label , Integer > labelToIndex = buildLabelMap (instructions );
194194 String jsMethodName = jsMethodIdentifier (cls , method );
195- out .append ("function* " ).append (jsMethodName ).append ("(" );
195+ String jsMethodBodyName = jsMethodBodyIdentifier (cls , method );
196+ boolean wrappedStaticMethod = isWrappedStaticMethod (method );
197+ out .append ("function* " ).append (wrappedStaticMethod ? jsMethodBodyName : jsMethodName ).append ("(" );
196198 boolean first = true ;
197199 if (!method .isStatic ()) {
198200 out .append ("__cn1ThisObject" );
@@ -207,15 +209,25 @@ private static void appendMethod(StringBuilder out, ByteCodeClass cls, BytecodeM
207209 out .append ("__cn1Arg" ).append (i + 1 );
208210 }
209211 out .append ("){\n " );
210- if (method .isStatic () && !"__CLINIT__" .equals (method .getMethodName ())) {
212+ if (! wrappedStaticMethod && method .isStatic () && !"__CLINIT__" .equals (method .getMethodName ())) {
211213 out .append (" jvm.ensureClassInitialized(\" " ).append (cls .getClsName ()).append ("\" );\n " );
212214 }
213- if (appendStraightLineMethodBody (out , cls , method , instructions , jsMethodName )) {
215+ if (appendStraightLineMethodBody (out , cls , method , instructions , wrappedStaticMethod ? jsMethodBodyName : jsMethodName )) {
216+ if (wrappedStaticMethod ) {
217+ appendWrappedStaticMethod (out , cls , method , jsMethodName , jsMethodBodyName );
218+ }
214219 return ;
215220 }
221+ boolean usesClassInitCache = hasClassInitSensitiveAccess (instructions );
216222 out .append (" const locals = new Array(" ).append (Math .max (1 , method .getMaxLocals ())).append (").fill(null);\n " );
217223 out .append (" const stack = [];\n " );
218224 out .append (" let pc = 0;\n " );
225+ if (usesClassInitCache ) {
226+ out .append (" const __cn1Init = Object.create(null);\n " );
227+ if (method .isStatic () && !"__CLINIT__" .equals (method .getMethodName ())) {
228+ out .append (" __cn1Init[\" " ).append (cls .getClsName ()).append ("\" ] = true;\n " );
229+ }
230+ }
219231 if (!method .isStatic ()) {
220232 out .append (" locals[0] = __cn1ThisObject;\n " );
221233 }
@@ -239,7 +251,7 @@ private static void appendMethod(StringBuilder out, ByteCodeClass cls, BytecodeM
239251 for (int i = 0 ; i < instructions .size (); i ++) {
240252 Instruction instruction = instructions .get (i );
241253 out .append (" case " ).append (i ).append (": {\n " );
242- appendInstruction (out , method , instructions , labelToIndex , instruction , i );
254+ appendInstruction (out , method , instructions , labelToIndex , instruction , i , usesClassInitCache );
243255 out .append (" }\n " );
244256 }
245257 out .append (" default:\n " );
@@ -261,15 +273,66 @@ private static void appendMethod(StringBuilder out, ByteCodeClass cls, BytecodeM
261273 out .append (" }\n " );
262274 }
263275 out .append ("}\n " );
276+ if (wrappedStaticMethod ) {
277+ appendWrappedStaticMethod (out , cls , method , jsMethodName , jsMethodBodyName );
278+ }
264279 if ("__CLINIT__" .equals (method .getMethodName ())) {
265- out .append ("jvm.classes[\" " ).append (cls .getClsName ()).append ("\" ].clinit = " ).append (jsMethodName ).append (";\n " );
280+ out .append ("jvm.classes[\" " ).append (cls .getClsName ()).append ("\" ].clinit = " )
281+ .append (wrappedStaticMethod ? jsMethodBodyName : jsMethodName ).append (";\n " );
266282 }
267283 if (!method .isStatic () && !method .isConstructor ()) {
268284 out .append ("jvm.addVirtualMethod(\" " ).append (cls .getClsName ()).append ("\" , \" " )
269285 .append (jsMethodName ).append ("\" , " ).append (jsMethodName ).append (");\n " );
270286 }
271287 }
272288
289+ private static boolean isWrappedStaticMethod (BytecodeMethod method ) {
290+ return method .isStatic () && !"__CLINIT__" .equals (method .getMethodName ());
291+ }
292+
293+ private static void appendWrappedStaticMethod (StringBuilder out , ByteCodeClass cls , BytecodeMethod method , String wrapperName , String bodyName ) {
294+ out .append ("function* " ).append (wrapperName ).append ("(" );
295+ appendMethodParameters (out , method );
296+ out .append ("){\n " );
297+ out .append (" jvm.ensureClassInitialized(\" " ).append (cls .getClsName ()).append ("\" );\n " );
298+ out .append (" return yield* " ).append (bodyName ).append ("(" );
299+ appendMethodParameterArguments (out , method );
300+ out .append (");\n " );
301+ out .append ("}\n " );
302+ }
303+
304+ private static void appendMethodParameters (StringBuilder out , BytecodeMethod method ) {
305+ boolean first = true ;
306+ if (!method .isStatic ()) {
307+ out .append ("__cn1ThisObject" );
308+ first = false ;
309+ }
310+ List <ByteCodeMethodArg > arguments = method .getArguments ();
311+ for (int i = 0 ; i < arguments .size (); i ++) {
312+ if (!first ) {
313+ out .append (", " );
314+ }
315+ first = false ;
316+ out .append ("__cn1Arg" ).append (i + 1 );
317+ }
318+ }
319+
320+ private static void appendMethodParameterArguments (StringBuilder out , BytecodeMethod method ) {
321+ boolean first = true ;
322+ if (!method .isStatic ()) {
323+ out .append ("__cn1ThisObject" );
324+ first = false ;
325+ }
326+ List <ByteCodeMethodArg > arguments = method .getArguments ();
327+ for (int i = 0 ; i < arguments .size (); i ++) {
328+ if (!first ) {
329+ out .append (", " );
330+ }
331+ first = false ;
332+ out .append ("__cn1Arg" ).append (i + 1 );
333+ }
334+ }
335+
273336 private static boolean appendStraightLineMethodBody (StringBuilder out , ByteCodeClass cls , BytecodeMethod method ,
274337 List <Instruction > instructions , String jsMethodName ) {
275338 if (!isStraightLineEligible (method , instructions )) {
@@ -279,7 +342,7 @@ private static boolean appendStraightLineMethodBody(StringBuilder out, ByteCodeC
279342 StringBuilder instructionBody = new StringBuilder ();
280343 StringBuilder body = new StringBuilder ();
281344 StraightLineContext ctx = new StraightLineContext (method .getMaxLocals (), method .getMaxStack ());
282- if (method . isStatic () && ! "__CLINIT__" . equals ( method . getMethodName () )) {
345+ if (isWrappedStaticMethod ( method )) {
283346 ctx .initializedClasses .add (cls .getClsName ());
284347 }
285348 if (!method .isStatic ()) {
@@ -787,7 +850,9 @@ private static void appendStraightLineEnsureClassInitialized(StringBuilder out,
787850 }
788851
789852 private static boolean appendStraightLineInvokeInstruction (StringBuilder out , Invoke invoke , StraightLineContext ctx ) {
853+ String owner = JavascriptNameUtil .sanitizeClassName (invoke .getOwner ());
790854 String methodId = JavascriptNameUtil .methodIdentifier (invoke .getOwner (), invoke .getName (), invoke .getDesc ());
855+ String methodBodyId = jsStaticMethodBodyIdentifier (invoke .getOwner (), invoke .getName (), invoke .getDesc ());
791856 List <String > args = JavascriptNameUtil .argumentTypes (invoke .getDesc ());
792857 boolean hasReturn = invoke .getDesc ().charAt (invoke .getDesc ().length () - 1 ) != 'V' ;
793858 String [] argValues = new String [args .size ()];
@@ -824,10 +889,16 @@ private static boolean appendStraightLineInvokeInstruction(StringBuilder out, In
824889 out .append (" }\n " );
825890 return true ;
826891 }
892+ if (invoke .getOpcode () == Opcodes .INVOKESTATIC ) {
893+ appendStraightLineEnsureClassInitialized (out , ctx , owner );
894+ }
895+ String invokedName = invoke .getOpcode () == Opcodes .INVOKESTATIC
896+ ? "(" + staticInvocationTargetExpression (methodId , methodBodyId ) + ")"
897+ : methodId ;
827898 if (hasReturn ) {
828- out .append (" { const __result = yield* " ).append (methodId ).append ("(" );
899+ out .append (" { const __result = yield* " ).append (invokedName ).append ("(" );
829900 } else {
830- out .append (" { yield* " ).append (methodId ).append ("(" );
901+ out .append (" { yield* " ).append (invokedName ).append ("(" );
831902 }
832903 appendInvocationArgumentExpressions (out , target , argValues );
833904 out .append (");" );
@@ -934,6 +1005,17 @@ private static String jsMethodIdentifier(ByteCodeClass cls, BytecodeMethod metho
9341005 return JavascriptNameUtil .methodIdentifier (cls .getClsName (), method .getMethodName (), method .getSignature ());
9351006 }
9361007
1008+ private static String jsMethodBodyIdentifier (ByteCodeClass cls , BytecodeMethod method ) {
1009+ if (isWrappedStaticMethod (method )) {
1010+ return jsStaticMethodBodyIdentifier (cls .getClsName (), method .getMethodName (), method .getSignature ());
1011+ }
1012+ return jsMethodIdentifier (cls , method );
1013+ }
1014+
1015+ private static String jsStaticMethodBodyIdentifier (String owner , String name , String desc ) {
1016+ return JavascriptNameUtil .methodIdentifier (owner , name , desc ) + "__impl" ;
1017+ }
1018+
9371019 private static void appendNativeStubIfNeeded (StringBuilder out , ByteCodeClass cls , BytecodeMethod method ) {
9381020 String jsMethodName = jsMethodIdentifier (cls , method );
9391021 JavascriptNativeRegistry .NativeCategory category = JavascriptNativeRegistry .categoryFor (jsMethodName );
@@ -997,7 +1079,7 @@ private static Map<Label, Integer> buildLabelMap(List<Instruction> instructions)
9971079 }
9981080
9991081 private static void appendInstruction (StringBuilder out , BytecodeMethod method , List <Instruction > allInstructions ,
1000- Map <Label , Integer > labelToIndex , Instruction instruction , int index ) {
1082+ Map <Label , Integer > labelToIndex , Instruction instruction , int index , boolean usesClassInitCache ) {
10011083 if (instruction instanceof LabelInstruction || instruction instanceof LineNumber || instruction instanceof LocalVariable
10021084 || instruction instanceof TryCatch ) {
10031085 out .append (" pc = " ).append (index + 1 ).append ("; break;\n " );
@@ -1027,15 +1109,15 @@ private static void appendInstruction(StringBuilder out, BytecodeMethod method,
10271109 return ;
10281110 }
10291111 if (instruction instanceof Field ) {
1030- appendFieldInstruction (out , (Field ) instruction , index );
1112+ appendFieldInstruction (out , (Field ) instruction , index , usesClassInitCache );
10311113 return ;
10321114 }
10331115 if (instruction instanceof Jump ) {
10341116 appendJumpInstruction (out , (Jump ) instruction , labelToIndex , index );
10351117 return ;
10361118 }
10371119 if (instruction instanceof Invoke ) {
1038- appendInvokeInstruction (out , (Invoke ) instruction , index );
1120+ appendInvokeInstruction (out , (Invoke ) instruction , index , usesClassInitCache );
10391121 return ;
10401122 }
10411123 if (instruction instanceof SwitchInstruction ) {
@@ -1483,20 +1565,20 @@ private static void appendTypeInstruction(StringBuilder out, TypeInstruction ins
14831565 }
14841566 }
14851567
1486- private static void appendFieldInstruction (StringBuilder out , Field field , int index ) {
1568+ private static void appendFieldInstruction (StringBuilder out , Field field , int index , boolean usesStaticFieldInitCache ) {
14871569 String owner = JavascriptNameUtil .sanitizeClassName (field .getOwner ());
14881570 String fieldName = field .getFieldName ();
14891571 String propertyName = JavascriptNameUtil .fieldProperty (field .getOwner (), fieldName );
14901572 switch (field .getOpcode ()) {
14911573 case Opcodes .GETSTATIC :
1492- out . append ( " jvm.ensureClassInitialized( \" " ). append ( owner ). append ( " \" ); stack.push(jvm.classes[ \" " )
1493- . append ( owner ). append ( " \" ].staticFields [\" " ).append (fieldName ).append ("\" ]); pc = " )
1494- .append (index + 1 ).append ("; break;\n " );
1574+ appendInterpreterEnsureClassInitialized ( out , owner , usesStaticFieldInitCache );
1575+ out . append ( " stack.push(jvm.classes [\" " ).append (owner ).append ("\" ].staticFields[ \" " )
1576+ .append (fieldName ). append ( " \" ]); pc = " ). append ( index + 1 ).append ("; break;\n " );
14951577 return ;
14961578 case Opcodes .PUTSTATIC :
1497- out . append ( " jvm.ensureClassInitialized( \" " ). append ( owner ). append ( " \" ); jvm.classes[ \" " )
1498- . append (owner ).append ("\" ].staticFields[\" " ).append (fieldName ). append ( " \" ] = stack.pop(); pc = " )
1499- .append (index + 1 ).append ("; break;\n " );
1579+ appendInterpreterEnsureClassInitialized ( out , owner , usesStaticFieldInitCache );
1580+ out . append ( " jvm.classes[ \" " ). append (owner ).append ("\" ].staticFields[\" " ).append (fieldName )
1581+ .append (" \" ] = stack.pop(); pc = " ). append ( index + 1 ).append ("; break;\n " );
15001582 return ;
15011583 case Opcodes .GETFIELD :
15021584 out .append (" { const target = stack.pop(); stack.push(target[\" " ).append (propertyName )
@@ -1511,6 +1593,30 @@ private static void appendFieldInstruction(StringBuilder out, Field field, int i
15111593 }
15121594 }
15131595
1596+ private static void appendInterpreterEnsureClassInitialized (StringBuilder out , String owner , boolean usesStaticFieldInitCache ) {
1597+ if (usesStaticFieldInitCache ) {
1598+ out .append (" if (!__cn1Init[\" " ).append (owner ).append ("\" ]) { jvm.ensureClassInitialized(\" " )
1599+ .append (owner ).append ("\" ); __cn1Init[\" " ).append (owner ).append ("\" ] = true; }\n " );
1600+ return ;
1601+ }
1602+ out .append (" jvm.ensureClassInitialized(\" " ).append (owner ).append ("\" );\n " );
1603+ }
1604+
1605+ private static boolean hasClassInitSensitiveAccess (List <Instruction > instructions ) {
1606+ for (Instruction instruction : instructions ) {
1607+ if (instruction instanceof Field ) {
1608+ int opcode = instruction .getOpcode ();
1609+ if (opcode == Opcodes .GETSTATIC || opcode == Opcodes .PUTSTATIC ) {
1610+ return true ;
1611+ }
1612+ }
1613+ if (instruction instanceof Invoke && instruction .getOpcode () == Opcodes .INVOKESTATIC ) {
1614+ return true ;
1615+ }
1616+ }
1617+ return false ;
1618+ }
1619+
15141620 private static void appendJumpInstruction (StringBuilder out , Jump jump , Map <Label , Integer > labelToIndex , int index ) {
15151621 Integer target = labelToIndex .get (jump .getLabel ());
15161622 if (target == null ) {
@@ -1573,9 +1679,10 @@ private static void appendJumpInstruction(StringBuilder out, Jump jump, Map<Labe
15731679 }
15741680 }
15751681
1576- private static void appendInvokeInstruction (StringBuilder out , Invoke invoke , int index ) {
1682+ private static void appendInvokeInstruction (StringBuilder out , Invoke invoke , int index , boolean usesClassInitCache ) {
15771683 String owner = JavascriptNameUtil .sanitizeClassName (invoke .getOwner ());
15781684 String methodId = JavascriptNameUtil .methodIdentifier (invoke .getOwner (), invoke .getName (), invoke .getDesc ());
1685+ String methodBodyId = jsStaticMethodBodyIdentifier (invoke .getOwner (), invoke .getName (), invoke .getDesc ());
15791686 List <String > args = JavascriptNameUtil .argumentTypes (invoke .getDesc ());
15801687 boolean hasReturn = invoke .getDesc ().charAt (invoke .getDesc ().length () - 1 ) != 'V' ;
15811688 int argCount = args .size ();
@@ -1615,11 +1722,16 @@ private static void appendInvokeInstruction(StringBuilder out, Invoke invoke, in
16151722 appendInvocationArgumentBindings (out , argCount , " " , "stack.pop()" );
16161723 if (invoke .getOpcode () != Opcodes .INVOKESTATIC ) {
16171724 out .append (" const __target = stack.pop();\n " );
1725+ } else {
1726+ appendInterpreterEnsureClassInitialized (out , owner , usesClassInitCache );
16181727 }
1728+ String invokedName = invoke .getOpcode () == Opcodes .INVOKESTATIC
1729+ ? "(" + staticInvocationTargetExpression (methodId , methodBodyId ) + ")"
1730+ : methodId ;
16191731 if (hasReturn ) {
1620- out .append (" const __result = yield* " ).append (methodId ).append ("(" );
1732+ out .append (" const __result = yield* " ).append (invokedName ).append ("(" );
16211733 } else {
1622- out .append (" yield* " ).append (methodId ).append ("(" );
1734+ out .append (" yield* " ).append (invokedName ).append ("(" );
16231735 }
16241736 appendInvocationArguments (out , invoke .getOpcode () != Opcodes .INVOKESTATIC , argCount );
16251737 out .append (");\n " );
@@ -1650,4 +1762,8 @@ private static void appendInvocationArgumentBindings(StringBuilder out, int argC
16501762 out .append (indent ).append ("const __arg" ).append (i ).append (" = " ).append (sourceExpression ).append (";\n " );
16511763 }
16521764 }
1765+
1766+ private static String staticInvocationTargetExpression (String methodId , String methodBodyId ) {
1767+ return "typeof " + methodBodyId + " === \" function\" ? " + methodBodyId + " : " + methodId ;
1768+ }
16531769}
0 commit comments