Chromium Code Reviews| Index: lib/src/codegen/js_codegen.dart |
| diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart |
| index 1f69fefc77ace646444ca3f6ddc6c8f4f11fbdbb..a08c7531a576e6e388f8fc6a01de7611aa7db211 100644 |
| --- a/lib/src/codegen/js_codegen.dart |
| +++ b/lib/src/codegen/js_codegen.dart |
| @@ -45,6 +45,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| final _exports = <String>[]; |
| final _lazyFields = <VariableDeclaration>[]; |
| final _properties = <FunctionDeclaration>[]; |
| + final _privateNames = new Set<String>(); |
|
Jennifer Messerly
2015/03/02 18:48:16
hmmm, another thought ... we could emit these as w
|
| JSCodegenVisitor(LibraryInfo libraryInfo, TypeRules rules) |
| : libraryInfo = libraryInfo, |
| @@ -75,17 +76,17 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| } |
| var name = _libraryName; |
| + var symbolInit = _privateNames.map(_initPrivateSymbol); |
| return new JS.Block([ |
| js.statement('var #;', name), |
| - js.statement(''' |
| - (function (#) { |
| - 'use strict'; |
| - #; |
| - })(# || (# = {})); |
| - ''', [name, body, name, name]) |
| + js.statement("(function (#) { 'use strict'; #; #; })(# || (# = {}));", |
| + [name, symbolInit, body, name, name]) |
| ]); |
| } |
| + JS.Statement _initPrivateSymbol(String name) => |
| + js.statement('let # = Symbol(#);', [name, js.string(name, "'")]); |
| + |
| @override |
| JS.Statement visitCompilationUnit(CompilationUnit node) { |
| // TODO(jmesserly): scriptTag, directives. |
| @@ -402,7 +403,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| var className = classDecl.name.name; |
| var name = _constructorName(className, node.constructorName); |
| - return js.statement('this.#(#);', [name, _visit(node.argumentList)]); |
| + return js.statement('this.#(#);', |
| + [_jsMemberName(name), _visit(node.argumentList)]); |
| } |
| JS.Statement _superConstructorCall(ClassDeclaration clazz, |
| @@ -452,7 +454,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| if (p is DefaultFormalParameter) p = p.parameter; |
| if (p is FieldFormalParameter) { |
| var name = p.identifier.name; |
| - body.add(js.statement('this.# = #;', [name, name])); |
| + body.add(js.statement('this.# = #;', |
| + [_jsMemberName(name), _visit(p)])); |
| unsetFields.remove(name); |
| } |
| } |
| @@ -482,7 +485,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| value = new JS.LiteralNull(); |
| } |
| } |
| - body.add(js.statement('this.# = #;', [name, value])); |
| + body.add(js.statement('this.# = #;', [_jsMemberName(name), value])); |
| }); |
| return _statement(body); |
| @@ -550,7 +553,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| var params = _visit(node.parameters); |
| if (params == null) params = []; |
| - return new JS.Method(new JS.PropertyName(_jsMethodName(node.name.name)), |
| + return new JS.Method(_jsMemberName(node.name.name), |
| new JS.Fun(params, _visit(node.body)), |
| isGetter: node.isGetter, |
| isSetter: node.isSetter, |
| @@ -643,7 +646,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| (e.library != libraryInfo.library || _needsModuleGetter(e))) { |
| return js.call('#.#', [jsLibraryName(e.library), name]); |
| } else if (currentClass != null && _needsImplicitThis(e)) { |
| - return js.call('this.#', name); |
| + return js.call('this.#', _jsMemberName(name)); |
| + } else if (e is ParameterElement && e.isInitializingFormal) { |
| + name = _fieldParameterName(name); |
| } |
| return new JS.VariableUse(name); |
| } |
| @@ -850,7 +855,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| result.add(new JS.Parameter(_jsNamedParameterName)); |
| break; |
| } |
| - result.add(new JS.Parameter(param.identifier.name)); |
| + result.add(_visit(param)); |
| } |
| return result; |
| } |
| @@ -1214,12 +1219,16 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| _visit(node.expression); |
| @override |
| - visitSimpleFormalParameter(SimpleFormalParameter node) => |
| - _visit(node.identifier); |
| + visitFormalParameter(FormalParameter node) => |
| + new JS.Parameter(node.identifier.name); |
| @override |
| - visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) => |
| - _visit(node.identifier); |
| + visitFieldFormalParameter(FieldFormalParameter node) => |
| + new JS.Parameter(_fieldParameterName(node.identifier.name)); |
| + |
| + /// Rename private names so they don't shadow the private field symbol. |
| + // TODO(jmesserly): replace this ad-hoc rename with a general strategy. |
| + _fieldParameterName(name) => name.startsWith('_') ? '\$$name' : name; |
| @override |
| JS.This visitThisExpression(ThisExpression node) => new JS.This(); |
| @@ -1246,7 +1255,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| return js.call( |
| 'dart.dload(#, #)', [_visit(target), js.string(name.name, "'")]); |
| } else { |
| - return js.call('#.#', [_visit(target), name.name]); |
| + return js.call('#.#', [_visit(target), _jsMemberName(name.name)]); |
| } |
| } |
| @@ -1594,11 +1603,34 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| return result; |
| } |
| - /// The following names are allowed for user-defined operators: |
| + /// This handles member renaming for private names and operators. |
| + /// |
| + /// Private names are generated using ES6 symbols: |
| + /// |
| + /// // At the top of the module: |
| + /// let _x = Symbol('_x'); |
| + /// let _y = Symbol('_y'); |
| + /// ... |
| + /// |
| + /// class Point { |
| + /// Point(x, y) { |
| + /// this[_x] = x; |
| + /// this[_y] = y; |
| + /// } |
| + /// get x() { return this[_x]; } |
| + /// get y() { return this[_y]; } |
| + /// } |
| + /// |
| + /// For user-defined operators the following names are allowed: |
| /// |
| /// <, >, <=, >=, ==, -, +, /, ˜/, *, %, |, ˆ, &, <<, >>, []=, [], ˜ |
| /// |
| - /// For the indexing operators, we use `get` and `set` instead: |
| + /// They generate code like: |
| + /// |
| + /// x['+'](y) |
| + /// |
| + /// There are three exceptions: [], []= and unary -. |
| + /// The indexing operators we use `get` and `set` instead: |
| /// |
| /// x.get('hi') |
| /// x.set('hi', 123) |
| @@ -1606,16 +1638,25 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| /// This follows the same pattern as EcmaScript 6 Map: |
| /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map> |
| /// |
| - /// For all others we use the operator name: |
| - /// |
| - /// x['+'](y) |
| + /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed |
| + /// for this transformation to happen, otherwise binary minus is assumed. |
| /// |
| /// Equality is a bit special, it is generated via the Dart `equals` runtime |
| /// helper, that checks for null. The user defined method is called '=='. |
| - String _jsMethodName(String name) { |
| - if (name == '[]') return 'get'; |
| - if (name == '[]=') return 'set'; |
| - return name; |
| + /// |
| + JS.Expression _jsMemberName(String name, {bool unary: false}) { |
| + if (name.startsWith('_')) { |
| + _privateNames.add(name); |
| + return new JS.VariableUse(name); |
| + } |
| + if (name == '[]') { |
| + name = 'get'; |
| + } else if (name == '[]=') { |
| + name = 'set'; |
| + } else if (unary && name == '-') { |
| + name = 'unary-'; |
| + } |
| + return new JS.PropertyName(name); |
| } |
| bool _externalOrNative(node) => |