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

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

Issue 1117793002: add checks needed for covariant generics, and List<E> (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 7 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 | « lib/runtime/dart_runtime.js ('k') | test/browser/runtime_tests.js » ('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 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 314 matching lines...) Expand 10 before | Expand all | Expand 10 after
325 if (member is ConstructorDeclaration) { 325 if (member is ConstructorDeclaration) {
326 ctors.add(member); 326 ctors.add(member);
327 } else if (member is FieldDeclaration) { 327 } else if (member is FieldDeclaration) {
328 (member.isStatic ? staticFields : fields).add(member); 328 (member.isStatic ? staticFields : fields).add(member);
329 } 329 }
330 } 330 }
331 331
332 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), 332 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name),
333 _classHeritage(node), _emitClassMethods(node, ctors, fields)); 333 _classHeritage(node), _emitClassMethods(node, ctors, fields));
334 334
335 var body = 335 String jsPeerName;
336 _finishClassMembers(classElem, classExpr, ctors, fields, staticFields); 336 var jsPeer = getAnnotationValue(node, _isJsPeerInterface);
337 if (jsPeer != null) {
338 jsPeerName = getConstantField(jsPeer, 'name', types.stringType);
339 }
340
341 var body = _finishClassMembers(
342 classElem, classExpr, ctors, fields, staticFields, jsPeerName);
337 343
338 var result = _finishClassDef(type, body); 344 var result = _finishClassDef(type, body);
339 345
340 var jsPeer = getAnnotationValue(node, _isJsPeerInterface); 346 if (jsPeerName != null) {
341 if (jsPeer != null) { 347 // This class isn't allowed to be lazy, because we need to set up
342 var jsPeerName = getConstantField(jsPeer, 'name', types.stringType); 348 // the native JS type eagerly at this point.
343 if (jsPeerName != null) { 349 // If we wanted to support laziness, we could defer the hookup until
344 // This class isn't allowed to be lazy, because we need to set up 350 // the end of the Dart library cycle load.
345 // the native JS type eagerly at this point. 351 assert(!_lazyClass(type));
346 // If we wanted to support laziness, we could defer the hookup until
347 // the end of the Dart library cycle load.
348 assert(!_lazyClass(type));
349 352
350 // TODO(jmesserly): this copies the dynamic members. 353 // TODO(jmesserly): this copies the dynamic members.
351 // Probably fine for objects coming from JS, but not if we actually 354 // Probably fine for objects coming from JS, but not if we actually
352 // want to support construction of instances with generic types other 355 // want to support construction of instances with generic types other
353 // than dynamic. See issue #154 for Array and List<E> related bug. 356 // than dynamic. See issue #154 for Array and List<E> related bug.
354 var copyMembers = js.statement( 357 var copyMembers = js.statement(
355 'dart.registerExtension(dart.global.#, #);', [ 358 'dart.registerExtension(dart.global.#, #);', [
356 _propertyName(jsPeerName), 359 _propertyName(jsPeerName),
357 classElem.name 360 classElem.name
358 ]); 361 ]);
359 return _statement([result, copyMembers]); 362 return _statement([result, copyMembers]);
360 }
361 } 363 }
362 return result; 364 return result;
363 } 365 }
364 366
365 @override 367 @override
366 JS.Statement visitEnumDeclaration(EnumDeclaration node) => 368 JS.Statement visitEnumDeclaration(EnumDeclaration node) =>
367 _unimplementedCall("Unimplemented enum: $node").toStatement(); 369 _unimplementedCall("Unimplemented enum: $node").toStatement();
368 370
369 /// Given a class element and body, complete the class declaration. 371 /// Given a class element and body, complete the class declaration.
370 /// This handles generic type parameters, laziness (in library-cycle cases), 372 /// This handles generic type parameters, laziness (in library-cycle cases),
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after
595 jsMethods.add(new JS.Method(js.call('$_SYMBOL.iterator'), body)); 597 jsMethods.add(new JS.Method(js.call('$_SYMBOL.iterator'), body));
596 } 598 }
597 return jsMethods.where((m) => m != null).toList(growable: false); 599 return jsMethods.where((m) => m != null).toList(growable: false);
598 } 600 }
599 601
600 /// Emit class members that need to come after the class declaration, such 602 /// Emit class members that need to come after the class declaration, such
601 /// as static fields. See [_emitClassMethods] for things that are emitted 603 /// as static fields. See [_emitClassMethods] for things that are emitted
602 /// inside the ES6 `class { ... }` node. 604 /// inside the ES6 `class { ... }` node.
603 JS.Statement _finishClassMembers(ClassElement classElem, 605 JS.Statement _finishClassMembers(ClassElement classElem,
604 JS.ClassExpression cls, List<ConstructorDeclaration> ctors, 606 JS.ClassExpression cls, List<ConstructorDeclaration> ctors,
605 List<FieldDeclaration> fields, List<FieldDeclaration> staticFields) { 607 List<FieldDeclaration> fields, List<FieldDeclaration> staticFields,
608 String jsPeerName) {
606 var name = classElem.name; 609 var name = classElem.name;
607 var body = <JS.Statement>[]; 610 var body = <JS.Statement>[];
608 body.add(new JS.ClassDeclaration(cls)); 611 body.add(new JS.ClassDeclaration(cls));
609 612
613 // TODO(jmesserly): we should really just extend native Array.
614 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) {
615 body.add(js.statement('dart.setBaseClass(#, dart.global.#);', [
616 classElem.name,
617 _propertyName(jsPeerName)
618 ]));
619 }
620
610 // Interfaces 621 // Interfaces
611 if (classElem.interfaces.isNotEmpty) { 622 if (classElem.interfaces.isNotEmpty) {
612 body.add(js.statement('#[dart.implements] = () => #;', [ 623 body.add(js.statement('#[dart.implements] = () => #;', [
613 name, 624 name,
614 new JS.ArrayInitializer( 625 new JS.ArrayInitializer(
615 classElem.interfaces.map(_emitTypeName).toList()) 626 classElem.interfaces.map(_emitTypeName).toList())
616 ])); 627 ]));
617 } 628 }
618 629
619 // Named constructors 630 // Named constructors
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
737 _visit(node.redirectedConstructor), 748 _visit(node.redirectedConstructor),
738 _visit(node.parameters) 749 _visit(node.parameters)
739 ]); 750 ]);
740 } 751 }
741 752
742 var body = <JS.Statement>[]; 753 var body = <JS.Statement>[];
743 754
744 // Generate optional/named argument value assignment. These can not have 755 // Generate optional/named argument value assignment. These can not have
745 // side effects, and may be used by the constructor's initializers, so it's 756 // side effects, and may be used by the constructor's initializers, so it's
746 // nice to do them first. 757 // nice to do them first.
747 var init = _emitArgumentInitializers(node.parameters); 758 var init = _emitArgumentInitializers(node, constructor: true);
748 if (init != null) body.add(init); 759 if (init != null) body.add(init);
749 760
750 // Redirecting constructors: these are not allowed to have initializers, 761 // Redirecting constructors: these are not allowed to have initializers,
751 // and the redirecting ctor invocation runs before field initializers. 762 // and the redirecting ctor invocation runs before field initializers.
752 var redirectCall = node.initializers.firstWhere( 763 var redirectCall = node.initializers.firstWhere(
753 (i) => i is RedirectingConstructorInvocation, orElse: () => null); 764 (i) => i is RedirectingConstructorInvocation, orElse: () => null);
754 765
755 if (redirectCall != null) { 766 if (redirectCall != null) {
756 body.add(_visit(redirectCall)); 767 body.add(_visit(redirectCall));
757 return new JS.Block(body); 768 return new JS.Block(body);
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
883 894
884 var body = <JS.Statement>[]; 895 var body = <JS.Statement>[];
885 fields.forEach((FieldElement e, JS.Expression initialValue) { 896 fields.forEach((FieldElement e, JS.Expression initialValue) {
886 var access = _emitMemberName(e.name, type: e.enclosingElement.type); 897 var access = _emitMemberName(e.name, type: e.enclosingElement.type);
887 body.add(js.statement('this.# = #;', [access, initialValue])); 898 body.add(js.statement('this.# = #;', [access, initialValue]));
888 }); 899 });
889 return _statement(body); 900 return _statement(body);
890 } 901 }
891 902
892 FormalParameterList _parametersOf(node) { 903 FormalParameterList _parametersOf(node) {
893 // Note: ConstructorDeclaration is intentionally skipped here so we can
894 // emit the argument initializers in a different place.
895 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we 904 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we
896 // could handle argument initializers more consistently in a separate 905 // could handle argument initializers more consistently in a separate
897 // lowering pass. 906 // lowering pass.
907 if (node is ConstructorDeclaration) return node.parameters;
898 if (node is MethodDeclaration) return node.parameters; 908 if (node is MethodDeclaration) return node.parameters;
899 if (node is FunctionDeclaration) node = node.functionExpression; 909 if (node is FunctionDeclaration) node = node.functionExpression;
900 if (node is FunctionExpression) return node.parameters; 910 return (node as FunctionExpression).parameters;
901 return null;
902 } 911 }
903 912
904 bool _hasArgumentInitializers(FormalParameterList parameters) { 913 /// Emits argument initializers, which handles optional/named args, as well
905 if (parameters == null) return false; 914 /// as generic type checks needed due to our covariance.
906 return parameters.parameters.any((p) => p.kind != ParameterKind.REQUIRED); 915 JS.Statement _emitArgumentInitializers(node, {bool constructor: false}) {
907 } 916 // Constructor argument initializers are emitted earlier in the code, rather
917 // than always when we visit the function body, so we control it explicitly.
918 if (node is ConstructorDeclaration != constructor) return null;
908 919
909 JS.Statement _emitArgumentInitializers(FormalParameterList parameters) { 920 var parameters = _parametersOf(node);
910 if (parameters == null || !_hasArgumentInitializers(parameters)) { 921 if (parameters == null) return null;
911 return null;
912 }
913 922
914 var body = []; 923 var body = [];
915 for (var param in parameters.parameters) { 924 for (var param in parameters.parameters) {
916 var jsParam = _visit(param.identifier); 925 var jsParam = _visit(param.identifier);
917 926
918 if (param.kind == ParameterKind.NAMED) { 927 if (param.kind == ParameterKind.NAMED) {
919 // Parameters will be passed using their real names, not the (possibly 928 // Parameters will be passed using their real names, not the (possibly
920 // renamed) local variable. 929 // renamed) local variable.
921 var paramName = js.string(param.identifier.name, "'"); 930 var paramName = js.string(param.identifier.name, "'");
922 body.add(js.statement('let # = # && # in # ? #.# : #;', [ 931 body.add(js.statement('let # = # && # in # ? #.# : #;', [
923 jsParam, 932 jsParam,
924 _namedArgTemp, 933 _namedArgTemp,
925 paramName, 934 paramName,
926 _namedArgTemp, 935 _namedArgTemp,
927 _namedArgTemp, 936 _namedArgTemp,
928 paramName, 937 paramName,
929 _defaultParamValue(param), 938 _defaultParamValue(param),
930 ])); 939 ]));
931 } else if (param.kind == ParameterKind.POSITIONAL) { 940 } else if (param.kind == ParameterKind.POSITIONAL) {
932 body.add(js.statement('if (# === void 0) # = #;', [ 941 body.add(js.statement('if (# === void 0) # = #;', [
933 jsParam, 942 jsParam,
934 jsParam, 943 jsParam,
935 _defaultParamValue(param) 944 _defaultParamValue(param)
936 ])); 945 ]));
937 } 946 }
947
948 // TODO(jmesserly): various problems here, see:
949 // https://github.com/dart-lang/dev_compiler/issues/161
950 var paramType = param.element.type;
951 if (!constructor && _hasTypeParameter(paramType)) {
952 body.add(js.statement(
953 'dart.as(#, #);', [jsParam, _emitTypeName(paramType)]));
954 }
938 } 955 }
939 return _statement(body); 956 return body.isEmpty ? null : _statement(body);
940 } 957 }
941 958
959 bool _hasTypeParameter(DartType t) => t is TypeParameterType ||
960 t is ParameterizedType && t.typeArguments.any(_hasTypeParameter);
961
942 JS.Expression _defaultParamValue(FormalParameter param) { 962 JS.Expression _defaultParamValue(FormalParameter param) {
943 if (param is DefaultFormalParameter && param.defaultValue != null) { 963 if (param is DefaultFormalParameter && param.defaultValue != null) {
944 return _visit(param.defaultValue); 964 return _visit(param.defaultValue);
945 } else { 965 } else {
946 return new JS.LiteralNull(); 966 return new JS.LiteralNull();
947 } 967 }
948 } 968 }
949 969
950 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { 970 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) {
951 if (node.isAbstract || _externalOrNative(node)) { 971 if (node.isAbstract || _externalOrNative(node)) {
(...skipping 280 matching lines...) Expand 10 before | Expand all | Expand 10 after
1232 _emitMemberName(id.name, type: getStaticType(target)), 1252 _emitMemberName(id.name, type: getStaticType(target)),
1233 _visit(rhs) 1253 _visit(rhs)
1234 ]); 1254 ]);
1235 } 1255 }
1236 1256
1237 return _visit(rhs).toAssignExpression(_visit(lhs)); 1257 return _visit(rhs).toAssignExpression(_visit(lhs));
1238 } 1258 }
1239 1259
1240 @override 1260 @override
1241 JS.Block visitExpressionFunctionBody(ExpressionFunctionBody node) { 1261 JS.Block visitExpressionFunctionBody(ExpressionFunctionBody node) {
1242 var initArgs = _emitArgumentInitializers(_parametersOf(node.parent)); 1262 var initArgs = _emitArgumentInitializers(node.parent);
1243 var ret = new JS.Return(_visit(node.expression)); 1263 var ret = new JS.Return(_visit(node.expression));
1244 return new JS.Block(initArgs != null ? [initArgs, ret] : [ret]); 1264 return new JS.Block(initArgs != null ? [initArgs, ret] : [ret]);
1245 } 1265 }
1246 1266
1247 @override 1267 @override
1248 JS.Block visitEmptyFunctionBody(EmptyFunctionBody node) => new JS.Block([]); 1268 JS.Block visitEmptyFunctionBody(EmptyFunctionBody node) => new JS.Block([]);
1249 1269
1250 @override 1270 @override
1251 JS.Block visitBlockFunctionBody(BlockFunctionBody node) { 1271 JS.Block visitBlockFunctionBody(BlockFunctionBody node) {
1252 var initArgs = _emitArgumentInitializers(_parametersOf(node.parent)); 1272 var initArgs = _emitArgumentInitializers(node.parent);
1253 var block = visitBlock(node.block); 1273 var block = visitBlock(node.block);
1254 if (initArgs != null) return new JS.Block([initArgs, block]); 1274 if (initArgs != null) return new JS.Block([initArgs, block]);
1255 return block; 1275 return block;
1256 } 1276 }
1257 1277
1258 @override 1278 @override
1259 JS.Block visitBlock(Block node) => new JS.Block(_visitList(node.statements)); 1279 JS.Block visitBlock(Block node) => new JS.Block(_visitList(node.statements));
1260 1280
1261 @override 1281 @override
1262 visitMethodInvocation(MethodInvocation node) { 1282 visitMethodInvocation(MethodInvocation node) {
(...skipping 288 matching lines...) Expand 10 before | Expand all | Expand 10 after
1551 if (expr is Literal && expr is! NullLiteral) return true; 1571 if (expr is Literal && expr is! NullLiteral) return true;
1552 if (expr is IsExpression) return true; 1572 if (expr is IsExpression) return true;
1553 if (expr is ThisExpression) return true; 1573 if (expr is ThisExpression) return true;
1554 if (expr is SuperExpression) return true; 1574 if (expr is SuperExpression) return true;
1555 if (expr is ParenthesizedExpression) { 1575 if (expr is ParenthesizedExpression) {
1556 return _isNonNullableExpression(expr.expression); 1576 return _isNonNullableExpression(expr.expression);
1557 } 1577 }
1558 if (expr is Conversion) { 1578 if (expr is Conversion) {
1559 return _isNonNullableExpression(expr.expression); 1579 return _isNonNullableExpression(expr.expression);
1560 } 1580 }
1581 if (expr is SimpleIdentifier) {
1582 // Type literals are not null.
1583 Element e = expr.staticElement;
1584 if (e is ClassElement || e is FunctionTypeAliasElement) return true;
1585 }
1561 DartType type = null; 1586 DartType type = null;
1562 if (expr is BinaryExpression) { 1587 if (expr is BinaryExpression) {
1563 type = getStaticType(expr.leftOperand); 1588 type = getStaticType(expr.leftOperand);
1564 } else if (expr is PrefixExpression) { 1589 } else if (expr is PrefixExpression) {
1565 type = getStaticType(expr.operand); 1590 type = getStaticType(expr.operand);
1566 } else if (expr is PostfixExpression) { 1591 } else if (expr is PostfixExpression) {
1567 type = getStaticType(expr.operand); 1592 type = getStaticType(expr.operand);
1568 } 1593 }
1569 if (type != null && _isJSBuiltinType(type)) { 1594 if (type != null && _isJSBuiltinType(type)) {
1570 return true; 1595 return true;
(...skipping 940 matching lines...) Expand 10 before | Expand all | Expand 10 after
2511 // TODO(jmesserly): validate the library. See issue #135. 2536 // TODO(jmesserly): validate the library. See issue #135.
2512 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; 2537 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName';
2513 2538
2514 bool _isJsPeerInterface(DartObjectImpl value) => 2539 bool _isJsPeerInterface(DartObjectImpl value) =>
2515 value.type.name == 'JsPeerInterface'; 2540 value.type.name == 'JsPeerInterface';
2516 2541
2517 // TODO(jacobr): we would like to do something like the following 2542 // TODO(jacobr): we would like to do something like the following
2518 // but we don't have summary support yet. 2543 // but we don't have summary support yet.
2519 // bool _supportJsExtensionMethod(AnnotatedNode node) => 2544 // bool _supportJsExtensionMethod(AnnotatedNode node) =>
2520 // _getAnnotation(node, "SupportJsExtensionMethod") != null; 2545 // _getAnnotation(node, "SupportJsExtensionMethod") != null;
OLDNEW
« no previous file with comments | « lib/runtime/dart_runtime.js ('k') | test/browser/runtime_tests.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698