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

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

Issue 1879373004: Implement modular compilation (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 8 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/src/compiler/ast_builder.dart ('k') | lib/src/compiler/command.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/compiler/code_generator.dart
diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/compiler/code_generator.dart
similarity index 78%
rename from lib/src/codegen/js_codegen.dart
rename to lib/src/compiler/code_generator.dart
index 35ebdb741cea72b4a74b53caea4c525f115fad63..ca56b1f7938fadaa362d2916ea0bc13415cc0d81 100644
--- a/lib/src/codegen/js_codegen.dart
+++ b/lib/src/compiler/code_generator.dart
@@ -2,76 +2,76 @@
// 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.
-import 'dart:collection' show HashSet, HashMap, SplayTreeSet;
+import 'dart:collection' show HashMap, HashSet;
import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
-import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/dart/ast/token.dart' show Token, TokenType;
import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/dart/element/visitor.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/ast/ast.dart' hide ConstantEvaluator;
-import 'package:analyzer/src/generated/constant.dart';
+
//TODO(leafp): Remove deprecated dependency
//ignore: DEPRECATED_MEMBER_USE
import 'package:analyzer/src/generated/element.dart'
- show DynamicElementImpl, DynamicTypeImpl, LocalVariableElementImpl;
+ show DynamicTypeImpl, LocalVariableElementImpl;
import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
-import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
-import 'package:analyzer/src/dart/ast/token.dart'
- show StringToken, Token, TokenType;
+import 'package:analyzer/src/generated/resolver.dart'
+ show TypeProvider, NamespaceBuilder;
+import 'package:analyzer/src/dart/ast/token.dart' show StringToken;
import 'package:analyzer/src/generated/type_system.dart'
show StrongTypeSystemImpl;
-import 'package:analyzer/src/task/strong/info.dart';
-
-import 'ast_builder.dart' show AstBuilder;
-import 'reify_coercions.dart' show CoercionReifier, Tuple2;
-
-import '../js/js_ast.dart' as JS;
-import '../js/js_ast.dart' show js;
+import 'package:analyzer/src/summary/summarize_elements.dart'
+ show PackageBundleAssembler;
+import 'package:analyzer/src/task/strong/info.dart' show DynamicInvoke;
+import '../js_ast/js_ast.dart' as JS;
+import '../js_ast/js_ast.dart' show js;
import '../closure/closure_annotator.dart' show ClosureAnnotator;
-import '../compiler.dart'
- show AbstractCompiler, corelibOrder, getCorelibModuleName;
-import '../options.dart' show CodegenOptions;
-import '../utils.dart';
-import 'js_field_storage.dart';
+import 'ast_builder.dart' show AstBuilder;
+import 'compiler.dart'
+ show BuildUnit, CompilerOptions, JSModuleFile, ModuleFormat;
+import 'element_helpers.dart';
+import 'element_loader.dart' show ElementLoader;
+import 'extension_types.dart' show ExtensionTypeSet;
+import 'js_field_storage.dart' show findFieldsNeedingStorage;
import 'js_interop.dart';
import 'js_names.dart' as JS;
import 'js_metalet.dart' as JS;
-import 'js_module_item_order.dart';
-import 'js_names.dart';
-import 'js_printer.dart' show writeJsLibrary;
-import 'js_typeref_codegen.dart';
-import 'module_builder.dart';
-import 'nullable_type_inference.dart';
-import 'side_effect_analysis.dart';
-
-// Various dynamic helpers we call.
-// If renaming these, make sure to check other places like the
-// _runtime.js file and comments.
-// TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can
-// import and generate calls to, rather than dart_runtime.js
-const DPUT = 'dput';
-const DLOAD = 'dload';
-const DINDEX = 'dindex';
-const DSETINDEX = 'dsetindex';
-const DCALL = 'dcall';
-const DSEND = 'dsend';
-
-class JSCodegenVisitor extends GeneralizingAstVisitor
+import 'js_typeref_codegen.dart' show JsTypeRefCodegen;
+import 'module_builder.dart'
+ show LegacyModuleBuilder, NodeModuleBuilder, pathToJSIdentifier;
+import 'nullable_type_inference.dart' show NullableTypeInference;
+import 'reify_coercions.dart' show CoercionReifier;
+import 'side_effect_analysis.dart' show ConstFieldVisitor, isStateless;
+import 'source_map_printer.dart' show SourceMapPrintingContext;
+import 'package:source_maps/source_maps.dart';
+
+class CodeGenerator extends GeneralizingAstVisitor
with ClosureAnnotator, JsTypeRefCodegen, NullableTypeInference {
- final AbstractCompiler compiler;
- final CodegenOptions options;
- final LibraryElement currentLibrary;
- final StrongTypeSystemImpl rules;
+ final AnalysisContext context;
+ final CompilerOptions options;
+ final rules = new StrongTypeSystemImpl();
+
+ /// The set of libraries we are currently compiling, and the temporaries used
+ /// to refer to them.
+ ///
+ /// We sometimes special case codegen for a single library, as it simplifies
+ /// name scoping requirements.
+ final _libraries = new Map<LibraryElement, JS.Identifier>();
+
+ /// Imported libraries, and the temporaries used to refer to them.
+ final _imports = new Map<LibraryElement, JS.TemporaryId>();
+
+ /// The list of output module items, in the order they need to be emitted in.
+ final _moduleItems = <JS.ModuleItem>[];
/// The global extension type table.
final ExtensionTypeSet _extensionTypes;
/// Information that is precomputed for this library, indicates which fields
/// need storage slots.
- final HashSet<FieldElement> _fieldsNeedingStorage;
+ HashSet<FieldElement> _fieldsNeedingStorage;
/// The variable for the target of the current `..` cascade expression.
///
@@ -85,212 +85,324 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
/// In an async* function, this represents the stream controller parameter.
JS.TemporaryId _asyncStarController;
- /// Imported libraries, and the temporaries used to refer to them.
- final _imports = new Map<LibraryElement, JS.TemporaryId>();
- final _exports = <String, String>{};
- final _properties = <FunctionDeclaration>[];
- final _privateNames = new HashMap<String, JS.TemporaryId>();
- final _moduleItems = <JS.Statement>[];
+ final _privateNames =
+ new HashMap<LibraryElement, HashMap<String, JS.TemporaryId>>();
final _temps = new HashMap<Element, JS.TemporaryId>();
- final _qualifiedIds = new List<Tuple2<Element, JS.MaybeQualifiedId>>();
- /// The name for the library's exports inside itself.
- /// `exports` was chosen as the most similar to ES module patterns.
final _dartxVar = new JS.Identifier('dartx');
- final _exportsVar = new JS.TemporaryId('exports');
final _runtimeLibVar = new JS.Identifier('dart');
final namedArgumentTemp = new JS.TemporaryId('opts');
- final TypeProvider _types;
+ final _hasDeferredSupertype = new HashSet<ClassElement>();
- ConstFieldVisitor _constField;
+ /// The type provider from the current Analysis [context].
+ final TypeProvider types;
- ModuleItemLoadOrder _loader;
+ final LibraryElement dartCoreLibrary;
+ final LibraryElement dartJSLibrary;
- /// _interceptors.JSArray<E>, used for List literals.
- ClassElement _jsArray;
+ /// The dart:async `StreamIterator<>` type.
+ final InterfaceType _asyncStreamIterator;
+
+ /// The dart:_interceptors JSArray element.
+ final ClassElement _jsArray;
+
+ ConstFieldVisitor _constField;
/// The current function body being compiled.
FunctionBody _currentFunction;
- /// The default value of the module object. See [visitLibraryDirective].
- String _jsModuleValue;
+ /// Helper class for emitting elements in the proper order to allow
+ /// JS to load the module.
+ ElementLoader _loader;
- bool _isDartRuntime;
+ BuildUnit _buildUnit;
- JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary,
- this._extensionTypes, this._fieldsNeedingStorage)
- : compiler = compiler,
- options = compiler.options.codegenOptions,
- _types = compiler.context.typeProvider {
- _loader = new ModuleItemLoadOrder(_emitModuleItem);
+ CodeGenerator(AnalysisContext c, this.options, this._extensionTypes)
+ : context = c,
+ types = c.typeProvider,
+ _asyncStreamIterator =
+ _getLibrary(c, 'dart:async').getType('StreamIterator').type,
+ _jsArray = _getLibrary(c, 'dart:_interceptors').getType('JSArray'),
+ dartCoreLibrary = _getLibrary(c, 'dart:core'),
+ dartJSLibrary = _getLibrary(c, 'dart:js');
- var context = compiler.context;
- var src = context.sourceFactory.forUri('dart:_interceptors');
- var interceptors = context.computeLibraryElement(src);
- _jsArray = interceptors.getType('JSArray');
- _isDartRuntime = currentLibrary.source.uri.toString() == 'dart:_runtime';
- }
+ LibraryElement get currentLibrary => _loader.currentElement.library;
- TypeProvider get types => _types;
+ /// The main entry point to JavaScript code generation.
+ ///
+ /// Takes the metadata for the build unit, as well as resolved trees and
+ /// errors, and computes the output module code and optionally the source map.
+ JSModuleFile compile(BuildUnit unit, List<CompilationUnit> compilationUnits,
+ List<String> errors) {
+ _buildUnit = unit;
- JS.Program emitLibrary(List<CompilationUnit> units) {
- // Modify the AST to make coercions explicit.
- units = new CoercionReifier().reify(units);
+ var jsTree = _emitModule(compilationUnits);
+ var codeAndSourceMap = _writeJSText(unit, jsTree);
- units.last.directives.forEach(_visit);
+ List<int> summary;
+ if (options.summarizeApi) {
+ var assembler = new PackageBundleAssembler();
+ compilationUnits
+ .map((u) => u.element.library)
+ .toSet()
+ .forEach(assembler.serializeLibraryElement);
+ summary = assembler.assemble().toBuffer();
+ }
- // Rather than directly visit declarations, we instead use [_loader] to
- // visit them. It has the ability to sort elements on demand, so
- // dependencies between top level items are handled with a minimal
- // reordering of the user's input code. The loader will call back into
- // this visitor via [_emitModuleItem] when it's ready to visit the item
- // for real.
- _loader.collectElements(currentLibrary, units);
+ return new JSModuleFile(
+ unit.name, errors, codeAndSourceMap.e0, codeAndSourceMap.e1, summary);
+ }
- // TODO(jmesserly): ideally we could do this at a smaller granularity.
- // We'll need to be consistent about when we're generating functions, and
- // only run this on the outermost function.
- inferNullableTypesInLibrary(units);
+ Tuple2<String, Map> _writeJSText(BuildUnit unit, JS.Program jsTree) {
+ var opts = new JS.JavaScriptPrintingOptions(
+ emitTypes: options.closure,
+ allowKeywordsInProperties: true,
+ allowSingleLineIfStatements: true);
+ JS.SimpleJavaScriptPrintingContext printer;
+ SourceMapBuilder sourceMap;
+ if (options.sourceMap) {
+ var sourceMapContext = new SourceMapPrintingContext();
+ sourceMap = sourceMapContext.sourceMap;
+ printer = sourceMapContext;
+ } else {
+ printer = new JS.SimpleJavaScriptPrintingContext();
+ }
- _constField = new ConstFieldVisitor(types, currentLibrary.source);
+ jsTree.accept(new JS.Printer(opts, printer,
+ localNamer: new JS.TemporaryNamer(jsTree)));
- for (var unit in units) {
- for (var decl in unit.declarations) {
- if (decl is TopLevelVariableDeclaration) {
- visitTopLevelVariableDeclaration(decl);
- } else {
- _loader.loadDeclaration(decl, decl.element);
- }
- }
+ if (options.sourceMap && options.sourceMapComment) {
+ printer.emit('\n//# sourceMappingURL=${unit.name}.js.map\n');
}
- // Flush any unwritten fields/properties.
- _flushLibraryProperties(_moduleItems);
+ return new Tuple2(printer.getText(), sourceMap?.build(unit.name + '.js'));
+ }
- // Mark all qualified names as qualified or not, depending on if they need
- // to be loaded lazily or not.
- for (var elementIdPairs in _qualifiedIds) {
- var element = elementIdPairs.e0;
- var id = elementIdPairs.e1;
- id.setQualified(!_loader.isLoaded(element));
+ JS.Program _emitModule(List<CompilationUnit> compilationUnits) {
+ if (_moduleItems.isNotEmpty) {
+ throw new StateError('Can only call emitModule once.');
}
- var moduleBuilder = new ModuleBuilder(options.moduleFormat);
+ _fieldsNeedingStorage = findFieldsNeedingStorage(
+ compilationUnits.map((u) => u.element), _extensionTypes);
- _exports.forEach(moduleBuilder.addExport);
+ // Transform the AST to make coercions explicit.
+ compilationUnits = CoercionReifier.reify(compilationUnits);
- var currentModuleName = compiler.getModuleName(currentLibrary.source.uri);
+ // Initialize our library variables.
var items = <JS.ModuleItem>[];
- if (!_isDartRuntime) {
- if (currentLibrary.source.isInSystemLibrary) {
- // Force the import order of runtime libs.
- // TODO(ochafik): Reduce this to a minimum.
- for (var libUri in corelibOrder.reversed) {
- var moduleName = compiler.getModuleName(libUri);
- if (moduleName == currentModuleName) continue;
- moduleBuilder.addImport(moduleName, null);
- }
+ for (var unit in compilationUnits) {
+ var library = unit.element.library;
+ if (unit.element != library.definingCompilationUnit) continue;
+
+ var libraryTemp = _isDartRuntime(library)
+ ? _runtimeLibVar
+ : new JS.TemporaryId(jsLibraryName(library));
+ _libraries[library] = libraryTemp;
+ items.add(new JS.ExportDeclaration(
+ js.call('const # = Object.create(null)', [libraryTemp])));
+
+ // dart:_runtime has a magic module that holds extenstion method symbols.
+ // TODO(jmesserly): find a cleaner design for this.
+ if (_isDartRuntime(library)) {
+ items.add(new JS.ExportDeclaration(
+ js.call('const # = Object.create(null)', [_dartxVar])));
+ }
+ }
+
+ // Collect all Element -> Node mappings, in case we need to forward declare
+ // any nodes.
+ var nodes = new HashMap<Element, AstNode>.identity();
+ compilationUnits.map(_collectElements).forEach(nodes.addAll);
+ _loader = new ElementLoader(_emitModuleItem, nodes);
+
+ // Add implicit dart:core dependency so it is first.
+ emitLibraryName(dartCoreLibrary);
+
+ //
+ // Visit each compilation unit and emit its code.
+ //
+ // NOTE: declarations are not necessarily emitted in this order.
+ // Order will be changed as needed so the resulting code can execute.
+ // This is done by forward declaring items.
+ compilationUnits.forEach(visitCompilationUnit);
+
+ // Declare imports
+ _finishImports(items);
+
+ // Add the module's code (produced by visiting compilation units, above)
+ _copyAndFlattenBlocks(items, _moduleItems);
+
+ // Build the module.
+ var module = new JS.Program(items, name: _buildUnit.name);
+
+ // Optional: lower module format. Otherwise just return it.
+ switch (options.moduleFormat) {
+ case ModuleFormat.legacy:
+ return new LegacyModuleBuilder().build(module);
+ case ModuleFormat.node:
+ return new NodeModuleBuilder().build(module);
+ case ModuleFormat.es6:
+ return module;
+ }
+ }
+
+ /// Flattens blocks in [items] to a single list.
+ ///
+ /// This will not flatten blocks that are marked as being scopes.
+ void _copyAndFlattenBlocks(
+ List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) {
+ for (var item in items) {
+ if (item is JS.Block && !item.isScope) {
+ _copyAndFlattenBlocks(result, item.statements);
+ } else {
+ result.add(item);
}
- moduleBuilder.addImport('dart/_runtime', _runtimeLibVar);
+ }
+ }
- var dartxImport =
- js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]);
- items.add(dartxImport);
+ String _libraryToModule(LibraryElement library) {
+ assert(!_libraries.containsKey(library));
+ var source = library.source;
+ // TODO(jmesserly): we need to split out HTML.
+ if (source.uri.scheme == 'dart') {
+ return 'dart_sdk';
+ }
+ var moduleName = _buildUnit.libraryToModule(source);
+ if (moduleName == null) {
+ throw new StateError('Could not find module containing "$library".');
}
- items.addAll(_moduleItems);
+ return moduleName;
+ }
+
+ void _finishImports(List<JS.ModuleItem> items) {
+ var modules = new Map<String, List<LibraryElement>>();
- _imports.forEach((LibraryElement lib, JS.TemporaryId temp) {
- moduleBuilder.addImport(compiler.getModuleName(lib.source.uri), temp,
- isLazy: _isDartRuntime || !_loader.libraryIsLoaded(lib));
+ for (var import in _imports.keys) {
+ modules.putIfAbsent(_libraryToModule(import), () => []).add(import);
+ }
+
+ String coreModuleName;
+ if (!_libraries.containsKey(dartCoreLibrary)) {
+ coreModuleName = _libraryToModule(dartCoreLibrary);
+ }
+ modules.forEach((module, libraries) {
+ // Generate import directives.
+ //
+ // Our import variables are temps and can get renamed. Since our renaming
+ // is integrated into js_ast, it is aware of this possibility and will
+ // generate an "as" if needed. For example:
+ //
+ // import {foo} from 'foo'; // if no rename needed
+ // import {foo as foo$} from 'foo'; // if rename was needed
+ //
+ var imports =
+ libraries.map((l) => new JS.NameSpecifier(_imports[l])).toList();
+ if (module == coreModuleName) {
+ imports.add(new JS.NameSpecifier(_runtimeLibVar));
+ imports.add(new JS.NameSpecifier(_dartxVar));
+ }
+ items.add(new JS.ImportDeclaration(
+ namedImports: imports, from: js.string(module, "'")));
});
+ }
- // TODO(jmesserly): scriptTag support.
- // Enable this if we know we're targetting command line environment?
- // It doesn't work in browser.
- // var jsBin = compiler.options.runnerOptions.v8Binary;
- // String scriptTag = null;
- // if (library.library.scriptTag != null) scriptTag = '/usr/bin/env $jsBin';
- return moduleBuilder.build(
- currentModuleName, _jsModuleValue, _exportsVar, items);
+ /// Collect toplevel elements and nodes we need to emit, and returns
+ /// an ordered map of these.
+ static Map<Element, AstNode> _collectElements(CompilationUnit unit) {
+ var map = <Element, AstNode>{};
+ for (var declaration in unit.declarations) {
+ if (declaration is TopLevelVariableDeclaration) {
+ for (var field in declaration.variables.variables) {
+ map[field.element] = field;
+ }
+ } else {
+ map[declaration.element] = declaration;
+ }
+ }
+ return map;
}
void _emitModuleItem(AstNode node) {
- // Attempt to group adjacent properties.
- if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems);
+ // TODO(jmesserly): ideally we could do this at a smaller granularity.
+ // We'll need to be consistent about when we're generating functions, and
+ // only run this on the outermost function.
+ inferNullableTypes(node);
var code = _visit(node);
if (code != null) _moduleItems.add(code);
}
@override
- void visitLibraryDirective(LibraryDirective node) {
- assert(_jsModuleValue == null);
+ void visitCompilationUnit(CompilationUnit unit) {
+ _constField = new ConstFieldVisitor(types, unit.element.source);
- var jsName = findAnnotation(node.element, isJSAnnotation);
- _jsModuleValue =
- getConstantField(jsName, 'name', types.stringType)?.toStringValue();
+ for (var declaration in unit.declarations) {
+ var element = declaration.element;
+ if (element != null) {
+ _loader.emitDeclaration(element);
+ } else {
+ declaration.accept(this);
+ }
+ }
+ for (var directive in unit.directives) {
+ directive.accept(this);
+ }
}
@override
+ void visitLibraryDirective(LibraryDirective node) {}
+
+ @override
void visitImportDirective(ImportDirective node) {
- // Nothing to do yet, but we'll want to convert this to an ES6 import once
- // we have support for modules.
+ // We don't handle imports here.
+ //
+ // Instead, we collect imports whenever we need to generate a reference
+ // to another library. This has the effect of collecting the actually used
+ // imports.
+ //
+ // TODO(jmesserly): if this is a prefixed import, consider adding the prefix
+ // as an alias?
}
@override
void visitPartDirective(PartDirective node) {}
+
@override
void visitPartOfDirective(PartOfDirective node) {}
@override
void visitExportDirective(ExportDirective node) {
- var exportName = emitLibraryName(node.uriElement);
+ ExportElement element = node.element;
+ var currentLibrary = element.library;
- var currentLibNames = currentLibrary.publicNamespace.definedNames;
+ var currentNames = currentLibrary.publicNamespace.definedNames;
+ var exportedNames =
+ new NamespaceBuilder().createExportNamespaceForDirective(element);
- var args = [_exportsVar, exportName];
- if (node.combinators.isNotEmpty) {
- var shownNames = <JS.Expression>[];
- var hiddenNames = <JS.Expression>[];
-
- var show = node.combinators.firstWhere((c) => c is ShowCombinator,
- orElse: () => null) as ShowCombinator;
- var hide = node.combinators.firstWhere((c) => c is HideCombinator,
- orElse: () => null) as HideCombinator;
- if (show != null) {
- shownNames.addAll(show.shownNames
- .map((i) => i.name)
- .where((s) => !currentLibNames.containsKey(s))
- .map((s) => js.string(s, "'")));
- }
- if (hide != null) {
- hiddenNames.addAll(hide.hiddenNames.map((i) => js.string(i.name, "'")));
- }
- args.add(new JS.ArrayInitializer(shownNames));
- args.add(new JS.ArrayInitializer(hiddenNames));
+ // TODO(jmesserly): we could collect all of the names for bulk re-export,
+ // but this is easier to implement for now.
+ void emitExport(Element export, {String suffix: ''}) {
+ var name = _emitTopLevelName(export, suffix: suffix);
+ _moduleItems.add(js.statement(
+ '#.# = #;', [emitLibraryName(currentLibrary), name.selector, name]));
}
- _moduleItems.add(js.statement('dart.export(#);', [args]));
- }
+ for (var export in exportedNames.definedNames.values) {
+ // Don't allow redefining names from this library.
+ if (currentNames.containsKey(export.name)) continue;
- JS.Identifier _initSymbol(JS.Identifier id) {
- var s =
- js.statement('const # = $_SYMBOL(#);', [id, js.string(id.name, "'")]);
- _moduleItems.add(s);
- return id;
- }
-
- // TODO(jmesserly): this is a temporary workaround for `Symbol` in core,
- // until we have better name tracking.
- String get _SYMBOL {
- var name = currentLibrary.name;
- if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol';
- return 'Symbol';
+ _loader.emitDeclaration(export);
+ if (export is ClassElement && export.typeParameters.isNotEmpty) {
+ // Export the generic name as well.
+ // TODO(jmesserly): revisit generic classes
+ emitExport(export, suffix: r'$');
+ }
+ emitExport(export);
+ }
}
- bool isPublic(String name) => !name.startsWith('_');
-
@override
visitAsExpression(AsExpression node) {
var from = getStaticType(node.expression);
@@ -304,7 +416,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
// All Dart number types map to a JS double.
if (_isNumberInJS(from) && _isNumberInJS(to)) {
// Make sure to check when converting to int.
- if (from != _types.intType && to == _types.intType) {
+ if (from != types.intType && to == types.intType) {
return js.call('dart.asInt(#)', [fromExpr]);
}
@@ -337,27 +449,30 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
String _jsTypeofName(DartType t) {
if (_isNumberInJS(t)) return 'number';
- if (t == _types.stringType) return 'string';
- if (t == _types.boolType) return 'boolean';
+ if (t == types.stringType) return 'string';
+ if (t == types.boolType) return 'boolean';
return null;
}
@override
visitFunctionTypeAlias(FunctionTypeAlias node) {
- var element = node.element;
- var type = element.type;
- var name = element.name;
+ FunctionTypeAliasElement element = node.element;
- var fnType = annotate(
- js.statement('const # = dart.typedef(#, () => #);', [
- name,
- js.string(name, "'"),
- _emitTypeName(type, lowerTypedef: true)
+ JS.Expression body = annotate(
+ js.call('dart.typedef(#, () => #)', [
+ js.string(element.name, "'"),
+ _emitTypeName(element.type, lowerTypedef: true)
]),
node,
- node.element);
+ element);
- return _finishClassDef(type, fnType);
+ var typeFormals = element.typeParameters;
+ if (typeFormals.isNotEmpty) {
+ return _defineClassTypeArguments(element, typeFormals,
+ js.statement('const # = #;', [element.name, body]));
+ } else {
+ return js.statement('# = #;', [_emitTopLevelName(element), body]);
+ }
}
@override
@@ -365,10 +480,10 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
@override
JS.Statement visitClassTypeAlias(ClassTypeAlias node) {
- var element = node.element;
+ ClassElement element = node.element;
// Forward all generative constructors from the base class.
- var body = <JS.Method>[];
+ var methods = <JS.Method>[];
var supertype = element.supertype;
if (!supertype.isObject) {
@@ -376,36 +491,39 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
var parentCtor = supertype.lookUpConstructor(ctor.name, ctor.library);
var fun = js.call('function() { super.#(...arguments); }',
[_constructorName(parentCtor)]) as JS.Fun;
- body.add(new JS.Method(_constructorName(ctor), fun));
+ methods.add(new JS.Method(_constructorName(ctor), fun));
}
}
- var cls = _emitClassDeclaration(element, body);
- return _finishClassDef(element.type, cls);
+ var classExpr = _emitClassExpression(element, methods);
+
+ var typeFormals = element.typeParameters;
+ if (typeFormals.isNotEmpty) {
+ return _defineClassTypeArguments(
+ element, typeFormals, new JS.ClassDeclaration(classExpr));
+ } else {
+ return js.statement('# = #;', [_emitTopLevelName(element), classExpr]);
+ }
}
- JS.Statement _emitJsType(String dartClassName, DartObject jsName) {
- var jsTypeName =
- getConstantField(jsName, 'name', types.stringType)?.toStringValue();
+ JS.Statement _emitJsType(Element e) {
+ var jsTypeName = getAnnotationName(e, isJSAnnotation);
+ if (jsTypeName == null || jsTypeName == e.name) return null;
- if (jsTypeName != null && jsTypeName != dartClassName) {
- // We export the JS type as if it was a Dart type. For example this allows
- // `dom.InputElement` to actually be HTMLInputElement.
- // TODO(jmesserly): if we had the JS name on the Element, we could just
- // generate it correctly when we refer to it.
- if (isPublic(dartClassName)) _addExport(dartClassName);
- return js.statement('const # = #;', [dartClassName, jsTypeName]);
- }
- return null;
+ // We export the JS type as if it was a Dart type. For example this allows
+ // `dom.InputElement` to actually be HTMLInputElement.
+ // TODO(jmesserly): if we had the JS name on the Element, we could just
+ // generate it correctly when we refer to it.
+ return js.statement('# = #;', [_emitTopLevelName(e), jsTypeName]);
}
@override
JS.Statement visitClassDeclaration(ClassDeclaration node) {
var classElem = node.element;
- var type = classElem.type;
- var jsName = findAnnotation(classElem, isJSAnnotation);
- if (jsName != null) return _emitJsType(node.name.name, jsName);
+ // If this is a JavaScript type, emit it now and then exit.
+ var jsTypeDef = _emitJsType(classElem);
+ if (jsTypeDef != null) return jsTypeDef;
var ctors = <ConstructorDeclaration>[];
var fields = <FieldDeclaration>[];
@@ -422,48 +540,50 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
}
var allFields = new List.from(fields)..addAll(staticFields);
-
- var classDecl = _emitClassDeclaration(
+ var classExpr = _emitClassExpression(
classElem, _emitClassMethods(node, ctors, fields),
fields: allFields);
- String jsPeerName;
- var jsPeer = findAnnotation(classElem, isJsPeerInterface);
- // Only look at "Native" annotations on registered extension types.
- // E.g., we're current ignoring the ones in dart:html.
- if (jsPeer == null && _extensionTypes.contains(classElem)) {
- jsPeer = findAnnotation(classElem, isNativeAnnotation);
- }
- if (jsPeer != null) {
- jsPeerName =
- getConstantField(jsPeer, 'name', types.stringType)?.toStringValue();
- if (jsPeerName.contains(',')) {
- jsPeerName = jsPeerName.split(',')[0];
- }
- }
+ var body = <JS.Statement>[];
+ var extensions = _extensionsToImplement(classElem);
+ _initExtensionSymbols(classElem, methods, fields, body);
- var body = _finishClassMembers(classElem, classDecl, ctors, fields,
- staticFields, methods, node.metadata, jsPeerName);
+ // Emit the class, e.g. `core.Object = class Object { ... }`
+ JS.Expression className = _defineClass(classElem, classExpr, body);
- var result = _finishClassDef(type, body);
+ // Emit things that come after the ES6 `class ... { ... }`.
+ _setBaseClass(classElem, className, body);
+ _defineNamedConstructors(ctors, body, className);
+ _emitVirtualFields(fields, className, body);
+ _emitClassSignature(methods, classElem, ctors, extensions, className, body);
+ _defineExtensionMembers(extensions, className, body);
+ _emitClassMetadata(node.metadata, className, body);
- if (jsPeerName != null) {
- // This class isn't allowed to be lazy, because we need to set up
- // the native JS type eagerly at this point.
- // If we wanted to support laziness, we could defer the hookup until
- // the end of the Dart library cycle load.
- assert(_loader.isLoaded(classElem));
+ JS.Statement classDef = _statement(body);
+ var typeFormals = classElem.typeParameters;
+ if (typeFormals.isNotEmpty) {
+ classDef = _defineClassTypeArguments(classElem, typeFormals, classDef);
+ }
- // TODO(jmesserly): this copies the dynamic members.
- // Probably fine for objects coming from JS, but not if we actually
- // want to support construction of instances with generic types other
- // than dynamic. See issue #154 for Array and List<E> related bug.
- var copyMembers = js.statement(
- 'dart.registerExtension(dart.global.#, #);',
- [_propertyName(jsPeerName), classElem.name]);
- return _statement([result, copyMembers]);
+ body = <JS.Statement>[classDef];
+ _emitStaticFields(staticFields, classElem, body);
+ _registerExtensionType(classElem, body);
+ return _statement(body);
+ }
+
+ JS.Expression _defineClass(ClassElement classElem,
+ JS.ClassExpression classExpr, List<JS.Statement> body) {
+ JS.Expression className;
+ if (classElem.typeParameters.isNotEmpty) {
+ // Generic classes will be defined inside a function that closes over the
+ // type parameter. So we can use their local variable name directly.
+ className = classExpr.name;
+ body.add(new JS.ClassDeclaration(classExpr));
+ } else {
+ className = _emitTopLevelName(classElem);
+ body.add(js.statement('# = #;', [className, classExpr]));
}
- return result;
+ return className;
}
List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) {
@@ -492,7 +612,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
var element = node.element;
var type = element.type;
var name = js.string(type.name);
- var id = new JS.Identifier(type.name);
// Generate a class per section 13 of the spec.
// TODO(vsm): Generate any accompanying metadata
@@ -514,9 +633,12 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
js.call('function() { return #[this.index]; }', nameMap) as JS.Fun);
// Create enum class
- var classExpr = new JS.ClassExpression(
- id, _emitClassHeritage(element), [constructor, toStringF]);
- var result = <JS.Statement>[js.statement('#', classExpr)];
+ var classExpr = new JS.ClassExpression(new JS.Identifier(type.name),
+ _emitClassHeritage(element), [constructor, toStringF]);
+ var id = _emitTopLevelName(element);
+ var result = [
+ js.statement('# = #', [id, classExpr])
+ ];
// Create static fields for each enum value
for (var i = 0; i < fields.length; ++i) {
@@ -530,58 +652,27 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
result.add(js.statement('#.values = dart.const(dart.list(#, #));',
[id, values, _emitTypeName(type)]));
- if (isPublic(type.name)) _addExport(type.name);
return _statement(result);
}
- /// Given a class element and body, complete the class declaration.
- /// This handles generic type parameters, laziness (in library-cycle cases),
- /// and ensuring dependencies are loaded first.
- JS.Statement _finishClassDef(ParameterizedType type, JS.Statement body) {
- var name = type.name;
- var genericName = '$name\$';
-
- JS.Statement genericDef = null;
- if (_typeFormalsOf(type).isNotEmpty) {
- genericDef = _emitGenericClassDef(type, body);
- }
- // The base class and all mixins must be declared before this class.
- if (!_loader.isLoaded(type.element)) {
- // TODO(jmesserly): the lazy class def is a simple solution for now.
- // We may want to consider other options in the future.
-
- if (genericDef != null) {
- return js.statement(
- '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }',
- [genericDef, _exportsVar, _propertyName(name), genericName]);
- }
-
- return js.statement(
- 'dart.defineLazyClass(#, { get #() { #; return #; } });',
- [_exportsVar, _propertyName(name), body, name]);
- }
-
- if (isPublic(name)) _addExport(name);
-
- if (genericDef != null) {
- var dynType = fillDynamicTypeArgs(type, types);
- var genericInst = _emitTypeName(dynType, lowerGeneric: true);
- return js.statement('{ #; let # = #; }', [genericDef, name, genericInst]);
- }
- return body;
- }
+ /// Wraps a possibly generic class in its type arguments.
+ JS.Statement _defineClassTypeArguments(TypeDefiningElement element,
+ List<TypeParameterElement> formals, JS.Statement body) {
+ assert(formals.isNotEmpty);
+ var genericDef =
+ js.statement('# = dart.generic((#) => { #; return #; });', [
+ _emitTopLevelName(element, suffix: r'$'),
+ _emitTypeFormals(formals),
+ body,
+ element.name
+ ]);
- JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) {
- var name = type.name;
- var genericName = '$name\$';
- var typeParams = _typeFormalsOf(type).map((p) => p.name);
- if (isPublic(name)) _addExport(genericName);
- return js.statement('const # = dart.generic(function(#) { #; return #; });',
- [genericName, typeParams, body, name]);
+ var dynType = fillDynamicTypeArgs(element.type);
+ var genericInst = _emitTypeName(dynType, lowerGeneric: true);
+ return js.statement(
+ '{ #; # = #; }', [genericDef, _emitTopLevelName(element), genericInst]);
}
- final _hasDeferredSupertype = new HashSet<ClassElement>();
-
bool _deferIfNeeded(DartType type, ClassElement current) {
if (type is ParameterizedType) {
var typeArguments = type.typeArguments;
@@ -596,7 +687,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
return false;
}
- JS.Statement _emitClassDeclaration(
+ JS.ClassExpression _emitClassExpression(
ClassElement element, List<JS.Method> methods,
{List<FieldDeclaration> fields}) {
String name = element.name;
@@ -604,29 +695,14 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
var typeParams = _emitTypeFormals(element.typeParameters);
var jsFields = fields?.map(_emitTypeScriptField)?.toList();
- // Workaround for Closure: super classes must be qualified paths.
- // TODO(jmesserly): is there a bug filed? we need to get out of the business
- // of working around bugs in other transpilers...
- JS.Statement workaroundSuper = null;
- if (options.closure && heritage is JS.Call) {
- var superVar = new JS.TemporaryId('$name\$super');
- workaroundSuper = js.statement('const # = #;', [superVar, heritage]);
- heritage = superVar;
- }
- var decl = new JS.ClassDeclaration(new JS.ClassExpression(
- new JS.Identifier(name), heritage, methods,
- typeParams: typeParams, fields: jsFields));
- if (workaroundSuper != null) {
- return new JS.Block([workaroundSuper, decl]);
- }
- return decl;
+ return new JS.ClassExpression(new JS.Identifier(name), heritage, methods,
+ typeParams: typeParams, fields: jsFields);
}
JS.Expression _emitClassHeritage(ClassElement element) {
var type = element.type;
if (type.isObject) return null;
- // Assume we can load eagerly, until proven otherwise.
_loader.startTopLevel(element);
// Find the super type
@@ -634,7 +710,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
var supertype = type.superclass;
if (_deferIfNeeded(supertype, element)) {
// Fall back to raw type.
- supertype = fillDynamicTypeArgs(supertype.element.type, _types);
+ supertype = fillDynamicTypeArgs(supertype.element.type);
_hasDeferredSupertype.add(element);
}
heritage = _emitTypeName(supertype);
@@ -646,6 +722,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
}
_loader.finishTopLevel(element);
+
return heritage;
}
@@ -662,12 +739,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
if (!node.isStatic) {
for (var decl in node.fields.variables) {
var field = decl.element;
- var name = decl.name.name;
- var annotation = findAnnotation(field, isJsName);
- if (annotation != null) {
- name = getConstantField(annotation, 'name', types.stringType)
- ?.toStringValue();
- }
+ var name = getAnnotationName(field, isJsName) ?? field.name;
// Generate getter
var fn = new JS.Fun([], js.statement('{ return this.#; }', [name]));
var method =
@@ -757,7 +829,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
// Otherwise, emit the adapter method, which wraps the Dart iterator in
// an ES6 iterator.
return new JS.Method(
- js.call('$_SYMBOL.iterator'),
+ js.call('Symbol.iterator'),
js.call('function() { return new dart.JsIterator(this.#); }',
[_emitMemberName('iterator', type: t)]) as JS.Fun);
}
@@ -772,147 +844,110 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
}
}
- /// Emit class members that need to come after the class declaration, such
- /// as static fields. See [_emitClassMethods] for things that are emitted
- /// inside the ES6 `class { ... }` node.
- JS.Statement _finishClassMembers(
- ClassElement classElem,
- JS.Statement classDecl,
- List<ConstructorDeclaration> ctors,
- List<FieldDeclaration> fields,
- List<FieldDeclaration> staticFields,
- List<MethodDeclaration> methods,
- List<Annotation> metadata,
- String jsPeerName) {
- var name = classElem.name;
- var body = <JS.Statement>[];
-
- if (_extensionTypes.contains(classElem)) {
- var dartxNames = <JS.Expression>[];
- for (var m in methods) {
- if (!m.isAbstract && !m.isStatic && m.element.isPublic) {
- dartxNames.add(_elementMemberName(m.element, allowExtensions: false));
- }
- }
- for (var f in fields) {
- if (!f.isStatic) {
- for (var d in f.fields.variables) {
- if (d.element.isPublic) {
- dartxNames.add(
- _elementMemberName(d.element.getter, allowExtensions: false));
- }
- }
- }
- }
- if (dartxNames.isNotEmpty) {
- body.add(js.statement('dart.defineExtensionNames(#)',
- [new JS.ArrayInitializer(dartxNames, multiline: true)]));
- }
+ /// Gets the JS peer for this Dart type if any, otherwise null.
+ ///
+ /// For example for dart:_interceptors `JSArray` this will return "Array",
+ /// referring to the JavaScript built-in `Array` type.
+ String _getJSPeerName(ClassElement classElem) {
+ var jsPeerName = getAnnotationName(
+ classElem,
+ (a) =>
+ isJsPeerInterface(a) ||
+ isNativeAnnotation(a) && _extensionTypes.contains(classElem));
+ if (jsPeerName != null && jsPeerName.contains(',')) {
+ jsPeerName = jsPeerName.split(',')[0];
+ }
+ return jsPeerName;
+ }
+
+ void _registerExtensionType(ClassElement classElem, List<JS.Statement> body) {
+ var jsPeerName = _getJSPeerName(classElem);
+ if (jsPeerName != null) {
+ // TODO(jmesserly): this copies the dynamic members.
+ // Probably fine for objects coming from JS, but not if we actually
+ // want to support construction of instances with generic types other
+ // than dynamic. See issue #154 for Array and List<E> related bug.
+ body.add(js.statement('dart.registerExtension(dart.global.#, #);',
+ [_propertyName(jsPeerName), _emitTopLevelName(classElem)]));
}
+ }
- body.add(classDecl);
-
- // TODO(jmesserly): we should really just extend native Array.
+ void _setBaseClass(ClassElement classElem, JS.Expression className,
+ List<JS.Statement> body) {
+ String jsPeerName = _getJSPeerName(classElem);
+ JS.Expression newBaseClass;
if (jsPeerName != null && classElem.typeParameters.isNotEmpty) {
- body.add(js.statement('dart.setBaseClass(#, dart.global.#);',
- [classElem.name, _propertyName(jsPeerName)]));
+ // TODO(jmesserly): we should really just extend Array in the first place.
+ newBaseClass = js.call('dart.global.#', [jsPeerName]);
+ } else if (_hasDeferredSupertype.contains(classElem)) {
+ newBaseClass = _emitTypeName(classElem.type.superclass);
}
-
- // Deferred Superclass
- if (_hasDeferredSupertype.contains(classElem)) {
- body.add(js.statement('#.prototype.__proto__ = #.prototype;',
- [name, _emitTypeName(classElem.type.superclass)]));
+ if (newBaseClass != null) {
+ body.add(
+ js.statement('dart.setBaseClass(#, #);', [className, newBaseClass]));
}
+ }
- // Interfaces
- if (classElem.interfaces.isNotEmpty) {
- body.add(js.statement('#[dart.implements] = () => #;', [
- name,
- new JS.ArrayInitializer(new List<JS.Expression>.from(
- classElem.interfaces.map(_emitTypeName)))
- ]));
+ /// Emits instance fields, if they are virtual
+ /// (in other words, they override a getter/setter pair).
+ void _emitVirtualFields(List<FieldDeclaration> fields,
+ JS.Expression className, List<JS.Statement> body) {
+ for (FieldDeclaration member in fields) {
+ for (VariableDeclaration field in member.fields.variables) {
+ if (_fieldsNeedingStorage.contains(field.element)) {
+ body.add(_overrideField(className, field.element));
+ }
+ }
}
+ }
- // Named constructors
+ void _defineNamedConstructors(List<ConstructorDeclaration> ctors,
+ List<JS.Statement> body, JS.Expression className) {
for (ConstructorDeclaration member in ctors) {
if (member.name != null && member.factoryKeyword == null) {
body.add(js.statement('dart.defineNamedConstructor(#, #);',
- [name, _emitMemberName(member.name.name, isStatic: true)]));
+ [className, _emitMemberName(member.name.name, isStatic: true)]));
}
}
+ }
- // Emits instance fields, if they are virtual
- // (in other words, they override a getter/setter pair).
- for (FieldDeclaration member in fields) {
+ /// Emits static fields for a class, and initialize them eagerly if possible,
+ /// otherwise define them as lazy properties.
+ void _emitStaticFields(List<FieldDeclaration> staticFields,
+ ClassElement classElem, List<JS.Statement> body) {
+ var lazyStatics = <VariableDeclaration>[];
+ for (FieldDeclaration member in staticFields) {
for (VariableDeclaration field in member.fields.variables) {
- if (_fieldsNeedingStorage.contains(field.element)) {
- body.add(_overrideField(field.element));
+ JS.Statement eagerField = _emitConstantStaticField(classElem, field);
+ if (eagerField != null) {
+ body.add(eagerField);
+ } else {
+ lazyStatics.add(field);
}
}
}
+ if (lazyStatics.isNotEmpty) {
+ body.add(_emitLazyFields(classElem, lazyStatics));
+ }
+ }
- // Emit the signature on the class recording the runtime type information
- var extensions = _extensionsToImplement(classElem);
- {
- var tStatics = <JS.Property>[];
- var tMethods = <JS.Property>[];
- var sNames = <JS.Expression>[];
- for (MethodDeclaration node in methods) {
- if (!(node.isSetter || node.isGetter || node.isAbstract)) {
- var name = node.name.name;
- var element = node.element;
- var inheritedElement =
- classElem.lookUpInheritedConcreteMethod(name, currentLibrary);
- if (inheritedElement != null &&
- inheritedElement.type == element.type) {
- continue;
- }
- var memberName = _elementMemberName(element);
- var parts = _emitFunctionTypeParts(element.type);
- var property =
- new JS.Property(memberName, new JS.ArrayInitializer(parts));
- if (node.isStatic) {
- tStatics.add(property);
- sNames.add(memberName);
- } else {
- tMethods.add(property);
- }
- }
- }
-
- var tCtors = <JS.Property>[];
- for (ConstructorDeclaration node in ctors) {
- var memberName = _constructorName(node.element);
- var element = node.element;
- var parts = _emitFunctionTypeParts(element.type, node.parameters);
- var property =
- new JS.Property(memberName, new JS.ArrayInitializer(parts));
- tCtors.add(property);
- }
-
- JS.Property build(String name, List<JS.Property> elements) {
- var o =
- new JS.ObjectInitializer(elements, multiline: elements.length > 1);
- var e = js.call('() => #', o);
- return new JS.Property(_propertyName(name), e);
- }
- var sigFields = <JS.Property>[];
- if (!tCtors.isEmpty) sigFields.add(build('constructors', tCtors));
- 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);
- }
- if (!sigFields.isEmpty || extensions.isNotEmpty) {
- var sig = new JS.ObjectInitializer(sigFields);
- var classExpr = new JS.Identifier(name);
- body.add(js.statement('dart.setSignature(#, #);', [classExpr, sig]));
- }
+ void _emitClassMetadata(List<Annotation> metadata, JS.Expression className,
+ List<JS.Statement> body) {
+ // TODO(vsm): Make this optional per #268.
+ // Metadata
+ if (metadata.isNotEmpty) {
+ body.add(js.statement('#[dart.metadata] = () => #;', [
+ className,
+ new JS.ArrayInitializer(
+ new List<JS.Expression>.from(metadata.map(_instantiateAnnotation)))
+ ]));
}
+ }
+ /// If a concrete class implements one of our extensions, we might need to
+ /// add forwarders.
+ void _defineExtensionMembers(List<ExecutableElement> extensions,
+ JS.Expression className, List<JS.Statement> body) {
// If a concrete class implements one of our extensions, we might need to
// add forwarders.
if (extensions.isNotEmpty) {
@@ -921,39 +956,113 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
methodNames.add(_elementMemberName(e));
}
body.add(js.statement('dart.defineExtensionMembers(#, #);', [
- name,
+ className,
new JS.ArrayInitializer(methodNames, multiline: methodNames.length > 4)
]));
}
+ }
- // TODO(vsm): Make this optional per #268.
- // Metadata
- if (metadata.isNotEmpty) {
- body.add(js.statement('#[dart.metadata] = () => #;', [
- name,
- new JS.ArrayInitializer(
- new List<JS.Expression>.from(metadata.map(_instantiateAnnotation)))
+ /// Emit the signature on the class recording the runtime type information
+ void _emitClassSignature(
+ List<MethodDeclaration> methods,
+ ClassElement classElem,
+ List<ConstructorDeclaration> ctors,
+ List<ExecutableElement> extensions,
+ JS.Expression className,
+ List<JS.Statement> body) {
+ if (classElem.interfaces.isNotEmpty) {
+ body.add(js.statement('#[dart.implements] = () => #;', [
+ className,
+ new JS.ArrayInitializer(new List<JS.Expression>.from(
+ classElem.interfaces.map(_emitTypeName)))
]));
}
- // 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);
+ var tStatics = <JS.Property>[];
+ var tMethods = <JS.Property>[];
+ var sNames = <JS.Expression>[];
+ for (MethodDeclaration node in methods) {
+ if (!(node.isSetter || node.isGetter || node.isAbstract)) {
+ var name = node.name.name;
+ var element = node.element;
+ var inheritedElement =
+ classElem.lookUpInheritedConcreteMethod(name, currentLibrary);
+ if (inheritedElement != null && inheritedElement.type == element.type) {
+ continue;
+ }
+ var memberName = _elementMemberName(element);
+ var parts = _emitFunctionTypeParts(element.type);
+ var property =
+ new JS.Property(memberName, new JS.ArrayInitializer(parts));
+ if (node.isStatic) {
+ tStatics.add(property);
+ sNames.add(memberName);
} else {
- lazyStatics.add(field);
+ tMethods.add(property);
}
}
}
- if (lazyStatics.isNotEmpty) {
- body.add(_emitLazyFields(classElem, lazyStatics));
+
+ var tCtors = <JS.Property>[];
+ for (ConstructorDeclaration node in ctors) {
+ var memberName = _constructorName(node.element);
+ var element = node.element;
+ var parts = _emitFunctionTypeParts(element.type, node.parameters);
+ var property =
+ new JS.Property(memberName, new JS.ArrayInitializer(parts));
+ tCtors.add(property);
}
- return _statement(body);
+ JS.Property build(String name, List<JS.Property> elements) {
+ var o =
+ new JS.ObjectInitializer(elements, multiline: elements.length > 1);
+ var e = js.call('() => #', o);
+ return new JS.Property(_propertyName(name), e);
+ }
+ var sigFields = <JS.Property>[];
+ if (!tCtors.isEmpty) sigFields.add(build('constructors', tCtors));
+ 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);
+ }
+ if (!sigFields.isEmpty || extensions.isNotEmpty) {
+ var sig = new JS.ObjectInitializer(sigFields);
+ body.add(js.statement('dart.setSignature(#, #);', [className, sig]));
+ }
+ }
+
+ /// Ensure `dartx.` symbols we will use are present.
+ void _initExtensionSymbols(
+ ClassElement classElem,
+ List<MethodDeclaration> methods,
+ List<FieldDeclaration> fields,
+ List<JS.Statement> body) {
+ if (_extensionTypes.contains(classElem)) {
+ var dartxNames = <JS.Expression>[];
+ for (var m in methods) {
+ if (!m.isAbstract && !m.isStatic && m.element.isPublic) {
+ dartxNames.add(_elementMemberName(m.element, allowExtensions: false));
+ }
+ }
+ for (var f in fields) {
+ if (!f.isStatic) {
+ for (var d in f.fields.variables) {
+ if (d.element.isPublic) {
+ dartxNames.add(
+ _elementMemberName(d.element.getter, allowExtensions: false));
+ }
+ }
+ }
+ }
+ if (dartxNames.isNotEmpty) {
+ body.add(js.statement('dart.defineExtensionNames(#)',
+ [new JS.ArrayInitializer(dartxNames, multiline: true)]));
+ }
+ }
}
List<ExecutableElement> _extensionsToImplement(ClassElement element) {
@@ -998,10 +1107,10 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
_collectExtensions(type.superclass, types);
}
- JS.Statement _overrideField(FieldElement e) {
+ JS.Statement _overrideField(JS.Expression className, FieldElement e) {
var cls = e.enclosingElement;
return js.statement('dart.virtualField(#, #)',
- [cls.name, _emitMemberName(e.name, type: cls.type)]);
+ [className, _emitMemberName(e.name, type: cls.type)]);
}
/// Generates the implicit default constructor for class C of the form
@@ -1129,13 +1238,13 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
JS.Block _emitConstructorBody(
ConstructorDeclaration node, List<FieldDeclaration> fields) {
var body = <JS.Statement>[];
+ ClassDeclaration cls = node.parent;
// Generate optional/named argument value assignment. These can not have
// side effects, and may be used by the constructor's initializers, so it's
// nice to do them first.
// Also for const constructors we need to ensure default values are
// available for use by top-level constant initializers.
- ClassDeclaration cls = node.parent;
if (node.constKeyword != null) _loader.startTopLevel(cls.element);
var init = _emitArgumentInitializers(node, constructor: true);
if (node.constKeyword != null) _loader.finishTopLevel(cls.element);
@@ -1191,7 +1300,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
// This will only happen if the code has errors:
// we're trying to generate an implicit constructor for a type where
// we don't have a default constructor in the supertype.
- assert(options.forceCompile);
+ assert(options.unsafeForceCompile);
return null;
}
}
@@ -1370,12 +1479,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
}
var params = visitFormalParameterList(node.parameters, destructure: false);
- String name = node.name.name;
- var annotation = findAnnotation(node.element, isJsName);
- if (annotation != null) {
- name = getConstantField(annotation, 'name', types.stringType)
- ?.toStringValue();
- }
+ String name =
+ getAnnotationName(node.element, isJSAnnotation) ?? node.name.name;
if (node.isGetter) {
return new JS.Fun(params, js.statement('{ return this.#; }', [name]));
} else if (node.isSetter) {
@@ -1445,15 +1550,23 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
if (_externalOrNative(node)) return null;
if (node.isGetter || node.isSetter) {
- // Add these later so we can use getter/setter syntax.
- _properties.add(node);
- return null;
+ // If we have a getter/setter pair, they need to be defined together.
+ PropertyAccessorElement element = node.element;
+ var props = <JS.Method>[];
+ var getter = element.variable.getter;
+ if (getter != null) {
+ props.add(_loader.customEmitDeclaration(getter, _emitTopLevelProperty));
+ }
+ var setter = element.variable.setter;
+ if (setter != null) {
+ props.add(_loader.customEmitDeclaration(setter, _emitTopLevelProperty));
+ }
+
+ return js.statement('dart.copyProperties(#, { # });',
+ [emitLibraryName(currentLibrary), props]);
}
var body = <JS.Statement>[];
- _flushLibraryProperties(body);
-
- var name = node.name.name;
var fn = _emitFunction(node.functionExpression);
if (currentLibrary.source.isInSystemLibrary &&
@@ -1461,16 +1574,14 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
fn = _simplifyPassThroughArrowFunCallBody(fn);
}
- var id = new JS.Identifier(name);
- body.add(annotate(new JS.FunctionDeclaration(id, fn), node, node.element));
- if (!_isDartRuntime) {
- body.add(_emitFunctionTagged(id, node.element.type, topLevel: true)
+ var element = node.element;
+ var nameExpr = _emitTopLevelName(element);
+ body.add(annotate(js.statement('# = #', [nameExpr, fn]), node, element));
+ if (!_isDartRuntime(element.library)) {
+ body.add(_emitFunctionTagged(nameExpr, element.type, topLevel: true)
.toStatement());
}
- if (isPublic(name)) {
- _addExport(name, getJSExportName(node.element, types) ?? name);
- }
return _statement(body);
}
@@ -1479,8 +1590,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
if (body is ExpressionFunctionBody) {
return _isJSInvocation(body.expression);
} else if (body is BlockFunctionBody) {
- if (body.block.statements.length == 1) {
- var stat = body.block.statements.single;
+ var statements = body.block.statements;
+ if (statements.length == 1) {
+ var stat = statements[0];
if (stat is ReturnStatement) {
return _isJSInvocation(stat.expression);
}
@@ -1556,10 +1668,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
type.normalParameterTypes.every((t) => t.isDynamic)) {
return js.call('dart.fn(#)', [fn]);
}
- if (lazy) {
- return js.call('dart.fn(#, () => #)', [fn, _emitFunctionRTTI(type)]);
- }
- return js.call('dart.fn(#, #)', [fn, _emitFunctionTypeParts(type)]);
+
+ String code = lazy ? '() => dart.definiteFunctionType(#)' : '#';
+ return js.call('dart.fn(#, $code)', [fn, _emitFunctionTypeParts(type)]);
}
throw 'Function has non function type: $type';
}
@@ -1736,8 +1847,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
// type literal
if (element is TypeDefiningElement) {
- return _emitTypeName(
- fillDynamicTypeArgs((element as dynamic).type, types));
+ return _emitTypeName(fillDynamicTypeArgs(element.type));
}
// library member
@@ -1758,7 +1868,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
// library prefix. We don't need those because static calls can't use
// the generic type.
if (isStatic) {
- var dynType = _emitTypeName(fillDynamicTypeArgs(type, types));
+ var dynType = _emitTypeName(fillDynamicTypeArgs(type));
return new JS.PropertyAccess(dynType, member);
}
@@ -1861,11 +1971,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
return [rt, ra];
}
- JS.Expression _emitFunctionRTTI(FunctionType type) {
- var parts = _emitFunctionTypeParts(type);
- return js.call('dart.definiteFunctionType(#)', [parts]);
- }
-
/// Emits a Dart [type] into code.
///
/// If [lowerTypedef] is set, a typedef will be expanded as if it were a
@@ -1896,9 +2001,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
}
// For now, reify generic method parameters as dynamic
bool _isGenericTypeParameter(DartType type) =>
- (type is TypeParameterType) &&
- !(type.element.enclosingElement is ClassElement ||
- type.element.enclosingElement is FunctionTypeAliasElement);
+ type is TypeParameterType &&
+ type.element.enclosingElement is! TypeDefiningElement;
if (_isGenericTypeParameter(type)) {
return js.call('dart.dynamic');
@@ -1910,16 +2014,10 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
if (type is ParameterizedType) {
var args = type.typeArguments;
- var isCurrentClass =
- args.isNotEmpty && _loader.isCurrentElement(type.element);
Iterable jsArgs = null;
- if (args
- .any((a) => a != types.dynamicType && !_isGenericTypeParameter(a))) {
+ if (args.any((a) => !a.isDynamic && !_isGenericTypeParameter(a))) {
jsArgs = args.map(_emitTypeName);
- } else if (lowerGeneric || isCurrentClass) {
- // When creating a `new S<dynamic>` we try and use the raw form
- // `new S()`, but this does not work if we're inside the same class,
- // because `S` refers to the current S<T> we are generating.
+ } else if (lowerGeneric) {
jsArgs = [];
}
if (jsArgs != null) {
@@ -1931,29 +2029,10 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
return _emitTopLevelName(element);
}
- JS.Expression _emitTopLevelName(Element e, {String suffix: ''}) {
- var libName = emitLibraryName(e.library);
-
- // Always qualify:
- // * mutable top-level fields
- // * elements from other libraries
- bool mutableTopLevel = e is TopLevelVariableElement &&
- !e.isConst &&
- !_isFinalJSDecl(e.computeNode());
- bool fromAnotherLibrary = e.library != currentLibrary;
- var nameExpr;
- if (fromAnotherLibrary) {
- nameExpr = _propertyName((getJSExportName(e, types) ?? e.name) + suffix);
- } else {
- nameExpr = _propertyName(e.name + suffix);
- }
- if (mutableTopLevel || fromAnotherLibrary) {
- return new JS.PropertyAccess(libName, nameExpr);
- }
-
- var id = new JS.MaybeQualifiedId(libName, nameExpr);
- _qualifiedIds.add(new Tuple2(e, id));
- return id;
+ JS.PropertyAccess _emitTopLevelName(Element e, {String suffix: ''}) {
+ String name = getJSExportName(e) + suffix;
+ return new JS.PropertyAccess(
+ emitLibraryName(e.library), _propertyName(name));
}
@override
@@ -2022,7 +2101,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
}
if (target != null && DynamicInvoke.get(target)) {
- return js.call('dart.$DPUT(#, #, #)',
+ return js.call('dart.dput(#, #, #)',
[_visit(target), _emitMemberName(id.name), _visit(rhs)]);
}
@@ -2087,7 +2166,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
String code;
if (target == null || isLibraryPrefix(target)) {
if (DynamicInvoke.get(node.methodName)) {
- code = 'dart.$DCALL(#, #)';
+ code = 'dart.dcall(#, #)';
} else {
code = '#(#)';
}
@@ -2102,12 +2181,12 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
var memberName = _emitMemberName(name, type: type, isStatic: isStatic);
if (DynamicInvoke.get(target)) {
- code = 'dart.$DSEND(#, #, #)';
+ code = 'dart.dsend(#, #, #)';
} else if (DynamicInvoke.get(node.methodName)) {
// This is a dynamic call to a statically known target. For example:
// class Foo { Function bar; }
// new Foo().bar(); // dynamic call
- code = 'dart.$DCALL(#.#, #)';
+ code = 'dart.dcall(#.#, #)';
} else if (_requiresStaticDispatch(target, name)) {
// Object methods require a helper for null checks.
return js.call('dart.#(#, #)',
@@ -2162,7 +2241,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
FunctionExpressionInvocation node) {
var code;
if (DynamicInvoke.get(node.function)) {
- code = 'dart.$DCALL(#, #)';
+ code = 'dart.dcall(#, #)';
} else {
code = '#(#)';
}
@@ -2217,7 +2296,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
JS.Expression name;
JS.SimpleBindingPattern structure = null;
String paramName = param.identifier.name;
- if (invalidVariableName(paramName)) {
+ if (JS.invalidVariableName(paramName)) {
name = js.string(paramName);
structure = new JS.SimpleBindingPattern(_visit(param.identifier));
} else {
@@ -2325,29 +2404,16 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
@override
visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
- for (var v in node.variables.variables) {
- _loader.loadDeclaration(v, v.element);
+ for (var variable in node.variables.variables) {
+ _loader.emitDeclaration(variable.element);
}
}
- /// Emits static fields.
- ///
- /// Instance fields are emitted in [_initializeFields].
- ///
- /// These are generally treated the same as top-level fields, see
- /// [visitTopLevelVariableDeclaration].
+ /// This is not used--we emit fields as we are emitting the class,
+ /// see [visitClassDeclaration].
@override
visitFieldDeclaration(FieldDeclaration node) {
- if (!node.isStatic) return;
-
- node.fields.variables.forEach(_emitModuleItem);
- }
-
- _addExport(String name, [String exportName]) {
- if (_exports.containsKey(name)) {
- throw 'Duplicate top level name found: $name';
- }
- _exports[name] = exportName ?? name;
+ assert(false);
}
@override
@@ -2356,8 +2422,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
// Special case a single variable with an initializer.
// This helps emit cleaner code for things like:
// var result = []..add(1)..add(2);
- if (node.variables.variables.length == 1) {
- var v = node.variables.variables.single;
+ var variables = node.variables.variables;
+ if (variables.length == 1) {
+ var v = variables[0];
if (v.initializer != null) {
var name = new JS.Identifier(v.name.name);
return _visit(v.initializer).toVariableDeclaration(name);
@@ -2419,7 +2486,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
if (eagerInit && !JS.invalidStaticFieldName(fieldName)) {
return annotate(
js.statement('#.# = #;', [
- classElem.name,
+ _emitTopLevelName(classElem),
_emitMemberName(fieldName, isStatic: true),
jsInit
]),
@@ -2434,7 +2501,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
}
/// Emits a top-level field.
- JS.Statement _emitTopLevelField(VariableDeclaration field) {
+ JS.ModuleItem _emitTopLevelField(VariableDeclaration field) {
TopLevelVariableElement element = field.element;
assert(element.isStatic);
@@ -2455,44 +2522,17 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
// Treat `final x = JS('', '...')` as a const (non-lazy) to help compile
// runtime helpers.
+ // TODO(jmesserly): we don't track dependencies correctly for these.
var isJSTopLevel = field.isFinal && _isFinalJSDecl(field);
- if (isJSTopLevel) eagerInit = true;
-
- var fieldName = field.name.name;
- var exportName = fieldName;
- if (element is TopLevelVariableElement) {
- exportName = getJSExportName(element, types) ?? fieldName;
- }
- if ((field.isConst && eagerInit && element is TopLevelVariableElement) ||
- 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, exportName);
- var declKeyword = field.isConst || field.isFinal ? 'const' : 'let';
- if (isJSTopLevel && jsInit is JS.ClassExpression) {
- return new JS.ClassDeclaration(jsInit);
- }
- return js.statement('#;', [
- annotate(
- new JS.VariableDeclarationList(declKeyword, [
- new JS.VariableInitialization(
- new JS.Identifier(fieldName,
- type: emitTypeRef(field.element.type)),
- jsInit)
- ]),
- field,
- field.element)
- ]);
- }
-
- if (eagerInit && !JS.invalidStaticFieldName(fieldName)) {
- return annotate(js.statement('# = #;', [_visit(field.name), jsInit]),
- field, field.element);
+ if (eagerInit || isJSTopLevel) {
+ return annotate(
+ js.statement('# = #;', [_emitTopLevelName(element), jsInit]),
+ field,
+ element);
}
- return _emitLazyFields(currentLibrary, [field]);
+ assert(element.library == currentLibrary);
+ return _emitLazyFields(element.library, [field]);
}
JS.Expression _visitInitializer(VariableDeclaration node) {
@@ -2530,13 +2570,12 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
JS.Expression objExpr;
if (target is ClassElement) {
- objExpr = new JS.Identifier(target.type.name);
+ objExpr = _emitTopLevelName(target);
} else {
objExpr = emitLibraryName(target);
}
- return js
- .statement('dart.defineLazyProperties(#, { # });', [objExpr, methods]);
+ return js.statement('dart.defineLazy(#, { # });', [objExpr, methods]);
}
PropertyAccessorElement _findAccessor(VariableElement element,
@@ -2550,13 +2589,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
return null;
}
- void _flushLibraryProperties(List<JS.Statement> body) {
- if (_properties.isEmpty) return;
- body.add(js.statement('dart.copyProperties(#, { # });',
- [_exportsVar, _properties.map(_emitTopLevelProperty)]));
- _properties.clear();
- }
-
JS.Expression _emitConstructorName(
ConstructorElement element, DartType type, SimpleIdentifier name) {
var typeName = _emitTypeName(type);
@@ -2615,10 +2647,10 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
/// "extension methods". This allows types to be extended without adding
/// extensions directly on the prototype.
bool isPrimitiveType(DartType t) =>
- typeIsPrimitiveInJS(t) || t == _types.stringType;
+ typeIsPrimitiveInJS(t) || t == types.stringType;
bool typeIsPrimitiveInJS(DartType t) =>
- _isNumberInJS(t) || t == _types.boolType;
+ _isNumberInJS(t) || t == types.boolType;
bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) =>
typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT);
@@ -2670,7 +2702,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
}
if (binaryOperationIsPrimitive(leftType, rightType) ||
- leftType == _types.stringType && op.type == TokenType.PLUS) {
+ leftType == types.stringType && op.type == TokenType.PLUS) {
// special cases where we inline the operation
// these values are assumed to be non-null (determined by the checker)
// TODO(jmesserly): it would be nice to just inline the method from core,
@@ -3019,7 +3051,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
var name = _emitMemberName(memberId.name,
type: getStaticType(target), isStatic: isStatic);
if (DynamicInvoke.get(target)) {
- return js.call('dart.$DLOAD(#, #)', [_visit(target), name]);
+ return js.call('dart.dload(#, #)', [_visit(target), name]);
}
String code;
@@ -3053,12 +3085,12 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
var memberName = _emitMemberName(name, unary: args.isEmpty, type: type);
if (DynamicInvoke.get(target)) {
// dynamic dispatch
- var dynamicHelper = const {'[]': DINDEX, '[]=': DSETINDEX}[name];
+ var dynamicHelper = const {'[]': 'dindex', '[]=': 'dsetindex'}[name];
if (dynamicHelper != null) {
return js.call(
'dart.$dynamicHelper(#, #)', [_visit(target), _visitList(args)]);
}
- return js.call('dart.$DSEND(#, #, #)',
+ return js.call('dart.dsend(#, #, #)',
[_visit(target), memberName, _visitList(args)]);
}
@@ -3189,20 +3221,14 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
//
// TODO(jmesserly): we may want a helper if these become common. For now the
// full desugaring seems okay.
- var context = compiler.context;
- var dart_async = context
- .computeLibraryElement(context.sourceFactory.forUri('dart:async'));
- var _streamIteratorType =
- rules.instantiateToBounds(dart_async.getType('StreamIterator').type);
-
+ var streamIterator = rules.instantiateToBounds(_asyncStreamIterator);
var createStreamIter = _emitInstanceCreationExpression(
- _streamIteratorType.element.unnamedConstructor,
- _streamIteratorType,
+ streamIterator.element.unnamedConstructor,
+ streamIterator,
null,
AstBuilder.argumentList([node.iterable]),
false);
- var iter =
- _visit(_createTemporary('it', _streamIteratorType, nullable: false));
+ var iter = _visit(_createTemporary('it', streamIterator, nullable: false));
var init = _visit(node.identifier);
if (init == null) {
@@ -3500,11 +3526,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
_visitList(nodes) as List<JS.Expression>, operator);
}
- /// Return the bound type parameters for a ParameterizedType
- List<TypeParameterElement> _typeFormalsOf(ParameterizedType type) {
- return type is FunctionType ? type.typeFormals : type.typeParameters;
- }
-
/// Like [_emitMemberName], but for declaration sites.
///
/// Unlike call sites, we always have an element available, so we can use it
@@ -3574,8 +3595,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
if (isStatic) return _propertyName(name);
if (name.startsWith('_')) {
- return _privateNames.putIfAbsent(
- name, () => _initSymbol(new JS.TemporaryId(name)) as JS.TemporaryId);
+ return _emitPrivateNameSymbol(currentLibrary, name);
}
if (name == '[]') {
@@ -3605,20 +3625,29 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
return _propertyName(name);
}
+ JS.TemporaryId _emitPrivateNameSymbol(LibraryElement library, String name) {
+ return _privateNames
+ .putIfAbsent(library, () => new HashMap())
+ .putIfAbsent(name, () {
+ var id = new JS.TemporaryId(name);
+ _moduleItems.add(
+ js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")]));
+ return id;
+ });
+ }
+
bool _externalOrNative(node) =>
node.externalKeyword != null || _functionBody(node) is NativeFunctionBody;
FunctionBody _functionBody(node) =>
node is FunctionDeclaration ? node.functionExpression.body : node.body;
- /// Choose a canonical name from the library element.
- /// This never uses the library's name (the identifier in the `library`
- /// declaration) as it doesn't have any meaningful rules enforced.
+ /// Returns the canonical name to refer to the Dart library.
JS.Identifier emitLibraryName(LibraryElement library) {
- if (library == currentLibrary) return _exportsVar;
- if (library.name == 'dart._runtime') return _runtimeLibVar;
- return _imports.putIfAbsent(
- library, () => new JS.TemporaryId(jsLibraryName(library)));
+ // It's either one of the libraries in this module, or it's an import.
+ return _libraries[library] ??
+ _imports.putIfAbsent(
+ library, () => new JS.TemporaryId(jsLibraryName(library)));
}
JS.Node annotate(JS.Node node, AstNode original, [Element element]) {
@@ -3635,15 +3664,15 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
///
/// JSNumber is the type that actually "implements" all numbers, hence it's
/// a subtype of int and double (and num). It's in our "dart:_interceptors".
- bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, _types.numType);
+ bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, types.numType);
bool _isObjectGetter(String name) {
- PropertyAccessorElement element = _types.objectType.element.getGetter(name);
+ PropertyAccessorElement element = types.objectType.element.getGetter(name);
return (element != null && !element.isStatic);
}
bool _isObjectMethod(String name) {
- MethodElement element = _types.objectType.element.getMethod(name);
+ MethodElement element = types.objectType.element.getMethod(name);
return (element != null && !element.isStatic);
}
@@ -3664,16 +3693,16 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
if (element.isAsynchronous) {
if (element.isGenerator) {
// Stream<T> -> T
- expectedType = _types.streamType;
+ expectedType = types.streamType;
} else {
// Future<T> -> T
// TODO(vsm): Revisit with issue #228.
- expectedType = _types.futureType;
+ expectedType = types.futureType;
}
} else {
if (element.isGenerator) {
// Iterable<T> -> T
- expectedType = _types.iterableType;
+ expectedType = types.iterableType;
} else {
// T -> T
return type;
@@ -3691,129 +3720,12 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
}
}
-class ExtensionTypeSet extends GeneralizingElementVisitor {
- final AnalysisContext _context;
- final TypeProvider _types;
-
- final _extensionTypes = new HashSet<ClassElement>();
- final _pendingLibraries = new HashSet<String>();
-
- ExtensionTypeSet(AbstractCompiler compiler)
- : _context = compiler.context,
- _types = compiler.context.typeProvider;
-
- visitClassElement(ClassElement element) {
- if (findAnnotation(element, isJsPeerInterface) != null ||
- findAnnotation(element, isNativeAnnotation) != null) {
- _addExtensionType(element.type);
- }
- }
-
- void _addExtensionType(InterfaceType t) {
- if (t.isObject || !_extensionTypes.add(t.element)) return;
- t = fillDynamicTypeArgs(t, _types) as InterfaceType;
- t.interfaces.forEach(_addExtensionType);
- t.mixins.forEach(_addExtensionType);
- _addExtensionType(t.superclass);
- }
-
- void _addExtensionTypesForLibrary(String libraryUri, List<String> typeNames) {
- var sourceFactory = _context.sourceFactory.forUri(libraryUri);
- var library = _context.computeLibraryElement(sourceFactory);
- for (var typeName in typeNames) {
- var element = library.getType(typeName);
- _addExtensionType(element.type);
- }
- }
-
- void _addExtensionTypes(String libraryUri) {
- var sourceFactory = _context.sourceFactory.forUri(libraryUri);
- var library = _context.computeLibraryElement(sourceFactory);
- visitLibraryElement(library);
- }
-
- void _addPendingExtensionTypes(String libraryUri) {
- _pendingLibraries.add(libraryUri);
- }
-
- bool contains(Element element) {
- if (_extensionTypes.contains(element)) return true;
- if (_pendingLibraries.isEmpty) return false;
- if (element is ClassElement) {
- var uri = element.library.source.uri.toString();
- if (_pendingLibraries.contains(uri)) {
- // Load all pending libraries
- for (var libraryUri in _pendingLibraries) {
- _addExtensionTypes(libraryUri);
- }
- _pendingLibraries.clear();
- return _extensionTypes.contains(element);
- }
- }
- return false;
- }
-}
-
-class JSGenerator {
- final AbstractCompiler compiler;
- final ExtensionTypeSet _extensionTypes;
- final TypeProvider _types;
-
- JSGenerator(AbstractCompiler compiler)
- : compiler = compiler,
- _types = compiler.context.typeProvider,
- _extensionTypes = new ExtensionTypeSet(compiler) {
- // TODO(vsm): Eventually, we want to make this extensible - i.e., find
- // annotations in user code as well. It would need to be summarized in
- // the element model - not searched this way on every compile. To make this
- // a little more efficient now, we do this in two phases.
-
- // First, core types:
- _extensionTypes._addExtensionTypes('dart:_interceptors');
- _extensionTypes._addExtensionTypes('dart:_native_typed_data');
- // TODO(vsm): If we're analyzing against the main SDK, those
- // types are not explicitly annotated.
- _extensionTypes._addExtensionType(_types.intType);
- _extensionTypes._addExtensionType(_types.doubleType);
- _extensionTypes._addExtensionType(_types.boolType);
- _extensionTypes._addExtensionType(_types.stringType);
- // These are used natively by dart:html but also not annotated.
- _extensionTypes
- ._addExtensionTypesForLibrary('dart:core', ['Comparable', 'Map']);
- _extensionTypes
- ._addExtensionTypesForLibrary('dart:collection', ['ListMixin']);
- _extensionTypes._addExtensionTypesForLibrary('dart:math', ['Rectangle']);
-
- // Second, html types - these are only searched if we use dart:html, etc.:
- _extensionTypes._addPendingExtensionTypes('dart:html');
- _extensionTypes._addPendingExtensionTypes('dart:indexed_db');
- _extensionTypes._addPendingExtensionTypes('dart:svg');
- _extensionTypes._addPendingExtensionTypes('dart:web_audio');
- _extensionTypes._addPendingExtensionTypes('dart:web_gl');
- _extensionTypes._addPendingExtensionTypes('dart:web_sql');
- }
-
- void generateLibrary(List<CompilationUnit> units) {
- var library = units.first.element.library;
- var fields =
- findFieldsNeedingStorage(units.map((c) => c.element), _extensionTypes);
- var rules = new StrongTypeSystemImpl();
- var codegen =
- new JSCodegenVisitor(compiler, rules, library, _extensionTypes, fields);
- var module = codegen.emitLibrary(units);
- var out = compiler.getOutputPath(library.source.uri);
- var options = compiler.options.codegenOptions;
- writeJsLibrary(module, out, compiler.inputBaseDir,
- emitTypes: options.closure,
- emitSourceMaps: options.emitSourceMaps,
- fileSystem: compiler.fileSystem);
- }
-}
-
/// Choose a canonical name from the library element.
/// This never uses the library's name (the identifier in the `library`
/// declaration) as it doesn't have any meaningful rules enforced.
-String jsLibraryName(LibraryElement library) => canonicalLibraryName(library);
+String jsLibraryName(LibraryElement library) {
+ return pathToJSIdentifier(library.source.uri.pathSegments.last);
+}
/// Shorthand for identifier-like property names.
/// For now, we emit them as strings and the printer restores them to
@@ -3835,3 +3747,12 @@ class TemporaryVariableElement extends LocalVariableElementImpl {
int get hashCode => identityHashCode(this);
bool operator ==(Object other) => identical(this, other);
}
+
+bool isLibraryPrefix(Expression node) =>
+ node is SimpleIdentifier && node.staticElement is PrefixElement;
+
+LibraryElement _getLibrary(AnalysisContext c, String uri) =>
+ c.computeLibraryElement(c.sourceFactory.forUri(uri));
+
+bool _isDartRuntime(LibraryElement l) =>
+ l.isInSdk && l.source.uri.toString() == 'dart:_runtime';
« no previous file with comments | « lib/src/compiler/ast_builder.dart ('k') | lib/src/compiler/command.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698