| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library dart2js.ir_builder_task; | 5 library dart2js.ir_builder_task; |
| 6 | 6 |
| 7 import '../closure.dart' as closure; | 7 import '../closure.dart' as closure; |
| 8 import '../common.dart'; | 8 import '../common.dart'; |
| 9 import '../common/names.dart' show | 9 import '../common/names.dart' show |
| 10 Names, | 10 Names, |
| (...skipping 1154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1165 irBuilder.buildRedirectingNativeFunctionBody(function, name, source); | 1165 irBuilder.buildRedirectingNativeFunctionBody(function, name, source); |
| 1166 } | 1166 } |
| 1167 } else { | 1167 } else { |
| 1168 irBuilder.buildReturn( | 1168 irBuilder.buildReturn( |
| 1169 value: build(node.expression), | 1169 value: build(node.expression), |
| 1170 sourceInformation: source); | 1170 sourceInformation: source); |
| 1171 } | 1171 } |
| 1172 } | 1172 } |
| 1173 | 1173 |
| 1174 visitSwitchStatement(ast.SwitchStatement node) { | 1174 visitSwitchStatement(ast.SwitchStatement node) { |
| 1175 // Dart switch cases can be labeled and be the target of continue from |
| 1176 // within the switch. Such cases are 'recursive'. If there are any |
| 1177 // recursive cases, we implement the switch using a pair of switches with |
| 1178 // the second one switching over a state variable in a loop. The first |
| 1179 // switch contains the non-recursive cases, and the second switch contains |
| 1180 // the recursive ones. |
| 1181 // |
| 1182 // For example, for the Dart switch: |
| 1183 // |
| 1184 // switch (E) { |
| 1185 // case 0: |
| 1186 // BODY0; |
| 1187 // break; |
| 1188 // LABEL0: case 1: |
| 1189 // BODY1; |
| 1190 // break; |
| 1191 // case 2: |
| 1192 // BODY2; |
| 1193 // continue LABEL1; |
| 1194 // LABEL1: case 3: |
| 1195 // BODY3; |
| 1196 // continue LABEL0; |
| 1197 // default: |
| 1198 // BODY4; |
| 1199 // } |
| 1200 // |
| 1201 // We translate it as if it were the JavaScript: |
| 1202 // |
| 1203 // var state = -1; |
| 1204 // switch (E) { |
| 1205 // case 0: |
| 1206 // BODY0; |
| 1207 // break; |
| 1208 // case 1: |
| 1209 // state = 0; // Recursive, label ID = 0. |
| 1210 // break; |
| 1211 // case 2: |
| 1212 // BODY2; |
| 1213 // state = 1; // Continue to label ID = 1. |
| 1214 // break; |
| 1215 // case 3: |
| 1216 // state = 1; // Recursive, label ID = 1. |
| 1217 // break; |
| 1218 // default: |
| 1219 // BODY4; |
| 1220 // } |
| 1221 // L: while (state != -1) { |
| 1222 // case 0: |
| 1223 // BODY1; |
| 1224 // break L; // Break from switch becomes break from loop. |
| 1225 // case 1: |
| 1226 // BODY2; |
| 1227 // state = 0; // Continue to label ID = 0. |
| 1228 // break; |
| 1229 // } |
| 1175 assert(irBuilder.isOpen); | 1230 assert(irBuilder.isOpen); |
| 1176 // We do not handle switch statements with continue to labeled cases. | 1231 // Preprocess: compute a list of cases that are the target of continue. |
| 1177 for (ast.SwitchCase switchCase in node.cases) { | 1232 // These are the so-called 'recursive' cases. |
| 1233 List<JumpTarget> continueTargets = <JumpTarget>[]; |
| 1234 List<ast.SwitchCase> switchCases = node.cases.nodes.toList(); |
| 1235 for (ast.SwitchCase switchCase in switchCases) { |
| 1178 for (ast.Node labelOrCase in switchCase.labelsAndCases) { | 1236 for (ast.Node labelOrCase in switchCase.labelsAndCases) { |
| 1179 if (labelOrCase is ast.Label) { | 1237 if (labelOrCase is ast.Label) { |
| 1180 LabelDefinition definition = elements.getLabelDefinition(labelOrCase); | 1238 LabelDefinition definition = elements.getLabelDefinition(labelOrCase); |
| 1181 if (definition != null && definition.isContinueTarget) { | 1239 if (definition != null && definition.isContinueTarget) { |
| 1182 return giveup(node, "continue to a labeled switch case"); | 1240 continueTargets.add(definition.target); |
| 1183 } | 1241 } |
| 1184 } | 1242 } |
| 1185 } | 1243 } |
| 1186 } | 1244 } |
| 1187 | 1245 |
| 1188 // Each switch case contains a list of interleaved labels and expressions | 1246 // If any cases are continue targets, use an anonymous local value to |
| 1189 // and a non-empty body. We can ignore the labels because they are not | 1247 // implement a state machine. The initial value is -1. |
| 1190 // jump targets. | 1248 ir.Primitive initial; |
| 1249 int stateIndex; |
| 1250 if (continueTargets.isNotEmpty) { |
| 1251 initial = irBuilder.buildIntegerConstant(-1); |
| 1252 stateIndex = irBuilder.environment.length; |
| 1253 irBuilder.environment.extend(null, initial); |
| 1254 } |
| 1255 |
| 1256 // Use a simple switch for the non-recursive cases. A break will go to the |
| 1257 // join-point after the switch. A continue to a labeled case will assign |
| 1258 // to the state variable and go to the join-point. |
| 1259 ir.Primitive value = visit(node.expression); |
| 1260 JumpCollector join = new ForwardJumpCollector(irBuilder.environment, |
| 1261 target: elements.getTargetDefinition(node)); |
| 1262 irBuilder.state.breakCollectors.add(join); |
| 1263 for (int i = 0; i < continueTargets.length; ++i) { |
| 1264 // The state value is i, the case's position in the list of recursive |
| 1265 // cases. |
| 1266 irBuilder.state.continueCollectors.add(new GotoJumpCollector( |
| 1267 continueTargets[i], stateIndex, i, join)); |
| 1268 } |
| 1269 |
| 1270 // For each non-default case use a pair of functions, one to translate the |
| 1271 // condition and one to translate the body. For the default case use a |
| 1272 // function to translate the body. Use continueTargetIterator as a pointer |
| 1273 // to the next recursive case. |
| 1274 Iterator<JumpTarget> continueTargetIterator = continueTargets.iterator; |
| 1275 continueTargetIterator.moveNext(); |
| 1191 List<SwitchCaseInfo> cases = <SwitchCaseInfo>[]; | 1276 List<SwitchCaseInfo> cases = <SwitchCaseInfo>[]; |
| 1192 SwitchCaseInfo defaultCase; | 1277 SubbuildFunction buildDefaultBody; |
| 1193 for (ast.SwitchCase switchCase in node.cases) { | 1278 for (ast.SwitchCase switchCase in switchCases) { |
| 1194 SwitchCaseInfo caseInfo = | 1279 JumpTarget nextContinueTarget = continueTargetIterator.current; |
| 1195 new SwitchCaseInfo(subbuildSequence(switchCase.statements)); | |
| 1196 if (switchCase.isDefaultCase) { | 1280 if (switchCase.isDefaultCase) { |
| 1197 defaultCase = caseInfo; | 1281 if (nextContinueTarget != null && |
| 1282 switchCase == nextContinueTarget.statement) { |
| 1283 // In this simple switch, recursive cases are as if they immediately |
| 1284 // continued to themselves. |
| 1285 buildDefaultBody = nested(() { |
| 1286 irBuilder.buildContinue(nextContinueTarget); |
| 1287 }); |
| 1288 continueTargetIterator.moveNext(); |
| 1289 } else { |
| 1290 // Non-recursive cases consist of the translation of the body. |
| 1291 // For the default case, there is implicitly a break if control |
| 1292 // flow reaches the end. |
| 1293 buildDefaultBody = nested(() { |
| 1294 irBuilder.buildSequence(switchCase.statements, visit); |
| 1295 if (irBuilder.isOpen) irBuilder.jumpTo(join); |
| 1296 }); |
| 1297 } |
| 1298 continue; |
| 1299 } |
| 1300 |
| 1301 ir.Primitive buildCondition(IrBuilder builder) { |
| 1302 // There can be multiple cases sharing the same body, because empty |
| 1303 // cases are allowed to fall through to the next one. Each case is |
| 1304 // a comparison, build a short-circuited disjunction of all of them. |
| 1305 return withBuilder(builder, () { |
| 1306 ir.Primitive condition; |
| 1307 for (ast.Node labelOrCase in switchCase.labelsAndCases) { |
| 1308 if (labelOrCase is ast.CaseMatch) { |
| 1309 ir.Primitive buildComparison() { |
| 1310 ir.Primitive constant = |
| 1311 translateConstant(labelOrCase.expression); |
| 1312 return irBuilder.buildIdentical(value, constant); |
| 1313 } |
| 1314 |
| 1315 if (condition == null) { |
| 1316 condition = buildComparison(); |
| 1317 } else { |
| 1318 condition = irBuilder.buildLogicalOperator(condition, |
| 1319 nested(buildComparison), isLazyOr: true); |
| 1320 } |
| 1321 } |
| 1322 } |
| 1323 return condition; |
| 1324 }); |
| 1325 } |
| 1326 |
| 1327 SubbuildFunction buildBody; |
| 1328 if (nextContinueTarget != null && |
| 1329 switchCase == nextContinueTarget.statement) { |
| 1330 // Recursive cases are as if they immediately continued to themselves. |
| 1331 buildBody = nested(() { |
| 1332 irBuilder.buildContinue(nextContinueTarget); |
| 1333 }); |
| 1334 continueTargetIterator.moveNext(); |
| 1198 } else { | 1335 } else { |
| 1199 cases.add(caseInfo); | 1336 // Non-recursive cases consist of the translation of the body. It is a |
| 1200 for (ast.Node labelOrCase in switchCase.labelsAndCases) { | 1337 // runtime error if control-flow reaches the end of the body of any but |
| 1201 if (labelOrCase is ast.CaseMatch) { | 1338 // the last case. |
| 1202 ir.Primitive constant = translateConstant(labelOrCase.expression); | 1339 buildBody = (IrBuilder builder) { |
| 1203 caseInfo.addConstant(constant); | 1340 withBuilder(builder, () { |
| 1341 irBuilder.buildSequence(switchCase.statements, visit); |
| 1342 if (irBuilder.isOpen) { |
| 1343 if (switchCase == switchCases.last) { |
| 1344 irBuilder.jumpTo(join); |
| 1345 } else { |
| 1346 Element error = helpers.fallThroughError; |
| 1347 ir.Primitive exception = irBuilder.buildInvokeStatic( |
| 1348 error, |
| 1349 new Selector.fromElement(error), |
| 1350 <ir.Primitive>[], |
| 1351 sourceInformationBuilder.buildGeneric(node)); |
| 1352 irBuilder.buildThrow(exception); |
| 1353 } |
| 1354 } |
| 1355 }); |
| 1356 return null; |
| 1357 }; |
| 1358 } |
| 1359 |
| 1360 cases.add(new SwitchCaseInfo(buildCondition, buildBody)); |
| 1361 } |
| 1362 |
| 1363 irBuilder.buildSimpleSwitch(join, cases, buildDefaultBody); |
| 1364 irBuilder.state.breakCollectors.removeLast(); |
| 1365 irBuilder.state.continueCollectors.length -= continueTargets.length; |
| 1366 if (continueTargets.isEmpty) return; |
| 1367 |
| 1368 // If there were recursive cases build a while loop whose body is a |
| 1369 // switch containing (only) the recursive cases. The condition is |
| 1370 // 'state != initialValue' so the loop is not taken when the state variable |
| 1371 // has not been assigned. |
| 1372 // |
| 1373 // 'loop' is the join-point of the exits from the inner switch which will |
| 1374 // perform another iteration of the loop. 'exit' is the join-point of the |
| 1375 // breaks from the switch, outside the loop. |
| 1376 JumpCollector loop = new ForwardJumpCollector(irBuilder.environment); |
| 1377 JumpCollector exit = new ForwardJumpCollector(irBuilder.environment, |
| 1378 target: elements.getTargetDefinition(node)); |
| 1379 irBuilder.state.breakCollectors.add(exit); |
| 1380 for (int i = 0; i < continueTargets.length; ++i) { |
| 1381 irBuilder.state.continueCollectors.add(new GotoJumpCollector( |
| 1382 continueTargets[i], stateIndex, i, loop)); |
| 1383 } |
| 1384 cases.clear(); |
| 1385 for (int i = 0; i < continueTargets.length; ++i) { |
| 1386 // The conditions compare to the recursive case index. |
| 1387 ir.Primitive buildCondition(IrBuilder builder) { |
| 1388 ir.Primitive constant = builder.buildIntegerConstant(i); |
| 1389 return builder.buildIdentical( |
| 1390 builder.environment.index2value[stateIndex], constant); |
| 1391 } |
| 1392 |
| 1393 ir.Primitive buildBody(IrBuilder builder) { |
| 1394 withBuilder(builder, () { |
| 1395 ast.SwitchCase switchCase = continueTargets[i].statement; |
| 1396 irBuilder.buildSequence(switchCase.statements, visit); |
| 1397 if (irBuilder.isOpen) { |
| 1398 if (switchCase == switchCases.last) { |
| 1399 irBuilder.jumpTo(exit); |
| 1400 } else { |
| 1401 Element error = helpers.fallThroughError; |
| 1402 ir.Primitive exception = irBuilder.buildInvokeStatic( |
| 1403 error, |
| 1404 new Selector.fromElement(error), |
| 1405 <ir.Primitive>[], |
| 1406 sourceInformationBuilder.buildGeneric(node)); |
| 1407 irBuilder.buildThrow(exception); |
| 1408 } |
| 1204 } | 1409 } |
| 1205 } | 1410 }); |
| 1206 } | 1411 return null; |
| 1207 } | 1412 } |
| 1208 ir.Primitive value = visit(node.expression); | 1413 |
| 1209 JumpTarget target = elements.getTargetDefinition(node); | 1414 cases.add(new SwitchCaseInfo(buildCondition, buildBody)); |
| 1210 Element error = helpers.fallThroughError; | 1415 } |
| 1211 irBuilder.buildSimpleSwitch(target, value, cases, defaultCase, error, | 1416 |
| 1212 sourceInformationBuilder.buildGeneric(node)); | 1417 // A loop with a simple switch in the body. |
| 1418 IrBuilder whileBuilder = irBuilder.makeDelimitedBuilder(); |
| 1419 whileBuilder.buildWhile( |
| 1420 buildCondition: (IrBuilder builder) { |
| 1421 ir.Primitive condition = builder.buildIdentical( |
| 1422 builder.environment.index2value[stateIndex], initial); |
| 1423 return builder.buildNegation(condition); |
| 1424 }, |
| 1425 buildBody: (IrBuilder builder) { |
| 1426 builder.buildSimpleSwitch(loop, cases, null); |
| 1427 }); |
| 1428 // Jump to the exit continuation. This jump is the body of the loop exit |
| 1429 // continuation, so the loop exit continuation can be eta-reduced. The |
| 1430 // jump is here for simplicity because `buildWhile` does not expose the |
| 1431 // loop's exit continuation directly and has already emitted all jumps |
| 1432 // to it anyway. |
| 1433 whileBuilder.jumpTo(exit); |
| 1434 irBuilder.add(new ir.LetCont(exit.continuation, whileBuilder.root)); |
| 1435 irBuilder.environment = exit.environment; |
| 1436 irBuilder.environment.discard(1); // Discard the state variable. |
| 1437 irBuilder.state.breakCollectors.removeLast(); |
| 1438 irBuilder.state.continueCollectors.length -= continueTargets.length; |
| 1213 } | 1439 } |
| 1214 | 1440 |
| 1215 visitTryStatement(ast.TryStatement node) { | 1441 visitTryStatement(ast.TryStatement node) { |
| 1216 List<CatchClauseInfo> catchClauseInfos = <CatchClauseInfo>[]; | 1442 List<CatchClauseInfo> catchClauseInfos = <CatchClauseInfo>[]; |
| 1217 for (ast.CatchBlock catchClause in node.catchBlocks.nodes) { | 1443 for (ast.CatchBlock catchClause in node.catchBlocks.nodes) { |
| 1218 LocalVariableElement exceptionVariable; | 1444 LocalVariableElement exceptionVariable; |
| 1219 if (catchClause.exception != null) { | 1445 if (catchClause.exception != null) { |
| 1220 exceptionVariable = elements[catchClause.exception]; | 1446 exceptionVariable = elements[catchClause.exception]; |
| 1221 } | 1447 } |
| 1222 LocalVariableElement stackTraceVariable; | 1448 LocalVariableElement stackTraceVariable; |
| (...skipping 2366 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3589 } | 3815 } |
| 3590 | 3816 |
| 3591 Element get closureConverter { | 3817 Element get closureConverter { |
| 3592 return _backend.helpers.closureConverter; | 3818 return _backend.helpers.closureConverter; |
| 3593 } | 3819 } |
| 3594 | 3820 |
| 3595 void addNativeMethod(FunctionElement function) { | 3821 void addNativeMethod(FunctionElement function) { |
| 3596 _backend.emitter.nativeEmitter.nativeMethods.add(function); | 3822 _backend.emitter.nativeEmitter.nativeMethods.add(function); |
| 3597 } | 3823 } |
| 3598 } | 3824 } |
| OLD | NEW |