Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(99)

Side by Side Diff: lib/src/codegen/js_codegen.dart

Issue 963343002: implement private members, fixes #74 (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | lib/src/js/printer.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | lib/src/js/printer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698