OLD | NEW |
---|---|
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 ddc.src.codegen.js_codegen; | 5 library ddc.src.codegen.js_codegen; |
6 | 6 |
7 import 'dart:io' show Directory, File; | 7 import 'dart:io' show Directory, File; |
8 | 8 |
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
(...skipping 27 matching lines...) Expand all Loading... | |
38 | 38 |
39 /// The variable for the target of the current `..` cascade expression. | 39 /// The variable for the target of the current `..` cascade expression. |
40 SimpleIdentifier _cascadeTarget; | 40 SimpleIdentifier _cascadeTarget; |
41 | 41 |
42 ClassDeclaration currentClass; | 42 ClassDeclaration currentClass; |
43 ConstantEvaluator _constEvaluator; | 43 ConstantEvaluator _constEvaluator; |
44 | 44 |
45 final _exports = <String>[]; | 45 final _exports = <String>[]; |
46 final _lazyFields = <VariableDeclaration>[]; | 46 final _lazyFields = <VariableDeclaration>[]; |
47 final _properties = <FunctionDeclaration>[]; | 47 final _properties = <FunctionDeclaration>[]; |
48 final _privateNames = new Set<String>(); | |
Jennifer Messerly
2015/03/02 18:48:16
hmmm, another thought ... we could emit these as w
| |
48 | 49 |
49 JSCodegenVisitor(LibraryInfo libraryInfo, TypeRules rules) | 50 JSCodegenVisitor(LibraryInfo libraryInfo, TypeRules rules) |
50 : libraryInfo = libraryInfo, | 51 : libraryInfo = libraryInfo, |
51 rules = rules, | 52 rules = rules, |
52 _libraryName = jsLibraryName(libraryInfo.library); | 53 _libraryName = jsLibraryName(libraryInfo.library); |
53 | 54 |
54 Element get currentLibrary => libraryInfo.library; | 55 Element get currentLibrary => libraryInfo.library; |
55 | 56 |
56 JS.Block generateLibrary( | 57 JS.Block generateLibrary( |
57 Iterable<CompilationUnit> units, CheckerReporter reporter) { | 58 Iterable<CompilationUnit> units, CheckerReporter reporter) { |
(...skipping 10 matching lines...) Expand all Loading... | |
68 } | 69 } |
69 | 70 |
70 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); | 71 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); |
71 | 72 |
72 // TODO(jmesserly): make these immutable in JS? | 73 // TODO(jmesserly): make these immutable in JS? |
73 for (var name in _exports) { | 74 for (var name in _exports) { |
74 body.add(js.statement('#.# = #;', [_libraryName, name, name])); | 75 body.add(js.statement('#.# = #;', [_libraryName, name, name])); |
75 } | 76 } |
76 | 77 |
77 var name = _libraryName; | 78 var name = _libraryName; |
79 var symbolInit = _privateNames.map(_initPrivateSymbol); | |
78 return new JS.Block([ | 80 return new JS.Block([ |
79 js.statement('var #;', name), | 81 js.statement('var #;', name), |
80 js.statement(''' | 82 js.statement("(function (#) { 'use strict'; #; #; })(# || (# = {}));", |
81 (function (#) { | 83 [name, symbolInit, body, name, name]) |
82 'use strict'; | |
83 #; | |
84 })(# || (# = {})); | |
85 ''', [name, body, name, name]) | |
86 ]); | 84 ]); |
87 } | 85 } |
88 | 86 |
87 JS.Statement _initPrivateSymbol(String name) => | |
88 js.statement('let # = Symbol(#);', [name, js.string(name, "'")]); | |
89 | |
89 @override | 90 @override |
90 JS.Statement visitCompilationUnit(CompilationUnit node) { | 91 JS.Statement visitCompilationUnit(CompilationUnit node) { |
91 // TODO(jmesserly): scriptTag, directives. | 92 // TODO(jmesserly): scriptTag, directives. |
92 var body = <JS.Statement>[]; | 93 var body = <JS.Statement>[]; |
93 for (var child in node.declarations) { | 94 for (var child in node.declarations) { |
94 // Attempt to group adjacent fields/properties. | 95 // Attempt to group adjacent fields/properties. |
95 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); | 96 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); |
96 if (child is! FunctionDeclaration) _flushLibraryProperties(body); | 97 if (child is! FunctionDeclaration) _flushLibraryProperties(body); |
97 | 98 |
98 var code = _visit(child); | 99 var code = _visit(child); |
(...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
395 return new JS.Block(body)..sourceInformation = node; | 396 return new JS.Block(body)..sourceInformation = node; |
396 } | 397 } |
397 | 398 |
398 @override | 399 @override |
399 JS.Statement visitRedirectingConstructorInvocation( | 400 JS.Statement visitRedirectingConstructorInvocation( |
400 RedirectingConstructorInvocation node) { | 401 RedirectingConstructorInvocation node) { |
401 ClassDeclaration classDecl = node.parent.parent; | 402 ClassDeclaration classDecl = node.parent.parent; |
402 var className = classDecl.name.name; | 403 var className = classDecl.name.name; |
403 | 404 |
404 var name = _constructorName(className, node.constructorName); | 405 var name = _constructorName(className, node.constructorName); |
405 return js.statement('this.#(#);', [name, _visit(node.argumentList)]); | 406 return js.statement('this.#(#);', |
407 [_jsMemberName(name), _visit(node.argumentList)]); | |
406 } | 408 } |
407 | 409 |
408 JS.Statement _superConstructorCall(ClassDeclaration clazz, | 410 JS.Statement _superConstructorCall(ClassDeclaration clazz, |
409 [SuperConstructorInvocation node]) { | 411 [SuperConstructorInvocation node]) { |
410 var superCtorName = node != null ? node.constructorName : null; | 412 var superCtorName = node != null ? node.constructorName : null; |
411 | 413 |
412 var element = clazz.element; | 414 var element = clazz.element; |
413 if (superCtorName == null && | 415 if (superCtorName == null && |
414 (element.type.isObject || element.supertype.isObject)) { | 416 (element.type.isObject || element.supertype.isObject)) { |
415 return null; | 417 return null; |
(...skipping 29 matching lines...) Expand all Loading... | |
445 } | 447 } |
446 } | 448 } |
447 } | 449 } |
448 | 450 |
449 // Initialize fields from `this.fieldName` parameters. | 451 // Initialize fields from `this.fieldName` parameters. |
450 if (parameters != null) { | 452 if (parameters != null) { |
451 for (var p in parameters.parameters) { | 453 for (var p in parameters.parameters) { |
452 if (p is DefaultFormalParameter) p = p.parameter; | 454 if (p is DefaultFormalParameter) p = p.parameter; |
453 if (p is FieldFormalParameter) { | 455 if (p is FieldFormalParameter) { |
454 var name = p.identifier.name; | 456 var name = p.identifier.name; |
455 body.add(js.statement('this.# = #;', [name, name])); | 457 body.add(js.statement('this.# = #;', |
458 [_jsMemberName(name), _visit(p)])); | |
456 unsetFields.remove(name); | 459 unsetFields.remove(name); |
457 } | 460 } |
458 } | 461 } |
459 } | 462 } |
460 | 463 |
461 // Run constructor field initializers such as `: foo = bar.baz` | 464 // Run constructor field initializers such as `: foo = bar.baz` |
462 if (initializers != null) { | 465 if (initializers != null) { |
463 for (var init in initializers) { | 466 for (var init in initializers) { |
464 if (init is ConstructorFieldInitializer) { | 467 if (init is ConstructorFieldInitializer) { |
465 body.add(js.statement( | 468 body.add(js.statement( |
466 '# = #;', [_visit(init.fieldName), _visit(init.expression)])); | 469 '# = #;', [_visit(init.fieldName), _visit(init.expression)])); |
467 unsetFields.remove(init.fieldName.name); | 470 unsetFields.remove(init.fieldName.name); |
468 } | 471 } |
469 } | 472 } |
470 } | 473 } |
471 | 474 |
472 // Initialize all remaining fields | 475 // Initialize all remaining fields |
473 unsetFields.forEach((name, field) { | 476 unsetFields.forEach((name, field) { |
474 JS.Expression value; | 477 JS.Expression value; |
475 if (field.initializer != null) { | 478 if (field.initializer != null) { |
476 value = _visit(field.initializer); | 479 value = _visit(field.initializer); |
477 } else { | 480 } else { |
478 var type = rules.elementType(field.element); | 481 var type = rules.elementType(field.element); |
479 if (rules.maybeNonNullableType(type)) { | 482 if (rules.maybeNonNullableType(type)) { |
480 value = js.call('dart.as(null, #)', _emitTypeName(type)); | 483 value = js.call('dart.as(null, #)', _emitTypeName(type)); |
481 } else { | 484 } else { |
482 value = new JS.LiteralNull(); | 485 value = new JS.LiteralNull(); |
483 } | 486 } |
484 } | 487 } |
485 body.add(js.statement('this.# = #;', [name, value])); | 488 body.add(js.statement('this.# = #;', [_jsMemberName(name), value])); |
486 }); | 489 }); |
487 | 490 |
488 return _statement(body); | 491 return _statement(body); |
489 } | 492 } |
490 | 493 |
491 FormalParameterList _parametersOf(node) { | 494 FormalParameterList _parametersOf(node) { |
492 // Note: ConstructorDeclaration is intentionally skipped here so we can | 495 // Note: ConstructorDeclaration is intentionally skipped here so we can |
493 // emit the argument initializers in a different place. | 496 // emit the argument initializers in a different place. |
494 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we | 497 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we |
495 // could handle argument initializers more consistently in a separate | 498 // could handle argument initializers more consistently in a separate |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
543 | 546 |
544 @override | 547 @override |
545 JS.Method visitMethodDeclaration(MethodDeclaration node) { | 548 JS.Method visitMethodDeclaration(MethodDeclaration node) { |
546 if (node.isAbstract || _externalOrNative(node)) { | 549 if (node.isAbstract || _externalOrNative(node)) { |
547 return null; | 550 return null; |
548 } | 551 } |
549 | 552 |
550 var params = _visit(node.parameters); | 553 var params = _visit(node.parameters); |
551 if (params == null) params = []; | 554 if (params == null) params = []; |
552 | 555 |
553 return new JS.Method(new JS.PropertyName(_jsMethodName(node.name.name)), | 556 return new JS.Method(_jsMemberName(node.name.name), |
554 new JS.Fun(params, _visit(node.body)), | 557 new JS.Fun(params, _visit(node.body)), |
555 isGetter: node.isGetter, | 558 isGetter: node.isGetter, |
556 isSetter: node.isSetter, | 559 isSetter: node.isSetter, |
557 isStatic: node.isStatic); | 560 isStatic: node.isStatic); |
558 } | 561 } |
559 | 562 |
560 @override | 563 @override |
561 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 564 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { |
562 assert(node.parent is CompilationUnit); | 565 assert(node.parent is CompilationUnit); |
563 | 566 |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
636 var e = node.staticElement; | 639 var e = node.staticElement; |
637 if (e == null) { | 640 if (e == null) { |
638 return js.commentExpression( | 641 return js.commentExpression( |
639 'Unimplemented unknown name', new JS.VariableUse(node.name)); | 642 'Unimplemented unknown name', new JS.VariableUse(node.name)); |
640 } | 643 } |
641 var name = node.name; | 644 var name = node.name; |
642 if (e.enclosingElement is CompilationUnitElement && | 645 if (e.enclosingElement is CompilationUnitElement && |
643 (e.library != libraryInfo.library || _needsModuleGetter(e))) { | 646 (e.library != libraryInfo.library || _needsModuleGetter(e))) { |
644 return js.call('#.#', [jsLibraryName(e.library), name]); | 647 return js.call('#.#', [jsLibraryName(e.library), name]); |
645 } else if (currentClass != null && _needsImplicitThis(e)) { | 648 } else if (currentClass != null && _needsImplicitThis(e)) { |
646 return js.call('this.#', name); | 649 return js.call('this.#', _jsMemberName(name)); |
650 } else if (e is ParameterElement && e.isInitializingFormal) { | |
651 name = _fieldParameterName(name); | |
647 } | 652 } |
648 return new JS.VariableUse(name); | 653 return new JS.VariableUse(name); |
649 } | 654 } |
650 | 655 |
651 JS.Expression _emitTypeName(DartType type) { | 656 JS.Expression _emitTypeName(DartType type) { |
652 var name = type.name; | 657 var name = type.name; |
653 var lib = type.element.library; | 658 var lib = type.element.library; |
654 if (name == '') { | 659 if (name == '') { |
655 // TODO(jmesserly): remove when we're using coercion reifier. | 660 // TODO(jmesserly): remove when we're using coercion reifier. |
656 return _unimplementedCall('Unimplemented type $type'); | 661 return _unimplementedCall('Unimplemented type $type'); |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
843 } | 848 } |
844 | 849 |
845 @override | 850 @override |
846 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) { | 851 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) { |
847 var result = <JS.Parameter>[]; | 852 var result = <JS.Parameter>[]; |
848 for (FormalParameter param in node.parameters) { | 853 for (FormalParameter param in node.parameters) { |
849 if (param.kind == ParameterKind.NAMED) { | 854 if (param.kind == ParameterKind.NAMED) { |
850 result.add(new JS.Parameter(_jsNamedParameterName)); | 855 result.add(new JS.Parameter(_jsNamedParameterName)); |
851 break; | 856 break; |
852 } | 857 } |
853 result.add(new JS.Parameter(param.identifier.name)); | 858 result.add(_visit(param)); |
854 } | 859 } |
855 return result; | 860 return result; |
856 } | 861 } |
857 | 862 |
858 @override | 863 @override |
859 JS.Statement visitExpressionStatement(ExpressionStatement node) => | 864 JS.Statement visitExpressionStatement(ExpressionStatement node) => |
860 _expressionStatement(_visit(node.expression)); | 865 _expressionStatement(_visit(node.expression)); |
861 | 866 |
862 // Some expressions may choose to generate themselves as JS statements | 867 // Some expressions may choose to generate themselves as JS statements |
863 // if their parent is in a statement context. | 868 // if their parent is in a statement context. |
(...skipping 343 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1207 } | 1212 } |
1208 return false; | 1213 return false; |
1209 } | 1214 } |
1210 | 1215 |
1211 @override | 1216 @override |
1212 visitParenthesizedExpression(ParenthesizedExpression node) => | 1217 visitParenthesizedExpression(ParenthesizedExpression node) => |
1213 // The printer handles precedence so we don't need to. | 1218 // The printer handles precedence so we don't need to. |
1214 _visit(node.expression); | 1219 _visit(node.expression); |
1215 | 1220 |
1216 @override | 1221 @override |
1217 visitSimpleFormalParameter(SimpleFormalParameter node) => | 1222 visitFormalParameter(FormalParameter node) => |
1218 _visit(node.identifier); | 1223 new JS.Parameter(node.identifier.name); |
1219 | 1224 |
1220 @override | 1225 @override |
1221 visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) => | 1226 visitFieldFormalParameter(FieldFormalParameter node) => |
1222 _visit(node.identifier); | 1227 new JS.Parameter(_fieldParameterName(node.identifier.name)); |
1228 | |
1229 /// Rename private names so they don't shadow the private field symbol. | |
1230 // TODO(jmesserly): replace this ad-hoc rename with a general strategy. | |
1231 _fieldParameterName(name) => name.startsWith('_') ? '\$$name' : name; | |
1223 | 1232 |
1224 @override | 1233 @override |
1225 JS.This visitThisExpression(ThisExpression node) => new JS.This(); | 1234 JS.This visitThisExpression(ThisExpression node) => new JS.This(); |
1226 | 1235 |
1227 @override | 1236 @override |
1228 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); | 1237 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); |
1229 | 1238 |
1230 @override | 1239 @override |
1231 visitPrefixedIdentifier(PrefixedIdentifier node) { | 1240 visitPrefixedIdentifier(PrefixedIdentifier node) { |
1232 if (node.prefix.staticElement is PrefixElement) { | 1241 if (node.prefix.staticElement is PrefixElement) { |
1233 return _visit(node.identifier); | 1242 return _visit(node.identifier); |
1234 } else { | 1243 } else { |
1235 return _visitGet(node.prefix, node.identifier); | 1244 return _visitGet(node.prefix, node.identifier); |
1236 } | 1245 } |
1237 } | 1246 } |
1238 | 1247 |
1239 @override | 1248 @override |
1240 visitPropertyAccess(PropertyAccess node) => | 1249 visitPropertyAccess(PropertyAccess node) => |
1241 _visitGet(_getTarget(node), node.propertyName); | 1250 _visitGet(_getTarget(node), node.propertyName); |
1242 | 1251 |
1243 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. | 1252 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. |
1244 _visitGet(Expression target, SimpleIdentifier name) { | 1253 _visitGet(Expression target, SimpleIdentifier name) { |
1245 if (rules.isDynamicTarget(target)) { | 1254 if (rules.isDynamicTarget(target)) { |
1246 return js.call( | 1255 return js.call( |
1247 'dart.dload(#, #)', [_visit(target), js.string(name.name, "'")]); | 1256 'dart.dload(#, #)', [_visit(target), js.string(name.name, "'")]); |
1248 } else { | 1257 } else { |
1249 return js.call('#.#', [_visit(target), name.name]); | 1258 return js.call('#.#', [_visit(target), _jsMemberName(name.name)]); |
1250 } | 1259 } |
1251 } | 1260 } |
1252 | 1261 |
1253 @override | 1262 @override |
1254 visitIndexExpression(IndexExpression node) { | 1263 visitIndexExpression(IndexExpression node) { |
1255 var target = _getTarget(node); | 1264 var target = _getTarget(node); |
1256 var code; | 1265 var code; |
1257 if (rules.isDynamicTarget(target)) { | 1266 if (rules.isDynamicTarget(target)) { |
1258 code = 'dart.dindex(#, #)'; | 1267 code = 'dart.dindex(#, #)'; |
1259 } else { | 1268 } else { |
(...skipping 327 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1587 var jsExpr = _visit(node); | 1596 var jsExpr = _visit(node); |
1588 if (result == null) { | 1597 if (result == null) { |
1589 result = jsExpr; | 1598 result = jsExpr; |
1590 } else { | 1599 } else { |
1591 result = new JS.Binary(operator, result, jsExpr); | 1600 result = new JS.Binary(operator, result, jsExpr); |
1592 } | 1601 } |
1593 } | 1602 } |
1594 return result; | 1603 return result; |
1595 } | 1604 } |
1596 | 1605 |
1597 /// The following names are allowed for user-defined operators: | 1606 /// This handles member renaming for private names and operators. |
1607 /// | |
1608 /// Private names are generated using ES6 symbols: | |
1609 /// | |
1610 /// // At the top of the module: | |
1611 /// let _x = Symbol('_x'); | |
1612 /// let _y = Symbol('_y'); | |
1613 /// ... | |
1614 /// | |
1615 /// class Point { | |
1616 /// Point(x, y) { | |
1617 /// this[_x] = x; | |
1618 /// this[_y] = y; | |
1619 /// } | |
1620 /// get x() { return this[_x]; } | |
1621 /// get y() { return this[_y]; } | |
1622 /// } | |
1623 /// | |
1624 /// For user-defined operators the following names are allowed: | |
1598 /// | 1625 /// |
1599 /// <, >, <=, >=, ==, -, +, /, ˜/, *, %, |, ˆ, &, <<, >>, []=, [], ˜ | 1626 /// <, >, <=, >=, ==, -, +, /, ˜/, *, %, |, ˆ, &, <<, >>, []=, [], ˜ |
1600 /// | 1627 /// |
1601 /// For the indexing operators, we use `get` and `set` instead: | 1628 /// They generate code like: |
1629 /// | |
1630 /// x['+'](y) | |
1631 /// | |
1632 /// There are three exceptions: [], []= and unary -. | |
1633 /// The indexing operators we use `get` and `set` instead: | |
1602 /// | 1634 /// |
1603 /// x.get('hi') | 1635 /// x.get('hi') |
1604 /// x.set('hi', 123) | 1636 /// x.set('hi', 123) |
1605 /// | 1637 /// |
1606 /// This follows the same pattern as EcmaScript 6 Map: | 1638 /// This follows the same pattern as EcmaScript 6 Map: |
1607 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_ Objects/Map> | 1639 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_ Objects/Map> |
1608 /// | 1640 /// |
1609 /// For all others we use the operator name: | 1641 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed |
1610 /// | 1642 /// for this transformation to happen, otherwise binary minus is assumed. |
1611 /// x['+'](y) | |
1612 /// | 1643 /// |
1613 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 1644 /// Equality is a bit special, it is generated via the Dart `equals` runtime |
1614 /// helper, that checks for null. The user defined method is called '=='. | 1645 /// helper, that checks for null. The user defined method is called '=='. |
1615 String _jsMethodName(String name) { | 1646 /// |
1616 if (name == '[]') return 'get'; | 1647 JS.Expression _jsMemberName(String name, {bool unary: false}) { |
1617 if (name == '[]=') return 'set'; | 1648 if (name.startsWith('_')) { |
1618 return name; | 1649 _privateNames.add(name); |
1650 return new JS.VariableUse(name); | |
1651 } | |
1652 if (name == '[]') { | |
1653 name = 'get'; | |
1654 } else if (name == '[]=') { | |
1655 name = 'set'; | |
1656 } else if (unary && name == '-') { | |
1657 name = 'unary-'; | |
1658 } | |
1659 return new JS.PropertyName(name); | |
1619 } | 1660 } |
1620 | 1661 |
1621 bool _externalOrNative(node) => | 1662 bool _externalOrNative(node) => |
1622 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 1663 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
1623 | 1664 |
1624 FunctionBody _functionBody(node) => | 1665 FunctionBody _functionBody(node) => |
1625 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 1666 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
1626 | 1667 |
1627 String _maybeBindThis(node) { | 1668 String _maybeBindThis(node) { |
1628 if (currentClass == null) return ''; | 1669 if (currentClass == null) return ''; |
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1821 | 1862 |
1822 // TODO(jmesserly): in many cases marking the end will be unncessary. | 1863 // TODO(jmesserly): in many cases marking the end will be unncessary. |
1823 printer.mark(_location(node.end)); | 1864 printer.mark(_location(node.end)); |
1824 } | 1865 } |
1825 | 1866 |
1826 String _getIdentifier(AstNode node) { | 1867 String _getIdentifier(AstNode node) { |
1827 if (node is SimpleIdentifier) return node.name; | 1868 if (node is SimpleIdentifier) return node.name; |
1828 return null; | 1869 return null; |
1829 } | 1870 } |
1830 } | 1871 } |
OLD | NEW |