| 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';
|
|
|