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 275 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
895 // could handle argument initializers more consistently in a separate | 906 // could handle argument initializers more consistently in a separate |
896 // lowering pass. | 907 // lowering pass. |
897 if (node is MethodDeclaration) return node.parameters; | 908 if (node is MethodDeclaration) return node.parameters; |
898 if (node is FunctionDeclaration) node = node.functionExpression; | 909 if (node is FunctionDeclaration) node = node.functionExpression; |
899 if (node is FunctionExpression) return node.parameters; | 910 if (node is FunctionExpression) return node.parameters; |
900 return null; | 911 return null; |
901 } | 912 } |
902 | 913 |
903 bool _hasArgumentInitializers(FormalParameterList parameters) { | 914 bool _hasArgumentInitializers(FormalParameterList parameters) { |
904 if (parameters == null) return false; | 915 if (parameters == null) return false; |
905 return parameters.parameters.any((p) => p.kind != ParameterKind.REQUIRED); | 916 return parameters.parameters.any((p) => p.kind != ParameterKind.REQUIRED || |
917 p.element.type is TypeParameterType); | |
906 } | 918 } |
907 | 919 |
920 /// Emits argument initializers, which handles optional/named args, as well | |
921 /// as generic type checks needed due to our covariance. | |
908 JS.Statement _emitArgumentInitializers(FormalParameterList parameters) { | 922 JS.Statement _emitArgumentInitializers(FormalParameterList parameters) { |
909 if (parameters == null || !_hasArgumentInitializers(parameters)) { | 923 if (parameters == null || !_hasArgumentInitializers(parameters)) { |
910 return null; | 924 return null; |
911 } | 925 } |
912 | 926 |
913 var body = []; | 927 var body = []; |
914 for (var param in parameters.parameters) { | 928 for (var param in parameters.parameters) { |
915 var jsParam = _visit(param.identifier); | 929 var jsParam = _visit(param.identifier); |
916 | 930 |
917 if (param.kind == ParameterKind.NAMED) { | 931 if (param.kind == ParameterKind.NAMED) { |
918 // Parameters will be passed using their real names, not the (possibly | 932 // Parameters will be passed using their real names, not the (possibly |
919 // renamed) local variable. | 933 // renamed) local variable. |
920 var paramName = js.string(param.identifier.name, "'"); | 934 var paramName = js.string(param.identifier.name, "'"); |
921 body.add(js.statement('let # = # && # in # ? #.# : #;', [ | 935 body.add(js.statement('let # = # && # in # ? #.# : #;', [ |
922 jsParam, | 936 jsParam, |
923 _namedArgTemp, | 937 _namedArgTemp, |
924 paramName, | 938 paramName, |
925 _namedArgTemp, | 939 _namedArgTemp, |
926 _namedArgTemp, | 940 _namedArgTemp, |
927 paramName, | 941 paramName, |
928 _defaultParamValue(param), | 942 _defaultParamValue(param), |
929 ])); | 943 ])); |
930 } else if (param.kind == ParameterKind.POSITIONAL) { | 944 } else if (param.kind == ParameterKind.POSITIONAL) { |
931 body.add(js.statement('if (# === void 0) # = #;', [ | 945 body.add(js.statement('if (# === void 0) # = #;', [ |
932 jsParam, | 946 jsParam, |
933 jsParam, | 947 jsParam, |
934 _defaultParamValue(param) | 948 _defaultParamValue(param) |
935 ])); | 949 ])); |
936 } | 950 } |
951 | |
952 // TODO(jmesserly): we could merge this with the named/optional code. | |
953 var paramType = param.element.type; | |
954 if (_hasTypeParameter(paramType)) { | |
955 body.add(js.statement( | |
956 'dart.as(#, #);', [jsParam, _emitTypeName(paramType)])); | |
vsm
2015/04/30 14:00:51
At some point, it'd be cleaner to hoist this befor
Jennifer Messerly
2015/05/01 20:43:04
agreed, discussed offline, filed https://github.co
| |
957 } | |
937 } | 958 } |
938 return _statement(body); | 959 return _statement(body); |
939 } | 960 } |
940 | 961 |
962 bool _hasTypeParameter(DartType t) => t is TypeParameterType || | |
963 t is ParameterizedType && t.typeArguments.any(_hasTypeParameter); | |
964 | |
941 JS.Expression _defaultParamValue(FormalParameter param) { | 965 JS.Expression _defaultParamValue(FormalParameter param) { |
942 if (param is DefaultFormalParameter && param.defaultValue != null) { | 966 if (param is DefaultFormalParameter && param.defaultValue != null) { |
943 return _visit(param.defaultValue); | 967 return _visit(param.defaultValue); |
944 } else { | 968 } else { |
945 return new JS.LiteralNull(); | 969 return new JS.LiteralNull(); |
946 } | 970 } |
947 } | 971 } |
948 | 972 |
949 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { | 973 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { |
950 if (node.isAbstract || _externalOrNative(node)) { | 974 if (node.isAbstract || _externalOrNative(node)) { |
(...skipping 599 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1550 if (expr is Literal && expr is! NullLiteral) return true; | 1574 if (expr is Literal && expr is! NullLiteral) return true; |
1551 if (expr is IsExpression) return true; | 1575 if (expr is IsExpression) return true; |
1552 if (expr is ThisExpression) return true; | 1576 if (expr is ThisExpression) return true; |
1553 if (expr is SuperExpression) return true; | 1577 if (expr is SuperExpression) return true; |
1554 if (expr is ParenthesizedExpression) { | 1578 if (expr is ParenthesizedExpression) { |
1555 return _isNonNullableExpression(expr.expression); | 1579 return _isNonNullableExpression(expr.expression); |
1556 } | 1580 } |
1557 if (expr is Conversion) { | 1581 if (expr is Conversion) { |
1558 return _isNonNullableExpression(expr.expression); | 1582 return _isNonNullableExpression(expr.expression); |
1559 } | 1583 } |
1584 if (expr is SimpleIdentifier) { | |
1585 // Type literals are not null. | |
1586 Element e = expr.staticElement; | |
1587 if (e is ClassElement || e is FunctionTypeAliasElement) return true; | |
1588 } | |
1560 DartType type = null; | 1589 DartType type = null; |
1561 if (expr is BinaryExpression) { | 1590 if (expr is BinaryExpression) { |
1562 type = getStaticType(expr.leftOperand); | 1591 type = getStaticType(expr.leftOperand); |
1563 } else if (expr is PrefixExpression) { | 1592 } else if (expr is PrefixExpression) { |
1564 type = getStaticType(expr.operand); | 1593 type = getStaticType(expr.operand); |
1565 } else if (expr is PostfixExpression) { | 1594 } else if (expr is PostfixExpression) { |
1566 type = getStaticType(expr.operand); | 1595 type = getStaticType(expr.operand); |
1567 } | 1596 } |
1568 if (type != null && _isJSBuiltinType(type)) { | 1597 if (type != null && _isJSBuiltinType(type)) { |
1569 return true; | 1598 return true; |
(...skipping 950 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2520 // TODO(jmesserly): validate the library. See issue #135. | 2549 // TODO(jmesserly): validate the library. See issue #135. |
2521 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; | 2550 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; |
2522 | 2551 |
2523 bool _isJsPeerInterface(DartObjectImpl value) => | 2552 bool _isJsPeerInterface(DartObjectImpl value) => |
2524 value.type.name == 'JsPeerInterface'; | 2553 value.type.name == 'JsPeerInterface'; |
2525 | 2554 |
2526 // TODO(jacobr): we would like to do something like the following | 2555 // TODO(jacobr): we would like to do something like the following |
2527 // but we don't have summary support yet. | 2556 // but we don't have summary support yet. |
2528 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 2557 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
2529 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 2558 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
OLD | NEW |