@@ -219,6 +219,7 @@ private static void appendMethod(StringBuilder out, ByteCodeClass cls, BytecodeM
219219 return ;
220220 }
221221 boolean usesClassInitCache = hasClassInitSensitiveAccess (instructions );
222+ boolean usesVirtualDispatchCache = hasVirtualDispatchAccess (instructions );
222223 out .append (" const locals = new Array(" ).append (Math .max (1 , method .getMaxLocals ())).append (").fill(null);\n " );
223224 out .append (" const stack = [];\n " );
224225 out .append (" let pc = 0;\n " );
@@ -228,6 +229,9 @@ private static void appendMethod(StringBuilder out, ByteCodeClass cls, BytecodeM
228229 out .append (" __cn1Init[\" " ).append (cls .getClsName ()).append ("\" ] = true;\n " );
229230 }
230231 }
232+ if (usesVirtualDispatchCache ) {
233+ out .append (" const __cn1Virtual = Object.create(null);\n " );
234+ }
231235 if (!method .isStatic ()) {
232236 out .append (" locals[0] = __cn1ThisObject;\n " );
233237 }
@@ -251,7 +255,7 @@ private static void appendMethod(StringBuilder out, ByteCodeClass cls, BytecodeM
251255 for (int i = 0 ; i < instructions .size (); i ++) {
252256 Instruction instruction = instructions .get (i );
253257 out .append (" case " ).append (i ).append (": {\n " );
254- appendInstruction (out , method , instructions , labelToIndex , instruction , i , usesClassInitCache );
258+ appendInstruction (out , method , instructions , labelToIndex , instruction , i , usesClassInitCache , usesVirtualDispatchCache );
255259 out .append (" }\n " );
256260 }
257261 out .append (" default:\n " );
@@ -799,13 +803,12 @@ private static boolean appendStraightLineTypeInstruction(StringBuilder out, Type
799803 }
800804 case Opcodes .CHECKCAST : {
801805 String value = ctx .peek (0 );
802- out .append (" if (" ).append (value ).append (" != null && !jvm.instanceOf(" ).append (value ).append (", \" " )
803- .append (typeName ).append ("\" )) throw new Error(\" ClassCastException\" );\n " );
806+ appendDirectCheckCast (out , " " , value , typeName , "throw new Error(\" ClassCastException\" )" );
804807 return true ;
805808 }
806809 case Opcodes .INSTANCEOF : {
807810 String value = ctx .pop ();
808- out .append (" " ).append (ctx .push ("(jvm.instanceOf(" + value + ", \" " + typeName + "\" ) ? 1 : 0) " )).append (";\n " );
811+ out .append (" " ).append (ctx .push (directInstanceOfExpression ( value , typeName ) + " ? 1 : 0" )).append (";\n " );
809812 return true ;
810813 }
811814 default :
@@ -874,7 +877,8 @@ private static boolean appendStraightLineInvokeInstruction(StringBuilder out, In
874877 if (invoke .getOpcode () == Opcodes .INVOKEVIRTUAL || invoke .getOpcode () == Opcodes .INVOKEINTERFACE ) {
875878 out .append (" {\n " );
876879 out .append (" const __target = " ).append (target ).append (";\n " );
877- out .append (" const __method = ((jvm.classes[__target.__class] && jvm.classes[__target.__class].methods) ? jvm.classes[__target.__class].methods[\" " ).append (methodId )
880+ out .append (" const __classDef = __target.__classDef;\n " );
881+ out .append (" const __method = ((__classDef && __classDef.methods) ? __classDef.methods[\" " ).append (methodId )
878882 .append ("\" ] : null) || jvm.resolveVirtual(__target.__class, \" " ).append (methodId ).append ("\" );\n " );
879883 if (hasReturn ) {
880884 out .append (" const __result = yield* __method(" );
@@ -1079,7 +1083,8 @@ private static Map<Label, Integer> buildLabelMap(List<Instruction> instructions)
10791083 }
10801084
10811085 private static void appendInstruction (StringBuilder out , BytecodeMethod method , List <Instruction > allInstructions ,
1082- Map <Label , Integer > labelToIndex , Instruction instruction , int index , boolean usesClassInitCache ) {
1086+ Map <Label , Integer > labelToIndex , Instruction instruction , int index , boolean usesClassInitCache ,
1087+ boolean usesVirtualDispatchCache ) {
10831088 if (instruction instanceof LabelInstruction || instruction instanceof LineNumber || instruction instanceof LocalVariable
10841089 || instruction instanceof TryCatch ) {
10851090 out .append (" pc = " ).append (index + 1 ).append ("; break;\n " );
@@ -1117,7 +1122,7 @@ private static void appendInstruction(StringBuilder out, BytecodeMethod method,
11171122 return ;
11181123 }
11191124 if (instruction instanceof Invoke ) {
1120- appendInvokeInstruction (out , (Invoke ) instruction , index , usesClassInitCache );
1125+ appendInvokeInstruction (out , (Invoke ) instruction , index , usesClassInitCache , usesVirtualDispatchCache );
11211126 return ;
11221127 }
11231128 if (instruction instanceof SwitchInstruction ) {
@@ -1552,19 +1557,32 @@ private static void appendTypeInstruction(StringBuilder out, TypeInstruction ins
15521557 .append ("\" , 1)); pc = " ).append (index + 1 ).append ("; break; }\n " );
15531558 return ;
15541559 case Opcodes .CHECKCAST :
1555- out .append (" { const value = stack[stack.length - 1]; if (value != null && !jvm.instanceOf(value, \" " )
1556- . append ( typeName )
1557- .append ("\" )) throw new Error( \" ClassCastException \" ); pc = " ).append (index + 1 ).append ("; break; }\n " );
1560+ out .append (" { const value = stack[stack.length - 1]; " );
1561+ appendDirectCheckCast ( out , "" , "value" , typeName , "throw new Error( \" ClassCastException \" )" );
1562+ out .append (" pc = " ).append (index + 1 ).append ("; break; }\n " );
15581563 return ;
15591564 case Opcodes .INSTANCEOF :
1560- out .append (" { const value = stack.pop(); stack.push(jvm.instanceOf(value, \" " ).append (typeName )
1561- .append ("\" ) ? 1 : 0); pc = " ).append (index + 1 ).append ("; break; }\n " );
1565+ out .append (" { const value = stack.pop(); stack.push(" ).append (directInstanceOfExpression ( "value" , typeName ) )
1566+ .append (" ? 1 : 0); pc = " ).append (index + 1 ).append ("; break; }\n " );
15621567 return ;
15631568 default :
15641569 throw new IllegalArgumentException ("Unsupported type opcode " + instruction .getOpcode ());
15651570 }
15661571 }
15671572
1573+ private static void appendDirectCheckCast (StringBuilder out , String indent , String valueExpression , String typeName , String failureStatement ) {
1574+ out .append (indent ).append ("if (" ).append (valueExpression ).append (" != null) { const __classDef = " ).append (valueExpression )
1575+ .append (".__classDef; if (" ).append (valueExpression ).append (".__class !== \" " ).append (typeName )
1576+ .append ("\" && !(__classDef && __classDef.assignableTo && __classDef.assignableTo[\" " ).append (typeName )
1577+ .append ("\" ])) " ).append (failureStatement ).append ("; }\n " );
1578+ }
1579+
1580+ private static String directInstanceOfExpression (String valueExpression , String typeName ) {
1581+ return "(" + valueExpression + " != null && (" + valueExpression + ".__class === \" " + typeName
1582+ + "\" || (" + valueExpression + ".__classDef && " + valueExpression + ".__classDef.assignableTo && "
1583+ + valueExpression + ".__classDef.assignableTo[\" " + typeName + "\" ])))" ;
1584+ }
1585+
15681586 private static void appendFieldInstruction (StringBuilder out , Field field , int index , boolean usesStaticFieldInitCache ) {
15691587 String owner = JavascriptNameUtil .sanitizeClassName (field .getOwner ());
15701588 String fieldName = field .getFieldName ();
@@ -1617,6 +1635,18 @@ private static boolean hasClassInitSensitiveAccess(List<Instruction> instruction
16171635 return false ;
16181636 }
16191637
1638+ private static boolean hasVirtualDispatchAccess (List <Instruction > instructions ) {
1639+ for (Instruction instruction : instructions ) {
1640+ if (instruction instanceof Invoke ) {
1641+ int opcode = instruction .getOpcode ();
1642+ if (opcode == Opcodes .INVOKEVIRTUAL || opcode == Opcodes .INVOKEINTERFACE ) {
1643+ return true ;
1644+ }
1645+ }
1646+ }
1647+ return false ;
1648+ }
1649+
16201650 private static void appendJumpInstruction (StringBuilder out , Jump jump , Map <Label , Integer > labelToIndex , int index ) {
16211651 Integer target = labelToIndex .get (jump .getLabel ());
16221652 if (target == null ) {
@@ -1679,7 +1709,8 @@ private static void appendJumpInstruction(StringBuilder out, Jump jump, Map<Labe
16791709 }
16801710 }
16811711
1682- private static void appendInvokeInstruction (StringBuilder out , Invoke invoke , int index , boolean usesClassInitCache ) {
1712+ private static void appendInvokeInstruction (StringBuilder out , Invoke invoke , int index , boolean usesClassInitCache ,
1713+ boolean usesVirtualDispatchCache ) {
16831714 String owner = JavascriptNameUtil .sanitizeClassName (invoke .getOwner ());
16841715 String methodId = JavascriptNameUtil .methodIdentifier (invoke .getOwner (), invoke .getName (), invoke .getDesc ());
16851716 String methodBodyId = jsStaticMethodBodyIdentifier (invoke .getOwner (), invoke .getName (), invoke .getDesc ());
@@ -1701,8 +1732,21 @@ private static void appendInvokeInstruction(StringBuilder out, Invoke invoke, in
17011732 out .append (" {\n " );
17021733 appendInvocationArgumentBindings (out , argCount , " " , "stack.pop()" );
17031734 out .append (" const __target = stack.pop();\n " );
1704- out .append (" const __method = ((jvm.classes[__target.__class] && jvm.classes[__target.__class].methods) ? jvm.classes[__target.__class].methods[\" " ).append (methodId )
1705- .append ("\" ] : null) || jvm.resolveVirtual(__target.__class, \" " ).append (methodId ).append ("\" );\n " );
1735+ out .append (" const __classDef = __target.__classDef;\n " );
1736+ out .append (" let __method = (__classDef && __classDef.methods) ? __classDef.methods[\" " ).append (methodId )
1737+ .append ("\" ] : null;\n " );
1738+ if (usesVirtualDispatchCache ) {
1739+ out .append (" if (!__method) {\n " );
1740+ out .append (" const __cacheKey = __target.__class + \" |" ).append (methodId ).append ("\" ;\n " );
1741+ out .append (" __method = __cn1Virtual[__cacheKey];\n " );
1742+ out .append (" if (!__method) {\n " );
1743+ out .append (" __method = jvm.resolveVirtual(__target.__class, \" " ).append (methodId ).append ("\" );\n " );
1744+ out .append (" __cn1Virtual[__cacheKey] = __method;\n " );
1745+ out .append (" }\n " );
1746+ out .append (" }\n " );
1747+ } else {
1748+ out .append (" if (!__method) __method = jvm.resolveVirtual(__target.__class, \" " ).append (methodId ).append ("\" );\n " );
1749+ }
17061750 if (hasReturn ) {
17071751 out .append (" const __result = yield* __method(" );
17081752 appendInvocationArguments (out , true , argCount );
0 commit comments