| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 code_generator; | 5 library code_generator; |
| 6 | 6 |
| 7 import 'glue.dart'; | 7 import 'glue.dart'; |
| 8 | 8 |
| 9 import '../../tree_ir/tree_ir_nodes.dart' as tree_ir; | 9 import '../../tree_ir/tree_ir_nodes.dart' as tree_ir; |
| 10 import '../../tree_ir/tree_ir_nodes.dart' show BuiltinOperator; | 10 import '../../tree_ir/tree_ir_nodes.dart' show BuiltinOperator; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 44 new Maplet<VariableElement, String>(); | 44 new Maplet<VariableElement, String>(); |
| 45 | 45 |
| 46 /// Variable names that have already been used. Used to avoid name clashes. | 46 /// Variable names that have already been used. Used to avoid name clashes. |
| 47 Set<String> usedVariableNames = new Set<String>(); | 47 Set<String> usedVariableNames = new Set<String>(); |
| 48 | 48 |
| 49 final tree_ir.FallthroughStack fallthrough = new tree_ir.FallthroughStack(); | 49 final tree_ir.FallthroughStack fallthrough = new tree_ir.FallthroughStack(); |
| 50 | 50 |
| 51 /// Stacks whose top element is the current target of an unlabeled break | 51 /// Stacks whose top element is the current target of an unlabeled break |
| 52 /// or continue. For continues, this is the loop node itself. | 52 /// or continue. For continues, this is the loop node itself. |
| 53 final tree_ir.FallthroughStack shortBreak = new tree_ir.FallthroughStack(); | 53 final tree_ir.FallthroughStack shortBreak = new tree_ir.FallthroughStack(); |
| 54 final tree_ir.FallthroughStack shortContinue = | 54 final tree_ir.FallthroughStack shortContinue = |
| 55 new tree_ir.FallthroughStack(); | 55 new tree_ir.FallthroughStack(); |
| 56 | 56 |
| 57 Set<tree_ir.Label> usedLabels = new Set<tree_ir.Label>(); | 57 Set<tree_ir.Label> usedLabels = new Set<tree_ir.Label>(); |
| 58 | 58 |
| 59 List<js.Statement> accumulator = new List<js.Statement>(); | 59 List<js.Statement> accumulator = new List<js.Statement>(); |
| 60 | 60 |
| 61 CodeGenerator(this.glue, this.registry); | 61 CodeGenerator(this.glue, this.registry); |
| 62 | 62 |
| 63 /// Generates JavaScript code for the body of [function]. | 63 /// Generates JavaScript code for the body of [function]. |
| 64 js.Fun buildFunction(tree_ir.FunctionDefinition function) { | 64 js.Fun buildFunction(tree_ir.FunctionDefinition function) { |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 176 } | 176 } |
| 177 | 177 |
| 178 @override | 178 @override |
| 179 js.Expression visitConditional(tree_ir.Conditional node) { | 179 js.Expression visitConditional(tree_ir.Conditional node) { |
| 180 return new js.Conditional( | 180 return new js.Conditional( |
| 181 visitExpression(node.condition), | 181 visitExpression(node.condition), |
| 182 visitExpression(node.thenExpression), | 182 visitExpression(node.thenExpression), |
| 183 visitExpression(node.elseExpression)); | 183 visitExpression(node.elseExpression)); |
| 184 } | 184 } |
| 185 | 185 |
| 186 js.Expression buildConstant(ConstantValue constant) { | 186 js.Expression buildConstant(ConstantValue constant, |
| 187 {SourceInformation sourceInformation}) { |
| 187 registry.registerCompileTimeConstant(constant); | 188 registry.registerCompileTimeConstant(constant); |
| 188 return glue.constantReference(constant); | 189 return glue.constantReference(constant) |
| 190 .withSourceInformation(sourceInformation); |
| 189 } | 191 } |
| 190 | 192 |
| 191 @override | 193 @override |
| 192 js.Expression visitConstant(tree_ir.Constant node) { | 194 js.Expression visitConstant(tree_ir.Constant node) { |
| 193 return buildConstant(node.value); | 195 return buildConstant( |
| 196 node.value, |
| 197 sourceInformation: node.sourceInformation); |
| 194 } | 198 } |
| 195 | 199 |
| 196 js.Expression compileConstant(ParameterElement parameter) { | 200 js.Expression compileConstant(ParameterElement parameter) { |
| 197 return buildConstant(glue.getConstantValueForVariable(parameter)); | 201 return buildConstant(glue.getConstantValueForVariable(parameter)); |
| 198 } | 202 } |
| 199 | 203 |
| 200 js.Expression buildStaticInvoke(Element target, | 204 js.Expression buildStaticInvoke(Element target, |
| 201 List<js.Expression> arguments, | 205 List<js.Expression> arguments, |
| 202 {SourceInformation sourceInformation}) { | 206 {SourceInformation sourceInformation}) { |
| 203 registry.registerStaticInvocation(target.declaration); | 207 registry.registerStaticInvocation(target.declaration); |
| 204 js.Expression elementAccess = glue.staticFunctionAccess(target); | 208 js.Expression elementAccess = glue.staticFunctionAccess(target); |
| 205 return new js.Call(elementAccess, arguments, | 209 return new js.Call(elementAccess, arguments, |
| 206 sourceInformation: sourceInformation); | 210 sourceInformation: sourceInformation); |
| 207 } | 211 } |
| 208 | 212 |
| 209 @override | 213 @override |
| 210 js.Expression visitInvokeConstructor(tree_ir.InvokeConstructor node) { | 214 js.Expression visitInvokeConstructor(tree_ir.InvokeConstructor node) { |
| 211 if (node.constant != null) return giveup(node); | 215 if (node.constant != null) return giveup(node); |
| 212 | 216 |
| 213 registry.registerInstantiatedType(node.type); | 217 registry.registerInstantiatedType(node.type); |
| 214 FunctionElement target = node.target; | 218 FunctionElement target = node.target; |
| 215 List<js.Expression> arguments = visitExpressionList(node.arguments); | 219 List<js.Expression> arguments = visitExpressionList(node.arguments); |
| 216 return buildStaticInvoke(target, arguments); | 220 return buildStaticInvoke( |
| 221 target, arguments, sourceInformation: node.sourceInformation); |
| 217 } | 222 } |
| 218 | 223 |
| 219 void registerMethodInvoke(tree_ir.InvokeMethod node) { | 224 void registerMethodInvoke(tree_ir.InvokeMethod node) { |
| 220 Selector selector = node.selector; | 225 Selector selector = node.selector; |
| 221 TypeMask mask = node.mask; | 226 TypeMask mask = node.mask; |
| 222 if (selector.isGetter) { | 227 if (selector.isGetter) { |
| 223 registry.registerDynamicGetter(new UniverseSelector(selector, mask)); | 228 registry.registerDynamicGetter(new UniverseSelector(selector, mask)); |
| 224 } else if (selector.isSetter) { | 229 } else if (selector.isSetter) { |
| 225 registry.registerDynamicSetter(new UniverseSelector(selector, mask)); | 230 registry.registerDynamicSetter(new UniverseSelector(selector, mask)); |
| 226 } else { | 231 } else { |
| 227 assert(invariant(CURRENT_ELEMENT_SPANNABLE, | 232 assert(invariant(CURRENT_ELEMENT_SPANNABLE, |
| 228 selector.isCall || selector.isOperator || | 233 selector.isCall || selector.isOperator || |
| 229 selector.isIndex || selector.isIndexSet, | 234 selector.isIndex || selector.isIndexSet, |
| 230 message: 'unexpected kind ${selector.kind}')); | 235 message: 'unexpected kind ${selector.kind}')); |
| 231 // TODO(sigurdm): We should find a better place to register the call. | 236 // TODO(sigurdm): We should find a better place to register the call. |
| 232 Selector call = new Selector.callClosureFrom(selector); | 237 Selector call = new Selector.callClosureFrom(selector); |
| 233 registry.registerDynamicInvocation(new UniverseSelector(call, null)); | 238 registry.registerDynamicInvocation(new UniverseSelector(call, null)); |
| 234 registry.registerDynamicInvocation(new UniverseSelector(selector, mask)); | 239 registry.registerDynamicInvocation(new UniverseSelector(selector, mask)); |
| 235 } | 240 } |
| 236 } | 241 } |
| 237 | 242 |
| 238 @override | 243 @override |
| 239 js.Expression visitInvokeMethod(tree_ir.InvokeMethod node) { | 244 js.Expression visitInvokeMethod(tree_ir.InvokeMethod node) { |
| 240 registerMethodInvoke(node); | 245 registerMethodInvoke(node); |
| 241 return js.propertyCall(visitExpression(node.receiver), | 246 return js.propertyCall(visitExpression(node.receiver), |
| 242 glue.invocationName(node.selector), | 247 glue.invocationName(node.selector), |
| 243 visitExpressionList(node.arguments)); | 248 visitExpressionList(node.arguments)) |
| 249 .withSourceInformation(node.sourceInformation); |
| 244 } | 250 } |
| 245 | 251 |
| 246 @override | 252 @override |
| 247 js.Expression visitInvokeStatic(tree_ir.InvokeStatic node) { | 253 js.Expression visitInvokeStatic(tree_ir.InvokeStatic node) { |
| 248 FunctionElement target = node.target; | 254 FunctionElement target = node.target; |
| 249 List<js.Expression> arguments = visitExpressionList(node.arguments); | 255 List<js.Expression> arguments = visitExpressionList(node.arguments); |
| 250 return buildStaticInvoke(target, arguments, | 256 return buildStaticInvoke(target, arguments, |
| 251 sourceInformation: node.sourceInformation); | 257 sourceInformation: node.sourceInformation); |
| 252 } | 258 } |
| 253 | 259 |
| 254 @override | 260 @override |
| 255 js.Expression visitInvokeMethodDirectly(tree_ir.InvokeMethodDirectly node) { | 261 js.Expression visitInvokeMethodDirectly(tree_ir.InvokeMethodDirectly node) { |
| 256 registry.registerDirectInvocation(node.target.declaration); | 262 registry.registerDirectInvocation(node.target.declaration); |
| 257 if (node.target is ConstructorBodyElement) { | 263 if (node.target is ConstructorBodyElement) { |
| 258 // A constructor body cannot be overriden or intercepted, so we can | 264 // A constructor body cannot be overriden or intercepted, so we can |
| 259 // use the short form for this invocation. | 265 // use the short form for this invocation. |
| 260 return js.js('#.#(#)', | 266 return js.js('#.#(#)', |
| 261 [visitExpression(node.receiver), | 267 [visitExpression(node.receiver), |
| 262 glue.instanceMethodName(node.target), | 268 glue.instanceMethodName(node.target), |
| 263 visitExpressionList(node.arguments)]); | 269 visitExpressionList(node.arguments)]) |
| 270 .withSourceInformation(node.sourceInformation); |
| 264 } | 271 } |
| 265 return js.js('#.#.call(#, #)', | 272 return js.js('#.#.call(#, #)', |
| 266 [glue.prototypeAccess(node.target.enclosingClass), | 273 [glue.prototypeAccess(node.target.enclosingClass), |
| 267 glue.invocationName(node.selector), | 274 glue.invocationName(node.selector), |
| 268 visitExpression(node.receiver), | 275 visitExpression(node.receiver), |
| 269 visitExpressionList(node.arguments)]); | 276 visitExpressionList(node.arguments)]) |
| 277 .withSourceInformation(node.sourceInformation); |
| 270 } | 278 } |
| 271 | 279 |
| 272 @override | 280 @override |
| 273 js.Expression visitLiteralList(tree_ir.LiteralList node) { | 281 js.Expression visitLiteralList(tree_ir.LiteralList node) { |
| 274 registry.registerInstantiatedClass(glue.listClass); | 282 registry.registerInstantiatedClass(glue.listClass); |
| 275 List<js.Expression> entries = visitExpressionList(node.values); | 283 List<js.Expression> entries = visitExpressionList(node.values); |
| 276 return new js.ArrayInitializer(entries); | 284 return new js.ArrayInitializer(entries); |
| 277 } | 285 } |
| 278 | 286 |
| 279 @override | 287 @override |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 418 } | 426 } |
| 419 | 427 |
| 420 @override | 428 @override |
| 421 void visitBreak(tree_ir.Break node) { | 429 void visitBreak(tree_ir.Break node) { |
| 422 if (isEffectiveBreakTarget(node, fallthrough.target)) { | 430 if (isEffectiveBreakTarget(node, fallthrough.target)) { |
| 423 // Fall through to break target or to equivalent break. | 431 // Fall through to break target or to equivalent break. |
| 424 fallthrough.use(); | 432 fallthrough.use(); |
| 425 } else if (isEffectiveBreakTarget(node, shortBreak.target)) { | 433 } else if (isEffectiveBreakTarget(node, shortBreak.target)) { |
| 426 // Unlabeled break to the break target or to an equivalent break. | 434 // Unlabeled break to the break target or to an equivalent break. |
| 427 shortBreak.use(); | 435 shortBreak.use(); |
| 428 accumulator.add(new js.Break(null)); | 436 accumulator.add(new js.Break(null)); |
| 429 } else { | 437 } else { |
| 430 usedLabels.add(node.target); | 438 usedLabels.add(node.target); |
| 431 accumulator.add(new js.Break(node.target.name)); | 439 accumulator.add(new js.Break(node.target.name)); |
| 432 } | 440 } |
| 433 } | 441 } |
| 434 | 442 |
| 435 @override | 443 @override |
| 436 void visitExpressionStatement(tree_ir.ExpressionStatement node) { | 444 void visitExpressionStatement(tree_ir.ExpressionStatement node) { |
| 437 accumulator.add(new js.ExpressionStatement( | 445 accumulator.add(new js.ExpressionStatement( |
| 438 visitExpression(node.expression))); | 446 visitExpression(node.expression))); |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 540 return node is tree_ir.Constant && node.value.isNull; | 548 return node is tree_ir.Constant && node.value.isNull; |
| 541 } | 549 } |
| 542 | 550 |
| 543 @override | 551 @override |
| 544 void visitReturn(tree_ir.Return node) { | 552 void visitReturn(tree_ir.Return node) { |
| 545 if (isNull(node.value) && fallthrough.target == null) { | 553 if (isNull(node.value) && fallthrough.target == null) { |
| 546 // Do nothing. Implicitly return JS undefined by falling over the end. | 554 // Do nothing. Implicitly return JS undefined by falling over the end. |
| 547 registry.registerCompileTimeConstant(new NullConstantValue()); | 555 registry.registerCompileTimeConstant(new NullConstantValue()); |
| 548 fallthrough.use(); | 556 fallthrough.use(); |
| 549 } else { | 557 } else { |
| 550 accumulator.add(new js.Return(visitExpression(node.value))); | 558 accumulator.add(new js.Return(visitExpression(node.value)) |
| 559 .withSourceInformation(node.sourceInformation)); |
| 551 } | 560 } |
| 552 } | 561 } |
| 553 | 562 |
| 554 @override | 563 @override |
| 555 void visitThrow(tree_ir.Throw node) { | 564 void visitThrow(tree_ir.Throw node) { |
| 556 accumulator.add(new js.Throw(visitExpression(node.value))); | 565 accumulator.add(new js.Throw(visitExpression(node.value))); |
| 557 } | 566 } |
| 558 | 567 |
| 559 @override | 568 @override |
| 560 void visitRethrow(tree_ir.Rethrow node) { | 569 void visitRethrow(tree_ir.Rethrow node) { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 589 // TODO(asgerf): To allow inlining of InvokeConstructor, CreateInstance must | 598 // TODO(asgerf): To allow inlining of InvokeConstructor, CreateInstance must |
| 590 // carry a DartType so we can register the instantiated type | 599 // carry a DartType so we can register the instantiated type |
| 591 // with its type arguments. Otherwise dataflow analysis is | 600 // with its type arguments. Otherwise dataflow analysis is |
| 592 // needed to reconstruct the instantiated type. | 601 // needed to reconstruct the instantiated type. |
| 593 registry.registerInstantiatedClass(classElement); | 602 registry.registerInstantiatedClass(classElement); |
| 594 if (classElement is ClosureClassElement) { | 603 if (classElement is ClosureClassElement) { |
| 595 registry.registerInstantiatedClosure(classElement.methodElement); | 604 registry.registerInstantiatedClosure(classElement.methodElement); |
| 596 } | 605 } |
| 597 js.Expression instance = new js.New( | 606 js.Expression instance = new js.New( |
| 598 glue.constructorAccess(classElement), | 607 glue.constructorAccess(classElement), |
| 599 visitExpressionList(node.arguments)); | 608 visitExpressionList(node.arguments)) |
| 609 .withSourceInformation(node.sourceInformation); |
| 600 | 610 |
| 601 List<tree_ir.Expression> typeInformation = node.typeInformation; | 611 List<tree_ir.Expression> typeInformation = node.typeInformation; |
| 602 assert(typeInformation.isEmpty || | 612 assert(typeInformation.isEmpty || |
| 603 typeInformation.length == classElement.typeVariables.length); | 613 typeInformation.length == classElement.typeVariables.length); |
| 604 if (typeInformation.isNotEmpty) { | 614 if (typeInformation.isNotEmpty) { |
| 605 FunctionElement helper = glue.getAddRuntimeTypeInformation(); | 615 FunctionElement helper = glue.getAddRuntimeTypeInformation(); |
| 606 js.Expression typeArguments = new js.ArrayInitializer( | 616 js.Expression typeArguments = new js.ArrayInitializer( |
| 607 visitExpressionList(typeInformation)); | 617 visitExpressionList(typeInformation)); |
| 608 return buildStaticHelperInvocation(helper, | 618 return buildStaticHelperInvocation(helper, |
| 609 <js.Expression>[instance, typeArguments]); | 619 <js.Expression>[instance, typeArguments], |
| 620 sourceInformation: node.sourceInformation); |
| 610 } else { | 621 } else { |
| 611 return instance; | 622 return instance; |
| 612 } | 623 } |
| 613 } | 624 } |
| 614 | 625 |
| 615 @override | 626 @override |
| 616 js.Expression visitCreateInvocationMirror( | 627 js.Expression visitCreateInvocationMirror( |
| 617 tree_ir.CreateInvocationMirror node) { | 628 tree_ir.CreateInvocationMirror node) { |
| 618 js.Expression name = js.string(node.selector.name); | 629 js.Expression name = js.string(node.selector.name); |
| 619 js.Expression internalName = | 630 js.Expression internalName = |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 697 } | 708 } |
| 698 | 709 |
| 699 @override | 710 @override |
| 700 js.Expression visitSetIndex(tree_ir.SetIndex node) { | 711 js.Expression visitSetIndex(tree_ir.SetIndex node) { |
| 701 return js.js('#[#] = #', | 712 return js.js('#[#] = #', |
| 702 [visitExpression(node.object), | 713 [visitExpression(node.object), |
| 703 visitExpression(node.index), | 714 visitExpression(node.index), |
| 704 visitExpression(node.value)]); | 715 visitExpression(node.value)]); |
| 705 } | 716 } |
| 706 | 717 |
| 707 js.Expression buildStaticHelperInvocation(FunctionElement helper, | 718 js.Expression buildStaticHelperInvocation( |
| 708 List<js.Expression> arguments) { | 719 FunctionElement helper, |
| 720 List<js.Expression> arguments, |
| 721 {SourceInformation sourceInformation}) { |
| 709 registry.registerStaticUse(helper); | 722 registry.registerStaticUse(helper); |
| 710 return buildStaticInvoke(helper, arguments); | 723 return buildStaticInvoke( |
| 724 helper, arguments, sourceInformation: sourceInformation); |
| 711 } | 725 } |
| 712 | 726 |
| 713 @override | 727 @override |
| 714 js.Expression visitReifyRuntimeType(tree_ir.ReifyRuntimeType node) { | 728 js.Expression visitReifyRuntimeType(tree_ir.ReifyRuntimeType node) { |
| 715 FunctionElement createType = glue.getCreateRuntimeType(); | 729 js.Expression typeToString = buildStaticHelperInvocation( |
| 716 FunctionElement typeToString = glue.getRuntimeTypeToString(); | 730 glue.getRuntimeTypeToString(), |
| 717 return buildStaticHelperInvocation(createType, | 731 [visitExpression(node.value)], |
| 718 [buildStaticHelperInvocation(typeToString, | 732 sourceInformation: node.sourceInformation); |
| 719 [visitExpression(node.value)])]); | 733 return buildStaticHelperInvocation( |
| 734 glue.getCreateRuntimeType(), |
| 735 [typeToString], |
| 736 sourceInformation: node.sourceInformation); |
| 720 } | 737 } |
| 721 | 738 |
| 722 @override | 739 @override |
| 723 js.Expression visitReadTypeVariable(tree_ir.ReadTypeVariable node) { | 740 js.Expression visitReadTypeVariable(tree_ir.ReadTypeVariable node) { |
| 724 ClassElement context = node.variable.element.enclosingClass; | 741 ClassElement context = node.variable.element.enclosingClass; |
| 725 js.Expression index = js.number(glue.getTypeVariableIndex(node.variable)); | 742 js.Expression index = js.number(glue.getTypeVariableIndex(node.variable)); |
| 726 if (glue.needsSubstitutionForTypeVariableAccess(context)) { | 743 if (glue.needsSubstitutionForTypeVariableAccess(context)) { |
| 727 js.Expression typeName = glue.getRuntimeTypeName(context); | 744 js.Expression typeName = glue.getRuntimeTypeName(context); |
| 728 return buildStaticHelperInvocation( | 745 return buildStaticHelperInvocation( |
| 729 glue.getRuntimeTypeArgument(), | 746 glue.getRuntimeTypeArgument(), |
| 730 [visitExpression(node.target), typeName, index]); | 747 [visitExpression(node.target), typeName, index], |
| 748 sourceInformation: node.sourceInformation); |
| 731 } else { | 749 } else { |
| 732 return buildStaticHelperInvocation( | 750 return buildStaticHelperInvocation( |
| 733 glue.getTypeArgumentByIndex(), | 751 glue.getTypeArgumentByIndex(), |
| 734 [visitExpression(node.target), index]); | 752 [visitExpression(node.target), index], |
| 753 sourceInformation: node.sourceInformation); |
| 735 } | 754 } |
| 736 } | 755 } |
| 737 | 756 |
| 738 @override | 757 @override |
| 739 js.Expression visitTypeExpression(tree_ir.TypeExpression node) { | 758 js.Expression visitTypeExpression(tree_ir.TypeExpression node) { |
| 740 List<js.Expression> arguments = visitExpressionList(node.arguments); | 759 List<js.Expression> arguments = visitExpressionList(node.arguments); |
| 741 return glue.generateTypeRepresentation(node.dartType, arguments); | 760 return glue.generateTypeRepresentation(node.dartType, arguments); |
| 742 } | 761 } |
| 743 | 762 |
| 744 js.Node handleForeignCode(tree_ir.ForeignCode node) { | 763 js.Node handleForeignCode(tree_ir.ForeignCode node) { |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 806 return js.js("typeof # === 'number' && Math.floor(#) === #", args); | 825 return js.js("typeof # === 'number' && Math.floor(#) === #", args); |
| 807 } | 826 } |
| 808 } | 827 } |
| 809 | 828 |
| 810 visitFunctionExpression(tree_ir.FunctionExpression node) { | 829 visitFunctionExpression(tree_ir.FunctionExpression node) { |
| 811 // FunctionExpressions are currently unused. | 830 // FunctionExpressions are currently unused. |
| 812 // We might need them if we want to emit raw JS nested functions. | 831 // We might need them if we want to emit raw JS nested functions. |
| 813 throw 'FunctionExpressions should not be used'; | 832 throw 'FunctionExpressions should not be used'; |
| 814 } | 833 } |
| 815 } | 834 } |
| OLD | NEW |