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

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

Issue 1100633006: Generate static calls for Object fields and methods (Closed) Base URL: https://github.com/dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 8 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
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 dev_compiler.src.codegen.js_codegen; 5 library dev_compiler.src.codegen.js_codegen;
6 6
7 import 'dart:collection' show HashSet, HashMap; 7 import 'dart:collection' show HashSet, HashMap;
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 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
61 final HashSet<FieldElement> _fieldsNeedingStorage; 61 final HashSet<FieldElement> _fieldsNeedingStorage;
62 62
63 /// The variable for the target of the current `..` cascade expression. 63 /// The variable for the target of the current `..` cascade expression.
64 SimpleIdentifier _cascadeTarget; 64 SimpleIdentifier _cascadeTarget;
65 65
66 /// The variable for the current catch clause 66 /// The variable for the current catch clause
67 SimpleIdentifier _catchParameter; 67 SimpleIdentifier _catchParameter;
68 68
69 ConstantEvaluator _constEvaluator; 69 ConstantEvaluator _constEvaluator;
70 70
71 /// Map of fields / properties / methods on Object.
72 Map<String, DartType> _objectMembers;
73
71 final _exports = new Set<String>(); 74 final _exports = new Set<String>();
72 final _lazyFields = <VariableDeclaration>[]; 75 final _lazyFields = <VariableDeclaration>[];
73 final _properties = <FunctionDeclaration>[]; 76 final _properties = <FunctionDeclaration>[];
74 final _privateNames = new HashMap<String, JSTemporary>(); 77 final _privateNames = new HashMap<String, JSTemporary>();
75 final _extensionMethodNames = new HashSet<String>(); 78 final _extensionMethodNames = new HashSet<String>();
76 final _pendingStatements = <JS.Statement>[]; 79 final _pendingStatements = <JS.Statement>[];
77 final _temps = new HashMap<Element, JSTemporary>(); 80 final _temps = new HashMap<Element, JSTemporary>();
78 81
79 /// The name for the library's exports inside itself. 82 /// The name for the library's exports inside itself.
80 /// This much be a constant because we interpolate it into template strings, 83 /// This much be a constant because we interpolate it into template strings,
81 /// and otherwise it would break caching for them. 84 /// and otherwise it would break caching for them.
82 /// `exports` was chosen as the most similar to ES module patterns. 85 /// `exports` was chosen as the most similar to ES module patterns.
83 final JSTemporary _exportsVar = new JSTemporary('exports'); 86 final JSTemporary _exportsVar = new JSTemporary('exports');
84 final JSTemporary _namedArgTemp = new JSTemporary('opts'); 87 final JSTemporary _namedArgTemp = new JSTemporary('opts');
85 88
86 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or 89 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or
87 /// [ClassTypeAlias]. 90 /// [ClassTypeAlias].
88 final _pendingClasses = new HashMap<Element, CompilationUnitMember>(); 91 final _pendingClasses = new HashMap<Element, CompilationUnitMember>();
89 92
90 /// Memoized results of [_lazyClass]. 93 /// Memoized results of [_lazyClass].
91 final _lazyClassMemo = new HashMap<Element, bool>(); 94 final _lazyClassMemo = new HashMap<Element, bool>();
92 95
93 /// Memoized results of [_inLibraryCycle]. 96 /// Memoized results of [_inLibraryCycle].
94 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); 97 final _libraryCycleMemo = new HashMap<LibraryElement, bool>();
95 98
96 JSCodegenVisitor(this.options, this.rules, this.libraryInfo, 99 JSCodegenVisitor(this.options, this.rules, this.libraryInfo,
97 this._extensionMethods, this._fieldsNeedingStorage); 100 this._extensionMethods, this._fieldsNeedingStorage) {
101 _objectMembers = getObjectMemberMap(rules.provider);
Jennifer Messerly 2015/04/23 19:01:42 thought here, TypeRules is our type (src/checker/r
vsm 2015/04/23 20:35:55 Yeah, I suppose that's better than here. Done.
102 }
98 103
99 LibraryElement get currentLibrary => libraryInfo.library; 104 LibraryElement get currentLibrary => libraryInfo.library;
100 TypeProvider get types => rules.provider; 105 TypeProvider get types => rules.provider;
101 106
102 JS.Program emitLibrary(LibraryUnit library) { 107 JS.Program emitLibrary(LibraryUnit library) {
103 String jsDefaultValue = null; 108 String jsDefaultValue = null;
104 109
105 // Modify the AST to make coercions explicit. 110 // Modify the AST to make coercions explicit.
106 new CoercionReifier(library, rules, options).reify(); 111 new CoercionReifier(library, rules, options).reify();
107 112
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 183
179 // TODO(jmesserly): scriptTag, directives. 184 // TODO(jmesserly): scriptTag, directives.
180 var body = <JS.Statement>[]; 185 var body = <JS.Statement>[];
181 for (var child in node.declarations) { 186 for (var child in node.declarations) {
182 // Attempt to group adjacent fields/properties. 187 // Attempt to group adjacent fields/properties.
183 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); 188 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body);
184 if (child is! FunctionDeclaration) _flushLibraryProperties(body); 189 if (child is! FunctionDeclaration) _flushLibraryProperties(body);
185 190
186 var code = _visit(child); 191 var code = _visit(child);
187 if (code != null) { 192 if (code != null) {
188 if (_pendingStatements.isNotEmpty) { 193 _flushPendingStatements(body);
189 body.addAll(_pendingStatements);
190 _pendingStatements.clear();
191 }
192 body.add(code); 194 body.add(code);
193 } 195 }
194 } 196 }
195 197
196 // Flush any unwritten fields/properties. 198 // Flush any unwritten fields/properties.
197 _flushLazyFields(body); 199 _flushLazyFields(body);
198 _flushLibraryProperties(body); 200 _flushLibraryProperties(body);
199 201
200 assert(_pendingStatements.isEmpty); 202 assert(_pendingStatements.isEmpty);
201 return _statement(body); 203 return _statement(body);
(...skipping 917 matching lines...) Expand 10 before | Expand all | Expand 10 after
1119 if (type.isVoid) { 1121 if (type.isVoid) {
1120 return js.call('dart.void'); 1122 return js.call('dart.void');
1121 } else if (type.isDynamic) { 1123 } else if (type.isDynamic) {
1122 return js.call('dart.dynamic'); 1124 return js.call('dart.dynamic');
1123 } 1125 }
1124 1126
1125 var name = type.name; 1127 var name = type.name;
1126 var element = type.element; 1128 var element = type.element;
1127 if (name == '' || lowerTypedef && type is FunctionType) { 1129 if (name == '' || lowerTypedef && type is FunctionType) {
1128 if (type is FunctionType) { 1130 if (type is FunctionType) {
1129 // TODO(vsm): Support all parameter types.
1130 var returnType = type.returnType; 1131 var returnType = type.returnType;
1131 var parameterTypes = type.normalParameterTypes; 1132 var parameterTypes = type.normalParameterTypes;
1132 var optionalTypes = type.optionalParameterTypes; 1133 var optionalTypes = type.optionalParameterTypes;
1133 var namedTypes = type.namedParameterTypes; 1134 var namedTypes = type.namedParameterTypes;
1134 if (namedTypes.isEmpty) { 1135 if (namedTypes.isEmpty) {
1135 if (optionalTypes.isEmpty) { 1136 if (optionalTypes.isEmpty) {
1136 return js.call('dart.functionType(#, #)', [ 1137 return js.call('dart.functionType(#, #)', [
1137 _emitTypeName(returnType), 1138 _emitTypeName(returnType),
1138 _emitTypeNames(parameterTypes) 1139 _emitTypeNames(parameterTypes)
1139 ]); 1140 ]);
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
1269 if (target == null || isLibraryPrefix(target)) { 1270 if (target == null || isLibraryPrefix(target)) {
1270 if (rules.isDynamicCall(node.methodName)) { 1271 if (rules.isDynamicCall(node.methodName)) {
1271 code = 'dart.$DCALL(#, #)'; 1272 code = 'dart.$DCALL(#, #)';
1272 } else { 1273 } else {
1273 code = '#(#)'; 1274 code = '#(#)';
1274 } 1275 }
1275 return js.call( 1276 return js.call(
1276 code, [_visit(node.methodName), _visit(node.argumentList)]); 1277 code, [_visit(node.methodName), _visit(node.argumentList)]);
1277 } 1278 }
1278 1279
1280 var type = getStaticType(target);
1281 var name = node.methodName.name;
1282 var memberName = _emitMemberName(name, type: type);
1283
1279 if (rules.isDynamicTarget(target)) { 1284 if (rules.isDynamicTarget(target)) {
1280 code = 'dart.$DSEND(#, #, #)'; 1285 code = 'dart.$DSEND(#, #, #)';
1281 } else if (rules.isDynamicCall(node.methodName)) { 1286 } else if (rules.isDynamicCall(node.methodName)) {
1282 // This is a dynamic call to a statically know target. For example: 1287 // This is a dynamic call to a statically know target. For example:
1283 // class Foo { Function bar; } 1288 // class Foo { Function bar; }
1284 // new Foo().bar(); // dynamic call 1289 // new Foo().bar(); // dynamic call
1285 code = 'dart.$DCALL(#.#, #)'; 1290 code = 'dart.$DCALL(#.#, #)';
1291 } else if (_objectMembers.containsKey(name)) {
Jennifer Messerly 2015/04/23 19:05:58 Is this only if `target` is nullable**? A test cas
vsm 2015/04/23 20:35:55 I added a check in. We need both checks as all ty
1292 assert(_objectMembers[name] is FunctionType);
1293 // Object methods require a helper for null checks.
1294 return js.call('dart.#(#, #)', [
1295 memberName,
1296 _visit(target),
1297 _visit(node.argumentList)
1298 ]);
1286 } else { 1299 } else {
1287 code = '#.#(#)'; 1300 code = '#.#(#)';
1288 } 1301 }
1289 return js.call(code, [ 1302
1290 _visit(target), 1303 return js.call(
1291 _emitMemberName(node.methodName.name, type: getStaticType(target)), 1304 code, [_visit(target), memberName, _visit(node.argumentList)]);
1292 _visit(node.argumentList)
1293 ]);
1294 } 1305 }
1295 1306
1296 /// Emits code for the `JS(...)` builtin. 1307 /// Emits code for the `JS(...)` builtin.
1297 _emitForeignJS(MethodInvocation node) { 1308 _emitForeignJS(MethodInvocation node) {
1298 var e = node.methodName.staticElement; 1309 var e = node.methodName.staticElement;
1299 if (e is FunctionElement && 1310 if (e is FunctionElement &&
1300 e.library.name == '_foreign_helper' && 1311 e.library.name == '_foreign_helper' &&
1301 e.name == 'JS') { 1312 e.name == 'JS') {
1302 var args = node.argumentList.arguments; 1313 var args = node.argumentList.arguments;
1303 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` 1314 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer`
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
1435 return new JS.VariableInitialization(name, _visitInitializer(node)); 1446 return new JS.VariableInitialization(name, _visitInitializer(node));
1436 } 1447 }
1437 1448
1438 JS.Expression _visitInitializer(VariableDeclaration node) { 1449 JS.Expression _visitInitializer(VariableDeclaration node) {
1439 var value = _visit(node.initializer); 1450 var value = _visit(node.initializer);
1440 // explicitly initialize to null, to avoid getting `undefined`. 1451 // explicitly initialize to null, to avoid getting `undefined`.
1441 // TODO(jmesserly): do this only for vars that aren't definitely assigned. 1452 // TODO(jmesserly): do this only for vars that aren't definitely assigned.
1442 return value != null ? value : new JS.LiteralNull(); 1453 return value != null ? value : new JS.LiteralNull();
1443 } 1454 }
1444 1455
1456 void _flushPendingStatements(List<JS.Statement> body) {
1457 if (_pendingStatements.isNotEmpty) {
1458 body.addAll(_pendingStatements);
1459 _pendingStatements.clear();
1460 }
1461 }
1462
1445 void _flushLazyFields(List<JS.Statement> body) { 1463 void _flushLazyFields(List<JS.Statement> body) {
1446 var code = _emitLazyFields(_exportsVar, _lazyFields); 1464 var code = _emitLazyFields(_exportsVar, _lazyFields);
1447 if (code != null) body.add(code); 1465 if (code != null) {
1466 // Ensure symbols for private fields are defined.
1467 _flushPendingStatements(body);
1468 body.add(code);
1469 }
1448 _lazyFields.clear(); 1470 _lazyFields.clear();
1449 } 1471 }
1450 1472
1451 JS.Statement _emitLazyFields( 1473 JS.Statement _emitLazyFields(
1452 JS.Expression objExpr, List<VariableDeclaration> fields) { 1474 JS.Expression objExpr, List<VariableDeclaration> fields) {
1453 if (fields.isEmpty) return null; 1475 if (fields.isEmpty) return null;
1454 1476
1455 var methods = []; 1477 var methods = [];
1456 for (var node in fields) { 1478 for (var node in fields) {
1457 var name = node.name.name; 1479 var name = node.name.name;
1458 methods.add(new JS.Method(_propertyName(name), 1480 var element = node.element;
1459 js.call('function() { return #; }', _visit(node.initializer)), 1481 var access = _emitMemberName(name, type: element.type, isStatic: true);
1482 methods.add(new JS.Method(
1483 access, js.call('function() { return #; }', _visit(node.initializer)),
1460 isGetter: true)); 1484 isGetter: true));
1461 1485
1462 // TODO(jmesserly): use a dummy setter to indicate writable. 1486 // TODO(jmesserly): use a dummy setter to indicate writable.
1463 if (!node.isFinal) { 1487 if (!node.isFinal) {
1464 methods.add(new JS.Method( 1488 methods.add(
1465 _propertyName(name), js.call('function(_) {}'), isSetter: true)); 1489 new JS.Method(access, js.call('function(_) {}'), isSetter: true));
1466 } 1490 }
1467 } 1491 }
1468 1492
1469 return js.statement( 1493 return js.statement(
1470 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); 1494 'dart.defineLazyProperties(#, { # });', [objExpr, methods]);
1471 } 1495 }
1472 1496
1473 void _flushLibraryProperties(List<JS.Statement> body) { 1497 void _flushLibraryProperties(List<JS.Statement> body) {
1474 if (_properties.isEmpty) return; 1498 if (_properties.isEmpty) return;
1475 body.add(js.statement('dart.copyProperties(#, { # });', [ 1499 body.add(js.statement('dart.copyProperties(#, { # });', [
(...skipping 393 matching lines...) Expand 10 before | Expand all | Expand 10 after
1869 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { 1893 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) {
1870 var name = _emitMemberName(memberId.name, type: getStaticType(target)); 1894 var name = _emitMemberName(memberId.name, type: getStaticType(target));
1871 if (rules.isDynamicTarget(target)) { 1895 if (rules.isDynamicTarget(target)) {
1872 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); 1896 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]);
1873 } 1897 }
1874 1898
1875 String code; 1899 String code;
1876 var member = memberId.staticElement; 1900 var member = memberId.staticElement;
1877 if (member != null && member is MethodElement) { 1901 if (member != null && member is MethodElement) {
1878 // Tear-off methods: explicitly bind it. 1902 // Tear-off methods: explicitly bind it.
1879 if (isStateless(target, target)) { 1903 if (isStateless(target, target) &&
1904 !_objectMembers.containsKey(memberId.name)) {
1880 return js.call('#.#.bind(#)', [_visit(target), name, _visit(target)]); 1905 return js.call('#.#.bind(#)', [_visit(target), name, _visit(target)]);
1881 } 1906 }
1882 code = 'dart.bind(#, #)'; 1907 code = 'dart.bind(#, #)';
1908 } else if (_objectMembers.containsKey(memberId.name)) {
1909 code = 'dart.#(#)';
1910 return js.call(code, [name, _visit(target)]);
1883 } else { 1911 } else {
1884 code = '#.#'; 1912 code = '#.#';
1885 } 1913 }
1886 1914
1887 return js.call(code, [_visit(target), name]); 1915 return js.call(code, [_visit(target), name]);
1888 } 1916 }
1889 1917
1890 /// Emits a generic send, like an operator method. 1918 /// Emits a generic send, like an operator method.
1891 /// 1919 ///
1892 /// **Please note** this function does not support method invocation syntax 1920 /// **Please note** this function does not support method invocation syntax
(...skipping 576 matching lines...) Expand 10 before | Expand all | Expand 10 after
2469 // TODO(jmesserly): validate the library. See issue #135. 2497 // TODO(jmesserly): validate the library. See issue #135.
2470 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; 2498 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName';
2471 2499
2472 bool _isJsPeerInterface(DartObjectImpl value) => 2500 bool _isJsPeerInterface(DartObjectImpl value) =>
2473 value.type.name == 'JsPeerInterface'; 2501 value.type.name == 'JsPeerInterface';
2474 2502
2475 // TODO(jacobr): we would like to do something like the following 2503 // TODO(jacobr): we would like to do something like the following
2476 // but we don't have summary support yet. 2504 // but we don't have summary support yet.
2477 // bool _supportJsExtensionMethod(AnnotatedNode node) => 2505 // bool _supportJsExtensionMethod(AnnotatedNode node) =>
2478 // _getAnnotation(node, "SupportJsExtensionMethod") != null; 2506 // _getAnnotation(node, "SupportJsExtensionMethod") != null;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698