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

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: Better static dispatch check 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 167 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 178
179 // TODO(jmesserly): scriptTag, directives. 179 // TODO(jmesserly): scriptTag, directives.
180 var body = <JS.Statement>[]; 180 var body = <JS.Statement>[];
181 for (var child in node.declarations) { 181 for (var child in node.declarations) {
182 // Attempt to group adjacent fields/properties. 182 // Attempt to group adjacent fields/properties.
183 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); 183 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body);
184 if (child is! FunctionDeclaration) _flushLibraryProperties(body); 184 if (child is! FunctionDeclaration) _flushLibraryProperties(body);
185 185
186 var code = _visit(child); 186 var code = _visit(child);
187 if (code != null) { 187 if (code != null) {
188 if (_pendingStatements.isNotEmpty) { 188 _flushPendingStatements(body);
189 body.addAll(_pendingStatements);
190 _pendingStatements.clear();
191 }
192 body.add(code); 189 body.add(code);
193 } 190 }
194 } 191 }
195 192
196 // Flush any unwritten fields/properties. 193 // Flush any unwritten fields/properties.
197 _flushLazyFields(body); 194 _flushLazyFields(body);
198 _flushLibraryProperties(body); 195 _flushLibraryProperties(body);
199 196
200 assert(_pendingStatements.isEmpty); 197 assert(_pendingStatements.isEmpty);
201 return _statement(body); 198 return _statement(body);
(...skipping 917 matching lines...) Expand 10 before | Expand all | Expand 10 after
1119 if (type.isVoid) { 1116 if (type.isVoid) {
1120 return js.call('dart.void'); 1117 return js.call('dart.void');
1121 } else if (type.isDynamic) { 1118 } else if (type.isDynamic) {
1122 return js.call('dart.dynamic'); 1119 return js.call('dart.dynamic');
1123 } 1120 }
1124 1121
1125 var name = type.name; 1122 var name = type.name;
1126 var element = type.element; 1123 var element = type.element;
1127 if (name == '' || lowerTypedef && type is FunctionType) { 1124 if (name == '' || lowerTypedef && type is FunctionType) {
1128 if (type is FunctionType) { 1125 if (type is FunctionType) {
1129 // TODO(vsm): Support all parameter types.
1130 var returnType = type.returnType; 1126 var returnType = type.returnType;
1131 var parameterTypes = type.normalParameterTypes; 1127 var parameterTypes = type.normalParameterTypes;
1132 var optionalTypes = type.optionalParameterTypes; 1128 var optionalTypes = type.optionalParameterTypes;
1133 var namedTypes = type.namedParameterTypes; 1129 var namedTypes = type.namedParameterTypes;
1134 if (namedTypes.isEmpty) { 1130 if (namedTypes.isEmpty) {
1135 if (optionalTypes.isEmpty) { 1131 if (optionalTypes.isEmpty) {
1136 return js.call('dart.functionType(#, #)', [ 1132 return js.call('dart.functionType(#, #)', [
1137 _emitTypeName(returnType), 1133 _emitTypeName(returnType),
1138 _emitTypeNames(parameterTypes) 1134 _emitTypeNames(parameterTypes)
1139 ]); 1135 ]);
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
1269 if (target == null || isLibraryPrefix(target)) { 1265 if (target == null || isLibraryPrefix(target)) {
1270 if (rules.isDynamicCall(node.methodName)) { 1266 if (rules.isDynamicCall(node.methodName)) {
1271 code = 'dart.$DCALL(#, #)'; 1267 code = 'dart.$DCALL(#, #)';
1272 } else { 1268 } else {
1273 code = '#(#)'; 1269 code = '#(#)';
1274 } 1270 }
1275 return js.call( 1271 return js.call(
1276 code, [_visit(node.methodName), _visit(node.argumentList)]); 1272 code, [_visit(node.methodName), _visit(node.argumentList)]);
1277 } 1273 }
1278 1274
1275 var type = getStaticType(target);
1276 var name = node.methodName.name;
1277 var memberName = _emitMemberName(name, type: type);
1278
1279 if (rules.isDynamicTarget(target)) { 1279 if (rules.isDynamicTarget(target)) {
1280 code = 'dart.$DSEND(#, #, #)'; 1280 code = 'dart.$DSEND(#, #, #)';
1281 } else if (rules.isDynamicCall(node.methodName)) { 1281 } else if (rules.isDynamicCall(node.methodName)) {
1282 // This is a dynamic call to a statically know target. For example: 1282 // This is a dynamic call to a statically know target. For example:
1283 // class Foo { Function bar; } 1283 // class Foo { Function bar; }
1284 // new Foo().bar(); // dynamic call 1284 // new Foo().bar(); // dynamic call
1285 code = 'dart.$DCALL(#.#, #)'; 1285 code = 'dart.$DCALL(#.#, #)';
1286 } else if (_requiresStaticDispatch(target, name)) {
1287 assert(rules.objectMembers[name] is FunctionType);
1288 // Object methods require a helper for null checks.
1289 return js.call('dart.#(#, #)', [
1290 memberName,
1291 _visit(target),
1292 _visit(node.argumentList)
1293 ]);
1286 } else { 1294 } else {
1287 code = '#.#(#)'; 1295 code = '#.#(#)';
1288 } 1296 }
1289 return js.call(code, [ 1297
1290 _visit(target), 1298 return js.call(
1291 _emitMemberName(node.methodName.name, type: getStaticType(target)), 1299 code, [_visit(target), memberName, _visit(node.argumentList)]);
1292 _visit(node.argumentList)
1293 ]);
1294 } 1300 }
1295 1301
1296 /// Emits code for the `JS(...)` builtin. 1302 /// Emits code for the `JS(...)` builtin.
1297 _emitForeignJS(MethodInvocation node) { 1303 _emitForeignJS(MethodInvocation node) {
1298 var e = node.methodName.staticElement; 1304 var e = node.methodName.staticElement;
1299 if (e is FunctionElement && 1305 if (e is FunctionElement &&
1300 e.library.name == '_foreign_helper' && 1306 e.library.name == '_foreign_helper' &&
1301 e.name == 'JS') { 1307 e.name == 'JS') {
1302 var args = node.argumentList.arguments; 1308 var args = node.argumentList.arguments;
1303 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` 1309 // 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)); 1441 return new JS.VariableInitialization(name, _visitInitializer(node));
1436 } 1442 }
1437 1443
1438 JS.Expression _visitInitializer(VariableDeclaration node) { 1444 JS.Expression _visitInitializer(VariableDeclaration node) {
1439 var value = _visit(node.initializer); 1445 var value = _visit(node.initializer);
1440 // explicitly initialize to null, to avoid getting `undefined`. 1446 // explicitly initialize to null, to avoid getting `undefined`.
1441 // TODO(jmesserly): do this only for vars that aren't definitely assigned. 1447 // TODO(jmesserly): do this only for vars that aren't definitely assigned.
1442 return value != null ? value : new JS.LiteralNull(); 1448 return value != null ? value : new JS.LiteralNull();
1443 } 1449 }
1444 1450
1451 void _flushPendingStatements(List<JS.Statement> body) {
1452 if (_pendingStatements.isNotEmpty) {
1453 body.addAll(_pendingStatements);
1454 _pendingStatements.clear();
1455 }
1456 }
1457
1445 void _flushLazyFields(List<JS.Statement> body) { 1458 void _flushLazyFields(List<JS.Statement> body) {
1446 var code = _emitLazyFields(_exportsVar, _lazyFields); 1459 var code = _emitLazyFields(_exportsVar, _lazyFields);
1447 if (code != null) body.add(code); 1460 if (code != null) {
1461 // Ensure symbols for private fields are defined.
1462 _flushPendingStatements(body);
1463 body.add(code);
1464 }
1448 _lazyFields.clear(); 1465 _lazyFields.clear();
1449 } 1466 }
1450 1467
1451 JS.Statement _emitLazyFields( 1468 JS.Statement _emitLazyFields(
1452 JS.Expression objExpr, List<VariableDeclaration> fields) { 1469 JS.Expression objExpr, List<VariableDeclaration> fields) {
1453 if (fields.isEmpty) return null; 1470 if (fields.isEmpty) return null;
1454 1471
1455 var methods = []; 1472 var methods = [];
1456 for (var node in fields) { 1473 for (var node in fields) {
1457 var name = node.name.name; 1474 var name = node.name.name;
1458 methods.add(new JS.Method(_propertyName(name), 1475 var element = node.element;
1459 js.call('function() { return #; }', _visit(node.initializer)), 1476 var access = _emitMemberName(name, type: element.type, isStatic: true);
1477 methods.add(new JS.Method(
1478 access, js.call('function() { return #; }', _visit(node.initializer)),
1460 isGetter: true)); 1479 isGetter: true));
1461 1480
1462 // TODO(jmesserly): use a dummy setter to indicate writable. 1481 // TODO(jmesserly): use a dummy setter to indicate writable.
1463 if (!node.isFinal) { 1482 if (!node.isFinal) {
1464 methods.add(new JS.Method( 1483 methods.add(
1465 _propertyName(name), js.call('function(_) {}'), isSetter: true)); 1484 new JS.Method(access, js.call('function(_) {}'), isSetter: true));
1466 } 1485 }
1467 } 1486 }
1468 1487
1469 return js.statement( 1488 return js.statement(
1470 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); 1489 'dart.defineLazyProperties(#, { # });', [objExpr, methods]);
1471 } 1490 }
1472 1491
1473 void _flushLibraryProperties(List<JS.Statement> body) { 1492 void _flushLibraryProperties(List<JS.Statement> body) {
1474 if (_properties.isEmpty) return; 1493 if (_properties.isEmpty) return;
1475 body.add(js.statement('dart.copyProperties(#, { # });', [ 1494 body.add(js.statement('dart.copyProperties(#, { # });', [
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
1518 1537
1519 bool _isNonNullableExpression(Expression expr) { 1538 bool _isNonNullableExpression(Expression expr) {
1520 // If the type is non-nullable, no further checking needed. 1539 // If the type is non-nullable, no further checking needed.
1521 if (rules.isNonNullableType(getStaticType(expr))) return true; 1540 if (rules.isNonNullableType(getStaticType(expr))) return true;
1522 1541
1523 // TODO(vsm): Revisit whether we really need this when we get 1542 // TODO(vsm): Revisit whether we really need this when we get
1524 // better non-nullability in the type system. 1543 // better non-nullability in the type system.
1525 1544
1526 if (expr is Literal && expr is! NullLiteral) return true; 1545 if (expr is Literal && expr is! NullLiteral) return true;
1527 if (expr is IsExpression) return true; 1546 if (expr is IsExpression) return true;
1547 if (expr is ThisExpression) return true;
1548 if (expr is SuperExpression) return true;
1528 if (expr is ParenthesizedExpression) { 1549 if (expr is ParenthesizedExpression) {
1529 return _isNonNullableExpression(expr.expression); 1550 return _isNonNullableExpression(expr.expression);
1530 } 1551 }
1531 if (expr is Conversion) { 1552 if (expr is Conversion) {
1532 return _isNonNullableExpression(expr.expression); 1553 return _isNonNullableExpression(expr.expression);
1533 } 1554 }
1534 DartType type = null; 1555 DartType type = null;
1535 if (expr is BinaryExpression) { 1556 if (expr is BinaryExpression) {
1536 type = getStaticType(expr.leftOperand); 1557 type = getStaticType(expr.leftOperand);
1537 } else if (expr is PrefixExpression) { 1558 } else if (expr is PrefixExpression) {
(...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after
1858 return _visit(node.identifier); 1879 return _visit(node.identifier);
1859 } else { 1880 } else {
1860 return _emitGet(node.prefix, node.identifier); 1881 return _emitGet(node.prefix, node.identifier);
1861 } 1882 }
1862 } 1883 }
1863 1884
1864 @override 1885 @override
1865 visitPropertyAccess(PropertyAccess node) => 1886 visitPropertyAccess(PropertyAccess node) =>
1866 _emitGet(_getTarget(node), node.propertyName); 1887 _emitGet(_getTarget(node), node.propertyName);
1867 1888
1889 bool _requiresStaticDispatch(Expression target, String memberName) {
1890 var type = getStaticType(target);
1891 if (!rules.objectMembers.containsKey(memberName)) {
1892 return false;
1893 }
1894 if (!type.isObject &&
1895 !_isJSBuiltinType(type) &&
1896 _isNonNullableExpression(target)) {
1897 return false;
1898 }
1899 return true;
1900 }
1901
1868 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. 1902 /// Shared code for [PrefixedIdentifier] and [PropertyAccess].
1869 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { 1903 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) {
1870 var name = _emitMemberName(memberId.name, type: getStaticType(target)); 1904 var name = _emitMemberName(memberId.name, type: getStaticType(target));
1871 if (rules.isDynamicTarget(target)) { 1905 if (rules.isDynamicTarget(target)) {
1872 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); 1906 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]);
1873 } 1907 }
1874 1908
1875 String code; 1909 String code;
1876 var member = memberId.staticElement; 1910 var member = memberId.staticElement;
1877 if (member != null && member is MethodElement) { 1911 if (member != null && member is MethodElement) {
1878 // Tear-off methods: explicitly bind it. 1912 // Tear-off methods: explicitly bind it.
1879 if (isStateless(target, target)) { 1913 if (isStateless(target, target) &&
1914 !rules.objectMembers.containsKey(memberId.name)) {
Jennifer Messerly 2015/04/23 21:09:30 we chatted about this, maybe pull this out into it
vsm 2015/04/23 21:23:32 Done.
1880 return js.call('#.#.bind(#)', [_visit(target), name, _visit(target)]); 1915 return js.call('#.#.bind(#)', [_visit(target), name, _visit(target)]);
1881 } 1916 }
1882 code = 'dart.bind(#, #)'; 1917 code = 'dart.bind(#, #)';
1918 } else if (_requiresStaticDispatch(target, memberId.name)) {
1919 return js.call('dart.#(#)', [name, _visit(target)]);
1883 } else { 1920 } else {
1884 code = '#.#'; 1921 code = '#.#';
1885 } 1922 }
1886 1923
1887 return js.call(code, [_visit(target), name]); 1924 return js.call(code, [_visit(target), name]);
1888 } 1925 }
1889 1926
1890 /// Emits a generic send, like an operator method. 1927 /// Emits a generic send, like an operator method.
1891 /// 1928 ///
1892 /// **Please note** this function does not support method invocation syntax 1929 /// **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. 2506 // TODO(jmesserly): validate the library. See issue #135.
2470 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; 2507 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName';
2471 2508
2472 bool _isJsPeerInterface(DartObjectImpl value) => 2509 bool _isJsPeerInterface(DartObjectImpl value) =>
2473 value.type.name == 'JsPeerInterface'; 2510 value.type.name == 'JsPeerInterface';
2474 2511
2475 // TODO(jacobr): we would like to do something like the following 2512 // TODO(jacobr): we would like to do something like the following
2476 // but we don't have summary support yet. 2513 // but we don't have summary support yet.
2477 // bool _supportJsExtensionMethod(AnnotatedNode node) => 2514 // bool _supportJsExtensionMethod(AnnotatedNode node) =>
2478 // _getAnnotation(node, "SupportJsExtensionMethod") != null; 2515 // _getAnnotation(node, "SupportJsExtensionMethod") != null;
OLDNEW
« lib/src/checker/rules.dart ('K') | « lib/src/checker/rules.dart ('k') | lib/src/utils.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698