Index: lib/src/codegen/js_codegen.dart |
diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart |
index 1ca3993e1a8c9303368e813f868f5389a17da4ea..7811615d6420a354b0495c94ada5ba7b8e99eff7 100644 |
--- a/lib/src/codegen/js_codegen.dart |
+++ b/lib/src/codegen/js_codegen.dart |
@@ -13,8 +13,9 @@ import 'package:analyzer/src/generated/element.dart'; |
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
import 'package:analyzer/src/generated/scanner.dart' |
show StringToken, Token, TokenType; |
+import 'package:analyzer/src/generated/type_system.dart' |
+ show StrongTypeSystemImpl; |
import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder; |
-import 'package:analyzer/src/task/strong/rules.dart'; |
import 'ast_builder.dart' show AstBuilder; |
import 'reify_coercions.dart' show CoercionReifier, Tuple2; |
@@ -57,8 +58,8 @@ const ListEquality _listEquality = const ListEquality(); |
class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
final AbstractCompiler compiler; |
final CodegenOptions options; |
- final TypeRules rules; |
final LibraryElement currentLibrary; |
+ final StrongTypeSystemImpl rules; |
/// The global extension type table. |
final HashSet<ClassElement> _extensionTypes; |
@@ -110,8 +111,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
bool _isDartUtils; |
- Map<String, DartType> _objectMembers; |
- |
JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary, |
this._extensionTypes, this._fieldsNeedingStorage) |
: compiler = compiler, |
@@ -124,11 +123,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
var interceptors = context.computeLibraryElement(src); |
_jsArray = interceptors.getType('JSArray'); |
_isDartUtils = currentLibrary.source.uri.toString() == 'dart:_utils'; |
- |
- _objectMembers = getObjectMemberMap(types); |
} |
- TypeProvider get types => rules.provider; |
+ TypeProvider get types => _types; |
JS.Program emitLibrary(LibraryUnit library) { |
// Modify the AST to make coercions explicit. |
@@ -333,7 +330,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
var fromExpr = _visit(node.expression); |
// Skip the cast if it's not needed. |
- if (rules.isSubTypeOf(from, to)) return fromExpr; |
+ if (rules.isSubtypeOf(from, to)) return fromExpr; |
// All Dart number types map to a JS double. |
if (_isNumberInJS(from) && _isNumberInJS(to)) { |
@@ -544,10 +541,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
var genericName = '$name\$'; |
JS.Statement genericDef = null; |
- if (type.typeParameters.isNotEmpty) { |
+ if (_boundTypeParametersOf(type).isNotEmpty) { |
genericDef = _emitGenericClassDef(type, body); |
} |
- |
// The base class and all mixins must be declared before this class. |
if (!_loader.isLoaded(type.element)) { |
// TODO(jmesserly): the lazy class def is a simple solution for now. |
@@ -577,7 +573,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) { |
var name = type.name; |
var genericName = '$name\$'; |
- var typeParams = type.typeParameters.map((p) => p.name); |
+ var typeParams = _boundTypeParametersOf(type).map((p) => p.name); |
if (isPublic(name)) _exports.add(genericName); |
return js.statement('const # = dart.generic(function(#) { #; return #; });', |
[genericName, typeParams, body, name]); |
@@ -611,7 +607,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
var supertype = type.superclass; |
if (_deferIfNeeded(supertype, element)) { |
// Fall back to raw type. |
- supertype = fillDynamicTypeArgs(supertype.element.type, rules.provider); |
+ supertype = fillDynamicTypeArgs(supertype.element.type, _types); |
_hasDeferredSupertype.add(element); |
} |
heritage = _emitTypeName(supertype); |
@@ -788,8 +784,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
var element = node.element; |
var inheritedElement = |
classElem.lookUpInheritedConcreteMethod(name, currentLibrary); |
- if (inheritedElement != null && |
- inheritedElement.type == element.type) continue; |
+ if (inheritedElement != null && inheritedElement.type == element.type) |
+ continue; |
var memberName = _elementMemberName(element); |
var parts = _emitFunctionTypeParts(element.type); |
var property = |
@@ -921,7 +917,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
if (fields.isEmpty && superCall == null) return null; |
dynamic body = _initializeFields(node, fields); |
- if (superCall != null) body = [ |
+ if (superCall != null) |
+ body = [ |
[body, superCall] |
]; |
var name = _constructorName(node.element.unnamedConstructor); |
@@ -1235,16 +1232,20 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
// 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)])); |
+ if (!constructor && _hasUnsoundTypeParameter(paramType)) { |
+ body.add(js |
+ .statement('dart.as(#, #);', [jsParam, _emitTypeName(paramType)])); |
} |
} |
return body.isEmpty ? null : _statement(body); |
} |
- bool _hasTypeParameter(DartType t) => t is TypeParameterType || |
- t is ParameterizedType && t.typeArguments.any(_hasTypeParameter); |
+ bool _isUnsoundTypeParameter(DartType t) => |
+ t is TypeParameterType && t.element.enclosingElement is ClassElement; |
+ |
+ bool _hasUnsoundTypeParameter(DartType t) => |
+ _isUnsoundTypeParameter(t) || |
+ t is ParameterizedType && t.typeArguments.any(_hasUnsoundTypeParameter); |
JS.Expression _defaultParamValue(FormalParameter param) { |
if (param is DefaultFormalParameter && param.defaultValue != null) { |
@@ -1379,7 +1380,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
} |
bool _executesAtTopLevel(AstNode node) { |
- var ancestor = node.getAncestor((n) => n is FunctionBody || |
+ var ancestor = node.getAncestor((n) => |
+ n is FunctionBody || |
(n is FieldDeclaration && n.staticKeyword == null) || |
(n is ConstructorDeclaration && n.constKeyword == null)); |
return ancestor == null; |
@@ -1520,7 +1522,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
} |
_asyncStarController = savedController; |
- var T = _emitTypeName(rules.getExpectedReturnType(body)); |
+ var T = _emitTypeName(_getExpectedReturnType(body)); |
return js.call('dart.#(#)', [ |
kind, |
[gen, T]..addAll(params) |
@@ -1566,6 +1568,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
// indirects back to a (possibly synthetic) field. |
var element = accessor; |
if (accessor is PropertyAccessorElement) element = accessor.variable; |
+ if (accessor is FunctionMember) element = accessor.baseElement; |
_loader.declareBeforeUse(element); |
@@ -1722,6 +1725,15 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
var parts = _emitFunctionTypeParts(type as FunctionType); |
return js.call('dart.functionType(#)', [parts]); |
} |
+ // For now, reify generic method parameters as dynamic |
+ bool _isGenericTypeParameter(DartType type) => |
+ (type is TypeParameterType) && |
+ !(type.element.enclosingElement is ClassElement || |
+ type.element.enclosingElement is FunctionTypeAliasElement); |
+ |
+ if (_isGenericTypeParameter(type)) { |
+ return js.call('dart.dynamic'); |
+ } |
if (type is TypeParameterType) { |
return new JS.Identifier(name); |
@@ -1732,7 +1744,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
var isCurrentClass = |
args.isNotEmpty && _loader.isCurrentElement(type.element); |
Iterable jsArgs = null; |
- if (args.any((a) => a != types.dynamicType)) { |
+ if (args |
+ .any((a) => a != types.dynamicType && !_isGenericTypeParameter(a))) { |
jsArgs = args.map(_emitTypeName); |
} else if (lowerGeneric || isCurrentClass) { |
// When creating a `new S<dynamic>` we try and use the raw form |
@@ -1814,8 +1827,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
if (lhs is IndexExpression) { |
var target = _getTarget(lhs); |
if (_useNativeJsIndexer(target.staticType)) { |
- return js.call( |
- '#[#] = #', [_visit(target), _visit(lhs.index), _visit(rhs)]); |
+ return js |
+ .call('#[#] = #', [_visit(target), _visit(lhs.index), _visit(rhs)]); |
} |
return _emitSend(target, '[]=', [lhs.index, rhs]); |
} |
@@ -1901,8 +1914,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
} else { |
code = '#(#)'; |
} |
- return js.call( |
- code, [_visit(node.methodName), _visit(node.argumentList)]); |
+ return js |
+ .call(code, [_visit(node.methodName), _visit(node.argumentList)]); |
} |
var type = getStaticType(target); |
@@ -1919,7 +1932,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
// new Foo().bar(); // dynamic call |
code = 'dart.$DCALL(#.#, #)'; |
} else if (_requiresStaticDispatch(target, name)) { |
- assert(rules.objectMembers[name] is FunctionType); |
// Object methods require a helper for null checks. |
return js.call('dart.#(#, #)', |
[memberName, _visit(target), _visit(node.argumentList)]); |
@@ -1927,8 +1939,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
code = '#.#(#)'; |
} |
- return js.call( |
- code, [_visit(target), memberName, _visit(node.argumentList)]); |
+ return js |
+ .call(code, [_visit(target), memberName, _visit(node.argumentList)]); |
} |
/// Emits code for the `JS(...)` builtin. |
@@ -2014,8 +2026,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
/// see discussion in https://github.com/dart-lang/dev_compiler/issues/392. |
bool _isDestructurableNamedParam(FormalParameter param) => |
_isNamedParam(param) && |
- !invalidVariableName(param.identifier.name) && |
- options.destructureNamedParams; |
+ !invalidVariableName(param.identifier.name) && |
+ options.destructureNamedParams; |
@override |
List<JS.Parameter> visitFormalParameterList(FormalParameterList node) => |
@@ -2187,7 +2199,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
return new JS.VariableInitialization(name, _visitInitializer(node)); |
} |
- bool _isFinalJSDecl(AstNode field) => field is VariableDeclaration && |
+ bool _isFinalJSDecl(AstNode field) => |
+ field is VariableDeclaration && |
field.isFinal && |
_isJSInvocation(field.initializer); |
@@ -2289,8 +2302,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
objExpr = new JS.Identifier(target.type.name); |
} |
- return js.statement( |
- 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); |
+ return js |
+ .statement('dart.defineLazyProperties(#, { # });', [objExpr, methods]); |
} |
PropertyAccessorElement _findAccessor(VariableElement element, |
@@ -2510,11 +2523,12 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
return _emitSend(left, op.lexeme, [right]); |
} |
- /// If the type [t] is [int] or [double], returns [num]. |
+ /// If the type [t] is [int] or [double], or a type parameter |
+ /// bounded by [int], [double] or [num] returns [num]. |
/// Otherwise returns [t]. |
DartType _canonicalizeNumTypes(DartType t) { |
var numType = types.numType; |
- if (t is InterfaceType && t.superclass == numType) return numType; |
+ if (rules.isSubtypeOf(t, numType)) return numType; |
return t; |
} |
@@ -2815,7 +2829,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
bool _requiresStaticDispatch(Expression target, String memberName) { |
var type = getStaticType(target); |
- if (!rules.objectMembers.containsKey(memberName)) { |
+ if (!_isObjectProperty(memberName)) { |
return false; |
} |
if (!type.isObject && |
@@ -3026,8 +3040,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
var init = _visit(node.identifier); |
if (init == null) { |
- init = js.call( |
- 'let # = #.current', [node.loopVariable.identifier.name, iter]); |
+ init = js |
+ .call('let # = #.current', [node.loopVariable.identifier.name, iter]); |
} else { |
init = js.call('# = #.current', [init, iter]); |
} |
@@ -3039,13 +3053,13 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
' } finally { #; }' |
'}', |
[ |
- iter, |
- createStreamIter, |
- new JS.Yield(js.call('#.moveNext()', iter)), |
- init, |
- _visit(node.body), |
- new JS.Yield(js.call('#.cancel()', iter)) |
- ]); |
+ iter, |
+ createStreamIter, |
+ new JS.Yield(js.call('#.moveNext()', iter)), |
+ init, |
+ _visit(node.body), |
+ new JS.Yield(js.call('#.cancel()', iter)) |
+ ]); |
} |
@override |
@@ -3124,8 +3138,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
if (node.catchKeyword != null) { |
var name = node.exceptionParameter; |
if (name != null && name != _catchParameter) { |
- body.add(js.statement( |
- 'let # = #;', [_visit(name), _visit(_catchParameter)])); |
+ body.add(js |
+ .statement('let # = #;', [_visit(name), _visit(_catchParameter)])); |
_catchParameter = name; |
} |
if (node.stackTraceParameter != null) { |
@@ -3313,6 +3327,13 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
_visitList(nodes) as List<JS.Expression>, operator); |
} |
+ /// Return the bound type parameters for a ParameterizedType |
+ List<TypeParameterElement> _boundTypeParametersOf(ParameterizedType type) { |
+ return (type is FunctionType) |
+ ? type.boundTypeParameters |
+ : type.typeParameters; |
+ } |
+ |
/// Like [_emitMemberName], but for declaration sites. |
/// |
/// Unlike call sites, we always have an element available, so we can use it |
@@ -3401,7 +3422,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
// Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
if (allowExtensions && |
_extensionTypes.contains(type.element) && |
- !_objectMembers.containsKey(name)) { |
+ !_isObjectProperty(name)) { |
return js.call('dartx.#', _propertyName(name)); |
} |
@@ -3423,7 +3444,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
library, () => new JS.TemporaryId(jsLibraryName(library))); |
} |
- DartType getStaticType(Expression e) => rules.getStaticType(e); |
+ DartType getStaticType(Expression e) => |
+ e.staticType ?? DynamicTypeImpl.instance; |
@override |
String getQualifiedName(TypeDefiningElement type) { |
@@ -3459,13 +3481,75 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
/// |
/// JSNumber is the type that actually "implements" all numbers, hence it's |
/// a subtype of int and double (and num). It's in our "dart:_interceptors". |
- bool _isNumberInJS(DartType t) => rules.isSubTypeOf(t, _types.numType); |
+ bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, _types.numType); |
+ |
+ bool _isObjectGetter(String name) { |
+ PropertyAccessorElement element = _types.objectType.element.getGetter(name); |
+ return (element != null && !element.isStatic); |
+ } |
+ |
+ bool _isObjectMethod(String name) { |
+ MethodElement element = _types.objectType.element.getMethod(name); |
+ return (element != null && !element.isStatic); |
+ } |
+ |
+ bool _isObjectProperty(String name) { |
+ return _isObjectGetter(name) || _isObjectMethod(name); |
+ } |
+ |
+ // TODO(leafp): This is copied out of the analyzer code checker. Should |
+ // be shared somewhere. |
Jennifer Messerly
2016/01/04 20:48:40
yeah, agree. Another idea is we could just store i
Leaf
2016/01/04 21:34:47
Acknowledged.
|
+ DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { |
+ FunctionType functionType; |
+ var parent = body.parent; |
+ if (parent is Declaration) { |
+ functionType = (parent.element as dynamic)?.type; |
+ } else { |
+ assert(parent is FunctionExpression); |
+ functionType = parent.staticType; |
+ } |
+ if (functionType == null) { |
+ return DynamicTypeImpl.instance; |
+ } |
+ var type = functionType.returnType; |
+ |
+ InterfaceType expectedType = null; |
+ if (body.isAsynchronous) { |
+ if (body.isGenerator) { |
+ // Stream<T> -> T |
+ expectedType = _types.streamType; |
+ } else { |
+ // Future<T> -> T |
+ // TODO(vsm): Revisit with issue #228. |
+ expectedType = _types.futureType; |
+ } |
+ } else { |
+ if (body.isGenerator) { |
+ // Iterable<T> -> T |
+ expectedType = _types.iterableType; |
+ } else { |
+ // T -> T |
+ return type; |
+ } |
+ } |
+ if (type.isDynamic) { |
+ return type; |
+ } else if (type is InterfaceType && type.element == expectedType.element) { |
+ return type.typeArguments[0]; |
+ } else { |
+ // TODO(leafp): The above only handles the case where the return type |
+ // is exactly Future/Stream/Iterable. Handle the subtype case. |
+ return DynamicTypeImpl.instance; |
+ } |
+ } |
} |
class JSGenerator extends CodeGenerator { |
final _extensionTypes = new HashSet<ClassElement>(); |
- |
- JSGenerator(AbstractCompiler compiler) : super(compiler) { |
+ final TypeProvider _types; |
+ JSGenerator(AbstractCompiler compiler) |
+ : _types = compiler.context.typeProvider, |
+ super(compiler) { |
// TODO(jacobr): determine the the set of types with extension methods from |
// the annotations rather than hard coding the list once the analyzer |
// supports summaries. |
@@ -3479,13 +3563,13 @@ class JSGenerator extends CodeGenerator { |
// Unfortunately our current analyzer rejects "implements int". |
// Fix was landed, so we can remove this hack once we're updated: |
// https://github.com/dart-lang/sdk/commit/d7cd11f86a02f55269fc8d9843e7758ebeeb81c8 |
- _addExtensionType(context.typeProvider.intType); |
- _addExtensionType(context.typeProvider.doubleType); |
+ _addExtensionType(_types.intType); |
+ _addExtensionType(_types.doubleType); |
} |
void _addExtensionType(InterfaceType t) { |
if (t.isObject || !_extensionTypes.add(t.element)) return; |
- t = fillDynamicTypeArgs(t, rules.provider) as InterfaceType; |
+ t = fillDynamicTypeArgs(t, _types) as InterfaceType; |
t.interfaces.forEach(_addExtensionType); |
t.mixins.forEach(_addExtensionType); |
_addExtensionType(t.superclass); |
@@ -3496,6 +3580,7 @@ class JSGenerator extends CodeGenerator { |
unit = unit.clone(); |
var library = unit.library.element.library; |
var fields = findFieldsNeedingStorage(unit, _extensionTypes); |
+ var rules = new StrongTypeSystemImpl(); |
var codegen = |
new JSCodegenVisitor(compiler, rules, library, _extensionTypes, fields); |
var module = codegen.emitLibrary(unit); |