| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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.cps_ir.type_propagation; | 4 library dart2js.cps_ir.type_propagation; |
| 5 | 5 |
| 6 import 'optimizers.dart'; | 6 import 'optimizers.dart'; |
| 7 | 7 |
| 8 import '../closure.dart' show | 8 import '../closure.dart' show ClosureClassElement; |
| 9 ClosureClassElement; | |
| 10 import '../common.dart'; | 9 import '../common.dart'; |
| 11 import '../common/names.dart' show | 10 import '../common/names.dart' show Identifiers, Selectors; |
| 12 Identifiers, | 11 import '../compiler.dart' as dart2js show Compiler; |
| 13 Selectors; | |
| 14 import '../compiler.dart' as dart2js show | |
| 15 Compiler; | |
| 16 import '../constants/constant_system.dart'; | 12 import '../constants/constant_system.dart'; |
| 17 import '../constants/values.dart'; | 13 import '../constants/values.dart'; |
| 18 import '../dart_types.dart' as types; | 14 import '../dart_types.dart' as types; |
| 19 import '../elements/elements.dart'; | 15 import '../elements/elements.dart'; |
| 20 import '../io/source_information.dart' show | 16 import '../io/source_information.dart' show SourceInformation; |
| 21 SourceInformation; | 17 import '../js_backend/backend_helpers.dart' show BackendHelpers; |
| 22 import '../js_backend/backend_helpers.dart' show | 18 import '../js_backend/js_backend.dart' show JavaScriptBackend; |
| 23 BackendHelpers; | 19 import '../js_backend/codegen/task.dart' show CpsFunctionCompiler; |
| 24 import '../js_backend/js_backend.dart' show | |
| 25 JavaScriptBackend; | |
| 26 import '../js_backend/codegen/task.dart' show | |
| 27 CpsFunctionCompiler; | |
| 28 import '../resolution/operators.dart'; | 20 import '../resolution/operators.dart'; |
| 29 import '../tree/tree.dart' as ast; | 21 import '../tree/tree.dart' as ast; |
| 30 import '../types/types.dart'; | 22 import '../types/types.dart'; |
| 31 import '../types/abstract_value_domain.dart' show | 23 import '../types/abstract_value_domain.dart' show AbstractBool; |
| 32 AbstractBool; | 24 import '../universe/selector.dart' show Selector; |
| 33 import '../universe/selector.dart' show | |
| 34 Selector; | |
| 35 import '../world.dart' show World; | 25 import '../world.dart' show World; |
| 36 import 'cps_fragment.dart'; | 26 import 'cps_fragment.dart'; |
| 37 import 'cps_ir_nodes.dart'; | 27 import 'cps_ir_nodes.dart'; |
| 38 import 'type_mask_system.dart'; | 28 import 'type_mask_system.dart'; |
| 39 import 'effects.dart'; | 29 import 'effects.dart'; |
| 40 | 30 |
| 41 class ConstantPropagationLattice { | 31 class ConstantPropagationLattice { |
| 42 final TypeMaskSystem typeSystem; | 32 final TypeMaskSystem typeSystem; |
| 43 final ConstantSystem constantSystem; | 33 final ConstantSystem constantSystem; |
| 44 final types.DartTypes dartTypes; | 34 final types.DartTypes dartTypes; |
| 45 final AbstractConstantValue anything; | 35 final AbstractConstantValue anything; |
| 46 final AbstractConstantValue nothing = new AbstractConstantValue.nothing(); | 36 final AbstractConstantValue nothing = new AbstractConstantValue.nothing(); |
| 47 final AbstractConstantValue nullValue; | 37 final AbstractConstantValue nullValue; |
| 48 final AbstractConstantValue trueValue; | 38 final AbstractConstantValue trueValue; |
| 49 final AbstractConstantValue falseValue; | 39 final AbstractConstantValue falseValue; |
| 50 | 40 |
| 51 ConstantPropagationLattice(CpsFunctionCompiler functionCompiler) | 41 ConstantPropagationLattice(CpsFunctionCompiler functionCompiler) |
| 52 : typeSystem = functionCompiler.typeSystem, | 42 : typeSystem = functionCompiler.typeSystem, |
| 53 constantSystem = functionCompiler.compiler.backend.constantSystem, | 43 constantSystem = functionCompiler.compiler.backend.constantSystem, |
| 54 dartTypes = functionCompiler.compiler.types, | 44 dartTypes = functionCompiler.compiler.types, |
| 55 anything = new AbstractConstantValue.nonConstant( | 45 anything = new AbstractConstantValue.nonConstant( |
| 56 functionCompiler.typeSystem.dynamicType), | 46 functionCompiler.typeSystem.dynamicType), |
| 57 nullValue = new AbstractConstantValue.constantValue( | 47 nullValue = new AbstractConstantValue.constantValue( |
| 58 new NullConstantValue(), new TypeMask.empty()), | 48 new NullConstantValue(), new TypeMask.empty()), |
| 59 trueValue = new AbstractConstantValue.constantValue( | 49 trueValue = new AbstractConstantValue.constantValue( |
| 60 new TrueConstantValue(), functionCompiler.typeSystem.boolType), | 50 new TrueConstantValue(), functionCompiler.typeSystem.boolType), |
| 61 falseValue = new AbstractConstantValue.constantValue( | 51 falseValue = new AbstractConstantValue.constantValue( |
| 62 new FalseConstantValue(), functionCompiler.typeSystem.boolType); | 52 new FalseConstantValue(), functionCompiler.typeSystem.boolType); |
| 63 | 53 |
| 64 AbstractConstantValue constant(ConstantValue value, [TypeMask type]) { | 54 AbstractConstantValue constant(ConstantValue value, [TypeMask type]) { |
| 65 if (type == null) type = typeSystem.getTypeOf(value); | 55 if (type == null) type = typeSystem.getTypeOf(value); |
| 66 return new AbstractConstantValue.constantValue(value, type); | 56 return new AbstractConstantValue.constantValue(value, type); |
| 67 } | 57 } |
| 68 | 58 |
| 69 AbstractConstantValue nonConstant([TypeMask type]) { | 59 AbstractConstantValue nonConstant([TypeMask type]) { |
| 70 if (type == null) type = typeSystem.dynamicType; | 60 if (type == null) type = typeSystem.dynamicType; |
| 71 return new AbstractConstantValue.nonConstant(type); | 61 return new AbstractConstantValue.nonConstant(type); |
| 72 } | 62 } |
| (...skipping 11 matching lines...) Expand all Loading... |
| 84 return x; | 74 return x; |
| 85 } else { | 75 } else { |
| 86 return new AbstractConstantValue.nonConstant( | 76 return new AbstractConstantValue.nonConstant( |
| 87 typeSystem.join(x.type, y.type)); | 77 typeSystem.join(x.type, y.type)); |
| 88 } | 78 } |
| 89 } | 79 } |
| 90 | 80 |
| 91 /// True if all members of this value are booleans. | 81 /// True if all members of this value are booleans. |
| 92 bool isDefinitelyBool(AbstractConstantValue value, {bool allowNull: false}) { | 82 bool isDefinitelyBool(AbstractConstantValue value, {bool allowNull: false}) { |
| 93 return value.isNothing || | 83 return value.isNothing || |
| 94 typeSystem.isDefinitelyBool(value.type, allowNull: allowNull); | 84 typeSystem.isDefinitelyBool(value.type, allowNull: allowNull); |
| 95 } | 85 } |
| 96 | 86 |
| 97 /// True if all members of this value are numbers. | 87 /// True if all members of this value are numbers. |
| 98 bool isDefinitelyNum(AbstractConstantValue value, {bool allowNull: false}) { | 88 bool isDefinitelyNum(AbstractConstantValue value, {bool allowNull: false}) { |
| 99 return value.isNothing || | 89 return value.isNothing || |
| 100 typeSystem.isDefinitelyNum(value.type, allowNull: allowNull); | 90 typeSystem.isDefinitelyNum(value.type, allowNull: allowNull); |
| 101 } | 91 } |
| 102 | 92 |
| 103 /// True if all members of this value are strings. | 93 /// True if all members of this value are strings. |
| 104 bool isDefinitelyString(AbstractConstantValue value, | 94 bool isDefinitelyString(AbstractConstantValue value, |
| 105 {bool allowNull: false}) { | 95 {bool allowNull: false}) { |
| 106 return value.isNothing || | 96 return value.isNothing || |
| 107 typeSystem.isDefinitelyString(value.type, allowNull: allowNull); | 97 typeSystem.isDefinitelyString(value.type, allowNull: allowNull); |
| 108 } | 98 } |
| 109 | 99 |
| 110 /// True if all members of this value are numbers, strings, or booleans. | 100 /// True if all members of this value are numbers, strings, or booleans. |
| 111 bool isDefinitelyNumStringBool(AbstractConstantValue value, | 101 bool isDefinitelyNumStringBool(AbstractConstantValue value, |
| 112 {bool allowNull: false}) { | 102 {bool allowNull: false}) { |
| 113 return value.isNothing || | 103 return value.isNothing || |
| 114 typeSystem.isDefinitelyNumStringBool(value.type, allowNull: allowNull); | 104 typeSystem.isDefinitelyNumStringBool(value.type, allowNull: allowNull); |
| 115 } | 105 } |
| 116 | 106 |
| 117 /// True if this value cannot be a string, number, or boolean. | 107 /// True if this value cannot be a string, number, or boolean. |
| 118 bool isDefinitelyNotNumStringBool(AbstractConstantValue value) { | 108 bool isDefinitelyNotNumStringBool(AbstractConstantValue value) { |
| 119 return value.isNothing || | 109 return value.isNothing || |
| 120 typeSystem.isDefinitelyNotNumStringBool(value.type); | 110 typeSystem.isDefinitelyNotNumStringBool(value.type); |
| 121 } | 111 } |
| 122 | 112 |
| 123 /// True if this value cannot be a non-integer double. | 113 /// True if this value cannot be a non-integer double. |
| 124 /// | 114 /// |
| 125 /// In other words, if true is returned, and the value is a number, then | 115 /// In other words, if true is returned, and the value is a number, then |
| 126 /// it is a whole number and is not NaN, Infinity, or minus Infinity. | 116 /// it is a whole number and is not NaN, Infinity, or minus Infinity. |
| 127 bool isDefinitelyNotNonIntegerDouble(AbstractConstantValue value) { | 117 bool isDefinitelyNotNonIntegerDouble(AbstractConstantValue value) { |
| 128 return value.isNothing || | 118 return value.isNothing || |
| 129 value.isConstant && !value.constant.isDouble || | 119 value.isConstant && !value.constant.isDouble || |
| 130 typeSystem.isDefinitelyNotNonIntegerDouble(value.type); | 120 typeSystem.isDefinitelyNotNonIntegerDouble(value.type); |
| 131 } | 121 } |
| 132 | 122 |
| 133 bool isDefinitelyInt(AbstractConstantValue value, | 123 bool isDefinitelyInt(AbstractConstantValue value, {bool allowNull: false}) { |
| 134 {bool allowNull: false}) { | |
| 135 return value.isNothing || | 124 return value.isNothing || |
| 136 typeSystem.isDefinitelyInt(value.type, allowNull: allowNull); | 125 typeSystem.isDefinitelyInt(value.type, allowNull: allowNull); |
| 137 } | 126 } |
| 138 | 127 |
| 139 bool isDefinitelyUint31(AbstractConstantValue value, | 128 bool isDefinitelyUint31(AbstractConstantValue value, |
| 140 {bool allowNull: false}) { | 129 {bool allowNull: false}) { |
| 141 return value.isNothing || | 130 return value.isNothing || |
| 142 typeSystem.isDefinitelyUint31(value.type, allowNull: allowNull); | 131 typeSystem.isDefinitelyUint31(value.type, allowNull: allowNull); |
| 143 } | 132 } |
| 144 | 133 |
| 145 bool isDefinitelyUint32(AbstractConstantValue value, | 134 bool isDefinitelyUint32(AbstractConstantValue value, |
| 146 {bool allowNull: false}) { | 135 {bool allowNull: false}) { |
| 147 return value.isNothing || | 136 return value.isNothing || |
| 148 typeSystem.isDefinitelyUint32(value.type, allowNull: allowNull); | 137 typeSystem.isDefinitelyUint32(value.type, allowNull: allowNull); |
| 149 } | 138 } |
| 150 | 139 |
| 151 bool isDefinitelyUint(AbstractConstantValue value, | 140 bool isDefinitelyUint(AbstractConstantValue value, {bool allowNull: false}) { |
| 152 {bool allowNull: false}) { | |
| 153 return value.isNothing || | 141 return value.isNothing || |
| 154 typeSystem.isDefinitelyUint(value.type, allowNull: allowNull); | 142 typeSystem.isDefinitelyUint(value.type, allowNull: allowNull); |
| 155 } | 143 } |
| 156 | 144 |
| 157 bool isDefinitelyArray(AbstractConstantValue value, | 145 bool isDefinitelyArray(AbstractConstantValue value, {bool allowNull: false}) { |
| 158 {bool allowNull: false}) { | |
| 159 return value.isNothing || | 146 return value.isNothing || |
| 160 typeSystem.isDefinitelyArray(value.type, allowNull: allowNull); | 147 typeSystem.isDefinitelyArray(value.type, allowNull: allowNull); |
| 161 } | 148 } |
| 162 | 149 |
| 163 bool isDefinitelyMutableArray(AbstractConstantValue value, | 150 bool isDefinitelyMutableArray(AbstractConstantValue value, |
| 164 {bool allowNull: false}) { | 151 {bool allowNull: false}) { |
| 165 return value.isNothing || | 152 return value.isNothing || |
| 166 typeSystem.isDefinitelyMutableArray(value.type, | 153 typeSystem.isDefinitelyMutableArray(value.type, allowNull: allowNull); |
| 167 allowNull: allowNull); | |
| 168 } | 154 } |
| 169 | 155 |
| 170 bool isDefinitelyFixedArray(AbstractConstantValue value, | 156 bool isDefinitelyFixedArray(AbstractConstantValue value, |
| 171 {bool allowNull: false}) { | 157 {bool allowNull: false}) { |
| 172 return value.isNothing || | 158 return value.isNothing || |
| 173 typeSystem.isDefinitelyFixedArray(value.type, | 159 typeSystem.isDefinitelyFixedArray(value.type, allowNull: allowNull); |
| 174 allowNull: allowNull); | |
| 175 } | 160 } |
| 176 | 161 |
| 177 bool isDefinitelyExtendableArray(AbstractConstantValue value, | 162 bool isDefinitelyExtendableArray(AbstractConstantValue value, |
| 178 {bool allowNull: false}) { | 163 {bool allowNull: false}) { |
| 179 return value.isNothing || | 164 return value.isNothing || |
| 180 typeSystem.isDefinitelyExtendableArray(value.type, | 165 typeSystem.isDefinitelyExtendableArray(value.type, |
| 181 allowNull: allowNull); | 166 allowNull: allowNull); |
| 182 } | 167 } |
| 183 | 168 |
| 184 bool isDefinitelyIndexable(AbstractConstantValue value, | 169 bool isDefinitelyIndexable(AbstractConstantValue value, |
| 185 {bool allowNull: false}) { | 170 {bool allowNull: false}) { |
| 186 return value.isNothing || | 171 return value.isNothing || |
| 187 typeSystem.isDefinitelyIndexable(value.type, allowNull: allowNull); | 172 typeSystem.isDefinitelyIndexable(value.type, allowNull: allowNull); |
| 188 } | 173 } |
| 189 | 174 |
| 190 /// Returns `true` if [value] represents an int value that must be in the | 175 /// Returns `true` if [value] represents an int value that must be in the |
| 191 /// inclusive range. | 176 /// inclusive range. |
| 192 bool isDefinitelyIntInRange(AbstractConstantValue value, {int min, int max}) { | 177 bool isDefinitelyIntInRange(AbstractConstantValue value, {int min, int max}) { |
| 193 if (value.isNothing) return true; | 178 if (value.isNothing) return true; |
| 194 if (!isDefinitelyInt(value)) return false; | 179 if (!isDefinitelyInt(value)) return false; |
| 195 PrimitiveConstantValue constant = value.constant; | 180 PrimitiveConstantValue constant = value.constant; |
| 196 if (constant == null) return false; | 181 if (constant == null) return false; |
| 197 if (!constant.isInt) return false; | 182 if (!constant.isInt) return false; |
| 198 if (min != null && constant.primitiveValue < min) return false; | 183 if (min != null && constant.primitiveValue < min) return false; |
| 199 if (max != null && constant.primitiveValue > max) return false; | 184 if (max != null && constant.primitiveValue > max) return false; |
| 200 return true; | 185 return true; |
| 201 } | 186 } |
| 202 | 187 |
| 203 /// Returns whether the given [value] is an instance of [type]. | 188 /// Returns whether the given [value] is an instance of [type]. |
| 204 /// | 189 /// |
| 205 /// Since [value] and [type] are not always known, [AbstractBool.Maybe] is | 190 /// Since [value] and [type] are not always known, [AbstractBool.Maybe] is |
| 206 /// returned if the answer is not known. | 191 /// returned if the answer is not known. |
| 207 /// | 192 /// |
| 208 /// [AbstractBool.Nothing] is returned if [value] is nothing. | 193 /// [AbstractBool.Nothing] is returned if [value] is nothing. |
| 209 /// | 194 /// |
| 210 /// If [allowNull] is true, `null` is considered an instance of anything, | 195 /// If [allowNull] is true, `null` is considered an instance of anything, |
| 211 /// otherwise it is only considered an instance of [Object], [dynamic], and | 196 /// otherwise it is only considered an instance of [Object], [dynamic], and |
| 212 /// [Null]. | 197 /// [Null]. |
| 213 AbstractBool isSubtypeOf(AbstractConstantValue value, | 198 AbstractBool isSubtypeOf(AbstractConstantValue value, types.DartType type, |
| 214 types.DartType type, | 199 {bool allowNull}) { |
| 215 {bool allowNull}) { | |
| 216 assert(allowNull != null); | 200 assert(allowNull != null); |
| 217 if (value.isNothing) { | 201 if (value.isNothing) { |
| 218 return AbstractBool.Nothing; | 202 return AbstractBool.Nothing; |
| 219 } | 203 } |
| 220 if (value.isConstant) { | 204 if (value.isConstant) { |
| 221 if (value.constant.isNull) { | 205 if (value.constant.isNull) { |
| 222 if (allowNull || | 206 if (allowNull || |
| 223 type.isObject || | 207 type.isObject || |
| 224 type.isDynamic || | 208 type.isDynamic || |
| 225 type == dartTypes.coreTypes.nullType) { | 209 type == dartTypes.coreTypes.nullType) { |
| 226 return AbstractBool.True; | 210 return AbstractBool.True; |
| 227 } | 211 } |
| 228 if (type is types.TypeVariableType) { | 212 if (type is types.TypeVariableType) { |
| 229 return AbstractBool.Maybe; | 213 return AbstractBool.Maybe; |
| 230 } | 214 } |
| 231 return AbstractBool.False; | 215 return AbstractBool.False; |
| 232 } | 216 } |
| 233 if (type == dartTypes.coreTypes.intType) { | 217 if (type == dartTypes.coreTypes.intType) { |
| 234 return constantSystem.isInt(value.constant) | 218 return constantSystem.isInt(value.constant) |
| 235 ? AbstractBool.True | 219 ? AbstractBool.True |
| 236 : AbstractBool.False; | 220 : AbstractBool.False; |
| 237 } | 221 } |
| 238 types.DartType valueType = value.constant.getType(dartTypes.coreTypes); | 222 types.DartType valueType = value.constant.getType(dartTypes.coreTypes); |
| 239 if (constantSystem.isSubtype(dartTypes, valueType, type)) { | 223 if (constantSystem.isSubtype(dartTypes, valueType, type)) { |
| 240 return AbstractBool.True; | 224 return AbstractBool.True; |
| 241 } | 225 } |
| 242 if (!dartTypes.isPotentialSubtype(valueType, type)) { | 226 if (!dartTypes.isPotentialSubtype(valueType, type)) { |
| 243 return AbstractBool.False; | 227 return AbstractBool.False; |
| 244 } | 228 } |
| 245 return AbstractBool.Maybe; | 229 return AbstractBool.Maybe; |
| 246 } | 230 } |
| 247 return typeSystem.isSubtypeOf(value.type, type, allowNull: allowNull); | 231 return typeSystem.isSubtypeOf(value.type, type, allowNull: allowNull); |
| 248 } | 232 } |
| 249 | 233 |
| 250 /// Returns the possible results of applying [operator] to [value], | 234 /// Returns the possible results of applying [operator] to [value], |
| 251 /// assuming the operation does not throw. | 235 /// assuming the operation does not throw. |
| 252 /// | 236 /// |
| 253 /// Because we do not explicitly track thrown values, we currently use the | 237 /// Because we do not explicitly track thrown values, we currently use the |
| 254 /// convention that constant values are returned from this method only | 238 /// convention that constant values are returned from this method only |
| 255 /// if the operation is known not to throw. | 239 /// if the operation is known not to throw. |
| 256 /// | 240 /// |
| 257 /// This method returns `null` if a good result could not be found. In that | 241 /// This method returns `null` if a good result could not be found. In that |
| 258 /// case, it is best to fall back on interprocedural type information. | 242 /// case, it is best to fall back on interprocedural type information. |
| 259 AbstractConstantValue unaryOp(UnaryOperator operator, | 243 AbstractConstantValue unaryOp( |
| 260 AbstractConstantValue value) { | 244 UnaryOperator operator, AbstractConstantValue value) { |
| 261 switch (operator.kind) { | 245 switch (operator.kind) { |
| 262 case UnaryOperatorKind.COMPLEMENT: | 246 case UnaryOperatorKind.COMPLEMENT: |
| 263 return bitNotSpecial(value); | 247 return bitNotSpecial(value); |
| 264 case UnaryOperatorKind.NEGATE: | 248 case UnaryOperatorKind.NEGATE: |
| 265 return negateSpecial(value); | 249 return negateSpecial(value); |
| 266 default: | 250 default: |
| 267 break; | 251 break; |
| 268 } | 252 } |
| 269 // TODO(asgerf): Also return information about whether this can throw? | 253 // TODO(asgerf): Also return information about whether this can throw? |
| 270 if (value.isNothing) { | 254 if (value.isNothing) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 282 /// Returns the possible results of applying [operator] to [left], [right], | 266 /// Returns the possible results of applying [operator] to [left], [right], |
| 283 /// assuming the operation does not throw. | 267 /// assuming the operation does not throw. |
| 284 /// | 268 /// |
| 285 /// Because we do not explicitly track thrown values, we currently use the | 269 /// Because we do not explicitly track thrown values, we currently use the |
| 286 /// convention that constant values are returned from this method only | 270 /// convention that constant values are returned from this method only |
| 287 /// if the operation is known not to throw. | 271 /// if the operation is known not to throw. |
| 288 /// | 272 /// |
| 289 /// This method returns `null` if a good result could not be found. In that | 273 /// This method returns `null` if a good result could not be found. In that |
| 290 /// case, it is best to fall back on interprocedural type information. | 274 /// case, it is best to fall back on interprocedural type information. |
| 291 AbstractConstantValue binaryOp(BinaryOperator operator, | 275 AbstractConstantValue binaryOp(BinaryOperator operator, |
| 292 AbstractConstantValue left, | 276 AbstractConstantValue left, AbstractConstantValue right) { |
| 293 AbstractConstantValue right) { | |
| 294 switch (operator.kind) { | 277 switch (operator.kind) { |
| 295 case BinaryOperatorKind.ADD: | 278 case BinaryOperatorKind.ADD: |
| 296 return addSpecial(left, right); | 279 return addSpecial(left, right); |
| 297 | 280 |
| 298 case BinaryOperatorKind.SUB: | 281 case BinaryOperatorKind.SUB: |
| 299 return subtractSpecial(left, right); | 282 return subtractSpecial(left, right); |
| 300 | 283 |
| 301 case BinaryOperatorKind.MUL: | 284 case BinaryOperatorKind.MUL: |
| 302 return multiplySpecial(left, right); | 285 return multiplySpecial(left, right); |
| 303 | 286 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 348 return nothing; | 331 return nothing; |
| 349 } | 332 } |
| 350 if (left.isConstant && right.isConstant) { | 333 if (left.isConstant && right.isConstant) { |
| 351 BinaryOperation operation = constantSystem.lookupBinary(operator); | 334 BinaryOperation operation = constantSystem.lookupBinary(operator); |
| 352 ConstantValue result = operation.fold(left.constant, right.constant); | 335 ConstantValue result = operation.fold(left.constant, right.constant); |
| 353 if (result != null) return constant(result); | 336 if (result != null) return constant(result); |
| 354 } | 337 } |
| 355 return null; // The caller will use return type from type inference. | 338 return null; // The caller will use return type from type inference. |
| 356 } | 339 } |
| 357 | 340 |
| 358 AbstractConstantValue foldUnary(UnaryOperation operation, | 341 AbstractConstantValue foldUnary( |
| 359 AbstractConstantValue value) { | 342 UnaryOperation operation, AbstractConstantValue value) { |
| 360 if (value.isNothing) return nothing; | 343 if (value.isNothing) return nothing; |
| 361 if (value.isConstant) { | 344 if (value.isConstant) { |
| 362 ConstantValue result = operation.fold(value.constant); | 345 ConstantValue result = operation.fold(value.constant); |
| 363 if (result != null) return constant(result); | 346 if (result != null) return constant(result); |
| 364 } | 347 } |
| 365 return null; | 348 return null; |
| 366 } | 349 } |
| 367 | 350 |
| 368 AbstractConstantValue bitNotSpecial(AbstractConstantValue value) { | 351 AbstractConstantValue bitNotSpecial(AbstractConstantValue value) { |
| 369 return foldUnary(constantSystem.bitNot, value); | 352 return foldUnary(constantSystem.bitNot, value); |
| 370 } | 353 } |
| 371 | 354 |
| 372 AbstractConstantValue negateSpecial(AbstractConstantValue value) { | 355 AbstractConstantValue negateSpecial(AbstractConstantValue value) { |
| 373 AbstractConstantValue folded = foldUnary(constantSystem.negate, value); | 356 AbstractConstantValue folded = foldUnary(constantSystem.negate, value); |
| 374 if (folded != null) return folded; | 357 if (folded != null) return folded; |
| 375 if (isDefinitelyInt(value, allowNull: true)) { | 358 if (isDefinitelyInt(value, allowNull: true)) { |
| 376 return nonConstant(typeSystem.intType); | 359 return nonConstant(typeSystem.intType); |
| 377 } | 360 } |
| 378 return null; | 361 return null; |
| 379 } | 362 } |
| 380 | 363 |
| 381 | |
| 382 AbstractConstantValue foldBinary(BinaryOperation operation, | 364 AbstractConstantValue foldBinary(BinaryOperation operation, |
| 383 AbstractConstantValue left, AbstractConstantValue right) { | 365 AbstractConstantValue left, AbstractConstantValue right) { |
| 384 if (left.isNothing || right.isNothing) return nothing; | 366 if (left.isNothing || right.isNothing) return nothing; |
| 385 if (left.isConstant && right.isConstant) { | 367 if (left.isConstant && right.isConstant) { |
| 386 ConstantValue result = operation.fold(left.constant, right.constant); | 368 ConstantValue result = operation.fold(left.constant, right.constant); |
| 387 if (result != null) return constant(result); | 369 if (result != null) return constant(result); |
| 388 } | 370 } |
| 389 return null; | 371 return null; |
| 390 } | 372 } |
| 391 | 373 |
| 392 AbstractConstantValue closedOnInt(AbstractConstantValue left, | 374 AbstractConstantValue closedOnInt( |
| 393 AbstractConstantValue right) { | 375 AbstractConstantValue left, AbstractConstantValue right) { |
| 394 if (isDefinitelyInt(left, allowNull: true) && | 376 if (isDefinitelyInt(left, allowNull: true) && |
| 395 isDefinitelyInt(right, allowNull: true)) { | 377 isDefinitelyInt(right, allowNull: true)) { |
| 396 return nonConstant(typeSystem.intType); | 378 return nonConstant(typeSystem.intType); |
| 397 } | 379 } |
| 398 return null; | 380 return null; |
| 399 } | 381 } |
| 400 | 382 |
| 401 AbstractConstantValue closedOnUint(AbstractConstantValue left, | 383 AbstractConstantValue closedOnUint( |
| 402 AbstractConstantValue right) { | 384 AbstractConstantValue left, AbstractConstantValue right) { |
| 403 if (isDefinitelyUint(left, allowNull: true) && | 385 if (isDefinitelyUint(left, allowNull: true) && |
| 404 isDefinitelyUint(right, allowNull: true)) { | 386 isDefinitelyUint(right, allowNull: true)) { |
| 405 return nonConstant(typeSystem.uintType); | 387 return nonConstant(typeSystem.uintType); |
| 406 } | 388 } |
| 407 return null; | 389 return null; |
| 408 } | 390 } |
| 409 | 391 |
| 410 AbstractConstantValue closedOnUint31(AbstractConstantValue left, | 392 AbstractConstantValue closedOnUint31( |
| 411 AbstractConstantValue right) { | 393 AbstractConstantValue left, AbstractConstantValue right) { |
| 412 if (isDefinitelyUint31(left, allowNull: true) && | 394 if (isDefinitelyUint31(left, allowNull: true) && |
| 413 isDefinitelyUint31(right, allowNull: true)) { | 395 isDefinitelyUint31(right, allowNull: true)) { |
| 414 return nonConstant(typeSystem.uint31Type); | 396 return nonConstant(typeSystem.uint31Type); |
| 415 } | 397 } |
| 416 return null; | 398 return null; |
| 417 } | 399 } |
| 418 | 400 |
| 419 AbstractConstantValue addSpecial(AbstractConstantValue left, | 401 AbstractConstantValue addSpecial( |
| 420 AbstractConstantValue right) { | 402 AbstractConstantValue left, AbstractConstantValue right) { |
| 421 AbstractConstantValue folded = foldBinary(constantSystem.add, left, right); | 403 AbstractConstantValue folded = foldBinary(constantSystem.add, left, right); |
| 422 if (folded != null) return folded; | 404 if (folded != null) return folded; |
| 423 if (isDefinitelyNum(left, allowNull: true)) { | 405 if (isDefinitelyNum(left, allowNull: true)) { |
| 424 if (isDefinitelyUint31(left, allowNull: true) && | 406 if (isDefinitelyUint31(left, allowNull: true) && |
| 425 isDefinitelyUint31(right, allowNull: true)) { | 407 isDefinitelyUint31(right, allowNull: true)) { |
| 426 return nonConstant(typeSystem.uint32Type); | 408 return nonConstant(typeSystem.uint32Type); |
| 427 } | 409 } |
| 428 return closedOnUint(left, right) ?? closedOnInt(left, right); | 410 return closedOnUint(left, right) ?? closedOnInt(left, right); |
| 429 } | 411 } |
| 430 return null; | 412 return null; |
| 431 } | 413 } |
| 432 | 414 |
| 433 AbstractConstantValue subtractSpecial(AbstractConstantValue left, | 415 AbstractConstantValue subtractSpecial( |
| 434 AbstractConstantValue right) { | 416 AbstractConstantValue left, AbstractConstantValue right) { |
| 435 AbstractConstantValue folded = | 417 AbstractConstantValue folded = |
| 436 foldBinary(constantSystem.subtract, left, right); | 418 foldBinary(constantSystem.subtract, left, right); |
| 437 return folded ?? closedOnInt(left, right); | 419 return folded ?? closedOnInt(left, right); |
| 438 } | 420 } |
| 439 | 421 |
| 440 AbstractConstantValue multiplySpecial(AbstractConstantValue left, | 422 AbstractConstantValue multiplySpecial( |
| 441 AbstractConstantValue right) { | 423 AbstractConstantValue left, AbstractConstantValue right) { |
| 442 AbstractConstantValue folded = | 424 AbstractConstantValue folded = |
| 443 foldBinary(constantSystem.multiply, left, right); | 425 foldBinary(constantSystem.multiply, left, right); |
| 444 return folded ?? closedOnUint(left, right) ?? closedOnInt(left, right); | 426 return folded ?? closedOnUint(left, right) ?? closedOnInt(left, right); |
| 445 } | 427 } |
| 446 | 428 |
| 447 AbstractConstantValue divideSpecial(AbstractConstantValue left, | 429 AbstractConstantValue divideSpecial( |
| 448 AbstractConstantValue right) { | 430 AbstractConstantValue left, AbstractConstantValue right) { |
| 449 return foldBinary(constantSystem.divide, left, right); | 431 return foldBinary(constantSystem.divide, left, right); |
| 450 } | 432 } |
| 451 | 433 |
| 452 AbstractConstantValue truncatingDivideSpecial( | 434 AbstractConstantValue truncatingDivideSpecial( |
| 453 AbstractConstantValue left, AbstractConstantValue right) { | 435 AbstractConstantValue left, AbstractConstantValue right) { |
| 454 AbstractConstantValue folded = | 436 AbstractConstantValue folded = |
| 455 foldBinary(constantSystem.truncatingDivide, left, right); | 437 foldBinary(constantSystem.truncatingDivide, left, right); |
| 456 if (folded != null) return folded; | 438 if (folded != null) return folded; |
| 457 if (isDefinitelyNum(left, allowNull: true)) { | 439 if (isDefinitelyNum(left, allowNull: true)) { |
| 458 if (isDefinitelyUint32(left, allowNull: true) && | 440 if (isDefinitelyUint32(left, allowNull: true) && |
| (...skipping 10 matching lines...) Expand all Loading... |
| 469 } | 451 } |
| 470 if (isDefinitelyUint(left, allowNull: true)) { | 452 if (isDefinitelyUint(left, allowNull: true)) { |
| 471 return nonConstant(typeSystem.uintType); | 453 return nonConstant(typeSystem.uintType); |
| 472 } | 454 } |
| 473 } | 455 } |
| 474 return nonConstant(typeSystem.intType); | 456 return nonConstant(typeSystem.intType); |
| 475 } | 457 } |
| 476 return null; | 458 return null; |
| 477 } | 459 } |
| 478 | 460 |
| 479 AbstractConstantValue moduloSpecial(AbstractConstantValue left, | 461 AbstractConstantValue moduloSpecial( |
| 480 AbstractConstantValue right) { | 462 AbstractConstantValue left, AbstractConstantValue right) { |
| 481 AbstractConstantValue folded = | 463 AbstractConstantValue folded = |
| 482 foldBinary(constantSystem.modulo, left, right); | 464 foldBinary(constantSystem.modulo, left, right); |
| 483 return folded ?? closedOnUint(left, right) ?? closedOnInt(left, right); | 465 return folded ?? closedOnUint(left, right) ?? closedOnInt(left, right); |
| 484 } | 466 } |
| 485 | 467 |
| 486 AbstractConstantValue remainderSpecial(AbstractConstantValue left, | 468 AbstractConstantValue remainderSpecial( |
| 487 AbstractConstantValue right) { | 469 AbstractConstantValue left, AbstractConstantValue right) { |
| 488 if (left.isNothing || right.isNothing) return nothing; | 470 if (left.isNothing || right.isNothing) return nothing; |
| 489 AbstractConstantValue folded = null; // Remainder not in constant system. | 471 AbstractConstantValue folded = null; // Remainder not in constant system. |
| 490 return folded ?? closedOnUint(left, right) ?? closedOnInt(left, right); | 472 return folded ?? closedOnUint(left, right) ?? closedOnInt(left, right); |
| 491 } | 473 } |
| 492 | 474 |
| 493 AbstractConstantValue codeUnitAtSpecial(AbstractConstantValue left, | 475 AbstractConstantValue codeUnitAtSpecial( |
| 494 AbstractConstantValue right) { | 476 AbstractConstantValue left, AbstractConstantValue right) { |
| 495 return foldBinary(constantSystem.codeUnitAt, left, right); | 477 return foldBinary(constantSystem.codeUnitAt, left, right); |
| 496 } | 478 } |
| 497 | 479 |
| 498 AbstractConstantValue equalSpecial(AbstractConstantValue left, | 480 AbstractConstantValue equalSpecial( |
| 499 AbstractConstantValue right) { | 481 AbstractConstantValue left, AbstractConstantValue right) { |
| 500 AbstractConstantValue folded = | 482 AbstractConstantValue folded = |
| 501 foldBinary(constantSystem.equal, left, right); | 483 foldBinary(constantSystem.equal, left, right); |
| 502 if (folded != null) return folded; | 484 if (folded != null) return folded; |
| 503 bool behavesLikeIdentity = | 485 bool behavesLikeIdentity = |
| 504 isDefinitelyNumStringBool(left, allowNull: true) || | 486 isDefinitelyNumStringBool(left, allowNull: true) || |
| 505 right.isNullConstant; | 487 right.isNullConstant; |
| 506 if (behavesLikeIdentity && | 488 if (behavesLikeIdentity && typeSystem.areDisjoint(left.type, right.type)) { |
| 507 typeSystem.areDisjoint(left.type, right.type)) { | |
| 508 return constant(new FalseConstantValue()); | 489 return constant(new FalseConstantValue()); |
| 509 } | 490 } |
| 510 return null; | 491 return null; |
| 511 } | 492 } |
| 512 | 493 |
| 513 AbstractConstantValue andSpecial(AbstractConstantValue left, | 494 AbstractConstantValue andSpecial( |
| 514 AbstractConstantValue right) { | 495 AbstractConstantValue left, AbstractConstantValue right) { |
| 515 AbstractConstantValue folded = | 496 AbstractConstantValue folded = |
| 516 foldBinary(constantSystem.bitAnd, left, right); | 497 foldBinary(constantSystem.bitAnd, left, right); |
| 517 if (folded != null) return folded; | 498 if (folded != null) return folded; |
| 518 if (isDefinitelyNum(left, allowNull: true)) { | 499 if (isDefinitelyNum(left, allowNull: true)) { |
| 519 if (isDefinitelyUint31(left, allowNull: true) || | 500 if (isDefinitelyUint31(left, allowNull: true) || |
| 520 isDefinitelyUint31(right, allowNull: true)) { | 501 isDefinitelyUint31(right, allowNull: true)) { |
| 521 // Either 31-bit argument will truncate the other. | 502 // Either 31-bit argument will truncate the other. |
| 522 return nonConstant(typeSystem.uint31Type); | 503 return nonConstant(typeSystem.uint31Type); |
| 523 } | 504 } |
| 524 } | 505 } |
| 525 return null; | 506 return null; |
| 526 } | 507 } |
| 527 | 508 |
| 528 AbstractConstantValue orSpecial(AbstractConstantValue left, | 509 AbstractConstantValue orSpecial( |
| 529 AbstractConstantValue right) { | 510 AbstractConstantValue left, AbstractConstantValue right) { |
| 530 AbstractConstantValue folded = | 511 AbstractConstantValue folded = |
| 531 foldBinary(constantSystem.bitOr, left, right); | 512 foldBinary(constantSystem.bitOr, left, right); |
| 532 return folded ?? closedOnUint31(left, right); | 513 return folded ?? closedOnUint31(left, right); |
| 533 } | 514 } |
| 534 | 515 |
| 535 AbstractConstantValue xorSpecial(AbstractConstantValue left, | 516 AbstractConstantValue xorSpecial( |
| 536 AbstractConstantValue right) { | 517 AbstractConstantValue left, AbstractConstantValue right) { |
| 537 AbstractConstantValue folded = | 518 AbstractConstantValue folded = |
| 538 foldBinary(constantSystem.bitXor, left, right); | 519 foldBinary(constantSystem.bitXor, left, right); |
| 539 return folded ?? closedOnUint31(left, right); | 520 return folded ?? closedOnUint31(left, right); |
| 540 } | 521 } |
| 541 | 522 |
| 542 AbstractConstantValue shiftLeftSpecial(AbstractConstantValue left, | 523 AbstractConstantValue shiftLeftSpecial( |
| 543 AbstractConstantValue right) { | 524 AbstractConstantValue left, AbstractConstantValue right) { |
| 544 return foldBinary(constantSystem.shiftLeft, left, right); | 525 return foldBinary(constantSystem.shiftLeft, left, right); |
| 545 } | 526 } |
| 546 | 527 |
| 547 AbstractConstantValue shiftRightSpecial(AbstractConstantValue left, | 528 AbstractConstantValue shiftRightSpecial( |
| 548 AbstractConstantValue right) { | 529 AbstractConstantValue left, AbstractConstantValue right) { |
| 549 AbstractConstantValue folded = | 530 AbstractConstantValue folded = |
| 550 foldBinary(constantSystem.shiftRight, left, right); | 531 foldBinary(constantSystem.shiftRight, left, right); |
| 551 if (folded != null) return folded; | 532 if (folded != null) return folded; |
| 552 if (isDefinitelyUint31(left, allowNull: true)) { | 533 if (isDefinitelyUint31(left, allowNull: true)) { |
| 553 return nonConstant(typeSystem.uint31Type); | 534 return nonConstant(typeSystem.uint31Type); |
| 554 } else if (isDefinitelyUint32(left, allowNull: true)) { | 535 } else if (isDefinitelyUint32(left, allowNull: true)) { |
| 555 if (isDefinitelyIntInRange(right, min: 1, max: 31)) { | 536 if (isDefinitelyIntInRange(right, min: 1, max: 31)) { |
| 556 // A zero will be shifted into the 'sign' bit. | 537 // A zero will be shifted into the 'sign' bit. |
| 557 return nonConstant(typeSystem.uint31Type); | 538 return nonConstant(typeSystem.uint31Type); |
| 558 } | 539 } |
| 559 return nonConstant(typeSystem.uint32Type); | 540 return nonConstant(typeSystem.uint32Type); |
| 560 } | 541 } |
| 561 return null; | 542 return null; |
| 562 } | 543 } |
| 563 | 544 |
| 564 AbstractConstantValue lessSpecial(AbstractConstantValue left, | 545 AbstractConstantValue lessSpecial( |
| 565 AbstractConstantValue right) { | 546 AbstractConstantValue left, AbstractConstantValue right) { |
| 566 if (isDefinitelyUint(left) && right.isZeroOrNegativeConstant) { | 547 if (isDefinitelyUint(left) && right.isZeroOrNegativeConstant) { |
| 567 return falseValue; // "uint < 0" is false. | 548 return falseValue; // "uint < 0" is false. |
| 568 } else if (left.isNegativeConstant && isDefinitelyUint(right)) { | 549 } else if (left.isNegativeConstant && isDefinitelyUint(right)) { |
| 569 return trueValue; // "-1 < uint" is true. | 550 return trueValue; // "-1 < uint" is true. |
| 570 } | 551 } |
| 571 return foldBinary(constantSystem.less, left, right); | 552 return foldBinary(constantSystem.less, left, right); |
| 572 } | 553 } |
| 573 | 554 |
| 574 AbstractConstantValue lessEqualSpecial(AbstractConstantValue left, | 555 AbstractConstantValue lessEqualSpecial( |
| 575 AbstractConstantValue right) { | 556 AbstractConstantValue left, AbstractConstantValue right) { |
| 576 if (isDefinitelyUint(left) && right.isNegativeConstant) { | 557 if (isDefinitelyUint(left) && right.isNegativeConstant) { |
| 577 return falseValue; // "uint <= -1" is false. | 558 return falseValue; // "uint <= -1" is false. |
| 578 } else if (left.isZeroOrNegativeConstant && isDefinitelyUint(right)) { | 559 } else if (left.isZeroOrNegativeConstant && isDefinitelyUint(right)) { |
| 579 return trueValue; // "0 <= uint" is true. | 560 return trueValue; // "0 <= uint" is true. |
| 580 } | 561 } |
| 581 return foldBinary(constantSystem.lessEqual, left, right); | 562 return foldBinary(constantSystem.lessEqual, left, right); |
| 582 } | 563 } |
| 583 | 564 |
| 584 AbstractConstantValue greaterSpecial(AbstractConstantValue left, | 565 AbstractConstantValue greaterSpecial( |
| 585 AbstractConstantValue right) { | 566 AbstractConstantValue left, AbstractConstantValue right) { |
| 586 if (left.isZeroOrNegativeConstant && isDefinitelyUint(right)) { | 567 if (left.isZeroOrNegativeConstant && isDefinitelyUint(right)) { |
| 587 return falseValue; // "0 > uint" is false | 568 return falseValue; // "0 > uint" is false |
| 588 } else if (isDefinitelyUint(left) && right.isNegativeConstant) { | 569 } else if (isDefinitelyUint(left) && right.isNegativeConstant) { |
| 589 return trueValue; // "uint > -1" is true | 570 return trueValue; // "uint > -1" is true |
| 590 } | 571 } |
| 591 return foldBinary(constantSystem.greater, left, right); | 572 return foldBinary(constantSystem.greater, left, right); |
| 592 } | 573 } |
| 593 | 574 |
| 594 AbstractConstantValue greaterEqualSpecial(AbstractConstantValue left, | 575 AbstractConstantValue greaterEqualSpecial( |
| 595 AbstractConstantValue right) { | 576 AbstractConstantValue left, AbstractConstantValue right) { |
| 596 if (left.isNegativeConstant && isDefinitelyUint(right)) { | 577 if (left.isNegativeConstant && isDefinitelyUint(right)) { |
| 597 return falseValue; // "-1 >= uint" is false | 578 return falseValue; // "-1 >= uint" is false |
| 598 } else if (isDefinitelyUint(left) && right.isZeroOrNegativeConstant) { | 579 } else if (isDefinitelyUint(left) && right.isZeroOrNegativeConstant) { |
| 599 return trueValue; // "uint >= 0" is true | 580 return trueValue; // "uint >= 0" is true |
| 600 } | 581 } |
| 601 return foldBinary(constantSystem.greaterEqual, left, right); | 582 return foldBinary(constantSystem.greaterEqual, left, right); |
| 602 } | 583 } |
| 603 | 584 |
| 604 AbstractConstantValue intConstant(int value) { | 585 AbstractConstantValue intConstant(int value) { |
| 605 return constant(new IntConstantValue(value)); | 586 return constant(new IntConstantValue(value)); |
| 606 } | 587 } |
| 607 | 588 |
| 608 AbstractConstantValue lengthSpecial(AbstractConstantValue input) { | 589 AbstractConstantValue lengthSpecial(AbstractConstantValue input) { |
| 609 if (input.isConstant) { | 590 if (input.isConstant) { |
| 610 ConstantValue constant = input.constant; | 591 ConstantValue constant = input.constant; |
| 611 if (constant is StringConstantValue) { | 592 if (constant is StringConstantValue) { |
| 612 return intConstant(constant.length); | 593 return intConstant(constant.length); |
| 613 } else if (constant is ListConstantValue) { | 594 } else if (constant is ListConstantValue) { |
| 614 return intConstant(constant.length); | 595 return intConstant(constant.length); |
| 615 } | 596 } |
| 616 } | 597 } |
| 617 int length = typeSystem.getContainerLength(input.type); | 598 int length = typeSystem.getContainerLength(input.type); |
| 618 if (length != null) { | 599 if (length != null) { |
| 619 return intConstant(length); | 600 return intConstant(length); |
| 620 } | 601 } |
| 621 return null; // The caller will use return type from type inference. | 602 return null; // The caller will use return type from type inference. |
| 622 } | 603 } |
| 623 | 604 |
| 624 AbstractConstantValue stringConstant(String value) { | 605 AbstractConstantValue stringConstant(String value) { |
| 625 return constant(new StringConstantValue(new ast.DartString.literal(value))); | 606 return constant(new StringConstantValue(new ast.DartString.literal(value))); |
| 626 } | 607 } |
| 627 | 608 |
| 628 AbstractConstantValue indexSpecial(AbstractConstantValue left, | 609 AbstractConstantValue indexSpecial( |
| 629 AbstractConstantValue right) { | 610 AbstractConstantValue left, AbstractConstantValue right) { |
| 630 if (left.isNothing || right.isNothing) return nothing; | 611 if (left.isNothing || right.isNothing) return nothing; |
| 631 if (right.isConstant) { | 612 if (right.isConstant) { |
| 632 ConstantValue index = right.constant; | 613 ConstantValue index = right.constant; |
| 633 if (left.isConstant) { | 614 if (left.isConstant) { |
| 634 ConstantValue receiver = left.constant; | 615 ConstantValue receiver = left.constant; |
| 635 if (receiver is StringConstantValue) { | 616 if (receiver is StringConstantValue) { |
| 636 if (index is IntConstantValue) { | 617 if (index is IntConstantValue) { |
| 637 String stringValue = receiver.primitiveValue.slowToString(); | 618 String stringValue = receiver.primitiveValue.slowToString(); |
| 638 int indexValue = index.primitiveValue; | 619 int indexValue = index.primitiveValue; |
| 639 if (0 <= indexValue && indexValue < stringValue.length) { | 620 if (0 <= indexValue && indexValue < stringValue.length) { |
| 640 return stringConstant(stringValue[indexValue]); | 621 return stringConstant(stringValue[indexValue]); |
| 641 } else { | 622 } else { |
| 642 return nothing; // Will throw. | 623 return nothing; // Will throw. |
| 643 } | 624 } |
| 644 } | 625 } |
| 645 } else if (receiver is ListConstantValue) { | 626 } else if (receiver is ListConstantValue) { |
| 646 if (index is IntConstantValue) { | 627 if (index is IntConstantValue) { |
| 647 int indexValue = index.primitiveValue; | 628 int indexValue = index.primitiveValue; |
| 648 if (0 <= indexValue && indexValue < receiver.length) { | 629 if (0 <= indexValue && indexValue < receiver.length) { |
| 649 return constant(receiver.entries[indexValue]); | 630 return constant(receiver.entries[indexValue]); |
| 650 } else { | 631 } else { |
| 651 return nothing; // Will throw. | 632 return nothing; // Will throw. |
| 652 } | 633 } |
| 653 } | 634 } |
| 654 } else if (receiver is MapConstantValue) { | 635 } else if (receiver is MapConstantValue) { |
| 655 ConstantValue result = receiver.lookup(index); | 636 ConstantValue result = receiver.lookup(index); |
| 656 if (result != null) { | 637 if (result != null) { |
| 657 return constant(result); | 638 return constant(result); |
| 658 } | 639 } |
| 659 return constant(new NullConstantValue()); | 640 return constant(new NullConstantValue()); |
| 660 } | 641 } |
| 661 } | 642 } |
| 662 TypeMask type = typeSystem.indexWithConstant(left.type, index); | 643 TypeMask type = typeSystem.indexWithConstant(left.type, index); |
| 663 if (type != null) return nonConstant(type); | 644 if (type != null) return nonConstant(type); |
| 664 } | 645 } |
| 665 // TODO(asgerf): Handle case where 'left' is a List or Map constant but | 646 // TODO(asgerf): Handle case where 'left' is a List or Map constant but |
| 666 // the index is unknown. | 647 // the index is unknown. |
| 667 return null; // The caller will use return type from type inference. | 648 return null; // The caller will use return type from type inference. |
| 668 } | 649 } |
| 669 | 650 |
| 670 AbstractConstantValue stringify(AbstractConstantValue value) { | 651 AbstractConstantValue stringify(AbstractConstantValue value) { |
| 671 if (value.isNothing) return nothing; | 652 if (value.isNothing) return nothing; |
| 672 if (value.isNonConst) return nonConstant(typeSystem.stringType); | 653 if (value.isNonConst) return nonConstant(typeSystem.stringType); |
| 673 ConstantValue constantValue = value.constant; | 654 ConstantValue constantValue = value.constant; |
| 674 if (constantValue is StringConstantValue) { | 655 if (constantValue is StringConstantValue) { |
| 675 return value; | 656 return value; |
| 676 } else if (constantValue is PrimitiveConstantValue) { | 657 } else if (constantValue is PrimitiveConstantValue) { |
| 677 // Note: The primitiveValue for a StringConstantValue is not suitable | 658 // Note: The primitiveValue for a StringConstantValue is not suitable |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 719 if (constantValue != null) return constant(constantValue, mask); | 700 if (constantValue != null) return constant(constantValue, mask); |
| 720 return nonConstant(mask); | 701 return nonConstant(mask); |
| 721 } | 702 } |
| 722 | 703 |
| 723 AbstractConstantValue nonNullable(AbstractConstantValue value) { | 704 AbstractConstantValue nonNullable(AbstractConstantValue value) { |
| 724 if (value.isNullConstant) return nothing; | 705 if (value.isNullConstant) return nothing; |
| 725 if (!value.isNullable) return value; | 706 if (!value.isNullable) return value; |
| 726 return nonConstant(value.type.nonNullable()); | 707 return nonConstant(value.type.nonNullable()); |
| 727 } | 708 } |
| 728 | 709 |
| 729 AbstractConstantValue intersectWithType(AbstractConstantValue value, | 710 AbstractConstantValue intersectWithType( |
| 730 TypeMask type) { | 711 AbstractConstantValue value, TypeMask type) { |
| 731 if (value.isNothing || typeSystem.areDisjoint(value.type, type)) { | 712 if (value.isNothing || typeSystem.areDisjoint(value.type, type)) { |
| 732 return nothing; | 713 return nothing; |
| 733 } else if (value.isConstant) { | 714 } else if (value.isConstant) { |
| 734 return value; | 715 return value; |
| 735 } else { | 716 } else { |
| 736 return nonConstant(typeSystem.intersection(value.type, type)); | 717 return nonConstant(typeSystem.intersection(value.type, type)); |
| 737 } | 718 } |
| 738 } | 719 } |
| 739 | 720 |
| 740 /// If [value] is an integer constant, returns its value, otherwise `null`. | 721 /// If [value] is an integer constant, returns its value, otherwise `null`. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 754 * | 735 * |
| 755 * Should be followed by the [ShrinkingReducer] pass. | 736 * Should be followed by the [ShrinkingReducer] pass. |
| 756 * | 737 * |
| 757 * Implemented according to 'Constant Propagation with Conditional Branches' | 738 * Implemented according to 'Constant Propagation with Conditional Branches' |
| 758 * by Wegman, Zadeck. | 739 * by Wegman, Zadeck. |
| 759 */ | 740 */ |
| 760 class TypePropagator extends Pass { | 741 class TypePropagator extends Pass { |
| 761 String get passName => 'Type propagation'; | 742 String get passName => 'Type propagation'; |
| 762 | 743 |
| 763 final CpsFunctionCompiler _functionCompiler; | 744 final CpsFunctionCompiler _functionCompiler; |
| 764 final Map<Variable, ConstantValue> _values= <Variable, ConstantValue>{}; | 745 final Map<Variable, ConstantValue> _values = <Variable, ConstantValue>{}; |
| 765 final ConstantPropagationLattice _lattice; | 746 final ConstantPropagationLattice _lattice; |
| 766 final bool recomputeAll; | 747 final bool recomputeAll; |
| 767 | 748 |
| 768 TypePropagator(CpsFunctionCompiler functionCompiler, | 749 TypePropagator(CpsFunctionCompiler functionCompiler, |
| 769 {this.recomputeAll: false}) | 750 {this.recomputeAll: false}) |
| 770 : _functionCompiler = functionCompiler, | 751 : _functionCompiler = functionCompiler, |
| 771 _lattice = new ConstantPropagationLattice(functionCompiler); | 752 _lattice = new ConstantPropagationLattice(functionCompiler); |
| 772 | 753 |
| 773 dart2js.Compiler get _compiler => _functionCompiler.compiler; | 754 dart2js.Compiler get _compiler => _functionCompiler.compiler; |
| 774 InternalErrorFunction get _internalError => _compiler.reporter.internalError; | 755 InternalErrorFunction get _internalError => _compiler.reporter.internalError; |
| 775 | 756 |
| 776 @override | 757 @override |
| 777 void rewrite(FunctionDefinition root) { | 758 void rewrite(FunctionDefinition root) { |
| 778 // Analyze. In this phase, the entire term is analyzed for reachability | 759 // Analyze. In this phase, the entire term is analyzed for reachability |
| 779 // and the abstract value of each expression. | 760 // and the abstract value of each expression. |
| 780 TypePropagationVisitor analyzer = new TypePropagationVisitor( | 761 TypePropagationVisitor analyzer = |
| 781 _lattice, | 762 new TypePropagationVisitor(_lattice, _values, _internalError); |
| 782 _values, | |
| 783 _internalError); | |
| 784 | 763 |
| 785 analyzer.analyze(root, recomputeAll); | 764 analyzer.analyze(root, recomputeAll); |
| 786 | 765 |
| 787 // Transform. Uses the data acquired in the previous analysis phase to | 766 // Transform. Uses the data acquired in the previous analysis phase to |
| 788 // replace branches with fixed targets and side-effect-free expressions | 767 // replace branches with fixed targets and side-effect-free expressions |
| 789 // with constant results or existing values that are in scope. | 768 // with constant results or existing values that are in scope. |
| 790 TransformingVisitor transformer = new TransformingVisitor( | 769 TransformingVisitor transformer = new TransformingVisitor( |
| 791 _compiler, | 770 _compiler, _functionCompiler, _lattice, analyzer, _internalError); |
| 792 _functionCompiler, | |
| 793 _lattice, | |
| 794 analyzer, | |
| 795 _internalError); | |
| 796 transformer.transform(root); | 771 transformer.transform(root); |
| 797 } | 772 } |
| 798 } | 773 } |
| 799 | 774 |
| 800 final Map<String, BuiltinOperator> NumBinaryBuiltins = | 775 final Map<String, BuiltinOperator> NumBinaryBuiltins = |
| 801 const <String, BuiltinOperator>{ | 776 const <String, BuiltinOperator>{ |
| 802 '+': BuiltinOperator.NumAdd, | 777 '+': BuiltinOperator.NumAdd, |
| 803 '-': BuiltinOperator.NumSubtract, | 778 '-': BuiltinOperator.NumSubtract, |
| 804 '*': BuiltinOperator.NumMultiply, | 779 '*': BuiltinOperator.NumMultiply, |
| 805 '/': BuiltinOperator.NumDivide, | 780 '/': BuiltinOperator.NumDivide, |
| 806 '&': BuiltinOperator.NumAnd, | 781 '&': BuiltinOperator.NumAnd, |
| 807 '|': BuiltinOperator.NumOr, | 782 '|': BuiltinOperator.NumOr, |
| 808 '^': BuiltinOperator.NumXor, | 783 '^': BuiltinOperator.NumXor, |
| 809 '<': BuiltinOperator.NumLt, | 784 '<': BuiltinOperator.NumLt, |
| 810 '<=': BuiltinOperator.NumLe, | 785 '<=': BuiltinOperator.NumLe, |
| 811 '>': BuiltinOperator.NumGt, | 786 '>': BuiltinOperator.NumGt, |
| 812 '>=': BuiltinOperator.NumGe | 787 '>=': BuiltinOperator.NumGe |
| 813 }; | 788 }; |
| 814 | 789 |
| 815 /** | 790 /** |
| 816 * Uses the information from a preceding analysis pass in order to perform the | 791 * Uses the information from a preceding analysis pass in order to perform the |
| 817 * actual transformations on the CPS graph. | 792 * actual transformations on the CPS graph. |
| 818 */ | 793 */ |
| 819 class TransformingVisitor extends DeepRecursiveVisitor { | 794 class TransformingVisitor extends DeepRecursiveVisitor { |
| 820 final TypePropagationVisitor analyzer; | 795 final TypePropagationVisitor analyzer; |
| 821 final ConstantPropagationLattice lattice; | 796 final ConstantPropagationLattice lattice; |
| 822 final dart2js.Compiler compiler; | 797 final dart2js.Compiler compiler; |
| 823 final CpsFunctionCompiler functionCompiler; | 798 final CpsFunctionCompiler functionCompiler; |
| 824 | 799 |
| 825 JavaScriptBackend get backend => compiler.backend; | 800 JavaScriptBackend get backend => compiler.backend; |
| 826 BackendHelpers get helpers => backend.helpers; | 801 BackendHelpers get helpers => backend.helpers; |
| 827 TypeMaskSystem get typeSystem => lattice.typeSystem; | 802 TypeMaskSystem get typeSystem => lattice.typeSystem; |
| 828 types.DartTypes get dartTypes => lattice.dartTypes; | 803 types.DartTypes get dartTypes => lattice.dartTypes; |
| 829 World get classWorld => typeSystem.classWorld; | 804 World get classWorld => typeSystem.classWorld; |
| 830 Map<Variable, ConstantValue> get values => analyzer.values; | 805 Map<Variable, ConstantValue> get values => analyzer.values; |
| 831 | 806 |
| 832 final InternalErrorFunction internalError; | 807 final InternalErrorFunction internalError; |
| 833 | 808 |
| 834 final List<Node> stack = <Node>[]; | 809 final List<Node> stack = <Node>[]; |
| 835 | 810 |
| 836 TypeCheckOperator checkIsNumber; | 811 TypeCheckOperator checkIsNumber; |
| 837 | 812 |
| 838 TransformingVisitor(this.compiler, | 813 TransformingVisitor(this.compiler, this.functionCompiler, this.lattice, |
| 839 this.functionCompiler, | 814 this.analyzer, this.internalError) { |
| 840 this.lattice, | |
| 841 this.analyzer, | |
| 842 this.internalError) { | |
| 843 checkIsNumber = new ClassTypeCheckOperator( | 815 checkIsNumber = new ClassTypeCheckOperator( |
| 844 helpers.jsNumberClass, | 816 helpers.jsNumberClass, BuiltinOperator.IsNotNumber); |
| 845 BuiltinOperator.IsNotNumber); | |
| 846 } | 817 } |
| 847 | 818 |
| 848 void transform(FunctionDefinition root) { | 819 void transform(FunctionDefinition root) { |
| 849 // If one of the parameters has no value, the function is unreachable. | 820 // If one of the parameters has no value, the function is unreachable. |
| 850 // We assume all values in scope have a non-empty type (otherwise the | 821 // We assume all values in scope have a non-empty type (otherwise the |
| 851 // scope is unreachable), so this optimization is required. | 822 // scope is unreachable), so this optimization is required. |
| 852 // TODO(asgerf): Can we avoid emitting the function is the first place? | 823 // TODO(asgerf): Can we avoid emitting the function is the first place? |
| 853 for (Parameter param in root.parameters) { | 824 for (Parameter param in root.parameters) { |
| 854 if (getValue(param).isNothing) { | 825 if (getValue(param).isNothing) { |
| 855 // Replace with `throw "Unreachable";` | 826 // Replace with `throw "Unreachable";` |
| 856 CpsFragment cps = new CpsFragment(null); | 827 CpsFragment cps = new CpsFragment(null); |
| 857 Primitive message = cps.makeConstant( | 828 Primitive message = |
| 858 new StringConstantValue.fromString("Unreachable")); | 829 cps.makeConstant(new StringConstantValue.fromString("Unreachable")); |
| 859 cps.put(new Throw(message)); | 830 cps.put(new Throw(message)); |
| 860 replaceSubtree(root.body, cps.result); | 831 replaceSubtree(root.body, cps.result); |
| 861 return; | 832 return; |
| 862 } | 833 } |
| 863 } | 834 } |
| 864 push(root.body); | 835 push(root.body); |
| 865 while (stack.isNotEmpty) { | 836 while (stack.isNotEmpty) { |
| 866 visit(stack.removeLast()); | 837 visit(stack.removeLast()); |
| 867 } | 838 } |
| 868 } | 839 } |
| (...skipping 292 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1161 if (node.argumentRefs.length > 1) return null; | 1132 if (node.argumentRefs.length > 1) return null; |
| 1162 if (node.callingConvention == CallingConvention.OneShotIntercepted) { | 1133 if (node.callingConvention == CallingConvention.OneShotIntercepted) { |
| 1163 return null; | 1134 return null; |
| 1164 } | 1135 } |
| 1165 | 1136 |
| 1166 bool trustPrimitives = compiler.options.trustPrimitives; | 1137 bool trustPrimitives = compiler.options.trustPrimitives; |
| 1167 | 1138 |
| 1168 /// Check that the receiver and argument satisfy the given type checks, and | 1139 /// Check that the receiver and argument satisfy the given type checks, and |
| 1169 /// throw a [NoSuchMethodError] or [ArgumentError] if the check fails. | 1140 /// throw a [NoSuchMethodError] or [ArgumentError] if the check fails. |
| 1170 CpsFragment makeGuard(TypeCheckOperator receiverGuard, | 1141 CpsFragment makeGuard(TypeCheckOperator receiverGuard, |
| 1171 [TypeCheckOperator argumentGuard]) { | 1142 [TypeCheckOperator argumentGuard]) { |
| 1172 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1143 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1173 | 1144 |
| 1174 // Make no guards if trusting primitives. | 1145 // Make no guards if trusting primitives. |
| 1175 if (trustPrimitives) return cps; | 1146 if (trustPrimitives) return cps; |
| 1176 | 1147 |
| 1177 // Determine which guards are needed. | 1148 // Determine which guards are needed. |
| 1178 ChecksNeeded receiverChecks = | 1149 ChecksNeeded receiverChecks = |
| 1179 receiverGuard.getChecksNeeded(node.receiver, classWorld); | 1150 receiverGuard.getChecksNeeded(node.receiver, classWorld); |
| 1180 bool needReceiverGuard = receiverChecks != ChecksNeeded.None; | 1151 bool needReceiverGuard = receiverChecks != ChecksNeeded.None; |
| 1181 bool needArgumentGuard = | 1152 bool needArgumentGuard = argumentGuard != null && |
| 1182 argumentGuard != null && | |
| 1183 argumentGuard.needsCheck(node.argument(0), classWorld); | 1153 argumentGuard.needsCheck(node.argument(0), classWorld); |
| 1184 | 1154 |
| 1185 if (!needReceiverGuard && !needArgumentGuard) return cps; | 1155 if (!needReceiverGuard && !needArgumentGuard) return cps; |
| 1186 | 1156 |
| 1187 // If we only need the receiver check, emit the specialized receiver | 1157 // If we only need the receiver check, emit the specialized receiver |
| 1188 // check instruction. Examples: | 1158 // check instruction. Examples: |
| 1189 // | 1159 // |
| 1190 // if (typeof receiver !== "number") return receiver.$lt; | 1160 // if (typeof receiver !== "number") return receiver.$lt; |
| 1191 // if (typeof receiver !== "number") return receiver.$lt(); | 1161 // if (typeof receiver !== "number") return receiver.$lt(); |
| 1192 // | 1162 // |
| 1193 if (!needArgumentGuard) { | 1163 if (!needArgumentGuard) { |
| 1194 Primitive condition = receiverGuard.makeCheck(cps, node.receiver); | 1164 Primitive condition = receiverGuard.makeCheck(cps, node.receiver); |
| 1195 cps.letPrim(new ReceiverCheck( | 1165 cps.letPrim(new ReceiverCheck( |
| 1196 node.receiver, | 1166 node.receiver, node.selector, node.sourceInformation, |
| 1197 node.selector, | |
| 1198 node.sourceInformation, | |
| 1199 condition: condition, | 1167 condition: condition, |
| 1200 useSelector: true, | 1168 useSelector: true, |
| 1201 isNullCheck: receiverChecks == ChecksNeeded.Null | 1169 isNullCheck: receiverChecks == ChecksNeeded.Null)); |
| 1202 )); | |
| 1203 return cps; | 1170 return cps; |
| 1204 } | 1171 } |
| 1205 | 1172 |
| 1206 // TODO(asgerf): We should consider specialized instructions for | 1173 // TODO(asgerf): We should consider specialized instructions for |
| 1207 // argument checks and receiver+argument checks, to avoid breaking up | 1174 // argument checks and receiver+argument checks, to avoid breaking up |
| 1208 // basic blocks. | 1175 // basic blocks. |
| 1209 | 1176 |
| 1210 // Emit as `H.iae(x)` if only the argument check may fail. For example: | 1177 // Emit as `H.iae(x)` if only the argument check may fail. For example: |
| 1211 // | 1178 // |
| 1212 // if (typeof argument !== "number") return H.iae(argument); | 1179 // if (typeof argument !== "number") return H.iae(argument); |
| 1213 // | 1180 // |
| 1214 if (!needReceiverGuard) { | 1181 if (!needReceiverGuard) { |
| 1215 cps.ifTruthy(argumentGuard.makeCheck(cps, node.argument(0))) | 1182 cps |
| 1216 .invokeStaticThrower(helpers.throwIllegalArgumentException, | 1183 .ifTruthy(argumentGuard.makeCheck(cps, node.argument(0))) |
| 1217 [node.argument(0)]); | 1184 .invokeStaticThrower( |
| 1185 helpers.throwIllegalArgumentException, [node.argument(0)]); |
| 1218 return cps; | 1186 return cps; |
| 1219 } | 1187 } |
| 1220 | 1188 |
| 1221 // Both receiver and argument check is needed. Emit as a combined check | 1189 // Both receiver and argument check is needed. Emit as a combined check |
| 1222 // using a one-shot interceptor to produce the exact error message in | 1190 // using a one-shot interceptor to produce the exact error message in |
| 1223 // the error case. For example: | 1191 // the error case. For example: |
| 1224 // | 1192 // |
| 1225 // if (typeof receiver !== "number" || typeof argument !== "number") | 1193 // if (typeof receiver !== "number" || typeof argument !== "number") |
| 1226 // return J.$lt(receiver, argument); | 1194 // return J.$lt(receiver, argument); |
| 1227 // | 1195 // |
| 1228 Continuation fail = cps.letCont(); | 1196 Continuation fail = cps.letCont(); |
| 1229 cps.ifTruthy(receiverGuard.makeCheck(cps, node.receiver)) | 1197 cps |
| 1230 .invokeContinuation(fail); | 1198 .ifTruthy(receiverGuard.makeCheck(cps, node.receiver)) |
| 1231 cps.ifTruthy(argumentGuard.makeCheck(cps, node.argument(0))) | 1199 .invokeContinuation(fail); |
| 1232 .invokeContinuation(fail); | 1200 cps |
| 1201 .ifTruthy(argumentGuard.makeCheck(cps, node.argument(0))) |
| 1202 .invokeContinuation(fail); |
| 1233 | 1203 |
| 1234 cps.insideContinuation(fail) | 1204 cps.insideContinuation(fail) |
| 1235 ..invokeMethod(node.receiver, node.selector, node.mask, | 1205 ..invokeMethod( |
| 1236 [node.argument(0)], | 1206 node.receiver, node.selector, node.mask, [node.argument(0)], |
| 1237 callingConvention: CallingConvention.OneShotIntercepted) | 1207 callingConvention: CallingConvention.OneShotIntercepted) |
| 1238 ..put(new Unreachable()); | 1208 ..put(new Unreachable()); |
| 1239 | 1209 |
| 1240 return cps; | 1210 return cps; |
| 1241 } | 1211 } |
| 1242 | 1212 |
| 1243 /// Replaces the call with [operator], using the receiver and first argument | 1213 /// Replaces the call with [operator], using the receiver and first argument |
| 1244 /// as operands (in that order). | 1214 /// as operands (in that order). |
| 1245 /// | 1215 /// |
| 1246 /// If [guard] is given, the receiver and argument are both checked using | 1216 /// If [guard] is given, the receiver and argument are both checked using |
| 1247 /// that operator. | 1217 /// that operator. |
| 1248 CpsFragment makeBinary(BuiltinOperator operator, | 1218 CpsFragment makeBinary(BuiltinOperator operator, |
| 1249 {TypeCheckOperator guard: TypeCheckOperator.none}) { | 1219 {TypeCheckOperator guard: TypeCheckOperator.none}) { |
| 1250 CpsFragment cps = makeGuard(guard, guard); | 1220 CpsFragment cps = makeGuard(guard, guard); |
| 1251 Primitive left = guard.makeRefinement(cps, node.receiver, classWorld); | 1221 Primitive left = guard.makeRefinement(cps, node.receiver, classWorld); |
| 1252 Primitive right = | 1222 Primitive right = guard.makeRefinement(cps, node.argument(0), classWorld); |
| 1253 guard.makeRefinement(cps, node.argument(0), classWorld); | |
| 1254 Primitive result = cps.applyBuiltin(operator, [left, right]); | 1223 Primitive result = cps.applyBuiltin(operator, [left, right]); |
| 1255 result.hint = node.hint; | 1224 result.hint = node.hint; |
| 1256 node.replaceUsesWith(result); | 1225 node.replaceUsesWith(result); |
| 1257 return cps; | 1226 return cps; |
| 1258 } | 1227 } |
| 1259 | 1228 |
| 1260 /// Like [makeBinary] but for unary operators with the receiver as the | 1229 /// Like [makeBinary] but for unary operators with the receiver as the |
| 1261 /// argument. | 1230 /// argument. |
| 1262 CpsFragment makeUnary(BuiltinOperator operator, | 1231 CpsFragment makeUnary(BuiltinOperator operator, |
| 1263 {TypeCheckOperator guard: TypeCheckOperator.none}) { | 1232 {TypeCheckOperator guard: TypeCheckOperator.none}) { |
| 1264 CpsFragment cps = makeGuard(guard); | 1233 CpsFragment cps = makeGuard(guard); |
| 1265 Primitive argument = | 1234 Primitive argument = guard.makeRefinement(cps, node.receiver, classWorld); |
| 1266 guard.makeRefinement(cps, node.receiver, classWorld); | |
| 1267 Primitive result = cps.applyBuiltin(operator, [argument]); | 1235 Primitive result = cps.applyBuiltin(operator, [argument]); |
| 1268 result.hint = node.hint; | 1236 result.hint = node.hint; |
| 1269 node.replaceUsesWith(result); | 1237 node.replaceUsesWith(result); |
| 1270 return cps; | 1238 return cps; |
| 1271 } | 1239 } |
| 1272 | 1240 |
| 1273 Selector renameToOptimizedSelector(String name) { | 1241 Selector renameToOptimizedSelector(String name) { |
| 1274 return new Selector.call( | 1242 return new Selector.call( |
| 1275 new Name(name, backend.helpers.interceptorsLibrary), | 1243 new Name(name, backend.helpers.interceptorsLibrary), |
| 1276 node.selector.callStructure); | 1244 node.selector.callStructure); |
| 1277 } | 1245 } |
| 1278 | 1246 |
| 1279 /// Replaces the call with a call to [name] with the same inputs. | 1247 /// Replaces the call with a call to [name] with the same inputs. |
| 1280 InvokeMethod makeRenamedInvoke(String name) { | 1248 InvokeMethod makeRenamedInvoke(String name) { |
| 1281 return new InvokeMethod(node.receiver, | 1249 return new InvokeMethod(node.receiver, renameToOptimizedSelector(name), |
| 1282 renameToOptimizedSelector(name), | 1250 node.mask, node.arguments.toList(), |
| 1283 node.mask, | |
| 1284 node.arguments.toList(), | |
| 1285 sourceInformation: node.sourceInformation, | 1251 sourceInformation: node.sourceInformation, |
| 1286 callingConvention: node.callingConvention, | 1252 callingConvention: node.callingConvention, |
| 1287 interceptor: node.interceptor); | 1253 interceptor: node.interceptor); |
| 1288 } | 1254 } |
| 1289 | 1255 |
| 1290 TypeMask successType = | 1256 TypeMask successType = |
| 1291 typeSystem.receiverTypeFor(node.selector, node.receiver.type); | 1257 typeSystem.receiverTypeFor(node.selector, node.receiver.type); |
| 1292 | 1258 |
| 1293 if (node.selector.isOperator && node.argumentRefs.length == 1) { | 1259 if (node.selector.isOperator && node.argumentRefs.length == 1) { |
| 1294 Primitive leftArg = node.receiver; | 1260 Primitive leftArg = node.receiver; |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1341 return makeBinary(BuiltinOperator.NumShl, guard: checkIsNumber); | 1307 return makeBinary(BuiltinOperator.NumShl, guard: checkIsNumber); |
| 1342 } | 1308 } |
| 1343 // Try to insert a shift-right operator. JavaScript's right shift is | 1309 // Try to insert a shift-right operator. JavaScript's right shift is |
| 1344 // consistent with Dart's only for left operands in the unsigned | 1310 // consistent with Dart's only for left operands in the unsigned |
| 1345 // 32-bit range. | 1311 // 32-bit range. |
| 1346 if (opname == '>>') { | 1312 if (opname == '>>') { |
| 1347 if (lattice.isDefinitelyUint32(left, allowNull: true) && | 1313 if (lattice.isDefinitelyUint32(left, allowNull: true) && |
| 1348 lattice.isDefinitelyIntInRange(right, min: 0, max: 31)) { | 1314 lattice.isDefinitelyIntInRange(right, min: 0, max: 31)) { |
| 1349 return makeBinary(BuiltinOperator.NumShr, guard: checkIsNumber); | 1315 return makeBinary(BuiltinOperator.NumShr, guard: checkIsNumber); |
| 1350 } else if (lattice.isDefinitelyUint(left) && | 1316 } else if (lattice.isDefinitelyUint(left) && |
| 1351 lattice.isDefinitelyUint(right)) { | 1317 lattice.isDefinitelyUint(right)) { |
| 1352 return makeRenamedInvoke('_shrBothPositive'); | 1318 return makeRenamedInvoke('_shrBothPositive'); |
| 1353 } else if (lattice.isDefinitelyUint(left) && | 1319 } else if (lattice.isDefinitelyUint(left) && |
| 1354 lattice.isDefinitelyNum(right)) { | 1320 lattice.isDefinitelyNum(right)) { |
| 1355 return makeRenamedInvoke('_shrReceiverPositive'); | 1321 return makeRenamedInvoke('_shrReceiverPositive'); |
| 1356 } else if (lattice.isDefinitelyNum(left) && | 1322 } else if (lattice.isDefinitelyNum(left) && |
| 1357 lattice.isDefinitelyUint(right)) { | 1323 lattice.isDefinitelyUint(right)) { |
| 1358 return makeRenamedInvoke('_shrOtherPositive'); | 1324 return makeRenamedInvoke('_shrOtherPositive'); |
| 1359 } | 1325 } |
| 1360 } | 1326 } |
| 1361 // Try to use remainder for '%'. Both operands must be non-negative | 1327 // Try to use remainder for '%'. Both operands must be non-negative |
| 1362 // and the divisor must be non-zero. | 1328 // and the divisor must be non-zero. |
| 1363 if (opname == '%' && | 1329 if (opname == '%' && |
| 1364 lattice.isDefinitelyUint(left, allowNull: true) && | 1330 lattice.isDefinitelyUint(left, allowNull: true) && |
| 1365 lattice.isDefinitelyUint(right) && | 1331 lattice.isDefinitelyUint(right) && |
| 1366 lattice.isDefinitelyIntInRange(right, min: 1)) { | 1332 lattice.isDefinitelyIntInRange(right, min: 1)) { |
| 1367 return makeBinary(BuiltinOperator.NumRemainder, | 1333 return makeBinary(BuiltinOperator.NumRemainder, |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1409 guard: checkIsNumber); | 1375 guard: checkIsNumber); |
| 1410 } | 1376 } |
| 1411 } | 1377 } |
| 1412 } else if (name == 'codeUnitAt') { | 1378 } else if (name == 'codeUnitAt') { |
| 1413 if (node.argumentRefs.length == 1) { | 1379 if (node.argumentRefs.length == 1) { |
| 1414 Primitive index = node.argument(0); | 1380 Primitive index = node.argument(0); |
| 1415 if (lattice.isDefinitelyString(receiverValue) && | 1381 if (lattice.isDefinitelyString(receiverValue) && |
| 1416 lattice.isDefinitelyInt(getValue(index))) { | 1382 lattice.isDefinitelyInt(getValue(index))) { |
| 1417 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1383 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1418 receiver = makeBoundsCheck(cps, receiver, index); | 1384 receiver = makeBoundsCheck(cps, receiver, index); |
| 1419 ApplyBuiltinOperator get = | 1385 ApplyBuiltinOperator get = cps.applyBuiltin( |
| 1420 cps.applyBuiltin(BuiltinOperator.CharCodeAt, | 1386 BuiltinOperator.CharCodeAt, <Primitive>[receiver, index]); |
| 1421 <Primitive>[receiver, index]); | |
| 1422 node.replaceUsesWith(get); | 1387 node.replaceUsesWith(get); |
| 1423 get.hint = node.hint; | 1388 get.hint = node.hint; |
| 1424 return cps; | 1389 return cps; |
| 1425 } | 1390 } |
| 1426 } | 1391 } |
| 1427 } | 1392 } |
| 1428 } | 1393 } |
| 1429 return null; | 1394 return null; |
| 1430 } | 1395 } |
| 1431 | 1396 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 1451 // TODO(asgerf): Inlining native fields will make some tests pass for the | 1416 // TODO(asgerf): Inlining native fields will make some tests pass for the |
| 1452 // wrong reason, so for testing reasons avoid inlining them. | 1417 // wrong reason, so for testing reasons avoid inlining them. |
| 1453 if (backend.isNative(target) || backend.isJsInterop(target)) { | 1418 if (backend.isNative(target) || backend.isJsInterop(target)) { |
| 1454 return null; | 1419 return null; |
| 1455 } | 1420 } |
| 1456 if (node.selector.isGetter) { | 1421 if (node.selector.isGetter) { |
| 1457 return new GetField(node.receiver, target); | 1422 return new GetField(node.receiver, target); |
| 1458 } else if (node.selector.isSetter) { | 1423 } else if (node.selector.isSetter) { |
| 1459 if (target.isFinal) return null; | 1424 if (target.isFinal) return null; |
| 1460 assert(node.hasNoUses); | 1425 assert(node.hasNoUses); |
| 1461 return new SetField(node.receiver, | 1426 return new SetField(node.receiver, target, node.argument(0)); |
| 1462 target, | |
| 1463 node.argument(0)); | |
| 1464 } else if (node.selector.isCall) { | 1427 } else if (node.selector.isCall) { |
| 1465 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1428 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1466 Primitive fieldValue = cps.letPrim(new GetField(node.receiver, target)); | 1429 Primitive fieldValue = cps.letPrim(new GetField(node.receiver, target)); |
| 1467 Primitive result = cps.invokeMethod( | 1430 Primitive result = cps.invokeMethod( |
| 1468 fieldValue, | 1431 fieldValue, |
| 1469 new Selector.callClosureFrom(node.selector), | 1432 new Selector.callClosureFrom(node.selector), |
| 1470 typeSystem.getFieldType(target), | 1433 typeSystem.getFieldType(target), |
| 1471 node.arguments.toList()); | 1434 node.arguments.toList()); |
| 1472 node.replaceUsesWith(result); | 1435 node.replaceUsesWith(result); |
| 1473 return cps; | 1436 return cps; |
| 1474 } else { | 1437 } else { |
| 1475 return null; | 1438 return null; |
| 1476 } | 1439 } |
| 1477 } | 1440 } |
| 1478 | 1441 |
| 1479 /// Create a check that throws if [index] is not a valid index on [list]. | 1442 /// Create a check that throws if [index] is not a valid index on [list]. |
| 1480 /// | 1443 /// |
| 1481 /// This function assumes that [index] is an integer. | 1444 /// This function assumes that [index] is an integer. |
| 1482 /// | 1445 /// |
| 1483 /// Returns a CPS fragment whose context is the branch where no error | 1446 /// Returns a CPS fragment whose context is the branch where no error |
| 1484 /// was thrown. | 1447 /// was thrown. |
| 1485 Primitive makeBoundsCheck(CpsFragment cps, | 1448 Primitive makeBoundsCheck(CpsFragment cps, Primitive list, Primitive index, |
| 1486 Primitive list, | 1449 [int checkKind = BoundsCheck.BOTH_BOUNDS | BoundsCheck.INTEGER]) { |
| 1487 Primitive index, | |
| 1488 [int checkKind = BoundsCheck.BOTH_BOUNDS | BoundsCheck.INTEGER]) { | |
| 1489 if (compiler.options.trustPrimitives) { | 1450 if (compiler.options.trustPrimitives) { |
| 1490 return cps.letPrim(new BoundsCheck.noCheck(list, cps.sourceInformation)); | 1451 return cps.letPrim(new BoundsCheck.noCheck(list, cps.sourceInformation)); |
| 1491 } else { | 1452 } else { |
| 1492 GetLength length = cps.letPrim(new GetLength(list)); | 1453 GetLength length = cps.letPrim(new GetLength(list)); |
| 1493 list = cps.refine(list, typeSystem.nonNullType); | 1454 list = cps.refine(list, typeSystem.nonNullType); |
| 1494 BoundsCheck check = cps.letPrim(new BoundsCheck(list, index, length, | 1455 BoundsCheck check = cps.letPrim(new BoundsCheck( |
| 1495 checkKind, cps.sourceInformation)); | 1456 list, index, length, checkKind, cps.sourceInformation)); |
| 1496 if (check.hasIntegerCheck) { | 1457 if (check.hasIntegerCheck) { |
| 1497 if (typeSystem.isDefinitelyInt(index.type)) { | 1458 if (typeSystem.isDefinitelyInt(index.type)) { |
| 1498 check.checks &= ~BoundsCheck.INTEGER; | 1459 check.checks &= ~BoundsCheck.INTEGER; |
| 1499 } else { | 1460 } else { |
| 1500 cps.refine(index, typeSystem.uint32Type); | 1461 cps.refine(index, typeSystem.uint32Type); |
| 1501 } | 1462 } |
| 1502 } | 1463 } |
| 1503 return check; | 1464 return check; |
| 1504 } | 1465 } |
| 1505 } | 1466 } |
| 1506 | 1467 |
| 1507 /// Create a check that throws if the length of [list] is not equal to | 1468 /// Create a check that throws if the length of [list] is not equal to |
| 1508 /// [originalLength]. | 1469 /// [originalLength]. |
| 1509 /// | 1470 /// |
| 1510 /// Returns a CPS fragment whose context is the branch where no error | 1471 /// Returns a CPS fragment whose context is the branch where no error |
| 1511 /// was thrown. | 1472 /// was thrown. |
| 1512 CpsFragment makeConcurrentModificationCheck(Primitive list, | 1473 CpsFragment makeConcurrentModificationCheck( |
| 1513 Primitive originalLength, | 1474 Primitive list, Primitive originalLength, SourceInformation sourceInfo) { |
| 1514 SourceInformation sourceInfo) { | |
| 1515 CpsFragment cps = new CpsFragment(sourceInfo); | 1475 CpsFragment cps = new CpsFragment(sourceInfo); |
| 1516 Primitive lengthChanged = cps.applyBuiltin( | 1476 Primitive lengthChanged = cps.applyBuiltin(BuiltinOperator.StrictNeq, |
| 1517 BuiltinOperator.StrictNeq, | |
| 1518 <Primitive>[originalLength, cps.letPrim(new GetLength(list))]); | 1477 <Primitive>[originalLength, cps.letPrim(new GetLength(list))]); |
| 1519 cps.ifTruthy(lengthChanged).invokeStaticThrower( | 1478 cps.ifTruthy(lengthChanged).invokeStaticThrower( |
| 1520 helpers.throwConcurrentModificationError, | 1479 helpers.throwConcurrentModificationError, <Primitive>[list]); |
| 1521 <Primitive>[list]); | |
| 1522 return cps; | 1480 return cps; |
| 1523 } | 1481 } |
| 1524 | 1482 |
| 1525 /// Tries to replace [node] with a direct `length` or index access. | 1483 /// Tries to replace [node] with a direct `length` or index access. |
| 1526 /// | 1484 /// |
| 1527 /// Returns `true` if the node was replaced. | 1485 /// Returns `true` if the node was replaced. |
| 1528 specializeIndexableAccess(InvokeMethod node) { | 1486 specializeIndexableAccess(InvokeMethod node) { |
| 1529 Primitive receiver = node.receiver; | 1487 Primitive receiver = node.receiver; |
| 1530 AbstractConstantValue receiverValue = getValue(receiver); | 1488 AbstractConstantValue receiverValue = getValue(receiver); |
| 1531 if (!typeSystem.isDefinitelyIndexable(receiverValue.type, | 1489 if (!typeSystem.isDefinitelyIndexable(receiverValue.type, |
| 1532 allowNull: true)) { | 1490 allowNull: true)) { |
| 1533 return null; | 1491 return null; |
| 1534 } | 1492 } |
| 1535 switch (node.selector.name) { | 1493 switch (node.selector.name) { |
| 1536 case 'length': | 1494 case 'length': |
| 1537 if (node.selector.isGetter) { | 1495 if (node.selector.isGetter) { |
| 1538 return new GetLength(receiver); | 1496 return new GetLength(receiver); |
| 1539 } | 1497 } |
| 1540 if (node.selector.isSetter) { | 1498 if (node.selector.isSetter) { |
| 1541 if (!typeSystem.isDefinitelyExtendableArray(receiver.type, | 1499 if (!typeSystem.isDefinitelyExtendableArray(receiver.type, |
| 1542 allowNull: true)) { | 1500 allowNull: true)) { |
| 1543 return null; | 1501 return null; |
| 1544 } | 1502 } |
| 1545 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1503 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1546 Primitive newLength = node.argument(0); | 1504 Primitive newLength = node.argument(0); |
| 1547 if (!typeSystem.isDefinitelyUint(newLength.type)) { | 1505 if (!typeSystem.isDefinitelyUint(newLength.type)) { |
| 1548 // TODO(asgerf): We could let the SetLength instruction throw for | 1506 // TODO(asgerf): We could let the SetLength instruction throw for |
| 1549 // negative right-hand sides (see length setter in js_array.dart). | 1507 // negative right-hand sides (see length setter in js_array.dart). |
| 1550 if (compiler.options.trustPrimitives) { | 1508 if (compiler.options.trustPrimitives) { |
| 1551 newLength = cps.refine(newLength, typeSystem.uint32Type); | 1509 newLength = cps.refine(newLength, typeSystem.uint32Type); |
| 1552 newLength.type = typeSystem.uint32Type; | 1510 newLength.type = typeSystem.uint32Type; |
| 1553 } else { | 1511 } else { |
| 1554 return null; | 1512 return null; |
| 1555 } | 1513 } |
| 1556 } | 1514 } |
| 1557 cps.letPrim(new ApplyBuiltinMethod( | 1515 cps.letPrim(new ApplyBuiltinMethod(BuiltinMethod.SetLength, receiver, |
| 1558 BuiltinMethod.SetLength, | 1516 [newLength], node.sourceInformation)); |
| 1559 receiver, | |
| 1560 [newLength], | |
| 1561 node.sourceInformation)); | |
| 1562 if (!typeSystem.isDefinitelyUint32(newLength.type)) { | 1517 if (!typeSystem.isDefinitelyUint32(newLength.type)) { |
| 1563 // If the setter succeeded, the length must have been a uint32. | 1518 // If the setter succeeded, the length must have been a uint32. |
| 1564 cps.refine(newLength, typeSystem.uint32Type); | 1519 cps.refine(newLength, typeSystem.uint32Type); |
| 1565 } | 1520 } |
| 1566 return cps; | 1521 return cps; |
| 1567 } | 1522 } |
| 1568 return null; | 1523 return null; |
| 1569 | 1524 |
| 1570 case '[]': | 1525 case '[]': |
| 1571 Primitive index = node.argument(0); | 1526 Primitive index = node.argument(0); |
| 1572 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1527 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1573 receiver = makeBoundsCheck(cps, receiver, index); | 1528 receiver = makeBoundsCheck(cps, receiver, index); |
| 1574 GetIndex get = cps.letPrim(new GetIndex(receiver, index)); | 1529 GetIndex get = cps.letPrim(new GetIndex(receiver, index)); |
| 1575 node.replaceUsesWith(get); | 1530 node.replaceUsesWith(get); |
| 1576 // TODO(asgerf): Make replaceUsesWith set the hint? | 1531 // TODO(asgerf): Make replaceUsesWith set the hint? |
| 1577 get.hint = node.hint; | 1532 get.hint = node.hint; |
| 1578 return cps; | 1533 return cps; |
| 1579 | 1534 |
| 1580 case '[]=': | 1535 case '[]=': |
| 1581 if (!typeSystem.isDefinitelyMutableIndexable(receiverValue.type, | 1536 if (!typeSystem.isDefinitelyMutableIndexable(receiverValue.type, |
| 1582 allowNull: true)) { | 1537 allowNull: true)) { |
| 1583 return null; | 1538 return null; |
| 1584 } | 1539 } |
| 1585 Primitive index = node.argument(0); | 1540 Primitive index = node.argument(0); |
| 1586 Primitive value = node.argument(1); | 1541 Primitive value = node.argument(1); |
| 1587 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1542 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1588 receiver = makeBoundsCheck(cps, receiver, index); | 1543 receiver = makeBoundsCheck(cps, receiver, index); |
| 1589 cps.letPrim(new SetIndex(receiver, index, value)); | 1544 cps.letPrim(new SetIndex(receiver, index, value)); |
| 1590 assert(node.hasNoUses); | 1545 assert(node.hasNoUses); |
| 1591 return cps; | 1546 return cps; |
| 1592 | 1547 |
| 1593 case 'isEmpty': | 1548 case 'isEmpty': |
| 1594 if (!node.selector.isGetter) return null; | 1549 if (!node.selector.isGetter) return null; |
| 1595 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1550 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1596 Primitive length = cps.letPrim(new GetLength(receiver)); | 1551 Primitive length = cps.letPrim(new GetLength(receiver)); |
| 1597 Constant zero = cps.makeZero(); | 1552 Constant zero = cps.makeZero(); |
| 1598 ApplyBuiltinOperator op = cps.applyBuiltin(BuiltinOperator.StrictEq, | 1553 ApplyBuiltinOperator op = |
| 1599 [length, zero]); | 1554 cps.applyBuiltin(BuiltinOperator.StrictEq, [length, zero]); |
| 1600 node.replaceUsesWith(op); | 1555 node.replaceUsesWith(op); |
| 1601 op.hint = node.hint; | 1556 op.hint = node.hint; |
| 1602 return cps; | 1557 return cps; |
| 1603 | 1558 |
| 1604 case 'isNotEmpty': | 1559 case 'isNotEmpty': |
| 1605 if (!node.selector.isGetter) return null; | 1560 if (!node.selector.isGetter) return null; |
| 1606 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1561 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1607 Primitive length = cps.letPrim(new GetLength(receiver)); | 1562 Primitive length = cps.letPrim(new GetLength(receiver)); |
| 1608 Constant zero = cps.makeZero(); | 1563 Constant zero = cps.makeZero(); |
| 1609 ApplyBuiltinOperator op = cps.applyBuiltin(BuiltinOperator.StrictNeq, | 1564 ApplyBuiltinOperator op = |
| 1610 [length, zero]); | 1565 cps.applyBuiltin(BuiltinOperator.StrictNeq, [length, zero]); |
| 1611 node.replaceUsesWith(op); | 1566 node.replaceUsesWith(op); |
| 1612 op.hint = node.hint; | 1567 op.hint = node.hint; |
| 1613 return cps; | 1568 return cps; |
| 1614 | 1569 |
| 1615 default: | 1570 default: |
| 1616 return null; | 1571 return null; |
| 1617 } | 1572 } |
| 1618 } | 1573 } |
| 1619 | 1574 |
| 1620 /// Tries to replace [node] with one or more direct array access operations. | 1575 /// Tries to replace [node] with one or more direct array access operations. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 1635 switch (node.selector.name) { | 1590 switch (node.selector.name) { |
| 1636 case 'add': | 1591 case 'add': |
| 1637 if (!node.selector.isCall || | 1592 if (!node.selector.isCall || |
| 1638 node.selector.positionalArgumentCount != 1 || | 1593 node.selector.positionalArgumentCount != 1 || |
| 1639 node.selector.namedArgumentCount != 0) { | 1594 node.selector.namedArgumentCount != 0) { |
| 1640 return null; | 1595 return null; |
| 1641 } | 1596 } |
| 1642 if (!isExtendable) return null; | 1597 if (!isExtendable) return null; |
| 1643 Primitive addedItem = node.argument(0); | 1598 Primitive addedItem = node.argument(0); |
| 1644 CpsFragment cps = new CpsFragment(sourceInfo); | 1599 CpsFragment cps = new CpsFragment(sourceInfo); |
| 1645 cps.invokeBuiltin(BuiltinMethod.Push, | 1600 cps.invokeBuiltin(BuiltinMethod.Push, list, <Primitive>[addedItem]); |
| 1646 list, | |
| 1647 <Primitive>[addedItem]); | |
| 1648 if (node.hasAtLeastOneUse) { | 1601 if (node.hasAtLeastOneUse) { |
| 1649 node.replaceUsesWith(cps.makeNull()); | 1602 node.replaceUsesWith(cps.makeNull()); |
| 1650 } | 1603 } |
| 1651 return cps; | 1604 return cps; |
| 1652 | 1605 |
| 1653 case 'removeLast': | 1606 case 'removeLast': |
| 1654 if (!node.selector.isCall || | 1607 if (!node.selector.isCall || node.selector.argumentCount != 0) { |
| 1655 node.selector.argumentCount != 0) { | |
| 1656 return null; | 1608 return null; |
| 1657 } | 1609 } |
| 1658 if (!isExtendable) return null; | 1610 if (!isExtendable) return null; |
| 1659 CpsFragment cps = new CpsFragment(sourceInfo); | 1611 CpsFragment cps = new CpsFragment(sourceInfo); |
| 1660 list = makeBoundsCheck(cps, list, cps.makeMinusOne(), | 1612 list = makeBoundsCheck( |
| 1661 BoundsCheck.EMPTINESS); | 1613 cps, list, cps.makeMinusOne(), BoundsCheck.EMPTINESS); |
| 1662 Primitive removedItem = cps.invokeBuiltin(BuiltinMethod.Pop, | 1614 Primitive removedItem = |
| 1663 list, | 1615 cps.invokeBuiltin(BuiltinMethod.Pop, list, <Primitive>[]); |
| 1664 <Primitive>[]); | |
| 1665 removedItem.hint = node.hint; | 1616 removedItem.hint = node.hint; |
| 1666 node.replaceUsesWith(removedItem); | 1617 node.replaceUsesWith(removedItem); |
| 1667 return cps; | 1618 return cps; |
| 1668 | 1619 |
| 1669 case 'addAll': | 1620 case 'addAll': |
| 1670 if (!node.selector.isCall || | 1621 if (!node.selector.isCall || node.selector.argumentCount != 1) { |
| 1671 node.selector.argumentCount != 1) { | |
| 1672 return null; | 1622 return null; |
| 1673 } | 1623 } |
| 1674 if (!isExtendable) return null; | 1624 if (!isExtendable) return null; |
| 1675 Primitive addedList = node.argument(0); | 1625 Primitive addedList = node.argument(0); |
| 1676 // Rewrite addAll([x1, ..., xN]) to push(x1), ..., push(xN). | 1626 // Rewrite addAll([x1, ..., xN]) to push(x1), ..., push(xN). |
| 1677 // Ensure that the list is not mutated between creation and use. | 1627 // Ensure that the list is not mutated between creation and use. |
| 1678 // We aim for the common case where this is the only use of the list, | 1628 // We aim for the common case where this is the only use of the list, |
| 1679 // which also guarantees that this list is not mutated before use. | 1629 // which also guarantees that this list is not mutated before use. |
| 1680 if (addedList is! LiteralList || !addedList.hasExactlyOneUse) { | 1630 if (addedList is! LiteralList || !addedList.hasExactlyOneUse) { |
| 1681 return null; | 1631 return null; |
| 1682 } | 1632 } |
| 1683 LiteralList addedLiteral = addedList; | 1633 LiteralList addedLiteral = addedList; |
| 1684 CpsFragment cps = new CpsFragment(sourceInfo); | 1634 CpsFragment cps = new CpsFragment(sourceInfo); |
| 1685 for (Reference value in addedLiteral.valueRefs) { | 1635 for (Reference value in addedLiteral.valueRefs) { |
| 1686 cps.invokeBuiltin(BuiltinMethod.Push, | 1636 cps.invokeBuiltin( |
| 1687 list, | 1637 BuiltinMethod.Push, list, <Primitive>[value.definition]); |
| 1688 <Primitive>[value.definition]); | |
| 1689 } | 1638 } |
| 1690 if (node.hasAtLeastOneUse) { | 1639 if (node.hasAtLeastOneUse) { |
| 1691 node.replaceUsesWith(cps.makeNull()); | 1640 node.replaceUsesWith(cps.makeNull()); |
| 1692 } | 1641 } |
| 1693 return cps; | 1642 return cps; |
| 1694 | 1643 |
| 1695 case 'elementAt': | 1644 case 'elementAt': |
| 1696 if (!node.selector.isCall || | 1645 if (!node.selector.isCall || |
| 1697 node.selector.positionalArgumentCount != 1 || | 1646 node.selector.positionalArgumentCount != 1 || |
| 1698 node.selector.namedArgumentCount != 0) { | 1647 node.selector.namedArgumentCount != 0) { |
| 1699 return null; | 1648 return null; |
| 1700 } | 1649 } |
| 1701 if (listValue.isNullable) return null; | 1650 if (listValue.isNullable) return null; |
| 1702 Primitive index = node.argument(0); | 1651 Primitive index = node.argument(0); |
| 1703 if (!lattice.isDefinitelyInt(getValue(index))) return null; | 1652 if (!lattice.isDefinitelyInt(getValue(index))) return null; |
| 1704 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1653 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1705 list = makeBoundsCheck(cps, list, index); | 1654 list = makeBoundsCheck(cps, list, index); |
| 1706 GetIndex get = cps.letPrim(new GetIndex(list, index)); | 1655 GetIndex get = cps.letPrim(new GetIndex(list, index)); |
| 1707 get.hint = node.hint; | 1656 get.hint = node.hint; |
| 1708 node.replaceUsesWith(get); | 1657 node.replaceUsesWith(get); |
| 1709 return cps; | 1658 return cps; |
| 1710 | 1659 |
| 1711 case 'forEach': | 1660 case 'forEach': |
| 1712 Element element = | 1661 Element element = |
| 1713 compiler.world.locateSingleElement(node.selector, listValue.type); | 1662 compiler.world.locateSingleElement(node.selector, listValue.type); |
| 1714 if (element == null || | 1663 if (element == null || !element.isFunction || !node.selector.isCall) |
| 1715 !element.isFunction || | 1664 return null; |
| 1716 !node.selector.isCall) return null; | |
| 1717 assert(node.selector.positionalArgumentCount == 1); | 1665 assert(node.selector.positionalArgumentCount == 1); |
| 1718 assert(node.selector.namedArgumentCount == 0); | 1666 assert(node.selector.namedArgumentCount == 0); |
| 1719 FunctionDefinition target = functionCompiler.compileToCpsIr(element); | 1667 FunctionDefinition target = functionCompiler.compileToCpsIr(element); |
| 1720 | 1668 |
| 1721 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1669 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 1722 Primitive result = cps.inlineFunction(target, | 1670 Primitive result = cps.inlineFunction( |
| 1723 node.receiver, | 1671 target, node.receiver, node.arguments.toList(), |
| 1724 node.arguments.toList(), | 1672 interceptor: node.interceptor, hint: node.hint); |
| 1725 interceptor: node.interceptor, | |
| 1726 hint: node.hint); | |
| 1727 node.replaceUsesWith(result); | 1673 node.replaceUsesWith(result); |
| 1728 return cps; | 1674 return cps; |
| 1729 | 1675 |
| 1730 case 'iterator': | 1676 case 'iterator': |
| 1731 // TODO(asgerf): This should be done differently. | 1677 // TODO(asgerf): This should be done differently. |
| 1732 // The types are recomputed in a very error-prone manner. | 1678 // The types are recomputed in a very error-prone manner. |
| 1733 if (!node.selector.isGetter) return null; | 1679 if (!node.selector.isGetter) return null; |
| 1734 Primitive iterator = node; | 1680 Primitive iterator = node; |
| 1735 LetPrim iteratorBinding = node.parent; | 1681 LetPrim iteratorBinding = node.parent; |
| 1736 | 1682 |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1758 InvokeMethod use = ref.parent; | 1704 InvokeMethod use = ref.parent; |
| 1759 if (use.selector == Selectors.current) { | 1705 if (use.selector == Selectors.current) { |
| 1760 // Rewrite iterator.current to a use of the 'current' variable. | 1706 // Rewrite iterator.current to a use of the 'current' variable. |
| 1761 if (use.hint != null) { | 1707 if (use.hint != null) { |
| 1762 // If 'current' was originally moved into a named variable, use | 1708 // If 'current' was originally moved into a named variable, use |
| 1763 // that variable name for the mutable variable. | 1709 // that variable name for the mutable variable. |
| 1764 current.hint = use.hint; | 1710 current.hint = use.hint; |
| 1765 } | 1711 } |
| 1766 use.replaceWith(new GetMutable(current)); | 1712 use.replaceWith(new GetMutable(current)); |
| 1767 } else { | 1713 } else { |
| 1768 assert (use.selector == Selectors.moveNext); | 1714 assert(use.selector == Selectors.moveNext); |
| 1769 // Rewrite iterator.moveNext() to: | 1715 // Rewrite iterator.moveNext() to: |
| 1770 // | 1716 // |
| 1771 // if (index < list.length) { | 1717 // if (index < list.length) { |
| 1772 // current = null; | 1718 // current = null; |
| 1773 // continuation(false); | 1719 // continuation(false); |
| 1774 // } else { | 1720 // } else { |
| 1775 // current = list[index]; | 1721 // current = list[index]; |
| 1776 // index = index + 1; | 1722 // index = index + 1; |
| 1777 // continuation(true); | 1723 // continuation(true); |
| 1778 // } | 1724 // } |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1811 // The check before the loop can often be eliminated because it | 1757 // The check before the loop can often be eliminated because it |
| 1812 // follows immediately after the 'iterator' call. | 1758 // follows immediately after the 'iterator' call. |
| 1813 InteriorNode parent = getEffectiveParent(use.parent); | 1759 InteriorNode parent = getEffectiveParent(use.parent); |
| 1814 if (!isFixedLength) { | 1760 if (!isFixedLength) { |
| 1815 if (parent is Continuation && parent.isRecursive) { | 1761 if (parent is Continuation && parent.isRecursive) { |
| 1816 // Check for concurrent modification before every invocation | 1762 // Check for concurrent modification before every invocation |
| 1817 // of the continuation. | 1763 // of the continuation. |
| 1818 // TODO(asgerf): Do this in a continuation so multiple | 1764 // TODO(asgerf): Do this in a continuation so multiple |
| 1819 // continues can share the same code. | 1765 // continues can share the same code. |
| 1820 for (Reference ref = parent.firstRef; | 1766 for (Reference ref = parent.firstRef; |
| 1821 ref != null; | 1767 ref != null; |
| 1822 ref = ref.next) { | 1768 ref = ref.next) { |
| 1823 Expression invocationCaller = ref.parent; | 1769 Expression invocationCaller = ref.parent; |
| 1824 if (getEffectiveParent(invocationCaller) == iteratorBinding) { | 1770 if (getEffectiveParent(invocationCaller) == iteratorBinding) { |
| 1825 // No need to check for concurrent modification immediately | 1771 // No need to check for concurrent modification immediately |
| 1826 // after the call to 'iterator'. | 1772 // after the call to 'iterator'. |
| 1827 continue; | 1773 continue; |
| 1828 } | 1774 } |
| 1829 CpsFragment check = makeConcurrentModificationCheck( | 1775 CpsFragment check = makeConcurrentModificationCheck( |
| 1830 list, originalLength, sourceInfo); | 1776 list, originalLength, sourceInfo); |
| 1831 insertBefore(invocationCaller, check); | 1777 insertBefore(invocationCaller, check); |
| 1832 } | 1778 } |
| 1833 } else { | 1779 } else { |
| 1834 cps.append(makeConcurrentModificationCheck( | 1780 cps.append(makeConcurrentModificationCheck( |
| 1835 list, originalLength, sourceInfo)); | 1781 list, originalLength, sourceInfo)); |
| 1836 } | 1782 } |
| 1837 } | 1783 } |
| 1838 | 1784 |
| 1839 // Check if there are more elements. | 1785 // Check if there are more elements. |
| 1840 Primitive hasMore = cps.applyBuiltin( | 1786 Primitive hasMore = cps.applyBuiltin(BuiltinOperator.NumLt, |
| 1841 BuiltinOperator.NumLt, | |
| 1842 [cps.getMutable(index), cps.letPrim(new GetLength(list))]); | 1787 [cps.getMutable(index), cps.letPrim(new GetLength(list))]); |
| 1843 | 1788 |
| 1844 // Return false if there are no more. | 1789 // Return false if there are no more. |
| 1845 CpsFragment falseBranch = cps.ifFalsy(hasMore); | 1790 CpsFragment falseBranch = cps.ifFalsy(hasMore); |
| 1846 falseBranch | 1791 falseBranch |
| 1847 ..setMutable(current, falseBranch.makeNull()) | 1792 ..setMutable(current, falseBranch.makeNull()) |
| 1848 ..invokeContinuation(moveNextCont, [falseBranch.makeFalse()]); | 1793 ..invokeContinuation(moveNextCont, [falseBranch.makeFalse()]); |
| 1849 | 1794 |
| 1850 // Return true if there are more element. | 1795 // Return true if there are more element. |
| 1851 current.type = typeSystem.elementTypeOfIndexable(listValue.type); | 1796 current.type = typeSystem.elementTypeOfIndexable(listValue.type); |
| 1852 cps.setMutable(current, | 1797 cps.setMutable(current, |
| 1853 cps.letPrim(new GetIndex(list, cps.getMutable(index)))); | 1798 cps.letPrim(new GetIndex(list, cps.getMutable(index)))); |
| 1854 cps.setMutable(index, cps.applyBuiltin( | 1799 cps.setMutable( |
| 1855 BuiltinOperator.NumAdd, | 1800 index, |
| 1856 [cps.getMutable(index), cps.makeOne()])); | 1801 cps.applyBuiltin(BuiltinOperator.NumAdd, |
| 1802 [cps.getMutable(index), cps.makeOne()])); |
| 1857 cps.invokeContinuation(moveNextCont, [cps.makeTrue()]); | 1803 cps.invokeContinuation(moveNextCont, [cps.makeTrue()]); |
| 1858 | 1804 |
| 1859 reanalyzeFragment(cps); | 1805 reanalyzeFragment(cps); |
| 1860 | 1806 |
| 1861 // Replace the moveNext() call. It will be visited later. | 1807 // Replace the moveNext() call. It will be visited later. |
| 1862 LetPrim let = use.parent; | 1808 LetPrim let = use.parent; |
| 1863 cps.context = moveNextCont; | 1809 cps.context = moveNextCont; |
| 1864 cps.insertBelow(let); | 1810 cps.insertBelow(let); |
| 1865 let.remove(); | 1811 let.remove(); |
| 1866 use..replaceUsesWith(result)..destroy(); | 1812 use |
| 1813 ..replaceUsesWith(result) |
| 1814 ..destroy(); |
| 1867 } | 1815 } |
| 1868 } | 1816 } |
| 1869 | 1817 |
| 1870 // All effective uses have been rewritten. | 1818 // All effective uses have been rewritten. |
| 1871 destroyRefinementsOfDeadPrimitive(iterator); | 1819 destroyRefinementsOfDeadPrimitive(iterator); |
| 1872 | 1820 |
| 1873 // Rewrite the iterator call to initializers for 'index' and 'current'. | 1821 // Rewrite the iterator call to initializers for 'index' and 'current'. |
| 1874 CpsFragment cps = new CpsFragment(); | 1822 CpsFragment cps = new CpsFragment(); |
| 1875 cps.letMutable(index, cps.makeZero()); | 1823 cps.letMutable(index, cps.makeZero()); |
| 1876 cps.letMutable(current, cps.makeNull()); | 1824 cps.letMutable(current, cps.makeNull()); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1920 | 1868 |
| 1921 // If the selector does not apply, don't bother (will throw at runtime). | 1869 // If the selector does not apply, don't bother (will throw at runtime). |
| 1922 if (!call.signatureApplies(target)) return null; | 1870 if (!call.signatureApplies(target)) return null; |
| 1923 | 1871 |
| 1924 // If some optional arguments are missing, give up. | 1872 // If some optional arguments are missing, give up. |
| 1925 // TODO(asgerf): Improve optimization by inserting default arguments. | 1873 // TODO(asgerf): Improve optimization by inserting default arguments. |
| 1926 if (call.argumentCount != signature.parameterCount) return null; | 1874 if (call.argumentCount != signature.parameterCount) return null; |
| 1927 | 1875 |
| 1928 // Replace with InvokeStatic. | 1876 // Replace with InvokeStatic. |
| 1929 // The tear-off will be cleaned up by shrinking reductions. | 1877 // The tear-off will be cleaned up by shrinking reductions. |
| 1930 return new InvokeStatic(target, | 1878 return new InvokeStatic(target, new Selector.fromElement(target), |
| 1931 new Selector.fromElement(target), | 1879 node.arguments.toList(), node.sourceInformation); |
| 1932 node.arguments.toList(), | |
| 1933 node.sourceInformation); | |
| 1934 } | 1880 } |
| 1935 if (tearOff is InvokeMethod && tearOff.selector.isGetter) { | 1881 if (tearOff is InvokeMethod && tearOff.selector.isGetter) { |
| 1936 Selector getter = tearOff.selector; | 1882 Selector getter = tearOff.selector; |
| 1937 | 1883 |
| 1938 // TODO(asgerf): Support torn-off intercepted methods. | 1884 // TODO(asgerf): Support torn-off intercepted methods. |
| 1939 if (isInterceptedSelector(getter)) return null; | 1885 if (isInterceptedSelector(getter)) return null; |
| 1940 | 1886 |
| 1941 LetPrim tearOffBinding = tearOff.parent; | 1887 LetPrim tearOffBinding = tearOff.parent; |
| 1942 | 1888 |
| 1943 Primitive object = tearOff.receiver; | 1889 Primitive object = tearOff.receiver; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1965 // therefore risk duplicating its side effects. | 1911 // therefore risk duplicating its side effects. |
| 1966 if (!isPure && tearOff.hasMultipleRefinedUses) return null; | 1912 if (!isPure && tearOff.hasMultipleRefinedUses) return null; |
| 1967 | 1913 |
| 1968 // If the getter call is impure, we risk reordering side effects, | 1914 // If the getter call is impure, we risk reordering side effects, |
| 1969 // unless it is immediately prior to the closure call. | 1915 // unless it is immediately prior to the closure call. |
| 1970 if (!isPure && getEffectiveParent(node.parent) != tearOffBinding) { | 1916 if (!isPure && getEffectiveParent(node.parent) != tearOffBinding) { |
| 1971 return null; | 1917 return null; |
| 1972 } | 1918 } |
| 1973 | 1919 |
| 1974 InvokeMethod invoke = new InvokeMethod( | 1920 InvokeMethod invoke = new InvokeMethod( |
| 1975 object, | 1921 object, |
| 1976 new Selector.call(getter.memberName, call.callStructure), | 1922 new Selector.call(getter.memberName, call.callStructure), |
| 1977 type, | 1923 type, |
| 1978 node.arguments.toList(), | 1924 node.arguments.toList(), |
| 1979 sourceInformation: node.sourceInformation); | 1925 sourceInformation: node.sourceInformation); |
| 1980 node.receiverRef.changeTo(new Parameter(null)); // Remove the tear off use
. | 1926 node.receiverRef |
| 1927 .changeTo(new Parameter(null)); // Remove the tear off use. |
| 1981 | 1928 |
| 1982 if (tearOff.hasNoRefinedUses) { | 1929 if (tearOff.hasNoRefinedUses) { |
| 1983 // Eliminate the getter call if it has no more uses. | 1930 // Eliminate the getter call if it has no more uses. |
| 1984 // This cannot be delegated to other optimizations because we need to | 1931 // This cannot be delegated to other optimizations because we need to |
| 1985 // avoid duplication of side effects. | 1932 // avoid duplication of side effects. |
| 1986 destroyRefinementsOfDeadPrimitive(tearOff); | 1933 destroyRefinementsOfDeadPrimitive(tearOff); |
| 1987 tearOff.destroy(); | 1934 tearOff.destroy(); |
| 1988 tearOffBinding.remove(); | 1935 tearOffBinding.remove(); |
| 1989 } else { | 1936 } else { |
| 1990 // There are more uses, so we cannot eliminate the getter call. This | 1937 // There are more uses, so we cannot eliminate the getter call. This |
| (...skipping 10 matching lines...) Expand all Loading... |
| 2001 /// Inlines a single-use closure if it leaves the closure object with only | 1948 /// Inlines a single-use closure if it leaves the closure object with only |
| 2002 /// field accesses. This is optimized later by [ScalarReplacer]. | 1949 /// field accesses. This is optimized later by [ScalarReplacer]. |
| 2003 CpsFragment specializeSingleUseClosureCall(InvokeMethod node) { | 1950 CpsFragment specializeSingleUseClosureCall(InvokeMethod node) { |
| 2004 Selector call = node.selector; | 1951 Selector call = node.selector; |
| 2005 if (!call.isClosureCall) return null; | 1952 if (!call.isClosureCall) return null; |
| 2006 | 1953 |
| 2007 assert(!isInterceptedSelector(call)); | 1954 assert(!isInterceptedSelector(call)); |
| 2008 assert(call.argumentCount == node.argumentRefs.length); | 1955 assert(call.argumentCount == node.argumentRefs.length); |
| 2009 | 1956 |
| 2010 Primitive receiver = node.receiver; | 1957 Primitive receiver = node.receiver; |
| 2011 if (receiver is !CreateInstance) return null; | 1958 if (receiver is! CreateInstance) return null; |
| 2012 CreateInstance createInstance = receiver; | 1959 CreateInstance createInstance = receiver; |
| 2013 if (!createInstance.hasExactlyOneUse) return null; | 1960 if (!createInstance.hasExactlyOneUse) return null; |
| 2014 | 1961 |
| 2015 // Inline only closures. This avoids inlining the 'call' method of a class | 1962 // Inline only closures. This avoids inlining the 'call' method of a class |
| 2016 // that has many allocation sites. | 1963 // that has many allocation sites. |
| 2017 if (createInstance.classElement is !ClosureClassElement) return null; | 1964 if (createInstance.classElement is! ClosureClassElement) return null; |
| 2018 | 1965 |
| 2019 ClosureClassElement closureClassElement = createInstance.classElement; | 1966 ClosureClassElement closureClassElement = createInstance.classElement; |
| 2020 Element element = closureClassElement.localLookup(Identifiers.call); | 1967 Element element = closureClassElement.localLookup(Identifiers.call); |
| 2021 | 1968 |
| 2022 if (element == null || !element.isFunction) return null; | 1969 if (element == null || !element.isFunction) return null; |
| 2023 FunctionElement functionElement = element; | 1970 FunctionElement functionElement = element; |
| 2024 if (functionElement.asyncMarker != AsyncMarker.SYNC) return null; | 1971 if (functionElement.asyncMarker != AsyncMarker.SYNC) return null; |
| 2025 | 1972 |
| 2026 if (!call.signatureApplies(functionElement)) return null; | 1973 if (!call.signatureApplies(functionElement)) return null; |
| 2027 // Inline only for exact match. | 1974 // Inline only for exact match. |
| 2028 // TODO(sra): Handle call with defaulted arguments. | 1975 // TODO(sra): Handle call with defaulted arguments. |
| 2029 Selector targetSelector = new Selector.fromElement(functionElement); | 1976 Selector targetSelector = new Selector.fromElement(functionElement); |
| 2030 if (call.callStructure != targetSelector.callStructure) return null; | 1977 if (call.callStructure != targetSelector.callStructure) return null; |
| 2031 | 1978 |
| 2032 // Don't inline if [target] contains try-catch or try-finally. JavaScript | 1979 // Don't inline if [target] contains try-catch or try-finally. JavaScript |
| 2033 // engines typically do poor optimization of the entire function containing | 1980 // engines typically do poor optimization of the entire function containing |
| 2034 // the 'try'. | 1981 // the 'try'. |
| 2035 if (functionElement.resolvedAst.elements.containsTryStatement) return null; | 1982 if (functionElement.resolvedAst.elements.containsTryStatement) return null; |
| 2036 | 1983 |
| 2037 FunctionDefinition target = | 1984 FunctionDefinition target = |
| 2038 functionCompiler.compileToCpsIr(functionElement); | 1985 functionCompiler.compileToCpsIr(functionElement); |
| 2039 | 1986 |
| 2040 // Accesses to closed-over values are field access primitives. We we don't | 1987 // Accesses to closed-over values are field access primitives. We we don't |
| 2041 // inline if there are other uses of 'this' since that could be an escape or | 1988 // inline if there are other uses of 'this' since that could be an escape or |
| 2042 // a recursive call. | 1989 // a recursive call. |
| 2043 for (Reference ref = target.receiverParameter.firstRef; | 1990 for (Reference ref = target.receiverParameter.firstRef; |
| 2044 ref != null; | 1991 ref != null; |
| 2045 ref = ref.next) { | 1992 ref = ref.next) { |
| 2046 Node use = ref.parent; | 1993 Node use = ref.parent; |
| 2047 if (use is GetField) continue; | 1994 if (use is GetField) continue; |
| 2048 // Closures do not currently have writable fields, but closure conversion | 1995 // Closures do not currently have writable fields, but closure conversion |
| 2049 // could esily be changed to allocate some cells in a closure object. | 1996 // could esily be changed to allocate some cells in a closure object. |
| 2050 if (use is SetField && ref == use.objectRef) continue; | 1997 if (use is SetField && ref == use.objectRef) continue; |
| 2051 return null; | 1998 return null; |
| 2052 } | 1999 } |
| 2053 | 2000 |
| 2054 CpsFragment cps = new CpsFragment(node.sourceInformation); | 2001 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 2055 Primitive returnValue = cps.inlineFunction(target, | 2002 Primitive returnValue = cps.inlineFunction( |
| 2056 node.receiver, | 2003 target, node.receiver, node.arguments.toList(), |
| 2057 node.arguments.toList(), | |
| 2058 hint: node.hint); | 2004 hint: node.hint); |
| 2059 node.replaceUsesWith(returnValue); | 2005 node.replaceUsesWith(returnValue); |
| 2060 return cps; | 2006 return cps; |
| 2061 } | 2007 } |
| 2062 | 2008 |
| 2063 visitInterceptor(Interceptor node) { | 2009 visitInterceptor(Interceptor node) { |
| 2064 // Replace the interceptor with its input if the value is not intercepted. | 2010 // Replace the interceptor with its input if the value is not intercepted. |
| 2065 // If the input might be null, we cannot do this since the interceptor | 2011 // If the input might be null, we cannot do this since the interceptor |
| 2066 // might have to return JSNull. That case is handled by visitInvokeMethod | 2012 // might have to return JSNull. That case is handled by visitInvokeMethod |
| 2067 // and visitInvokeMethodDirectly which can sometimes tolerate that null | 2013 // and visitInvokeMethodDirectly which can sometimes tolerate that null |
| 2068 // is used instead of JSNull. | 2014 // is used instead of JSNull. |
| 2069 Primitive input = node.input; | 2015 Primitive input = node.input; |
| 2070 if (!input.type.isNullable && | 2016 if (!input.type.isNullable && |
| 2071 typeSystem.areDisjoint(input.type, typeSystem.interceptorType)) { | 2017 typeSystem.areDisjoint(input.type, typeSystem.interceptorType)) { |
| 2072 node.replaceUsesWith(input); | 2018 node.replaceUsesWith(input); |
| 2073 } | 2019 } |
| 2074 } | 2020 } |
| 2075 | 2021 |
| 2076 visitInvokeConstructor(InvokeConstructor node) { | 2022 visitInvokeConstructor(InvokeConstructor node) { |
| 2077 node.effects = | 2023 node.effects = |
| 2078 Effects.from(compiler.world.getSideEffectsOfElement(node.target)); | 2024 Effects.from(compiler.world.getSideEffectsOfElement(node.target)); |
| 2079 } | 2025 } |
| 2080 | 2026 |
| 2081 visitInvokeMethodDirectly(InvokeMethodDirectly node) { | 2027 visitInvokeMethodDirectly(InvokeMethodDirectly node) { |
| 2082 Element target = node.target; | 2028 Element target = node.target; |
| 2083 if (target is ConstructorBodyElement) { | 2029 if (target is ConstructorBodyElement) { |
| 2084 ConstructorBodyElement constructorBody = target; | 2030 ConstructorBodyElement constructorBody = target; |
| 2085 target = constructorBody.constructor; | 2031 target = constructorBody.constructor; |
| 2086 } | 2032 } |
| 2087 node.effects = | 2033 node.effects = Effects.from(compiler.world.getSideEffectsOfElement(target)); |
| 2088 Effects.from(compiler.world.getSideEffectsOfElement(target)); | |
| 2089 TypeMask receiverType = node.receiver.type; | 2034 TypeMask receiverType = node.receiver.type; |
| 2090 if (node.callingConvention == CallingConvention.Intercepted && | 2035 if (node.callingConvention == CallingConvention.Intercepted && |
| 2091 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { | 2036 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { |
| 2092 // Some direct calls take an interceptor because the target class is | 2037 // Some direct calls take an interceptor because the target class is |
| 2093 // mixed into a native class. If it is known at the call site that the | 2038 // mixed into a native class. If it is known at the call site that the |
| 2094 // receiver is non-intercepted, get rid of the interceptor. | 2039 // receiver is non-intercepted, get rid of the interceptor. |
| 2095 node.interceptorRef.changeTo(node.receiver); | 2040 node.interceptorRef.changeTo(node.receiver); |
| 2096 } | 2041 } |
| 2097 } | 2042 } |
| 2098 | 2043 |
| 2099 visitInvokeMethod(InvokeMethod node) { | 2044 visitInvokeMethod(InvokeMethod node) { |
| 2100 var specialized = | 2045 var specialized = specializeOperatorCall(node) ?? |
| 2101 specializeOperatorCall(node) ?? | |
| 2102 specializeFieldAccess(node) ?? | 2046 specializeFieldAccess(node) ?? |
| 2103 specializeIndexableAccess(node) ?? | 2047 specializeIndexableAccess(node) ?? |
| 2104 specializeArrayAccess(node) ?? | 2048 specializeArrayAccess(node) ?? |
| 2105 specializeSingleUseClosureCall(node) ?? | 2049 specializeSingleUseClosureCall(node) ?? |
| 2106 specializeClosureCall(node); | 2050 specializeClosureCall(node); |
| 2107 if (specialized != null) return specialized; | 2051 if (specialized != null) return specialized; |
| 2108 | 2052 |
| 2109 TypeMask receiverType = node.receiver.type; | 2053 TypeMask receiverType = node.receiver.type; |
| 2110 node.mask = typeSystem.intersection(node.mask, receiverType); | 2054 node.mask = typeSystem.intersection(node.mask, receiverType); |
| 2111 | 2055 |
| 2112 node.effects = Effects.from( | 2056 node.effects = Effects.from( |
| 2113 compiler.world.getSideEffectsOfSelector(node.selector, node.mask)); | 2057 compiler.world.getSideEffectsOfSelector(node.selector, node.mask)); |
| 2114 | 2058 |
| 2115 bool canBeNonThrowingCallOnNull = | 2059 bool canBeNonThrowingCallOnNull = |
| 2116 selectorsOnNull.contains(node.selector) && | 2060 selectorsOnNull.contains(node.selector) && receiverType.isNullable; |
| 2117 receiverType.isNullable; | |
| 2118 | 2061 |
| 2119 if (node.callingConvention == CallingConvention.Intercepted && | 2062 if (node.callingConvention == CallingConvention.Intercepted && |
| 2120 !canBeNonThrowingCallOnNull && | 2063 !canBeNonThrowingCallOnNull && |
| 2121 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { | 2064 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { |
| 2122 // Use the Dart receiver as the JS receiver. This changes the wording of | 2065 // Use the Dart receiver as the JS receiver. This changes the wording of |
| 2123 // the error message when the receiver is null, but we accept this. | 2066 // the error message when the receiver is null, but we accept this. |
| 2124 node.interceptorRef.changeTo(node.receiver); | 2067 node.interceptorRef.changeTo(node.receiver); |
| 2125 | 2068 |
| 2126 // Replace the extra receiver argument with a dummy value if the | 2069 // Replace the extra receiver argument with a dummy value if the |
| 2127 // target definitely does not use it. | 2070 // target definitely does not use it. |
| 2128 if (typeSystem.targetIgnoresReceiverArgument(receiverType, | 2071 if (typeSystem.targetIgnoresReceiverArgument( |
| 2129 node.selector)) { | 2072 receiverType, node.selector)) { |
| 2130 node.makeDummyIntercepted(); | 2073 node.makeDummyIntercepted(); |
| 2131 } | 2074 } |
| 2132 } | 2075 } |
| 2133 } | 2076 } |
| 2134 | 2077 |
| 2135 CpsFragment visitTypeCast(TypeCast node) { | 2078 CpsFragment visitTypeCast(TypeCast node) { |
| 2136 AbstractConstantValue value = getValue(node.value); | 2079 AbstractConstantValue value = getValue(node.value); |
| 2137 switch (lattice.isSubtypeOf(value, node.dartType, allowNull: true)) { | 2080 switch (lattice.isSubtypeOf(value, node.dartType, allowNull: true)) { |
| 2138 case AbstractBool.Maybe: | 2081 case AbstractBool.Maybe: |
| 2139 case AbstractBool.Nothing: | 2082 case AbstractBool.Nothing: |
| (...skipping 13 matching lines...) Expand all Loading... |
| 2153 | 2096 |
| 2154 /// Specialize calls to internal static methods. | 2097 /// Specialize calls to internal static methods. |
| 2155 specializeInternalMethodCall(InvokeStatic node) { | 2098 specializeInternalMethodCall(InvokeStatic node) { |
| 2156 if (node.target == backend.helpers.stringInterpolationHelper) { | 2099 if (node.target == backend.helpers.stringInterpolationHelper) { |
| 2157 Primitive argument = node.argument(0); | 2100 Primitive argument = node.argument(0); |
| 2158 AbstractConstantValue value = getValue(argument); | 2101 AbstractConstantValue value = getValue(argument); |
| 2159 if (lattice.isDefinitelyString(value)) { | 2102 if (lattice.isDefinitelyString(value)) { |
| 2160 node.replaceUsesWith(argument); | 2103 node.replaceUsesWith(argument); |
| 2161 return new CpsFragment(); | 2104 return new CpsFragment(); |
| 2162 } else if (typeSystem.isDefinitelySelfInterceptor(value.type)) { | 2105 } else if (typeSystem.isDefinitelySelfInterceptor(value.type)) { |
| 2163 TypeMask toStringReturn = typeSystem.getInvokeReturnType( | 2106 TypeMask toStringReturn = |
| 2164 Selectors.toString_, value.type); | 2107 typeSystem.getInvokeReturnType(Selectors.toString_, value.type); |
| 2165 if (typeSystem.isDefinitelyString(toStringReturn)) { | 2108 if (typeSystem.isDefinitelyString(toStringReturn)) { |
| 2166 CpsFragment cps = new CpsFragment(node.sourceInformation); | 2109 CpsFragment cps = new CpsFragment(node.sourceInformation); |
| 2167 Primitive invoke = cps.invokeMethod( | 2110 Primitive invoke = cps.invokeMethod( |
| 2168 argument, | 2111 argument, Selectors.toString_, value.type, [], |
| 2169 Selectors.toString_, | |
| 2170 value.type, | |
| 2171 [], | |
| 2172 callingConvention: CallingConvention.DummyIntercepted); | 2112 callingConvention: CallingConvention.DummyIntercepted); |
| 2173 node.replaceUsesWith(invoke); | 2113 node.replaceUsesWith(invoke); |
| 2174 return cps; | 2114 return cps; |
| 2175 } | 2115 } |
| 2176 } | 2116 } |
| 2177 } else if (node.target == compiler.identicalFunction) { | 2117 } else if (node.target == compiler.identicalFunction) { |
| 2178 if (node.argumentRefs.length == 2) { | 2118 if (node.argumentRefs.length == 2) { |
| 2179 return new ApplyBuiltinOperator(BuiltinOperator.Identical, | 2119 return new ApplyBuiltinOperator(BuiltinOperator.Identical, |
| 2180 [node.argument(0), node.argument(1)], | 2120 [node.argument(0), node.argument(1)], node.sourceInformation); |
| 2181 node.sourceInformation); | |
| 2182 } | 2121 } |
| 2183 } | 2122 } |
| 2184 return null; | 2123 return null; |
| 2185 } | 2124 } |
| 2186 | 2125 |
| 2187 visitInvokeStatic(InvokeStatic node) { | 2126 visitInvokeStatic(InvokeStatic node) { |
| 2188 node.effects = Effects.from( | 2127 node.effects = |
| 2189 compiler.world.getSideEffectsOfElement(node.target)); | 2128 Effects.from(compiler.world.getSideEffectsOfElement(node.target)); |
| 2190 return specializeInternalMethodCall(node); | 2129 return specializeInternalMethodCall(node); |
| 2191 } | 2130 } |
| 2192 | 2131 |
| 2193 AbstractConstantValue getValue(Variable node) { | 2132 AbstractConstantValue getValue(Variable node) { |
| 2194 assert(node.type != null); | 2133 assert(node.type != null); |
| 2195 ConstantValue constant = values[node]; | 2134 ConstantValue constant = values[node]; |
| 2196 if (constant != null) { | 2135 if (constant != null) { |
| 2197 return new AbstractConstantValue.constantValue(constant, node.type); | 2136 return new AbstractConstantValue.constantValue(constant, node.type); |
| 2198 } | 2137 } |
| 2199 return new AbstractConstantValue.nonConstant(node.type); | 2138 return new AbstractConstantValue.nonConstant(node.type); |
| 2200 } | 2139 } |
| 2201 | 2140 |
| 2202 | |
| 2203 /*************************** PRIMITIVES **************************/ | 2141 /*************************** PRIMITIVES **************************/ |
| 2204 // | 2142 // |
| 2205 // The visit method for a primitive may return one of the following: | 2143 // The visit method for a primitive may return one of the following: |
| 2206 // - Primitive: | 2144 // - Primitive: |
| 2207 // The visited primitive will be replaced by the returned primitive. | 2145 // The visited primitive will be replaced by the returned primitive. |
| 2208 // The type of the primitive will be recomputed. | 2146 // The type of the primitive will be recomputed. |
| 2209 // - CpsFragment: | 2147 // - CpsFragment: |
| 2210 // The primitive binding will be destroyed and replaced by the given | 2148 // The primitive binding will be destroyed and replaced by the given |
| 2211 // code fragment. All types in the fragment will be recomputed. | 2149 // code fragment. All types in the fragment will be recomputed. |
| 2212 // - Null: | 2150 // - Null: |
| (...skipping 10 matching lines...) Expand all Loading... |
| 2223 // Concatenate consecutive constants. | 2161 // Concatenate consecutive constants. |
| 2224 bool argumentsWereRemoved = false; | 2162 bool argumentsWereRemoved = false; |
| 2225 int i = 0; | 2163 int i = 0; |
| 2226 while (i < node.argumentRefs.length - 1) { | 2164 while (i < node.argumentRefs.length - 1) { |
| 2227 int startOfSequence = i; | 2165 int startOfSequence = i; |
| 2228 AbstractConstantValue firstValue = getValue(node.argument(i++)); | 2166 AbstractConstantValue firstValue = getValue(node.argument(i++)); |
| 2229 if (!firstValue.isConstant) continue; | 2167 if (!firstValue.isConstant) continue; |
| 2230 AbstractConstantValue secondValue = getValue(node.argument(i++)); | 2168 AbstractConstantValue secondValue = getValue(node.argument(i++)); |
| 2231 if (!secondValue.isConstant) continue; | 2169 if (!secondValue.isConstant) continue; |
| 2232 | 2170 |
| 2233 ast.DartString string = | 2171 ast.DartString string = new ast.ConsDartString( |
| 2234 new ast.ConsDartString(getString(firstValue), | 2172 getString(firstValue), getString(secondValue)); |
| 2235 getString(secondValue)); | |
| 2236 | 2173 |
| 2237 // We found a sequence of at least two constants. | 2174 // We found a sequence of at least two constants. |
| 2238 // Look for the end of the sequence. | 2175 // Look for the end of the sequence. |
| 2239 while (i < node.argumentRefs.length) { | 2176 while (i < node.argumentRefs.length) { |
| 2240 AbstractConstantValue value = getValue(node.argument(i)); | 2177 AbstractConstantValue value = getValue(node.argument(i)); |
| 2241 if (!value.isConstant) break; | 2178 if (!value.isConstant) break; |
| 2242 string = new ast.ConsDartString(string, getString(value)); | 2179 string = new ast.ConsDartString(string, getString(value)); |
| 2243 ++i; | 2180 ++i; |
| 2244 } | 2181 } |
| 2245 Constant prim = | 2182 Constant prim = |
| (...skipping 27 matching lines...) Expand all Loading... |
| 2273 BuiltinOperator newOperator; | 2210 BuiltinOperator newOperator; |
| 2274 if (left.isNullConstant || right.isNullConstant) { | 2211 if (left.isNullConstant || right.isNullConstant) { |
| 2275 // Use `==` for comparing against null, so JS undefined and JS null | 2212 // Use `==` for comparing against null, so JS undefined and JS null |
| 2276 // are considered equal. | 2213 // are considered equal. |
| 2277 newOperator = BuiltinOperator.LooseEq; | 2214 newOperator = BuiltinOperator.LooseEq; |
| 2278 } else if (!left.isNullable || !right.isNullable) { | 2215 } else if (!left.isNullable || !right.isNullable) { |
| 2279 // If at most one operand can be Dart null, we can use `===`. | 2216 // If at most one operand can be Dart null, we can use `===`. |
| 2280 // This is not safe when we might compare JS null and JS undefined. | 2217 // This is not safe when we might compare JS null and JS undefined. |
| 2281 newOperator = BuiltinOperator.StrictEq; | 2218 newOperator = BuiltinOperator.StrictEq; |
| 2282 } else if (lattice.isDefinitelyNum(left, allowNull: true) && | 2219 } else if (lattice.isDefinitelyNum(left, allowNull: true) && |
| 2283 lattice.isDefinitelyNum(right, allowNull: true)) { | 2220 lattice.isDefinitelyNum(right, allowNull: true)) { |
| 2284 // If both operands can be null, but otherwise are of the same type, | 2221 // If both operands can be null, but otherwise are of the same type, |
| 2285 // we can use `==` for comparison. | 2222 // we can use `==` for comparison. |
| 2286 // This is not safe e.g. for comparing strings against numbers. | 2223 // This is not safe e.g. for comparing strings against numbers. |
| 2287 newOperator = BuiltinOperator.LooseEq; | 2224 newOperator = BuiltinOperator.LooseEq; |
| 2288 } else if (lattice.isDefinitelyString(left, allowNull: true) && | 2225 } else if (lattice.isDefinitelyString(left, allowNull: true) && |
| 2289 lattice.isDefinitelyString(right, allowNull: true)) { | 2226 lattice.isDefinitelyString(right, allowNull: true)) { |
| 2290 newOperator = BuiltinOperator.LooseEq; | 2227 newOperator = BuiltinOperator.LooseEq; |
| 2291 } else if (lattice.isDefinitelyBool(left, allowNull: true) && | 2228 } else if (lattice.isDefinitelyBool(left, allowNull: true) && |
| 2292 lattice.isDefinitelyBool(right, allowNull: true)) { | 2229 lattice.isDefinitelyBool(right, allowNull: true)) { |
| 2293 newOperator = BuiltinOperator.LooseEq; | 2230 newOperator = BuiltinOperator.LooseEq; |
| 2294 } | 2231 } |
| 2295 if (newOperator != null) { | 2232 if (newOperator != null) { |
| 2296 return new ApplyBuiltinOperator(newOperator, | 2233 return new ApplyBuiltinOperator( |
| 2297 node.arguments.toList(), | 2234 newOperator, node.arguments.toList(), node.sourceInformation); |
| 2298 node.sourceInformation); | |
| 2299 } | 2235 } |
| 2300 break; | 2236 break; |
| 2301 | 2237 |
| 2302 case BuiltinOperator.StrictEq: | 2238 case BuiltinOperator.StrictEq: |
| 2303 case BuiltinOperator.LooseEq: | 2239 case BuiltinOperator.LooseEq: |
| 2304 case BuiltinOperator.StrictNeq: | 2240 case BuiltinOperator.StrictNeq: |
| 2305 case BuiltinOperator.LooseNeq: | 2241 case BuiltinOperator.LooseNeq: |
| 2306 bool negated = | 2242 bool negated = node.operator == BuiltinOperator.StrictNeq || |
| 2307 node.operator == BuiltinOperator.StrictNeq || | |
| 2308 node.operator == BuiltinOperator.LooseNeq; | 2243 node.operator == BuiltinOperator.LooseNeq; |
| 2309 for (int firstIndex in [0, 1]) { | 2244 for (int firstIndex in [0, 1]) { |
| 2310 int secondIndex = 1 - firstIndex; | 2245 int secondIndex = 1 - firstIndex; |
| 2311 Primitive firstArg = node.argument(firstIndex); | 2246 Primitive firstArg = node.argument(firstIndex); |
| 2312 Primitive secondArg = node.argument(secondIndex); | 2247 Primitive secondArg = node.argument(secondIndex); |
| 2313 AbstractConstantValue first = getValue(firstArg); | 2248 AbstractConstantValue first = getValue(firstArg); |
| 2314 if (!lattice.isDefinitelyBool(first)) continue; | 2249 if (!lattice.isDefinitelyBool(first)) continue; |
| 2315 AbstractConstantValue second = getValue(secondArg); | 2250 AbstractConstantValue second = getValue(secondArg); |
| 2316 if (!second.isConstant || !second.constant.isBool) continue; | 2251 if (!second.isConstant || !second.constant.isBool) continue; |
| 2317 bool isTrueConstant = second.constant.isTrue; | 2252 bool isTrueConstant = second.constant.isTrue; |
| 2318 if (isTrueConstant == !negated) { | 2253 if (isTrueConstant == !negated) { |
| 2319 // (x === true) ==> x | 2254 // (x === true) ==> x |
| 2320 // (x !== false) ==> x | 2255 // (x !== false) ==> x |
| 2321 node.replaceUsesWith(firstArg); | 2256 node.replaceUsesWith(firstArg); |
| 2322 return null; | 2257 return null; |
| 2323 } else { | 2258 } else { |
| 2324 // (x === false) ==> !x | 2259 // (x === false) ==> !x |
| 2325 // (x !== true) ==> !x | 2260 // (x !== true) ==> !x |
| 2326 return new ApplyBuiltinOperator( | 2261 return new ApplyBuiltinOperator( |
| 2327 BuiltinOperator.IsFalsy, | 2262 BuiltinOperator.IsFalsy, [firstArg], node.sourceInformation); |
| 2328 [firstArg], | |
| 2329 node.sourceInformation); | |
| 2330 } | 2263 } |
| 2331 } | 2264 } |
| 2332 break; | 2265 break; |
| 2333 | 2266 |
| 2334 default: | 2267 default: |
| 2335 } | 2268 } |
| 2336 return null; | 2269 return null; |
| 2337 } | 2270 } |
| 2338 | 2271 |
| 2339 void visitApplyBuiltinMethod(ApplyBuiltinMethod node) { | 2272 void visitApplyBuiltinMethod(ApplyBuiltinMethod node) {} |
| 2340 } | |
| 2341 | 2273 |
| 2342 visitTypeTest(TypeTest node) { | 2274 visitTypeTest(TypeTest node) { |
| 2343 Primitive prim = node.value; | 2275 Primitive prim = node.value; |
| 2344 | 2276 |
| 2345 Primitive unaryBuiltinOperator(BuiltinOperator operator) => | 2277 Primitive unaryBuiltinOperator(BuiltinOperator operator) => |
| 2346 new ApplyBuiltinOperator( | 2278 new ApplyBuiltinOperator( |
| 2347 operator, <Primitive>[prim], node.sourceInformation); | 2279 operator, <Primitive>[prim], node.sourceInformation); |
| 2348 | 2280 |
| 2349 AbstractConstantValue value = getValue(prim); | 2281 AbstractConstantValue value = getValue(prim); |
| 2350 types.DartType dartType = node.dartType; | 2282 types.DartType dartType = node.dartType; |
| 2351 | 2283 |
| 2352 if (!(dartType.isInterfaceType && dartType.isRaw)) { | 2284 if (!(dartType.isInterfaceType && dartType.isRaw)) { |
| 2353 // TODO(23685): Efficient function arity check. | 2285 // TODO(23685): Efficient function arity check. |
| 2354 // TODO(sra): Pass interceptor to runtime subtype functions. | 2286 // TODO(sra): Pass interceptor to runtime subtype functions. |
| 2355 return null; | 2287 return null; |
| 2356 } | 2288 } |
| 2357 | 2289 |
| 2358 if (dartType == dartTypes.coreTypes.intType) { | 2290 if (dartType == dartTypes.coreTypes.intType) { |
| 2359 // Compile as typeof x === 'number' && Math.floor(x) === x | 2291 // Compile as typeof x === 'number' && Math.floor(x) === x |
| 2360 if (lattice.isDefinitelyNum(value, allowNull: true)) { | 2292 if (lattice.isDefinitelyNum(value, allowNull: true)) { |
| 2361 // If value is null or a number, we can skip the typeof test. | 2293 // If value is null or a number, we can skip the typeof test. |
| 2362 return new ApplyBuiltinOperator( | 2294 return new ApplyBuiltinOperator(BuiltinOperator.IsFloor, |
| 2363 BuiltinOperator.IsFloor, | 2295 <Primitive>[prim, prim], node.sourceInformation); |
| 2364 <Primitive>[prim, prim], | |
| 2365 node.sourceInformation); | |
| 2366 } | 2296 } |
| 2367 if (lattice.isDefinitelyNotNonIntegerDouble(value)) { | 2297 if (lattice.isDefinitelyNotNonIntegerDouble(value)) { |
| 2368 // If the value cannot be a non-integer double, but might not be a | 2298 // If the value cannot be a non-integer double, but might not be a |
| 2369 // number at all, we can skip the Math.floor test. | 2299 // number at all, we can skip the Math.floor test. |
| 2370 return unaryBuiltinOperator(BuiltinOperator.IsNumber); | 2300 return unaryBuiltinOperator(BuiltinOperator.IsNumber); |
| 2371 } | 2301 } |
| 2372 return new ApplyBuiltinOperator( | 2302 return new ApplyBuiltinOperator(BuiltinOperator.IsInteger, |
| 2373 BuiltinOperator.IsInteger, | 2303 <Primitive>[prim, prim, prim], node.sourceInformation); |
| 2374 <Primitive>[prim, prim, prim], | |
| 2375 node.sourceInformation); | |
| 2376 } | 2304 } |
| 2377 if (node.dartType == dartTypes.coreTypes.numType || | 2305 if (node.dartType == dartTypes.coreTypes.numType || |
| 2378 node.dartType == dartTypes.coreTypes.doubleType) { | 2306 node.dartType == dartTypes.coreTypes.doubleType) { |
| 2379 return new ApplyBuiltinOperator( | 2307 return new ApplyBuiltinOperator( |
| 2380 BuiltinOperator.IsNumber, | 2308 BuiltinOperator.IsNumber, <Primitive>[prim], node.sourceInformation); |
| 2381 <Primitive>[prim], | |
| 2382 node.sourceInformation); | |
| 2383 } | 2309 } |
| 2384 | 2310 |
| 2385 AbstractBool isNullableSubtype = | 2311 AbstractBool isNullableSubtype = |
| 2386 lattice.isSubtypeOf(value, node.dartType, allowNull: true); | 2312 lattice.isSubtypeOf(value, node.dartType, allowNull: true); |
| 2387 AbstractBool isNullPassingTest = | 2313 AbstractBool isNullPassingTest = |
| 2388 lattice.isSubtypeOf(lattice.nullValue, node.dartType, allowNull: false); | 2314 lattice.isSubtypeOf(lattice.nullValue, node.dartType, allowNull: false); |
| 2389 if (isNullableSubtype == AbstractBool.True && | 2315 if (isNullableSubtype == AbstractBool.True && |
| 2390 isNullPassingTest == AbstractBool.False) { | 2316 isNullPassingTest == AbstractBool.False) { |
| 2391 // Null is the only value not satisfying the type test. | 2317 // Null is the only value not satisfying the type test. |
| 2392 // Replace the type test with a null-check. | 2318 // Replace the type test with a null-check. |
| 2393 // This has lower priority than the 'typeof'-based tests because | 2319 // This has lower priority than the 'typeof'-based tests because |
| 2394 // 'typeof' expressions might give the VM some more useful information. | 2320 // 'typeof' expressions might give the VM some more useful information. |
| 2395 Primitive nullConst = makeConstantPrimitive(new NullConstantValue()); | 2321 Primitive nullConst = makeConstantPrimitive(new NullConstantValue()); |
| 2396 new LetPrim(nullConst).insertAbove(node.parent); | 2322 new LetPrim(nullConst).insertAbove(node.parent); |
| 2397 return new ApplyBuiltinOperator( | 2323 return new ApplyBuiltinOperator(BuiltinOperator.LooseNeq, |
| 2398 BuiltinOperator.LooseNeq, | 2324 <Primitive>[prim, nullConst], node.sourceInformation); |
| 2399 <Primitive>[prim, nullConst], | |
| 2400 node.sourceInformation); | |
| 2401 } | 2325 } |
| 2402 | 2326 |
| 2403 if (dartType.element == functionCompiler.glue.jsFixedArrayClass) { | 2327 if (dartType.element == functionCompiler.glue.jsFixedArrayClass) { |
| 2404 // TODO(sra): Check input is restricted to JSArray. | 2328 // TODO(sra): Check input is restricted to JSArray. |
| 2405 return unaryBuiltinOperator(BuiltinOperator.IsFixedLengthJSArray); | 2329 return unaryBuiltinOperator(BuiltinOperator.IsFixedLengthJSArray); |
| 2406 } | 2330 } |
| 2407 | 2331 |
| 2408 if (dartType.element == functionCompiler.glue.jsExtendableArrayClass) { | 2332 if (dartType.element == functionCompiler.glue.jsExtendableArrayClass) { |
| 2409 // TODO(sra): Check input is restricted to JSArray. | 2333 // TODO(sra): Check input is restricted to JSArray. |
| 2410 return unaryBuiltinOperator(BuiltinOperator.IsExtendableJSArray); | 2334 return unaryBuiltinOperator(BuiltinOperator.IsExtendableJSArray); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 2441 return null; | 2365 return null; |
| 2442 } | 2366 } |
| 2443 | 2367 |
| 2444 visitBoundsCheck(BoundsCheck node) { | 2368 visitBoundsCheck(BoundsCheck node) { |
| 2445 // Eliminate bounds checks using constant folding. | 2369 // Eliminate bounds checks using constant folding. |
| 2446 // The [BoundsChecker] pass does not try to eliminate checks that could be | 2370 // The [BoundsChecker] pass does not try to eliminate checks that could be |
| 2447 // eliminated by constant folding. | 2371 // eliminated by constant folding. |
| 2448 if (node.hasNoChecks) return; | 2372 if (node.hasNoChecks) return; |
| 2449 Primitive indexPrim = node.index; | 2373 Primitive indexPrim = node.index; |
| 2450 int index = lattice.intValue(getValue(indexPrim)); | 2374 int index = lattice.intValue(getValue(indexPrim)); |
| 2451 int length = node.lengthRef == null | 2375 int length = |
| 2452 ? null | 2376 node.lengthRef == null ? null : lattice.intValue(getValue(node.length)); |
| 2453 : lattice.intValue(getValue(node.length)); | |
| 2454 if (index != null && length != null && index < length) { | 2377 if (index != null && length != null && index < length) { |
| 2455 node.checks &= ~BoundsCheck.UPPER_BOUND; | 2378 node.checks &= ~BoundsCheck.UPPER_BOUND; |
| 2456 } | 2379 } |
| 2457 if (index != null && index >= 0) { | 2380 if (index != null && index >= 0) { |
| 2458 node.checks &= ~BoundsCheck.LOWER_BOUND; | 2381 node.checks &= ~BoundsCheck.LOWER_BOUND; |
| 2459 } | 2382 } |
| 2460 if (length != null && length > 0) { | 2383 if (length != null && length > 0) { |
| 2461 node.checks &= ~BoundsCheck.EMPTINESS; | 2384 node.checks &= ~BoundsCheck.EMPTINESS; |
| 2462 } | 2385 } |
| 2463 if (typeSystem.isDefinitelyInt(indexPrim.type)) { | 2386 if (typeSystem.isDefinitelyInt(indexPrim.type)) { |
| 2464 node.checks &= ~BoundsCheck.INTEGER; | 2387 node.checks &= ~BoundsCheck.INTEGER; |
| 2465 } | 2388 } |
| 2466 if (!node.lengthUsedInCheck && node.lengthRef != null) { | 2389 if (!node.lengthUsedInCheck && node.lengthRef != null) { |
| 2467 node..lengthRef.unlink()..lengthRef = null; | 2390 node |
| 2391 ..lengthRef.unlink() |
| 2392 ..lengthRef = null; |
| 2468 } | 2393 } |
| 2469 if (node.checks == BoundsCheck.NONE) { | 2394 if (node.checks == BoundsCheck.NONE) { |
| 2470 // We can't remove the bounds check node because it may still be used to | 2395 // We can't remove the bounds check node because it may still be used to |
| 2471 // restrict code motion. But the index is no longer needed. | 2396 // restrict code motion. But the index is no longer needed. |
| 2472 // TODO(asgerf): Since this was eliminated by constant folding, it should | 2397 // TODO(asgerf): Since this was eliminated by constant folding, it should |
| 2473 // be safe to remove because any path-sensitive information we relied | 2398 // be safe to remove because any path-sensitive information we relied |
| 2474 // upon to do this are expressed by other refinement nodes that also | 2399 // upon to do this are expressed by other refinement nodes that also |
| 2475 // restrict code motion. However, if we want to run this pass after | 2400 // restrict code motion. However, if we want to run this pass after |
| 2476 // [BoundsChecker] that would not be safe any more, so for now we | 2401 // [BoundsChecker] that would not be safe any more, so for now we |
| 2477 // keep the node for forward compatibilty. | 2402 // keep the node for forward compatibilty. |
| 2478 node..indexRef.unlink()..indexRef = null; | 2403 node |
| 2404 ..indexRef.unlink() |
| 2405 ..indexRef = null; |
| 2479 } | 2406 } |
| 2480 } | 2407 } |
| 2481 | 2408 |
| 2482 visitReceiverCheck(ReceiverCheck node) { | 2409 visitReceiverCheck(ReceiverCheck node) { |
| 2483 Primitive input = node.value; | 2410 Primitive input = node.value; |
| 2484 if (!input.type.isNullable && | 2411 if (!input.type.isNullable && |
| 2485 (node.isNullCheck || | 2412 (node.isNullCheck || |
| 2486 !input.type.needsNoSuchMethodHandling(node.selector, classWorld))) { | 2413 !input.type.needsNoSuchMethodHandling(node.selector, classWorld))) { |
| 2487 node.replaceUsesWith(input); | 2414 node.replaceUsesWith(input); |
| 2488 return new CpsFragment(); | 2415 return new CpsFragment(); |
| 2489 } | 2416 } |
| 2490 return null; | 2417 return null; |
| 2491 } | 2418 } |
| 2492 | 2419 |
| 2493 visitGetLength(GetLength node) { | 2420 visitGetLength(GetLength node) { |
| 2494 node.isFinal = typeSystem.isDefinitelyFixedLengthIndexable( | 2421 node.isFinal = typeSystem.isDefinitelyFixedLengthIndexable(node.object.type, |
| 2495 node.object.type, allowNull: true); | 2422 allowNull: true); |
| 2496 } | 2423 } |
| 2497 | 2424 |
| 2498 visitReadTypeVariable(ReadTypeVariable node) { | 2425 visitReadTypeVariable(ReadTypeVariable node) { |
| 2499 // Pattern match on | 2426 // Pattern match on |
| 2500 // | 2427 // |
| 2501 // ReadTypeVariable(var, CreateInstance(..., TypeExpression(arguments))) | 2428 // ReadTypeVariable(var, CreateInstance(..., TypeExpression(arguments))) |
| 2502 // | 2429 // |
| 2503 // and extract the argument that corresponds to the type variable. This is a | 2430 // and extract the argument that corresponds to the type variable. This is a |
| 2504 // shrinking reduction. | 2431 // shrinking reduction. |
| 2505 // | 2432 // |
| (...skipping 26 matching lines...) Expand all Loading... |
| 2532 } | 2459 } |
| 2533 return null; | 2460 return null; |
| 2534 } | 2461 } |
| 2535 | 2462 |
| 2536 bool isNullConstant(Primitive prim) => prim is Constant && prim.value.isNull; | 2463 bool isNullConstant(Primitive prim) => prim is Constant && prim.value.isNull; |
| 2537 | 2464 |
| 2538 visitCreateInstance(CreateInstance node) { | 2465 visitCreateInstance(CreateInstance node) { |
| 2539 Primitive typeInformation = node.typeInformation; | 2466 Primitive typeInformation = node.typeInformation; |
| 2540 if (typeInformation is TypeExpression && | 2467 if (typeInformation is TypeExpression && |
| 2541 typeInformation.arguments.every(isNullConstant)) { | 2468 typeInformation.arguments.every(isNullConstant)) { |
| 2542 node..typeInformationRef.unlink()..typeInformationRef = null; | 2469 node |
| 2470 ..typeInformationRef.unlink() |
| 2471 ..typeInformationRef = null; |
| 2543 } | 2472 } |
| 2544 } | 2473 } |
| 2545 } | 2474 } |
| 2546 | 2475 |
| 2547 /** | 2476 /** |
| 2548 * Runs an analysis pass on the given function definition in order to detect | 2477 * Runs an analysis pass on the given function definition in order to detect |
| 2549 * const-ness as well as reachability, both of which are used in the subsequent | 2478 * const-ness as well as reachability, both of which are used in the subsequent |
| 2550 * transformation pass. | 2479 * transformation pass. |
| 2551 */ | 2480 */ |
| 2552 class TypePropagationVisitor implements Visitor { | 2481 class TypePropagationVisitor implements Visitor { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2584 } | 2513 } |
| 2585 | 2514 |
| 2586 AbstractConstantValue constantValue(ConstantValue constant, [TypeMask type]) { | 2515 AbstractConstantValue constantValue(ConstantValue constant, [TypeMask type]) { |
| 2587 return lattice.constant(constant, type); | 2516 return lattice.constant(constant, type); |
| 2588 } | 2517 } |
| 2589 | 2518 |
| 2590 // Stores the current lattice value for primitives and mutable variables. | 2519 // Stores the current lattice value for primitives and mutable variables. |
| 2591 // Access through [getValue] and [setValue]. | 2520 // Access through [getValue] and [setValue]. |
| 2592 final Map<Variable, ConstantValue> values; | 2521 final Map<Variable, ConstantValue> values; |
| 2593 | 2522 |
| 2594 TypePropagationVisitor(this.lattice, | 2523 TypePropagationVisitor(this.lattice, this.values, this.internalError); |
| 2595 this.values, | |
| 2596 this.internalError); | |
| 2597 | 2524 |
| 2598 void analyze(FunctionDefinition root, bool recomputeAll) { | 2525 void analyze(FunctionDefinition root, bool recomputeAll) { |
| 2599 reachableContinuations.clear(); | 2526 reachableContinuations.clear(); |
| 2600 if (recomputeAll) { | 2527 if (recomputeAll) { |
| 2601 new ResetAnalysisInfo(reachableContinuations, values).visit(root); | 2528 new ResetAnalysisInfo(reachableContinuations, values).visit(root); |
| 2602 } | 2529 } |
| 2603 | 2530 |
| 2604 // Initially, only the root node is reachable. | 2531 // Initially, only the root node is reachable. |
| 2605 push(root); | 2532 push(root); |
| 2606 | 2533 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 2623 // Process all usages of a changed definition. | 2550 // Process all usages of a changed definition. |
| 2624 Definition def = defWorklist.removeLast(); | 2551 Definition def = defWorklist.removeLast(); |
| 2625 | 2552 |
| 2626 // Visit all uses of this definition. This might add new entries to | 2553 // Visit all uses of this definition. This might add new entries to |
| 2627 // [nodeWorklist], for example by visiting a newly-constant usage within | 2554 // [nodeWorklist], for example by visiting a newly-constant usage within |
| 2628 // a branch node. | 2555 // a branch node. |
| 2629 for (Reference ref = def.firstRef; ref != null; ref = ref.next) { | 2556 for (Reference ref = def.firstRef; ref != null; ref = ref.next) { |
| 2630 visit(ref.parent); | 2557 visit(ref.parent); |
| 2631 } | 2558 } |
| 2632 } else { | 2559 } else { |
| 2633 break; // Both worklists empty. | 2560 break; // Both worklists empty. |
| 2634 } | 2561 } |
| 2635 } | 2562 } |
| 2636 } | 2563 } |
| 2637 | 2564 |
| 2638 /// Adds [node] to the worklist. | 2565 /// Adds [node] to the worklist. |
| 2639 void push(Node node) { | 2566 void push(Node node) { |
| 2640 nodeWorklist.add(node); | 2567 nodeWorklist.add(node); |
| 2641 } | 2568 } |
| 2642 | 2569 |
| 2643 /// If the passed node is not yet reachable, mark it reachable and add it | 2570 /// If the passed node is not yet reachable, mark it reachable and add it |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2677 assert(newValue.kind >= oldValue.kind); | 2604 assert(newValue.kind >= oldValue.kind); |
| 2678 | 2605 |
| 2679 values[node] = newValue.isConstant ? newValue.constant : null; | 2606 values[node] = newValue.isConstant ? newValue.constant : null; |
| 2680 defWorklist.add(node); | 2607 defWorklist.add(node); |
| 2681 } | 2608 } |
| 2682 | 2609 |
| 2683 /// Sets the type of the given primitive. | 2610 /// Sets the type of the given primitive. |
| 2684 /// | 2611 /// |
| 2685 /// If [updateValue] is a constant and [canReplace] is true, the primitive | 2612 /// If [updateValue] is a constant and [canReplace] is true, the primitive |
| 2686 /// is also marked as safe for elimination, so it can be constant-folded. | 2613 /// is also marked as safe for elimination, so it can be constant-folded. |
| 2687 void setResult(UnsafePrimitive prim, | 2614 void setResult(UnsafePrimitive prim, AbstractConstantValue updateValue, |
| 2688 AbstractConstantValue updateValue, | 2615 {bool canReplace: false}) { |
| 2689 {bool canReplace: false}) { | |
| 2690 // TODO(asgerf): Separate constant folding from side effect analysis. | 2616 // TODO(asgerf): Separate constant folding from side effect analysis. |
| 2691 setValue(prim, updateValue); | 2617 setValue(prim, updateValue); |
| 2692 prim.isSafeForElimination = canReplace && updateValue.isConstant; | 2618 prim.isSafeForElimination = canReplace && updateValue.isConstant; |
| 2693 } | 2619 } |
| 2694 | 2620 |
| 2695 bool isInterceptedSelector(Selector selector) { | 2621 bool isInterceptedSelector(Selector selector) { |
| 2696 return backend.isInterceptedSelector(selector); | 2622 return backend.isInterceptedSelector(selector); |
| 2697 } | 2623 } |
| 2698 | 2624 |
| 2699 // -------------------------- Visitor overrides ------------------------------ | 2625 // -------------------------- Visitor overrides ------------------------------ |
| 2700 void visit(Node node) { node.accept(this); } | 2626 void visit(Node node) { |
| 2627 node.accept(this); |
| 2628 } |
| 2701 | 2629 |
| 2702 void visitFunctionDefinition(FunctionDefinition node) { | 2630 void visitFunctionDefinition(FunctionDefinition node) { |
| 2703 if (node.interceptorParameter != null) { | 2631 if (node.interceptorParameter != null) { |
| 2704 setValue(node.interceptorParameter, nonConstant(typeSystem.nonNullType)); | 2632 setValue(node.interceptorParameter, nonConstant(typeSystem.nonNullType)); |
| 2705 } | 2633 } |
| 2706 // If the abstract value of the function parameters is Nothing, use the | 2634 // If the abstract value of the function parameters is Nothing, use the |
| 2707 // inferred parameter type. Otherwise (e.g., when inlining) do not | 2635 // inferred parameter type. Otherwise (e.g., when inlining) do not |
| 2708 // change the abstract value. | 2636 // change the abstract value. |
| 2709 if (node.receiverParameter != null && | 2637 if (node.receiverParameter != null && |
| 2710 getValue(node.receiverParameter).isNothing) { | 2638 getValue(node.receiverParameter).isNothing) { |
| 2711 setValue(node.receiverParameter, | 2639 setValue(node.receiverParameter, |
| 2712 nonConstant(typeSystem.getReceiverType(node.element))); | 2640 nonConstant(typeSystem.getReceiverType(node.element))); |
| 2713 } | 2641 } |
| 2714 bool hasParameterWithoutValue = false; | 2642 bool hasParameterWithoutValue = false; |
| 2715 for (Parameter param in node.parameters) { | 2643 for (Parameter param in node.parameters) { |
| 2716 if (getValue(param).isNothing) { | 2644 if (getValue(param).isNothing) { |
| 2717 TypeMask type = param.hint is ParameterElement | 2645 TypeMask type = param.hint is ParameterElement |
| 2718 ? typeSystem.getParameterType(param.hint) | 2646 ? typeSystem.getParameterType(param.hint) |
| 2719 : typeSystem.dynamicType; | 2647 : typeSystem.dynamicType; |
| 2720 setValue(param, lattice.fromMask(type)); | 2648 setValue(param, lattice.fromMask(type)); |
| 2721 if (type.isEmpty) hasParameterWithoutValue = true; | 2649 if (type.isEmpty) hasParameterWithoutValue = true; |
| 2722 } | 2650 } |
| 2723 } | 2651 } |
| 2724 if (!hasParameterWithoutValue) { // Don't analyze unreachable code. | 2652 if (!hasParameterWithoutValue) { |
| 2653 // Don't analyze unreachable code. |
| 2725 push(node.body); | 2654 push(node.body); |
| 2726 } | 2655 } |
| 2727 } | 2656 } |
| 2728 | 2657 |
| 2729 void visitLetPrim(LetPrim node) { | 2658 void visitLetPrim(LetPrim node) { |
| 2730 visit(node.primitive); // No reason to delay visits to primitives. | 2659 visit(node.primitive); // No reason to delay visits to primitives. |
| 2731 push(node.body); | 2660 push(node.body); |
| 2732 } | 2661 } |
| 2733 | 2662 |
| 2734 void visitLetCont(LetCont node) { | 2663 void visitLetCont(LetCont node) { |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2841 AbstractConstantValue right = getValue(node.argument(0)); | 2770 AbstractConstantValue right = getValue(node.argument(0)); |
| 2842 BinaryOperator operator = BinaryOperator.parse(opname); | 2771 BinaryOperator operator = BinaryOperator.parse(opname); |
| 2843 AbstractConstantValue result = | 2772 AbstractConstantValue result = |
| 2844 lattice.binaryOp(operator, receiver, right); | 2773 lattice.binaryOp(operator, receiver, right); |
| 2845 return finish(result, canReplace: !receiver.isNullable); | 2774 return finish(result, canReplace: !receiver.isNullable); |
| 2846 } | 2775 } |
| 2847 return finish(null); | 2776 return finish(null); |
| 2848 } | 2777 } |
| 2849 | 2778 |
| 2850 void visitApplyBuiltinOperator(ApplyBuiltinOperator node) { | 2779 void visitApplyBuiltinOperator(ApplyBuiltinOperator node) { |
| 2851 | |
| 2852 void unaryOp( | 2780 void unaryOp( |
| 2853 AbstractConstantValue operation(AbstractConstantValue argument), | 2781 AbstractConstantValue operation(AbstractConstantValue argument), |
| 2854 TypeMask defaultType) { | 2782 TypeMask defaultType) { |
| 2855 AbstractConstantValue value = getValue(node.argument(0)); | 2783 AbstractConstantValue value = getValue(node.argument(0)); |
| 2856 setValue(node, operation(value) ?? nonConstant(defaultType)); | 2784 setValue(node, operation(value) ?? nonConstant(defaultType)); |
| 2857 } | 2785 } |
| 2858 | 2786 |
| 2859 void binaryOp( | 2787 void binaryOp( |
| 2860 AbstractConstantValue operation(AbstractConstantValue left, | 2788 AbstractConstantValue operation( |
| 2861 AbstractConstantValue right), | 2789 AbstractConstantValue left, AbstractConstantValue right), |
| 2862 TypeMask defaultType) { | 2790 TypeMask defaultType) { |
| 2863 AbstractConstantValue left = getValue(node.argument(0)); | 2791 AbstractConstantValue left = getValue(node.argument(0)); |
| 2864 AbstractConstantValue right = getValue(node.argument(1)); | 2792 AbstractConstantValue right = getValue(node.argument(1)); |
| 2865 setValue(node, operation(left, right) ?? nonConstant(defaultType)); | 2793 setValue(node, operation(left, right) ?? nonConstant(defaultType)); |
| 2866 } | 2794 } |
| 2867 | 2795 |
| 2868 void binaryNumOp( | 2796 void binaryNumOp( |
| 2869 AbstractConstantValue operation(AbstractConstantValue left, | 2797 AbstractConstantValue operation( |
| 2870 AbstractConstantValue right)) { | 2798 AbstractConstantValue left, AbstractConstantValue right)) { |
| 2871 binaryOp(operation, typeSystem.numType); | 2799 binaryOp(operation, typeSystem.numType); |
| 2872 } | 2800 } |
| 2873 | 2801 |
| 2874 void binaryUint32Op( | 2802 void binaryUint32Op( |
| 2875 AbstractConstantValue operation(AbstractConstantValue left, | 2803 AbstractConstantValue operation( |
| 2876 AbstractConstantValue right)) { | 2804 AbstractConstantValue left, AbstractConstantValue right)) { |
| 2877 binaryOp(operation, typeSystem.uint32Type); | 2805 binaryOp(operation, typeSystem.uint32Type); |
| 2878 } | 2806 } |
| 2879 | 2807 |
| 2880 void binaryBoolOp( | 2808 void binaryBoolOp( |
| 2881 AbstractConstantValue operation(AbstractConstantValue left, | 2809 AbstractConstantValue operation( |
| 2882 AbstractConstantValue right)) { | 2810 AbstractConstantValue left, AbstractConstantValue right)) { |
| 2883 binaryOp(operation, typeSystem.boolType); | 2811 binaryOp(operation, typeSystem.boolType); |
| 2884 } | 2812 } |
| 2885 | 2813 |
| 2886 switch (node.operator) { | 2814 switch (node.operator) { |
| 2887 case BuiltinOperator.StringConcatenate: | 2815 case BuiltinOperator.StringConcatenate: |
| 2888 ast.DartString stringValue = const ast.LiteralDartString(''); | 2816 ast.DartString stringValue = const ast.LiteralDartString(''); |
| 2889 for (Reference<Primitive> arg in node.argumentRefs) { | 2817 for (Reference<Primitive> arg in node.argumentRefs) { |
| 2890 AbstractConstantValue value = getValue(arg.definition); | 2818 AbstractConstantValue value = getValue(arg.definition); |
| 2891 if (value.isNothing) { | 2819 if (value.isNothing) { |
| 2892 setValue(node, lattice.nothing); | 2820 setValue(node, lattice.nothing); |
| 2893 return; // And come back later | 2821 return; // And come back later |
| 2894 } else if (value.isConstant && | 2822 } else if (value.isConstant && |
| 2895 value.constant.isString && | 2823 value.constant.isString && |
| 2896 stringValue != null) { | 2824 stringValue != null) { |
| 2897 StringConstantValue constant = value.constant; | 2825 StringConstantValue constant = value.constant; |
| 2898 stringValue = | 2826 stringValue = |
| 2899 new ast.ConsDartString(stringValue, constant.primitiveValue); | 2827 new ast.ConsDartString(stringValue, constant.primitiveValue); |
| 2900 } else { | 2828 } else { |
| 2901 stringValue = null; | 2829 stringValue = null; |
| 2902 break; | 2830 break; |
| 2903 } | 2831 } |
| 2904 } | 2832 } |
| 2905 if (stringValue == null) { | 2833 if (stringValue == null) { |
| 2906 setValue(node, nonConstant(typeSystem.stringType)); | 2834 setValue(node, nonConstant(typeSystem.stringType)); |
| 2907 } else { | 2835 } else { |
| 2908 setValue(node, constantValue(new StringConstantValue(stringValue))); | 2836 setValue(node, constantValue(new StringConstantValue(stringValue))); |
| 2909 } | 2837 } |
| 2910 break; | 2838 break; |
| 2911 | 2839 |
| 2912 case BuiltinOperator.CharCodeAt: | 2840 case BuiltinOperator.CharCodeAt: |
| 2913 binaryOp(lattice.codeUnitAtSpecial, typeSystem.uint31Type); | 2841 binaryOp(lattice.codeUnitAtSpecial, typeSystem.uint31Type); |
| 2914 break; | 2842 break; |
| 2915 | 2843 |
| 2916 case BuiltinOperator.Identical: | 2844 case BuiltinOperator.Identical: |
| 2917 case BuiltinOperator.StrictEq: | 2845 case BuiltinOperator.StrictEq: |
| 2918 case BuiltinOperator.StrictNeq: | 2846 case BuiltinOperator.StrictNeq: |
| 2919 case BuiltinOperator.LooseEq: | 2847 case BuiltinOperator.LooseEq: |
| 2920 case BuiltinOperator.LooseNeq: | 2848 case BuiltinOperator.LooseNeq: |
| 2921 bool negated = | 2849 bool negated = node.operator == BuiltinOperator.StrictNeq || |
| 2922 node.operator == BuiltinOperator.StrictNeq || | |
| 2923 node.operator == BuiltinOperator.LooseNeq; | 2850 node.operator == BuiltinOperator.LooseNeq; |
| 2924 AbstractConstantValue left = getValue(node.argument(0)); | 2851 AbstractConstantValue left = getValue(node.argument(0)); |
| 2925 AbstractConstantValue right = getValue(node.argument(1)); | 2852 AbstractConstantValue right = getValue(node.argument(1)); |
| 2926 if (left.isNothing || right.isNothing) { | 2853 if (left.isNothing || right.isNothing) { |
| 2927 setValue(node, lattice.nothing); | 2854 setValue(node, lattice.nothing); |
| 2928 return; | 2855 return; |
| 2929 } | 2856 } |
| 2930 if (left.isConstant && right.isConstant) { | 2857 if (left.isConstant && right.isConstant) { |
| 2931 ConstantValue equal = lattice.constantSystem.identity.fold( | 2858 ConstantValue equal = lattice.constantSystem.identity |
| 2932 left.constant, right.constant); | 2859 .fold(left.constant, right.constant); |
| 2933 if (equal != null && equal.isBool) { | 2860 if (equal != null && equal.isBool) { |
| 2934 ConstantValue result = | 2861 ConstantValue result = |
| 2935 new BoolConstantValue(equal.isTrue == !negated); | 2862 new BoolConstantValue(equal.isTrue == !negated); |
| 2936 setValue(node, constantValue(result, typeSystem.boolType)); | 2863 setValue(node, constantValue(result, typeSystem.boolType)); |
| 2937 return; | 2864 return; |
| 2938 } | 2865 } |
| 2939 } | 2866 } |
| 2940 if (typeSystem.areDisjoint(left.type, right.type)) { | 2867 if (typeSystem.areDisjoint(left.type, right.type)) { |
| 2941 ConstantValue result = new BoolConstantValue(negated); | 2868 ConstantValue result = new BoolConstantValue(negated); |
| 2942 setValue(node, constantValue(result, typeSystem.boolType)); | 2869 setValue(node, constantValue(result, typeSystem.boolType)); |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3031 case BuiltinOperator.IsUnmodifiableJSArray: | 2958 case BuiltinOperator.IsUnmodifiableJSArray: |
| 3032 case BuiltinOperator.IsModifiableJSArray: | 2959 case BuiltinOperator.IsModifiableJSArray: |
| 3033 setValue(node, nonConstant(typeSystem.boolType)); | 2960 setValue(node, nonConstant(typeSystem.boolType)); |
| 3034 break; | 2961 break; |
| 3035 } | 2962 } |
| 3036 } | 2963 } |
| 3037 | 2964 |
| 3038 void visitApplyBuiltinMethod(ApplyBuiltinMethod node) { | 2965 void visitApplyBuiltinMethod(ApplyBuiltinMethod node) { |
| 3039 AbstractConstantValue receiver = getValue(node.receiver); | 2966 AbstractConstantValue receiver = getValue(node.receiver); |
| 3040 if (node.method == BuiltinMethod.Pop) { | 2967 if (node.method == BuiltinMethod.Pop) { |
| 3041 setValue(node, nonConstant( | 2968 setValue( |
| 3042 typeSystem.elementTypeOfIndexable(receiver.type))); | 2969 node, nonConstant(typeSystem.elementTypeOfIndexable(receiver.type))); |
| 3043 } else { | 2970 } else { |
| 3044 setValue(node, nonConstant()); | 2971 setValue(node, nonConstant()); |
| 3045 } | 2972 } |
| 3046 } | 2973 } |
| 3047 | 2974 |
| 3048 void visitInvokeMethodDirectly(InvokeMethodDirectly node) { | 2975 void visitInvokeMethodDirectly(InvokeMethodDirectly node) { |
| 3049 if (node.isConstructorBodyCall) { | 2976 if (node.isConstructorBodyCall) { |
| 3050 setResult(node, lattice.nullValue); | 2977 setResult(node, lattice.nullValue); |
| 3051 } else if (node.isTearOff) { | 2978 } else if (node.isTearOff) { |
| 3052 setResult(node, nonConstant(typeSystem.functionType)); | 2979 setResult(node, nonConstant(typeSystem.functionType)); |
| 3053 } else { | 2980 } else { |
| 3054 setResult(node, nonConstant(typeSystem.getReturnType(node.target))); | 2981 setResult(node, nonConstant(typeSystem.getReturnType(node.target))); |
| 3055 } | 2982 } |
| 3056 } | 2983 } |
| 3057 | 2984 |
| 3058 void visitInvokeConstructor(InvokeConstructor node) { | 2985 void visitInvokeConstructor(InvokeConstructor node) { |
| 3059 if (node.allocationSiteType != null) { | 2986 if (node.allocationSiteType != null) { |
| 3060 setResult(node, nonConstant(node.allocationSiteType)); | 2987 setResult(node, nonConstant(node.allocationSiteType)); |
| 3061 } else { | 2988 } else { |
| 3062 setResult(node, nonConstant(typeSystem.getReturnType(node.target))); | 2989 setResult(node, nonConstant(typeSystem.getReturnType(node.target))); |
| 3063 } | 2990 } |
| 3064 } | 2991 } |
| 3065 | 2992 |
| 3066 void visitThrow(Throw node) { | 2993 void visitThrow(Throw node) {} |
| 3067 } | |
| 3068 | 2994 |
| 3069 void visitRethrow(Rethrow node) { | 2995 void visitRethrow(Rethrow node) {} |
| 3070 } | |
| 3071 | 2996 |
| 3072 void visitUnreachable(Unreachable node) { | 2997 void visitUnreachable(Unreachable node) {} |
| 3073 } | |
| 3074 | 2998 |
| 3075 void visitBranch(Branch node) { | 2999 void visitBranch(Branch node) { |
| 3076 AbstractConstantValue conditionCell = getValue(node.condition); | 3000 AbstractConstantValue conditionCell = getValue(node.condition); |
| 3077 AbstractBool boolifiedValue = node.isStrictCheck | 3001 AbstractBool boolifiedValue = node.isStrictCheck |
| 3078 ? lattice.strictBoolify(conditionCell) | 3002 ? lattice.strictBoolify(conditionCell) |
| 3079 : lattice.boolify(conditionCell); | 3003 : lattice.boolify(conditionCell); |
| 3080 switch (boolifiedValue) { | 3004 switch (boolifiedValue) { |
| 3081 case AbstractBool.Nothing: | 3005 case AbstractBool.Nothing: |
| 3082 break; | 3006 break; |
| 3083 case AbstractBool.True: | 3007 case AbstractBool.True: |
| (...skipping 17 matching lines...) Expand all Loading... |
| 3101 // TODO(sra): We could see if we can find the value in the interceptor | 3025 // TODO(sra): We could see if we can find the value in the interceptor |
| 3102 // expression. It would probably have no benefit - we only see | 3026 // expression. It would probably have no benefit - we only see |
| 3103 // TypeTestViaFlag after rewriting TypeTest and the rewrite of TypeTest | 3027 // TypeTestViaFlag after rewriting TypeTest and the rewrite of TypeTest |
| 3104 // would already have done the interesting optimizations. | 3028 // would already have done the interesting optimizations. |
| 3105 setValue(node, nonConstant(typeSystem.boolType)); | 3029 setValue(node, nonConstant(typeSystem.boolType)); |
| 3106 } | 3030 } |
| 3107 | 3031 |
| 3108 void handleTypeTest( | 3032 void handleTypeTest( |
| 3109 Primitive node, AbstractConstantValue input, types.DartType dartType) { | 3033 Primitive node, AbstractConstantValue input, types.DartType dartType) { |
| 3110 TypeMask boolType = typeSystem.boolType; | 3034 TypeMask boolType = typeSystem.boolType; |
| 3111 switch(lattice.isSubtypeOf(input, dartType, allowNull: false)) { | 3035 switch (lattice.isSubtypeOf(input, dartType, allowNull: false)) { |
| 3112 case AbstractBool.Nothing: | 3036 case AbstractBool.Nothing: |
| 3113 break; // And come back later. | 3037 break; // And come back later. |
| 3114 | 3038 |
| 3115 case AbstractBool.True: | 3039 case AbstractBool.True: |
| 3116 setValue(node, constantValue(new TrueConstantValue(), boolType)); | 3040 setValue(node, constantValue(new TrueConstantValue(), boolType)); |
| 3117 break; | 3041 break; |
| 3118 | 3042 |
| 3119 case AbstractBool.False: | 3043 case AbstractBool.False: |
| 3120 setValue(node, constantValue(new FalseConstantValue(), boolType)); | 3044 setValue(node, constantValue(new FalseConstantValue(), boolType)); |
| 3121 break; | 3045 break; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 3137 setValue(node, input); | 3061 setValue(node, input); |
| 3138 break; | 3062 break; |
| 3139 | 3063 |
| 3140 case AbstractBool.False: | 3064 case AbstractBool.False: |
| 3141 setValue(node, lattice.nothing); // Cast fails. | 3065 setValue(node, lattice.nothing); // Cast fails. |
| 3142 break; | 3066 break; |
| 3143 | 3067 |
| 3144 case AbstractBool.Maybe: | 3068 case AbstractBool.Maybe: |
| 3145 // Narrow type of output to those that survive the cast. | 3069 // Narrow type of output to those that survive the cast. |
| 3146 TypeMask type = input.type.intersection( | 3070 TypeMask type = input.type.intersection( |
| 3147 typeSystem.subtypesOf(node.dartType).nullable(), | 3071 typeSystem.subtypesOf(node.dartType).nullable(), classWorld); |
| 3148 classWorld); | |
| 3149 setValue(node, nonConstant(type)); | 3072 setValue(node, nonConstant(type)); |
| 3150 break; | 3073 break; |
| 3151 } | 3074 } |
| 3152 } | 3075 } |
| 3153 | 3076 |
| 3154 void visitSetMutable(SetMutable node) { | 3077 void visitSetMutable(SetMutable node) { |
| 3155 setValue(node.variable, getValue(node.value)); | 3078 setValue(node.variable, getValue(node.value)); |
| 3156 } | 3079 } |
| 3157 | 3080 |
| 3158 void visitLiteralList(LiteralList node) { | 3081 void visitLiteralList(LiteralList node) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 3170 setValue(node, nonConstant(typeSystem.getTypeOf(value))); | 3093 setValue(node, nonConstant(typeSystem.getTypeOf(value))); |
| 3171 } else { | 3094 } else { |
| 3172 setValue(node, constantValue(value, typeSystem.getTypeOf(value))); | 3095 setValue(node, constantValue(value, typeSystem.getTypeOf(value))); |
| 3173 } | 3096 } |
| 3174 } | 3097 } |
| 3175 | 3098 |
| 3176 void visitGetMutable(GetMutable node) { | 3099 void visitGetMutable(GetMutable node) { |
| 3177 setValue(node, getValue(node.variable)); | 3100 setValue(node, getValue(node.variable)); |
| 3178 } | 3101 } |
| 3179 | 3102 |
| 3180 void visitMutableVariable(MutableVariable node) { | 3103 void visitMutableVariable(MutableVariable node) {} |
| 3181 } | |
| 3182 | 3104 |
| 3183 void visitParameter(Parameter node) { | 3105 void visitParameter(Parameter node) {} |
| 3184 } | |
| 3185 | 3106 |
| 3186 void visitContinuation(Continuation node) { | 3107 void visitContinuation(Continuation node) { |
| 3187 node.parameters.forEach(visit); | 3108 node.parameters.forEach(visit); |
| 3188 | 3109 |
| 3189 if (node.body != null) { | 3110 if (node.body != null) { |
| 3190 push(node.body); | 3111 push(node.body); |
| 3191 } | 3112 } |
| 3192 } | 3113 } |
| 3193 | 3114 |
| 3194 void visitGetStatic(GetStatic node) { | 3115 void visitGetStatic(GetStatic node) { |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3270 | 3191 |
| 3271 void visitCreateInvocationMirror(CreateInvocationMirror node) { | 3192 void visitCreateInvocationMirror(CreateInvocationMirror node) { |
| 3272 // TODO(asgerf): Expose [Invocation] type. | 3193 // TODO(asgerf): Expose [Invocation] type. |
| 3273 setValue(node, nonConstant(typeSystem.nonNullType)); | 3194 setValue(node, nonConstant(typeSystem.nonNullType)); |
| 3274 } | 3195 } |
| 3275 | 3196 |
| 3276 @override | 3197 @override |
| 3277 void visitForeignCode(ForeignCode node) { | 3198 void visitForeignCode(ForeignCode node) { |
| 3278 bool firstArgumentIsNullable = false; | 3199 bool firstArgumentIsNullable = false; |
| 3279 if (node.argumentRefs.length > 0) { | 3200 if (node.argumentRefs.length > 0) { |
| 3280 AbstractConstantValue first = getValue(node.argumentRefs.first.definition)
; | 3201 AbstractConstantValue first = |
| 3202 getValue(node.argumentRefs.first.definition); |
| 3281 if (first.isNothing) { | 3203 if (first.isNothing) { |
| 3282 setValue(node, nothing); | 3204 setValue(node, nothing); |
| 3283 return; | 3205 return; |
| 3284 } | 3206 } |
| 3285 firstArgumentIsNullable = first.isNullable; | 3207 firstArgumentIsNullable = first.isNullable; |
| 3286 } | 3208 } |
| 3287 setValue(node, nonConstant(node.storedType)); | 3209 setValue(node, nonConstant(node.storedType)); |
| 3288 node.isSafeForElimination = | 3210 node.isSafeForElimination = |
| 3289 !node.nativeBehavior.sideEffects.hasSideEffects() && | 3211 !node.nativeBehavior.sideEffects.hasSideEffects() && |
| 3290 (!node.nativeBehavior.throwBehavior.canThrow || | 3212 (!node.nativeBehavior.throwBehavior.canThrow || |
| 3291 (!firstArgumentIsNullable && | 3213 (!firstArgumentIsNullable && |
| 3292 node.nativeBehavior.throwBehavior.isOnlyNullNSMGuard)); | 3214 node.nativeBehavior.throwBehavior.isOnlyNullNSMGuard)); |
| 3293 } | 3215 } |
| 3294 | 3216 |
| 3295 @override | 3217 @override |
| 3296 void visitGetLength(GetLength node) { | 3218 void visitGetLength(GetLength node) { |
| 3297 AbstractConstantValue input = getValue(node.object); | 3219 AbstractConstantValue input = getValue(node.object); |
| 3298 node.objectIsNotNull = input.isDefinitelyNotNull; | 3220 node.objectIsNotNull = input.isDefinitelyNotNull; |
| 3299 AbstractConstantValue length = lattice.lengthSpecial(input); | 3221 AbstractConstantValue length = lattice.lengthSpecial(input); |
| 3300 if (length != null) { | 3222 if (length != null) { |
| 3301 // TODO(asgerf): Constant-folding the length might degrade the VM's | 3223 // TODO(asgerf): Constant-folding the length might degrade the VM's |
| 3302 // own bounds-check elimination? | 3224 // own bounds-check elimination? |
| 3303 setValue(node, length); | 3225 setValue(node, length); |
| 3304 } else { | 3226 } else { |
| 3305 setValue(node, nonConstant(typeSystem.uint32Type)); | 3227 setValue(node, nonConstant(typeSystem.uint32Type)); |
| 3306 } | 3228 } |
| 3307 } | 3229 } |
| 3308 | 3230 |
| 3309 @override | 3231 @override |
| 3310 void visitGetIndex(GetIndex node) { | 3232 void visitGetIndex(GetIndex node) { |
| 3311 AbstractConstantValue object = getValue(node.object); | 3233 AbstractConstantValue object = getValue(node.object); |
| 3312 if (object.isNothing || object.isNullConstant) { | 3234 if (object.isNothing || object.isNullConstant) { |
| 3313 setValue(node, nothing); | 3235 setValue(node, nothing); |
| 3314 } else { | 3236 } else { |
| 3315 node.objectIsNotNull = object.isDefinitelyNotNull; | 3237 node.objectIsNotNull = object.isDefinitelyNotNull; |
| 3316 setValue(node, nonConstant(typeSystem.elementTypeOfIndexable(object.type))
); | 3238 setValue( |
| 3239 node, nonConstant(typeSystem.elementTypeOfIndexable(object.type))); |
| 3317 } | 3240 } |
| 3318 } | 3241 } |
| 3319 | 3242 |
| 3320 @override | 3243 @override |
| 3321 void visitSetIndex(SetIndex node) {} | 3244 void visitSetIndex(SetIndex node) {} |
| 3322 | 3245 |
| 3323 @override | 3246 @override |
| 3324 void visitAwait(Await node) { | 3247 void visitAwait(Await node) { |
| 3325 setResult(node, nonConstant()); | 3248 setResult(node, nonConstant()); |
| 3326 } | 3249 } |
| 3327 | 3250 |
| 3328 @override | 3251 @override |
| 3329 visitYield(Yield node) { | 3252 visitYield(Yield node) { |
| 3330 setValue(node, nonConstant()); | 3253 setValue(node, nonConstant()); |
| 3331 } | 3254 } |
| 3332 | 3255 |
| 3333 @override | 3256 @override |
| 3334 void visitRefinement(Refinement node) { | 3257 void visitRefinement(Refinement node) { |
| 3335 setValue(node, lattice.intersectWithType( | 3258 setValue( |
| 3336 getValue(node.value.definition), | 3259 node, |
| 3337 node.refineType)); | 3260 lattice.intersectWithType( |
| 3261 getValue(node.value.definition), node.refineType)); |
| 3338 } | 3262 } |
| 3339 | 3263 |
| 3340 @override | 3264 @override |
| 3341 void visitBoundsCheck(BoundsCheck node) { | 3265 void visitBoundsCheck(BoundsCheck node) { |
| 3342 setValue(node, getValue(node.object)); | 3266 setValue(node, getValue(node.object)); |
| 3343 } | 3267 } |
| 3344 | 3268 |
| 3345 @override | 3269 @override |
| 3346 void visitReceiverCheck(ReceiverCheck node) { | 3270 void visitReceiverCheck(ReceiverCheck node) { |
| 3347 AbstractConstantValue value = getValue(node.value); | 3271 AbstractConstantValue value = getValue(node.value); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 3362 /// Represents the abstract value of a primitive value at some point in the | 3286 /// Represents the abstract value of a primitive value at some point in the |
| 3363 /// program. Abstract values of all kinds have a type [T]. | 3287 /// program. Abstract values of all kinds have a type [T]. |
| 3364 /// | 3288 /// |
| 3365 /// The different kinds of abstract values represents the knowledge about the | 3289 /// The different kinds of abstract values represents the knowledge about the |
| 3366 /// constness of the value: | 3290 /// constness of the value: |
| 3367 /// NOTHING: cannot have any value | 3291 /// NOTHING: cannot have any value |
| 3368 /// CONSTANT: is a constant. The value is stored in the [constant] field, | 3292 /// CONSTANT: is a constant. The value is stored in the [constant] field, |
| 3369 /// and the type of the constant is in the [type] field. | 3293 /// and the type of the constant is in the [type] field. |
| 3370 /// NONCONST: not a constant, but [type] may hold some information. | 3294 /// NONCONST: not a constant, but [type] may hold some information. |
| 3371 class AbstractConstantValue { | 3295 class AbstractConstantValue { |
| 3372 static const int NOTHING = 0; | 3296 static const int NOTHING = 0; |
| 3373 static const int CONSTANT = 1; | 3297 static const int CONSTANT = 1; |
| 3374 static const int NONCONST = 2; | 3298 static const int NONCONST = 2; |
| 3375 | 3299 |
| 3376 final int kind; | 3300 final int kind; |
| 3377 final ConstantValue constant; | 3301 final ConstantValue constant; |
| 3378 final TypeMask type; | 3302 final TypeMask type; |
| 3379 | 3303 |
| 3380 AbstractConstantValue._internal(this.kind, this.constant, this.type) { | 3304 AbstractConstantValue._internal(this.kind, this.constant, this.type) { |
| 3381 assert(kind != CONSTANT || constant != null); | 3305 assert(kind != CONSTANT || constant != null); |
| 3382 assert(constant is! SyntheticConstantValue); | 3306 assert(constant is! SyntheticConstantValue); |
| 3383 } | 3307 } |
| 3384 | 3308 |
| 3385 AbstractConstantValue.nothing() | 3309 AbstractConstantValue.nothing() |
| 3386 : this._internal(NOTHING, null, new TypeMask.nonNullEmpty()); | 3310 : this._internal(NOTHING, null, new TypeMask.nonNullEmpty()); |
| 3387 | 3311 |
| 3388 AbstractConstantValue.constantValue(ConstantValue constant, TypeMask type) | 3312 AbstractConstantValue.constantValue(ConstantValue constant, TypeMask type) |
| 3389 : this._internal(CONSTANT, constant, type); | 3313 : this._internal(CONSTANT, constant, type); |
| 3390 | 3314 |
| 3391 factory AbstractConstantValue.nonConstant(TypeMask type) { | 3315 factory AbstractConstantValue.nonConstant(TypeMask type) { |
| 3392 if (type.isEmpty) { | 3316 if (type.isEmpty) { |
| 3393 return new AbstractConstantValue.nothing(); | 3317 return new AbstractConstantValue.nothing(); |
| 3394 } else if (type.isNull) { | 3318 } else if (type.isNull) { |
| 3395 return new AbstractConstantValue.constantValue( | 3319 return new AbstractConstantValue.constantValue( |
| 3396 new NullConstantValue(), type); | 3320 new NullConstantValue(), type); |
| 3397 } else { | 3321 } else { |
| 3398 return new AbstractConstantValue._internal(NONCONST, null, type); | 3322 return new AbstractConstantValue._internal(NONCONST, null, type); |
| 3399 } | 3323 } |
| 3400 } | 3324 } |
| 3401 | 3325 |
| 3402 bool get isNothing => (kind == NOTHING); | 3326 bool get isNothing => (kind == NOTHING); |
| 3403 bool get isConstant => (kind == CONSTANT); | 3327 bool get isConstant => (kind == CONSTANT); |
| 3404 bool get isNonConst => (kind == NONCONST); | 3328 bool get isNonConst => (kind == NONCONST); |
| 3405 bool get isNullConstant => kind == CONSTANT && constant.isNull; | 3329 bool get isNullConstant => kind == CONSTANT && constant.isNull; |
| 3406 bool get isTrueConstant => kind == CONSTANT && constant.isTrue; | 3330 bool get isTrueConstant => kind == CONSTANT && constant.isTrue; |
| 3407 bool get isFalseConstant => kind == CONSTANT && constant.isFalse; | 3331 bool get isFalseConstant => kind == CONSTANT && constant.isFalse; |
| 3408 bool get isZeroConstant => kind == CONSTANT && constant.isZero; | 3332 bool get isZeroConstant => kind == CONSTANT && constant.isZero; |
| 3409 bool get isZeroOrNegativeConstant { | 3333 bool get isZeroOrNegativeConstant { |
| 3410 if (kind != CONSTANT || !constant.isNum) return false; | 3334 if (kind != CONSTANT || !constant.isNum) return false; |
| 3411 PrimitiveConstantValue value = constant; | 3335 PrimitiveConstantValue value = constant; |
| 3412 return value.primitiveValue <= 0; | 3336 return value.primitiveValue <= 0; |
| 3413 } | 3337 } |
| 3338 |
| 3414 bool get isNegativeConstant { | 3339 bool get isNegativeConstant { |
| 3415 if (kind != CONSTANT || !constant.isNum) return false; | 3340 if (kind != CONSTANT || !constant.isNum) return false; |
| 3416 PrimitiveConstantValue value = constant; | 3341 PrimitiveConstantValue value = constant; |
| 3417 return value.primitiveValue < 0; | 3342 return value.primitiveValue < 0; |
| 3418 } | 3343 } |
| 3419 | 3344 |
| 3420 bool get isNullable => kind != NOTHING && type.isNullable; | 3345 bool get isNullable => kind != NOTHING && type.isNullable; |
| 3421 bool get isDefinitelyNotNull => kind == NOTHING || !type.isNullable; | 3346 bool get isDefinitelyNotNull => kind == NOTHING || !type.isNullable; |
| 3422 | 3347 |
| 3423 int get hashCode { | 3348 int get hashCode { |
| 3424 int hash = kind * 31 + constant.hashCode * 59 + type.hashCode * 67; | 3349 int hash = kind * 31 + constant.hashCode * 59 + type.hashCode * 67; |
| 3425 return hash & 0x3fffffff; | 3350 return hash & 0x3fffffff; |
| 3426 } | 3351 } |
| 3427 | 3352 |
| 3428 bool operator ==(AbstractConstantValue that) { | 3353 bool operator ==(AbstractConstantValue that) { |
| 3429 return that.kind == this.kind && | 3354 return that.kind == this.kind && |
| 3430 that.constant == this.constant && | 3355 that.constant == this.constant && |
| 3431 that.type == this.type; | 3356 that.type == this.type; |
| 3432 } | 3357 } |
| 3433 | 3358 |
| 3434 String toString() { | 3359 String toString() { |
| 3435 switch (kind) { | 3360 switch (kind) { |
| 3436 case NOTHING: return "Nothing"; | 3361 case NOTHING: |
| 3437 case CONSTANT: return "Constant: ${constant.unparse()}: $type"; | 3362 return "Nothing"; |
| 3438 case NONCONST: return "Non-constant: $type"; | 3363 case CONSTANT: |
| 3439 default: assert(false); | 3364 return "Constant: ${constant.unparse()}: $type"; |
| 3365 case NONCONST: |
| 3366 return "Non-constant: $type"; |
| 3367 default: |
| 3368 assert(false); |
| 3440 } | 3369 } |
| 3441 return null; | 3370 return null; |
| 3442 } | 3371 } |
| 3443 } | 3372 } |
| 3444 | 3373 |
| 3445 /// Suggested name for a synthesized loop index. | 3374 /// Suggested name for a synthesized loop index. |
| 3446 class LoopIndexEntity extends Entity { | 3375 class LoopIndexEntity extends Entity { |
| 3447 String get name => 'i'; | 3376 String get name => 'i'; |
| 3448 } | 3377 } |
| 3449 | 3378 |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3566 } | 3495 } |
| 3567 | 3496 |
| 3568 Primitive makeCheck(CpsFragment cps, Primitive value) { | 3497 Primitive makeCheck(CpsFragment cps, Primitive value) { |
| 3569 return cps.applyBuiltin(negatedOperator, [value]); | 3498 return cps.applyBuiltin(negatedOperator, [value]); |
| 3570 } | 3499 } |
| 3571 | 3500 |
| 3572 Primitive makeRefinement(CpsFragment cps, Primitive value, World world) { | 3501 Primitive makeRefinement(CpsFragment cps, Primitive value, World world) { |
| 3573 return cps.refine(value, new TypeMask.nonNullSubclass(classElement, world)); | 3502 return cps.refine(value, new TypeMask.nonNullSubclass(classElement, world)); |
| 3574 } | 3503 } |
| 3575 } | 3504 } |
| OLD | NEW |