Index: pkg/dart2js_incremental/lib/library_updater.dart |
diff --git a/pkg/dart2js_incremental/lib/library_updater.dart b/pkg/dart2js_incremental/lib/library_updater.dart |
deleted file mode 100644 |
index 7994891952d448b86ed51ad2eb3e34440730cfc8..0000000000000000000000000000000000000000 |
--- a/pkg/dart2js_incremental/lib/library_updater.dart |
+++ /dev/null |
@@ -1,1521 +0,0 @@ |
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-library dart2js_incremental.library_updater; |
- |
-import 'dart:async' show |
- Future; |
- |
-import 'package:compiler/compiler.dart' as api; |
- |
-import 'package:compiler/src/compiler.dart' show |
- Compiler; |
- |
-import 'package:compiler/src/diagnostics/messages.dart' show |
- MessageKind; |
- |
-import 'package:compiler/src/elements/elements.dart' show |
- ClassElement, |
- CompilationUnitElement, |
- Element, |
- FunctionElement, |
- LibraryElement, |
- STATE_NOT_STARTED, |
- ScopeContainerElement; |
- |
-import 'package:compiler/src/enqueue.dart' show |
- EnqueueTask; |
- |
-import 'package:compiler/src/parser/listener.dart' show |
- Listener; |
- |
-import 'package:compiler/src/parser/node_listener.dart' show |
- NodeListener; |
- |
-import 'package:compiler/src/parser/partial_elements.dart' show |
- PartialClassElement, |
- PartialElement, |
- PartialFieldList, |
- PartialFunctionElement; |
- |
-import 'package:compiler/src/parser/parser.dart' show |
- Parser; |
- |
-import 'package:compiler/src/scanner/scanner.dart' show |
- Scanner; |
- |
-import 'package:compiler/src/tokens/token.dart' show |
- Token; |
- |
-import 'package:compiler/src/tokens/token_constants.dart' show |
- EOF_TOKEN; |
- |
-import 'package:compiler/src/script.dart' show |
- Script; |
- |
-import 'package:compiler/src/io/source_file.dart' show |
- CachingUtf8BytesSourceFile, |
- SourceFile, |
- StringSourceFile; |
- |
-import 'package:compiler/src/tree/tree.dart' show |
- ClassNode, |
- FunctionExpression, |
- LibraryTag, |
- NodeList, |
- Part, |
- StringNode, |
- unparse; |
- |
-import 'package:compiler/src/js/js.dart' show |
- js; |
- |
-import 'package:compiler/src/js/js.dart' as jsAst; |
- |
-import 'package:compiler/src/js_emitter/js_emitter.dart' show |
- CodeEmitterTask, |
- computeMixinClass; |
- |
-import 'package:compiler/src/js_emitter/full_emitter/emitter.dart' |
- as full show Emitter; |
- |
-import 'package:compiler/src/js_emitter/model.dart' show |
- Class, |
- Method; |
- |
-import 'package:compiler/src/js_emitter/program_builder/program_builder.dart' |
- show ProgramBuilder; |
- |
-import 'package:js_runtime/shared/embedded_names.dart' |
- as embeddedNames; |
- |
-import 'package:compiler/src/js_backend/js_backend.dart' show |
- JavaScriptBackend, |
- Namer; |
- |
-import 'package:compiler/src/util/util.dart' show |
- Link, |
- LinkBuilder; |
- |
-import 'package:compiler/src/elements/modelx.dart' show |
- ClassElementX, |
- CompilationUnitElementX, |
- DeclarationSite, |
- ElementX, |
- FieldElementX, |
- LibraryElementX; |
- |
-import 'package:compiler/src/universe/selector.dart' show |
- Selector; |
- |
-import 'package:compiler/src/constants/values.dart' show |
- ConstantValue; |
- |
-import 'package:compiler/src/library_loader.dart' show |
- TagState; |
- |
-import 'diff.dart' show |
- Difference, |
- computeDifference; |
- |
-import 'dart2js_incremental.dart' show |
- IncrementalCompilationFailed, |
- IncrementalCompiler; |
- |
-typedef void Logger(message); |
- |
-typedef bool Reuser( |
- Token diffToken, |
- PartialElement before, |
- PartialElement after); |
- |
-class FailedUpdate { |
- /// Either an [Element] or a [Difference]. |
- final context; |
- final String message; |
- |
- FailedUpdate(this.context, this.message); |
- |
- String toString() { |
- if (context == null) return '$message'; |
- return 'In $context:\n $message'; |
- } |
-} |
- |
-abstract class _IncrementalCompilerContext { |
- IncrementalCompiler incrementalCompiler; |
- |
- Set<ClassElementX> _emittedClasses; |
- |
- Set<ClassElementX> _directlyInstantiatedClasses; |
- |
- Set<ConstantValue> _compiledConstants; |
-} |
- |
-class IncrementalCompilerContext extends _IncrementalCompilerContext { |
- final Set<Uri> _uriWithUpdates = new Set<Uri>(); |
- |
- void set incrementalCompiler(IncrementalCompiler value) { |
- if (super.incrementalCompiler != null) { |
- throw new StateError("Can't set [incrementalCompiler] more than once."); |
- } |
- super.incrementalCompiler = value; |
- } |
- |
- void registerUriWithUpdates(Iterable<Uri> uris) { |
- _uriWithUpdates.addAll(uris); |
- } |
- |
- void _captureState(Compiler compiler) { |
- JavaScriptBackend backend = compiler.backend; |
- Set neededClasses = backend.emitter.neededClasses; |
- if (neededClasses == null) { |
- neededClasses = new Set(); |
- } |
- _emittedClasses = new Set.from(neededClasses); |
- |
- _directlyInstantiatedClasses = |
- new Set.from(compiler.codegenWorld.directlyInstantiatedClasses); |
- |
- // This breaks constant tracking of the incremental compiler. It would need |
- // to capture the emitted constants. |
- List<ConstantValue> constants = null; |
- if (constants == null) constants = <ConstantValue>[]; |
- _compiledConstants = new Set<ConstantValue>.identity()..addAll(constants); |
- } |
- |
- bool _uriHasUpdate(Uri uri) => _uriWithUpdates.contains(uri); |
-} |
- |
-class LibraryUpdater extends JsFeatures { |
- final Compiler compiler; |
- |
- final api.CompilerInputProvider inputProvider; |
- |
- final Logger logTime; |
- |
- final Logger logVerbose; |
- |
- final List<Update> updates = <Update>[]; |
- |
- final List<FailedUpdate> _failedUpdates = <FailedUpdate>[]; |
- |
- final Set<ElementX> _elementsToInvalidate = new Set<ElementX>(); |
- |
- final Set<ElementX> _removedElements = new Set<ElementX>(); |
- |
- final Set<ClassElementX> _classesWithSchemaChanges = |
- new Set<ClassElementX>(); |
- |
- final IncrementalCompilerContext _context; |
- |
- final Map<Uri, Future> _sources = <Uri, Future>{}; |
- |
- /// Cached tokens of entry compilation units. |
- final Map<LibraryElementX, Token> _entryUnitTokens = |
- <LibraryElementX, Token>{}; |
- |
- /// Cached source files for entry compilation units. |
- final Map<LibraryElementX, SourceFile> _entrySourceFiles = |
- <LibraryElementX, SourceFile>{}; |
- |
- bool _hasComputedNeeds = false; |
- |
- bool _hasCapturedCompilerState = false; |
- |
- LibraryUpdater( |
- this.compiler, |
- this.inputProvider, |
- this.logTime, |
- this.logVerbose, |
- this._context) { |
- // TODO(ahe): Would like to remove this from the constructor. However, the |
- // state must be captured before calling [reuseCompiler]. |
- // Proper solution might be: [reuseCompiler] should not clear the sets that |
- // are captured in [IncrementalCompilerContext._captureState]. |
- _ensureCompilerStateCaptured(); |
- } |
- |
- /// Returns the classes emitted by [compiler]. |
- Set<ClassElementX> get _emittedClasses => _context._emittedClasses; |
- |
- /// Returns the directly instantantiated classes seen by [compiler] (this |
- /// includes interfaces and may be different from [_emittedClasses] that only |
- /// includes interfaces used in type tests). |
- Set<ClassElementX> get _directlyInstantiatedClasses { |
- return _context._directlyInstantiatedClasses; |
- } |
- |
- /// Returns the constants emitted by [compiler]. |
- Set<ConstantValue> get _compiledConstants => _context._compiledConstants; |
- |
- /// When [true], updates must be applied (using [applyUpdates]) before the |
- /// [compiler]'s state correctly reflects the updated program. |
- bool get hasPendingUpdates => !updates.isEmpty; |
- |
- bool get failed => !_failedUpdates.isEmpty; |
- |
- /// Used as tear-off passed to [LibraryLoaderTask.resetAsync]. |
- Future<bool> reuseLibrary(LibraryElement library) { |
- _ensureCompilerStateCaptured(); |
- assert(compiler != null); |
- if (library.isPlatformLibrary) { |
- logTime('Reusing $library (assumed read-only).'); |
- return new Future.value(true); |
- } |
- return _haveTagsChanged(library).then((bool haveTagsChanged) { |
- if (haveTagsChanged) { |
- cannotReuse( |
- library, |
- "Changes to library, import, export, or part declarations not" |
- " supported."); |
- return true; |
- } |
- |
- bool isChanged = false; |
- List<Future<Script>> futureScripts = <Future<Script>>[]; |
- |
- for (CompilationUnitElementX unit in library.compilationUnits) { |
- Uri uri = unit.script.resourceUri; |
- if (_context._uriHasUpdate(uri)) { |
- isChanged = true; |
- futureScripts.add(_updatedScript(unit.script, library)); |
- } else { |
- futureScripts.add(new Future.value(unit.script)); |
- } |
- } |
- |
- if (!isChanged) { |
- logTime("Reusing $library, source didn't change."); |
- return true; |
- } |
- |
- return Future.wait(futureScripts).then( |
- (List<Script> scripts) => canReuseLibrary(library, scripts)); |
- }).whenComplete(() => _cleanUp(library)); |
- } |
- |
- void _cleanUp(LibraryElementX library) { |
- _entryUnitTokens.remove(library); |
- _entrySourceFiles.remove(library); |
- } |
- |
- Future<Script> _updatedScript(Script before, LibraryElementX library) { |
- if (before == library.entryCompilationUnit.script && |
- _entrySourceFiles.containsKey(library)) { |
- return new Future.value(before.copyWithFile(_entrySourceFiles[library])); |
- } |
- |
- return _readUri(before.resourceUri).then((bytes) { |
- Uri uri = before.file.uri; |
- String filename = before.file.filename; |
- SourceFile sourceFile = bytes is String |
- ? new StringSourceFile(uri, filename, bytes) |
- : new CachingUtf8BytesSourceFile(uri, filename, bytes); |
- return before.copyWithFile(sourceFile); |
- }); |
- } |
- |
- Future<bool> _haveTagsChanged(LibraryElement library) { |
- Script before = library.entryCompilationUnit.script; |
- if (!_context._uriHasUpdate(before.resourceUri)) { |
- // The entry compilation unit hasn't been updated. So the tags aren't |
- // changed. |
- return new Future<bool>.value(false); |
- } |
- |
- return _updatedScript(before, library).then((Script script) { |
- _entrySourceFiles[library] = script.file; |
- Token token = new Scanner(_entrySourceFiles[library]).tokenize(); |
- _entryUnitTokens[library] = token; |
- // Using two parsers to only create the nodes we want ([LibraryTag]). |
- Parser parser = new Parser(new Listener(), compiler.options); |
- NodeListener listener = new NodeListener( |
- compiler, library.entryCompilationUnit); |
- Parser nodeParser = new Parser(listener, compiler.options); |
- Iterator<LibraryTag> tags = library.tags.iterator; |
- while (token.kind != EOF_TOKEN) { |
- token = parser.parseMetadataStar(token); |
- if (parser.optional('library', token) || |
- parser.optional('import', token) || |
- parser.optional('export', token) || |
- parser.optional('part', token)) { |
- if (!tags.moveNext()) return true; |
- token = nodeParser.parseTopLevelDeclaration(token); |
- LibraryTag tag = listener.popNode(); |
- assert(listener.nodes.isEmpty); |
- if (unparse(tags.current) != unparse(tag)) { |
- return true; |
- } |
- } else { |
- break; |
- } |
- } |
- return tags.moveNext(); |
- }); |
- } |
- |
- Future _readUri(Uri uri) { |
- return _sources.putIfAbsent(uri, () => inputProvider(uri)); |
- } |
- |
- void _ensureCompilerStateCaptured() { |
- // TODO(ahe): [compiler] shouldn't be null, remove the following line. |
- if (compiler == null) return; |
- |
- if (_hasCapturedCompilerState) return; |
- _context._captureState(compiler); |
- _hasCapturedCompilerState = true; |
- } |
- |
- /// Returns true if [library] can be reused. |
- /// |
- /// This methods also computes the [updates] (patches) needed to have |
- /// [library] reflect the modifications in [scripts]. |
- bool canReuseLibrary(LibraryElement library, List<Script> scripts) { |
- logTime('Attempting to reuse ${library}.'); |
- |
- Uri entryUri = library.entryCompilationUnit.script.resourceUri; |
- Script entryScript = |
- scripts.singleWhere((Script script) => script.resourceUri == entryUri); |
- LibraryElement newLibrary = |
- new LibraryElementX(entryScript, library.canonicalUri); |
- if (_entryUnitTokens.containsKey(library)) { |
- compiler.dietParser.dietParse( |
- newLibrary.entryCompilationUnit, _entryUnitTokens[library]); |
- } else { |
- compiler.scanner.scanLibrary(newLibrary); |
- } |
- |
- TagState tagState = new TagState(); |
- for (LibraryTag tag in newLibrary.tags) { |
- if (tag.isImport) { |
- tagState.checkTag(TagState.IMPORT_OR_EXPORT, tag, compiler); |
- } else if (tag.isExport) { |
- tagState.checkTag(TagState.IMPORT_OR_EXPORT, tag, compiler); |
- } else if (tag.isLibraryName) { |
- tagState.checkTag(TagState.LIBRARY, tag, compiler); |
- if (newLibrary.libraryTag == null) { |
- // Use the first if there are multiple (which is reported as an |
- // error in [TagState.checkTag]). |
- newLibrary.libraryTag = tag; |
- } |
- } else if (tag.isPart) { |
- tagState.checkTag(TagState.PART, tag, compiler); |
- } |
- } |
- |
- // TODO(ahe): Process tags using TagState, not |
- // LibraryLoaderTask.processLibraryTags. |
- Link<CompilationUnitElement> units = library.compilationUnits; |
- for (Script script in scripts) { |
- CompilationUnitElementX unit = units.head; |
- units = units.tail; |
- if (script != entryScript) { |
- // TODO(ahe): Copied from library_loader. |
- CompilationUnitElement newUnit = |
- new CompilationUnitElementX(script, newLibrary); |
- compiler.withCurrentElement(newUnit, () { |
- compiler.scanner.scan(newUnit); |
- if (unit.partTag == null) { |
- compiler.reportError(unit, MessageKind.MISSING_PART_OF_TAG); |
- } |
- }); |
- } |
- } |
- |
- logTime('New library synthesized.'); |
- return canReuseScopeContainerElement(library, newLibrary); |
- } |
- |
- bool cannotReuse(context, String message) { |
- _failedUpdates.add(new FailedUpdate(context, message)); |
- logVerbose(message); |
- return false; |
- } |
- |
- bool canReuseScopeContainerElement( |
- ScopeContainerElement element, |
- ScopeContainerElement newElement) { |
- List<Difference> differences = computeDifference(element, newElement); |
- logTime('Differences computed.'); |
- for (Difference difference in differences) { |
- logTime('Looking at difference: $difference'); |
- |
- if (difference.before == null && difference.after is PartialElement) { |
- canReuseAddedElement(difference.after, element, newElement); |
- continue; |
- } |
- if (difference.after == null && difference.before is PartialElement) { |
- canReuseRemovedElement(difference.before, element); |
- continue; |
- } |
- Token diffToken = difference.token; |
- if (diffToken == null) { |
- cannotReuse(difference, "No difference token."); |
- continue; |
- } |
- if (difference.after is! PartialElement && |
- difference.before is! PartialElement) { |
- cannotReuse(difference, "Don't know how to recompile."); |
- continue; |
- } |
- PartialElement before = difference.before; |
- PartialElement after = difference.after; |
- |
- Reuser reuser; |
- |
- if (before is PartialFunctionElement && after is PartialFunctionElement) { |
- reuser = canReuseFunction; |
- } else if (before is PartialClassElement && |
- after is PartialClassElement) { |
- reuser = canReuseClass; |
- } else { |
- reuser = unableToReuse; |
- } |
- if (!reuser(diffToken, before, after)) { |
- assert(!_failedUpdates.isEmpty); |
- continue; |
- } |
- } |
- |
- return _failedUpdates.isEmpty; |
- } |
- |
- bool canReuseAddedElement( |
- PartialElement element, |
- ScopeContainerElement container, |
- ScopeContainerElement syntheticContainer) { |
- if (element is PartialFunctionElement) { |
- addFunction(element, container); |
- return true; |
- } else if (element is PartialClassElement) { |
- addClass(element, container); |
- return true; |
- } else if (element is PartialFieldList) { |
- addFields(element, container, syntheticContainer); |
- return true; |
- } |
- return cannotReuse(element, "Adding ${element.runtimeType} not supported."); |
- } |
- |
- void addFunction( |
- PartialFunctionElement element, |
- /* ScopeContainerElement */ container) { |
- invalidateScopesAffectedBy(element, container); |
- |
- updates.add(new AddedFunctionUpdate(compiler, element, container)); |
- } |
- |
- void addClass( |
- PartialClassElement element, |
- LibraryElementX library) { |
- invalidateScopesAffectedBy(element, library); |
- |
- updates.add(new AddedClassUpdate(compiler, element, library)); |
- } |
- |
- /// Called when a field in [definition] has changed. |
- /// |
- /// There's no direct link from a [PartialFieldList] to its implied |
- /// [FieldElementX], so instead we use [syntheticContainer], the (synthetic) |
- /// container created by [canReuseLibrary], or [canReuseClass] (through |
- /// [PartialClassElement.parseNode]). This container is scanned looking for |
- /// fields whose declaration site is [definition]. |
- // TODO(ahe): It would be nice if [computeDifference] returned this |
- // information directly. |
- void addFields( |
- PartialFieldList definition, |
- ScopeContainerElement container, |
- ScopeContainerElement syntheticContainer) { |
- List<FieldElementX> fields = <FieldElementX>[]; |
- syntheticContainer.forEachLocalMember((ElementX member) { |
- if (member.declarationSite == definition) { |
- fields.add(member); |
- } |
- }); |
- for (FieldElementX field in fields) { |
- // TODO(ahe): This only works when there's one field per |
- // PartialFieldList. |
- addField(field, container); |
- } |
- } |
- |
- void addField(FieldElementX element, ScopeContainerElement container) { |
- invalidateScopesAffectedBy(element, container); |
- if (element.isInstanceMember) { |
- _classesWithSchemaChanges.add(container); |
- } |
- updates.add(new AddedFieldUpdate(compiler, element, container)); |
- } |
- |
- bool canReuseRemovedElement( |
- PartialElement element, |
- ScopeContainerElement container) { |
- if (element is PartialFunctionElement) { |
- removeFunction(element); |
- return true; |
- } else if (element is PartialClassElement) { |
- removeClass(element); |
- return true; |
- } else if (element is PartialFieldList) { |
- removeFields(element, container); |
- return true; |
- } |
- return cannotReuse( |
- element, "Removing ${element.runtimeType} not supported."); |
- } |
- |
- void removeFunction(PartialFunctionElement element) { |
- logVerbose("Removed method $element."); |
- |
- invalidateScopesAffectedBy(element, element.enclosingElement); |
- |
- _removedElements.add(element); |
- |
- updates.add(new RemovedFunctionUpdate(compiler, element)); |
- } |
- |
- void removeClass(PartialClassElement element) { |
- logVerbose("Removed class $element."); |
- |
- invalidateScopesAffectedBy(element, element.library); |
- |
- _removedElements.add(element); |
- element.forEachLocalMember((ElementX member) { |
- _removedElements.add(member); |
- }); |
- |
- updates.add(new RemovedClassUpdate(compiler, element)); |
- } |
- |
- void removeFields( |
- PartialFieldList definition, |
- ScopeContainerElement container) { |
- List<FieldElementX> fields = <FieldElementX>[]; |
- container.forEachLocalMember((ElementX member) { |
- if (member.declarationSite == definition) { |
- fields.add(member); |
- } |
- }); |
- for (FieldElementX field in fields) { |
- // TODO(ahe): This only works when there's one field per |
- // PartialFieldList. |
- removeField(field); |
- } |
- } |
- |
- void removeField(FieldElementX element) { |
- logVerbose("Removed field $element."); |
- if (!element.isInstanceMember) { |
- cannotReuse(element, "Not an instance field."); |
- } else { |
- removeInstanceField(element); |
- } |
- } |
- |
- void removeInstanceField(FieldElementX element) { |
- PartialClassElement cls = element.enclosingClass; |
- |
- _classesWithSchemaChanges.add(cls); |
- invalidateScopesAffectedBy(element, cls); |
- |
- _removedElements.add(element); |
- |
- updates.add(new RemovedFieldUpdate(compiler, element)); |
- } |
- |
- void invalidateScopesAffectedBy( |
- ElementX element, |
- /* ScopeContainerElement */ container) { |
- for (ScopeContainerElement scope in scopesAffectedBy(element, container)) { |
- scanSites(scope, (Element member, DeclarationSite site) { |
- // TODO(ahe): Cache qualifiedNamesIn to avoid quadratic behavior. |
- Set<String> names = qualifiedNamesIn(site); |
- if (canNamesResolveStaticallyTo(names, element, container)) { |
- _elementsToInvalidate.add(member); |
- } |
- }); |
- } |
- } |
- |
- /// Invoke [f] on each [DeclarationSite] in [element]. If [element] is a |
- /// [ScopeContainerElement], invoke f on all local members as well. |
- void scanSites( |
- Element element, |
- void f(ElementX element, DeclarationSite site)) { |
- DeclarationSite site = declarationSite(element); |
- if (site != null) { |
- f(element, site); |
- } |
- if (element is ScopeContainerElement) { |
- element.forEachLocalMember((member) { scanSites(member, f); }); |
- } |
- } |
- |
- /// Assume [element] is either removed from or added to [container], and |
- /// return all [ScopeContainerElement] that can see this change. |
- List<ScopeContainerElement> scopesAffectedBy( |
- Element element, |
- /* ScopeContainerElement */ container) { |
- // TODO(ahe): Use library export graph to compute this. |
- // TODO(ahe): Should return all user-defined libraries and packages. |
- LibraryElement library = container.library; |
- List<ScopeContainerElement> result = <ScopeContainerElement>[library]; |
- |
- if (!container.isClass) return result; |
- |
- ClassElement cls = container; |
- |
- var externalSubtypes = |
- compiler.world.subtypesOf(cls).where((e) => e.library != library); |
- |
- return result..addAll(externalSubtypes); |
- } |
- |
- /// Returns true if function [before] can be reused to reflect the changes in |
- /// [after]. |
- /// |
- /// If [before] can be reused, an update (patch) is added to [updates]. |
- bool canReuseFunction( |
- Token diffToken, |
- PartialFunctionElement before, |
- PartialFunctionElement after) { |
- FunctionExpression node = |
- after.parseNode(compiler.parsingContext).asFunctionExpression(); |
- if (node == null) { |
- return cannotReuse(after, "Not a function expression: '$node'"); |
- } |
- Token last = after.endToken; |
- if (node.body != null) { |
- last = node.body.getBeginToken(); |
- } |
- if (isTokenBetween(diffToken, after.beginToken, last)) { |
- removeFunction(before); |
- addFunction(after, before.enclosingElement); |
- return true; |
- } |
- logVerbose('Simple modification of ${after} detected'); |
- updates.add(new FunctionUpdate(compiler, before, after)); |
- return true; |
- } |
- |
- bool canReuseClass( |
- Token diffToken, |
- PartialClassElement before, |
- PartialClassElement after) { |
- ClassNode node = after.parseNode(compiler.parsingContext).asClassNode(); |
- if (node == null) { |
- return cannotReuse(after, "Not a ClassNode: '$node'"); |
- } |
- NodeList body = node.body; |
- if (body == null) { |
- return cannotReuse(after, "Class has no body."); |
- } |
- if (isTokenBetween(diffToken, node.beginToken, body.beginToken)) { |
- logVerbose('Class header modified in ${after}'); |
- updates.add(new ClassUpdate(compiler, before, after)); |
- before.forEachLocalMember((ElementX member) { |
- // TODO(ahe): Quadratic. |
- invalidateScopesAffectedBy(member, before); |
- }); |
- } |
- return canReuseScopeContainerElement(before, after); |
- } |
- |
- bool isTokenBetween(Token token, Token first, Token last) { |
- Token current = first; |
- while (current != last && current.kind != EOF_TOKEN) { |
- if (current == token) { |
- return true; |
- } |
- current = current.next; |
- } |
- return false; |
- } |
- |
- bool unableToReuse( |
- Token diffToken, |
- PartialElement before, |
- PartialElement after) { |
- return cannotReuse( |
- after, |
- 'Unhandled change:' |
- ' ${before} (${before.runtimeType} -> ${after.runtimeType}).'); |
- } |
- |
- /// Apply the collected [updates]. Return a list of elements that needs to be |
- /// recompiled after applying the updates. Any elements removed as a |
- /// consequence of applying the patches are added to [removals] if provided. |
- List<Element> applyUpdates([List<Update> removals]) { |
- for (Update update in updates) { |
- update.captureState(); |
- } |
- if (!_failedUpdates.isEmpty) { |
- throw new IncrementalCompilationFailed(_failedUpdates.join('\n\n')); |
- } |
- for (ElementX element in _elementsToInvalidate) { |
- compiler.forgetElement(element); |
- element.reuseElement(); |
- } |
- List<Element> elementsToInvalidate = <Element>[]; |
- for (ElementX element in _elementsToInvalidate) { |
- if (!_removedElements.contains(element)) { |
- elementsToInvalidate.add(element); |
- } |
- } |
- for (Update update in updates) { |
- Element element = update.apply(); |
- if (update.isRemoval) { |
- if (removals != null) { |
- removals.add(update); |
- } |
- } else { |
- elementsToInvalidate.add(element); |
- } |
- } |
- return elementsToInvalidate; |
- } |
- |
- String computeUpdateJs() { |
- List<Update> removals = <Update>[]; |
- List<Element> updatedElements = applyUpdates(removals); |
- if (compiler.progress != null) { |
- compiler.progress.reset(); |
- } |
- for (Element element in updatedElements) { |
- if (!element.isClass) { |
- enqueuer.resolution.addToWorkList(element); |
- } else { |
- NO_WARN(element).ensureResolved(compiler); |
- } |
- } |
- compiler.processQueue(enqueuer.resolution, null); |
- |
- compiler.phase = Compiler.PHASE_DONE_RESOLVING; |
- |
- // TODO(ahe): Clean this up. Don't call this method in analyze-only mode. |
- if (compiler.options.analyzeOnly) return "/* analyze only */"; |
- |
- Set<ClassElementX> changedClasses = |
- new Set<ClassElementX>.from(_classesWithSchemaChanges); |
- for (Element element in updatedElements) { |
- if (!element.isClass) { |
- enqueuer.codegen.addToWorkList(element); |
- } else { |
- changedClasses.add(element); |
- } |
- } |
- compiler.processQueue(enqueuer.codegen, null); |
- |
- // Run through all compiled methods and see if they may apply to |
- // newlySeenSelectors. |
- for (Element e in enqueuer.codegen.generatedCode.keys) { |
- if (e.isFunction && !e.isConstructor && |
- e.functionSignature.hasOptionalParameters) { |
- for (Selector selector in enqueuer.codegen.newlySeenSelectors) { |
- // TODO(ahe): Group selectors by name at this point for improved |
- // performance. |
- if (e.isInstanceMember && selector.applies(e, compiler.world)) { |
- // TODO(ahe): Don't use |
- // enqueuer.codegen.newlyEnqueuedElements directly like |
- // this, make a copy. |
- enqueuer.codegen.newlyEnqueuedElements.add(e); |
- } |
- if (selector.name == namer.closureInvocationSelectorName) { |
- selector = new Selector.call( |
- e.name, e.library, |
- selector.argumentCount, selector.namedArguments); |
- if (selector.appliesUnnamed(e, compiler.world)) { |
- // TODO(ahe): Also make a copy here. |
- enqueuer.codegen.newlyEnqueuedElements.add(e); |
- } |
- } |
- } |
- } |
- } |
- |
- List<jsAst.Statement> updates = <jsAst.Statement>[]; |
- |
- Set<ClassElementX> newClasses = new Set.from( |
- compiler.codegenWorld.directlyInstantiatedClasses); |
- newClasses.removeAll(_directlyInstantiatedClasses); |
- |
- if (!newClasses.isEmpty) { |
- // Ask the emitter to compute "needs" (only) if new classes were |
- // instantiated. |
- _ensureAllNeededEntitiesComputed(); |
- newClasses = new Set.from(emitter.neededClasses); |
- newClasses.removeAll(_emittedClasses); |
- } else { |
- // Make sure that the set of emitted classes is preserved for subsequent |
- // updates. |
- // TODO(ahe): This is a bit convoluted, find a better approach. |
- emitter.neededClasses |
- ..clear() |
- ..addAll(_emittedClasses); |
- } |
- |
- List<jsAst.Statement> inherits = <jsAst.Statement>[]; |
- |
- for (ClassElementX cls in newClasses) { |
- jsAst.Node classAccess = emitter.constructorAccess(cls); |
- String name = namer.className(cls); |
- |
- updates.add( |
- js.statement( |
- r'# = #', [classAccess, invokeDefineClass(cls)])); |
- |
- ClassElement superclass = cls.superclass; |
- if (superclass != null) { |
- jsAst.Node superAccess = emitter.constructorAccess(superclass); |
- inherits.add( |
- js.statement( |
- r'this.inheritFrom(#, #)', [classAccess, superAccess])); |
- } |
- } |
- |
- // Call inheritFrom after all classes have been created. This way we don't |
- // need to sort the classes by having superclasses defined before their |
- // subclasses. |
- updates.addAll(inherits); |
- |
- for (ClassElementX cls in changedClasses) { |
- ClassElement superclass = cls.superclass; |
- jsAst.Node superAccess = |
- superclass == null ? js('null') |
- : emitter.constructorAccess(superclass); |
- jsAst.Node classAccess = emitter.constructorAccess(cls); |
- updates.add( |
- js.statement( |
- r'# = this.schemaChange(#, #, #)', |
- [classAccess, invokeDefineClass(cls), classAccess, superAccess])); |
- } |
- |
- for (RemovalUpdate update in removals) { |
- update.writeUpdateJsOn(updates); |
- } |
- for (Element element in enqueuer.codegen.newlyEnqueuedElements) { |
- if (element.isField) { |
- updates.addAll(computeFieldUpdateJs(element)); |
- } else { |
- updates.add(computeMethodUpdateJs(element)); |
- } |
- } |
- |
- Set<ConstantValue> newConstants = new Set<ConstantValue>.identity()..addAll( |
- compiler.backend.constants.compiledConstants); |
- newConstants.removeAll(_compiledConstants); |
- |
- if (!newConstants.isEmpty) { |
- _ensureAllNeededEntitiesComputed(); |
- List<ConstantValue> constants = |
- emitter.outputConstantLists[compiler.deferredLoadTask.mainOutputUnit]; |
- if (constants != null) { |
- for (ConstantValue constant in constants) { |
- if (!_compiledConstants.contains(constant)) { |
- full.Emitter fullEmitter = emitter.emitter; |
- jsAst.Statement constantInitializer = |
- fullEmitter.buildConstantInitializer(constant).toStatement(); |
- updates.add(constantInitializer); |
- } |
- } |
- } |
- } |
- |
- updates.add(js.statement(r''' |
-if (this.pendingStubs) { |
- this.pendingStubs.map(function(e) { return e(); }); |
- this.pendingStubs = void 0; |
-} |
-''')); |
- |
- if (updates.length == 1) { |
- return prettyPrintJs(updates.single); |
- } else { |
- return prettyPrintJs(js.statement('{#}', [updates])); |
- } |
- } |
- |
- jsAst.Expression invokeDefineClass(ClassElementX cls) { |
- String name = namer.className(cls); |
- var descriptor = js('Object.create(null)'); |
- return js( |
- r''' |
-(new Function( |
- "$collectedClasses", "$desc", |
- this.defineClass(#name, #computeFields) +"\n;return " + #name))( |
- {#name: [,#descriptor]})''', |
- {'name': js.string(name), |
- 'computeFields': js.stringArray(computeFields(cls)), |
- 'descriptor': descriptor}); |
- } |
- |
- jsAst.Node computeMethodUpdateJs(Element element) { |
- Method member = new ProgramBuilder(compiler, namer, emitter) |
- .buildMethodHackForIncrementalCompilation(element); |
- if (member == null) { |
- compiler.internalError(element, '${element.runtimeType}'); |
- } |
- ClassBuilder builder = new ClassBuilder(element, namer); |
- containerBuilder.addMemberMethod(member, builder); |
- jsAst.Node partialDescriptor = |
- builder.toObjectInitializer(emitClassDescriptor: false); |
- |
- String name = member.name; |
- jsAst.Node function = member.code; |
- bool isStatic = !element.isInstanceMember; |
- |
- /// Either a global object (non-instance members) or a prototype (instance |
- /// members). |
- jsAst.Node holder; |
- |
- if (element.isInstanceMember) { |
- holder = emitter.prototypeAccess(element.enclosingClass); |
- } else { |
- holder = js('#', namer.globalObjectFor(element)); |
- } |
- |
- jsAst.Expression globalFunctionsAccess = |
- emitter.generateEmbeddedGlobalAccess(embeddedNames.GLOBAL_FUNCTIONS); |
- |
- return js.statement( |
- r'this.addMethod(#, #, #, #, #)', |
- [partialDescriptor, js.string(name), holder, |
- new jsAst.LiteralBool(isStatic), globalFunctionsAccess]); |
- } |
- |
- List<jsAst.Statement> computeFieldUpdateJs(FieldElementX element) { |
- if (element.isInstanceMember) { |
- // Any initializers are inlined in factory methods, and the field is |
- // declared by adding its class to [_classesWithSchemaChanges]. |
- return const <jsAst.Statement>[]; |
- } |
- // A static (or top-level) field. |
- if (backend.constants.lazyStatics.contains(element)) { |
- full.Emitter fullEmitter = emitter.emitter; |
- jsAst.Expression init = |
- fullEmitter.buildLazilyInitializedStaticField( |
- element, isolateProperties: namer.staticStateHolder); |
- if (init == null) { |
- throw new StateError("Initializer optimized away for $element"); |
- } |
- return <jsAst.Statement>[init.toStatement()]; |
- } else { |
- // TODO(ahe): When a field is referenced it is enqueued. If the field has |
- // no initializer, it will not have any associated code, so it will |
- // appear as if it was newly enqueued. |
- if (element.initializer == null) { |
- return const <jsAst.Statement>[]; |
- } else { |
- throw new StateError("Don't know how to compile $element"); |
- } |
- } |
- } |
- |
- String prettyPrintJs(jsAst.Node node) { |
- jsAst.JavaScriptPrintingOptions options = |
- new jsAst.JavaScriptPrintingOptions(); |
- jsAst.JavaScriptPrintingContext context = |
- new jsAst.Dart2JSJavaScriptPrintingContext(compiler, null); |
- jsAst.Printer printer = new jsAst.Printer(options, context); |
- printer.blockOutWithoutBraces(node); |
- return context.outBuffer.getText(); |
- } |
- |
- String callNameFor(FunctionElement element) { |
- // TODO(ahe): Call a method in the compiler to obtain this name. |
- String callPrefix = namer.callPrefix; |
- int parameterCount = element.functionSignature.parameterCount; |
- return '$callPrefix\$$parameterCount'; |
- } |
- |
- List<String> computeFields(ClassElement cls) { |
- return new EmitterHelper(compiler).computeFields(cls); |
- } |
- |
- void _ensureAllNeededEntitiesComputed() { |
- if (_hasComputedNeeds) return; |
- emitter.computeAllNeededEntities(); |
- _hasComputedNeeds = true; |
- } |
-} |
- |
-/// Represents an update (aka patch) of [before] to [after]. We use the word |
-/// "update" to avoid confusion with the compiler feature of "patch" methods. |
-abstract class Update { |
- final Compiler compiler; |
- |
- PartialElement get before; |
- |
- PartialElement get after; |
- |
- Update(this.compiler); |
- |
- /// Applies the update to [before] and returns that element. |
- Element apply(); |
- |
- bool get isRemoval => false; |
- |
- /// Called before any patches are applied to capture any state that is needed |
- /// later. |
- void captureState() { |
- } |
-} |
- |
-/// Represents an update of a function element. |
-class FunctionUpdate extends Update with ReuseFunction { |
- final PartialFunctionElement before; |
- |
- final PartialFunctionElement after; |
- |
- FunctionUpdate(Compiler compiler, this.before, this.after) |
- : super(compiler); |
- |
- PartialFunctionElement apply() { |
- patchElement(); |
- reuseElement(); |
- return before; |
- } |
- |
- /// Destructively change the tokens in [before] to match those of [after]. |
- void patchElement() { |
- before.beginToken = after.beginToken; |
- before.endToken = after.endToken; |
- before.getOrSet = after.getOrSet; |
- } |
-} |
- |
-abstract class ReuseFunction { |
- Compiler get compiler; |
- |
- PartialFunctionElement get before; |
- |
- /// Reset various caches and remove this element from the compiler's internal |
- /// state. |
- void reuseElement() { |
- compiler.forgetElement(before); |
- before.reuseElement(); |
- } |
-} |
- |
-abstract class RemovalUpdate extends Update { |
- ElementX get element; |
- |
- RemovalUpdate(Compiler compiler) |
- : super(compiler); |
- |
- bool get isRemoval => true; |
- |
- void writeUpdateJsOn(List<jsAst.Statement> updates); |
- |
- void removeFromEnclosing() { |
- // TODO(ahe): Need to recompute duplicated elements logic again. Simplest |
- // solution is probably to remove all elements from enclosing scope and add |
- // them back. |
- if (element.isTopLevel) { |
- removeFromLibrary(element.library); |
- } else { |
- removeFromEnclosingClass(element.enclosingClass); |
- } |
- } |
- |
- void removeFromEnclosingClass(PartialClassElement cls) { |
- cls.localMembersCache = null; |
- cls.localMembersReversed = cls.localMembersReversed.copyWithout(element); |
- cls.localScope.contents.remove(element.name); |
- } |
- |
- void removeFromLibrary(LibraryElementX library) { |
- library.localMembers = library.localMembers.copyWithout(element); |
- library.localScope.contents.remove(element.name); |
- } |
-} |
- |
-class RemovedFunctionUpdate extends RemovalUpdate |
- with JsFeatures, ReuseFunction { |
- final PartialFunctionElement element; |
- |
- /// Name of property to remove using JavaScript "delete". Null for |
- /// non-instance methods. |
- String name; |
- |
- /// For instance methods, access to class object. Otherwise, access to the |
- /// method itself. |
- jsAst.Node elementAccess; |
- |
- bool wasStateCaptured = false; |
- |
- RemovedFunctionUpdate(Compiler compiler, this.element) |
- : super(compiler); |
- |
- PartialFunctionElement get before => element; |
- |
- PartialFunctionElement get after => null; |
- |
- void captureState() { |
- if (wasStateCaptured) throw "captureState was called twice."; |
- wasStateCaptured = true; |
- |
- if (element.isInstanceMember) { |
- elementAccess = emitter.constructorAccess(element.enclosingClass); |
- name = namer.instanceMethodName(element); |
- } else { |
- elementAccess = emitter.staticFunctionAccess(element); |
- } |
- } |
- |
- PartialFunctionElement apply() { |
- if (!wasStateCaptured) throw "captureState must be called before apply."; |
- removeFromEnclosing(); |
- reuseElement(); |
- return null; |
- } |
- |
- void writeUpdateJsOn(List<jsAst.Statement> updates) { |
- if (elementAccess == null) { |
- compiler.internalError( |
- element, 'No elementAccess for ${element.runtimeType}'); |
- } |
- if (element.isInstanceMember) { |
- if (name == null) { |
- compiler.internalError(element, 'No name for ${element.runtimeType}'); |
- } |
- updates.add( |
- js.statement('delete #.prototype.#', [elementAccess, name])); |
- } else { |
- updates.add(js.statement('delete #', [elementAccess])); |
- } |
- } |
-} |
- |
-class RemovedClassUpdate extends RemovalUpdate with JsFeatures { |
- final PartialClassElement element; |
- |
- bool wasStateCaptured = false; |
- |
- final List<jsAst.Node> accessToStatics = <jsAst.Node>[]; |
- |
- RemovedClassUpdate(Compiler compiler, this.element) |
- : super(compiler); |
- |
- PartialClassElement get before => element; |
- |
- PartialClassElement get after => null; |
- |
- void captureState() { |
- if (wasStateCaptured) throw "captureState was called twice."; |
- wasStateCaptured = true; |
- accessToStatics.add(emitter.constructorAccess(element)); |
- |
- element.forEachLocalMember((ElementX member) { |
- if (!member.isInstanceMember) { |
- accessToStatics.add(emitter.staticFunctionAccess(member)); |
- } |
- }); |
- } |
- |
- PartialClassElement apply() { |
- if (!wasStateCaptured) { |
- throw new StateError("captureState must be called before apply."); |
- } |
- |
- removeFromEnclosing(); |
- |
- element.forEachLocalMember((ElementX member) { |
- compiler.forgetElement(member); |
- member.reuseElement(); |
- }); |
- |
- compiler.forgetElement(element); |
- element.reuseElement(); |
- |
- return null; |
- } |
- |
- void writeUpdateJsOn(List<jsAst.Statement> updates) { |
- if (accessToStatics.isEmpty) { |
- throw |
- new StateError("captureState must be called before writeUpdateJsOn."); |
- } |
- |
- for (jsAst.Node access in accessToStatics) { |
- updates.add(js.statement('delete #', [access])); |
- } |
- } |
-} |
- |
-class RemovedFieldUpdate extends RemovalUpdate with JsFeatures { |
- final FieldElementX element; |
- |
- bool wasStateCaptured = false; |
- |
- jsAst.Node prototypeAccess; |
- |
- String getterName; |
- |
- String setterName; |
- |
- RemovedFieldUpdate(Compiler compiler, this.element) |
- : super(compiler); |
- |
- PartialFieldList get before => element.declarationSite; |
- |
- PartialFieldList get after => null; |
- |
- void captureState() { |
- if (wasStateCaptured) throw "captureState was called twice."; |
- wasStateCaptured = true; |
- |
- prototypeAccess = emitter.prototypeAccess(element.enclosingClass); |
- getterName = namer.getterForElement(element); |
- setterName = namer.setterForElement(element); |
- } |
- |
- FieldElementX apply() { |
- if (!wasStateCaptured) { |
- throw new StateError("captureState must be called before apply."); |
- } |
- |
- removeFromEnclosing(); |
- |
- return element; |
- } |
- |
- void writeUpdateJsOn(List<jsAst.Statement> updates) { |
- if (!wasStateCaptured) { |
- throw new StateError( |
- "captureState must be called before writeUpdateJsOn."); |
- } |
- |
- updates.add( |
- js.statement('delete #.#', [prototypeAccess, getterName])); |
- updates.add( |
- js.statement('delete #.#', [prototypeAccess, setterName])); |
- } |
-} |
- |
-class AddedFunctionUpdate extends Update with JsFeatures { |
- final PartialFunctionElement element; |
- |
- final /* ScopeContainerElement */ container; |
- |
- AddedFunctionUpdate(Compiler compiler, this.element, this.container) |
- : super(compiler) { |
- if (container == null) { |
- throw "container is null"; |
- } |
- } |
- |
- PartialFunctionElement get before => null; |
- |
- PartialFunctionElement get after => element; |
- |
- PartialFunctionElement apply() { |
- Element enclosing = container; |
- if (enclosing.isLibrary) { |
- // TODO(ahe): Reuse compilation unit of element instead? |
- enclosing = enclosing.compilationUnit; |
- } |
- PartialFunctionElement copy = element.copyWithEnclosing(enclosing); |
- NO_WARN(container).addMember(copy, compiler); |
- return copy; |
- } |
-} |
- |
-class AddedClassUpdate extends Update with JsFeatures { |
- final PartialClassElement element; |
- |
- final LibraryElementX library; |
- |
- AddedClassUpdate(Compiler compiler, this.element, this.library) |
- : super(compiler); |
- |
- PartialClassElement get before => null; |
- |
- PartialClassElement get after => element; |
- |
- PartialClassElement apply() { |
- // TODO(ahe): Reuse compilation unit of element instead? |
- CompilationUnitElementX compilationUnit = library.compilationUnit; |
- PartialClassElement copy = element.copyWithEnclosing(compilationUnit); |
- compilationUnit.addMember(copy, compiler); |
- return copy; |
- } |
-} |
- |
-class AddedFieldUpdate extends Update with JsFeatures { |
- final FieldElementX element; |
- |
- final ScopeContainerElement container; |
- |
- AddedFieldUpdate(Compiler compiler, this.element, this.container) |
- : super(compiler); |
- |
- PartialFieldList get before => null; |
- |
- PartialFieldList get after => element.declarationSite; |
- |
- FieldElementX apply() { |
- Element enclosing = container; |
- if (enclosing.isLibrary) { |
- // TODO(ahe): Reuse compilation unit of element instead? |
- enclosing = enclosing.compilationUnit; |
- } |
- FieldElementX copy = element.copyWithEnclosing(enclosing); |
- NO_WARN(container).addMember(copy, compiler); |
- return copy; |
- } |
-} |
- |
- |
-class ClassUpdate extends Update with JsFeatures { |
- final PartialClassElement before; |
- |
- final PartialClassElement after; |
- |
- ClassUpdate(Compiler compiler, this.before, this.after) |
- : super(compiler); |
- |
- PartialClassElement apply() { |
- patchElement(); |
- reuseElement(); |
- return before; |
- } |
- |
- /// Destructively change the tokens in [before] to match those of [after]. |
- void patchElement() { |
- before.cachedNode = after.cachedNode; |
- before.beginToken = after.beginToken; |
- before.endToken = after.endToken; |
- } |
- |
- void reuseElement() { |
- before.supertype = null; |
- before.interfaces = null; |
- before.nativeTagInfo = null; |
- before.supertypeLoadState = STATE_NOT_STARTED; |
- before.resolutionState = STATE_NOT_STARTED; |
- before.isProxy = false; |
- before.hasIncompleteHierarchy = false; |
- before.backendMembers = const Link<Element>(); |
- before.allSupertypesAndSelf = null; |
- } |
-} |
- |
-/// Returns all qualified names in [element] with less than four identifiers. A |
-/// qualified name is an identifier followed by a sequence of dots and |
-/// identifiers, for example, "x", and "x.y.z". But not "x.y.z.w" ("w" is the |
-/// fourth identifier). |
-/// |
-/// The longest possible name that can be resolved is three identifiers, for |
-/// example, "prefix.MyClass.staticMethod". Since four or more identifiers |
-/// cannot resolve to anything statically, they're not included in the returned |
-/// value of this method. |
-Set<String> qualifiedNamesIn(PartialElement element) { |
- Token beginToken = element.beginToken; |
- Token endToken = element.endToken; |
- Token token = beginToken; |
- if (element is PartialClassElement) { |
- ClassNode node = element.cachedNode; |
- if (node != null) { |
- NodeList body = node.body; |
- if (body != null) { |
- endToken = body.beginToken; |
- } |
- } |
- } |
- Set<String> names = new Set<String>(); |
- do { |
- if (token.isIdentifier()) { |
- String name = token.value; |
- // [name] is a single "identifier". |
- names.add(name); |
- if (identical('.', token.next.stringValue) && |
- token.next.next.isIdentifier()) { |
- token = token.next.next; |
- name += '.${token.value}'; |
- // [name] is "idenfifier.idenfifier". |
- names.add(name); |
- |
- if (identical('.', token.next.stringValue) && |
- token.next.next.isIdentifier()) { |
- token = token.next.next; |
- name += '.${token.value}'; |
- // [name] is "idenfifier.idenfifier.idenfifier". |
- names.add(name); |
- |
- while (identical('.', token.next.stringValue) && |
- token.next.next.isIdentifier()) { |
- // Skip remaining identifiers, they cannot statically resolve to |
- // anything, and must be dynamic sends. |
- token = token.next.next; |
- } |
- } |
- } |
- } |
- token = token.next; |
- } while (token.kind != EOF_TOKEN && token != endToken); |
- return names; |
-} |
- |
-/// Returns true if one of the qualified names in names (as computed by |
-/// [qualifiedNamesIn]) could be a static reference to [element]. |
-bool canNamesResolveStaticallyTo( |
- Set<String> names, |
- Element element, |
- /* ScopeContainerElement */ container) { |
- if (names.contains(element.name)) return true; |
- if (container != null && container.isClass) { |
- // [names] contains C.m, where C is the name of [container], and m is the |
- // name of [element]. |
- if (names.contains("${container.name}.${element.name}")) return true; |
- } |
- // TODO(ahe): Check for prefixes as well. |
- return false; |
-} |
- |
-DeclarationSite declarationSite(Element element) { |
- return element is ElementX ? element.declarationSite : null; |
-} |
- |
-abstract class JsFeatures { |
- Compiler get compiler; |
- |
- JavaScriptBackend get backend => compiler.backend; |
- |
- Namer get namer => backend.namer; |
- |
- CodeEmitterTask get emitter => backend.emitter; |
- |
- ContainerBuilder get containerBuilder { |
- full.Emitter fullEmitter = emitter.emitter; |
- return fullEmitter.containerBuilder; |
- } |
- |
- EnqueueTask get enqueuer => compiler.enqueuer; |
-} |
- |
-class EmitterHelper extends JsFeatures { |
- final Compiler compiler; |
- |
- EmitterHelper(this.compiler); |
- |
- ClassEmitter get classEmitter { |
- full.Emitter fullEmitter = emitter.emitter; |
- return fullEmitter.classEmitter; |
- } |
- |
- List<String> computeFields(ClassElement classElement) { |
- Class cls = new ProgramBuilder(compiler, namer, emitter) |
- .buildFieldsHackForIncrementalCompilation(classElement); |
- // TODO(ahe): Rewrite for new emitter. |
- ClassBuilder builder = new ClassBuilder(classElement, namer); |
- classEmitter.emitFields(cls, builder); |
- return builder.fields; |
- } |
-} |
- |
-// TODO(ahe): Remove this method. |
-NO_WARN(x) => x; |