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 |
9 ClosureClassElement; | 9 ClosureClassElement; |
10 import '../common.dart'; | 10 import '../common.dart'; |
(...skipping 1142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1153 return compiler.world.allFunctions.filter(selector, receiverType); | 1153 return compiler.world.allFunctions.filter(selector, receiverType); |
1154 } | 1154 } |
1155 | 1155 |
1156 /************************* CALL EXPRESSIONS *************************/ | 1156 /************************* CALL EXPRESSIONS *************************/ |
1157 | 1157 |
1158 /// Replaces [node] with a more specialized instruction, if possible. | 1158 /// Replaces [node] with a more specialized instruction, if possible. |
1159 /// | 1159 /// |
1160 /// Returns `true` if the node was replaced. | 1160 /// Returns `true` if the node was replaced. |
1161 specializeOperatorCall(InvokeMethod node) { | 1161 specializeOperatorCall(InvokeMethod node) { |
1162 if (!backend.isInterceptedSelector(node.selector)) return null; | 1162 if (!backend.isInterceptedSelector(node.selector)) return null; |
1163 if (node.dartArgumentsLength > 1) return null; | 1163 if (node.argumentRefs.length > 1) return null; |
1164 if (node.callingConvention == CallingConvention.OneShotIntercepted) { | 1164 if (node.callingConvention == CallingConvention.OneShotIntercepted) { |
1165 return null; | 1165 return null; |
1166 } | 1166 } |
1167 | 1167 |
1168 bool trustPrimitives = compiler.trustPrimitives; | 1168 bool trustPrimitives = compiler.trustPrimitives; |
1169 | 1169 |
1170 /// Check that the receiver and argument satisfy the given type checks, and | 1170 /// Check that the receiver and argument satisfy the given type checks, and |
1171 /// throw a [NoSuchMethodError] or [ArgumentError] if the check fails. | 1171 /// throw a [NoSuchMethodError] or [ArgumentError] if the check fails. |
1172 CpsFragment makeGuard(TypeCheckOperator receiverGuard, | 1172 CpsFragment makeGuard(TypeCheckOperator receiverGuard, |
1173 [TypeCheckOperator argumentGuard]) { | 1173 [TypeCheckOperator argumentGuard]) { |
1174 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1174 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1175 | 1175 |
1176 // Make no guards if trusting primitives. | 1176 // Make no guards if trusting primitives. |
1177 if (trustPrimitives) return cps; | 1177 if (trustPrimitives) return cps; |
1178 | 1178 |
1179 // Determine which guards are needed. | 1179 // Determine which guards are needed. |
1180 ChecksNeeded receiverChecks = | 1180 ChecksNeeded receiverChecks = |
1181 receiverGuard.getChecksNeeded(node.dartReceiver, classWorld); | 1181 receiverGuard.getChecksNeeded(node.receiver, classWorld); |
1182 bool needReceiverGuard = receiverChecks != ChecksNeeded.None; | 1182 bool needReceiverGuard = receiverChecks != ChecksNeeded.None; |
1183 bool needArgumentGuard = | 1183 bool needArgumentGuard = |
1184 argumentGuard != null && | 1184 argumentGuard != null && |
1185 argumentGuard.needsCheck(node.dartArgument(0), classWorld); | 1185 argumentGuard.needsCheck(node.argument(0), classWorld); |
1186 | 1186 |
1187 if (!needReceiverGuard && !needArgumentGuard) return cps; | 1187 if (!needReceiverGuard && !needArgumentGuard) return cps; |
1188 | 1188 |
1189 // If we only need the receiver check, emit the specialized receiver | 1189 // If we only need the receiver check, emit the specialized receiver |
1190 // check instruction. Examples: | 1190 // check instruction. Examples: |
1191 // | 1191 // |
1192 // if (typeof receiver !== "number") return receiver.$lt; | 1192 // if (typeof receiver !== "number") return receiver.$lt; |
1193 // if (typeof receiver !== "number") return receiver.$lt(); | 1193 // if (typeof receiver !== "number") return receiver.$lt(); |
1194 // | 1194 // |
1195 if (!needArgumentGuard) { | 1195 if (!needArgumentGuard) { |
1196 Primitive condition = receiverGuard.makeCheck(cps, node.dartReceiver); | 1196 Primitive condition = receiverGuard.makeCheck(cps, node.receiver); |
1197 cps.letPrim(new ReceiverCheck( | 1197 cps.letPrim(new ReceiverCheck( |
1198 node.dartReceiver, | 1198 node.receiver, |
1199 node.selector, | 1199 node.selector, |
1200 node.sourceInformation, | 1200 node.sourceInformation, |
1201 condition: condition, | 1201 condition: condition, |
1202 useSelector: true, | 1202 useSelector: true, |
1203 isNullCheck: receiverChecks == ChecksNeeded.Null | 1203 isNullCheck: receiverChecks == ChecksNeeded.Null |
1204 )); | 1204 )); |
1205 return cps; | 1205 return cps; |
1206 } | 1206 } |
1207 | 1207 |
1208 // TODO(asgerf): We should consider specialized instructions for | 1208 // TODO(asgerf): We should consider specialized instructions for |
1209 // argument checks and receiver+argument checks, to avoid breaking up | 1209 // argument checks and receiver+argument checks, to avoid breaking up |
1210 // basic blocks. | 1210 // basic blocks. |
1211 | 1211 |
1212 // Emit as `H.iae(x)` if only the argument check may fail. For example: | 1212 // Emit as `H.iae(x)` if only the argument check may fail. For example: |
1213 // | 1213 // |
1214 // if (typeof argument !== "number") return H.iae(argument); | 1214 // if (typeof argument !== "number") return H.iae(argument); |
1215 // | 1215 // |
1216 if (!needReceiverGuard) { | 1216 if (!needReceiverGuard) { |
1217 cps.ifTruthy(argumentGuard.makeCheck(cps, node.dartArgument(0))) | 1217 cps.ifTruthy(argumentGuard.makeCheck(cps, node.argument(0))) |
1218 .invokeStaticThrower(helpers.throwIllegalArgumentException, | 1218 .invokeStaticThrower(helpers.throwIllegalArgumentException, |
1219 [node.dartArgument(0)]); | 1219 [node.argument(0)]); |
1220 return cps; | 1220 return cps; |
1221 } | 1221 } |
1222 | 1222 |
1223 // Both receiver and argument check is needed. Emit as a combined check | 1223 // Both receiver and argument check is needed. Emit as a combined check |
1224 // using a one-shot interceptor to produce the exact error message in | 1224 // using a one-shot interceptor to produce the exact error message in |
1225 // the error case. For example: | 1225 // the error case. For example: |
1226 // | 1226 // |
1227 // if (typeof receiver !== "number" || typeof argument !== "number") | 1227 // if (typeof receiver !== "number" || typeof argument !== "number") |
1228 // return J.$lt(receiver, argument); | 1228 // return J.$lt(receiver, argument); |
1229 // | 1229 // |
1230 Continuation fail = cps.letCont(); | 1230 Continuation fail = cps.letCont(); |
1231 cps.ifTruthy(receiverGuard.makeCheck(cps, node.dartReceiver)) | 1231 cps.ifTruthy(receiverGuard.makeCheck(cps, node.receiver)) |
1232 .invokeContinuation(fail); | 1232 .invokeContinuation(fail); |
1233 cps.ifTruthy(argumentGuard.makeCheck(cps, node.dartArgument(0))) | 1233 cps.ifTruthy(argumentGuard.makeCheck(cps, node.argument(0))) |
1234 .invokeContinuation(fail); | 1234 .invokeContinuation(fail); |
1235 | 1235 |
1236 cps.insideContinuation(fail) | 1236 cps.insideContinuation(fail) |
1237 ..invokeMethod(node.dartReceiver, node.selector, node.mask, | 1237 ..invokeMethod(node.receiver, node.selector, node.mask, |
1238 [node.dartArgument(0)], CallingConvention.OneShotIntercepted) | 1238 [node.argument(0)], |
| 1239 callingConvention: CallingConvention.OneShotIntercepted) |
1239 ..put(new Unreachable()); | 1240 ..put(new Unreachable()); |
1240 | 1241 |
1241 return cps; | 1242 return cps; |
1242 } | 1243 } |
1243 | 1244 |
1244 /// Replaces the call with [operator], using the receiver and first argument | 1245 /// Replaces the call with [operator], using the receiver and first argument |
1245 /// as operands (in that order). | 1246 /// as operands (in that order). |
1246 /// | 1247 /// |
1247 /// If [guard] is given, the receiver and argument are both checked using | 1248 /// If [guard] is given, the receiver and argument are both checked using |
1248 /// that operator. | 1249 /// that operator. |
1249 CpsFragment makeBinary(BuiltinOperator operator, | 1250 CpsFragment makeBinary(BuiltinOperator operator, |
1250 {TypeCheckOperator guard: TypeCheckOperator.none}) { | 1251 {TypeCheckOperator guard: TypeCheckOperator.none}) { |
1251 CpsFragment cps = makeGuard(guard, guard); | 1252 CpsFragment cps = makeGuard(guard, guard); |
1252 Primitive left = guard.makeRefinement(cps, node.dartReceiver, classWorld); | 1253 Primitive left = guard.makeRefinement(cps, node.receiver, classWorld); |
1253 Primitive right = | 1254 Primitive right = |
1254 guard.makeRefinement(cps, node.dartArgument(0), classWorld); | 1255 guard.makeRefinement(cps, node.argument(0), classWorld); |
1255 Primitive result = cps.applyBuiltin(operator, [left, right]); | 1256 Primitive result = cps.applyBuiltin(operator, [left, right]); |
1256 result.hint = node.hint; | 1257 result.hint = node.hint; |
1257 node.replaceUsesWith(result); | 1258 node.replaceUsesWith(result); |
1258 return cps; | 1259 return cps; |
1259 } | 1260 } |
1260 | 1261 |
1261 /// Like [makeBinary] but for unary operators with the receiver as the | 1262 /// Like [makeBinary] but for unary operators with the receiver as the |
1262 /// argument. | 1263 /// argument. |
1263 CpsFragment makeUnary(BuiltinOperator operator, | 1264 CpsFragment makeUnary(BuiltinOperator operator, |
1264 {TypeCheckOperator guard: TypeCheckOperator.none}) { | 1265 {TypeCheckOperator guard: TypeCheckOperator.none}) { |
1265 CpsFragment cps = makeGuard(guard); | 1266 CpsFragment cps = makeGuard(guard); |
1266 Primitive argument = | 1267 Primitive argument = |
1267 guard.makeRefinement(cps, node.dartReceiver, classWorld); | 1268 guard.makeRefinement(cps, node.receiver, classWorld); |
1268 Primitive result = cps.applyBuiltin(operator, [argument]); | 1269 Primitive result = cps.applyBuiltin(operator, [argument]); |
1269 result.hint = node.hint; | 1270 result.hint = node.hint; |
1270 node.replaceUsesWith(result); | 1271 node.replaceUsesWith(result); |
1271 return cps; | 1272 return cps; |
1272 } | 1273 } |
1273 | 1274 |
1274 Selector renameToOptimizedSelector(String name) { | 1275 Selector renameToOptimizedSelector(String name) { |
1275 return new Selector.call( | 1276 return new Selector.call( |
1276 new Name(name, backend.helpers.interceptorsLibrary), | 1277 new Name(name, backend.helpers.interceptorsLibrary), |
1277 node.selector.callStructure); | 1278 node.selector.callStructure); |
1278 } | 1279 } |
1279 | 1280 |
1280 /// Replaces the call with a call to [name] with the same inputs. | 1281 /// Replaces the call with a call to [name] with the same inputs. |
1281 InvokeMethod makeRenamedInvoke(String name) { | 1282 InvokeMethod makeRenamedInvoke(String name) { |
1282 return new InvokeMethod(node.receiver, | 1283 return new InvokeMethod(node.receiver, |
1283 renameToOptimizedSelector(name), | 1284 renameToOptimizedSelector(name), |
1284 node.mask, | 1285 node.mask, |
1285 node.arguments.toList(), | 1286 node.arguments.toList(), |
1286 sourceInformation: node.sourceInformation, | 1287 sourceInformation: node.sourceInformation, |
1287 callingConvention: node.callingConvention); | 1288 callingConvention: node.callingConvention, |
| 1289 interceptor: node.interceptor); |
1288 } | 1290 } |
1289 | 1291 |
1290 TypeMask successType = | 1292 TypeMask successType = |
1291 typeSystem.receiverTypeFor(node.selector, node.dartReceiver.type); | 1293 typeSystem.receiverTypeFor(node.selector, node.receiver.type); |
1292 | 1294 |
1293 if (node.selector.isOperator && node.dartArgumentsLength == 1) { | 1295 if (node.selector.isOperator && node.argumentRefs.length == 1) { |
1294 Primitive leftArg = node.dartReceiver; | 1296 Primitive leftArg = node.receiver; |
1295 Primitive rightArg = node.dartArgument(0); | 1297 Primitive rightArg = node.argument(0); |
1296 AbstractConstantValue left = getValue(leftArg); | 1298 AbstractConstantValue left = getValue(leftArg); |
1297 AbstractConstantValue right = getValue(rightArg); | 1299 AbstractConstantValue right = getValue(rightArg); |
1298 | 1300 |
1299 String opname = node.selector.name; | 1301 String opname = node.selector.name; |
1300 if (opname == '==') { | 1302 if (opname == '==') { |
1301 // Equality is special due to its treatment of null values and the | 1303 // Equality is special due to its treatment of null values and the |
1302 // fact that Dart-null corresponds to both JS-null and JS-undefined. | 1304 // fact that Dart-null corresponds to both JS-null and JS-undefined. |
1303 // Please see documentation for IsFalsy, StrictEq, and LooseEq. | 1305 // Please see documentation for IsFalsy, StrictEq, and LooseEq. |
1304 if (left.isNullConstant || right.isNullConstant) { | 1306 if (left.isNullConstant || right.isNullConstant) { |
1305 return makeBinary(BuiltinOperator.Identical); | 1307 return makeBinary(BuiltinOperator.Identical); |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1376 } | 1378 } |
1377 } | 1379 } |
1378 if (lattice.isDefinitelyString(left, allowNull: trustPrimitives) && | 1380 if (lattice.isDefinitelyString(left, allowNull: trustPrimitives) && |
1379 lattice.isDefinitelyString(right, allowNull: trustPrimitives) && | 1381 lattice.isDefinitelyString(right, allowNull: trustPrimitives) && |
1380 opname == '+') { | 1382 opname == '+') { |
1381 // TODO(asgerf): Add IsString builtin so we can use a guard here. | 1383 // TODO(asgerf): Add IsString builtin so we can use a guard here. |
1382 return makeBinary(BuiltinOperator.StringConcatenate); | 1384 return makeBinary(BuiltinOperator.StringConcatenate); |
1383 } | 1385 } |
1384 } | 1386 } |
1385 } | 1387 } |
1386 if (node.selector.isOperator && node.dartArgumentsLength == 0) { | 1388 if (node.selector.isOperator && node.argumentRefs.length == 0) { |
1387 if (typeSystem.isDefinitelyNum(successType)) { | 1389 if (typeSystem.isDefinitelyNum(successType)) { |
1388 String opname = node.selector.name; | 1390 String opname = node.selector.name; |
1389 if (opname == '~') { | 1391 if (opname == '~') { |
1390 return makeUnary(BuiltinOperator.NumBitNot, guard: checkIsNumber); | 1392 return makeUnary(BuiltinOperator.NumBitNot, guard: checkIsNumber); |
1391 } | 1393 } |
1392 if (opname == 'unary-') { | 1394 if (opname == 'unary-') { |
1393 return makeUnary(BuiltinOperator.NumNegate, guard: checkIsNumber); | 1395 return makeUnary(BuiltinOperator.NumNegate, guard: checkIsNumber); |
1394 } | 1396 } |
1395 } | 1397 } |
1396 } | 1398 } |
1397 if (node.selector.isCall) { | 1399 if (node.selector.isCall) { |
1398 String name = node.selector.name; | 1400 String name = node.selector.name; |
1399 Primitive receiver = node.dartReceiver; | 1401 Primitive receiver = node.receiver; |
1400 AbstractConstantValue receiverValue = getValue(receiver); | 1402 AbstractConstantValue receiverValue = getValue(receiver); |
1401 if (name == 'remainder') { | 1403 if (name == 'remainder') { |
1402 if (node.dartArgumentsLength == 1) { | 1404 if (node.argumentRefs.length == 1) { |
1403 Primitive arg = node.dartArgument(0); | 1405 Primitive arg = node.argument(0); |
1404 AbstractConstantValue argValue = getValue(arg); | 1406 AbstractConstantValue argValue = getValue(arg); |
1405 if (lattice.isDefinitelyInt(receiverValue, allowNull: true) && | 1407 if (lattice.isDefinitelyInt(receiverValue, allowNull: true) && |
1406 lattice.isDefinitelyInt(argValue) && | 1408 lattice.isDefinitelyInt(argValue) && |
1407 isIntNotZero(argValue)) { | 1409 isIntNotZero(argValue)) { |
1408 return makeBinary(BuiltinOperator.NumRemainder, | 1410 return makeBinary(BuiltinOperator.NumRemainder, |
1409 guard: checkIsNumber); | 1411 guard: checkIsNumber); |
1410 } | 1412 } |
1411 } | 1413 } |
1412 } else if (name == 'codeUnitAt') { | 1414 } else if (name == 'codeUnitAt') { |
1413 if (node.dartArgumentsLength == 1) { | 1415 if (node.argumentRefs.length == 1) { |
1414 Primitive index = node.dartArgument(0); | 1416 Primitive index = node.argument(0); |
1415 if (lattice.isDefinitelyString(receiverValue) && | 1417 if (lattice.isDefinitelyString(receiverValue) && |
1416 lattice.isDefinitelyInt(getValue(index))) { | 1418 lattice.isDefinitelyInt(getValue(index))) { |
1417 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1419 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1418 receiver = makeBoundsCheck(cps, receiver, index); | 1420 receiver = makeBoundsCheck(cps, receiver, index); |
1419 ApplyBuiltinOperator get = | 1421 ApplyBuiltinOperator get = |
1420 cps.applyBuiltin(BuiltinOperator.CharCodeAt, | 1422 cps.applyBuiltin(BuiltinOperator.CharCodeAt, |
1421 <Primitive>[receiver, index]); | 1423 <Primitive>[receiver, index]); |
1422 node.replaceUsesWith(get); | 1424 node.replaceUsesWith(get); |
1423 get.hint = node.hint; | 1425 get.hint = node.hint; |
1424 return cps; | 1426 return cps; |
(...skipping 13 matching lines...) Expand all Loading... |
1438 bool isInterceptedSelector(Selector selector) { | 1440 bool isInterceptedSelector(Selector selector) { |
1439 return backend.isInterceptedSelector(selector); | 1441 return backend.isInterceptedSelector(selector); |
1440 } | 1442 } |
1441 | 1443 |
1442 /// If [node] is a getter or setter invocation, tries to replace the | 1444 /// If [node] is a getter or setter invocation, tries to replace the |
1443 /// invocation with a direct access to a field. | 1445 /// invocation with a direct access to a field. |
1444 /// | 1446 /// |
1445 /// Returns `true` if the node was replaced. | 1447 /// Returns `true` if the node was replaced. |
1446 Primitive specializeFieldAccess(InvokeMethod node) { | 1448 Primitive specializeFieldAccess(InvokeMethod node) { |
1447 if (!node.selector.isGetter && !node.selector.isSetter) return null; | 1449 if (!node.selector.isGetter && !node.selector.isSetter) return null; |
1448 AbstractConstantValue receiver = getValue(node.dartReceiver); | 1450 AbstractConstantValue receiver = getValue(node.receiver); |
1449 Element target = | 1451 Element target = |
1450 typeSystem.locateSingleElement(receiver.type, node.selector); | 1452 typeSystem.locateSingleElement(receiver.type, node.selector); |
1451 if (target is! FieldElement) return null; | 1453 if (target is! FieldElement) return null; |
1452 // TODO(asgerf): Inlining native fields will make some tests pass for the | 1454 // TODO(asgerf): Inlining native fields will make some tests pass for the |
1453 // wrong reason, so for testing reasons avoid inlining them. | 1455 // wrong reason, so for testing reasons avoid inlining them. |
1454 if (backend.isNative(target) || backend.isJsInterop(target)) { | 1456 if (backend.isNative(target) || backend.isJsInterop(target)) { |
1455 return null; | 1457 return null; |
1456 } | 1458 } |
1457 if (node.selector.isGetter) { | 1459 if (node.selector.isGetter) { |
1458 return new GetField(node.dartReceiver, target); | 1460 return new GetField(node.receiver, target); |
1459 } else { | 1461 } else { |
1460 if (target.isFinal) return null; | 1462 if (target.isFinal) return null; |
1461 assert(node.hasNoUses); | 1463 assert(node.hasNoUses); |
1462 return new SetField(node.dartReceiver, | 1464 return new SetField(node.receiver, |
1463 target, | 1465 target, |
1464 node.dartArgument(0)); | 1466 node.argument(0)); |
1465 } | 1467 } |
1466 } | 1468 } |
1467 | 1469 |
1468 /// Create a check that throws if [index] is not a valid index on [list]. | 1470 /// Create a check that throws if [index] is not a valid index on [list]. |
1469 /// | 1471 /// |
1470 /// This function assumes that [index] is an integer. | 1472 /// This function assumes that [index] is an integer. |
1471 /// | 1473 /// |
1472 /// Returns a CPS fragment whose context is the branch where no error | 1474 /// Returns a CPS fragment whose context is the branch where no error |
1473 /// was thrown. | 1475 /// was thrown. |
1474 Primitive makeBoundsCheck(CpsFragment cps, | 1476 Primitive makeBoundsCheck(CpsFragment cps, |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1508 cps.ifTruthy(lengthChanged).invokeStaticThrower( | 1510 cps.ifTruthy(lengthChanged).invokeStaticThrower( |
1509 helpers.throwConcurrentModificationError, | 1511 helpers.throwConcurrentModificationError, |
1510 <Primitive>[list]); | 1512 <Primitive>[list]); |
1511 return cps; | 1513 return cps; |
1512 } | 1514 } |
1513 | 1515 |
1514 /// Tries to replace [node] with a direct `length` or index access. | 1516 /// Tries to replace [node] with a direct `length` or index access. |
1515 /// | 1517 /// |
1516 /// Returns `true` if the node was replaced. | 1518 /// Returns `true` if the node was replaced. |
1517 specializeIndexableAccess(InvokeMethod node) { | 1519 specializeIndexableAccess(InvokeMethod node) { |
1518 Primitive receiver = node.dartReceiver; | 1520 Primitive receiver = node.receiver; |
1519 AbstractConstantValue receiverValue = getValue(receiver); | 1521 AbstractConstantValue receiverValue = getValue(receiver); |
1520 if (!typeSystem.isDefinitelyIndexable(receiverValue.type, | 1522 if (!typeSystem.isDefinitelyIndexable(receiverValue.type, |
1521 allowNull: true)) { | 1523 allowNull: true)) { |
1522 return null; | 1524 return null; |
1523 } | 1525 } |
1524 switch (node.selector.name) { | 1526 switch (node.selector.name) { |
1525 case 'length': | 1527 case 'length': |
1526 if (node.selector.isGetter) { | 1528 if (node.selector.isGetter) { |
1527 return new GetLength(receiver); | 1529 return new GetLength(receiver); |
1528 } | 1530 } |
1529 if (node.selector.isSetter) { | 1531 if (node.selector.isSetter) { |
1530 if (!typeSystem.isDefinitelyExtendableArray(receiver.type, | 1532 if (!typeSystem.isDefinitelyExtendableArray(receiver.type, |
1531 allowNull: true)) { | 1533 allowNull: true)) { |
1532 return null; | 1534 return null; |
1533 } | 1535 } |
1534 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1536 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1535 Primitive newLength = node.dartArgument(0); | 1537 Primitive newLength = node.argument(0); |
1536 if (!typeSystem.isDefinitelyUint(newLength.type)) { | 1538 if (!typeSystem.isDefinitelyUint(newLength.type)) { |
1537 // TODO(asgerf): We could let the SetLength instruction throw for | 1539 // TODO(asgerf): We could let the SetLength instruction throw for |
1538 // negative right-hand sides (see length setter in js_array.dart). | 1540 // negative right-hand sides (see length setter in js_array.dart). |
1539 if (compiler.trustPrimitives) { | 1541 if (compiler.trustPrimitives) { |
1540 newLength = cps.refine(newLength, typeSystem.uint32Type); | 1542 newLength = cps.refine(newLength, typeSystem.uint32Type); |
1541 newLength.type = typeSystem.uint32Type; | 1543 newLength.type = typeSystem.uint32Type; |
1542 } else { | 1544 } else { |
1543 return null; | 1545 return null; |
1544 } | 1546 } |
1545 } | 1547 } |
1546 cps.letPrim(new ApplyBuiltinMethod( | 1548 cps.letPrim(new ApplyBuiltinMethod( |
1547 BuiltinMethod.SetLength, | 1549 BuiltinMethod.SetLength, |
1548 receiver, | 1550 receiver, |
1549 [newLength], | 1551 [newLength], |
1550 node.sourceInformation)); | 1552 node.sourceInformation)); |
1551 if (!typeSystem.isDefinitelyUint32(newLength.type)) { | 1553 if (!typeSystem.isDefinitelyUint32(newLength.type)) { |
1552 // If the setter succeeded, the length must have been a uint32. | 1554 // If the setter succeeded, the length must have been a uint32. |
1553 cps.refine(newLength, typeSystem.uint32Type); | 1555 cps.refine(newLength, typeSystem.uint32Type); |
1554 } | 1556 } |
1555 return cps; | 1557 return cps; |
1556 } | 1558 } |
1557 return null; | 1559 return null; |
1558 | 1560 |
1559 case '[]': | 1561 case '[]': |
1560 Primitive index = node.dartArgument(0); | 1562 Primitive index = node.argument(0); |
1561 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1563 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1562 receiver = makeBoundsCheck(cps, receiver, index); | 1564 receiver = makeBoundsCheck(cps, receiver, index); |
1563 GetIndex get = cps.letPrim(new GetIndex(receiver, index)); | 1565 GetIndex get = cps.letPrim(new GetIndex(receiver, index)); |
1564 node.replaceUsesWith(get); | 1566 node.replaceUsesWith(get); |
1565 // TODO(asgerf): Make replaceUsesWith set the hint? | 1567 // TODO(asgerf): Make replaceUsesWith set the hint? |
1566 get.hint = node.hint; | 1568 get.hint = node.hint; |
1567 return cps; | 1569 return cps; |
1568 | 1570 |
1569 case '[]=': | 1571 case '[]=': |
1570 if (!typeSystem.isDefinitelyMutableIndexable(receiverValue.type, | 1572 if (!typeSystem.isDefinitelyMutableIndexable(receiverValue.type, |
1571 allowNull: true)) { | 1573 allowNull: true)) { |
1572 return null; | 1574 return null; |
1573 } | 1575 } |
1574 Primitive index = node.dartArgument(0); | 1576 Primitive index = node.argument(0); |
1575 Primitive value = node.dartArgument(1); | 1577 Primitive value = node.argument(1); |
1576 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1578 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1577 receiver = makeBoundsCheck(cps, receiver, index); | 1579 receiver = makeBoundsCheck(cps, receiver, index); |
1578 cps.letPrim(new SetIndex(receiver, index, value)); | 1580 cps.letPrim(new SetIndex(receiver, index, value)); |
1579 assert(node.hasNoUses); | 1581 assert(node.hasNoUses); |
1580 return cps; | 1582 return cps; |
1581 | 1583 |
1582 case 'isEmpty': | 1584 case 'isEmpty': |
1583 if (!node.selector.isGetter) return null; | 1585 if (!node.selector.isGetter) return null; |
1584 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1586 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1585 Primitive length = cps.letPrim(new GetLength(receiver)); | 1587 Primitive length = cps.letPrim(new GetLength(receiver)); |
(...skipping 17 matching lines...) Expand all Loading... |
1603 | 1605 |
1604 default: | 1606 default: |
1605 return null; | 1607 return null; |
1606 } | 1608 } |
1607 } | 1609 } |
1608 | 1610 |
1609 /// Tries to replace [node] with one or more direct array access operations. | 1611 /// Tries to replace [node] with one or more direct array access operations. |
1610 /// | 1612 /// |
1611 /// Returns `true` if the node was replaced. | 1613 /// Returns `true` if the node was replaced. |
1612 CpsFragment specializeArrayAccess(InvokeMethod node) { | 1614 CpsFragment specializeArrayAccess(InvokeMethod node) { |
1613 Primitive list = node.dartReceiver; | 1615 Primitive list = node.receiver; |
1614 AbstractConstantValue listValue = getValue(list); | 1616 AbstractConstantValue listValue = getValue(list); |
1615 // Ensure that the object is a native list or null. | 1617 // Ensure that the object is a native list or null. |
1616 if (!lattice.isDefinitelyArray(listValue, allowNull: true)) { | 1618 if (!lattice.isDefinitelyArray(listValue, allowNull: true)) { |
1617 return null; | 1619 return null; |
1618 } | 1620 } |
1619 bool isFixedLength = | 1621 bool isFixedLength = |
1620 lattice.isDefinitelyFixedArray(listValue, allowNull: true); | 1622 lattice.isDefinitelyFixedArray(listValue, allowNull: true); |
1621 bool isExtendable = | 1623 bool isExtendable = |
1622 lattice.isDefinitelyExtendableArray(listValue, allowNull: true); | 1624 lattice.isDefinitelyExtendableArray(listValue, allowNull: true); |
1623 SourceInformation sourceInfo = node.sourceInformation; | 1625 SourceInformation sourceInfo = node.sourceInformation; |
1624 switch (node.selector.name) { | 1626 switch (node.selector.name) { |
1625 case 'add': | 1627 case 'add': |
1626 if (!node.selector.isCall || | 1628 if (!node.selector.isCall || |
1627 node.selector.positionalArgumentCount != 1 || | 1629 node.selector.positionalArgumentCount != 1 || |
1628 node.selector.namedArgumentCount != 0) { | 1630 node.selector.namedArgumentCount != 0) { |
1629 return null; | 1631 return null; |
1630 } | 1632 } |
1631 if (!isExtendable) return null; | 1633 if (!isExtendable) return null; |
1632 Primitive addedItem = node.dartArgument(0); | 1634 Primitive addedItem = node.argument(0); |
1633 CpsFragment cps = new CpsFragment(sourceInfo); | 1635 CpsFragment cps = new CpsFragment(sourceInfo); |
1634 cps.invokeBuiltin(BuiltinMethod.Push, | 1636 cps.invokeBuiltin(BuiltinMethod.Push, |
1635 list, | 1637 list, |
1636 <Primitive>[addedItem]); | 1638 <Primitive>[addedItem]); |
1637 if (node.hasAtLeastOneUse) { | 1639 if (node.hasAtLeastOneUse) { |
1638 node.replaceUsesWith(cps.makeNull()); | 1640 node.replaceUsesWith(cps.makeNull()); |
1639 } | 1641 } |
1640 return cps; | 1642 return cps; |
1641 | 1643 |
1642 case 'removeLast': | 1644 case 'removeLast': |
(...skipping 11 matching lines...) Expand all Loading... |
1654 removedItem.hint = node.hint; | 1656 removedItem.hint = node.hint; |
1655 node.replaceUsesWith(removedItem); | 1657 node.replaceUsesWith(removedItem); |
1656 return cps; | 1658 return cps; |
1657 | 1659 |
1658 case 'addAll': | 1660 case 'addAll': |
1659 if (!node.selector.isCall || | 1661 if (!node.selector.isCall || |
1660 node.selector.argumentCount != 1) { | 1662 node.selector.argumentCount != 1) { |
1661 return null; | 1663 return null; |
1662 } | 1664 } |
1663 if (!isExtendable) return null; | 1665 if (!isExtendable) return null; |
1664 Primitive addedList = node.dartArgument(0); | 1666 Primitive addedList = node.argument(0); |
1665 // Rewrite addAll([x1, ..., xN]) to push(x1), ..., push(xN). | 1667 // Rewrite addAll([x1, ..., xN]) to push(x1), ..., push(xN). |
1666 // Ensure that the list is not mutated between creation and use. | 1668 // Ensure that the list is not mutated between creation and use. |
1667 // We aim for the common case where this is the only use of the list, | 1669 // We aim for the common case where this is the only use of the list, |
1668 // which also guarantees that this list is not mutated before use. | 1670 // which also guarantees that this list is not mutated before use. |
1669 if (addedList is! LiteralList || !addedList.hasExactlyOneUse) { | 1671 if (addedList is! LiteralList || !addedList.hasExactlyOneUse) { |
1670 return null; | 1672 return null; |
1671 } | 1673 } |
1672 LiteralList addedLiteral = addedList; | 1674 LiteralList addedLiteral = addedList; |
1673 CpsFragment cps = new CpsFragment(sourceInfo); | 1675 CpsFragment cps = new CpsFragment(sourceInfo); |
1674 for (Reference value in addedLiteral.valueRefs) { | 1676 for (Reference value in addedLiteral.valueRefs) { |
1675 cps.invokeBuiltin(BuiltinMethod.Push, | 1677 cps.invokeBuiltin(BuiltinMethod.Push, |
1676 list, | 1678 list, |
1677 <Primitive>[value.definition]); | 1679 <Primitive>[value.definition]); |
1678 } | 1680 } |
1679 if (node.hasAtLeastOneUse) { | 1681 if (node.hasAtLeastOneUse) { |
1680 node.replaceUsesWith(cps.makeNull()); | 1682 node.replaceUsesWith(cps.makeNull()); |
1681 } | 1683 } |
1682 return cps; | 1684 return cps; |
1683 | 1685 |
1684 case 'elementAt': | 1686 case 'elementAt': |
1685 if (!node.selector.isCall || | 1687 if (!node.selector.isCall || |
1686 node.selector.positionalArgumentCount != 1 || | 1688 node.selector.positionalArgumentCount != 1 || |
1687 node.selector.namedArgumentCount != 0) { | 1689 node.selector.namedArgumentCount != 0) { |
1688 return null; | 1690 return null; |
1689 } | 1691 } |
1690 if (listValue.isNullable) return null; | 1692 if (listValue.isNullable) return null; |
1691 Primitive index = node.dartArgument(0); | 1693 Primitive index = node.argument(0); |
1692 if (!lattice.isDefinitelyInt(getValue(index))) return null; | 1694 if (!lattice.isDefinitelyInt(getValue(index))) return null; |
1693 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1695 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1694 list = makeBoundsCheck(cps, list, index); | 1696 list = makeBoundsCheck(cps, list, index); |
1695 GetIndex get = cps.letPrim(new GetIndex(list, index)); | 1697 GetIndex get = cps.letPrim(new GetIndex(list, index)); |
1696 get.hint = node.hint; | 1698 get.hint = node.hint; |
1697 node.replaceUsesWith(get); | 1699 node.replaceUsesWith(get); |
1698 return cps; | 1700 return cps; |
1699 | 1701 |
1700 case 'forEach': | 1702 case 'forEach': |
1701 Element element = | 1703 Element element = |
1702 compiler.world.locateSingleElement(node.selector, listValue.type); | 1704 compiler.world.locateSingleElement(node.selector, listValue.type); |
1703 if (element == null || | 1705 if (element == null || |
1704 !element.isFunction || | 1706 !element.isFunction || |
1705 !node.selector.isCall) return null; | 1707 !node.selector.isCall) return null; |
1706 assert(node.selector.positionalArgumentCount == 1); | 1708 assert(node.selector.positionalArgumentCount == 1); |
1707 assert(node.selector.namedArgumentCount == 0); | 1709 assert(node.selector.namedArgumentCount == 0); |
1708 FunctionDefinition target = functionCompiler.compileToCpsIr(element); | 1710 FunctionDefinition target = functionCompiler.compileToCpsIr(element); |
1709 | 1711 |
1710 CpsFragment cps = new CpsFragment(node.sourceInformation); | 1712 CpsFragment cps = new CpsFragment(node.sourceInformation); |
1711 Primitive result = cps.inlineFunction(target, | 1713 Primitive result = cps.inlineFunction(target, |
1712 node.receiver, | 1714 node.receiver, |
1713 node.arguments.toList(), | 1715 node.arguments.toList(), |
| 1716 interceptor: node.interceptor, |
1714 hint: node.hint); | 1717 hint: node.hint); |
1715 node.replaceUsesWith(result); | 1718 node.replaceUsesWith(result); |
1716 return cps; | 1719 return cps; |
1717 | 1720 |
1718 case 'iterator': | 1721 case 'iterator': |
1719 // TODO(asgerf): This should be done differently. | 1722 // TODO(asgerf): This should be done differently. |
1720 // The types are recomputed in a very error-prone manner. | 1723 // The types are recomputed in a very error-prone manner. |
1721 if (!node.selector.isGetter) return null; | 1724 if (!node.selector.isGetter) return null; |
1722 Primitive iterator = node; | 1725 Primitive iterator = node; |
1723 LetPrim iteratorBinding = node.parent; | 1726 LetPrim iteratorBinding = node.parent; |
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1891 /// => | 1894 /// => |
1892 /// obj.foo$<n>(<args>) | 1895 /// obj.foo$<n>(<args>) |
1893 /// | 1896 /// |
1894 Primitive specializeClosureCall(InvokeMethod node) { | 1897 Primitive specializeClosureCall(InvokeMethod node) { |
1895 Selector call = node.selector; | 1898 Selector call = node.selector; |
1896 if (!call.isClosureCall) return null; | 1899 if (!call.isClosureCall) return null; |
1897 | 1900 |
1898 assert(!isInterceptedSelector(call)); | 1901 assert(!isInterceptedSelector(call)); |
1899 assert(call.argumentCount == node.argumentRefs.length); | 1902 assert(call.argumentCount == node.argumentRefs.length); |
1900 | 1903 |
1901 Primitive tearOff = node.dartReceiver.effectiveDefinition; | 1904 Primitive tearOff = node.receiver.effectiveDefinition; |
1902 // Note: We don't know if [tearOff] is actually a tear-off. | 1905 // Note: We don't know if [tearOff] is actually a tear-off. |
1903 // We name variables based on the pattern we are trying to match. | 1906 // We name variables based on the pattern we are trying to match. |
1904 | 1907 |
1905 if (tearOff is GetStatic && tearOff.element.isFunction) { | 1908 if (tearOff is GetStatic && tearOff.element.isFunction) { |
1906 FunctionElement target = tearOff.element; | 1909 FunctionElement target = tearOff.element; |
1907 FunctionSignature signature = target.functionSignature; | 1910 FunctionSignature signature = target.functionSignature; |
1908 | 1911 |
1909 // If the selector does not apply, don't bother (will throw at runtime). | 1912 // If the selector does not apply, don't bother (will throw at runtime). |
1910 if (!call.signatureApplies(target)) return null; | 1913 if (!call.signatureApplies(target)) return null; |
1911 | 1914 |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2021 // engines typically do poor optimization of the entire function containing | 2024 // engines typically do poor optimization of the entire function containing |
2022 // the 'try'. | 2025 // the 'try'. |
2023 if (functionElement.resolvedAst.elements.containsTryStatement) return null; | 2026 if (functionElement.resolvedAst.elements.containsTryStatement) return null; |
2024 | 2027 |
2025 FunctionDefinition target = | 2028 FunctionDefinition target = |
2026 functionCompiler.compileToCpsIr(functionElement); | 2029 functionCompiler.compileToCpsIr(functionElement); |
2027 | 2030 |
2028 // Accesses to closed-over values are field access primitives. We we don't | 2031 // Accesses to closed-over values are field access primitives. We we don't |
2029 // inline if there are other uses of 'this' since that could be an escape or | 2032 // inline if there are other uses of 'this' since that could be an escape or |
2030 // a recursive call. | 2033 // a recursive call. |
2031 for (Reference ref = target.thisParameter.firstRef; | 2034 for (Reference ref = target.receiverParameter.firstRef; |
2032 ref != null; | 2035 ref != null; |
2033 ref = ref.next) { | 2036 ref = ref.next) { |
2034 Node use = ref.parent; | 2037 Node use = ref.parent; |
2035 if (use is GetField) continue; | 2038 if (use is GetField) continue; |
2036 // Closures do not currently have writable fields, but closure conversion | 2039 // Closures do not currently have writable fields, but closure conversion |
2037 // could esily be changed to allocate some cells in a closure object. | 2040 // could esily be changed to allocate some cells in a closure object. |
2038 if (use is SetField && ref == use.objectRef) continue; | 2041 if (use is SetField && ref == use.objectRef) continue; |
2039 return null; | 2042 return null; |
2040 } | 2043 } |
2041 | 2044 |
(...skipping 25 matching lines...) Expand all Loading... |
2067 } | 2070 } |
2068 | 2071 |
2069 visitInvokeMethodDirectly(InvokeMethodDirectly node) { | 2072 visitInvokeMethodDirectly(InvokeMethodDirectly node) { |
2070 Element target = node.target; | 2073 Element target = node.target; |
2071 if (target is ConstructorBodyElement) { | 2074 if (target is ConstructorBodyElement) { |
2072 ConstructorBodyElement constructorBody = target; | 2075 ConstructorBodyElement constructorBody = target; |
2073 target = constructorBody.constructor; | 2076 target = constructorBody.constructor; |
2074 } | 2077 } |
2075 node.effects = | 2078 node.effects = |
2076 Effects.from(compiler.world.getSideEffectsOfElement(target)); | 2079 Effects.from(compiler.world.getSideEffectsOfElement(target)); |
2077 TypeMask receiverType = node.dartReceiver.type; | 2080 TypeMask receiverType = node.receiver.type; |
2078 if (node.callingConvention == CallingConvention.Intercepted && | 2081 if (node.callingConvention == CallingConvention.Intercepted && |
2079 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { | 2082 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { |
2080 // Some direct calls take an interceptor because the target class is | 2083 // Some direct calls take an interceptor because the target class is |
2081 // mixed into a native class. If it is known at the call site that the | 2084 // mixed into a native class. If it is known at the call site that the |
2082 // receiver is non-intercepted, get rid of the interceptor. | 2085 // receiver is non-intercepted, get rid of the interceptor. |
2083 node.receiverRef.changeTo(node.dartReceiver); | 2086 node.interceptorRef.changeTo(node.receiver); |
2084 } | 2087 } |
2085 } | 2088 } |
2086 | 2089 |
2087 visitInvokeMethod(InvokeMethod node) { | 2090 visitInvokeMethod(InvokeMethod node) { |
2088 var specialized = | 2091 var specialized = |
2089 specializeOperatorCall(node) ?? | 2092 specializeOperatorCall(node) ?? |
2090 specializeFieldAccess(node) ?? | 2093 specializeFieldAccess(node) ?? |
2091 specializeIndexableAccess(node) ?? | 2094 specializeIndexableAccess(node) ?? |
2092 specializeArrayAccess(node) ?? | 2095 specializeArrayAccess(node) ?? |
2093 specializeSingleUseClosureCall(node) ?? | 2096 specializeSingleUseClosureCall(node) ?? |
2094 specializeClosureCall(node); | 2097 specializeClosureCall(node); |
2095 if (specialized != null) return specialized; | 2098 if (specialized != null) return specialized; |
2096 | 2099 |
2097 TypeMask receiverType = node.dartReceiver.type; | 2100 TypeMask receiverType = node.receiver.type; |
2098 node.mask = typeSystem.intersection(node.mask, receiverType); | 2101 node.mask = typeSystem.intersection(node.mask, receiverType); |
2099 | 2102 |
2100 node.effects = Effects.from( | 2103 node.effects = Effects.from( |
2101 compiler.world.getSideEffectsOfSelector(node.selector, node.mask)); | 2104 compiler.world.getSideEffectsOfSelector(node.selector, node.mask)); |
2102 | 2105 |
2103 bool canBeNonThrowingCallOnNull = | 2106 bool canBeNonThrowingCallOnNull = |
2104 selectorsOnNull.contains(node.selector) && | 2107 selectorsOnNull.contains(node.selector) && |
2105 receiverType.isNullable; | 2108 receiverType.isNullable; |
2106 | 2109 |
2107 if (node.callingConvention == CallingConvention.Intercepted && | 2110 if (node.callingConvention == CallingConvention.Intercepted && |
2108 !canBeNonThrowingCallOnNull && | 2111 !canBeNonThrowingCallOnNull && |
2109 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { | 2112 typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) { |
2110 // Use the Dart receiver as the JS receiver. This changes the wording of | 2113 // Use the Dart receiver as the JS receiver. This changes the wording of |
2111 // the error message when the receiver is null, but we accept this. | 2114 // the error message when the receiver is null, but we accept this. |
2112 node.receiverRef.changeTo(node.dartReceiver); | 2115 node.interceptorRef.changeTo(node.receiver); |
2113 | 2116 |
2114 // Replace the extra receiver argument with a dummy value if the | 2117 // Replace the extra receiver argument with a dummy value if the |
2115 // target definitely does not use it. | 2118 // target definitely does not use it. |
2116 if (typeSystem.targetIgnoresReceiverArgument(receiverType, | 2119 if (typeSystem.targetIgnoresReceiverArgument(receiverType, |
2117 node.selector)) { | 2120 node.selector)) { |
2118 Constant dummy = makeConstantPrimitive(new IntConstantValue(0)); | 2121 node.makeDummyIntercepted(); |
2119 new LetPrim(dummy).insertAbove(node.parent); | |
2120 node.argumentRefs[0].changeTo(dummy); | |
2121 node.callingConvention = CallingConvention.DummyIntercepted; | |
2122 } | 2122 } |
2123 } | 2123 } |
2124 } | 2124 } |
2125 | 2125 |
2126 CpsFragment visitTypeCast(TypeCast node) { | 2126 CpsFragment visitTypeCast(TypeCast node) { |
2127 AbstractConstantValue value = getValue(node.value); | 2127 AbstractConstantValue value = getValue(node.value); |
2128 switch (lattice.isSubtypeOf(value, node.dartType, allowNull: true)) { | 2128 switch (lattice.isSubtypeOf(value, node.dartType, allowNull: true)) { |
2129 case AbstractBool.Maybe: | 2129 case AbstractBool.Maybe: |
2130 case AbstractBool.Nothing: | 2130 case AbstractBool.Nothing: |
2131 return null; | 2131 return null; |
(...skipping 16 matching lines...) Expand all Loading... |
2148 Primitive argument = node.argument(0); | 2148 Primitive argument = node.argument(0); |
2149 AbstractConstantValue value = getValue(argument); | 2149 AbstractConstantValue value = getValue(argument); |
2150 if (lattice.isDefinitelyString(value)) { | 2150 if (lattice.isDefinitelyString(value)) { |
2151 node.replaceUsesWith(argument); | 2151 node.replaceUsesWith(argument); |
2152 return new CpsFragment(); | 2152 return new CpsFragment(); |
2153 } else if (typeSystem.isDefinitelySelfInterceptor(value.type)) { | 2153 } else if (typeSystem.isDefinitelySelfInterceptor(value.type)) { |
2154 TypeMask toStringReturn = typeSystem.getInvokeReturnType( | 2154 TypeMask toStringReturn = typeSystem.getInvokeReturnType( |
2155 Selectors.toString_, value.type); | 2155 Selectors.toString_, value.type); |
2156 if (typeSystem.isDefinitelyString(toStringReturn)) { | 2156 if (typeSystem.isDefinitelyString(toStringReturn)) { |
2157 CpsFragment cps = new CpsFragment(node.sourceInformation); | 2157 CpsFragment cps = new CpsFragment(node.sourceInformation); |
2158 Primitive invoke = cps.invokeMethod(argument, | 2158 Primitive invoke = cps.invokeMethod( |
| 2159 argument, |
2159 Selectors.toString_, | 2160 Selectors.toString_, |
2160 value.type, | 2161 value.type, |
2161 [cps.makeZero()], | 2162 [], |
2162 CallingConvention.DummyIntercepted); | 2163 callingConvention: CallingConvention.DummyIntercepted); |
2163 node.replaceUsesWith(invoke); | 2164 node.replaceUsesWith(invoke); |
2164 return cps; | 2165 return cps; |
2165 } | 2166 } |
2166 } | 2167 } |
2167 } else if (node.target == compiler.identicalFunction) { | 2168 } else if (node.target == compiler.identicalFunction) { |
2168 if (node.argumentRefs.length == 2) { | 2169 if (node.argumentRefs.length == 2) { |
2169 return new ApplyBuiltinOperator(BuiltinOperator.Identical, | 2170 return new ApplyBuiltinOperator(BuiltinOperator.Identical, |
2170 [node.argument(0), node.argument(1)], | 2171 [node.argument(0), node.argument(1)], |
2171 node.sourceInformation); | 2172 node.sourceInformation); |
2172 } | 2173 } |
(...skipping 500 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2673 } | 2674 } |
2674 | 2675 |
2675 bool isInterceptedSelector(Selector selector) { | 2676 bool isInterceptedSelector(Selector selector) { |
2676 return backend.isInterceptedSelector(selector); | 2677 return backend.isInterceptedSelector(selector); |
2677 } | 2678 } |
2678 | 2679 |
2679 // -------------------------- Visitor overrides ------------------------------ | 2680 // -------------------------- Visitor overrides ------------------------------ |
2680 void visit(Node node) { node.accept(this); } | 2681 void visit(Node node) { node.accept(this); } |
2681 | 2682 |
2682 void visitFunctionDefinition(FunctionDefinition node) { | 2683 void visitFunctionDefinition(FunctionDefinition node) { |
2683 bool isIntercepted = backend.isInterceptedMethod(node.element); | 2684 if (node.interceptorParameter != null) { |
2684 | 2685 setValue(node.interceptorParameter, nonConstant(typeSystem.nonNullType)); |
| 2686 } |
2685 // If the abstract value of the function parameters is Nothing, use the | 2687 // If the abstract value of the function parameters is Nothing, use the |
2686 // inferred parameter type. Otherwise (e.g., when inlining) do not | 2688 // inferred parameter type. Otherwise (e.g., when inlining) do not |
2687 // change the abstract value. | 2689 // change the abstract value. |
2688 if (node.thisParameter != null && getValue(node.thisParameter).isNothing) { | 2690 if (node.receiverParameter != null && |
2689 if (isIntercepted && | 2691 getValue(node.receiverParameter).isNothing) { |
2690 !typeSystem.methodIgnoresReceiverArgument(node.element)) { | 2692 setValue(node.receiverParameter, |
2691 setValue(node.thisParameter, nonConstant(typeSystem.nonNullType)); | 2693 nonConstant(typeSystem.getReceiverType(node.element))); |
2692 } else { | |
2693 setValue(node.thisParameter, | |
2694 nonConstant(typeSystem.getReceiverType(node.element))); | |
2695 } | |
2696 } | |
2697 if (isIntercepted && getValue(node.parameters[0]).isNothing) { | |
2698 if (typeSystem.methodIgnoresReceiverArgument(node.element)) { | |
2699 setValue(node.parameters[0], nonConstant()); | |
2700 } else { | |
2701 setValue(node.parameters[0], | |
2702 nonConstant(typeSystem.getReceiverType(node.element))); | |
2703 } | |
2704 } | 2694 } |
2705 bool hasParameterWithoutValue = false; | 2695 bool hasParameterWithoutValue = false; |
2706 for (Parameter param in node.parameters.skip(isIntercepted ? 1 : 0)) { | 2696 for (Parameter param in node.parameters) { |
2707 if (getValue(param).isNothing) { | 2697 if (getValue(param).isNothing) { |
2708 TypeMask type = param.hint is ParameterElement | 2698 TypeMask type = param.hint is ParameterElement |
2709 ? typeSystem.getParameterType(param.hint) | 2699 ? typeSystem.getParameterType(param.hint) |
2710 : typeSystem.dynamicType; | 2700 : typeSystem.dynamicType; |
2711 setValue(param, lattice.fromMask(type)); | 2701 setValue(param, lattice.fromMask(type)); |
2712 if (type.isEmpty) hasParameterWithoutValue = true; | 2702 if (type.isEmpty) hasParameterWithoutValue = true; |
2713 } | 2703 } |
2714 } | 2704 } |
2715 if (!hasParameterWithoutValue) { // Don't analyze unreachable code. | 2705 if (!hasParameterWithoutValue) { // Don't analyze unreachable code. |
2716 push(node.body); | 2706 push(node.body); |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2766 // Forward the constant status of all continuation invokes to the | 2756 // Forward the constant status of all continuation invokes to the |
2767 // continuation. Note that this is effectively a phi node in SSA terms. | 2757 // continuation. Note that this is effectively a phi node in SSA terms. |
2768 for (int i = 0; i < node.argumentRefs.length; i++) { | 2758 for (int i = 0; i < node.argumentRefs.length; i++) { |
2769 Primitive def = node.argument(i); | 2759 Primitive def = node.argument(i); |
2770 AbstractConstantValue cell = getValue(def); | 2760 AbstractConstantValue cell = getValue(def); |
2771 setValue(cont.parameters[i], cell); | 2761 setValue(cont.parameters[i], cell); |
2772 } | 2762 } |
2773 } | 2763 } |
2774 | 2764 |
2775 void visitInvokeMethod(InvokeMethod node) { | 2765 void visitInvokeMethod(InvokeMethod node) { |
2776 AbstractConstantValue receiver = getValue(node.dartReceiver); | 2766 AbstractConstantValue receiver = getValue(node.receiver); |
2777 if (receiver.isNothing) { | 2767 if (receiver.isNothing) { |
2778 return setResult(node, lattice.nothing); | 2768 return setResult(node, lattice.nothing); |
2779 } | 2769 } |
2780 | 2770 |
2781 void finish(AbstractConstantValue result, {bool canReplace: false}) { | 2771 void finish(AbstractConstantValue result, {bool canReplace: false}) { |
2782 if (result == null) { | 2772 if (result == null) { |
2783 canReplace = false; | 2773 canReplace = false; |
2784 result = lattice.getInvokeReturnType(node.selector, receiver); | 2774 result = lattice.getInvokeReturnType(node.selector, receiver); |
2785 } | 2775 } |
2786 setResult(node, result, canReplace: canReplace); | 2776 setResult(node, result, canReplace: canReplace); |
2787 } | 2777 } |
2788 | 2778 |
2789 if (node.selector.isGetter) { | 2779 if (node.selector.isGetter) { |
2790 // Constant fold known length of containers. | 2780 // Constant fold known length of containers. |
2791 if (node.selector == Selectors.length) { | 2781 if (node.selector == Selectors.length) { |
2792 if (typeSystem.isDefinitelyIndexable(receiver.type, allowNull: true)) { | 2782 if (typeSystem.isDefinitelyIndexable(receiver.type, allowNull: true)) { |
2793 AbstractConstantValue length = lattice.lengthSpecial(receiver); | 2783 AbstractConstantValue length = lattice.lengthSpecial(receiver); |
2794 return finish(length, canReplace: !receiver.isNullable); | 2784 return finish(length, canReplace: !receiver.isNullable); |
2795 } | 2785 } |
2796 } | 2786 } |
2797 return finish(null); | 2787 return finish(null); |
2798 } | 2788 } |
2799 | 2789 |
2800 if (node.selector.isCall) { | 2790 if (node.selector.isCall) { |
2801 if (node.selector == Selectors.codeUnitAt) { | 2791 if (node.selector == Selectors.codeUnitAt) { |
2802 AbstractConstantValue right = getValue(node.dartArgument(0)); | 2792 AbstractConstantValue right = getValue(node.argument(0)); |
2803 AbstractConstantValue result = | 2793 AbstractConstantValue result = |
2804 lattice.codeUnitAtSpecial(receiver, right); | 2794 lattice.codeUnitAtSpecial(receiver, right); |
2805 return finish(result, canReplace: !receiver.isNullable); | 2795 return finish(result, canReplace: !receiver.isNullable); |
2806 } | 2796 } |
2807 return finish(null); | 2797 return finish(null); |
2808 } | 2798 } |
2809 | 2799 |
2810 if (node.selector == Selectors.index) { | 2800 if (node.selector == Selectors.index) { |
2811 AbstractConstantValue right = getValue(node.dartArgument(0)); | 2801 AbstractConstantValue right = getValue(node.argument(0)); |
2812 AbstractConstantValue result = lattice.indexSpecial(receiver, right); | 2802 AbstractConstantValue result = lattice.indexSpecial(receiver, right); |
2813 return finish(result, canReplace: !receiver.isNullable); | 2803 return finish(result, canReplace: !receiver.isNullable); |
2814 } | 2804 } |
2815 | 2805 |
2816 if (!node.selector.isOperator) { | 2806 if (!node.selector.isOperator) { |
2817 return finish(null); | 2807 return finish(null); |
2818 } | 2808 } |
2819 | 2809 |
2820 // Calculate the resulting constant if possible. | 2810 // Calculate the resulting constant if possible. |
2821 String opname = node.selector.name; | 2811 String opname = node.selector.name; |
2822 if (node.dartArgumentsLength == 0) { | 2812 if (node.argumentRefs.length == 0) { |
2823 // Unary operator. | 2813 // Unary operator. |
2824 if (opname == "unary-") { | 2814 if (opname == "unary-") { |
2825 opname = "-"; | 2815 opname = "-"; |
2826 } | 2816 } |
2827 UnaryOperator operator = UnaryOperator.parse(opname); | 2817 UnaryOperator operator = UnaryOperator.parse(opname); |
2828 AbstractConstantValue result = lattice.unaryOp(operator, receiver); | 2818 AbstractConstantValue result = lattice.unaryOp(operator, receiver); |
2829 return finish(result, canReplace: !receiver.isNullable); | 2819 return finish(result, canReplace: !receiver.isNullable); |
2830 } else if (node.dartArgumentsLength == 1) { | 2820 } else if (node.argumentRefs.length == 1) { |
2831 // Binary operator. | 2821 // Binary operator. |
2832 AbstractConstantValue right = getValue(node.dartArgument(0)); | 2822 AbstractConstantValue right = getValue(node.argument(0)); |
2833 BinaryOperator operator = BinaryOperator.parse(opname); | 2823 BinaryOperator operator = BinaryOperator.parse(opname); |
2834 AbstractConstantValue result = | 2824 AbstractConstantValue result = |
2835 lattice.binaryOp(operator, receiver, right); | 2825 lattice.binaryOp(operator, receiver, right); |
2836 return finish(result, canReplace: !receiver.isNullable); | 2826 return finish(result, canReplace: !receiver.isNullable); |
2837 } | 2827 } |
2838 return finish(null); | 2828 return finish(null); |
2839 } | 2829 } |
2840 | 2830 |
2841 void visitApplyBuiltinOperator(ApplyBuiltinOperator node) { | 2831 void visitApplyBuiltinOperator(ApplyBuiltinOperator node) { |
2842 | 2832 |
(...skipping 714 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3557 } | 3547 } |
3558 | 3548 |
3559 Primitive makeCheck(CpsFragment cps, Primitive value) { | 3549 Primitive makeCheck(CpsFragment cps, Primitive value) { |
3560 return cps.applyBuiltin(negatedOperator, [value]); | 3550 return cps.applyBuiltin(negatedOperator, [value]); |
3561 } | 3551 } |
3562 | 3552 |
3563 Primitive makeRefinement(CpsFragment cps, Primitive value, World world) { | 3553 Primitive makeRefinement(CpsFragment cps, Primitive value, World world) { |
3564 return cps.refine(value, new TypeMask.nonNullSubclass(classElement, world)); | 3554 return cps.refine(value, new TypeMask.nonNullSubclass(classElement, world)); |
3565 } | 3555 } |
3566 } | 3556 } |
OLD | NEW |