Chromium Code Reviews| 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 |