| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 part of ssa; | 5 part of ssa; |
| 6 | 6 |
| 7 abstract class HVisitor<R> { | 7 abstract class HVisitor<R> { |
| 8 R visitAdd(HAdd node); | 8 R visitAdd(HAdd node); |
| 9 R visitBailoutTarget(HBailoutTarget node); | 9 R visitBailoutTarget(HBailoutTarget node); |
| 10 R visitBitAnd(HBitAnd node); | 10 R visitBitAnd(HBitAnd node); |
| (...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 254 } | 254 } |
| 255 | 255 |
| 256 visitInstruction(HInstruction instruction) {} | 256 visitInstruction(HInstruction instruction) {} |
| 257 | 257 |
| 258 visitBinaryArithmetic(HBinaryArithmetic node) => visitInvokeBinary(node); | 258 visitBinaryArithmetic(HBinaryArithmetic node) => visitInvokeBinary(node); |
| 259 visitBinaryBitOp(HBinaryBitOp node) => visitBinaryArithmetic(node); | 259 visitBinaryBitOp(HBinaryBitOp node) => visitBinaryArithmetic(node); |
| 260 visitInvoke(HInvoke node) => visitInstruction(node); | 260 visitInvoke(HInvoke node) => visitInstruction(node); |
| 261 visitInvokeBinary(HInvokeBinary node) => visitInvokeStatic(node); | 261 visitInvokeBinary(HInvokeBinary node) => visitInvokeStatic(node); |
| 262 visitInvokeDynamic(HInvokeDynamic node) => visitInvoke(node); | 262 visitInvokeDynamic(HInvokeDynamic node) => visitInvoke(node); |
| 263 visitInvokeDynamicField(HInvokeDynamicField node) => visitInvokeDynamic(node); | 263 visitInvokeDynamicField(HInvokeDynamicField node) => visitInvokeDynamic(node); |
| 264 visitInvokeUnary(HInvokeUnary node) => visitInvokeStatic(node); | 264 visitInvokeUnary(HInvokeUnary node) => visitInstruction(node); |
| 265 visitConditionalBranch(HConditionalBranch node) => visitControlFlow(node); | 265 visitConditionalBranch(HConditionalBranch node) => visitControlFlow(node); |
| 266 visitControlFlow(HControlFlow node) => visitInstruction(node); | 266 visitControlFlow(HControlFlow node) => visitInstruction(node); |
| 267 visitFieldAccess(HFieldAccess node) => visitInstruction(node); | 267 visitFieldAccess(HFieldAccess node) => visitInstruction(node); |
| 268 visitRelational(HRelational node) => visitInvokeBinary(node); | 268 visitRelational(HRelational node) => visitInvokeBinary(node); |
| 269 | 269 |
| 270 visitAdd(HAdd node) => visitBinaryArithmetic(node); | 270 visitAdd(HAdd node) => visitBinaryArithmetic(node); |
| 271 visitBailoutTarget(HBailoutTarget node) => visitInstruction(node); | 271 visitBailoutTarget(HBailoutTarget node) => visitInstruction(node); |
| 272 visitBitAnd(HBitAnd node) => visitBinaryBitOp(node); | 272 visitBitAnd(HBitAnd node) => visitBinaryBitOp(node); |
| 273 visitBitNot(HBitNot node) => visitInvokeUnary(node); | 273 visitBitNot(HBitNot node) => visitInvokeUnary(node); |
| 274 visitBitOr(HBitOr node) => visitBinaryBitOp(node); | 274 visitBitOr(HBitOr node) => visitBinaryBitOp(node); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 286 visitFieldGet(HFieldGet node) => visitFieldAccess(node); | 286 visitFieldGet(HFieldGet node) => visitFieldAccess(node); |
| 287 visitFieldSet(HFieldSet node) => visitFieldAccess(node); | 287 visitFieldSet(HFieldSet node) => visitFieldAccess(node); |
| 288 visitForeign(HForeign node) => visitInstruction(node); | 288 visitForeign(HForeign node) => visitInstruction(node); |
| 289 visitForeignNew(HForeignNew node) => visitForeign(node); | 289 visitForeignNew(HForeignNew node) => visitForeign(node); |
| 290 visitGoto(HGoto node) => visitControlFlow(node); | 290 visitGoto(HGoto node) => visitControlFlow(node); |
| 291 visitGreater(HGreater node) => visitRelational(node); | 291 visitGreater(HGreater node) => visitRelational(node); |
| 292 visitGreaterEqual(HGreaterEqual node) => visitRelational(node); | 292 visitGreaterEqual(HGreaterEqual node) => visitRelational(node); |
| 293 visitIdentity(HIdentity node) => visitRelational(node); | 293 visitIdentity(HIdentity node) => visitRelational(node); |
| 294 visitIf(HIf node) => visitConditionalBranch(node); | 294 visitIf(HIf node) => visitConditionalBranch(node); |
| 295 visitIndex(HIndex node) => visitInstruction(node); | 295 visitIndex(HIndex node) => visitInstruction(node); |
| 296 visitIndexAssign(HIndexAssign node) => visitInvokeStatic(node); | 296 visitIndexAssign(HIndexAssign node) => visitInstruction(node); |
| 297 visitIntegerCheck(HIntegerCheck node) => visitCheck(node); | 297 visitIntegerCheck(HIntegerCheck node) => visitCheck(node); |
| 298 visitInterceptor(HInterceptor node) => visitInstruction(node); | 298 visitInterceptor(HInterceptor node) => visitInstruction(node); |
| 299 visitInvokeClosure(HInvokeClosure node) | 299 visitInvokeClosure(HInvokeClosure node) |
| 300 => visitInvokeDynamic(node); | 300 => visitInvokeDynamic(node); |
| 301 visitInvokeDynamicMethod(HInvokeDynamicMethod node) | 301 visitInvokeDynamicMethod(HInvokeDynamicMethod node) |
| 302 => visitInvokeDynamic(node); | 302 => visitInvokeDynamic(node); |
| 303 visitInvokeDynamicGetter(HInvokeDynamicGetter node) | 303 visitInvokeDynamicGetter(HInvokeDynamicGetter node) |
| 304 => visitInvokeDynamicField(node); | 304 => visitInvokeDynamicField(node); |
| 305 visitInvokeDynamicSetter(HInvokeDynamicSetter node) | 305 visitInvokeDynamicSetter(HInvokeDynamicSetter node) |
| 306 => visitInvokeDynamicField(node); | 306 => visitInvokeDynamicField(node); |
| (...skipping 1015 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1322 | 1322 |
| 1323 class HInvokeDynamicMethod extends HInvokeDynamic { | 1323 class HInvokeDynamicMethod extends HInvokeDynamic { |
| 1324 HInvokeDynamicMethod(Selector selector, List<HInstruction> inputs) | 1324 HInvokeDynamicMethod(Selector selector, List<HInstruction> inputs) |
| 1325 : super(selector, null, inputs); | 1325 : super(selector, null, inputs); |
| 1326 String toString() => 'invoke dynamic method: $selector'; | 1326 String toString() => 'invoke dynamic method: $selector'; |
| 1327 accept(HVisitor visitor) => visitor.visitInvokeDynamicMethod(this); | 1327 accept(HVisitor visitor) => visitor.visitInvokeDynamicMethod(this); |
| 1328 | 1328 |
| 1329 bool isIndexOperatorOnIndexablePrimitive(HTypeMap types) { | 1329 bool isIndexOperatorOnIndexablePrimitive(HTypeMap types) { |
| 1330 return isInterceptorCall | 1330 return isInterceptorCall |
| 1331 && selector.kind == SelectorKind.INDEX | 1331 && selector.kind == SelectorKind.INDEX |
| 1332 && selector.name == const SourceString('[]') |
| 1332 && inputs[1].isIndexablePrimitive(types); | 1333 && inputs[1].isIndexablePrimitive(types); |
| 1333 } | 1334 } |
| 1334 | 1335 |
| 1335 HType computeDesiredTypeForInput(HInstruction input, | 1336 HType computeDesiredTypeForInput(HInstruction input, |
| 1336 HTypeMap types, | 1337 HTypeMap types, |
| 1337 Compiler compiler) { | 1338 Compiler compiler) { |
| 1338 // TODO(ngeoffray): Move this logic into a different class that | 1339 // TODO(ngeoffray): Move this logic into a different class that |
| 1339 // will know what type it wants for a given selector. | 1340 // will know what type it wants for a given selector. |
| 1340 if (selector.kind != SelectorKind.INDEX) return HType.UNKNOWN; | |
| 1341 if (!isInterceptorCall) return HType.UNKNOWN; | 1341 if (!isInterceptorCall) return HType.UNKNOWN; |
| 1342 | 1342 |
| 1343 HInstruction index = inputs[2]; | 1343 if (selector.kind == SelectorKind.INDEX) { |
| 1344 if (input == inputs[1] && | 1344 HInstruction index = inputs[2]; |
| 1345 (index.isTypeUnknown(types) || index.isNumber(types))) { | 1345 if (input == inputs[1] && |
| 1346 return HType.INDEXABLE_PRIMITIVE; | 1346 (index.isTypeUnknown(types) || index.isNumber(types))) { |
| 1347 return selector.name == const SourceString('[]') |
| 1348 ? HType.INDEXABLE_PRIMITIVE |
| 1349 : HType.MUTABLE_ARRAY; |
| 1350 } |
| 1351 // The index should be an int when the receiver is a string or array. |
| 1352 // However it turns out that inserting an integer check in the optimized |
| 1353 // version is cheaper than having another bailout case. This is true, |
| 1354 // because the integer check will simply throw if it fails. |
| 1355 return HType.UNKNOWN; |
| 1356 } else if (selector.kind == SelectorKind.OPERATOR) { |
| 1357 HType propagatedType = types[this]; |
| 1358 if (selector.name == const SourceString('-') && input == inputs[1]) { |
| 1359 // If the outgoing type should be a number (integer, double or both) we |
| 1360 // want the outgoing type to be the input too. |
| 1361 // If we don't know the outgoing type we try to make it a number. |
| 1362 if (propagatedType.isNumber()) return propagatedType; |
| 1363 if (propagatedType.isUnknown()) return HType.NUMBER; |
| 1364 } else if (selector.name == const SourceString('~') |
| 1365 && input == inputs[1]) { |
| 1366 if (propagatedType.isUnknown() || propagatedType.isNumber()) { |
| 1367 return HType.INTEGER; |
| 1368 } |
| 1369 } |
| 1370 return HType.UNKNOWN; |
| 1347 } | 1371 } |
| 1348 // The index should be an int when the receiver is a string or array. | 1372 return HType.UNKNOWN; |
| 1349 // However it turns out that inserting an integer check in the optimized | 1373 } |
| 1350 // version is cheaper than having another bailout case. This is true, | 1374 |
| 1351 // because the integer check will simply throw if it fails. | 1375 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) { |
| 1376 // TODO(ngeoffray): Move this logic into a different class that |
| 1377 // will know what type it has for a given selector. |
| 1378 if (!isInterceptorCall) return HType.UNKNOWN; |
| 1379 |
| 1380 if (selector.kind == SelectorKind.OPERATOR) { |
| 1381 if (selector.name == const SourceString('-')) { |
| 1382 HType operandType = types[inputs[1]]; |
| 1383 if (operandType.isNumber()) return operandType; |
| 1384 } else if (selector.name == const SourceString('~')) { |
| 1385 // All bitwise operations on primitive types either produce an |
| 1386 // integer or throw an error. |
| 1387 if (inputs[1].isPrimitive(types)) return HType.INTEGER; |
| 1388 } |
| 1389 } |
| 1352 return HType.UNKNOWN; | 1390 return HType.UNKNOWN; |
| 1353 } | 1391 } |
| 1354 } | 1392 } |
| 1355 | 1393 |
| 1356 abstract class HInvokeDynamicField extends HInvokeDynamic { | 1394 abstract class HInvokeDynamicField extends HInvokeDynamic { |
| 1357 final bool isSideEffectFree; | 1395 final bool isSideEffectFree; |
| 1358 HInvokeDynamicField( | 1396 HInvokeDynamicField( |
| 1359 Selector selector, Element element, List<HInstruction> inputs, | 1397 Selector selector, Element element, List<HInstruction> inputs, |
| 1360 this.isSideEffectFree) | 1398 this.isSideEffectFree) |
| 1361 : super(selector, element, inputs); | 1399 : super(selector, element, inputs); |
| (...skipping 471 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1833 : super(target, left, right); | 1871 : super(target, left, right); |
| 1834 accept(HVisitor visitor) => visitor.visitBitXor(this); | 1872 accept(HVisitor visitor) => visitor.visitBitXor(this); |
| 1835 | 1873 |
| 1836 BinaryOperation operation(ConstantSystem constantSystem) | 1874 BinaryOperation operation(ConstantSystem constantSystem) |
| 1837 => constantSystem.bitXor; | 1875 => constantSystem.bitXor; |
| 1838 int typeCode() => HInstruction.BIT_XOR_TYPECODE; | 1876 int typeCode() => HInstruction.BIT_XOR_TYPECODE; |
| 1839 bool typeEquals(other) => other is HBitXor; | 1877 bool typeEquals(other) => other is HBitXor; |
| 1840 bool dataEquals(HInstruction other) => true; | 1878 bool dataEquals(HInstruction other) => true; |
| 1841 } | 1879 } |
| 1842 | 1880 |
| 1843 abstract class HInvokeUnary extends HInvokeStatic { | 1881 abstract class HInvokeUnary extends HInstruction { |
| 1844 HInvokeUnary(HStatic target, HInstruction input) | 1882 HInvokeUnary(HInstruction input) : super(<HInstruction>[input]); |
| 1845 : super(<HInstruction>[target, input]); | |
| 1846 | 1883 |
| 1847 HInstruction get operand => inputs[1]; | 1884 HInstruction get operand => inputs[0]; |
| 1848 | 1885 |
| 1849 void prepareGvn(HTypeMap types) { | 1886 void prepareGvn(HTypeMap types) { |
| 1850 clearAllSideEffects(); | 1887 clearAllSideEffects(); |
| 1851 // A unary arithmetic expression can take part in global value | 1888 setUseGvn(); |
| 1852 // numbering and does not have any side-effects if its input is a | |
| 1853 // number. | |
| 1854 if (isBuiltin(types)) { | |
| 1855 setUseGvn(); | |
| 1856 } else { | |
| 1857 setAllSideEffects(); | |
| 1858 } | |
| 1859 } | 1889 } |
| 1860 | 1890 |
| 1861 bool isBuiltin(HTypeMap types) => operand.isNumber(types); | |
| 1862 | |
| 1863 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) { | |
| 1864 HType operandType = types[operand]; | |
| 1865 if (operandType.isNumber()) return operandType; | |
| 1866 return HType.UNKNOWN; | |
| 1867 } | |
| 1868 | |
| 1869 HType computeDesiredTypeForNonTargetInput(HInstruction input, | |
| 1870 HTypeMap types, | |
| 1871 Compiler compiler) { | |
| 1872 HType propagatedType = types[this]; | |
| 1873 // If the outgoing type should be a number (integer, double or both) we | |
| 1874 // want the outgoing type to be the input too. | |
| 1875 // If we don't know the outgoing type we try to make it a number. | |
| 1876 if (propagatedType.isNumber()) return propagatedType; | |
| 1877 if (propagatedType.isUnknown()) return HType.NUMBER; | |
| 1878 return HType.UNKNOWN; | |
| 1879 } | |
| 1880 | |
| 1881 HType computeLikelyType(HTypeMap types, Compiler compiler) => HType.NUMBER; | |
| 1882 | |
| 1883 UnaryOperation operation(ConstantSystem constantSystem); | 1891 UnaryOperation operation(ConstantSystem constantSystem); |
| 1884 } | 1892 } |
| 1885 | 1893 |
| 1886 class HNegate extends HInvokeUnary { | 1894 class HNegate extends HInvokeUnary { |
| 1887 HNegate(HStatic target, HInstruction input) : super(target, input); | 1895 HNegate(HInstruction input) : super(input); |
| 1888 accept(HVisitor visitor) => visitor.visitNegate(this); | 1896 accept(HVisitor visitor) => visitor.visitNegate(this); |
| 1889 | 1897 |
| 1898 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) { |
| 1899 return types[operand]; |
| 1900 } |
| 1901 |
| 1890 UnaryOperation operation(ConstantSystem constantSystem) | 1902 UnaryOperation operation(ConstantSystem constantSystem) |
| 1891 => constantSystem.negate; | 1903 => constantSystem.negate; |
| 1892 int typeCode() => HInstruction.NEGATE_TYPECODE; | 1904 int typeCode() => HInstruction.NEGATE_TYPECODE; |
| 1893 bool typeEquals(other) => other is HNegate; | 1905 bool typeEquals(other) => other is HNegate; |
| 1894 bool dataEquals(HInstruction other) => true; | 1906 bool dataEquals(HInstruction other) => true; |
| 1895 } | 1907 } |
| 1896 | 1908 |
| 1897 class HBitNot extends HInvokeUnary { | 1909 class HBitNot extends HInvokeUnary { |
| 1898 HBitNot(HStatic target, HInstruction input) : super(target, input); | 1910 HBitNot(HInstruction input) : super(input); |
| 1899 accept(HVisitor visitor) => visitor.visitBitNot(this); | 1911 accept(HVisitor visitor) => visitor.visitBitNot(this); |
| 1900 | 1912 |
| 1901 HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) { | 1913 HType get guaranteedType => HType.INTEGER; |
| 1902 // All bitwise operations on primitive types either produce an | |
| 1903 // integer or throw an error. | |
| 1904 if (operand.isPrimitive(types)) return HType.INTEGER; | |
| 1905 return HType.UNKNOWN; | |
| 1906 } | |
| 1907 | |
| 1908 HType computeDesiredTypeForNonTargetInput(HInstruction input, | |
| 1909 HTypeMap types, | |
| 1910 Compiler compiler) { | |
| 1911 HType propagatedType = types[this]; | |
| 1912 // Bit operations only work on integers. If there is no desired output | |
| 1913 // type or if it as a number we want to get an integer as input. | |
| 1914 if (propagatedType.isUnknown() || propagatedType.isNumber()) { | |
| 1915 return HType.INTEGER; | |
| 1916 } | |
| 1917 return HType.UNKNOWN; | |
| 1918 } | |
| 1919 | |
| 1920 UnaryOperation operation(ConstantSystem constantSystem) | 1914 UnaryOperation operation(ConstantSystem constantSystem) |
| 1921 => constantSystem.bitNot; | 1915 => constantSystem.bitNot; |
| 1922 int typeCode() => HInstruction.BIT_NOT_TYPECODE; | 1916 int typeCode() => HInstruction.BIT_NOT_TYPECODE; |
| 1923 bool typeEquals(other) => other is HBitNot; | 1917 bool typeEquals(other) => other is HBitNot; |
| 1924 bool dataEquals(HInstruction other) => true; | 1918 bool dataEquals(HInstruction other) => true; |
| 1925 } | 1919 } |
| 1926 | 1920 |
| 1927 class HExit extends HControlFlow { | 1921 class HExit extends HControlFlow { |
| 1928 HExit() : super(const <HInstruction>[]); | 1922 HExit() : super(const <HInstruction>[]); |
| 1929 toString() => 'exit'; | 1923 toString() => 'exit'; |
| (...skipping 548 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2478 } | 2472 } |
| 2479 | 2473 |
| 2480 HInstruction get receiver => inputs[0]; | 2474 HInstruction get receiver => inputs[0]; |
| 2481 HInstruction get index => inputs[1]; | 2475 HInstruction get index => inputs[1]; |
| 2482 | 2476 |
| 2483 int typeCode() => HInstruction.INDEX_TYPECODE; | 2477 int typeCode() => HInstruction.INDEX_TYPECODE; |
| 2484 bool typeEquals(HInstruction other) => other is HIndex; | 2478 bool typeEquals(HInstruction other) => other is HIndex; |
| 2485 bool dataEquals(HIndex other) => true; | 2479 bool dataEquals(HIndex other) => true; |
| 2486 } | 2480 } |
| 2487 | 2481 |
| 2488 class HIndexAssign extends HInvokeStatic { | 2482 class HIndexAssign extends HInstruction { |
| 2489 HIndexAssign(HStatic target, | 2483 HIndexAssign(HInstruction receiver, |
| 2490 HInstruction receiver, | |
| 2491 HInstruction index, | 2484 HInstruction index, |
| 2492 HInstruction value) | 2485 HInstruction value) |
| 2493 : super(<HInstruction>[target, receiver, index, value]); | 2486 : super(<HInstruction>[receiver, index, value]); |
| 2494 toString() => 'index assign operator'; | 2487 String toString() => 'index assign operator'; |
| 2495 accept(HVisitor visitor) => visitor.visitIndexAssign(this); | 2488 accept(HVisitor visitor) => visitor.visitIndexAssign(this); |
| 2496 | 2489 |
| 2497 HInstruction get receiver => inputs[1]; | 2490 HInstruction get receiver => inputs[0]; |
| 2498 HInstruction get index => inputs[2]; | 2491 HInstruction get index => inputs[1]; |
| 2499 HInstruction get value => inputs[3]; | 2492 HInstruction get value => inputs[2]; |
| 2500 | 2493 |
| 2501 void prepareGvn(HTypeMap types) { | 2494 void prepareGvn(HTypeMap types) { |
| 2502 clearAllSideEffects(); | 2495 clearAllSideEffects(); |
| 2503 if (isBuiltin(types)) { | 2496 setChangesIndex(); |
| 2504 setChangesIndex(); | |
| 2505 } else { | |
| 2506 setAllSideEffects(); | |
| 2507 } | |
| 2508 } | 2497 } |
| 2509 | |
| 2510 // Note, that we don't have a computeTypeFromInputTypes, since [HIndexAssign] | |
| 2511 // is never used as input. | |
| 2512 | |
| 2513 HType computeDesiredTypeForNonTargetInput(HInstruction input, | |
| 2514 HTypeMap types, | |
| 2515 Compiler compiler) { | |
| 2516 if (input == receiver && | |
| 2517 (index.isTypeUnknown(types) || index.isNumber(types))) { | |
| 2518 return HType.MUTABLE_ARRAY; | |
| 2519 } | |
| 2520 // The index should be an int when the receiver is a string or array. | |
| 2521 // However it turns out that inserting an integer check in the optimized | |
| 2522 // version is cheaper than having another bailout case. This is true, | |
| 2523 // because the integer check will simply throw if it fails. | |
| 2524 return HType.UNKNOWN; | |
| 2525 } | |
| 2526 | |
| 2527 bool isBuiltin(HTypeMap types) | |
| 2528 => receiver.isMutableArray(types) && index.isInteger(types); | |
| 2529 bool isJsStatement(HTypeMap types) => !isBuiltin(types); | |
| 2530 } | 2498 } |
| 2531 | 2499 |
| 2532 class HIs extends HInstruction { | 2500 class HIs extends HInstruction { |
| 2533 final DartType typeExpression; | 2501 final DartType typeExpression; |
| 2534 final bool nullOk; | 2502 final bool nullOk; |
| 2535 | 2503 |
| 2536 HIs.withArgumentChecks(this.typeExpression, | 2504 HIs.withArgumentChecks(this.typeExpression, |
| 2537 HInstruction expression, | 2505 HInstruction expression, |
| 2538 List<HInstruction> checks, | 2506 List<HInstruction> checks, |
| 2539 [this.nullOk = false]) | 2507 [this.nullOk = false]) |
| (...skipping 407 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2947 HBasicBlock get start => expression.start; | 2915 HBasicBlock get start => expression.start; |
| 2948 HBasicBlock get end { | 2916 HBasicBlock get end { |
| 2949 // We don't create a switch block if there are no cases. | 2917 // We don't create a switch block if there are no cases. |
| 2950 assert(!statements.isEmpty); | 2918 assert(!statements.isEmpty); |
| 2951 return statements.last.end; | 2919 return statements.last.end; |
| 2952 } | 2920 } |
| 2953 | 2921 |
| 2954 bool accept(HStatementInformationVisitor visitor) => | 2922 bool accept(HStatementInformationVisitor visitor) => |
| 2955 visitor.visitSwitchInfo(this); | 2923 visitor.visitSwitchInfo(this); |
| 2956 } | 2924 } |
| OLD | NEW |