Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(407)

Unified Diff: lib/src/compiler/code_generator.dart

Issue 1988023008: Name and hoist types (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | test/browser/runtime_tests.js » ('j') | test/browser/runtime_tests.js » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..bbe4e74655aeb14bb5730a87b8760c4abd8a602d 100644
--- a/lib/src/compiler/code_generator.dart
+++ b/lib/src/compiler/code_generator.dart
@@ -66,6 +66,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 +255,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);
@@ -568,7 +575,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 +847,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 +905,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 +1173,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 +1273,12 @@ 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));
+ // TODO(leafp): Settle on a policy for this. Naming these makes the
+ // signature more compact, but it generates a lot of noise at the
+ // top level, and it's not clear that there's much benefit.
+ var type =
+ _emitFunctionType(element.type, nameType: false, definite: true);
+ var property = new JS.Property(memberName, type);
if (node.isStatic) {
tStatics.add(property);
sNames.add(memberName);
@@ -1275,10 +1292,11 @@ 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: false,
+ definite: true);
+ var property = new JS.Property(memberName, type);
tCtors.add(property);
}
@@ -1918,25 +1936,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 +2017,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 +2254,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 +2285,37 @@ 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, 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 +2326,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 +2339,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;
}
@@ -2325,6 +2367,8 @@ class CodeGenerator extends GeneralizingAstVisitor
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 +2391,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 +2408,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)
+ : typeRep;
}
}
@@ -4565,3 +4614,210 @@ LibraryElement _getLibrary(AnalysisContext c, String uri) =>
bool _isDartRuntime(LibraryElement l) =>
l.isInSdk && l.source.uri.toString() == 'dart:_runtime';
+
+Set<TypeParameterElement> _freeTypeParameters(DartType t) {
+ var result = new HashSet<TypeParameterElement>();
+ void find(DartType t) {
+ if (t is TypeParameterType) {
+ result.add(t.element);
+ } else if (t is FunctionType) {
+ find(t.returnType);
+ t.parameters.forEach((p) => find(p.type));
+ t.typeFormals.forEach((p) => find(p.bound));
+ t.typeFormals.forEach(result.remove);
+ } else if (t is InterfaceType) {
+ t.typeArguments.forEach(find);
+ }
+ }
+ find(t);
+ return result;
+}
+
+/// _CacheTable tracks cache variables for variables that
+/// are emitted in place with a hoisted variable for a cache.
+class _CacheTable {
+ /// Mapping from types to their canonical names.
+ final _names = new Map<DartType, JS.TemporaryId>();
+ Iterable<DartType> get keys => _names.keys.toList();
+
+ JS.Statement _dischargeType(DartType type) {
+ var name = _names.remove(type);
+ if (name != null) {
+ return (js.statement('let #;', [name]));
+ }
+ return null;
+ }
+
+ /// Emit a list of statements declaring the cache variables for
+ /// types tracked by this table. If [typeFilter] is given,
+ /// only emit the types listed in the filter.
+ List<JS.Statement> discharge([Iterable<DartType> typeFilter]) {
+ var decls = <JS.Statement>[];
+ var types = typeFilter ?? keys;
+ for (var t in types) {
+ var stmt = _dischargeType(t);
+ if (stmt != null) decls.add(stmt);
+ }
+ return decls;
+ }
+
+ bool isNamed(DartType type) => _names.containsKey(type);
+
+ /// If [type] is not already in the table, choose a new canonical
+ /// variable to contain it. Emit an expression which uses [typeRep] to
+ /// lazily initialize the cache in place.
+ JS.Expression nameType(DartType type, JS.Expression typeRep) {
+ var temp = _names[type];
+ if (temp == null) {
+ ;
+ _names[type] = temp = chooseTypeName(type);
+ }
+ return js.call('# || (# = #)', [temp, temp, typeRep]);
+ }
+
+ String _typeString(DartType type, {bool flat: false}) {
+ if (type is ParameterizedType && type.name != null) {
+ var clazz = type.name;
+ var params = type.typeArguments;
+ if (params == null) return clazz;
+ if (params.every((p) => p.isDynamic)) return clazz;
+ var paramStrings = params.map(_typeString);
+ var paramString = paramStrings.join("\$");
+ return "${clazz}Of${paramString}";
+ }
+ if (type is FunctionType) {
+ if (flat) return "Fn";
+ var rType = _typeString(type.returnType, flat: true);
+ var paramStrings = type.normalParameterTypes
+ .take(3)
+ .map((p) => _typeString(p, flat: true));
+ var paramString = paramStrings.join("And");
+ var count = type.normalParameterTypes.length;
+ if (count > 3 ||
+ type.namedParameterTypes.isNotEmpty ||
+ type.optionalParameterTypes.isNotEmpty) {
+ paramString = "${paramString}__";
+ } else if (count == 0) {
+ paramString = "Void";
+ }
+ return "${paramString}To${rType}";
+ }
+ if (type is TypeParameterType) return type.name;
+ if (type.name != null) return type.name;
+ return "type";
+ }
+
+ /// Heuristically choose a good name for the cache and generator
+ /// variables.
+ JS.Identifier chooseTypeName(DartType type) {
+ return new JS.TemporaryId(_typeString(type));
+ }
+}
+
+/// _GeneratorTable tracks types which have been
+/// named and hoisted.
+class _GeneratorTable extends _CacheTable {
+ final _defs = new Map<DartType, JS.Expression>();
+
+ _GeneratorTable();
+
+ JS.Statement _dischargeType(DartType t) {
+ var name = _names.remove(t);
+ if (name != null) {
+ JS.Expression init = _defs.remove(t);
+ assert(init != null);
+ return js.statement(
+ 'let # = () => ((# = dart.constFn(#))());', [name, name, init]);
+ }
+ return null;
+ }
+
+ /// If [type] does not already have a generator name chosen for it,
+ /// assign it one, using [typeRep] as the initializer for it.
+ /// Emit an expression which calls the generator name.
+ JS.Expression nameType(DartType type, JS.Expression typeRep) {
+ var temp = _names[type];
+ if (temp == null) {
+ _names[type] = temp = chooseTypeName(type);
+ _defs[type] = typeRep;
+ }
+ return js.call('#()', [temp]);
+ }
+}
+
+class _TypeTable {
+ /// Cache variable names for types emitted in place.
+ final _cacheNames = new _CacheTable();
+
+ /// Cache variable names for definite function types emitted in place.
+ final _definiteCacheNames = new _CacheTable();
+
+ /// Generator variable names for hoisted types.
+ final _generators = new _GeneratorTable();
+
+ /// Generator variable names for hoisted definite function types.
+ final _definiteGenerators = new _GeneratorTable();
+
+ /// Mapping from type parameters to the types which must have their
+ /// cache/generator variables discharged at the binding site for the
+ /// type variable since the type definition depends on the type
+ /// parameter.
+ final _scopeDependencies = new Map<TypeParameterElement, List<DartType>>();
+
+ /// Emit a list of statements declaring the cache variables and generator
+ /// definitions tracked by the table. If [formals] is present, only
+ /// emit the definitions which depend on the formals.
+ List<JS.Statement> discharge([List<TypeParameterElement> formals]) {
+ var filter = formals?.expand((p) => _scopeDependencies[p] ?? []);
+ var stmts = [
+ _cacheNames,
+ _definiteCacheNames,
+ _generators,
+ _definiteGenerators
+ ].expand((c) => c.discharge(filter)).toList();
+ formals?.forEach(_scopeDependencies.remove);
+ return stmts;
+ }
+
+ /// Record the dependencies of the type on its free variables
+ bool recordScopeDependencies(DartType type) {
+ var fvs = _freeTypeParameters(type);
+ // TODO(leafp): This is a hack to avoid trying to hoist out of
+ // generic functions and generic function types. This often degrades
+ // readability to little or no benefit. It would be good to do this
+ // when we know that we can hoist it to an outer scope, but for
+ // now we just disable it.
+ if (fvs.any((i) => i.enclosingElement is FunctionTypedElement)) {
+ return true;
+ }
+ void addScope(TypeParameterElement i) {
+ List<DartType> types = _scopeDependencies[i];
+ if (types == null) _scopeDependencies[i] = types = [];
+ types.add(type);
+ }
+ fvs.forEach(addScope);
+ return false;
+ }
+
+ /// Given a type [type], and a JS expression [typeRep] which implements it,
+ /// Add the type and its representation to the table, returning an
+ /// expression which implements the type (but which caches the value).
+ /// If [hoist] is true, then the JS representation will be hoisted up
+ /// as far as possible and shared between instances of the type.
+ /// If [hoist] is false, the cache variable will be hoisted up as
+ /// far as possible and shared between instances of the type, but the
+ /// initializer expression will be emitted in place.
+ /// The boolean parameter [definite] distinguishes between definite function
+ /// types and other types (since the same DartType may have different
+ /// representations as definite and indefinite function types).
+ JS.Expression nameType(DartType type, JS.Expression typeRep, bool hoist,
+ {bool definite: false}) {
+ var table = hoist
+ ? (definite ? _definiteGenerators : _generators)
+ : (definite ? _definiteCacheNames : _cacheNames);
+ if (!table.isNamed(type)) {
+ if (recordScopeDependencies(type)) return typeRep;
+ }
+ return table.nameType(type, typeRep);
+ }
+}
« no previous file with comments | « no previous file | test/browser/runtime_tests.js » ('j') | test/browser/runtime_tests.js » ('J')

Powered by Google App Engine
This is Rietveld 408576698