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

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

Issue 1636233002: fixes #427, static fields emitted outside the scope of their class (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: merged2 Created 4 years, 11 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 | « lib/runtime/dart/typed_data.js ('k') | lib/src/codegen/js_module_item_order.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/codegen/js_codegen.dart
diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart
index bd109027374ae8cf747ddeded42e602b1363b04a..36e1f829f9faea7db0d69c705eb92a89378fcfbd 100644
--- a/lib/src/codegen/js_codegen.dart
+++ b/lib/src/codegen/js_codegen.dart
@@ -81,7 +81,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
/// Imported libraries, and the temporaries used to refer to them.
final _imports = new Map<LibraryElement, JS.TemporaryId>();
final _exports = new Set<String>();
- final _lazyFields = <VariableDeclaration>[];
final _properties = <FunctionDeclaration>[];
final _privateNames = new HashMap<String, JS.TemporaryId>();
final _moduleItems = <JS.Statement>[];
@@ -155,20 +154,10 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
} else {
_loader.loadDeclaration(decl, decl.element);
}
- if (decl is ClassDeclaration) {
- // Static fields can be emitted into the top-level code, so they need
- // to potentially be ordered independently of the class.
- for (var member in decl.members) {
- if (member is FieldDeclaration) {
- visitFieldDeclaration(member);
- }
- }
- }
}
}
// Flush any unwritten fields/properties.
- _flushLazyFields(_moduleItems);
_flushLibraryProperties(_moduleItems);
// Mark all qualified names as qualified or not, depending on if they need
@@ -212,8 +201,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
}
void _emitModuleItem(AstNode node) {
- // Attempt to group adjacent fields/properties.
- if (node is! VariableDeclaration) _flushLazyFields(_moduleItems);
+ // Attempt to group adjacent properties.
if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems);
var code = _visit(node);
@@ -406,12 +394,13 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
var ctors = <ConstructorDeclaration>[];
var fields = <FieldDeclaration>[];
+ var staticFields = <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 FieldDeclaration) {
+ (member.isStatic ? staticFields : fields).add(member);
} else if (member is MethodDeclaration) {
methods.add(member);
}
@@ -427,8 +416,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
getConstantField(jsPeer, 'name', types.stringType)?.toStringValue();
}
- var body = _finishClassMembers(classElem, classExpr, ctors, fields, methods,
- node.metadata, jsPeerName);
+ var body = _finishClassMembers(classElem, classExpr, ctors, fields,
+ staticFields, methods, node.metadata, jsPeerName);
var result = _finishClassDef(type, body);
@@ -677,6 +666,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
JS.ClassExpression cls,
List<ConstructorDeclaration> ctors,
List<FieldDeclaration> fields,
+ List<FieldDeclaration> staticFields,
List<MethodDeclaration> methods,
List<Annotation> metadata,
String jsPeerName) {
@@ -727,12 +717,12 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
}
}
- // Instance fields, if they override getter/setter pairs
+ // Emits instance fields, if they are virtual
+ // (in other words, they override a getter/setter pair).
for (FieldDeclaration member in fields) {
- for (VariableDeclaration fieldDecl in member.fields.variables) {
- var field = fieldDecl.element as FieldElement;
- if (_fieldsNeedingStorage.contains(field)) {
- body.add(_overrideField(field));
+ for (VariableDeclaration field in member.fields.variables) {
+ if (_fieldsNeedingStorage.contains(field.element)) {
+ body.add(_overrideField(field.element));
}
}
}
@@ -822,6 +812,23 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
]));
}
+ // Emits static fields. These are eager initialized if possible, otherwise
+ // they are made lazy.
+ var lazyStatics = <VariableDeclaration>[];
+ for (FieldDeclaration member in staticFields) {
+ for (VariableDeclaration field in member.fields.variables) {
+ JS.Statement eagerField = _emitConstantStaticField(classElem, field);
+ if (eagerField != null) {
+ body.add(eagerField);
+ } else {
+ lazyStatics.add(field);
+ }
+ }
+ }
+ if (lazyStatics.isNotEmpty) {
+ body.add(_emitLazyFields(classElem, lazyStatics));
+ }
+
return _statement(body);
}
@@ -2121,21 +2128,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
}
}
- /// Emits static fields.
- ///
- /// Instance fields are emitted in [_initializeFields].
- ///
- /// These are generally treated the same as top-level fields, see
- /// [visitTopLevelVariableDeclaration].
- @override
- visitFieldDeclaration(FieldDeclaration node) {
- if (!node.isStatic) return;
-
- for (var f in node.fields.variables) {
- _loader.loadDeclaration(f, f.element);
- }
- }
-
_addExport(String name) {
if (!_exports.add(name)) throw 'Duplicate top level name found: $name';
}
@@ -2164,7 +2156,11 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
@override
visitVariableDeclaration(VariableDeclaration node) {
- if (node.element is PropertyInducingElement) return _emitStaticField(node);
+ if (node.element is PropertyInducingElement) {
+ // Static and instance fields are handled elsewhere.
+ assert(node.element is TopLevelVariableElement);
+ return _emitTopLevelField(node);
+ }
var name = new JS.Identifier(node.name.name);
return new JS.VariableInitialization(name, _visitInitializer(node));
@@ -2175,11 +2171,53 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
field.isFinal &&
_isJSInvocation(field.initializer);
- /// Emits a static or top-level field.
- JS.Statement _emitStaticField(VariableDeclaration field) {
+ /// Try to emit a constant static field.
+ ///
+ /// If the field's initializer does not cause side effects, and if all of
+ /// dependencies are safe to refer to while we are initializing the class,
+ /// then we can initialize it eagerly:
+ ///
+ /// // Baz must be const constructor, and the name "Baz" must be defined
+ /// // by this point.
+ /// Foo.bar = dart.const(new Baz(42));
+ ///
+ /// Otherwise, we'll need to generate a lazy-static field. That ensures
+ /// correct visible behavior, as well as avoiding referencing something that
+ /// isn't defined yet (because it is defined later in the module).
+ JS.Statement _emitConstantStaticField(
+ ClassElement classElem, VariableDeclaration field) {
PropertyInducingElement element = field.element;
assert(element.isStatic);
+ _loader.startCheckingReferences();
+ JS.Expression jsInit = _visitInitializer(field);
+ bool isLoaded = _loader.finishCheckingReferences();
+
+ bool eagerInit =
+ isLoaded && (field.isConst || _constField.isFieldInitConstant(field));
+
+ var fieldName = field.name.name;
+ if (eagerInit && !JS.invalidStaticFieldName(fieldName)) {
+ return annotateVariable(
+ js.statement('#.# = #;', [
+ classElem.name,
+ _emitMemberName(fieldName, isStatic: true),
+ jsInit
+ ]),
+ field.element);
+ }
+
+ // This means it should be treated as a lazy field.
+ // TODO(jmesserly): we're throwing away the initializer expression,
+ // which will force us to regenerate it.
+ return null;
+ }
+
+ /// Emits a top-level field.
+ JS.Statement _emitTopLevelField(VariableDeclaration field) {
+ TopLevelVariableElement element = field.element;
+ assert(element.isStatic);
+
bool eagerInit;
JS.Expression jsInit;
if (field.isConst || _constField.isFieldInitConstant(field)) {
@@ -2189,6 +2227,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
_loader.finishTopLevel(element);
eagerInit = _loader.isLoaded(element);
} else {
+ // TODO(jmesserly): we're visiting the initializer here, and again
+ // later on when we emit lazy fields. That seems busted.
jsInit = _visitInitializer(field);
eagerInit = false;
}
@@ -2198,16 +2238,11 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
var isJSTopLevel = field.isFinal && _isFinalJSDecl(field);
if (isJSTopLevel) eagerInit = true;
- var fieldName = field.name.name;
- if (element is TopLevelVariableElement) {
- fieldName = _getJSExportName(element) ?? fieldName;
- }
- if ((field.isConst && eagerInit && element is TopLevelVariableElement) ||
- isJSTopLevel) {
+ var fieldName = _getJSExportName(element) ?? field.name.name;
+ if (field.isConst && eagerInit || isJSTopLevel) {
// constant fields don't change, so we can generate them as `let`
// but add them to the module's exports. However, make sure we generate
// anything they depend on first.
-
if (isPublic(fieldName)) _addExport(fieldName);
var declKeyword = field.isConst || field.isFinal ? 'const' : 'let';
return annotateVariable(
@@ -2221,17 +2256,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
js.statement('# = #;', [_visit(field.name), jsInit]), field.element);
}
- var body = <JS.Statement>[];
- if (_lazyFields.isNotEmpty) {
- var existingTarget = _lazyFields[0].element.enclosingElement;
- if (existingTarget != element.enclosingElement) {
- _flushLazyFields(body);
- }
- }
-
- _lazyFields.add(field);
-
- return _statement(body);
+ return _emitLazyFields(currentLibrary, [field]);
}
JS.Expression _visitInitializer(VariableDeclaration node) {
@@ -2241,18 +2266,13 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
return value ?? new JS.LiteralNull();
}
- void _flushLazyFields(List<JS.Statement> body) {
- if (_lazyFields.isEmpty) return;
- body.add(_emitLazyFields(_lazyFields));
- _lazyFields.clear();
- }
-
- JS.Statement _emitLazyFields(List<VariableDeclaration> fields) {
+ JS.Statement _emitLazyFields(
+ Element target, List<VariableDeclaration> fields) {
var methods = [];
for (var node in fields) {
var name = node.name.name;
var element = node.element;
- var access = _emitMemberName(name, type: element.type, isStatic: true);
+ var access = _emitMemberName(name, isStatic: true);
methods.add(annotate(
new JS.Method(
access,
@@ -2261,8 +2281,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
isGetter: true),
_findAccessor(element, getter: true)));
- // TODO(jmesserly): use a dummy setter to indicate writable.
- if (!node.isFinal) {
+ // TODO(jmesserly): currently uses a dummy setter to indicate writable.
+ if (!node.isFinal && !node.isConst) {
methods.add(annotate(
new JS.Method(access, js.call('function(_) {}') as JS.Fun,
isSetter: true),
@@ -2270,10 +2290,11 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
}
}
- JS.Expression objExpr = _exportsVar;
- var target = _lazyFields[0].element.enclosingElement;
+ JS.Expression objExpr;
if (target is ClassElement) {
objExpr = new JS.Identifier(target.type.name);
+ } else {
+ objExpr = _libraryName(target);
}
return js
@@ -2821,9 +2842,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
member = (member as PropertyAccessorElement).variable;
}
bool isStatic = member is ClassMemberElement && member.isStatic;
- if (isStatic) {
- _loader.declareBeforeUse(member);
- }
var name = _emitMemberName(memberId.name,
type: getStaticType(target), isStatic: isStatic);
if (DynamicInvoke.get(target)) {
« no previous file with comments | « lib/runtime/dart/typed_data.js ('k') | lib/src/codegen/js_module_item_order.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698