OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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; |
OLD | NEW |