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:collection' show HashSet; |
7 import 'dart:io' show Directory, File; | 8 import 'dart:io' show Directory, File; |
8 | 9 |
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 11 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
11 import 'package:analyzer/src/generated/constant.dart'; | 12 import 'package:analyzer/src/generated/constant.dart'; |
12 import 'package:analyzer/src/generated/element.dart'; | 13 import 'package:analyzer/src/generated/element.dart'; |
13 import 'package:analyzer/src/generated/scanner.dart' | 14 import 'package:analyzer/src/generated/scanner.dart' |
14 show StringToken, Token, TokenType; | 15 show StringToken, Token, TokenType; |
15 import 'package:source_maps/source_maps.dart' as srcmaps show Printer; | 16 import 'package:source_maps/source_maps.dart' as srcmaps show Printer; |
16 import 'package:source_maps/source_maps.dart' show SourceMapSpan; | 17 import 'package:source_maps/source_maps.dart' show SourceMapSpan; |
(...skipping 22 matching lines...) Expand all Loading... |
39 SimpleIdentifier _cascadeTarget; | 40 SimpleIdentifier _cascadeTarget; |
40 /// The variable for the current catch clause | 41 /// The variable for the current catch clause |
41 String _catchParameter; | 42 String _catchParameter; |
42 | 43 |
43 ClassDeclaration currentClass; | 44 ClassDeclaration currentClass; |
44 ConstantEvaluator _constEvaluator; | 45 ConstantEvaluator _constEvaluator; |
45 | 46 |
46 final _exports = <String>[]; | 47 final _exports = <String>[]; |
47 final _lazyFields = <VariableDeclaration>[]; | 48 final _lazyFields = <VariableDeclaration>[]; |
48 final _properties = <FunctionDeclaration>[]; | 49 final _properties = <FunctionDeclaration>[]; |
| 50 final _privateNames = new HashSet<String>(); |
| 51 final _pendingPrivateNames = <String>[]; |
49 | 52 |
50 JSCodegenVisitor(LibraryInfo libraryInfo, TypeRules rules) | 53 JSCodegenVisitor(LibraryInfo libraryInfo, TypeRules rules) |
51 : libraryInfo = libraryInfo, | 54 : libraryInfo = libraryInfo, |
52 rules = rules; | 55 rules = rules; |
53 | 56 |
54 Element get currentLibrary => libraryInfo.library; | 57 Element get currentLibrary => libraryInfo.library; |
55 | 58 |
56 JS.Block generateLibrary( | 59 JS.Block generateLibrary( |
57 Iterable<CompilationUnit> units, CheckerReporter reporter) { | 60 Iterable<CompilationUnit> units, CheckerReporter reporter) { |
58 var body = <JS.Statement>[]; | 61 var body = <JS.Statement>[]; |
(...skipping 11 matching lines...) Expand all Loading... |
70 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); | 73 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); |
71 | 74 |
72 // TODO(jmesserly): make these immutable in JS? | 75 // TODO(jmesserly): make these immutable in JS? |
73 for (var name in _exports) { | 76 for (var name in _exports) { |
74 body.add(js.statement('$_EXPORTS.# = #;', [name, name])); | 77 body.add(js.statement('$_EXPORTS.# = #;', [name, name])); |
75 } | 78 } |
76 | 79 |
77 var name = jsLibraryName(libraryInfo.library); | 80 var name = jsLibraryName(libraryInfo.library); |
78 return new JS.Block([ | 81 return new JS.Block([ |
79 js.statement('var #;', name), | 82 js.statement('var #;', name), |
80 js.statement(''' | 83 js.statement("(function($_EXPORTS) { 'use strict'; #; })(# || (# = {}));", |
81 (function ($_EXPORTS) { | 84 [body, name, name]) |
82 'use strict'; | |
83 #; | |
84 })(# || (# = {})); | |
85 ''', [body, name, name]) | |
86 ]); | 85 ]); |
87 } | 86 } |
88 | 87 |
| 88 JS.Statement _initPrivateSymbol(String name) => |
| 89 js.statement('let # = Symbol(#);', [name, js.string(name, "'")]); |
| 90 |
89 @override | 91 @override |
90 JS.Statement visitCompilationUnit(CompilationUnit node) { | 92 JS.Statement visitCompilationUnit(CompilationUnit node) { |
91 // TODO(jmesserly): scriptTag, directives. | 93 // TODO(jmesserly): scriptTag, directives. |
92 var body = <JS.Statement>[]; | 94 var body = <JS.Statement>[]; |
93 for (var child in node.declarations) { | 95 for (var child in node.declarations) { |
94 // Attempt to group adjacent fields/properties. | 96 // Attempt to group adjacent fields/properties. |
95 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); | 97 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); |
96 if (child is! FunctionDeclaration) _flushLibraryProperties(body); | 98 if (child is! FunctionDeclaration) _flushLibraryProperties(body); |
97 | 99 |
98 var code = _visit(child); | 100 var code = _visit(child); |
99 if (code != null) body.add(code); | 101 |
| 102 if (code != null) { |
| 103 if (_pendingPrivateNames.isNotEmpty) { |
| 104 body.addAll(_pendingPrivateNames.map(_initPrivateSymbol)); |
| 105 _pendingPrivateNames.clear(); |
| 106 } |
| 107 body.add(code); |
| 108 } |
100 } | 109 } |
| 110 |
101 // Flush any unwritten fields/properties. | 111 // Flush any unwritten fields/properties. |
102 _flushLazyFields(body); | 112 _flushLazyFields(body); |
103 _flushLibraryProperties(body); | 113 _flushLibraryProperties(body); |
| 114 |
| 115 assert(_pendingPrivateNames.isEmpty); |
104 return _statement(body); | 116 return _statement(body); |
105 } | 117 } |
106 | 118 |
107 bool isPublic(String name) => !name.startsWith('_'); | 119 bool isPublic(String name) => !name.startsWith('_'); |
108 | 120 |
109 /// Conversions that we don't handle end up here. | 121 /// Conversions that we don't handle end up here. |
110 @override | 122 @override |
111 visitConversion(Conversion node) { | 123 visitConversion(Conversion node) { |
112 var from = node.baseType; | 124 var from = node.baseType; |
113 var to = node.convertedType; | 125 var to = node.convertedType; |
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
395 return new JS.Block(body)..sourceInformation = node; | 407 return new JS.Block(body)..sourceInformation = node; |
396 } | 408 } |
397 | 409 |
398 @override | 410 @override |
399 JS.Statement visitRedirectingConstructorInvocation( | 411 JS.Statement visitRedirectingConstructorInvocation( |
400 RedirectingConstructorInvocation node) { | 412 RedirectingConstructorInvocation node) { |
401 ClassDeclaration classDecl = node.parent.parent; | 413 ClassDeclaration classDecl = node.parent.parent; |
402 var className = classDecl.name.name; | 414 var className = classDecl.name.name; |
403 | 415 |
404 var name = _constructorName(className, node.constructorName); | 416 var name = _constructorName(className, node.constructorName); |
405 return js.statement('this.#(#);', [name, _visit(node.argumentList)]); | 417 return js.statement( |
| 418 'this.#(#);', [_jsMemberName(name), _visit(node.argumentList)]); |
406 } | 419 } |
407 | 420 |
408 JS.Statement _superConstructorCall(ClassDeclaration clazz, | 421 JS.Statement _superConstructorCall(ClassDeclaration clazz, |
409 [SuperConstructorInvocation node]) { | 422 [SuperConstructorInvocation node]) { |
410 var superCtorName = node != null ? node.constructorName : null; | 423 var superCtorName = node != null ? node.constructorName : null; |
411 | 424 |
412 var element = clazz.element; | 425 var element = clazz.element; |
413 if (superCtorName == null && | 426 if (superCtorName == null && |
414 (element.type.isObject || element.supertype.isObject)) { | 427 (element.type.isObject || element.supertype.isObject)) { |
415 return null; | 428 return null; |
(...skipping 29 matching lines...) Expand all Loading... |
445 } | 458 } |
446 } | 459 } |
447 } | 460 } |
448 | 461 |
449 // Initialize fields from `this.fieldName` parameters. | 462 // Initialize fields from `this.fieldName` parameters. |
450 if (parameters != null) { | 463 if (parameters != null) { |
451 for (var p in parameters.parameters) { | 464 for (var p in parameters.parameters) { |
452 if (p is DefaultFormalParameter) p = p.parameter; | 465 if (p is DefaultFormalParameter) p = p.parameter; |
453 if (p is FieldFormalParameter) { | 466 if (p is FieldFormalParameter) { |
454 var name = p.identifier.name; | 467 var name = p.identifier.name; |
455 body.add(js.statement('this.# = #;', [name, name])); | 468 body.add( |
| 469 js.statement('this.# = #;', [_jsMemberName(name), _visit(p)])); |
456 unsetFields.remove(name); | 470 unsetFields.remove(name); |
457 } | 471 } |
458 } | 472 } |
459 } | 473 } |
460 | 474 |
461 // Run constructor field initializers such as `: foo = bar.baz` | 475 // Run constructor field initializers such as `: foo = bar.baz` |
462 if (initializers != null) { | 476 if (initializers != null) { |
463 for (var init in initializers) { | 477 for (var init in initializers) { |
464 if (init is ConstructorFieldInitializer) { | 478 if (init is ConstructorFieldInitializer) { |
465 body.add(js.statement( | 479 body.add(js.statement( |
466 '# = #;', [_visit(init.fieldName), _visit(init.expression)])); | 480 '# = #;', [_visit(init.fieldName), _visit(init.expression)])); |
467 unsetFields.remove(init.fieldName.name); | 481 unsetFields.remove(init.fieldName.name); |
468 } | 482 } |
469 } | 483 } |
470 } | 484 } |
471 | 485 |
472 // Initialize all remaining fields | 486 // Initialize all remaining fields |
473 unsetFields.forEach((name, field) { | 487 unsetFields.forEach((name, field) { |
474 JS.Expression value; | 488 JS.Expression value; |
475 if (field.initializer != null) { | 489 if (field.initializer != null) { |
476 value = _visit(field.initializer); | 490 value = _visit(field.initializer); |
477 } else { | 491 } else { |
478 var type = rules.elementType(field.element); | 492 var type = rules.elementType(field.element); |
479 if (rules.maybeNonNullableType(type)) { | 493 if (rules.maybeNonNullableType(type)) { |
480 value = js.call('dart.as(null, #)', _emitTypeName(type)); | 494 value = js.call('dart.as(null, #)', _emitTypeName(type)); |
481 } else { | 495 } else { |
482 value = new JS.LiteralNull(); | 496 value = new JS.LiteralNull(); |
483 } | 497 } |
484 } | 498 } |
485 body.add(js.statement('this.# = #;', [name, value])); | 499 body.add(js.statement('this.# = #;', [_jsMemberName(name), value])); |
486 }); | 500 }); |
487 | 501 |
488 return _statement(body); | 502 return _statement(body); |
489 } | 503 } |
490 | 504 |
491 FormalParameterList _parametersOf(node) { | 505 FormalParameterList _parametersOf(node) { |
492 // Note: ConstructorDeclaration is intentionally skipped here so we can | 506 // Note: ConstructorDeclaration is intentionally skipped here so we can |
493 // emit the argument initializers in a different place. | 507 // emit the argument initializers in a different place. |
494 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we | 508 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we |
495 // could handle argument initializers more consistently in a separate | 509 // could handle argument initializers more consistently in a separate |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
543 | 557 |
544 @override | 558 @override |
545 JS.Method visitMethodDeclaration(MethodDeclaration node) { | 559 JS.Method visitMethodDeclaration(MethodDeclaration node) { |
546 if (node.isAbstract || _externalOrNative(node)) { | 560 if (node.isAbstract || _externalOrNative(node)) { |
547 return null; | 561 return null; |
548 } | 562 } |
549 | 563 |
550 var params = _visit(node.parameters); | 564 var params = _visit(node.parameters); |
551 if (params == null) params = []; | 565 if (params == null) params = []; |
552 | 566 |
553 return new JS.Method(new JS.PropertyName(_jsMethodName(node.name.name)), | 567 return new JS.Method( |
554 new JS.Fun(params, _visit(node.body)), | 568 _jsMemberName(node.name.name), new JS.Fun(params, _visit(node.body)), |
555 isGetter: node.isGetter, | 569 isGetter: node.isGetter, |
556 isSetter: node.isSetter, | 570 isSetter: node.isSetter, |
557 isStatic: node.isStatic); | 571 isStatic: node.isStatic); |
558 } | 572 } |
559 | 573 |
560 @override | 574 @override |
561 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 575 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { |
562 assert(node.parent is CompilationUnit); | 576 assert(node.parent is CompilationUnit); |
563 | 577 |
564 if (_externalOrNative(node)) return null; | 578 if (_externalOrNative(node)) return null; |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
636 var e = node.staticElement; | 650 var e = node.staticElement; |
637 if (e == null) { | 651 if (e == null) { |
638 return js.commentExpression( | 652 return js.commentExpression( |
639 'Unimplemented unknown name', new JS.VariableUse(node.name)); | 653 'Unimplemented unknown name', new JS.VariableUse(node.name)); |
640 } | 654 } |
641 var name = node.name; | 655 var name = node.name; |
642 if (e.enclosingElement is CompilationUnitElement && | 656 if (e.enclosingElement is CompilationUnitElement && |
643 (e.library != libraryInfo.library || _needsModuleGetter(e))) { | 657 (e.library != libraryInfo.library || _needsModuleGetter(e))) { |
644 return js.call('#.#', [_libraryName(e.library), name]); | 658 return js.call('#.#', [_libraryName(e.library), name]); |
645 } else if (currentClass != null && _needsImplicitThis(e)) { | 659 } else if (currentClass != null && _needsImplicitThis(e)) { |
646 return js.call('this.#', name); | 660 return js.call('this.#', _jsMemberName(name)); |
| 661 } else if (e is ParameterElement && e.isInitializingFormal) { |
| 662 name = _fieldParameterName(name); |
647 } | 663 } |
648 return new JS.VariableUse(name); | 664 return new JS.VariableUse(name); |
649 } | 665 } |
650 | 666 |
651 JS.Expression _emitTypeName(DartType type) { | 667 JS.Expression _emitTypeName(DartType type) { |
652 var name = type.name; | 668 var name = type.name; |
653 var lib = type.element.library; | 669 var lib = type.element.library; |
654 if (name == '') { | 670 if (name == '') { |
655 // TODO(jmesserly): remove when we're using coercion reifier. | 671 // TODO(jmesserly): remove when we're using coercion reifier. |
656 return _unimplementedCall('Unimplemented type $type'); | 672 return _unimplementedCall('Unimplemented type $type'); |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
843 } | 859 } |
844 | 860 |
845 @override | 861 @override |
846 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) { | 862 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) { |
847 var result = <JS.Parameter>[]; | 863 var result = <JS.Parameter>[]; |
848 for (FormalParameter param in node.parameters) { | 864 for (FormalParameter param in node.parameters) { |
849 if (param.kind == ParameterKind.NAMED) { | 865 if (param.kind == ParameterKind.NAMED) { |
850 result.add(new JS.Parameter(_jsNamedParameterName)); | 866 result.add(new JS.Parameter(_jsNamedParameterName)); |
851 break; | 867 break; |
852 } | 868 } |
853 result.add(new JS.Parameter(param.identifier.name)); | 869 result.add(_visit(param)); |
854 } | 870 } |
855 return result; | 871 return result; |
856 } | 872 } |
857 | 873 |
858 @override | 874 @override |
859 JS.Statement visitExpressionStatement(ExpressionStatement node) => | 875 JS.Statement visitExpressionStatement(ExpressionStatement node) => |
860 _expressionStatement(_visit(node.expression)); | 876 _expressionStatement(_visit(node.expression)); |
861 | 877 |
862 // Some expressions may choose to generate themselves as JS statements | 878 // Some expressions may choose to generate themselves as JS statements |
863 // if their parent is in a statement context. | 879 // if their parent is in a statement context. |
(...skipping 342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1206 } | 1222 } |
1207 return false; | 1223 return false; |
1208 } | 1224 } |
1209 | 1225 |
1210 @override | 1226 @override |
1211 visitParenthesizedExpression(ParenthesizedExpression node) => | 1227 visitParenthesizedExpression(ParenthesizedExpression node) => |
1212 // The printer handles precedence so we don't need to. | 1228 // The printer handles precedence so we don't need to. |
1213 _visit(node.expression); | 1229 _visit(node.expression); |
1214 | 1230 |
1215 @override | 1231 @override |
1216 visitSimpleFormalParameter(SimpleFormalParameter node) => | 1232 visitFormalParameter(FormalParameter node) => |
1217 _visit(node.identifier); | 1233 new JS.Parameter(node.identifier.name); |
1218 | 1234 |
1219 @override | 1235 @override |
1220 visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) => | 1236 visitFieldFormalParameter(FieldFormalParameter node) => |
1221 _visit(node.identifier); | 1237 new JS.Parameter(_fieldParameterName(node.identifier.name)); |
| 1238 |
| 1239 /// Rename private names so they don't shadow the private field symbol. |
| 1240 // TODO(jmesserly): replace this ad-hoc rename with a general strategy. |
| 1241 _fieldParameterName(name) => name.startsWith('_') ? '\$$name' : name; |
1222 | 1242 |
1223 @override | 1243 @override |
1224 JS.This visitThisExpression(ThisExpression node) => new JS.This(); | 1244 JS.This visitThisExpression(ThisExpression node) => new JS.This(); |
1225 | 1245 |
1226 @override | 1246 @override |
1227 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); | 1247 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); |
1228 | 1248 |
1229 @override | 1249 @override |
1230 visitPrefixedIdentifier(PrefixedIdentifier node) { | 1250 visitPrefixedIdentifier(PrefixedIdentifier node) { |
1231 if (node.prefix.staticElement is PrefixElement) { | 1251 if (node.prefix.staticElement is PrefixElement) { |
1232 return _visit(node.identifier); | 1252 return _visit(node.identifier); |
1233 } else { | 1253 } else { |
1234 return _visitGet(node.prefix, node.identifier); | 1254 return _visitGet(node.prefix, node.identifier); |
1235 } | 1255 } |
1236 } | 1256 } |
1237 | 1257 |
1238 @override | 1258 @override |
1239 visitPropertyAccess(PropertyAccess node) => | 1259 visitPropertyAccess(PropertyAccess node) => |
1240 _visitGet(_getTarget(node), node.propertyName); | 1260 _visitGet(_getTarget(node), node.propertyName); |
1241 | 1261 |
1242 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. | 1262 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. |
1243 _visitGet(Expression target, SimpleIdentifier name) { | 1263 _visitGet(Expression target, SimpleIdentifier name) { |
1244 if (rules.isDynamicTarget(target)) { | 1264 if (rules.isDynamicTarget(target)) { |
1245 return js.call( | 1265 return js.call( |
1246 'dart.dload(#, #)', [_visit(target), js.string(name.name, "'")]); | 1266 'dart.dload(#, #)', [_visit(target), js.string(name.name, "'")]); |
1247 } else { | 1267 } else { |
1248 return js.call('#.#', [_visit(target), name.name]); | 1268 return js.call('#.#', [_visit(target), _jsMemberName(name.name)]); |
1249 } | 1269 } |
1250 } | 1270 } |
1251 | 1271 |
1252 @override | 1272 @override |
1253 visitIndexExpression(IndexExpression node) { | 1273 visitIndexExpression(IndexExpression node) { |
1254 var target = _getTarget(node); | 1274 var target = _getTarget(node); |
1255 var code; | 1275 var code; |
1256 if (rules.isDynamicTarget(target)) { | 1276 if (rules.isDynamicTarget(target)) { |
1257 code = 'dart.dindex(#, #)'; | 1277 code = 'dart.dindex(#, #)'; |
1258 } else { | 1278 } else { |
(...skipping 23 matching lines...) Expand all Loading... |
1282 visitThrowExpression(ThrowExpression node) { | 1302 visitThrowExpression(ThrowExpression node) { |
1283 var expr = _visit(node.expression); | 1303 var expr = _visit(node.expression); |
1284 if (node.parent is ExpressionStatement) { | 1304 if (node.parent is ExpressionStatement) { |
1285 return js.statement('throw #;', expr); | 1305 return js.statement('throw #;', expr); |
1286 } else { | 1306 } else { |
1287 return js.call('dart.throw_(#)', expr); | 1307 return js.call('dart.throw_(#)', expr); |
1288 } | 1308 } |
1289 } | 1309 } |
1290 | 1310 |
1291 @override | 1311 @override |
1292 visitRethrowExpression(RethrowExpression node){ | 1312 visitRethrowExpression(RethrowExpression node) { |
1293 if (node.parent is ExpressionStatement) { | 1313 if (node.parent is ExpressionStatement) { |
1294 return js.statement('throw #;', _catchParameter); | 1314 return js.statement('throw #;', _catchParameter); |
1295 } else { | 1315 } else { |
1296 return js.call('dart.throw_(#)', _catchParameter); | 1316 return js.call('dart.throw_(#)', _catchParameter); |
1297 } | 1317 } |
1298 } | 1318 } |
1299 | 1319 |
1300 @override | 1320 @override |
1301 JS.If visitIfStatement(IfStatement node) { | 1321 JS.If visitIfStatement(IfStatement node) { |
1302 return new JS.If(_visit(node.condition), _visit(node.thenStatement), | 1322 return new JS.If(_visit(node.condition), _visit(node.thenStatement), |
(...skipping 301 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1604 var jsExpr = _visit(node); | 1624 var jsExpr = _visit(node); |
1605 if (result == null) { | 1625 if (result == null) { |
1606 result = jsExpr; | 1626 result = jsExpr; |
1607 } else { | 1627 } else { |
1608 result = new JS.Binary(operator, result, jsExpr); | 1628 result = new JS.Binary(operator, result, jsExpr); |
1609 } | 1629 } |
1610 } | 1630 } |
1611 return result; | 1631 return result; |
1612 } | 1632 } |
1613 | 1633 |
1614 /// The following names are allowed for user-defined operators: | 1634 /// This handles member renaming for private names and operators. |
| 1635 /// |
| 1636 /// Private names are generated using ES6 symbols: |
| 1637 /// |
| 1638 /// // At the top of the module: |
| 1639 /// let _x = Symbol('_x'); |
| 1640 /// let _y = Symbol('_y'); |
| 1641 /// ... |
| 1642 /// |
| 1643 /// class Point { |
| 1644 /// Point(x, y) { |
| 1645 /// this[_x] = x; |
| 1646 /// this[_y] = y; |
| 1647 /// } |
| 1648 /// get x() { return this[_x]; } |
| 1649 /// get y() { return this[_y]; } |
| 1650 /// } |
| 1651 /// |
| 1652 /// For user-defined operators the following names are allowed: |
1615 /// | 1653 /// |
1616 /// <, >, <=, >=, ==, -, +, /, ˜/, *, %, |, ˆ, &, <<, >>, []=, [], ˜ | 1654 /// <, >, <=, >=, ==, -, +, /, ˜/, *, %, |, ˆ, &, <<, >>, []=, [], ˜ |
1617 /// | 1655 /// |
1618 /// For the indexing operators, we use `get` and `set` instead: | 1656 /// They generate code like: |
| 1657 /// |
| 1658 /// x['+'](y) |
| 1659 /// |
| 1660 /// There are three exceptions: [], []= and unary -. |
| 1661 /// The indexing operators we use `get` and `set` instead: |
1619 /// | 1662 /// |
1620 /// x.get('hi') | 1663 /// x.get('hi') |
1621 /// x.set('hi', 123) | 1664 /// x.set('hi', 123) |
1622 /// | 1665 /// |
1623 /// This follows the same pattern as EcmaScript 6 Map: | 1666 /// This follows the same pattern as EcmaScript 6 Map: |
1624 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_
Objects/Map> | 1667 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_
Objects/Map> |
1625 /// | 1668 /// |
1626 /// For all others we use the operator name: | 1669 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed |
1627 /// | 1670 /// for this transformation to happen, otherwise binary minus is assumed. |
1628 /// x['+'](y) | |
1629 /// | 1671 /// |
1630 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 1672 /// Equality is a bit special, it is generated via the Dart `equals` runtime |
1631 /// helper, that checks for null. The user defined method is called '=='. | 1673 /// helper, that checks for null. The user defined method is called '=='. |
1632 String _jsMethodName(String name) { | 1674 /// |
1633 if (name == '[]') return 'get'; | 1675 JS.Expression _jsMemberName(String name, {bool unary: false}) { |
1634 if (name == '[]=') return 'set'; | 1676 if (name.startsWith('_')) { |
1635 return name; | 1677 if (_privateNames.add(name)) _pendingPrivateNames.add(name); |
| 1678 return new JS.VariableUse(name); |
| 1679 } |
| 1680 if (name == '[]') { |
| 1681 name = 'get'; |
| 1682 } else if (name == '[]=') { |
| 1683 name = 'set'; |
| 1684 } else if (unary && name == '-') { |
| 1685 name = 'unary-'; |
| 1686 } |
| 1687 return new JS.PropertyName(name); |
1636 } | 1688 } |
1637 | 1689 |
1638 bool _externalOrNative(node) => | 1690 bool _externalOrNative(node) => |
1639 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 1691 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
1640 | 1692 |
1641 FunctionBody _functionBody(node) => | 1693 FunctionBody _functionBody(node) => |
1642 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 1694 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
1643 | 1695 |
1644 /// Choose a canonical name from the library element. | 1696 /// Choose a canonical name from the library element. |
1645 /// This never uses the library's name (the identifier in the `library` | 1697 /// This never uses the library's name (the identifier in the `library` |
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1852 | 1904 |
1853 // TODO(jmesserly): in many cases marking the end will be unncessary. | 1905 // TODO(jmesserly): in many cases marking the end will be unncessary. |
1854 printer.mark(_location(node.end)); | 1906 printer.mark(_location(node.end)); |
1855 } | 1907 } |
1856 | 1908 |
1857 String _getIdentifier(AstNode node) { | 1909 String _getIdentifier(AstNode node) { |
1858 if (node is SimpleIdentifier) return node.name; | 1910 if (node is SimpleIdentifier) return node.name; |
1859 return null; | 1911 return null; |
1860 } | 1912 } |
1861 } | 1913 } |
OLD | NEW |