| Index: lib/src/codegen/js_codegen.dart
|
| diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart
|
| index 0c9199c7f46c6969dd6918f2c7b413ca89946466..a0c750b5519a024c9da84f430df81edab25777b9 100644
|
| --- a/lib/src/codegen/js_codegen.dart
|
| +++ b/lib/src/codegen/js_codegen.dart
|
| @@ -332,32 +332,34 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| var classExpr = new JS.ClassExpression(new JS.Identifier(type.name),
|
| _classHeritage(node), _emitClassMethods(node, ctors, fields));
|
|
|
| - var body =
|
| - _finishClassMembers(classElem, classExpr, ctors, fields, staticFields);
|
| + String jsPeerName;
|
| + var jsPeer = getAnnotationValue(node, _isJsPeerInterface);
|
| + if (jsPeer != null) {
|
| + jsPeerName = getConstantField(jsPeer, 'name', types.stringType);
|
| + }
|
| +
|
| + var body = _finishClassMembers(
|
| + classElem, classExpr, ctors, fields, staticFields, jsPeerName);
|
|
|
| var result = _finishClassDef(type, body);
|
|
|
| - var jsPeer = getAnnotationValue(node, _isJsPeerInterface);
|
| - if (jsPeer != null) {
|
| - var jsPeerName = getConstantField(jsPeer, 'name', types.stringType);
|
| - if (jsPeerName != null) {
|
| - // This class isn't allowed to be lazy, because we need to set up
|
| - // the native JS type eagerly at this point.
|
| - // If we wanted to support laziness, we could defer the hookup until
|
| - // the end of the Dart library cycle load.
|
| - assert(!_lazyClass(type));
|
| -
|
| - // TODO(jmesserly): this copies the dynamic members.
|
| - // Probably fine for objects coming from JS, but not if we actually
|
| - // want to support construction of instances with generic types other
|
| - // than dynamic. See issue #154 for Array and List<E> related bug.
|
| - var copyMembers = js.statement(
|
| - 'dart.registerExtension(dart.global.#, #);', [
|
| - _propertyName(jsPeerName),
|
| - classElem.name
|
| - ]);
|
| - return _statement([result, copyMembers]);
|
| - }
|
| + if (jsPeerName != null) {
|
| + // This class isn't allowed to be lazy, because we need to set up
|
| + // the native JS type eagerly at this point.
|
| + // If we wanted to support laziness, we could defer the hookup until
|
| + // the end of the Dart library cycle load.
|
| + assert(!_lazyClass(type));
|
| +
|
| + // TODO(jmesserly): this copies the dynamic members.
|
| + // Probably fine for objects coming from JS, but not if we actually
|
| + // want to support construction of instances with generic types other
|
| + // than dynamic. See issue #154 for Array and List<E> related bug.
|
| + var copyMembers = js.statement(
|
| + 'dart.registerExtension(dart.global.#, #);', [
|
| + _propertyName(jsPeerName),
|
| + classElem.name
|
| + ]);
|
| + return _statement([result, copyMembers]);
|
| }
|
| return result;
|
| }
|
| @@ -602,11 +604,20 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| /// inside the ES6 `class { ... }` node.
|
| JS.Statement _finishClassMembers(ClassElement classElem,
|
| JS.ClassExpression cls, List<ConstructorDeclaration> ctors,
|
| - List<FieldDeclaration> fields, List<FieldDeclaration> staticFields) {
|
| + List<FieldDeclaration> fields, List<FieldDeclaration> staticFields,
|
| + String jsPeerName) {
|
| var name = classElem.name;
|
| var body = <JS.Statement>[];
|
| body.add(new JS.ClassDeclaration(cls));
|
|
|
| + // TODO(jmesserly): we should really just extend native Array.
|
| + if (jsPeerName != null && classElem.typeParameters.isNotEmpty) {
|
| + body.add(js.statement('dart.setBaseClass(#, dart.global.#);', [
|
| + classElem.name,
|
| + _propertyName(jsPeerName)
|
| + ]));
|
| + }
|
| +
|
| // Interfaces
|
| if (classElem.interfaces.isNotEmpty) {
|
| body.add(js.statement('#[dart.implements] = () => #;', [
|
| @@ -744,7 +755,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| // Generate optional/named argument value assignment. These can not have
|
| // side effects, and may be used by the constructor's initializers, so it's
|
| // nice to do them first.
|
| - var init = _emitArgumentInitializers(node.parameters);
|
| + var init = _emitArgumentInitializers(node, constructor: true);
|
| if (init != null) body.add(init);
|
|
|
| // Redirecting constructors: these are not allowed to have initializers,
|
| @@ -890,26 +901,24 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| }
|
|
|
| FormalParameterList _parametersOf(node) {
|
| - // Note: ConstructorDeclaration is intentionally skipped here so we can
|
| - // emit the argument initializers in a different place.
|
| // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we
|
| // could handle argument initializers more consistently in a separate
|
| // lowering pass.
|
| + if (node is ConstructorDeclaration) return node.parameters;
|
| if (node is MethodDeclaration) return node.parameters;
|
| if (node is FunctionDeclaration) node = node.functionExpression;
|
| - if (node is FunctionExpression) return node.parameters;
|
| - return null;
|
| + return (node as FunctionExpression).parameters;
|
| }
|
|
|
| - bool _hasArgumentInitializers(FormalParameterList parameters) {
|
| - if (parameters == null) return false;
|
| - return parameters.parameters.any((p) => p.kind != ParameterKind.REQUIRED);
|
| - }
|
| + /// Emits argument initializers, which handles optional/named args, as well
|
| + /// as generic type checks needed due to our covariance.
|
| + JS.Statement _emitArgumentInitializers(node, {bool constructor: false}) {
|
| + // Constructor argument initializers are emitted earlier in the code, rather
|
| + // than always when we visit the function body, so we control it explicitly.
|
| + if (node is ConstructorDeclaration != constructor) return null;
|
|
|
| - JS.Statement _emitArgumentInitializers(FormalParameterList parameters) {
|
| - if (parameters == null || !_hasArgumentInitializers(parameters)) {
|
| - return null;
|
| - }
|
| + var parameters = _parametersOf(node);
|
| + if (parameters == null) return null;
|
|
|
| var body = [];
|
| for (var param in parameters.parameters) {
|
| @@ -935,10 +944,21 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| _defaultParamValue(param)
|
| ]));
|
| }
|
| +
|
| + // TODO(jmesserly): various problems here, see:
|
| + // https://github.com/dart-lang/dev_compiler/issues/161
|
| + var paramType = param.element.type;
|
| + if (!constructor && _hasTypeParameter(paramType)) {
|
| + body.add(js.statement(
|
| + 'dart.as(#, #);', [jsParam, _emitTypeName(paramType)]));
|
| + }
|
| }
|
| - return _statement(body);
|
| + return body.isEmpty ? null : _statement(body);
|
| }
|
|
|
| + bool _hasTypeParameter(DartType t) => t is TypeParameterType ||
|
| + t is ParameterizedType && t.typeArguments.any(_hasTypeParameter);
|
| +
|
| JS.Expression _defaultParamValue(FormalParameter param) {
|
| if (param is DefaultFormalParameter && param.defaultValue != null) {
|
| return _visit(param.defaultValue);
|
| @@ -1239,7 +1259,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
|
|
| @override
|
| JS.Block visitExpressionFunctionBody(ExpressionFunctionBody node) {
|
| - var initArgs = _emitArgumentInitializers(_parametersOf(node.parent));
|
| + var initArgs = _emitArgumentInitializers(node.parent);
|
| var ret = new JS.Return(_visit(node.expression));
|
| return new JS.Block(initArgs != null ? [initArgs, ret] : [ret]);
|
| }
|
| @@ -1249,7 +1269,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
|
|
| @override
|
| JS.Block visitBlockFunctionBody(BlockFunctionBody node) {
|
| - var initArgs = _emitArgumentInitializers(_parametersOf(node.parent));
|
| + var initArgs = _emitArgumentInitializers(node.parent);
|
| var block = visitBlock(node.block);
|
| if (initArgs != null) return new JS.Block([initArgs, block]);
|
| return block;
|
| @@ -1558,6 +1578,11 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| if (expr is Conversion) {
|
| return _isNonNullableExpression(expr.expression);
|
| }
|
| + if (expr is SimpleIdentifier) {
|
| + // Type literals are not null.
|
| + Element e = expr.staticElement;
|
| + if (e is ClassElement || e is FunctionTypeAliasElement) return true;
|
| + }
|
| DartType type = null;
|
| if (expr is BinaryExpression) {
|
| type = getStaticType(expr.leftOperand);
|
|
|