4
4
5
5
library ddc.src.codegen.js_codegen;
6
6
7
+ import 'dart:collection' show HashSet;
7
8
import 'dart:io' show Directory, File;
8
9
9
10
import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
@@ -46,6 +47,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
46
47
final _exports = < String > [];
47
48
final _lazyFields = < VariableDeclaration > [];
48
49
final _properties = < FunctionDeclaration > [];
50
+ final _privateNames = new HashSet <String >();
51
+ final _pendingPrivateNames = < String > [];
49
52
50
53
JSCodegenVisitor (LibraryInfo libraryInfo, TypeRules rules)
51
54
: libraryInfo = libraryInfo,
@@ -77,15 +80,14 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
77
80
var name = jsLibraryName (libraryInfo.library);
78
81
return new JS .Block ([
79
82
js.statement ('var #;' , name),
80
- js.statement ('''
81
- (function ($_EXPORTS ) {
82
- 'use strict';
83
- #;
84
- })(# || (# = {}));
85
- ''' , [body, name, name])
83
+ js.statement ("(function($_EXPORTS ) { 'use strict'; #; })(# || (# = {}));" ,
84
+ [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
91
@override
90
92
JS .Statement visitCompilationUnit (CompilationUnit node) {
91
93
// TODO(jmesserly): scriptTag, directives.
@@ -96,11 +98,21 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
96
98
if (child is ! FunctionDeclaration ) _flushLibraryProperties (body);
97
99
98
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
111
// Flush any unwritten fields/properties.
102
112
_flushLazyFields (body);
103
113
_flushLibraryProperties (body);
114
+
115
+ assert (_pendingPrivateNames.isEmpty);
104
116
return _statement (body);
105
117
}
106
118
@@ -402,7 +414,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
402
414
var className = classDecl.name.name;
403
415
404
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
421
JS .Statement _superConstructorCall (ClassDeclaration clazz,
@@ -452,7 +465,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
452
465
if (p is DefaultFormalParameter ) p = p.parameter;
453
466
if (p is FieldFormalParameter ) {
454
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
470
unsetFields.remove (name);
457
471
}
458
472
}
@@ -482,7 +496,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
482
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
502
return _statement (body);
@@ -550,8 +564,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
550
564
var params = _visit (node.parameters);
551
565
if (params == null ) params = [];
552
566
553
- return new JS .Method (new JS . PropertyName ( _jsMethodName (node.name.name)),
554
- new JS .Fun (params, _visit (node.body)),
567
+ return new JS .Method (
568
+ _jsMemberName (node.name.name), new JS .Fun (params, _visit (node.body)),
555
569
isGetter: node.isGetter,
556
570
isSetter: node.isSetter,
557
571
isStatic: node.isStatic);
@@ -643,7 +657,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
643
657
(e.library != libraryInfo.library || _needsModuleGetter (e))) {
644
658
return js.call ('#.#' , [_libraryName (e.library), name]);
645
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
664
return new JS .VariableUse (name);
649
665
}
@@ -850,7 +866,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
850
866
result.add (new JS .Parameter (_jsNamedParameterName));
851
867
break ;
852
868
}
853
- result.add (new JS . Parameter (param.identifier.name ));
869
+ result.add (_visit (param));
854
870
}
855
871
return result;
856
872
}
@@ -1213,12 +1229,16 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
1213
1229
_visit (node.expression);
1214
1230
1215
1231
@override
1216
- visitSimpleFormalParameter ( SimpleFormalParameter node) =>
1217
- _visit (node.identifier);
1232
+ visitFormalParameter ( FormalParameter node) =>
1233
+ new JS . Parameter (node.identifier.name );
1218
1234
1219
1235
@override
1220
- visitFunctionTypedFormalParameter (FunctionTypedFormalParameter node) =>
1221
- _visit (node.identifier);
1236
+ visitFieldFormalParameter (FieldFormalParameter node) =>
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
1243
@override
1224
1244
JS .This visitThisExpression (ThisExpression node) => new JS .This ();
@@ -1245,7 +1265,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
1245
1265
return js.call (
1246
1266
'dart.dload(#, #)' , [_visit (target), js.string (name.name, "'" )]);
1247
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
@@ -1289,7 +1309,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
1289
1309
}
1290
1310
1291
1311
@override
1292
- visitRethrowExpression (RethrowExpression node){
1312
+ visitRethrowExpression (RethrowExpression node) {
1293
1313
if (node.parent is ExpressionStatement ) {
1294
1314
return js.statement ('throw #;' , _catchParameter);
1295
1315
} else {
@@ -1611,28 +1631,60 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
1611
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
1663
/// x.get('hi')
1621
1664
/// x.set('hi', 123)
1622
1665
///
1623
1666
/// This follows the same pattern as EcmaScript 6 Map:
1624
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:
1627
- ///
1628
- /// x['+'](y)
1669
+ /// Unary minus looks like: `x['unary-']()` . Note that [unary] must be passed
1670
+ /// for this transformation to happen, otherwise binary minus is assumed.
1629
1671
///
1630
1672
/// Equality is a bit special, it is generated via the Dart `equals` runtime
1631
1673
/// helper, that checks for null. The user defined method is called '=='.
1632
- String _jsMethodName (String name) {
1633
- if (name == '[]' ) return 'get' ;
1634
- if (name == '[]=' ) return 'set' ;
1635
- return name;
1674
+ ///
1675
+ JS .Expression _jsMemberName (String name, {bool unary: false }) {
1676
+ if (name.startsWith ('_' )) {
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
1690
bool _externalOrNative (node) =>
0 commit comments