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() { |