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 closurelib; | 7 import '../closure.dart' as closurelib; |
8 import '../closure.dart' hide ClosureScope; | 8 import '../closure.dart' hide ClosureScope; |
9 import '../constants/expressions.dart'; | 9 import '../constants/expressions.dart'; |
10 import '../dart_types.dart'; | 10 import '../dart_types.dart'; |
11 import '../dart2jslib.dart'; | 11 import '../dart2jslib.dart'; |
12 import '../elements/elements.dart'; | 12 import '../elements/elements.dart'; |
13 import '../elements/modelx.dart' show SynthesizedConstructorElementX, | 13 import '../elements/modelx.dart' show SynthesizedConstructorElementX, |
14 ConstructorBodyElementX, FunctionSignatureX; | 14 ConstructorBodyElementX, FunctionSignatureX; |
15 import '../io/source_information.dart'; | 15 import '../io/source_information.dart'; |
16 import '../js_backend/js_backend.dart' show JavaScriptBackend; | 16 import '../js_backend/js_backend.dart' show JavaScriptBackend; |
17 import '../resolution/semantic_visitor.dart'; | 17 import '../resolution/semantic_visitor.dart'; |
18 import '../resolution/operators.dart' as op; | 18 import '../resolution/operators.dart' as op; |
19 import '../tree/tree.dart' as ast; | 19 import '../tree/tree.dart' as ast; |
20 import '../universe/universe.dart' show SelectorKind, CallStructure; | 20 import '../universe/universe.dart' show SelectorKind, CallStructure; |
21 import '../constants/values.dart' show ConstantValue; | |
22 import 'cps_ir_nodes.dart' as ir; | 21 import 'cps_ir_nodes.dart' as ir; |
23 import 'cps_ir_builder.dart'; | 22 import 'cps_ir_builder.dart'; |
24 import '../native/native.dart' show NativeBehavior; | 23 import '../native/native.dart' show NativeBehavior; |
25 | 24 |
26 // TODO(karlklose): remove. | 25 // TODO(karlklose): remove. |
27 import '../js/js.dart' as js show js, Template, Expression; | 26 import '../js/js.dart' as js show js, Template, Expression; |
28 import '../ssa/ssa.dart' show TypeMaskFactory; | 27 import '../ssa/ssa.dart' show TypeMaskFactory; |
29 import '../types/types.dart' show TypeMask; | 28 import '../types/types.dart' show TypeMask; |
30 import '../util/util.dart'; | 29 import '../util/util.dart'; |
31 | 30 |
(...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
347 variableElement: variableElement, | 346 variableElement: variableElement, |
348 variableSelector: selector, | 347 variableSelector: selector, |
349 buildBody: subbuild(node.body), | 348 buildBody: subbuild(node.body), |
350 target: elements.getTargetDefinition(node), | 349 target: elements.getTargetDefinition(node), |
351 closureScope: getClosureScopeForNode(node)); | 350 closureScope: getClosureScopeForNode(node)); |
352 } | 351 } |
353 | 352 |
354 ir.Primitive visitVariableDefinitions(ast.VariableDefinitions node) { | 353 ir.Primitive visitVariableDefinitions(ast.VariableDefinitions node) { |
355 assert(irBuilder.isOpen); | 354 assert(irBuilder.isOpen); |
356 if (node.modifiers.isConst) { | 355 if (node.modifiers.isConst) { |
357 // Do nothing. | 356 for (ast.SendSet definition in node.definitions.nodes) { |
358 // handleLocalConstantGet inlines the constant at use-site. | 357 assert(!definition.arguments.isEmpty); |
| 358 assert(definition.arguments.tail.isEmpty); |
| 359 VariableElement element = elements[definition]; |
| 360 ConstantExpression value = getConstantForVariable(element); |
| 361 irBuilder.declareLocalConstant(element, value); |
| 362 } |
359 } else { | 363 } else { |
360 for (ast.Node definition in node.definitions.nodes) { | 364 for (ast.Node definition in node.definitions.nodes) { |
361 Element element = elements[definition]; | 365 Element element = elements[definition]; |
362 ir.Primitive initialValue; | 366 ir.Primitive initialValue; |
363 // Definitions are either SendSets if there is an initializer, or | 367 // Definitions are either SendSets if there is an initializer, or |
364 // Identifiers if there is no initializer. | 368 // Identifiers if there is no initializer. |
365 if (definition is ast.SendSet) { | 369 if (definition is ast.SendSet) { |
366 assert(!definition.arguments.isEmpty); | 370 assert(!definition.arguments.isEmpty); |
367 assert(definition.arguments.tail.isEmpty); | 371 assert(definition.arguments.tail.isEmpty); |
368 initialValue = visit(definition.arguments.head); | 372 initialValue = visit(definition.arguments.head); |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
488 ir.Primitive visitLiteralNull(ast.LiteralNull node) { | 492 ir.Primitive visitLiteralNull(ast.LiteralNull node) { |
489 assert(irBuilder.isOpen); | 493 assert(irBuilder.isOpen); |
490 return irBuilder.buildNullConstant(); | 494 return irBuilder.buildNullConstant(); |
491 } | 495 } |
492 | 496 |
493 ir.Primitive visitLiteralString(ast.LiteralString node) { | 497 ir.Primitive visitLiteralString(ast.LiteralString node) { |
494 assert(irBuilder.isOpen); | 498 assert(irBuilder.isOpen); |
495 return irBuilder.buildDartStringConstant(node.dartString); | 499 return irBuilder.buildDartStringConstant(node.dartString); |
496 } | 500 } |
497 | 501 |
498 ConstantValue getConstantForNode(ast.Node node) { | 502 ConstantExpression getConstantForNode(ast.Node node) { |
499 ConstantValue constant = | 503 ConstantExpression constant = |
500 irBuilder.state.constants.getConstantValueForNode(node, elements); | 504 irBuilder.state.constants.getConstantForNode(node, elements); |
501 assert(invariant(node, constant != null, | 505 assert(invariant(node, constant != null, |
502 message: 'No constant computed for $node')); | 506 message: 'No constant computed for $node')); |
503 return constant; | 507 return constant; |
504 } | 508 } |
505 | 509 |
506 ConstantValue getConstantForVariable(VariableElement element) { | 510 ConstantExpression getConstantForVariable(VariableElement element) { |
507 ConstantValue constant = | 511 ConstantExpression constant = |
508 irBuilder.state.constants.getConstantValueForVariable(element); | 512 irBuilder.state.constants.getConstantForVariable(element); |
509 assert(invariant(element, constant != null, | 513 assert(invariant(element, constant != null, |
510 message: 'No constant computed for $element')); | 514 message: 'No constant computed for $element')); |
511 return constant; | 515 return constant; |
512 } | 516 } |
513 | 517 |
514 ir.Primitive buildConstantExpression(ConstantExpression expression) { | 518 /// Builds a constant pulling the value from the constant environment. |
515 return irBuilder.buildConstant( | 519 // TODO(johnniwinther): Remove this when [IrBuilder.buildConstant] only takes |
| 520 // a [ConstantExpression]. |
| 521 ir.Primitive buildConstant(ConstantExpression expression) { |
| 522 return irBuilder.buildConstant(expression, |
516 irBuilder.state.constants.getConstantValue(expression)); | 523 irBuilder.state.constants.getConstantValue(expression)); |
517 } | 524 } |
518 | 525 |
519 ir.Primitive visitLiteralList(ast.LiteralList node) { | 526 ir.Primitive visitLiteralList(ast.LiteralList node) { |
520 if (node.isConst) { | 527 if (node.isConst) { |
521 return translateConstant(node); | 528 return translateConstant(node); |
522 } | 529 } |
523 List<ir.Primitive> values = node.elements.nodes.mapToList(visit); | 530 List<ir.Primitive> values = node.elements.nodes.mapToList(visit); |
524 InterfaceType type = elements.getType(node); | 531 InterfaceType type = elements.getType(node); |
525 return irBuilder.buildListLiteral(type, values); | 532 return irBuilder.buildListLiteral(type, values); |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
603 /// Returns `true` if [node] is a super call. | 610 /// Returns `true` if [node] is a super call. |
604 // TODO(johnniwinther): Remove the need for this. | 611 // TODO(johnniwinther): Remove the need for this. |
605 bool isSuperCall(ast.Send node) { | 612 bool isSuperCall(ast.Send node) { |
606 return node != null && node.receiver != null && node.receiver.isSuper(); | 613 return node != null && node.receiver != null && node.receiver.isSuper(); |
607 } | 614 } |
608 | 615 |
609 @override | 616 @override |
610 ir.Primitive handleConstantGet( | 617 ir.Primitive handleConstantGet( |
611 ast.Node node, | 618 ast.Node node, |
612 ConstantExpression constant, _) { | 619 ConstantExpression constant, _) { |
613 return buildConstantExpression(constant); | 620 return buildConstant(constant); |
614 } | 621 } |
615 | 622 |
616 /// If [node] is null, returns this. | 623 /// If [node] is null, returns this. |
617 /// Otherwise visits [node] and returns the result. | 624 /// Otherwise visits [node] and returns the result. |
618 ir.Primitive translateReceiver(ast.Expression node) { | 625 ir.Primitive translateReceiver(ast.Expression node) { |
619 return node != null ? visit(node) : irBuilder.buildThis(); | 626 return node != null ? visit(node) : irBuilder.buildThis(); |
620 } | 627 } |
621 | 628 |
622 @override | 629 @override |
623 ir.Primitive handleDynamicGet( | 630 ir.Primitive handleDynamicGet( |
(...skipping 16 matching lines...) Expand all Loading... |
640 return irBuilder.buildIfNotNullSend( | 647 return irBuilder.buildIfNotNullSend( |
641 target, | 648 target, |
642 nested(() => irBuilder.buildDynamicGet(target, selector))); | 649 nested(() => irBuilder.buildDynamicGet(target, selector))); |
643 } | 650 } |
644 | 651 |
645 @override | 652 @override |
646 ir.Primitive visitDynamicTypeLiteralGet( | 653 ir.Primitive visitDynamicTypeLiteralGet( |
647 ast.Send node, | 654 ast.Send node, |
648 ConstantExpression constant, | 655 ConstantExpression constant, |
649 _) { | 656 _) { |
650 return buildConstantExpression(constant); | 657 return buildConstant(constant); |
651 } | 658 } |
652 | 659 |
653 @override | 660 @override |
654 ir.Primitive visitLocalVariableGet( | 661 ir.Primitive visitLocalVariableGet( |
655 ast.Send node, | 662 ast.Send node, |
656 LocalVariableElement element, | 663 LocalVariableElement element, |
657 _) { | 664 _) { |
658 return element.isConst | 665 return element.isConst |
659 ? irBuilder.buildConstant(getConstantForVariable(element)) | 666 ? buildConstant(getConstantForVariable(element)) |
660 : irBuilder.buildLocalVariableGet(element); | 667 : irBuilder.buildLocalVariableGet(element); |
661 } | 668 } |
662 | 669 |
663 @override | 670 @override |
664 ir.Primitive handleLocalGet( | 671 ir.Primitive handleLocalGet( |
665 ast.Send node, | 672 ast.Send node, |
666 LocalElement element, | 673 LocalElement element, |
667 _) { | 674 _) { |
668 return irBuilder.buildLocalVariableGet(element); | 675 return irBuilder.buildLocalVariableGet(element); |
669 } | 676 } |
(...skipping 289 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
959 translateDynamicArguments(arguments, callStructure)); | 966 translateDynamicArguments(arguments, callStructure)); |
960 } | 967 } |
961 | 968 |
962 @override | 969 @override |
963 ir.Primitive handleConstantInvoke( | 970 ir.Primitive handleConstantInvoke( |
964 ast.Send node, | 971 ast.Send node, |
965 ConstantExpression constant, | 972 ConstantExpression constant, |
966 ast.NodeList arguments, | 973 ast.NodeList arguments, |
967 CallStructure callStructure, | 974 CallStructure callStructure, |
968 _) { | 975 _) { |
969 ir.Primitive target = buildConstantExpression(constant); | 976 ir.Primitive target = buildConstant(constant); |
970 return translateCallInvoke(target, arguments, callStructure); | 977 return translateCallInvoke(target, arguments, callStructure); |
971 } | 978 } |
972 | 979 |
973 @override | 980 @override |
974 ir.Primitive handleDynamicInvoke( | 981 ir.Primitive handleDynamicInvoke( |
975 ast.Send node, | 982 ast.Send node, |
976 ast.Node receiver, | 983 ast.Node receiver, |
977 ast.NodeList arguments, | 984 ast.NodeList arguments, |
978 Selector selector, | 985 Selector selector, |
979 _) { | 986 _) { |
(...skipping 308 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1288 return irBuilder.buildStaticSetterSet(setter, visit(rhs)); | 1295 return irBuilder.buildStaticSetterSet(setter, visit(rhs)); |
1289 } | 1296 } |
1290 | 1297 |
1291 @override | 1298 @override |
1292 ir.Primitive handleTypeLiteralConstantCompounds( | 1299 ir.Primitive handleTypeLiteralConstantCompounds( |
1293 ast.SendSet node, | 1300 ast.SendSet node, |
1294 ConstantExpression constant, | 1301 ConstantExpression constant, |
1295 CompoundRhs rhs, | 1302 CompoundRhs rhs, |
1296 arg) { | 1303 arg) { |
1297 return translateCompounds( | 1304 return translateCompounds( |
1298 getValue: () => buildConstantExpression(constant), | 1305 getValue: () => buildConstant(constant), |
1299 rhs: rhs, | 1306 rhs: rhs, |
1300 setValue: (value) {}); // The binary operator will throw before this. | 1307 setValue: (value) {}); // The binary operator will throw before this. |
1301 } | 1308 } |
1302 | 1309 |
1303 @override | 1310 @override |
1304 ir.Primitive handleDynamicCompounds( | 1311 ir.Primitive handleDynamicCompounds( |
1305 ast.Send node, | 1312 ast.Send node, |
1306 ast.Node receiver, | 1313 ast.Node receiver, |
1307 CompoundRhs rhs, | 1314 CompoundRhs rhs, |
1308 Selector getterSelector, | 1315 Selector getterSelector, |
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1509 setValue: (ir.Primitive result) { | 1516 setValue: (ir.Primitive result) { |
1510 if (isSetterValid) { | 1517 if (isSetterValid) { |
1511 irBuilder.buildSuperIndexSet(indexSetFunction, indexValue, result); | 1518 irBuilder.buildSuperIndexSet(indexSetFunction, indexValue, result); |
1512 } else { | 1519 } else { |
1513 buildInstanceNoSuchMethod( | 1520 buildInstanceNoSuchMethod( |
1514 new Selector.indexSet(), <ir.Primitive>[indexValue, result]); | 1521 new Selector.indexSet(), <ir.Primitive>[indexValue, result]); |
1515 } | 1522 } |
1516 }); | 1523 }); |
1517 } | 1524 } |
1518 | 1525 |
1519 /// Evaluates a string interpolation and appends each part to [accumulator] | |
1520 /// (after stringify conversion). | |
1521 void buildStringParts(ast.Node node, List<ir.Primitive> accumulator) { | |
1522 if (node is ast.StringJuxtaposition) { | |
1523 buildStringParts(node.first, accumulator); | |
1524 buildStringParts(node.second, accumulator); | |
1525 } else if (node is ast.StringInterpolation) { | |
1526 buildStringParts(node.string, accumulator); | |
1527 for (ast.StringInterpolationPart part in node.parts) { | |
1528 buildStringParts(part.expression, accumulator); | |
1529 buildStringParts(part.string, accumulator); | |
1530 } | |
1531 } else if (node is ast.LiteralString) { | |
1532 // Empty strings often occur at the end of a string interpolation, | |
1533 // do not bother to include them. | |
1534 if (!node.dartString.isEmpty) { | |
1535 accumulator.add(irBuilder.buildDartStringConstant(node.dartString)); | |
1536 } | |
1537 } else if (node is ast.ParenthesizedExpression) { | |
1538 buildStringParts(node, accumulator); | |
1539 } else { | |
1540 ir.Primitive value = visit(node); | |
1541 accumulator.add(irBuilder.buildStringify(value)); | |
1542 } | |
1543 } | |
1544 | |
1545 ir.Primitive visitStringJuxtaposition(ast.StringJuxtaposition node) { | 1526 ir.Primitive visitStringJuxtaposition(ast.StringJuxtaposition node) { |
1546 assert(irBuilder.isOpen); | 1527 assert(irBuilder.isOpen); |
1547 List<ir.Primitive> parts = <ir.Primitive>[]; | 1528 ir.Primitive first = visit(node.first); |
1548 buildStringParts(node, parts); | 1529 ir.Primitive second = visit(node.second); |
1549 return irBuilder.buildStringConcatenation(parts); | 1530 return irBuilder.buildStringConcatenation([first, second]); |
1550 } | 1531 } |
1551 | 1532 |
1552 ir.Primitive visitStringInterpolation(ast.StringInterpolation node) { | 1533 ir.Primitive visitStringInterpolation(ast.StringInterpolation node) { |
1553 assert(irBuilder.isOpen); | 1534 assert(irBuilder.isOpen); |
1554 List<ir.Primitive> parts = <ir.Primitive>[]; | 1535 List<ir.Primitive> arguments = []; |
1555 buildStringParts(node, parts); | 1536 arguments.add(visitLiteralString(node.string)); |
1556 return irBuilder.buildStringConcatenation(parts); | 1537 var it = node.parts.iterator; |
| 1538 while (it.moveNext()) { |
| 1539 ast.StringInterpolationPart part = it.current; |
| 1540 arguments.add(visit(part.expression)); |
| 1541 arguments.add(visitLiteralString(part.string)); |
| 1542 } |
| 1543 return irBuilder.buildStringConcatenation(arguments); |
1557 } | 1544 } |
1558 | 1545 |
1559 ir.Primitive translateConstant(ast.Node node) { | 1546 ir.Primitive translateConstant(ast.Node node) { |
1560 assert(irBuilder.isOpen); | 1547 assert(irBuilder.isOpen); |
1561 return irBuilder.buildConstant(getConstantForNode(node)); | 1548 return buildConstant(getConstantForNode(node)); |
1562 } | 1549 } |
1563 | 1550 |
1564 ir.Primitive visitThrow(ast.Throw node) { | 1551 ir.Primitive visitThrow(ast.Throw node) { |
1565 assert(irBuilder.isOpen); | 1552 assert(irBuilder.isOpen); |
1566 // This function is not called for throw expressions occurring as | 1553 // This function is not called for throw expressions occurring as |
1567 // statements. | 1554 // statements. |
1568 return irBuilder.buildNonTailThrow(visit(node.expression)); | 1555 return irBuilder.buildNonTailThrow(visit(node.expression)); |
1569 } | 1556 } |
1570 | 1557 |
1571 ir.Primitive buildStaticNoSuchMethod( | 1558 ir.Primitive buildStaticNoSuchMethod( |
(...skipping 533 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2105 JavaScriptBackend get _backend => _compiler.backend; | 2092 JavaScriptBackend get _backend => _compiler.backend; |
2106 | 2093 |
2107 GlobalProgramInformation(this._compiler); | 2094 GlobalProgramInformation(this._compiler); |
2108 | 2095 |
2109 /// Returns [true], if the analysis could not determine that the type | 2096 /// Returns [true], if the analysis could not determine that the type |
2110 /// arguments for the class [cls] are never used in the program. | 2097 /// arguments for the class [cls] are never used in the program. |
2111 bool requiresRuntimeTypesFor(ClassElement cls) { | 2098 bool requiresRuntimeTypesFor(ClassElement cls) { |
2112 return cls.typeVariables.isNotEmpty && _backend.classNeedsRti(cls); | 2099 return cls.typeVariables.isNotEmpty && _backend.classNeedsRti(cls); |
2113 } | 2100 } |
2114 | 2101 |
2115 FunctionElement get stringifyFunction { | |
2116 return _backend.getStringInterpolationHelper(); | |
2117 } | |
2118 | |
2119 FunctionElement get throwTypeErrorHelper => _backend.getThrowTypeError(); | 2102 FunctionElement get throwTypeErrorHelper => _backend.getThrowTypeError(); |
2120 | 2103 |
2121 ClassElement get nullClass => _compiler.nullClass; | 2104 ClassElement get nullClass => _compiler.nullClass; |
2122 | 2105 |
2123 DartType unaliasType(DartType type) => type.unalias(_compiler); | 2106 DartType unaliasType(DartType type) => type.unalias(_compiler); |
2124 | 2107 |
2125 TypeMask getTypeMaskForForeign(NativeBehavior behavior) { | 2108 TypeMask getTypeMaskForForeign(NativeBehavior behavior) { |
2126 return TypeMaskFactory.fromNativeBehavior(behavior, _compiler); | 2109 return TypeMaskFactory.fromNativeBehavior(behavior, _compiler); |
2127 } | 2110 } |
2128 } | 2111 } |
(...skipping 762 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2891 [irBuilder.buildStringConstant(element.name)]); | 2874 [irBuilder.buildStringConstant(element.name)]); |
2892 } | 2875 } |
2893 | 2876 |
2894 @override | 2877 @override |
2895 ir.Primitive handleStaticFieldGet(ast.Send node, FieldElement field, _) { | 2878 ir.Primitive handleStaticFieldGet(ast.Send node, FieldElement field, _) { |
2896 SourceInformation src = sourceInformationBuilder.buildGet(node); | 2879 SourceInformation src = sourceInformationBuilder.buildGet(node); |
2897 return buildStaticFieldGet(field, src); | 2880 return buildStaticFieldGet(field, src); |
2898 } | 2881 } |
2899 | 2882 |
2900 ir.Primitive buildStaticFieldGet(FieldElement field, SourceInformation src) { | 2883 ir.Primitive buildStaticFieldGet(FieldElement field, SourceInformation src) { |
2901 ConstantValue constant = getConstantForVariable(field); | 2884 ConstantExpression constant = |
| 2885 backend.constants.getConstantForVariable(field); |
2902 if (constant != null && !field.isAssignable) { | 2886 if (constant != null && !field.isAssignable) { |
2903 return irBuilder.buildConstant(constant); | 2887 return buildConstant(constant); |
2904 } else if (backend.constants.lazyStatics.contains(field)) { | 2888 } else if (backend.constants.lazyStatics.contains(field)) { |
2905 return irBuilder.buildStaticFieldLazyGet(field, src); | 2889 return irBuilder.buildStaticFieldLazyGet(field, src); |
2906 } else { | 2890 } else { |
2907 return irBuilder.buildStaticFieldGet(field, src); | 2891 return irBuilder.buildStaticFieldGet(field, src); |
2908 } | 2892 } |
2909 } | 2893 } |
2910 | 2894 |
2911 /// Build code to handle foreign code, that is, native JavaScript code, or | 2895 /// Build code to handle foreign code, that is, native JavaScript code, or |
2912 /// builtin values and operations of the backend. | 2896 /// builtin values and operations of the backend. |
2913 ir.Primitive handleForeignCode(ast.Send node, | 2897 ir.Primitive handleForeignCode(ast.Send node, |
(...skipping 271 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3185 } | 3169 } |
3186 | 3170 |
3187 processSetStatic(ir.SetStatic node) { | 3171 processSetStatic(ir.SetStatic node) { |
3188 node.body = replacementFor(node.body); | 3172 node.body = replacementFor(node.body); |
3189 } | 3173 } |
3190 | 3174 |
3191 processContinuation(ir.Continuation node) { | 3175 processContinuation(ir.Continuation node) { |
3192 node.body = replacementFor(node.body); | 3176 node.body = replacementFor(node.body); |
3193 } | 3177 } |
3194 } | 3178 } |
OLD | NEW |