Chromium Code Reviews| Index: lib/src/codegen/js_codegen.dart |
| diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart |
| index 693afed564dfa13bc4030bb6337685136203b85f..bcdfad8606608494ad920ffe8730f974c0754734 100644 |
| --- a/lib/src/codegen/js_codegen.dart |
| +++ b/lib/src/codegen/js_codegen.dart |
| @@ -80,7 +80,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>[]; |
| @@ -154,20 +153,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 |
| @@ -240,8 +229,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); |
| @@ -434,12 +422,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); |
| } |
| @@ -455,8 +444,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); |
| @@ -705,6 +694,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) { |
| @@ -755,12 +745,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)); |
| } |
| } |
| } |
| @@ -850,6 +840,23 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
| ])); |
| } |
| + // Emits static fields. These are eager initialized if possible, otherwise |
| + // they are made static. |
|
vsm
2016/01/27 14:29:29
"made lazy"?
Jennifer Messerly
2016/01/27 17:26:28
good catch, done!
|
| + 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); |
| } |
| @@ -2149,21 +2156,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'; |
| } |
| @@ -2192,7 +2184,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)); |
| @@ -2203,11 +2199,41 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
| field.isFinal && |
| _isJSInvocation(field.initializer); |
| - /// Emits a static or top-level field. |
| - JS.Statement _emitStaticField(VariableDeclaration field) { |
| + /// Emits a static field. If the field is a lazy static, |
| + JS.Statement _emitConstantStaticField( |
| + ClassElement classElem, VariableDeclaration field) { |
| PropertyInducingElement element = field.element; |
| assert(element.isStatic); |
| + // If the field is constant, try to avoid the lazy static. |
| + _loader.startCheckingReferences(); |
| + JS.Expression jsInit = _visitInitializer(field); |
| + bool isLoaded = _loader.finishCheckingReferences(); |
| + bool eagerInit = |
| + isLoaded && (field.isConst || _constField.isFieldInitConstant(field)); |
|
vsm
2016/01/27 14:29:29
Could this be an '||' instead of an '&&'? I.e., i
Jennifer Messerly
2016/01/27 17:26:28
The initializer must not cause visible side effect
|
| + |
| + 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)) { |
| @@ -2217,6 +2243,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; |
| } |
| @@ -2226,16 +2254,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( |
| @@ -2249,17 +2272,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) { |
| @@ -2269,18 +2282,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, |
| @@ -2289,8 +2297,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), |
| @@ -2298,10 +2306,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 |
| @@ -2849,9 +2858,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)) { |