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

Unified Diff: lib/src/codegen/js_codegen.dart

Issue 1138793002: Tag closures with their types (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Address jmesserly's comments Created 5 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
Index: lib/src/codegen/js_codegen.dart
diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart
index 3ba321bf2cf8936668f328652fe4584bbd0bc3b7..2a85f77cae73b54bea1a75ceb03ebe27cbfcd755 100644
--- a/lib/src/codegen/js_codegen.dart
+++ b/lib/src/codegen/js_codegen.dart
@@ -335,11 +335,14 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
var ctors = <ConstructorDeclaration>[];
var fields = <FieldDeclaration>[];
+ var methods = <MethodDeclaration>[];
for (var member in node.members) {
if (member is ConstructorDeclaration) {
ctors.add(member);
} else if (member is FieldDeclaration && !member.isStatic) {
fields.add(member);
+ } else if (member is MethodDeclaration) {
+ methods.add(member);
}
}
@@ -352,8 +355,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
jsPeerName = getConstantField(jsPeer, 'name', types.stringType);
}
- var body =
- _finishClassMembers(classElem, classExpr, ctors, fields, jsPeerName);
+ var body = _finishClassMembers(
+ classElem, classExpr, ctors, fields, methods, jsPeerName);
var result = _finishClassDef(type, body);
@@ -536,7 +539,8 @@ 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, String jsPeerName) {
+ List<FieldDeclaration> fields, List<MethodDeclaration> methods,
+ String jsPeerName) {
var name = classElem.name;
var body = <JS.Statement>[];
body.add(new JS.ClassDeclaration(cls));
@@ -578,6 +582,49 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
}
}
+ // Emit the signature on the class recording the runtime type information
+ {
+ var tStatics = [];
+ var tMethods = [];
+ var sNames = [];
+ var cType = classElem.type;
+ for (MethodDeclaration node in methods) {
+ if (!(node.isSetter || node.isGetter || node.isAbstract)) {
+ var name = node.name.name;
+ var element = node.element;
+ var unary = node.parameters.parameters.isEmpty;
+ var memberName = _emitMemberName(name,
+ type: cType, unary: unary, isStatic: node.isStatic);
+ var property =
+ new JS.Property(memberName, _emitTypeName(element.type));
+ if (node.isStatic) {
+ tStatics.add(property);
+ sNames.add(memberName);
+ } else tMethods.add(property);
+ }
+ }
+ build(name, elements) {
+ var o =
+ new JS.ObjectInitializer(elements, vertical: elements.length > 1);
+ var e = js.call('() => #', o);
+ var p = new JS.Property(_propertyName(name), e);
+ return p;
+ }
+ var sigFields = [];
+ if (!tMethods.isEmpty) sigFields.add(build('methods', tMethods));
+ if (!tStatics.isEmpty) {
+ assert(!sNames.isEmpty);
+ var aNames = new JS.Property(
+ _propertyName('names'), new JS.ArrayInitializer(sNames));
+ sigFields.add(build('statics', tStatics));
+ sigFields.add(aNames);
+ }
+
+ var sig = new JS.ObjectInitializer(sigFields);
+ var classExpr = new JS.Identifier(name);
+ body.add(js.statement('dart.setSignature(#, #);', [classExpr, sig]));
+ }
+
return _statement(body);
}
@@ -915,10 +962,11 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
_flushLibraryProperties(body);
var name = node.name.name;
- body.add(js.comment('Function $name: ${node.element.type}'));
- body.add(new JS.FunctionDeclaration(
- new JS.Identifier(name), _visit(node.functionExpression)));
+ var id = new JS.Identifier(name);
+ body.add(new JS.FunctionDeclaration(id, _visit(node.functionExpression)));
+ body.add(_emitFunctionTagged(id, node.element.type, topLevel: true)
+ .toStatement());
if (isPublic(name)) _addExport(name);
return _statement(body);
@@ -930,14 +978,53 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
isGetter: node.isGetter, isSetter: node.isSetter);
}
+ bool _executesAtTopLevel(AstNode node) {
+ var ancestor = node.getAncestor((n) => n is FunctionBody ||
+ (n is FieldDeclaration && n.staticKeyword == null) ||
+ (n is ConstructorDeclaration && n.constKeyword == null));
+ return ancestor == null;
+ }
+
+ bool _typeIsLoaded(DartType type) {
+ if (type is FunctionType && (type.name == '' || type.name == null)) {
+ return (_typeIsLoaded(type.returnType) &&
+ type.optionalParameterTypes.every(_typeIsLoaded) &&
+ type.namedParameterTypes.values.every(_typeIsLoaded) &&
+ type.normalParameterTypes.every(_typeIsLoaded));
+ }
+ if (type.isDynamic || type.isVoid || type.isBottom) return true;
+ return _loader.isLoaded(type.element);
+ }
+
+ JS.Expression _emitFunctionTagged(JS.Expression clos, DartType type,
+ {topLevel: false}) {
+ var name = type.name;
+ var lazy = topLevel && !_typeIsLoaded(type);
+
+ if (type is FunctionType && (name == '' || name == null)) {
+ if (type.returnType.isDynamic &&
+ type.optionalParameterTypes.isEmpty &&
+ type.namedParameterTypes.isEmpty &&
+ type.normalParameterTypes.every((t) => t.isDynamic)) {
+ return js.call('dart.fn(#)', [clos]);
+ }
+ if (lazy) {
+ return js.call('dart.fn(#, () => #)', [clos, _emitTypeName(type)]);
+ }
+ return js.call('dart.fn(#, #)', [clos, _emitFunctionTypeParts(type)]);
+ }
+ throw 'Function has non function type: $type';
+ }
+
@override
JS.Expression visitFunctionExpression(FunctionExpression node) {
var params = _visit(node.parameters);
if (params == null) params = [];
var parent = node.parent;
- if (parent is FunctionDeclaration &&
- parent.parent is! FunctionDeclarationStatement) {
+ var inDecl = parent is FunctionDeclaration;
+ var inStmt = parent.parent is FunctionDeclarationStatement;
+ if (inDecl && !inStmt) {
return new JS.Fun(params, _visit(node.body));
} else {
String code;
@@ -950,7 +1037,13 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
code = '(#) => { #; }';
body = nodeBody;
}
- return js.call(code, [params, _visit(body)]);
+ var clos = js.call(code, [params, _visit(body)]);
+ if (!inStmt) {
+ var type = getStaticType(node);
+ return _emitFunctionTagged(clos, type,
+ topLevel: _executesAtTopLevel(node));
+ }
+ return clos;
}
}
@@ -967,8 +1060,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
// `this`, but it seems harmless enough to just do it always.
var name = new JS.Identifier(func.name.name);
return new JS.Block([
- js.comment("// Function ${func.name.name}: ${func.element.type}\n"),
- js.statement('let # = #;', [name, _visit(func.functionExpression)])
+ js.statement('let # = #;', [name, _visit(func.functionExpression)]),
+ _emitFunctionTagged(name, func.element.type).toStatement()
]);
}
@@ -1066,6 +1159,26 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
return new JS.ObjectInitializer(properties);
}
+ List<JS.Expression> _emitFunctionTypeParts(FunctionType type) {
+ var returnType = type.returnType;
+ var parameterTypes = type.normalParameterTypes;
+ var optionalTypes = type.optionalParameterTypes;
+ var namedTypes = type.namedParameterTypes;
+ var rt = _emitTypeName(returnType);
+ var ra = _emitTypeNames(parameterTypes);
+ if (!namedTypes.isEmpty) {
+ assert(optionalTypes.isEmpty);
+ var na = _emitTypeProperties(namedTypes);
+ return [rt, ra, na];
+ }
+ if (!optionalTypes.isEmpty) {
+ assert(namedTypes.isEmpty);
+ var oa = _emitTypeNames(optionalTypes);
+ return [rt, ra, oa];
+ }
+ return [rt, ra];
+ }
+
/// Emits a Dart [type] into code.
///
/// If [lowerTypedef] is set, a typedef will be expanded as if it were a
@@ -1080,6 +1193,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
return js.call('dart.void');
} else if (type.isDynamic) {
return js.call('dart.dynamic');
+ } else if (type.isBottom) {
+ return js.call('dart.bottom');
}
_loader.declareBeforeUse(type.element);
@@ -1089,33 +1204,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
// to canonicalize them too, at least when inside the same library.
var name = type.name;
var element = type.element;
- if (name == '' || lowerTypedef) {
- var fnType = type as FunctionType;
- var returnType = fnType.returnType;
- var parameterTypes = fnType.normalParameterTypes;
- var optionalTypes = fnType.optionalParameterTypes;
- var namedTypes = fnType.namedParameterTypes;
- if (namedTypes.isEmpty) {
- if (optionalTypes.isEmpty) {
- return js.call('dart.functionType(#, #)', [
- _emitTypeName(returnType),
- _emitTypeNames(parameterTypes)
- ]);
- } else {
- return js.call('dart.functionType(#, #, #)', [
- _emitTypeName(returnType),
- _emitTypeNames(parameterTypes),
- _emitTypeNames(optionalTypes)
- ]);
- }
- } else {
- assert(optionalTypes.isEmpty);
- return js.call('dart.functionType(#, #, #)', [
- _emitTypeName(returnType),
- _emitTypeNames(parameterTypes),
- _emitTypeProperties(namedTypes)
- ]);
- }
+ if (name == '' || name == null || lowerTypedef) {
+ var parts = _emitFunctionTypeParts(type as FunctionType);
+ return js.call('dart.functionType(#)', [parts]);
}
if (type is TypeParameterType) {

Powered by Google App Engine
This is Rietveld 408576698