nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java
changeset 29283 fb47e4d25a9f
parent 29281 8cc2618a07aa
child 29410 cdfd8fbb2b1d
equal deleted inserted replaced
29282:a8523237b66c 29283:fb47e4d25a9f
   200     private static final Call CREATE_FUNCTION_OBJECT = CompilerConstants.staticCallNoLookup(ScriptFunctionImpl.class,
   200     private static final Call CREATE_FUNCTION_OBJECT = CompilerConstants.staticCallNoLookup(ScriptFunctionImpl.class,
   201             "create", ScriptFunction.class, Object[].class, int.class, ScriptObject.class);
   201             "create", ScriptFunction.class, Object[].class, int.class, ScriptObject.class);
   202     private static final Call CREATE_FUNCTION_OBJECT_NO_SCOPE = CompilerConstants.staticCallNoLookup(ScriptFunctionImpl.class,
   202     private static final Call CREATE_FUNCTION_OBJECT_NO_SCOPE = CompilerConstants.staticCallNoLookup(ScriptFunctionImpl.class,
   203             "create", ScriptFunction.class, Object[].class, int.class);
   203             "create", ScriptFunction.class, Object[].class, int.class);
   204 
   204 
       
   205     private static final Call TO_NUMBER_FOR_EQ = CompilerConstants.staticCallNoLookup(JSType.class,
       
   206             "toNumberForEq", double.class, Object.class);
       
   207     private static final Call TO_NUMBER_FOR_STRICT_EQ = CompilerConstants.staticCallNoLookup(JSType.class,
       
   208             "toNumberForStrictEq", double.class, Object.class);
       
   209 
       
   210 
   205     private static final Class<?> ITERATOR_CLASS = Iterator.class;
   211     private static final Class<?> ITERATOR_CLASS = Iterator.class;
   206     static {
   212     static {
   207         assert ITERATOR_CLASS == CompilerConstants.ITERATOR_PREFIX.type();
   213         assert ITERATOR_CLASS == CompilerConstants.ITERATOR_PREFIX.type();
   208     }
   214     }
   209     private static final Type ITERATOR_TYPE = Type.typeFor(ITERATOR_CLASS);
   215     private static final Type ITERATOR_TYPE = Type.typeFor(ITERATOR_CLASS);
   616         assert Type.generic(method.peekType(1)) == operandBounds.narrowest;
   622         assert Type.generic(method.peekType(1)) == operandBounds.narrowest;
   617 
   623 
   618         return method;
   624         return method;
   619     }
   625     }
   620 
   626 
       
   627     /**
       
   628      * Similar to {@link #loadBinaryOperands(BinaryNode)} but used specifically for loading operands of
       
   629      * relational and equality comparison operators where at least one argument is non-object. (When both
       
   630      * arguments are objects, we use {@link ScriptRuntime#EQ(Object, Object)}, {@link ScriptRuntime#LT(Object, Object)}
       
   631      * etc. methods instead. Additionally, {@code ScriptRuntime} methods are used for strict (in)equality comparison
       
   632      * of a boolean to anything that isn't a boolean.) This method handles the special case where one argument
       
   633      * is an object and another is a primitive. Naively, these could also be delegated to {@code ScriptRuntime} methods
       
   634      * by boxing the primitive. However, in all such cases the comparison is performed on numeric values, so it is
       
   635      * possible to strength-reduce the operation by taking the number value of the object argument instead and
       
   636      * comparing that to the primitive value ("primitive" will always be int, long, double, or boolean, and booleans
       
   637      * compare as ints in these cases, so they're essentially numbers too). This method will emit code for loading
       
   638      * arguments for such strength-reduced comparison. When both arguments are primitives, it just delegates to
       
   639      * {@link #loadBinaryOperands(BinaryNode)}.
       
   640      *
       
   641      * @param cmp the comparison operation for which the operands need to be loaded on stack.
       
   642      * @return the current method emitter.
       
   643      */
       
   644     MethodEmitter loadComparisonOperands(final BinaryNode cmp) {
       
   645         final Expression lhs = cmp.lhs();
       
   646         final Expression rhs = cmp.rhs();
       
   647         final Type lhsType = lhs.getType();
       
   648         final Type rhsType = rhs.getType();
       
   649 
       
   650         // Only used when not both are object, for that we have ScriptRuntime.LT etc.
       
   651         assert !(lhsType.isObject() && rhsType.isObject());
       
   652 
       
   653         if (lhsType.isObject() || rhsType.isObject()) {
       
   654             // We can reorder CONVERT LEFT and LOAD RIGHT only if either the left is a primitive, or the right
       
   655             // is a local. This is more strict than loadBinaryNode reorder criteria, as it can allow JS primitive
       
   656             // types too (notably: String is a JS primitive, but not a JVM primitive). We disallow String otherwise
       
   657             // we would prematurely convert it to number when comparing to an optimistic expression, e.g. in
       
   658             // "Hello" === String("Hello") the RHS starts out as an optimistic-int function call. If we allowed
       
   659             // reordering, we'd end up with ToNumber("Hello") === {I%}String("Hello") that is obviously incorrect.
       
   660             final boolean canReorder = lhsType.isPrimitive() || rhs.isLocal();
       
   661             // If reordering is allowed, and we're using a relational operator (that is, <, <=, >, >=) and not an
       
   662             // (in)equality operator, then we encourage combining of LOAD and CONVERT into a single operation.
       
   663             // This is because relational operators' semantics prescribes vanilla ToNumber() conversion, while
       
   664             // (in)equality operators need the specialized JSType.toNumberFor[Strict]Equals. E.g. in the code snippet
       
   665             // "i < obj.size" (where i is primitive and obj.size is statically an object), ".size" will thus be allowed
       
   666             // to compile as:
       
   667             //   invokedynamic dyn:getProp|getElem|getMethod:size(Object;)D
       
   668             // instead of the more costly:
       
   669             //   invokedynamic dyn:getProp|getElem|getMethod:size(Object;)Object
       
   670             //   invokestatic JSType.toNumber(Object)D
       
   671             // Note also that even if this is allowed, we're only using it on operands that are non-optimistic, as
       
   672             // otherwise the logic for determining effective optimistic-ness would turn an optimistic double return
       
   673             // into a freely coercible one, which would be wrong.
       
   674             final boolean canCombineLoadAndConvert = canReorder && cmp.isRelational();
       
   675 
       
   676             // LOAD LEFT
       
   677             loadExpression(lhs, canCombineLoadAndConvert && !lhs.isOptimistic() ? TypeBounds.NUMBER : TypeBounds.UNBOUNDED);
       
   678 
       
   679             final Type lhsLoadedType = method.peekType();
       
   680             final TokenType tt = cmp.tokenType();
       
   681             if (canReorder) {
       
   682                 // Can reorder CONVERT LEFT and LOAD RIGHT
       
   683                 emitObjectToNumberComparisonConversion(method, tt);
       
   684                 loadExpression(rhs, canCombineLoadAndConvert && !rhs.isOptimistic() ? TypeBounds.NUMBER : TypeBounds.UNBOUNDED);
       
   685             } else {
       
   686                 // Can't reorder CONVERT LEFT and LOAD RIGHT
       
   687                 loadExpression(rhs, TypeBounds.UNBOUNDED);
       
   688                 if (lhsLoadedType != Type.NUMBER) {
       
   689                     method.swap();
       
   690                     emitObjectToNumberComparisonConversion(method, tt);
       
   691                     method.swap();
       
   692                 }
       
   693             }
       
   694 
       
   695             // CONVERT RIGHT
       
   696             emitObjectToNumberComparisonConversion(method, tt);
       
   697             return method;
       
   698         }
       
   699         // For primitive operands, just don't do anything special.
       
   700         return loadBinaryOperands(cmp);
       
   701     }
       
   702 
       
   703     private static void emitObjectToNumberComparisonConversion(final MethodEmitter method, final TokenType tt) {
       
   704         switch(tt) {
       
   705         case EQ:
       
   706         case NE:
       
   707             if (method.peekType().isObject()) {
       
   708                 TO_NUMBER_FOR_EQ.invoke(method);
       
   709                 return;
       
   710             }
       
   711             break;
       
   712         case EQ_STRICT:
       
   713         case NE_STRICT:
       
   714             if (method.peekType().isObject()) {
       
   715                 TO_NUMBER_FOR_STRICT_EQ.invoke(method);
       
   716                 return;
       
   717             }
       
   718             break;
       
   719         default:
       
   720             break;
       
   721         }
       
   722         method.convert(Type.NUMBER);
       
   723     }
       
   724 
   621     private static final Type undefinedToNumber(final Type type) {
   725     private static final Type undefinedToNumber(final Type type) {
   622         return type == Type.UNDEFINED ? Type.NUMBER : type;
   726         return type == Type.UNDEFINED ? Type.NUMBER : type;
   623     }
   727     }
   624 
   728 
   625     private static final class TypeBounds {
   729     private static final class TypeBounds {
   626         final Type narrowest;
   730         final Type narrowest;
   627         final Type widest;
   731         final Type widest;
   628 
   732 
   629         static final TypeBounds UNBOUNDED = new TypeBounds(Type.UNKNOWN, Type.OBJECT);
   733         static final TypeBounds UNBOUNDED = new TypeBounds(Type.UNKNOWN, Type.OBJECT);
   630         static final TypeBounds INT = exact(Type.INT);
   734         static final TypeBounds INT = exact(Type.INT);
       
   735         static final TypeBounds NUMBER = exact(Type.NUMBER);
   631         static final TypeBounds OBJECT = exact(Type.OBJECT);
   736         static final TypeBounds OBJECT = exact(Type.OBJECT);
   632         static final TypeBounds BOOLEAN = exact(Type.BOOLEAN);
   737         static final TypeBounds BOOLEAN = exact(Type.BOOLEAN);
   633 
   738 
   634         static TypeBounds exact(final Type type) {
   739         static TypeBounds exact(final Type type) {
   635             return new TypeBounds(type, type);
   740             return new TypeBounds(type, type);
  2754             newRuntimeNode = runtimeNode.setRequest(request == Request.IS_UNDEFINED ? Request.EQ_STRICT : Request.NE_STRICT);
  2859             newRuntimeNode = runtimeNode.setRequest(request == Request.IS_UNDEFINED ? Request.EQ_STRICT : Request.NE_STRICT);
  2755         } else {
  2860         } else {
  2756             newRuntimeNode = runtimeNode;
  2861             newRuntimeNode = runtimeNode;
  2757         }
  2862         }
  2758 
  2863 
  2759         new OptimisticOperation(newRuntimeNode, TypeBounds.UNBOUNDED) {
  2864         for (final Expression arg : args) {
  2760             @Override
  2865             loadExpression(arg, TypeBounds.OBJECT);
  2761             void loadStack() {
  2866         }
  2762                 for (final Expression arg : args) {
  2867 
  2763                     loadExpression(arg, TypeBounds.OBJECT);
  2868         method.invokestatic(
  2764                 }
  2869                 CompilerConstants.className(ScriptRuntime.class),
  2765             }
  2870                 newRuntimeNode.getRequest().toString(),
  2766             @Override
  2871                 new FunctionSignature(
  2767             void consumeStack() {
  2872                     false,
  2768                 method.invokestatic(
  2873                     false,
  2769                         CompilerConstants.className(ScriptRuntime.class),
  2874                     newRuntimeNode.getType(),
  2770                         newRuntimeNode.getRequest().toString(),
  2875                     args.size()).toString());
  2771                         new FunctionSignature(
       
  2772                             false,
       
  2773                             false,
       
  2774                             newRuntimeNode.getType(),
       
  2775                             args.size()).toString());
       
  2776             }
       
  2777         }.emit();
       
  2778 
  2876 
  2779         method.convert(newRuntimeNode.getType());
  2877         method.convert(newRuntimeNode.getType());
  2780     }
  2878     }
  2781 
  2879 
  2782     private void defineCommonSplitMethodParameters() {
  2880     private void defineCommonSplitMethodParameters() {
  3987             }
  4085             }
  3988         }.evaluate(binaryNode, resultBounds);
  4086         }.evaluate(binaryNode, resultBounds);
  3989     }
  4087     }
  3990 
  4088 
  3991     private void loadCmp(final BinaryNode binaryNode, final Condition cond) {
  4089     private void loadCmp(final BinaryNode binaryNode, final Condition cond) {
  3992         assert comparisonOperandsArePrimitive(binaryNode) : binaryNode;
  4090         loadComparisonOperands(binaryNode);
  3993         loadBinaryOperands(binaryNode);
       
  3994 
  4091 
  3995         final Label trueLabel  = new Label("trueLabel");
  4092         final Label trueLabel  = new Label("trueLabel");
  3996         final Label afterLabel = new Label("skip");
  4093         final Label afterLabel = new Label("skip");
  3997 
  4094 
  3998         method.conditionalJump(cond, trueLabel);
  4095         method.conditionalJump(cond, trueLabel);
  4000         method.load(Boolean.FALSE);
  4097         method.load(Boolean.FALSE);
  4001         method._goto(afterLabel);
  4098         method._goto(afterLabel);
  4002         method.label(trueLabel);
  4099         method.label(trueLabel);
  4003         method.load(Boolean.TRUE);
  4100         method.load(Boolean.TRUE);
  4004         method.label(afterLabel);
  4101         method.label(afterLabel);
  4005     }
       
  4006 
       
  4007     private static boolean comparisonOperandsArePrimitive(final BinaryNode binaryNode) {
       
  4008         final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
       
  4009         return widest.isNumeric() || widest.isBoolean();
       
  4010     }
  4102     }
  4011 
  4103 
  4012     private void loadMOD(final BinaryNode binaryNode, final TypeBounds resultBounds) {
  4104     private void loadMOD(final BinaryNode binaryNode, final TypeBounds resultBounds) {
  4013         new BinaryArith() {
  4105         new BinaryArith() {
  4014             @Override
  4106             @Override