Chromium Code Reviews| 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 1155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 assert(irBuilder.isOpen); | 1175 assert(irBuilder.isOpen); |
| 1176 // We do not handle switch statements with continue to labeled cases. | 1176 // Preprocess: compute a list of cases that are the target of continue. |
| 1177 for (ast.SwitchCase switchCase in node.cases) { | 1177 // These are the so-called 'recursive' cases. |
| 1178 List<JumpTarget> continueTargets = <JumpTarget>[]; | |
| 1179 List<ast.SwitchCase> switchCases = node.cases.nodes.toList(); | |
| 1180 for (ast.SwitchCase switchCase in switchCases) { | |
| 1178 for (ast.Node labelOrCase in switchCase.labelsAndCases) { | 1181 for (ast.Node labelOrCase in switchCase.labelsAndCases) { |
| 1179 if (labelOrCase is ast.Label) { | 1182 if (labelOrCase is ast.Label) { |
| 1180 LabelDefinition definition = elements.getLabelDefinition(labelOrCase); | 1183 LabelDefinition definition = elements.getLabelDefinition(labelOrCase); |
| 1181 if (definition != null && definition.isContinueTarget) { | 1184 if (definition != null && definition.isContinueTarget) { |
| 1182 return giveup(node, "continue to a labeled switch case"); | 1185 continueTargets.add(definition.target); |
| 1183 } | 1186 } |
| 1184 } | 1187 } |
| 1185 } | 1188 } |
| 1186 } | 1189 } |
| 1187 | 1190 |
| 1188 // Each switch case contains a list of interleaved labels and expressions | 1191 // 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 | 1192 // implement a state machine. The initial value is -1. |
| 1190 // jump targets. | 1193 ir.Primitive initial; |
| 1194 int stateIndex; | |
| 1195 if (continueTargets.isNotEmpty) { | |
| 1196 initial = irBuilder.buildIntegerConstant(-1); | |
| 1197 stateIndex = irBuilder.environment.length; | |
| 1198 irBuilder.environment.extend(null, initial); | |
| 1199 } | |
| 1200 | |
| 1201 // Use a simple switch for the non-recursive cases. A break will go to the | |
| 1202 // join-point after the switch. A continue to a labeled case will assign | |
| 1203 // to the state variable and go to the join-point. | |
| 1204 ir.Primitive value = visit(node.expression); | |
| 1205 JumpCollector join = new ForwardJumpCollector(irBuilder.environment, | |
| 1206 target: elements.getTargetDefinition(node)); | |
| 1207 irBuilder.state.breakCollectors.add(join); | |
| 1208 for (int i = 0; i < continueTargets.length; ++i) { | |
| 1209 // The state value is i, the case's position in the list of recursive | |
| 1210 // cases. | |
| 1211 irBuilder.state.continueCollectors.add(new GotoJumpCollector( | |
| 1212 continueTargets[i], stateIndex, i, join)); | |
| 1213 } | |
| 1214 | |
| 1215 // For each non-default case use a pair of functions, one to translate the | |
| 1216 // condition and one to translate the body. For the default case use a | |
| 1217 // function to translate the body. Use continueTargetIterator as a pointer | |
| 1218 // to the next recursive case. | |
| 1219 Iterator<JumpTarget> continueTargetIterator = continueTargets.iterator; | |
| 1220 continueTargetIterator.moveNext(); | |
| 1191 List<SwitchCaseInfo> cases = <SwitchCaseInfo>[]; | 1221 List<SwitchCaseInfo> cases = <SwitchCaseInfo>[]; |
| 1192 SwitchCaseInfo defaultCase; | 1222 SubbuildFunction buildDefaultBody; |
| 1193 for (ast.SwitchCase switchCase in node.cases) { | 1223 for (ast.SwitchCase switchCase in switchCases) { |
| 1194 SwitchCaseInfo caseInfo = | 1224 JumpTarget nextContinueTarget = continueTargetIterator.current; |
| 1195 new SwitchCaseInfo(subbuildSequence(switchCase.statements)); | |
| 1196 if (switchCase.isDefaultCase) { | 1225 if (switchCase.isDefaultCase) { |
| 1197 defaultCase = caseInfo; | 1226 if (nextContinueTarget != null && |
| 1227 switchCase == nextContinueTarget.statement) { | |
| 1228 // In this simple switch, recursive cases are as if they immediately | |
| 1229 // continued to themselves. | |
|
asgerf
2016/01/19 22:45:24
I don't understand what's going one here, and the
Kevin Millikin (Google)
2016/01/25 12:46:37
Sorry. I originally had such an example but I did
| |
| 1230 buildDefaultBody = nested(() { | |
| 1231 irBuilder.buildContinue(nextContinueTarget); | |
| 1232 }); | |
| 1233 continueTargetIterator.moveNext(); | |
| 1234 } else { | |
| 1235 // Non-recursive cases consist of the translation of the body. | |
| 1236 // For the default case, there is implicitly a break if control | |
| 1237 // flow reaches the end. | |
| 1238 buildDefaultBody = nested(() { | |
| 1239 irBuilder.buildSequence(switchCase.statements, visit); | |
| 1240 if (irBuilder.isOpen) irBuilder.jumpTo(join); | |
| 1241 }); | |
| 1242 } | |
| 1243 continue; | |
| 1244 } | |
| 1245 | |
| 1246 ir.Primitive buildCondition(IrBuilder builder) { | |
| 1247 // There can be multiple cases sharing the same body, because empty | |
| 1248 // cases are allowed to fall through to the next one. Each case is | |
| 1249 // a comparison, build a short-circuited disjunction of all of them. | |
| 1250 return withBuilder(builder, () { | |
| 1251 ir.Primitive condition; | |
| 1252 for (ast.Node labelOrCase in switchCase.labelsAndCases) { | |
| 1253 if (labelOrCase is ast.CaseMatch) { | |
| 1254 ir.Primitive buildComparison() { | |
| 1255 ir.Primitive constant = | |
| 1256 translateConstant(labelOrCase.expression); | |
| 1257 return irBuilder.buildIdentical(value, constant); | |
| 1258 } | |
| 1259 | |
| 1260 if (condition == null) { | |
| 1261 condition = buildComparison(); | |
| 1262 } else { | |
| 1263 condition = irBuilder.buildLogicalOperator(condition, | |
| 1264 nested(buildComparison), isLazyOr: true); | |
| 1265 } | |
| 1266 } | |
| 1267 } | |
| 1268 return condition; | |
| 1269 }); | |
| 1270 } | |
| 1271 | |
| 1272 SubbuildFunction buildBody; | |
| 1273 if (nextContinueTarget != null && | |
| 1274 switchCase == nextContinueTarget.statement) { | |
| 1275 // Recursive cases are as if they immediately continued to themselves. | |
|
asgerf
2016/01/19 22:45:24
Same as above, I just can't tell what the branch c
| |
| 1276 buildBody = nested(() { | |
| 1277 irBuilder.buildContinue(nextContinueTarget); | |
| 1278 }); | |
| 1279 continueTargetIterator.moveNext(); | |
| 1198 } else { | 1280 } else { |
| 1199 cases.add(caseInfo); | 1281 // Non-recursive cases consist of the translation of the body. It is a |
| 1200 for (ast.Node labelOrCase in switchCase.labelsAndCases) { | 1282 // runtime error if control-flow reaches the end of the body of any but |
| 1201 if (labelOrCase is ast.CaseMatch) { | 1283 // the last case. |
| 1202 ir.Primitive constant = translateConstant(labelOrCase.expression); | 1284 buildBody = (IrBuilder builder) { |
| 1203 caseInfo.addConstant(constant); | 1285 withBuilder(builder, () { |
| 1286 irBuilder.buildSequence(switchCase.statements, visit); | |
| 1287 if (irBuilder.isOpen) { | |
| 1288 if (switchCase == switchCases.last) { | |
| 1289 irBuilder.jumpTo(join); | |
| 1290 } else { | |
| 1291 Element error = helpers.fallThroughError; | |
| 1292 ir.Primitive exception = irBuilder.buildInvokeStatic( | |
| 1293 error, | |
| 1294 new Selector.fromElement(error), | |
| 1295 <ir.Primitive>[], | |
| 1296 sourceInformationBuilder.buildGeneric(node)); | |
| 1297 irBuilder.buildThrow(exception); | |
| 1298 } | |
| 1299 } | |
| 1300 }); | |
| 1301 return null; | |
| 1302 }; | |
| 1303 } | |
| 1304 | |
| 1305 cases.add(new SwitchCaseInfo(buildCondition, buildBody)); | |
| 1306 } | |
| 1307 | |
| 1308 irBuilder.buildSimpleSwitch(join, cases, buildDefaultBody); | |
| 1309 irBuilder.state.breakCollectors.removeLast(); | |
| 1310 irBuilder.state.continueCollectors.length -= continueTargets.length; | |
| 1311 if (continueTargets.isEmpty) return; | |
| 1312 | |
| 1313 // If there were recursive cases build a while loop whose body is a | |
| 1314 // switch containing (only) the recursive cases. The condition is | |
| 1315 // 'state != initialValue' so the loop is not taken when the state variable | |
| 1316 // has not been assigned. | |
| 1317 // | |
| 1318 // 'loop' is the join-point of the exits from the inner switch which will | |
| 1319 // perform another iteration of the loop. 'exit' is the join-point of the | |
| 1320 // breaks from the switch, outside the loop. | |
| 1321 JumpCollector loop = new ForwardJumpCollector(irBuilder.environment); | |
| 1322 JumpCollector exit = new ForwardJumpCollector(irBuilder.environment, | |
| 1323 target: elements.getTargetDefinition(node)); | |
| 1324 irBuilder.state.breakCollectors.add(exit); | |
| 1325 for (int i = 0; i < continueTargets.length; ++i) { | |
| 1326 irBuilder.state.continueCollectors.add(new GotoJumpCollector( | |
| 1327 continueTargets[i], stateIndex, i, loop)); | |
| 1328 } | |
| 1329 cases.clear(); | |
| 1330 for (int i = 0; i < continueTargets.length; ++i) { | |
| 1331 // The conditions compare to the recursive case index. | |
| 1332 ir.Primitive buildCondition(IrBuilder builder) { | |
| 1333 ir.Primitive constant = builder.buildIntegerConstant(i); | |
| 1334 return builder.buildIdentical( | |
| 1335 builder.environment.index2value[stateIndex], constant); | |
| 1336 } | |
| 1337 | |
| 1338 ir.Primitive buildBody(IrBuilder builder) { | |
| 1339 withBuilder(builder, () { | |
| 1340 ast.SwitchCase switchCase = continueTargets[i].statement; | |
| 1341 irBuilder.buildSequence(switchCase.statements, visit); | |
| 1342 if (irBuilder.isOpen) { | |
| 1343 if (switchCase == switchCases.last) { | |
| 1344 irBuilder.jumpTo(exit); | |
| 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 } | |
| 1204 } | 1354 } |
| 1205 } | 1355 }); |
| 1206 } | 1356 return null; |
| 1207 } | 1357 } |
| 1208 ir.Primitive value = visit(node.expression); | 1358 |
| 1209 JumpTarget target = elements.getTargetDefinition(node); | 1359 cases.add(new SwitchCaseInfo(buildCondition, buildBody)); |
| 1210 Element error = helpers.fallThroughError; | 1360 } |
| 1211 irBuilder.buildSimpleSwitch(target, value, cases, defaultCase, error, | 1361 |
| 1212 sourceInformationBuilder.buildGeneric(node)); | 1362 // A loop with a simple switch in the body. |
| 1363 IrBuilder whileBuilder = irBuilder.makeDelimitedBuilder(); | |
| 1364 whileBuilder.buildWhile( | |
| 1365 buildCondition: (IrBuilder builder) { | |
| 1366 ir.Primitive condition = builder.buildIdentical( | |
| 1367 builder.environment.index2value[stateIndex], initial); | |
| 1368 return builder.buildNegation(condition); | |
| 1369 }, | |
| 1370 buildBody: (IrBuilder builder) { | |
| 1371 builder.buildSimpleSwitch(loop, cases, null); | |
| 1372 }); | |
| 1373 // Jump to the exit continuation. This jump is the body of the loop exit | |
| 1374 // continuation, so the loop exit continuation can be eta-reduced. The | |
| 1375 // jump is here for simplicity because `buildWhile` does not expose the | |
| 1376 // loop's exit continuation directly and has already emitted all jumps | |
| 1377 // to it anyway. | |
| 1378 whileBuilder.jumpTo(exit); | |
| 1379 irBuilder.add(new ir.LetCont(exit.continuation, whileBuilder.root)); | |
| 1380 irBuilder.environment = exit.environment; | |
| 1381 irBuilder.environment.discard(1); // Discard the state variable. | |
| 1382 irBuilder.state.breakCollectors.removeLast(); | |
| 1383 irBuilder.state.continueCollectors.length -= continueTargets.length; | |
| 1213 } | 1384 } |
| 1214 | 1385 |
| 1215 visitTryStatement(ast.TryStatement node) { | 1386 visitTryStatement(ast.TryStatement node) { |
| 1216 List<CatchClauseInfo> catchClauseInfos = <CatchClauseInfo>[]; | 1387 List<CatchClauseInfo> catchClauseInfos = <CatchClauseInfo>[]; |
| 1217 for (ast.CatchBlock catchClause in node.catchBlocks.nodes) { | 1388 for (ast.CatchBlock catchClause in node.catchBlocks.nodes) { |
| 1218 LocalVariableElement exceptionVariable; | 1389 LocalVariableElement exceptionVariable; |
| 1219 if (catchClause.exception != null) { | 1390 if (catchClause.exception != null) { |
| 1220 exceptionVariable = elements[catchClause.exception]; | 1391 exceptionVariable = elements[catchClause.exception]; |
| 1221 } | 1392 } |
| 1222 LocalVariableElement stackTraceVariable; | 1393 LocalVariableElement stackTraceVariable; |
| (...skipping 2326 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3549 } | 3720 } |
| 3550 | 3721 |
| 3551 Element get closureConverter { | 3722 Element get closureConverter { |
| 3552 return _backend.helpers.closureConverter; | 3723 return _backend.helpers.closureConverter; |
| 3553 } | 3724 } |
| 3554 | 3725 |
| 3555 void addNativeMethod(FunctionElement function) { | 3726 void addNativeMethod(FunctionElement function) { |
| 3556 _backend.emitter.nativeEmitter.nativeMethods.add(function); | 3727 _backend.emitter.nativeEmitter.nativeMethods.add(function); |
| 3557 } | 3728 } |
| 3558 } | 3729 } |
| OLD | NEW |