diff --git a/pkg/compiler/lib/src/cps_ir/backward_null_check_remover.dart b/pkg/compiler/lib/src/cps_ir/backward_null_check_remover.dart index b55a433ffa2..6a2c16fac20 100644 --- a/pkg/compiler/lib/src/cps_ir/backward_null_check_remover.dart +++ b/pkg/compiler/lib/src/cps_ir/backward_null_check_remover.dart @@ -19,11 +19,11 @@ import 'cps_fragment.dart'; /// /// print(x.length); /// -/// `x.length` will throw when x is null, so the original [NullCheck] is not +/// `x.length` will throw when x is null, so the original [ReceiverCheck] is not /// needed. This changes the error message, but at least for now we are /// willing to accept this. /// -/// Note that code motion may not occur after this pass, since the [NullCheck] +/// Note that code motion may not occur after this pass, since the [ReceiverCheck] /// nodes are not there to restrict it. // // TODO(asgerf): It would be nice with a clear specification of when we allow @@ -53,7 +53,7 @@ class BackwardNullCheckRemover extends BlockVisitor implements Pass { /// Returns a reference to an operand of [prim], where [prim] throws if null /// is passed into that operand. Reference getNullCheckedOperand(Primitive prim) { - if (prim is NullCheck) return prim.value; + if (prim is ReceiverCheck) return prim.value; if (prim is GetLength) return prim.object; if (prim is GetField) return prim.object; if (prim is GetIndex) return prim.object; @@ -71,7 +71,7 @@ class BackwardNullCheckRemover extends BlockVisitor implements Pass { /// It has been determined that the null check in [prim] made redundant by /// [newNullCheck]. Eliminate [prim] if it is not needed any more. void tryEliminateRedundantNullCheck(Primitive prim, Primitive newNullCheck) { - if (prim is NullCheck) { + if (prim is ReceiverCheck && prim.isNullCheck) { Primitive value = prim.value.definition; LetPrim let = prim.parent; prim..replaceUsesWith(value)..destroy(); diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart index 4395c00995b..7cb92608d54 100644 --- a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart +++ b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart @@ -260,7 +260,7 @@ abstract class Primitive extends Variable { // TODO(johnniwinther): Require source information for all primitives. SourceInformation get sourceInformation => null; - /// If this is a [Refinement], [BoundsCheck] or [NullCheck] node, returns the + /// If this is a [Refinement], [BoundsCheck] or [ReceiverCheck] node, returns the /// value being refined, the indexable object being checked, or the value /// that was checked to be non-null, respectively. /// @@ -805,7 +805,7 @@ class Refinement extends Primitive { class BoundsCheck extends Primitive { final Reference object; Reference index; - Reference length; // FIXME write docs for length + Reference length; int checks; final SourceInformation sourceInformation; @@ -879,53 +879,99 @@ class BoundsCheck extends Primitive { Primitive get effectiveDefinition => object.definition.effectiveDefinition; } -/// Throw an exception if [value] is `null`. +/// Throw a [NoSuchMethodError] if [value] cannot respond to [selector]. /// /// Returns [value] so this can be used to restrict code motion. /// -/// In the simplest form this compiles to `value.toString;`. +/// The check can take one of three forms: /// -/// [selector] holds the selector that is the cause of the null check. This is -/// usually a method that was inlined where [value] the receiver. +/// value.toString; +/// value.selectorName; +/// value.selectorName(); (should only be used if check always fails) /// -/// If [selector] is set and [useSelector] is true, `toString` is replaced with -/// the (possibly minified) invocation name of the selector. This can be -/// shorter and generate a more meaningful error message, but is expensive if -/// [value] is non-null and does not have that property at runtime. +/// The first two forms are used when it is known that only null fails the +/// check. Additionally, the check may be guarded by a [condition], allowing +/// for three more forms: /// -/// If [condition] is set, it is assumed that [condition] is true if and only -/// if [value] is null. The check then compiles to: +/// if (condition) value.toString; (this form is valid but unused) +/// if (condition) value.selectorName; +/// if (condition) value.selectorName(); /// -/// if (condition) value.toString; (or .selector if non-null) +/// The condition must be true if and only if the check should fail. It should +/// ideally be of a form understood by JS engines, e.g. a `typeof` test. /// -/// The latter form is useful when [condition] is a form understood by the JS -/// runtime, such as a `typeof` test. -class NullCheck extends Primitive { +/// If [useSelector] is false, the first form instead becomes `value.toString;`. +/// This form is faster when the value is non-null and the accessed property has +/// been removed by tree shaking. +/// +/// [selector] may not be one of the selectors implemented by the null object. +class ReceiverCheck extends Primitive { final Reference value; final Selector selector; - final bool useSelector; - final Reference condition; final SourceInformation sourceInformation; + final Reference condition; + final int _flags; - NullCheck(Primitive value, this.sourceInformation, - {Primitive condition, - this.selector, - this.useSelector: false}) - : this.value = new Reference(value), - this.condition = - condition == null ? null : new Reference(condition); + static const int _USE_SELECTOR = 1 << 0; + static const int _NULL_CHECK = 1 << 1; - NullCheck.guarded(Primitive condition, Primitive value, this.selector, - this.sourceInformation) - : this.condition = new Reference(condition), - this.value = new Reference(value), - this.useSelector = true; + /// True if the selector name should be used in the check; otherwise + /// `toString` will be used. + bool get useSelector => _flags & _USE_SELECTOR != 0; + + /// True if null is the only possible input that cannot respond to [selector]. + bool get isNullCheck => _flags & _NULL_CHECK != 0; + + + /// Constructor for creating checks in arbitrary configurations. + /// + /// Consider using one of the named constructors instead. + /// + /// [useSelector] and [isNullCheck] are mandatory named arguments. + ReceiverCheck(Primitive value, this.selector, this.sourceInformation, + {Primitive condition, bool useSelector, bool isNullCheck}) + : value = new Reference(value), + condition = _optionalReference(condition), + _flags = (useSelector ? _USE_SELECTOR : 0) | + (isNullCheck ? _NULL_CHECK : 0); + + /// Simplified constructor for building null checks. + /// + /// Null must be the only possible input value that does not respond to + /// [selector]. + ReceiverCheck.nullCheck( + Primitive value, + Selector selector, + SourceInformation sourceInformation, + {Primitive condition}) + : this(value, + selector, + sourceInformation, + condition: condition, + useSelector: condition != null, + isNullCheck: true); + + /// Simplified constructor for building the general check of form: + /// + /// if (condition) value.selectorName(); + /// + ReceiverCheck.generalCheck( + Primitive value, + Selector selector, + SourceInformation sourceInformation, + Primitive condition) + : this(value, + selector, + sourceInformation, + condition: condition, + useSelector: true, + isNullCheck: false); bool get isSafeForElimination => false; bool get isSafeForReordering => false; bool get hasValue => true; - accept(Visitor visitor) => visitor.visitNullCheck(this); + accept(Visitor visitor) => visitor.visitReceiverCheck(this); void setParentPointers() { value.parent = this; @@ -935,6 +981,10 @@ class NullCheck extends Primitive { } Primitive get effectiveDefinition => value.definition.effectiveDefinition; + + String get nullCheckString => isNullCheck ? 'null-check' : 'general-check'; + String get useSelectorString => useSelector ? 'use-selector' : 'no-selector'; + String get flagString => '$nullCheckString $useSelectorString'; } /// An "is" type test. @@ -1930,6 +1980,16 @@ class Yield extends UnsafePrimitive { } } +Reference _reference(Primitive definition) { + return new Reference(definition); +} + +Reference _optionalReference(Primitive definition) { + return definition == null + ? null + : new Reference(definition); +} + List> _referenceList(Iterable definitions) { return definitions.map((e) => new Reference(e)).toList(); } @@ -2079,7 +2139,7 @@ abstract class Visitor implements BlockVisitor { T visitSetIndex(SetIndex node); T visitRefinement(Refinement node); T visitBoundsCheck(BoundsCheck node); - T visitNullCheck(NullCheck node); + T visitReceiverCheck(ReceiverCheck node); T visitForeignCode(ForeignCode node); } @@ -2402,8 +2462,8 @@ class DeepRecursiveVisitor implements Visitor { } } - processNullCheck(NullCheck node) {} - visitNullCheck(NullCheck node) { + processNullCheck(ReceiverCheck node) {} + visitReceiverCheck(ReceiverCheck node) { processNullCheck(node); processReference(node.value); if (node.condition != null) { @@ -2763,11 +2823,13 @@ class DefinitionCopyingVisitor extends Visitor { } } - Definition visitNullCheck(NullCheck node) { - return new NullCheck(getCopy(node.value), node.sourceInformation, + Definition visitReceiverCheck(ReceiverCheck node) { + return new ReceiverCheck(getCopy(node.value), + node.selector, + node.sourceInformation, condition: node.condition == null ? null : getCopy(node.condition), - selector: node.selector, - useSelector: node.useSelector); + useSelector: node.useSelector, + isNullCheck: node.isNullCheck); } Definition visitForeignCode(ForeignCode node) { diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart index 37836bf7717..952f881fcc9 100644 --- a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart +++ b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart @@ -448,10 +448,11 @@ class SExpressionStringifier extends Indentation implements Visitor { return '(BoundsCheck $object $index $length ${node.checkString})'; } - String visitNullCheck(NullCheck node) { + String visitReceiverCheck(ReceiverCheck node) { String value = access(node.value); String condition = optionalAccess(node.condition); - return '(NullCheck $value $condition (${node.selector ?? ""}))'; + return '(ReceiverCheck $value ${node.selector} $condition ' + '${node.flagString}))'; } } diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart index db1592db321..dd69e3e497e 100644 --- a/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart +++ b/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart @@ -393,10 +393,11 @@ class IRTracer extends TracerUtil implements cps_ir.Visitor { return 'BoundsCheck $object $index $length ${node.checkString}'; } - visitNullCheck(cps_ir.NullCheck node) { + visitReceiverCheck(cps_ir.ReceiverCheck node) { String value = formatReference(node.value); String condition = formatReference(node.condition); - return 'NullCheck $value condition:$condition selector:${node.selector}'; + return 'ReceiverCheck $value $condition ${node.selector} ' + '${node.flagString}'; } } @@ -681,7 +682,7 @@ class BlockCollector implements cps_ir.Visitor { unexpectedNode(node); } - visitNullCheck(cps_ir.NullCheck node) { + visitReceiverCheck(cps_ir.ReceiverCheck node) { unexpectedNode(node); } } diff --git a/pkg/compiler/lib/src/cps_ir/gvn.dart b/pkg/compiler/lib/src/cps_ir/gvn.dart index a4a591f3ae2..46f5d936ca7 100644 --- a/pkg/compiler/lib/src/cps_ir/gvn.dart +++ b/pkg/compiler/lib/src/cps_ir/gvn.dart @@ -192,7 +192,7 @@ class GVN extends TrampolineRecursiveVisitor implements Pass { bool isHoistablePrimitive(Primitive prim) { if (prim.isSafeForElimination) return true; - if (prim is NullCheck || + if (prim is ReceiverCheck || prim is BoundsCheck || prim is GetLength || prim is GetField || diff --git a/pkg/compiler/lib/src/cps_ir/inline.dart b/pkg/compiler/lib/src/cps_ir/inline.dart index 554e93e3923..972241e1389 100644 --- a/pkg/compiler/lib/src/cps_ir/inline.dart +++ b/pkg/compiler/lib/src/cps_ir/inline.dart @@ -405,6 +405,14 @@ class InliningVisitor extends TrampolineRecursiveVisitor { return null; } + if (isBlacklisted(target)) return null; + + if (invoke.callingConvention == CallingConvention.OneShotIntercepted) { + // One-shot interceptor calls with a known target are only inserted on + // uncommon code paths, so they should not be inlined. + return null; + } + Reference dartReceiver = invoke.dartReceiverReference; TypeMask abstractReceiver = dartReceiver == null ? null : abstractType(dartReceiver); @@ -512,7 +520,9 @@ class InliningVisitor extends TrampolineRecursiveVisitor { CpsFragment fragment, Primitive dartReceiver, TypeMask abstractReceiver) { - Selector selector = invoke is InvokeMethod ? invoke.selector : null; + if (invoke is! InvokeMethod) return dartReceiver; + InvokeMethod invokeMethod = invoke; + Selector selector = invokeMethod.selector; if (typeSystem.isDefinitelyNum(abstractReceiver, allowNull: true)) { Primitive condition = _fragment.letPrim( new ApplyBuiltinOperator(BuiltinOperator.IsNotNumber, @@ -520,15 +530,16 @@ class InliningVisitor extends TrampolineRecursiveVisitor { invoke.sourceInformation)); condition.type = typeSystem.boolType; Primitive check = _fragment.letPrim( - new NullCheck.guarded( - condition, dartReceiver, selector, invoke.sourceInformation)); + new ReceiverCheck.nullCheck(dartReceiver, selector, + invoke.sourceInformation, + condition: condition)); check.type = abstractReceiver.nonNullable(); return check; } Primitive check = _fragment.letPrim( - new NullCheck(dartReceiver, invoke.sourceInformation, - selector: selector)); + new ReceiverCheck.nullCheck(dartReceiver, selector, + invoke.sourceInformation)); check.type = abstractReceiver.nonNullable(); return check; } @@ -571,4 +582,16 @@ class InliningVisitor extends TrampolineRecursiveVisitor { } return tryInlining(node, node.target, null); } + + bool isBlacklisted(FunctionElement target) { + ClassElement enclosingClass = target.enclosingClass; + if (target.isOperator && + (enclosingClass == backend.helpers.jsNumberClass || + enclosingClass == backend.helpers.jsDoubleClass || + enclosingClass == backend.helpers.jsIntClass)) { + // These should be handled by operator specialization. + return true; + } + return false; + } } diff --git a/pkg/compiler/lib/src/cps_ir/redundant_join.dart b/pkg/compiler/lib/src/cps_ir/redundant_join.dart index 808c1c73eed..f1191ff5e17 100644 --- a/pkg/compiler/lib/src/cps_ir/redundant_join.dart +++ b/pkg/compiler/lib/src/cps_ir/redundant_join.dart @@ -212,7 +212,7 @@ class RedundantJoinEliminator extends TrampolineRecursiveVisitor implements Pass /// After lifting LetConts in the main pass above, parameter objects can have /// multiple bindings. Each reference implicitly refers to the binding that /// is currently in scope. -/// +/// /// This returns the IR to its normal form after redundant joins have been /// eliminated. class AlphaRenamer extends TrampolineRecursiveVisitor { diff --git a/pkg/compiler/lib/src/cps_ir/type_propagation.dart b/pkg/compiler/lib/src/cps_ir/type_propagation.dart index 6c896e1cf8e..c231aa92830 100644 --- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart +++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart @@ -702,6 +702,17 @@ class ConstantPropagationLattice { return nonConstant(value.type.nonNullable()); } + AbstractConstantValue intersectWithType(AbstractConstantValue value, + TypeMask type) { + if (value.isNothing || typeSystem.areDisjoint(value.type, type)) { + return nothing; + } else if (value.isConstant) { + return value; + } else { + return nonConstant(typeSystem.intersection(value.type, type)); + } + } + /// If [value] is an integer constant, returns its value, otherwise `null`. int intValue(AbstractConstantValue value) { if (value.isConstant && value.constant.isInt) { @@ -798,11 +809,17 @@ class TransformingVisitor extends DeepRecursiveVisitor { final List stack = []; + TypeCheckOperator checkIsNumber; + TransformingVisitor(this.compiler, this.functionCompiler, this.lattice, this.analyzer, - this.internalError); + this.internalError) { + checkIsNumber = new ClassTypeCheckOperator( + helpers.jsNumberClass, + BuiltinOperator.IsNotNumber); + } void transform(FunctionDefinition root) { // If one of the parameters has no value, the function is unreachable. @@ -1116,41 +1133,99 @@ class TransformingVisitor extends DeepRecursiveVisitor { /// /// Returns `true` if the node was replaced. specializeOperatorCall(InvokeMethod node) { + if (!backend.isInterceptedSelector(node.selector)) return null; + if (node.dartArgumentsLength > 1) return null; + if (node.callingConvention == CallingConvention.OneShotIntercepted) { + return null; + } + bool trustPrimitives = compiler.trustPrimitives; - /// Throws a [NoSuchMethodError] if the receiver is null, where [guard] - /// is a predicate that is true if and only if the receiver is null. - /// - /// See [NullCheck.guarded]. - Primitive guardReceiver(CpsFragment cps, BuiltinOperator guard) { - if (guard == null || getValue(node.dartReceiver).isDefinitelyNotNull) { - return node.dartReceiver; + /// Check that the receiver and argument satisfy the given type checks, and + /// throw a [NoSuchMethodError] or [ArgumentError] if the check fails. + CpsFragment makeGuard(TypeCheckOperator receiverGuard, + [TypeCheckOperator argumentGuard]) { + CpsFragment cps = new CpsFragment(node.sourceInformation); + + // Make no guards if trusting primitives. + if (trustPrimitives) return cps; + + // Determine which guards are needed. + ChecksNeeded receiverChecks = + receiverGuard.getChecksNeeded(node.dartReceiver, classWorld); + bool needReceiverGuard = receiverChecks != ChecksNeeded.None; + bool needArgumentGuard = + argumentGuard != null && + argumentGuard.needsCheck(node.dartArgument(0), classWorld); + + if (!needReceiverGuard && !needArgumentGuard) return cps; + + // If we only need the receiver check, emit the specialized receiver + // check instruction. Examples: + // + // if (typeof receiver !== "number") return receiver.$lt; + // if (typeof receiver !== "number") return receiver.$lt(); + // + if (!needArgumentGuard) { + Primitive condition = receiverGuard.makeCheck(cps, node.dartReceiver); + cps.letPrim(new ReceiverCheck( + node.dartReceiver, + node.selector, + node.sourceInformation, + condition: condition, + useSelector: true, + isNullCheck: receiverChecks == ChecksNeeded.Null + )); + return cps; } - if (!trustPrimitives) { - // TODO(asgerf): Perhaps a separate optimization should decide that - // the guarded check is better based on the type? - Primitive check = cps.applyBuiltin(guard, [node.dartReceiver]); - return cps.letPrim(new NullCheck.guarded(check, node.dartReceiver, - node.selector, node.sourceInformation)); - } else { - // Refine the receiver to be non-null for use in the operator. - // This restricts code motion and improves the type computed for the - // built-in operator that depends on it. - // This must be done even if trusting primitives. - return cps.letPrim( - new Refinement(node.dartReceiver, typeSystem.nonNullType)); + + // TODO(asgerf): We should consider specialized instructions for + // argument checks and receiver+argument checks, to avoid breaking up + // basic blocks. + + // Emit as `H.iae(x)` if only the argument check may fail. For example: + // + // if (typeof argument !== "number") return H.iae(argument); + // + if (!needReceiverGuard) { + cps.ifTruthy(argumentGuard.makeCheck(cps, node.dartArgument(0))) + .invokeStaticThrower(helpers.throwIllegalArgumentException, + [node.dartArgument(0)]); + return cps; } + + // Both receiver and argument check is needed. Emit as a combined check + // using a one-shot interceptor to produce the exact error message in + // the error case. For example: + // + // if (typeof receiver !== "number" || typeof argument !== "number") + // return J.$lt(receiver, argument); + // + Continuation fail = cps.letCont(); + cps.ifTruthy(receiverGuard.makeCheck(cps, node.dartReceiver)) + .invokeContinuation(fail); + cps.ifTruthy(argumentGuard.makeCheck(cps, node.dartArgument(0))) + .invokeContinuation(fail); + + cps.insideContinuation(fail) + ..invokeMethod(node.dartReceiver, node.selector, node.mask, + [node.dartArgument(0)], CallingConvention.OneShotIntercepted) + ..put(new Unreachable()); + + return cps; } /// Replaces the call with [operator], using the receiver and first argument /// as operands (in that order). /// - /// If [guard] is given, the receiver is checked using [guardReceiver], - /// unless it is known not to be null. - CpsFragment makeBinary(BuiltinOperator operator, {BuiltinOperator guard}) { - CpsFragment cps = new CpsFragment(node.sourceInformation); - Primitive left = guardReceiver(cps, guard); - Primitive right = node.dartArgument(0); + /// If [guard] is given, the receiver and argument are both checked using + /// that operator. + CpsFragment makeBinary(BuiltinOperator operator, + {TypeCheckOperator guard: TypeCheckOperator.none}) { + CpsFragment cps = makeGuard(guard, guard); + Primitive left = guard.makeRefinement(cps, node.dartReceiver, classWorld); + Primitive right = + guard.makeRefinement(cps, node.dartArgument(0), classWorld); Primitive result = cps.applyBuiltin(operator, [left, right]); result.hint = node.hint; node.replaceUsesWith(result); @@ -1159,15 +1234,20 @@ class TransformingVisitor extends DeepRecursiveVisitor { /// Like [makeBinary] but for unary operators with the receiver as the /// argument. - CpsFragment makeUnary(BuiltinOperator operator, {BuiltinOperator guard}) { - CpsFragment cps = new CpsFragment(node.sourceInformation); - Primitive argument = guardReceiver(cps, guard); + CpsFragment makeUnary(BuiltinOperator operator, + {TypeCheckOperator guard: TypeCheckOperator.none}) { + CpsFragment cps = makeGuard(guard); + Primitive argument = + guard.makeRefinement(cps, node.dartReceiver, classWorld); Primitive result = cps.applyBuiltin(operator, [argument]); result.hint = node.hint; node.replaceUsesWith(result); return cps; } + TypeMask successType = + typeSystem.receiverTypeFor(node.selector, node.dartReceiver.type); + if (node.selector.isOperator && node.dartArgumentsLength == 1) { Primitive leftArg = node.dartReceiver; Primitive rightArg = node.dartArgument(0); @@ -1198,13 +1278,17 @@ class TransformingVisitor extends DeepRecursiveVisitor { return makeBinary(BuiltinOperator.Identical); } } else { - if (lattice.isDefinitelyNum(left, allowNull: true) && - lattice.isDefinitelyNum(right, allowNull: trustPrimitives)) { + if (typeSystem.isDefinitelyNum(successType)) { // Try to insert a numeric operator. BuiltinOperator operator = NumBinaryBuiltins[opname]; if (operator != null) { - return makeBinary(operator, guard: BuiltinOperator.IsNotNumber); + return makeBinary(operator, guard: checkIsNumber); } + + // The following specializations only apply to integers. + // The Math.floor test is quite large, so we only apply these in cases + // where the guard does not involve Math.floor. + // Shift operators are not in [NumBinaryBuiltins] because Dart shifts // behave different to JS shifts, especially in the handling of the // shift count. @@ -1212,8 +1296,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { if (opname == '<<' && lattice.isDefinitelyInt(left, allowNull: true) && lattice.isDefinitelyIntInRange(right, min: 0, max: 31)) { - return makeBinary(BuiltinOperator.NumShl, - guard: BuiltinOperator.IsNotNumber); + return makeBinary(BuiltinOperator.NumShl, guard: checkIsNumber); } // Try to insert a shift-right operator. JavaScript's right shift is // consistent with Dart's only for left operands in the unsigned @@ -1221,8 +1304,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { if (opname == '>>' && lattice.isDefinitelyUint32(left, allowNull: true) && lattice.isDefinitelyIntInRange(right, min: 0, max: 31)) { - return makeBinary(BuiltinOperator.NumShr, - guard: BuiltinOperator.IsNotNumber); + return makeBinary(BuiltinOperator.NumShr, guard: checkIsNumber); } // Try to use remainder for '%'. Both operands must be non-negative // and the divisor must be non-zero. @@ -1231,14 +1313,14 @@ class TransformingVisitor extends DeepRecursiveVisitor { lattice.isDefinitelyUint(right) && lattice.isDefinitelyIntInRange(right, min: 1)) { return makeBinary(BuiltinOperator.NumRemainder, - guard: BuiltinOperator.IsNotNumber); + guard: checkIsNumber); } if (opname == '~/' && lattice.isDefinitelyUint32(left, allowNull: true) && lattice.isDefinitelyIntInRange(right, min: 2)) { return makeBinary(BuiltinOperator.NumTruncatingDivideToSigned32, - guard: BuiltinOperator.IsNotNumber); + guard: checkIsNumber); } } if (lattice.isDefinitelyString(left, allowNull: trustPrimitives) && @@ -1250,18 +1332,13 @@ class TransformingVisitor extends DeepRecursiveVisitor { } } if (node.selector.isOperator && node.dartArgumentsLength == 0) { - Primitive argument = node.dartReceiver; - AbstractConstantValue value = getValue(argument); - - if (lattice.isDefinitelyNum(value, allowNull: true)) { + if (typeSystem.isDefinitelyNum(successType)) { String opname = node.selector.name; if (opname == '~') { - return makeUnary(BuiltinOperator.NumBitNot, - guard: BuiltinOperator.IsNotNumber); + return makeUnary(BuiltinOperator.NumBitNot, guard: checkIsNumber); } if (opname == 'unary-') { - return makeUnary(BuiltinOperator.NumNegate, - guard: BuiltinOperator.IsNotNumber); + return makeUnary(BuiltinOperator.NumNegate, guard: checkIsNumber); } } } @@ -1277,7 +1354,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { lattice.isDefinitelyInt(argValue) && isIntNotZero(argValue)) { return makeBinary(BuiltinOperator.NumRemainder, - guard: BuiltinOperator.IsNotNumber); + guard: checkIsNumber); } } } else if (name == 'codeUnitAt') { @@ -1391,6 +1468,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { case '[]': Primitive index = node.dartArgument(0); + // TODO(asgerf): Consider inserting a guard and specialize anyway. if (!lattice.isDefinitelyInt(getValue(index))) return null; CpsFragment cps = new CpsFragment(node.sourceInformation); receiver = makeBoundsCheck(cps, receiver, index); @@ -2282,9 +2360,12 @@ class TransformingVisitor extends DeepRecursiveVisitor { } } - visitNullCheck(NullCheck node) { - if (!getValue(node.value.definition).isNullable) { - node.replaceUsesWith(node.value.definition); + visitReceiverCheck(ReceiverCheck node) { + Primitive input = node.value.definition; + if (!input.type.isNullable && + (node.isNullCheck || + !input.type.needsNoSuchMethodHandling(node.selector, classWorld))) { + node.replaceUsesWith(input); return new CpsFragment(); } return null; @@ -3128,16 +3209,9 @@ class TypePropagationVisitor implements Visitor { @override void visitRefinement(Refinement node) { - AbstractConstantValue value = getValue(node.value.definition); - if (value.isNothing || - typeSystem.areDisjoint(value.type, node.refineType)) { - setValue(node, nothing); - } else if (value.isConstant) { - setValue(node, value); - } else { - setValue(node, - nonConstant(value.type.intersection(node.refineType, classWorld))); - } + setValue(node, lattice.intersectWithType( + getValue(node.value.definition), + node.refineType)); } @override @@ -3146,8 +3220,19 @@ class TypePropagationVisitor implements Visitor { } @override - void visitNullCheck(NullCheck node) { - setValue(node, lattice.nonNullable(getValue(node.value.definition))); + void visitReceiverCheck(ReceiverCheck node) { + AbstractConstantValue value = getValue(node.value.definition); + if (node.isNullCheck) { + // Avoid expensive TypeMask operations for null checks. + setValue(node, lattice.nonNullable(value)); + } else if (value.isConstant && + !value.type.needsNoSuchMethodHandling(node.selector, classWorld)) { + // Preserve constants, unless the check fails for the constant. + setValue(node, value); + } else { + setValue(node, + nonConstant(typeSystem.receiverTypeFor(node.selector, value.type))); + } } } @@ -3269,3 +3354,89 @@ class ResetAnalysisInfo extends TrampolineRecursiveVisitor { clear(node.variable); } } + +enum ChecksNeeded { + /// No check is needed. + None, + + /// Only null may fail the check. + Null, + + /// Full check required. + Complete, +} + +/// Generates runtime checks against a some type criteria, and determines at +/// compile-time if the check is needed. +/// +/// This class only generates the condition for determining if a check should +/// fail. Throwing the appropriate error in response to a failure is handled +/// elsewhere. +abstract class TypeCheckOperator { + const TypeCheckOperator(); + static const TypeCheckOperator none = const NoTypeCheckOperator(); + + /// Determines to what extent a runtime check is needed. + /// + /// Sometimes a check can be slightly improved if it is known that null is the + /// only possible input that fails the check. + ChecksNeeded getChecksNeeded(Primitive value, World world); + + /// Make an expression that returns `true` if [value] should fail the check. + /// + /// The result should be used in a check of the form: + /// + /// if (makeCheck(value)) throw Error(value); + /// + Primitive makeCheck(CpsFragment cps, Primitive value); + + /// Refine [value] after a succesful check. + Primitive makeRefinement(CpsFragment cps, Primitive value, World world); + + bool needsCheck(Primitive value, World world) { + return getChecksNeeded(value, world) != ChecksNeeded.None; + } +} + +/// Check that always passes. +class NoTypeCheckOperator extends TypeCheckOperator { + const NoTypeCheckOperator(); + + ChecksNeeded getChecksNeeded(Primitive value, World world) { + return ChecksNeeded.None; + } + + Primitive makeCheck(CpsFragment cps, Primitive value) { + return cps.makeFalse(); + } + + Primitive makeRefinement(CpsFragment cps, Primitive value, World world) { + return value; + } +} + +/// Checks using a built-in operator that a value is an instance of a given +/// class. +class ClassTypeCheckOperator extends TypeCheckOperator { + ClassElement classElement; + BuiltinOperator negatedOperator; + + ClassTypeCheckOperator(this.classElement, this.negatedOperator); + + ChecksNeeded getChecksNeeded(Primitive value, World world) { + TypeMask type = value.type; + if (type.satisfies(classElement, world)) { + return type.isNullable ? ChecksNeeded.Null : ChecksNeeded.None; + } else { + return ChecksNeeded.Complete; + } + } + + Primitive makeCheck(CpsFragment cps, Primitive value) { + return cps.applyBuiltin(negatedOperator, [value]); + } + + Primitive makeRefinement(CpsFragment cps, Primitive value, World world) { + return cps.refine(value, new TypeMask.nonNullSubclass(classElement, world)); + } +} diff --git a/pkg/compiler/lib/src/cps_ir/update_refinements.dart b/pkg/compiler/lib/src/cps_ir/update_refinements.dart index 48e3242457e..3500521ac01 100644 --- a/pkg/compiler/lib/src/cps_ir/update_refinements.dart +++ b/pkg/compiler/lib/src/cps_ir/update_refinements.dart @@ -3,6 +3,7 @@ library dart2js.cps_ir.update_refinements; import 'cps_ir_nodes.dart'; import 'optimizers.dart' show Pass; import 'type_mask_system.dart'; +import '../world.dart'; /// Updates all references to use the most refined version in scope. /// @@ -19,6 +20,7 @@ class UpdateRefinements extends TrampolineRecursiveVisitor implements Pass { String get passName => 'Update refinements'; final TypeMaskSystem typeSystem; + World get classWorld => typeSystem.classWorld; Map refinementFor = {}; @@ -34,20 +36,21 @@ class UpdateRefinements extends TrampolineRecursiveVisitor implements Pass { return next; } - visitNullCheck(NullCheck node) { + visitReceiverCheck(ReceiverCheck node) { if (refine(node.value)) { + // Update the type if the input has changed. Primitive value = node.value.definition; - if (value.type.isNullable) { - // Update the type if the input has changed. - node.type = value.type.nonNullable(); + if (value.type.needsNoSuchMethodHandling(node.selector, classWorld)) { + node.type = typeSystem.receiverTypeFor(node.selector, value.type); } else { + // Check is no longer needed. node..replaceUsesWith(value)..destroy(); LetPrim letPrim = node.parent; letPrim.remove(); return; } } - // Use the NullCheck as a refinement. + // Use the ReceiverCheck as a refinement. Primitive value = node.effectiveDefinition; Primitive old = refinementFor[value]; refinementFor[value] = node; diff --git a/pkg/compiler/lib/src/js_backend/codegen/codegen.dart b/pkg/compiler/lib/src/js_backend/codegen/codegen.dart index 1815ee2a8b7..eb1e6994d4e 100644 --- a/pkg/compiler/lib/src/js_backend/codegen/codegen.dart +++ b/pkg/compiler/lib/src/js_backend/codegen/codegen.dart @@ -1031,7 +1031,7 @@ class CodeGenerator extends tree_ir.StatementVisitor } @override - visitNullCheck(tree_ir.NullCheck node) { + visitReceiverCheck(tree_ir.ReceiverCheck node) { js.Expression value = visitExpression(node.value); // TODO(sra): Try to use the selector even when [useSelector] is false. The // reason we use 'toString' is that it is always defined so avoids a slow @@ -1041,9 +1041,12 @@ class CodeGenerator extends tree_ir.StatementVisitor // hook for that selector. We don't know these things here, but the decision // could be deferred by creating a deferred property that was resolved after // codegen. - js.Expression access = node.selector != null && node.useSelector + js.Expression access = node.useSelector ? js.js('#.#', [value, glue.invocationName(node.selector)]) : js.js('#.toString', [value]); + if (node.useInvoke) { + access = new js.Call(access, []); + } if (node.condition != null) { js.Expression condition = visitExpression(node.condition); js.Statement body = isNullReturn(node.next) diff --git a/pkg/compiler/lib/src/tree_ir/optimization/pull_into_initializers.dart b/pkg/compiler/lib/src/tree_ir/optimization/pull_into_initializers.dart index 1ada85b35d7..3785f7802f8 100644 --- a/pkg/compiler/lib/src/tree_ir/optimization/pull_into_initializers.dart +++ b/pkg/compiler/lib/src/tree_ir/optimization/pull_into_initializers.dart @@ -168,7 +168,7 @@ class PullIntoInitializers extends RecursiveTransformer return node; } - Statement visitNullCheck(NullCheck node) { + Statement visitReceiverCheck(ReceiverCheck node) { if (node.condition != null) { node.condition = visitExpression(node.condition); // The value occurs in conditional context, so don't pull from that. diff --git a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart index 1c92ff5eeb3..368513776e3 100644 --- a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart +++ b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart @@ -1261,7 +1261,7 @@ class StatementRewriter extends Transformer implements Pass { } @override - Statement visitNullCheck(NullCheck node) { + Statement visitReceiverCheck(ReceiverCheck node) { inEmptyEnvironment(() { node.next = visitStatement(node.next); }); diff --git a/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart b/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart index 15dc292cb6a..a9c28318480 100644 --- a/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart +++ b/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart @@ -699,12 +699,16 @@ class Builder implements cps_ir.Visitor/**/ { } } - visitNullCheck(cps_ir.NullCheck node) => (Statement next) { - return new NullCheck( + visitReceiverCheck(cps_ir.ReceiverCheck node) => (Statement next) { + // The CPS IR uses 'isNullCheck' because the semantics are important. + // In the Tree IR, syntax is more important, so the receiver check uses + // "useInvoke" to denote if an invocation should be emitted. + return new ReceiverCheck( condition: getVariableUseOrNull(node.condition), value: getVariableUse(node.value), selector: node.selector, useSelector: node.useSelector, + useInvoke: !node.isNullCheck, next: next, sourceInformation: node.sourceInformation); }; diff --git a/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart b/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart index 2b4d3011163..ec2e34abdac 100644 --- a/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart +++ b/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart @@ -950,23 +950,24 @@ class Yield extends Statement { } } -class NullCheck extends Statement { +class ReceiverCheck extends Statement { Expression condition; Expression value; Selector selector; bool useSelector; + bool useInvoke; Statement next; SourceInformation sourceInformation; - NullCheck({this.condition, this.value, this.selector, this.useSelector, - this.next, this.sourceInformation}); + ReceiverCheck({this.condition, this.value, this.selector, this.useSelector, + this.useInvoke, this.next, this.sourceInformation}); accept(StatementVisitor visitor) { - return visitor.visitNullCheck(this); + return visitor.visitReceiverCheck(this); } accept1(StatementVisitor1 visitor, arg) { - return visitor.visitNullCheck(this, arg); + return visitor.visitReceiverCheck(this, arg); } } @@ -1059,7 +1060,7 @@ abstract class StatementVisitor { S visitUnreachable(Unreachable node); S visitForeignStatement(ForeignStatement node); S visitYield(Yield node); - S visitNullCheck(NullCheck node); + S visitReceiverCheck(ReceiverCheck node); } abstract class StatementVisitor1 { @@ -1077,7 +1078,7 @@ abstract class StatementVisitor1 { S visitUnreachable(Unreachable node, A arg); S visitForeignStatement(ForeignStatement node, A arg); S visitYield(Yield node, A arg); - S visitNullCheck(NullCheck node, A arg); + S visitReceiverCheck(ReceiverCheck node, A arg); } abstract class RecursiveVisitor implements StatementVisitor, ExpressionVisitor { @@ -1286,7 +1287,7 @@ abstract class RecursiveVisitor implements StatementVisitor, ExpressionVisitor { visitStatement(node.next); } - visitNullCheck(NullCheck node) { + visitReceiverCheck(ReceiverCheck node) { if (node.condition != null) visitExpression(node.condition); visitExpression(node.value); visitStatement(node.next); @@ -1546,7 +1547,7 @@ class RecursiveTransformer extends Transformer { return node; } - visitNullCheck(NullCheck node) { + visitReceiverCheck(ReceiverCheck node) { if (node.condition != null) { node.condition = visitExpression(node.condition); } diff --git a/pkg/compiler/lib/src/tree_ir/tree_ir_tracer.dart b/pkg/compiler/lib/src/tree_ir/tree_ir_tracer.dart index 6d2ce7eac42..01aed83b869 100644 --- a/pkg/compiler/lib/src/tree_ir/tree_ir_tracer.dart +++ b/pkg/compiler/lib/src/tree_ir/tree_ir_tracer.dart @@ -179,7 +179,7 @@ class BlockCollector extends StatementVisitor { visitStatement(node.next); } - visitNullCheck(NullCheck node) { + visitReceiverCheck(ReceiverCheck node) { _addStatement(node); visitStatement(node.next); } @@ -345,7 +345,7 @@ class TreeTracer extends TracerUtil with StatementVisitor { } @override - visitNullCheck(NullCheck node) { + visitReceiverCheck(ReceiverCheck node) { printStatement(null, 'NullCheck ${expr(node.value)}'); } } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_1.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_1.js index 758c03c8d23..be268f4a7d3 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_1.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_1.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$sub$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$sub$n(x, y); + P.print(x - y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_10.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_10.js index 8679a25c40d..9c4c3f3cba1 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_10.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_10.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$gt$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$gt$n(x, y); + P.print(x > y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_11.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_11.js index b5ff041ab2c..23f5fca882a 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_11.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_11.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$lt$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$lt$n(x, y); + P.print(x < y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_12.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_12.js index 525a5d002c5..0fd94d22512 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_12.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_12.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$ge$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$ge$n(x, y); + P.print(x >= y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_13.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_13.js index f434bb78d5a..481395cb7da 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_13.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_13.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$le$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$le$n(x, y); + P.print(x <= y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_19.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_19.js index c614b758b3d..5125dcbb0bc 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_19.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_19.js @@ -10,12 +10,14 @@ // } function() { - var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null), v0 = typeof y === "number"; - P.print(J.$div$n(x, 2)); + var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); + if (typeof x !== "number") + return x.$div(); + P.print(x / 2); P.print(true); - P.print(v0); - if (!v0) - throw H.wrapException(H.argumentErrorValue(y)); + P.print(typeof y === "number"); + if (typeof y !== "number") + return H.iae(y); P.print(x + y); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_2.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_2.js index 33c7bf9920c..0de632e3976 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_2.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_2.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$div$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$div$n(x, y); + P.print(x / y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_20.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_20.js index 915d2371cee..13b3053e386 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_20.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_20.js @@ -10,12 +10,14 @@ // } function() { - var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null), v0 = typeof y === "number"; - P.print(J.$div$n(x, 2)); + var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); + if (typeof x !== "number") + return x.$div(); + P.print(x / 2); P.print(true); - P.print(v0); - if (!v0) - throw H.wrapException(H.argumentErrorValue(y)); + P.print(typeof y === "number"); + if (typeof y !== "number") + return H.iae(y); P.print(x * y); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_21.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_21.js index 2c4e7c7b2b6..26cfc957632 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_21.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_21.js @@ -10,9 +10,12 @@ // } function() { - var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null), v0 = typeof y === "number"; - P.print(J.$div$n(x, 2)); + var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null), v0; + if (typeof x !== "number") + return x.$div(); + P.print(x / 2); P.print(true); + v0 = typeof y === "number"; P.print(v0); if (!v0) throw H.wrapException(H.argumentErrorValue(y)); diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_7.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_7.js index 6e152f78354..cb58cd33ef7 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_7.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_7.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$and$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$and$n(x, y); + P.print((x & y) >>> 0); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_8.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_8.js index 438d6dc7213..7c0109d9411 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_8.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_8.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$or$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$or$n(x, y); + P.print((x | y) >>> 0); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_9.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_9.js index 43418b58a98..c1c6becaeb0 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_9.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_9.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$xor$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$xor$n(x, y); + P.print((x ^ y) >>> 0); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_1.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_1.js index 758c03c8d23..be268f4a7d3 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_1.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_1.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$sub$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$sub$n(x, y); + P.print(x - y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_10.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_10.js index b5ff041ab2c..23f5fca882a 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_10.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_10.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$lt$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$lt$n(x, y); + P.print(x < y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_11.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_11.js index 8679a25c40d..9c4c3f3cba1 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_11.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_11.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$gt$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$gt$n(x, y); + P.print(x > y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_12.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_12.js index f434bb78d5a..481395cb7da 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_12.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_12.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$le$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$le$n(x, y); + P.print(x <= y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_13.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_13.js index 525a5d002c5..0fd94d22512 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_13.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_13.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$ge$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$ge$n(x, y); + P.print(x >= y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_2.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_2.js index 33c7bf9920c..0de632e3976 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_2.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_2.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$div$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$div$n(x, y); + P.print(x / y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_7.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_7.js index 6e152f78354..cb58cd33ef7 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_7.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_7.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$and$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$and$n(x, y); + P.print((x & y) >>> 0); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_8.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_8.js index 438d6dc7213..7c0109d9411 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_8.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_8.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$or$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$or$n(x, y); + P.print((x | y) >>> 0); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_9.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_9.js index 43418b58a98..c1c6becaeb0 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_9.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_9.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$xor$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$xor$n(x, y); + P.print((x ^ y) >>> 0); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/operators2_3.js b/tests/compiler/dart2js/cps_ir/expected/operators2_3.js index bf578b27148..0ec84408c9f 100644 --- a/tests/compiler/dart2js/cps_ir/expected/operators2_3.js +++ b/tests/compiler/dart2js/cps_ir/expected/operators2_3.js @@ -1,12 +1,14 @@ // Expectation for test: // // Method to test: function(foo) -// foo(a) => a % 13; +// import 'package:expect/expect.dart'; +// +// @NoInline() foo(a) => a % 13; +// // main() { // print(foo(5)); // print(foo(-100)); // } function(a) { - var result = a % 13; - return result === 0 ? 0 : result > 0 ? result : result + 13; + return C.JSInt_methods.$mod(a, 13); } diff --git a/tests/compiler/dart2js/cps_ir/expected/operators2_6.js b/tests/compiler/dart2js/cps_ir/expected/operators2_6.js index 79fe43a2d9c..d9cfe873d84 100644 --- a/tests/compiler/dart2js/cps_ir/expected/operators2_6.js +++ b/tests/compiler/dart2js/cps_ir/expected/operators2_6.js @@ -1,11 +1,14 @@ // Expectation for test: // // Method to test: function(foo) -// foo(a) => a ~/ 13; +// import 'package:expect/expect.dart'; +// +// @NoInline() foo(a) => a ~/ 13; +// // main() { // print(foo(5)); // print(foo(-100)); // } function(a) { - return (a | 0) === a && (13 | 0) === 13 ? a / 13 | 0 : C.JSNumber_methods.toInt$0(a / 13); + return C.JSInt_methods.$tdiv(a, 13); } diff --git a/tests/compiler/dart2js/cps_ir/expected/operators2_7.js b/tests/compiler/dart2js/cps_ir/expected/operators2_7.js index 9faff59c99f..403b8312c7d 100644 --- a/tests/compiler/dart2js/cps_ir/expected/operators2_7.js +++ b/tests/compiler/dart2js/cps_ir/expected/operators2_7.js @@ -1,6 +1,9 @@ // Expectation for test: // // Method to test: function(foo) -// foo(a) => a ~/ 13; +// import 'package:expect/expect.dart'; +// +// @NoInline() foo(a) => a ~/ 13; +// // main() { // print(foo.toString()); // print(foo(5)); diff --git a/tests/compiler/dart2js/cps_ir/expected/operators2_8.js b/tests/compiler/dart2js/cps_ir/expected/operators2_8.js index 2f1ce60fb82..b322ba0c2fb 100644 --- a/tests/compiler/dart2js/cps_ir/expected/operators2_8.js +++ b/tests/compiler/dart2js/cps_ir/expected/operators2_8.js @@ -1,11 +1,14 @@ // Expectation for test: // // Method to test: function(foo) -// foo(a) => a ~/ 13; +// import 'package:expect/expect.dart'; +// +// @NoInline() foo(a) => a ~/ 13; +// // main() { // print(foo(5)); // print(foo(8000000000)); // } function(a) { - return (a | 0) === a && (13 | 0) === 13 ? a / 13 | 0 : C.JSNumber_methods.toInt$0(a / 13); + return C.JSInt_methods.$tdiv(a, 13); } diff --git a/tests/compiler/dart2js/cps_ir/input/operators2_3.dart b/tests/compiler/dart2js/cps_ir/input/operators2_3.dart index f1dc5acd9c9..2cd9a5340a3 100644 --- a/tests/compiler/dart2js/cps_ir/input/operators2_3.dart +++ b/tests/compiler/dart2js/cps_ir/input/operators2_3.dart @@ -1,5 +1,8 @@ // Method to test: function(foo) -foo(a) => a % 13; +import 'package:expect/expect.dart'; + +@NoInline() foo(a) => a % 13; + main() { print(foo(5)); print(foo(-100)); diff --git a/tests/compiler/dart2js/cps_ir/input/operators2_6.dart b/tests/compiler/dart2js/cps_ir/input/operators2_6.dart index 4b763a62310..88bd1374b18 100644 --- a/tests/compiler/dart2js/cps_ir/input/operators2_6.dart +++ b/tests/compiler/dart2js/cps_ir/input/operators2_6.dart @@ -1,5 +1,8 @@ // Method to test: function(foo) -foo(a) => a ~/ 13; +import 'package:expect/expect.dart'; + +@NoInline() foo(a) => a ~/ 13; + main() { print(foo(5)); print(foo(-100)); diff --git a/tests/compiler/dart2js/cps_ir/input/operators2_7.dart b/tests/compiler/dart2js/cps_ir/input/operators2_7.dart index 225701be571..f8fe827ab2c 100644 --- a/tests/compiler/dart2js/cps_ir/input/operators2_7.dart +++ b/tests/compiler/dart2js/cps_ir/input/operators2_7.dart @@ -1,5 +1,8 @@ // Method to test: function(foo) -foo(a) => a ~/ 13; +import 'package:expect/expect.dart'; + +@NoInline() foo(a) => a ~/ 13; + main() { print(foo.toString()); print(foo(5)); diff --git a/tests/compiler/dart2js/cps_ir/input/operators2_8.dart b/tests/compiler/dart2js/cps_ir/input/operators2_8.dart index 05e3bb0aa32..ec9b8b363f1 100644 --- a/tests/compiler/dart2js/cps_ir/input/operators2_8.dart +++ b/tests/compiler/dart2js/cps_ir/input/operators2_8.dart @@ -1,5 +1,8 @@ // Method to test: function(foo) -foo(a) => a ~/ 13; +import 'package:expect/expect.dart'; + +@NoInline() foo(a) => a ~/ 13; + main() { print(foo(5)); print(foo(8000000000));