Index: lib/src/compiler/code_generator.dart |
diff --git a/lib/src/compiler/code_generator.dart b/lib/src/compiler/code_generator.dart |
index ba65adf354cb69cffa88c9b4bcc8fb43bc2759e8..32d6976ca22645bdd0b8577a115f20cf48bc9c74 100644 |
--- a/lib/src/compiler/code_generator.dart |
+++ b/lib/src/compiler/code_generator.dart |
@@ -46,6 +46,7 @@ import 'nullable_type_inference.dart' show NullableTypeInference; |
import 'reify_coercions.dart' show CoercionReifier; |
import 'side_effect_analysis.dart' show ConstFieldVisitor, isStateless; |
import 'source_map_printer.dart' show SourceMapPrintingContext; |
+import 'type_utilities.dart'; |
class CodeGenerator extends GeneralizingAstVisitor |
with ClosureAnnotator, JsTypeRefCodegen, NullableTypeInference { |
@@ -66,6 +67,9 @@ class CodeGenerator extends GeneralizingAstVisitor |
/// The list of output module items, in the order they need to be emitted in. |
final _moduleItems = <JS.ModuleItem>[]; |
+ /// Table of named and possibly hoisted types. |
+ final _typeTable = new TypeTable(); |
+ |
/// The global extension type table. |
final ExtensionTypeSet _extensionTypes; |
@@ -252,6 +256,10 @@ class CodeGenerator extends GeneralizingAstVisitor |
// Declare imports |
_finishImports(items); |
+ // Discharge the type table cache variables and |
+ // hoisted definitions. |
+ items.addAll(_typeTable.discharge()); |
+ |
// Add the module's code (produced by visiting compilation units, above) |
_copyAndFlattenBlocks(items, _moduleItems); |
@@ -531,7 +539,10 @@ class CodeGenerator extends GeneralizingAstVisitor |
return jsFrom; |
} |
- return js.call('dart.as(#, #)', [jsFrom, _emitType(to)]); |
+ var type = _emitType(to, |
+ nameType: options.nameTypeTests || options.hoistTypeTests, |
+ hoistType: options.hoistTypeTests); |
+ return js.call('dart.as(#, #)', [jsFrom, type]); |
} |
@override |
@@ -545,7 +556,12 @@ class CodeGenerator extends GeneralizingAstVisitor |
result = js.call('typeof # == #', [lhs, js.string(typeofName, "'")]); |
} else { |
// Always go through a runtime helper, because implicit interfaces. |
- result = js.call('dart.is(#, #)', [lhs, _emitType(type)]); |
+ |
+ var castType = _emitType(type, |
+ nameType: options.nameTypeTests || options.hoistTypeTests, |
+ hoistType: options.hoistTypeTests); |
+ |
+ result = js.call('dart.is(#, #)', [lhs, castType]); |
} |
if (node.notOperator != null) { |
@@ -568,7 +584,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
JS.Expression body = annotate( |
js.call('dart.typedef(#, () => #)', [ |
js.string(element.name, "'"), |
- _emitType(element.type, lowerTypedef: true) |
+ _emitType(element.type, nameType: false, lowerTypedef: true) |
]), |
node, |
element); |
@@ -840,8 +856,12 @@ class CodeGenerator extends GeneralizingAstVisitor |
JS.Statement _defineClassTypeArguments(TypeDefiningElement element, |
List<TypeParameterElement> formals, JS.Statement body) { |
assert(formals.isNotEmpty); |
- var genericCall = js.call('dart.generic((#) => { #; return #; })', |
- [_emitTypeFormals(formals), body, element.name]); |
+ var genericCall = js.call('dart.generic((#) => { #; #; return #; })', [ |
+ _emitTypeFormals(formals), |
+ _typeTable.discharge(formals), |
+ body, |
+ element.name |
+ ]); |
if (element.library.isDartAsync && |
(element.name == "Future" || element.name == "_Future")) { |
genericCall = js.call('dart.flattenFutures(#)', [genericCall]); |
@@ -894,10 +914,13 @@ class CodeGenerator extends GeneralizingAstVisitor |
supertype = fillDynamicTypeArgs(supertype.element.type); |
_hasDeferredSupertype.add(element); |
} |
- heritage = _emitType(supertype); |
+ // We could choose to name the superclasses, but it's |
+ // not clear that there's much benefit |
+ heritage = _emitType(supertype, nameType: false); |
if (type.mixins.isNotEmpty) { |
- var mixins = type.mixins.map(_emitType).toList(); |
+ var mixins = |
+ type.mixins.map((t) => _emitType(t, nameType: false)).toList(); |
mixins.insert(0, heritage); |
heritage = js.call('dart.mixin(#)', [mixins]); |
} |
@@ -1159,7 +1182,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
'dart.setExtensionBaseClass(#, #);', [className, newBaseClass])); |
} else if (_hasDeferredSupertype.contains(classElem)) { |
var newBaseClass = _emitType(classElem.type.superclass, |
- subClass: classElem, className: className); |
+ nameType: false, subClass: classElem, className: className); |
body.add( |
js.statement('dart.setBaseClass(#, #);', [className, newBaseClass])); |
} |
@@ -1259,9 +1282,11 @@ class CodeGenerator extends GeneralizingAstVisitor |
} |
var memberName = _elementMemberName(element, |
useExtension: _extensionTypes.isNativeClass(classElem)); |
- var parts = _emitFunctionTypeParts(element.type); |
- var property = |
- new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
+ var type = _emitFunctionType(element.type, |
+ nameType: options.hoistSignatureTypes, |
+ hoistType: options.hoistSignatureTypes, |
+ definite: true); |
+ var property = new JS.Property(memberName, type); |
if (node.isStatic) { |
tStatics.add(property); |
sNames.add(memberName); |
@@ -1275,10 +1300,12 @@ class CodeGenerator extends GeneralizingAstVisitor |
for (ConstructorDeclaration node in ctors) { |
var memberName = _constructorName(node.element); |
var element = node.element; |
- var parts = _emitFunctionTypeParts(element.type, |
- parameters: node.parameters.parameters); |
- var property = |
- new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
+ var type = _emitFunctionType(element.type, |
+ parameters: node.parameters.parameters, |
+ nameType: options.hoistSignatureTypes, |
+ hoistType: options.hoistSignatureTypes, |
+ definite: true); |
+ var property = new JS.Property(memberName, type); |
tCtors.add(property); |
} |
@@ -1690,8 +1717,10 @@ class CodeGenerator extends GeneralizingAstVisitor |
// https://github.com/dart-lang/dev_compiler/issues/116 |
var paramType = param.element.type; |
if (node is MethodDeclaration && _unsoundCovariant(paramType, true)) { |
- body.add( |
- js.statement('dart.as(#, #);', [jsParam, _emitType(paramType)])); |
+ var castType = _emitType(paramType, |
+ nameType: options.nameTypeTests || options.hoistTypeTests, |
+ hoistType: options.hoistTypeTests); |
+ body.add(js.statement('dart.as(#, #);', [jsParam, castType])); |
} |
} |
return body.isEmpty ? null : _statement(body); |
@@ -1918,25 +1947,12 @@ class CodeGenerator extends GeneralizingAstVisitor |
JS.Expression _emitFunctionTagged(JS.Expression fn, DartType type, |
{topLevel: false}) { |
- var name = type.name; |
var lazy = topLevel && !_typeIsLoaded(type); |
- |
- if (type is FunctionType && (name == '' || name == null)) { |
- if (type.typeFormals.isEmpty && |
- type.returnType.isDynamic && |
- type.optionalParameterTypes.isEmpty && |
- type.namedParameterTypes.isEmpty && |
- type.normalParameterTypes.every((t) => t.isDynamic)) { |
- return js.call('dart.fn(#)', [fn]); |
- } |
- |
- var parts = _emitFunctionTypeParts(type); |
- if (lazy) { |
- return js.call( |
- 'dart.lazyFn(#, () => #)', [fn, new JS.ArrayInitializer(parts)]); |
- } else { |
- return js.call('dart.fn(#, #)', [fn, parts]); |
- } |
+ var typeRep = _emitFunctionType(type, definite: true); |
+ if (lazy) { |
+ return js.call('dart.lazyFn(#, () => #)', [fn, typeRep]); |
+ } else { |
+ return js.call('dart.fn(#, #)', [fn, typeRep]); |
} |
throw 'Function has non function type: $type'; |
} |
@@ -2012,21 +2028,21 @@ class CodeGenerator extends GeneralizingAstVisitor |
FormalParameterList parameters, FunctionBody body) { |
FunctionType type = element.type; |
- // sync*, async, async* |
- if (element.isAsynchronous || element.isGenerator) { |
- return new JS.Fun( |
- visitFormalParameterList(parameters, destructure: false), |
- new JS.Block([ |
- _emitGeneratorFunctionBody(element, parameters, body).toReturn() |
- ]), |
- typeParams: _emitTypeFormals(type.typeFormals), |
- returnType: emitTypeRef(type.returnType)); |
+ // normal function (sync), vs (sync*, async, async*) |
+ var stdFn = !(element.isAsynchronous || element.isGenerator); |
+ var formals = visitFormalParameterList(parameters, destructure: stdFn); |
+ var code = (stdFn) |
+ ? _visit(body) |
+ : new JS.Block( |
+ [_emitGeneratorFunctionBody(element, parameters, body).toReturn()]); |
+ var typeFormals = _emitTypeFormals(type.typeFormals); |
+ var returnType = emitTypeRef(type.returnType); |
+ if (type.typeFormals.isNotEmpty) { |
+ code = new JS.Block( |
+ [new JS.Block(_typeTable.discharge(type.typeFormals)), code]); |
} |
- |
- // normal function (sync) |
- return new JS.Fun(visitFormalParameterList(parameters), _visit(body), |
- typeParams: _emitTypeFormals(type.typeFormals), |
- returnType: emitTypeRef(type.returnType)); |
+ return new JS.Fun(formals, code, |
+ typeParams: typeFormals, returnType: returnType); |
} |
JS.Expression _emitGeneratorFunctionBody(ExecutableElement element, |
@@ -2249,13 +2265,15 @@ class CodeGenerator extends GeneralizingAstVisitor |
? p.metadata |
: (p as DefaultFormalParameter).parameter.metadata; |
- JS.ArrayInitializer _emitTypeNames(List<DartType> types, |
- [List<FormalParameter> parameters]) { |
+ JS.ArrayInitializer _emitTypeNames( |
+ List<DartType> types, List<FormalParameter> parameters, |
+ {bool nameType: true, bool hoistType: true}) { |
var result = <JS.Expression>[]; |
for (int i = 0; i < types.length; ++i) { |
var metadata = |
parameters != null ? _parameterMetadata(parameters[i]) : []; |
- var typeName = _emitType(types[i]); |
+ var typeName = |
+ _emitType(types[i], nameType: nameType, hoistType: hoistType); |
var value = typeName; |
if (options.emitMetadata && metadata.isNotEmpty) { |
metadata = metadata.map(_instantiateAnnotation).toList(); |
@@ -2278,13 +2296,38 @@ class CodeGenerator extends GeneralizingAstVisitor |
/// Emit the pieces of a function type, as an array of return type, |
/// regular args, and optional/named args. |
+ JS.Expression _emitFunctionType(FunctionType type, |
+ {List<FormalParameter> parameters, |
+ bool lowerTypedef: false, |
+ bool nameType: true, |
+ bool hoistType: true, |
+ definite: false}) { |
+ var parts = _emitFunctionTypeParts(type, |
+ parameters: parameters, |
+ lowerTypedef: lowerTypedef, |
+ nameType: nameType, |
+ hoistType: hoistType); |
+ var helper = (definite) ? 'definiteFunctionType' : 'functionType'; |
+ var fullType = js.call('dart.${helper}(#)', [parts]); |
+ if (!nameType) return fullType; |
+ return _typeTable.nameType(type, fullType, |
+ hoistType: hoistType, definite: definite); |
+ } |
+ |
+ /// Emit the pieces of a function type, as an array of return type, |
+ /// regular args, and optional/named args. |
List<JS.Expression> _emitFunctionTypeParts(FunctionType type, |
- {List<FormalParameter> parameters, bool lowerTypedef: false}) { |
+ {List<FormalParameter> parameters, |
+ bool lowerTypedef: false, |
+ bool nameType: true, |
+ bool hoistType: true}) { |
var parameterTypes = type.normalParameterTypes; |
var optionalTypes = type.optionalParameterTypes; |
var namedTypes = type.namedParameterTypes; |
- var rt = _emitType(type.returnType); |
- var ra = _emitTypeNames(parameterTypes, parameters); |
+ var rt = |
+ _emitType(type.returnType, nameType: nameType, hoistType: hoistType); |
+ var ra = _emitTypeNames(parameterTypes, parameters, |
+ nameType: nameType, hoistType: hoistType); |
List<JS.Expression> typeParts; |
if (namedTypes.isNotEmpty) { |
@@ -2295,7 +2338,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
} else if (optionalTypes.isNotEmpty) { |
assert(namedTypes.isEmpty); |
var oa = _emitTypeNames( |
- optionalTypes, parameters?.sublist(parameterTypes.length)); |
+ optionalTypes, parameters?.sublist(parameterTypes.length), |
+ nameType: nameType, hoistType: hoistType); |
typeParts = [rt, ra, oa]; |
} else { |
typeParts = [rt, ra]; |
@@ -2307,7 +2351,17 @@ class CodeGenerator extends GeneralizingAstVisitor |
// function types (as callable functions). See discussion at: |
// https://github.com/dart-lang/dev_compiler/issues/526 |
var tf = _emitTypeFormals(typeFormals); |
- typeParts = [new JS.ArrowFun(tf, new JS.ArrayInitializer(typeParts))]; |
+ var names = _typeTable.discharge(typeFormals); |
+ var parts = new JS.ArrayInitializer(typeParts); |
+ if (names.isEmpty) { |
+ typeParts = [ |
+ js.call('(#) => #', [tf, parts]) |
+ ]; |
+ } else { |
+ typeParts = [ |
+ js.call('(#) => {#; return #;}', [tf, names, parts]) |
+ ]; |
+ } |
} |
return typeParts; |
} |
@@ -2322,9 +2376,14 @@ class CodeGenerator extends GeneralizingAstVisitor |
/// If [subClass] is set, then we are setting the base class for the given |
/// class and should emit the given [className], which will already be |
/// defined. |
+ /// |
+ /// If [nameType] is true, then the type will be named. In addition, |
+ /// if [hoistType] is true, then the named type will be hoisted. |
JS.Expression _emitType(DartType type, |
{bool lowerTypedef: false, |
bool lowerGeneric: false, |
+ bool nameType: true, |
+ bool hoistType: true, |
ClassElement subClass, |
JS.Expression className}) { |
// The void and dynamic types are not defined in core. |
@@ -2347,9 +2406,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
// TODO(jmesserly): should we change how typedefs work? They currently |
// go through use similar logic as generic classes. This makes them |
// different from universal function types. |
- var ft = type as FunctionType; |
- var parts = _emitFunctionTypeParts(ft, lowerTypedef: lowerTypedef); |
- return js.call('dart.functionType(#)', [parts]); |
+ return _emitFunctionType(type as FunctionType, |
+ lowerTypedef: lowerTypedef, nameType: nameType, hoistType: hoistType); |
} |
if (type is TypeParameterType) { |
@@ -2365,14 +2423,20 @@ class CodeGenerator extends GeneralizingAstVisitor |
var args = type.typeArguments; |
Iterable jsArgs = null; |
if (args.any((a) => !a.isDynamic)) { |
- jsArgs = args |
- .map((x) => _emitType(x, subClass: subClass, className: className)); |
+ jsArgs = args.map((x) => _emitType(x, |
+ nameType: nameType, |
+ hoistType: hoistType, |
+ subClass: subClass, |
+ className: className)); |
} else if (lowerGeneric) { |
jsArgs = []; |
} |
if (jsArgs != null) { |
var genericName = _emitTopLevelName(element, suffix: '\$'); |
- return js.call('#(#)', [genericName, jsArgs]); |
+ var typeRep = js.call('#(#)', [genericName, jsArgs]); |
+ return nameType |
+ ? _typeTable.nameType(type, typeRep, hoistType: hoistType) |
+ : typeRep; |
} |
} |
@@ -3100,7 +3164,9 @@ class CodeGenerator extends GeneralizingAstVisitor |
if (element == null) { |
// TODO(jmesserly): this only happens if we had a static error. |
// Should we generate a throw instead? |
- ctor = _emitType(type); |
+ ctor = _emitType(type, |
+ nameType: options.hoistInstanceCreation, |
+ hoistType: options.hoistInstanceCreation); |
if (name != null) { |
ctor = new JS.PropertyAccess(ctor, _propertyName(name.name)); |
} |
@@ -4056,9 +4122,12 @@ class CodeGenerator extends GeneralizingAstVisitor |
// TODO(jmesserly): this is inconsistent with [visitIsExpression], which |
// has special case for typeof. |
+ var castType = _emitType(clause.exceptionType.type, |
+ nameType: options.nameTypeTests || options.hoistTypeTests, |
+ hoistType: options.hoistTypeTests); |
+ |
return new JS.If( |
- js.call('dart.is(#, #)', |
- [_visit(_catchParameter), _emitType(clause.exceptionType.type),]), |
+ js.call('dart.is(#, #)', [_visit(_catchParameter), castType]), |
then, |
otherwise); |
} |