| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 library dart2js.ir_nodes; | 4 library dart2js.ir_nodes; |
| 5 | 5 |
| 6 import 'dart:collection'; | 6 import 'dart:collection'; |
| 7 import 'cps_fragment.dart' show CpsFragment; | 7 import 'cps_fragment.dart' show CpsFragment; |
| 8 import 'cps_ir_nodes_sexpr.dart'; | 8 import 'cps_ir_nodes_sexpr.dart'; |
| 9 import '../constants/values.dart' as values; | 9 import '../constants/values.dart' as values; |
| 10 import '../dart_types.dart' show DartType, InterfaceType, TypeVariableType; | 10 import '../dart_types.dart' show DartType, InterfaceType, TypeVariableType; |
| (...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 253 bool get isSafeForElimination; | 253 bool get isSafeForElimination; |
| 254 | 254 |
| 255 /// True if time-of-evaluation is irrelevant for the given primitive, | 255 /// True if time-of-evaluation is irrelevant for the given primitive, |
| 256 /// assuming its inputs are the same values. | 256 /// assuming its inputs are the same values. |
| 257 bool get isSafeForReordering; | 257 bool get isSafeForReordering; |
| 258 | 258 |
| 259 /// The source information associated with this primitive. | 259 /// The source information associated with this primitive. |
| 260 // TODO(johnniwinther): Require source information for all primitives. | 260 // TODO(johnniwinther): Require source information for all primitives. |
| 261 SourceInformation get sourceInformation => null; | 261 SourceInformation get sourceInformation => null; |
| 262 | 262 |
| 263 /// If this is a [Refinement], [BoundsCheck] or [NullCheck] node, returns the | 263 /// If this is a [Refinement], [BoundsCheck] or [ReceiverCheck] node, returns
the |
| 264 /// value being refined, the indexable object being checked, or the value | 264 /// value being refined, the indexable object being checked, or the value |
| 265 /// that was checked to be non-null, respectively. | 265 /// that was checked to be non-null, respectively. |
| 266 /// | 266 /// |
| 267 /// Those instructions all return the corresponding operand directly, and | 267 /// Those instructions all return the corresponding operand directly, and |
| 268 /// this getter can be used to get (closer to) where the value came from. | 268 /// this getter can be used to get (closer to) where the value came from. |
| 269 // | 269 // |
| 270 // TODO(asgerf): Also do this for [TypeCast]? | 270 // TODO(asgerf): Also do this for [TypeCast]? |
| 271 Primitive get effectiveDefinition => this; | 271 Primitive get effectiveDefinition => this; |
| 272 | 272 |
| 273 /// Like [effectiveDefinition] but only unfolds [Refinement] nodes. | 273 /// Like [effectiveDefinition] but only unfolds [Refinement] nodes. |
| (...skipping 524 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 798 /// and the [length] reference may be null if there is no upper bound or | 798 /// and the [length] reference may be null if there is no upper bound or |
| 799 /// emptiness check. | 799 /// emptiness check. |
| 800 /// | 800 /// |
| 801 /// If a separate code motion guard for the index is required, e.g. because it | 801 /// If a separate code motion guard for the index is required, e.g. because it |
| 802 /// must be known to be non-negative in an operator that does not involve | 802 /// must be known to be non-negative in an operator that does not involve |
| 803 /// [object], a [Refinement] can be created for it with the non-negative integer | 803 /// [object], a [Refinement] can be created for it with the non-negative integer |
| 804 /// type. | 804 /// type. |
| 805 class BoundsCheck extends Primitive { | 805 class BoundsCheck extends Primitive { |
| 806 final Reference<Primitive> object; | 806 final Reference<Primitive> object; |
| 807 Reference<Primitive> index; | 807 Reference<Primitive> index; |
| 808 Reference<Primitive> length; // FIXME write docs for length | 808 Reference<Primitive> length; |
| 809 int checks; | 809 int checks; |
| 810 final SourceInformation sourceInformation; | 810 final SourceInformation sourceInformation; |
| 811 | 811 |
| 812 /// If true, check that `index >= 0`. | 812 /// If true, check that `index >= 0`. |
| 813 bool get hasLowerBoundCheck => checks & LOWER_BOUND != 0; | 813 bool get hasLowerBoundCheck => checks & LOWER_BOUND != 0; |
| 814 | 814 |
| 815 /// If true, check that `index < object.length`. | 815 /// If true, check that `index < object.length`. |
| 816 bool get hasUpperBoundCheck => checks & UPPER_BOUND != 0; | 816 bool get hasUpperBoundCheck => checks & UPPER_BOUND != 0; |
| 817 | 817 |
| 818 /// If true, check that `object.length !== 0`. | 818 /// If true, check that `object.length !== 0`. |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 872 } | 872 } |
| 873 } | 873 } |
| 874 | 874 |
| 875 bool get isSafeForElimination => checks == NONE; | 875 bool get isSafeForElimination => checks == NONE; |
| 876 bool get isSafeForReordering => false; | 876 bool get isSafeForReordering => false; |
| 877 bool get hasValue => true; // Can be referenced to restrict code motion. | 877 bool get hasValue => true; // Can be referenced to restrict code motion. |
| 878 | 878 |
| 879 Primitive get effectiveDefinition => object.definition.effectiveDefinition; | 879 Primitive get effectiveDefinition => object.definition.effectiveDefinition; |
| 880 } | 880 } |
| 881 | 881 |
| 882 /// Throw an exception if [value] is `null`. | 882 /// Throw a [NoSuchMethodError] if [value] cannot respond to [selector]. |
| 883 /// | 883 /// |
| 884 /// Returns [value] so this can be used to restrict code motion. | 884 /// Returns [value] so this can be used to restrict code motion. |
| 885 /// | 885 /// |
| 886 /// In the simplest form this compiles to `value.toString;`. | 886 /// The check can take one of three forms: |
| 887 /// | 887 /// |
| 888 /// [selector] holds the selector that is the cause of the null check. This is | 888 /// value.toString; |
| 889 /// usually a method that was inlined where [value] the receiver. | 889 /// value.selectorName; |
| 890 /// value.selectorName(); (should only be used if check always fails) |
| 890 /// | 891 /// |
| 891 /// If [selector] is set and [useSelector] is true, `toString` is replaced with | 892 /// The first two forms are used when it is known that only null fails the |
| 892 /// the (possibly minified) invocation name of the selector. This can be | 893 /// check. Additionally, the check may be guarded by a [condition], allowing |
| 893 /// shorter and generate a more meaningful error message, but is expensive if | 894 /// for three more forms: |
| 894 /// [value] is non-null and does not have that property at runtime. | |
| 895 /// | 895 /// |
| 896 /// If [condition] is set, it is assumed that [condition] is true if and only | 896 /// if (condition) value.toString; (this form is valid but unused) |
| 897 /// if [value] is null. The check then compiles to: | 897 /// if (condition) value.selectorName; |
| 898 /// if (condition) value.selectorName(); |
| 898 /// | 899 /// |
| 899 /// if (condition) value.toString; (or .selector if non-null) | 900 /// The condition must be true if and only if the check should fail. It should |
| 901 /// ideally be of a form understood by JS engines, e.g. a `typeof` test. |
| 900 /// | 902 /// |
| 901 /// The latter form is useful when [condition] is a form understood by the JS | 903 /// If [useSelector] is false, the first form instead becomes `value.toString;`. |
| 902 /// runtime, such as a `typeof` test. | 904 /// This form is faster when the value is non-null and the accessed property has |
| 903 class NullCheck extends Primitive { | 905 /// been removed by tree shaking. |
| 906 /// |
| 907 /// [selector] may not be one of the selectors implemented by the null object. |
| 908 class ReceiverCheck extends Primitive { |
| 904 final Reference<Primitive> value; | 909 final Reference<Primitive> value; |
| 905 final Selector selector; | 910 final Selector selector; |
| 906 final bool useSelector; | 911 final SourceInformation sourceInformation; |
| 907 final Reference<Primitive> condition; | 912 final Reference<Primitive> condition; |
| 908 final SourceInformation sourceInformation; | 913 final int _flags; |
| 909 | 914 |
| 910 NullCheck(Primitive value, this.sourceInformation, | 915 static const int _USE_SELECTOR = 1 << 0; |
| 911 {Primitive condition, | 916 static const int _NULL_CHECK = 1 << 1; |
| 912 this.selector, | |
| 913 this.useSelector: false}) | |
| 914 : this.value = new Reference<Primitive>(value), | |
| 915 this.condition = | |
| 916 condition == null ? null : new Reference<Primitive>(condition); | |
| 917 | 917 |
| 918 NullCheck.guarded(Primitive condition, Primitive value, this.selector, | 918 /// True if the selector name should be used in the check; otherwise |
| 919 this.sourceInformation) | 919 /// `toString` will be used. |
| 920 : this.condition = new Reference<Primitive>(condition), | 920 bool get useSelector => _flags & _USE_SELECTOR != 0; |
| 921 this.value = new Reference<Primitive>(value), | 921 |
| 922 this.useSelector = true; | 922 /// True if null is the only possible input that cannot respond to [selector]. |
| 923 bool get isNullCheck => _flags & _NULL_CHECK != 0; |
| 924 |
| 925 |
| 926 /// Constructor for creating checks in arbitrary configurations. |
| 927 /// |
| 928 /// Consider using one of the named constructors instead. |
| 929 /// |
| 930 /// [useSelector] and [isNullCheck] are mandatory named arguments. |
| 931 ReceiverCheck(Primitive value, this.selector, this.sourceInformation, |
| 932 {Primitive condition, bool useSelector, bool isNullCheck}) |
| 933 : value = new Reference<Primitive>(value), |
| 934 condition = _optionalReference(condition), |
| 935 _flags = (useSelector ? _USE_SELECTOR : 0) | |
| 936 (isNullCheck ? _NULL_CHECK : 0); |
| 937 |
| 938 /// Simplified constructor for building null checks. |
| 939 /// |
| 940 /// Null must be the only possible input value that does not respond to |
| 941 /// [selector]. |
| 942 ReceiverCheck.nullCheck( |
| 943 Primitive value, |
| 944 Selector selector, |
| 945 SourceInformation sourceInformation, |
| 946 {Primitive condition}) |
| 947 : this(value, |
| 948 selector, |
| 949 sourceInformation, |
| 950 condition: condition, |
| 951 useSelector: condition != null, |
| 952 isNullCheck: true); |
| 953 |
| 954 /// Simplified constructor for building the general check of form: |
| 955 /// |
| 956 /// if (condition) value.selectorName(); |
| 957 /// |
| 958 ReceiverCheck.generalCheck( |
| 959 Primitive value, |
| 960 Selector selector, |
| 961 SourceInformation sourceInformation, |
| 962 Primitive condition) |
| 963 : this(value, |
| 964 selector, |
| 965 sourceInformation, |
| 966 condition: condition, |
| 967 useSelector: true, |
| 968 isNullCheck: false); |
| 923 | 969 |
| 924 bool get isSafeForElimination => false; | 970 bool get isSafeForElimination => false; |
| 925 bool get isSafeForReordering => false; | 971 bool get isSafeForReordering => false; |
| 926 bool get hasValue => true; | 972 bool get hasValue => true; |
| 927 | 973 |
| 928 accept(Visitor visitor) => visitor.visitNullCheck(this); | 974 accept(Visitor visitor) => visitor.visitReceiverCheck(this); |
| 929 | 975 |
| 930 void setParentPointers() { | 976 void setParentPointers() { |
| 931 value.parent = this; | 977 value.parent = this; |
| 932 if (condition != null) { | 978 if (condition != null) { |
| 933 condition.parent = this; | 979 condition.parent = this; |
| 934 } | 980 } |
| 935 } | 981 } |
| 936 | 982 |
| 937 Primitive get effectiveDefinition => value.definition.effectiveDefinition; | 983 Primitive get effectiveDefinition => value.definition.effectiveDefinition; |
| 984 |
| 985 String get nullCheckString => isNullCheck ? 'null-check' : 'general-check'; |
| 986 String get useSelectorString => useSelector ? 'use-selector' : 'no-selector'; |
| 987 String get flagString => '$nullCheckString $useSelectorString'; |
| 938 } | 988 } |
| 939 | 989 |
| 940 /// An "is" type test. | 990 /// An "is" type test. |
| 941 /// | 991 /// |
| 942 /// Returns `true` if [value] is an instance of [dartType]. | 992 /// Returns `true` if [value] is an instance of [dartType]. |
| 943 /// | 993 /// |
| 944 /// [type] must not be the [Object], `dynamic` or [Null] types (though it might | 994 /// [type] must not be the [Object], `dynamic` or [Null] types (though it might |
| 945 /// be a type variable containing one of these types). This design is chosen | 995 /// be a type variable containing one of these types). This design is chosen |
| 946 /// to simplify code generation for type tests. | 996 /// to simplify code generation for type tests. |
| 947 class TypeTest extends Primitive { | 997 class TypeTest extends Primitive { |
| (...skipping 975 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1923 return visitor.visitYield(this); | 1973 return visitor.visitYield(this); |
| 1924 } | 1974 } |
| 1925 | 1975 |
| 1926 bool get hasValue => true; | 1976 bool get hasValue => true; |
| 1927 | 1977 |
| 1928 void setParentPointers() { | 1978 void setParentPointers() { |
| 1929 input.parent = this; | 1979 input.parent = this; |
| 1930 } | 1980 } |
| 1931 } | 1981 } |
| 1932 | 1982 |
| 1983 Reference<Primitive> _reference(Primitive definition) { |
| 1984 return new Reference<Primitive>(definition); |
| 1985 } |
| 1986 |
| 1987 Reference<Primitive> _optionalReference(Primitive definition) { |
| 1988 return definition == null |
| 1989 ? null |
| 1990 : new Reference<Primitive>(definition); |
| 1991 } |
| 1992 |
| 1933 List<Reference<Primitive>> _referenceList(Iterable<Primitive> definitions) { | 1993 List<Reference<Primitive>> _referenceList(Iterable<Primitive> definitions) { |
| 1934 return definitions.map((e) => new Reference<Primitive>(e)).toList(); | 1994 return definitions.map((e) => new Reference<Primitive>(e)).toList(); |
| 1935 } | 1995 } |
| 1936 | 1996 |
| 1937 void _setParentsOnNodes(List<Node> nodes, Node parent) { | 1997 void _setParentsOnNodes(List<Node> nodes, Node parent) { |
| 1938 for (Node node in nodes) { | 1998 for (Node node in nodes) { |
| 1939 node.parent = parent; | 1999 node.parent = parent; |
| 1940 } | 2000 } |
| 1941 } | 2001 } |
| 1942 | 2002 |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2070 T visitCreateInvocationMirror(CreateInvocationMirror node); | 2130 T visitCreateInvocationMirror(CreateInvocationMirror node); |
| 2071 T visitTypeTest(TypeTest node); | 2131 T visitTypeTest(TypeTest node); |
| 2072 T visitTypeTestViaFlag(TypeTestViaFlag node); | 2132 T visitTypeTestViaFlag(TypeTestViaFlag node); |
| 2073 T visitApplyBuiltinOperator(ApplyBuiltinOperator node); | 2133 T visitApplyBuiltinOperator(ApplyBuiltinOperator node); |
| 2074 T visitApplyBuiltinMethod(ApplyBuiltinMethod node); | 2134 T visitApplyBuiltinMethod(ApplyBuiltinMethod node); |
| 2075 T visitGetLength(GetLength node); | 2135 T visitGetLength(GetLength node); |
| 2076 T visitGetIndex(GetIndex node); | 2136 T visitGetIndex(GetIndex node); |
| 2077 T visitSetIndex(SetIndex node); | 2137 T visitSetIndex(SetIndex node); |
| 2078 T visitRefinement(Refinement node); | 2138 T visitRefinement(Refinement node); |
| 2079 T visitBoundsCheck(BoundsCheck node); | 2139 T visitBoundsCheck(BoundsCheck node); |
| 2080 T visitNullCheck(NullCheck node); | 2140 T visitReceiverCheck(ReceiverCheck node); |
| 2081 T visitForeignCode(ForeignCode node); | 2141 T visitForeignCode(ForeignCode node); |
| 2082 } | 2142 } |
| 2083 | 2143 |
| 2084 /// Recursively visits all children of a CPS term. | 2144 /// Recursively visits all children of a CPS term. |
| 2085 /// | 2145 /// |
| 2086 /// The user of the class is responsible for avoiding stack overflows from | 2146 /// The user of the class is responsible for avoiding stack overflows from |
| 2087 /// deep recursion, e.g. by overriding methods to cut off recursion at certain | 2147 /// deep recursion, e.g. by overriding methods to cut off recursion at certain |
| 2088 /// points. | 2148 /// points. |
| 2089 /// | 2149 /// |
| 2090 /// All recursive invocations occur through the [visit] method, which the | 2150 /// All recursive invocations occur through the [visit] method, which the |
| (...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2393 processBoundsCheck(node); | 2453 processBoundsCheck(node); |
| 2394 processReference(node.object); | 2454 processReference(node.object); |
| 2395 if (node.index != null) { | 2455 if (node.index != null) { |
| 2396 processReference(node.index); | 2456 processReference(node.index); |
| 2397 } | 2457 } |
| 2398 if (node.length != null) { | 2458 if (node.length != null) { |
| 2399 processReference(node.length); | 2459 processReference(node.length); |
| 2400 } | 2460 } |
| 2401 } | 2461 } |
| 2402 | 2462 |
| 2403 processNullCheck(NullCheck node) {} | 2463 processNullCheck(ReceiverCheck node) {} |
| 2404 visitNullCheck(NullCheck node) { | 2464 visitReceiverCheck(ReceiverCheck node) { |
| 2405 processNullCheck(node); | 2465 processNullCheck(node); |
| 2406 processReference(node.value); | 2466 processReference(node.value); |
| 2407 if (node.condition != null) { | 2467 if (node.condition != null) { |
| 2408 processReference(node.condition); | 2468 processReference(node.condition); |
| 2409 } | 2469 } |
| 2410 } | 2470 } |
| 2411 } | 2471 } |
| 2412 | 2472 |
| 2413 typedef void StackAction(); | 2473 typedef void StackAction(); |
| 2414 | 2474 |
| (...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2754 return new BoundsCheck.noCheck(getCopy(node.object), | 2814 return new BoundsCheck.noCheck(getCopy(node.object), |
| 2755 node.sourceInformation); | 2815 node.sourceInformation); |
| 2756 } else { | 2816 } else { |
| 2757 return new BoundsCheck(getCopy(node.object), getCopy(node.index), | 2817 return new BoundsCheck(getCopy(node.object), getCopy(node.index), |
| 2758 node.length == null ? null : getCopy(node.length), | 2818 node.length == null ? null : getCopy(node.length), |
| 2759 node.checks, | 2819 node.checks, |
| 2760 node.sourceInformation); | 2820 node.sourceInformation); |
| 2761 } | 2821 } |
| 2762 } | 2822 } |
| 2763 | 2823 |
| 2764 Definition visitNullCheck(NullCheck node) { | 2824 Definition visitReceiverCheck(ReceiverCheck node) { |
| 2765 return new NullCheck(getCopy(node.value), node.sourceInformation, | 2825 return new ReceiverCheck(getCopy(node.value), |
| 2826 node.selector, |
| 2827 node.sourceInformation, |
| 2766 condition: node.condition == null ? null : getCopy(node.condition), | 2828 condition: node.condition == null ? null : getCopy(node.condition), |
| 2767 selector: node.selector, | 2829 useSelector: node.useSelector, |
| 2768 useSelector: node.useSelector); | 2830 isNullCheck: node.isNullCheck); |
| 2769 } | 2831 } |
| 2770 | 2832 |
| 2771 Definition visitForeignCode(ForeignCode node) { | 2833 Definition visitForeignCode(ForeignCode node) { |
| 2772 return new ForeignCode(node.codeTemplate, node.storedType, | 2834 return new ForeignCode(node.codeTemplate, node.storedType, |
| 2773 getList(node.arguments), | 2835 getList(node.arguments), |
| 2774 node.nativeBehavior, | 2836 node.nativeBehavior, |
| 2775 dependency: node.dependency); | 2837 dependency: node.dependency); |
| 2776 } | 2838 } |
| 2777 } | 2839 } |
| 2778 | 2840 |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2909 plug(new Branch.loose(_definitions.getCopy(node.condition), | 2971 plug(new Branch.loose(_definitions.getCopy(node.condition), |
| 2910 _copies[node.trueContinuation.definition], | 2972 _copies[node.trueContinuation.definition], |
| 2911 _copies[node.falseContinuation.definition]) | 2973 _copies[node.falseContinuation.definition]) |
| 2912 ..isStrictCheck = node.isStrictCheck); | 2974 ..isStrictCheck = node.isStrictCheck); |
| 2913 } | 2975 } |
| 2914 | 2976 |
| 2915 visitUnreachable(Unreachable node) { | 2977 visitUnreachable(Unreachable node) { |
| 2916 plug(new Unreachable()); | 2978 plug(new Unreachable()); |
| 2917 } | 2979 } |
| 2918 } | 2980 } |
| OLD | NEW |