Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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 import 'package:kernel/ast.dart' as ir; | 5 import 'package:kernel/ast.dart' as ir; |
| 6 | 6 |
| 7 import '../common.dart'; | 7 import '../common.dart'; |
| 8 import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem; | 8 import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem; |
| 9 import '../common/names.dart'; | 9 import '../common/names.dart'; |
| 10 import '../common/tasks.dart' show CompilerTask; | 10 import '../common/tasks.dart' show CompilerTask; |
| (...skipping 1053 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1064 @override | 1064 @override |
| 1065 void visitLet(ir.Let let) { | 1065 void visitLet(ir.Let let) { |
| 1066 ir.VariableDeclaration variable = let.variable; | 1066 ir.VariableDeclaration variable = let.variable; |
| 1067 variable.initializer.accept(this); | 1067 variable.initializer.accept(this); |
| 1068 HInstruction initializedValue = pop(); | 1068 HInstruction initializedValue = pop(); |
| 1069 // TODO(sra): Apply inferred type information. | 1069 // TODO(sra): Apply inferred type information. |
| 1070 letBindings[variable] = initializedValue; | 1070 letBindings[variable] = initializedValue; |
| 1071 let.body.accept(this); | 1071 let.body.accept(this); |
| 1072 } | 1072 } |
| 1073 | 1073 |
| 1074 // TODO(het): Also extract type arguments | 1074 /// Extracts the list of instructions for the expressions in the list. |
| 1075 /// Extracts the list of instructions for the expressions in the arguments. | 1075 List<HInstruction> _visitList(List<ir.Expression> expressions) { |
| 1076 List<HInstruction> _visitArguments(ir.Arguments arguments) { | |
| 1077 List<HInstruction> result = <HInstruction>[]; | 1076 List<HInstruction> result = <HInstruction>[]; |
| 1078 | 1077 for (ir.Expression expression in expressions) { |
| 1079 for (ir.Expression argument in arguments.positional) { | 1078 expression.accept(this); |
| 1080 argument.accept(this); | |
| 1081 result.add(pop()); | 1079 result.add(pop()); |
| 1082 } | 1080 } |
| 1083 for (ir.NamedExpression argument in arguments.named) { | |
| 1084 argument.value.accept(this); | |
| 1085 result.add(pop()); | |
| 1086 } | |
| 1087 | |
| 1088 return result; | 1081 return result; |
| 1089 } | 1082 } |
| 1090 | 1083 |
| 1084 /// Builds the list of instructions for the expressions in the arguments to a | |
| 1085 /// dynamic target (member function). | |
|
Siggi Cherem (dart-lang)
2016/12/06 18:04:15
now might be a good opportunity to document why th
sra1
2016/12/06 18:27:37
Done.
| |
| 1086 List<HInstruction> _visitArgumentsForDynamicTarget( | |
| 1087 Selector selector, ir.Arguments arguments) { | |
| 1088 List<HInstruction> values = _visitList(arguments.positional); | |
| 1089 | |
| 1090 if (arguments.named.isEmpty) return values; | |
| 1091 | |
| 1092 var namedValues = <String, HInstruction>{}; | |
| 1093 for (ir.NamedExpression argument in arguments.named) { | |
| 1094 argument.value.accept(this); | |
| 1095 namedValues[argument.name] = pop(); | |
| 1096 } | |
| 1097 for (String name in selector.callStructure.getOrderedNamedArguments()) { | |
| 1098 values.add(namedValues[name]); | |
| 1099 } | |
| 1100 | |
| 1101 return values; | |
| 1102 } | |
| 1103 | |
| 1104 /// Build argument list in canonical order for a static [target], including | |
| 1105 /// defaulted arguments. | |
| 1106 List<HInstruction> _visitArgumentsForStaticTarget( | |
| 1107 ir.FunctionNode target, ir.Arguments arguments) { | |
| 1108 // Visit arguments in source order, then re-order and fill in defaults. | |
| 1109 var values = _visitList(arguments.positional); | |
| 1110 | |
| 1111 while (values.length < target.positionalParameters.length) { | |
| 1112 ir.VariableDeclaration parameter = | |
| 1113 target.positionalParameters[values.length]; | |
| 1114 values.add(_defaultValueForParameter(parameter)); | |
| 1115 } | |
| 1116 | |
| 1117 if (arguments.named.isEmpty) return values; | |
| 1118 | |
| 1119 var namedValues = <String, HInstruction>{}; | |
| 1120 for (ir.NamedExpression argument in arguments.named) { | |
| 1121 argument.value.accept(this); | |
| 1122 namedValues[argument.name] = pop(); | |
| 1123 } | |
| 1124 | |
| 1125 // Visit named arguments in parameter-position order, selecting provided or | |
| 1126 // default value. | |
| 1127 // TODO(sra): Ensure the stored order is canonical so we don't have to | |
| 1128 // sort. The old builder uses CallStructure.makeArgumentList which depends | |
| 1129 // on the old element model. | |
| 1130 var namedParameters = target.namedParameters.toList() | |
| 1131 ..sort((ir.VariableDeclaration a, ir.VariableDeclaration b) => | |
| 1132 a.name.compareTo(b.name)); | |
| 1133 for (ir.VariableDeclaration parameter in namedParameters) { | |
| 1134 HInstruction value = namedValues[parameter.name]; | |
| 1135 if (value == null) { | |
| 1136 values.add(_defaultValueForParameter(parameter)); | |
| 1137 } else { | |
| 1138 values.add(value); | |
| 1139 namedValues.remove(parameter.name); | |
| 1140 } | |
| 1141 } | |
| 1142 assert(namedValues.isEmpty); | |
| 1143 | |
| 1144 return values; | |
| 1145 } | |
| 1146 | |
| 1147 HInstruction _defaultValueForParameter(ir.VariableDeclaration parameter) { | |
| 1148 ir.Expression initializer = parameter.initializer; | |
| 1149 if (initializer == null) return graph.addConstantNull(compiler); | |
| 1150 // TODO(sra): Evaluate constant in ir.Node domain. | |
|
Siggi Cherem (dart-lang)
2016/12/06 18:04:15
when we do - I'd like to request from kernel-ir so
sra1
2016/12/06 18:27:37
Acknowledged.
| |
| 1151 ConstantValue constant = | |
| 1152 astAdapter.getConstantForParameterDefaultValue(initializer); | |
| 1153 if (constant == null) return graph.addConstantNull(compiler); | |
| 1154 return graph.addConstant(constant, compiler); | |
| 1155 } | |
| 1156 | |
| 1091 @override | 1157 @override |
| 1092 void visitStaticInvocation(ir.StaticInvocation invocation) { | 1158 void visitStaticInvocation(ir.StaticInvocation invocation) { |
| 1093 ir.Procedure target = invocation.target; | 1159 ir.Procedure target = invocation.target; |
| 1094 if (astAdapter.isInForeignLibrary(target)) { | 1160 if (astAdapter.isInForeignLibrary(target)) { |
| 1095 handleInvokeStaticForeign(invocation, target); | 1161 handleInvokeStaticForeign(invocation, target); |
| 1096 return; | 1162 return; |
| 1097 } | 1163 } |
| 1098 TypeMask typeMask = astAdapter.returnTypeOf(target); | 1164 TypeMask typeMask = astAdapter.returnTypeOf(target); |
| 1099 | 1165 |
| 1100 List<HInstruction> arguments = _visitArguments(invocation.arguments); | 1166 // TODO(sra): For JS interop external functions, use a different function to |
| 1167 // build arguments. | |
| 1168 List<HInstruction> arguments = | |
| 1169 _visitArgumentsForStaticTarget(target.function, invocation.arguments); | |
| 1101 | 1170 |
| 1102 _pushStaticInvocation(target, arguments, typeMask); | 1171 _pushStaticInvocation(target, arguments, typeMask); |
| 1103 } | 1172 } |
| 1104 | 1173 |
| 1105 void handleInvokeStaticForeign( | 1174 void handleInvokeStaticForeign( |
| 1106 ir.StaticInvocation invocation, ir.Procedure target) { | 1175 ir.StaticInvocation invocation, ir.Procedure target) { |
| 1107 String name = target.name.name; | 1176 String name = target.name.name; |
| 1108 if (name == 'JS') { | 1177 if (name == 'JS') { |
| 1109 handleForeignJs(invocation); | 1178 handleForeignJs(invocation); |
| 1110 } else if (name == 'JS_CURRENT_ISOLATE_CONTEXT') { | 1179 } else if (name == 'JS_CURRENT_ISOLATE_CONTEXT') { |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1237 _pushStaticInvocation(target, <HInstruction>[], backend.dynamicType); | 1306 _pushStaticInvocation(target, <HInstruction>[], backend.dynamicType); |
| 1238 } | 1307 } |
| 1239 } | 1308 } |
| 1240 | 1309 |
| 1241 void handleForeignJsCallInIsolate(ir.StaticInvocation invocation) { | 1310 void handleForeignJsCallInIsolate(ir.StaticInvocation invocation) { |
| 1242 if (_unexpectedForeignArguments(invocation, 2, 2)) { | 1311 if (_unexpectedForeignArguments(invocation, 2, 2)) { |
| 1243 stack.add(graph.addConstantNull(compiler)); // Result expected on stack. | 1312 stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
| 1244 return; | 1313 return; |
| 1245 } | 1314 } |
| 1246 | 1315 |
| 1247 List<HInstruction> inputs = _visitArguments(invocation.arguments); | 1316 List<HInstruction> inputs = _visitList(invocation.arguments.positional); |
|
Johnni Winther
2016/12/06 08:36:37
I'd prefer calling [_visitArgumentsForStaticTarget
sra1
2016/12/06 18:27:37
The _unexpectedForeignArguments call ensures there
| |
| 1248 | 1317 |
| 1249 if (!compiler.hasIsolateSupport) { | 1318 if (!compiler.hasIsolateSupport) { |
| 1250 // If the isolate library is not used, we ignore the isolate argument and | 1319 // If the isolate library is not used, we ignore the isolate argument and |
| 1251 // just invoke the closure. | 1320 // just invoke the closure. |
| 1252 push(new HInvokeClosure(new Selector.callClosure(0), | 1321 push(new HInvokeClosure(new Selector.callClosure(0), |
| 1253 <HInstruction>[inputs[1]], backend.dynamicType)); | 1322 <HInstruction>[inputs[1]], backend.dynamicType)); |
| 1254 } else { | 1323 } else { |
| 1255 // Call a helper method from the isolate library. | 1324 // Call a helper method from the isolate library. |
| 1256 ir.Procedure callInIsolate = astAdapter.callInIsolate; | 1325 ir.Procedure callInIsolate = astAdapter.callInIsolate; |
| 1257 if (callInIsolate == null) { | 1326 if (callInIsolate == null) { |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1307 stack.add(graph.addConstantNull(compiler)); // Result expected on stack. | 1376 stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
| 1308 return; | 1377 return; |
| 1309 } | 1378 } |
| 1310 | 1379 |
| 1311 void handleForeignJsSetStaticState(ir.StaticInvocation invocation) { | 1380 void handleForeignJsSetStaticState(ir.StaticInvocation invocation) { |
| 1312 if (_unexpectedForeignArguments(invocation, 1, 1)) { | 1381 if (_unexpectedForeignArguments(invocation, 1, 1)) { |
| 1313 stack.add(graph.addConstantNull(compiler)); // Result expected on stack. | 1382 stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
| 1314 return; | 1383 return; |
| 1315 } | 1384 } |
| 1316 | 1385 |
| 1317 List<HInstruction> inputs = _visitArguments(invocation.arguments); | 1386 List<HInstruction> inputs = _visitList(invocation.arguments.positional); |
| 1318 | 1387 |
| 1319 String isolateName = backend.namer.staticStateHolder; | 1388 String isolateName = backend.namer.staticStateHolder; |
| 1320 SideEffects sideEffects = new SideEffects.empty(); | 1389 SideEffects sideEffects = new SideEffects.empty(); |
| 1321 sideEffects.setAllSideEffects(); | 1390 sideEffects.setAllSideEffects(); |
| 1322 push(new HForeignCode(js.js.parseForeignJS("$isolateName = #"), | 1391 push(new HForeignCode( |
| 1323 backend.dynamicType, inputs, | 1392 js.js.parseForeignJS("$isolateName = #"), backend.dynamicType, inputs, |
| 1324 nativeBehavior: native.NativeBehavior.CHANGES_OTHER, | 1393 nativeBehavior: native.NativeBehavior.CHANGES_OTHER, |
| 1325 effects: sideEffects)); | 1394 effects: sideEffects)); |
| 1326 } | 1395 } |
| 1327 | 1396 |
| 1328 void handleForeignJsGetStaticState(ir.StaticInvocation invocation) { | 1397 void handleForeignJsGetStaticState(ir.StaticInvocation invocation) { |
| 1329 if (_unexpectedForeignArguments(invocation, 0, 0)) { | 1398 if (_unexpectedForeignArguments(invocation, 0, 0)) { |
| 1330 stack.add(graph.addConstantNull(compiler)); // Result expected on stack. | 1399 stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
| 1331 return; | 1400 return; |
| 1332 } | 1401 } |
| 1333 | 1402 |
| (...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1511 isStatement: !nativeBehavior.codeTemplate.isExpression, | 1580 isStatement: !nativeBehavior.codeTemplate.isExpression, |
| 1512 effects: nativeBehavior.sideEffects, | 1581 effects: nativeBehavior.sideEffects, |
| 1513 nativeBehavior: nativeBehavior)..sourceInformation = sourceInformation); | 1582 nativeBehavior: nativeBehavior)..sourceInformation = sourceInformation); |
| 1514 } | 1583 } |
| 1515 | 1584 |
| 1516 void handleJsStringConcat(ir.StaticInvocation invocation) { | 1585 void handleJsStringConcat(ir.StaticInvocation invocation) { |
| 1517 if (_unexpectedForeignArguments(invocation, 2, 2)) { | 1586 if (_unexpectedForeignArguments(invocation, 2, 2)) { |
| 1518 stack.add(graph.addConstantNull(compiler)); // Result expected on stack. | 1587 stack.add(graph.addConstantNull(compiler)); // Result expected on stack. |
| 1519 return; | 1588 return; |
| 1520 } | 1589 } |
| 1521 List<HInstruction> inputs = _visitArguments(invocation.arguments); | 1590 List<HInstruction> inputs = _visitList(invocation.arguments.positional); |
| 1522 push(new HStringConcat(inputs[0], inputs[1], backend.stringType)); | 1591 push(new HStringConcat(inputs[0], inputs[1], backend.stringType)); |
| 1523 } | 1592 } |
| 1524 | 1593 |
| 1525 void _pushStaticInvocation( | 1594 void _pushStaticInvocation( |
| 1526 ir.Node target, List<HInstruction> arguments, TypeMask typeMask) { | 1595 ir.Node target, List<HInstruction> arguments, TypeMask typeMask) { |
| 1527 HInvokeStatic instruction = new HInvokeStatic( | 1596 HInvokeStatic instruction = new HInvokeStatic( |
| 1528 astAdapter.getMember(target), arguments, typeMask, | 1597 astAdapter.getMember(target), arguments, typeMask, |
| 1529 targetCanThrow: astAdapter.getCanThrow(target)); | 1598 targetCanThrow: astAdapter.getCanThrow(target)); |
| 1530 if (currentImplicitInstantiations.isNotEmpty) { | 1599 if (currentImplicitInstantiations.isNotEmpty) { |
| 1531 instruction.instantiatedTypes = | 1600 instruction.instantiatedTypes = |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1563 } | 1632 } |
| 1564 | 1633 |
| 1565 // TODO(het): Decide when to inline | 1634 // TODO(het): Decide when to inline |
| 1566 @override | 1635 @override |
| 1567 void visitMethodInvocation(ir.MethodInvocation invocation) { | 1636 void visitMethodInvocation(ir.MethodInvocation invocation) { |
| 1568 // Handle `x == null` specially. When these come from null-aware operators, | 1637 // Handle `x == null` specially. When these come from null-aware operators, |
| 1569 // there is no mapping in the astAdapter. | 1638 // there is no mapping in the astAdapter. |
| 1570 if (_handleEqualsNull(invocation)) return; | 1639 if (_handleEqualsNull(invocation)) return; |
| 1571 invocation.receiver.accept(this); | 1640 invocation.receiver.accept(this); |
| 1572 HInstruction receiver = pop(); | 1641 HInstruction receiver = pop(); |
| 1573 | 1642 Selector selector = astAdapter.getSelector(invocation); |
| 1574 _pushDynamicInvocation( | 1643 _pushDynamicInvocation( |
| 1575 invocation, | 1644 invocation, |
| 1576 astAdapter.typeOfInvocation(invocation), | 1645 astAdapter.typeOfInvocation(invocation), |
| 1577 <HInstruction>[receiver] | 1646 <HInstruction>[receiver] |
| 1578 ..addAll(_visitArguments(invocation.arguments))); | 1647 ..addAll( |
| 1648 _visitArgumentsForDynamicTarget(selector, invocation.arguments))); | |
| 1579 } | 1649 } |
| 1580 | 1650 |
| 1581 bool _handleEqualsNull(ir.MethodInvocation invocation) { | 1651 bool _handleEqualsNull(ir.MethodInvocation invocation) { |
| 1582 if (invocation.name.name == '==') { | 1652 if (invocation.name.name == '==') { |
| 1583 ir.Arguments arguments = invocation.arguments; | 1653 ir.Arguments arguments = invocation.arguments; |
| 1584 if (arguments.types.isEmpty && | 1654 if (arguments.types.isEmpty && |
| 1585 arguments.positional.length == 1 && | 1655 arguments.positional.length == 1 && |
| 1586 arguments.named.isEmpty) { | 1656 arguments.named.isEmpty) { |
| 1587 bool finish(ir.Expression comparand) { | 1657 bool finish(ir.Expression comparand) { |
| 1588 comparand.accept(this); | 1658 comparand.accept(this); |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 1609 static ir.Class _containingClass(ir.TreeNode node) { | 1679 static ir.Class _containingClass(ir.TreeNode node) { |
| 1610 while (node != null) { | 1680 while (node != null) { |
| 1611 if (node is ir.Class) return node; | 1681 if (node is ir.Class) return node; |
| 1612 node = node.parent; | 1682 node = node.parent; |
| 1613 } | 1683 } |
| 1614 return null; | 1684 return null; |
| 1615 } | 1685 } |
| 1616 | 1686 |
| 1617 @override | 1687 @override |
| 1618 void visitSuperMethodInvocation(ir.SuperMethodInvocation invocation) { | 1688 void visitSuperMethodInvocation(ir.SuperMethodInvocation invocation) { |
| 1619 List<HInstruction> arguments = _visitArguments(invocation.arguments); | 1689 Selector selector = astAdapter.getSelector(invocation); |
| 1690 List<HInstruction> arguments = _visitArgumentsForStaticTarget( | |
| 1691 invocation.interfaceTarget.function, invocation.arguments); | |
| 1620 HInstruction receiver = localsHandler.readThis(); | 1692 HInstruction receiver = localsHandler.readThis(); |
| 1621 Selector selector = astAdapter.getSelector(invocation); | |
| 1622 ir.Class surroundingClass = _containingClass(invocation); | 1693 ir.Class surroundingClass = _containingClass(invocation); |
| 1623 | 1694 |
| 1624 List<HInstruction> inputs = <HInstruction>[]; | 1695 List<HInstruction> inputs = <HInstruction>[]; |
| 1625 if (astAdapter.isIntercepted(invocation)) { | 1696 if (astAdapter.isIntercepted(invocation)) { |
| 1626 inputs.add(_interceptorFor(receiver)); | 1697 inputs.add(_interceptorFor(receiver)); |
| 1627 } | 1698 } |
| 1628 inputs.add(receiver); | 1699 inputs.add(receiver); |
| 1629 inputs.addAll(arguments); | 1700 inputs.addAll(arguments); |
| 1630 | 1701 |
| 1631 HInstruction instruction = new HInvokeSuper( | 1702 HInstruction instruction = new HInvokeSuper( |
| 1632 astAdapter.getMethod(invocation.interfaceTarget), | 1703 astAdapter.getMethod(invocation.interfaceTarget), |
| 1633 astAdapter.getClass(surroundingClass), | 1704 astAdapter.getClass(surroundingClass), |
| 1634 selector, | 1705 selector, |
| 1635 inputs, | 1706 inputs, |
| 1636 astAdapter.returnTypeOf(invocation.interfaceTarget), | 1707 astAdapter.returnTypeOf(invocation.interfaceTarget), |
| 1637 null, | 1708 null, |
| 1638 isSetter: selector.isSetter || selector.isIndexSet); | 1709 isSetter: selector.isSetter || selector.isIndexSet); |
| 1639 instruction.sideEffects = | 1710 instruction.sideEffects = |
| 1640 compiler.closedWorld.getSideEffectsOfSelector(selector, null); | 1711 compiler.closedWorld.getSideEffectsOfSelector(selector, null); |
| 1641 push(instruction); | 1712 push(instruction); |
| 1642 } | 1713 } |
| 1643 | 1714 |
| 1644 @override | 1715 @override |
| 1645 void visitConstructorInvocation(ir.ConstructorInvocation invocation) { | 1716 void visitConstructorInvocation(ir.ConstructorInvocation invocation) { |
| 1646 ir.Constructor target = invocation.target; | 1717 ir.Constructor target = invocation.target; |
| 1647 List<HInstruction> arguments = _visitArguments(invocation.arguments); | 1718 // TODO(sra): For JS-interop targets, process arguments differently. |
| 1719 List<HInstruction> arguments = | |
| 1720 _visitArgumentsForStaticTarget(target.function, invocation.arguments); | |
| 1648 TypeMask typeMask = new TypeMask.nonNullExact( | 1721 TypeMask typeMask = new TypeMask.nonNullExact( |
| 1649 astAdapter.getElement(target.enclosingClass), compiler.closedWorld); | 1722 astAdapter.getElement(target.enclosingClass), compiler.closedWorld); |
| 1650 _pushStaticInvocation(target, arguments, typeMask); | 1723 _pushStaticInvocation(target, arguments, typeMask); |
| 1651 } | 1724 } |
| 1652 | 1725 |
| 1653 @override | 1726 @override |
| 1654 void visitIsExpression(ir.IsExpression isExpression) { | 1727 void visitIsExpression(ir.IsExpression isExpression) { |
| 1655 isExpression.operand.accept(this); | 1728 isExpression.operand.accept(this); |
| 1656 HInstruction expression = pop(); | 1729 HInstruction expression = pop(); |
| 1657 | 1730 |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1725 push(new HNot(popBoolified(), backend.boolType)); | 1798 push(new HNot(popBoolified(), backend.boolType)); |
| 1726 } | 1799 } |
| 1727 | 1800 |
| 1728 @override | 1801 @override |
| 1729 void visitStringConcatenation(ir.StringConcatenation stringConcat) { | 1802 void visitStringConcatenation(ir.StringConcatenation stringConcat) { |
| 1730 KernelStringBuilder stringBuilder = new KernelStringBuilder(this); | 1803 KernelStringBuilder stringBuilder = new KernelStringBuilder(this); |
| 1731 stringConcat.accept(stringBuilder); | 1804 stringConcat.accept(stringBuilder); |
| 1732 stack.add(stringBuilder.result); | 1805 stack.add(stringBuilder.result); |
| 1733 } | 1806 } |
| 1734 } | 1807 } |
| OLD | NEW |