| Index: lib/src/codegen/js_codegen.dart
|
| diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart
|
| index fc9137af6d91e2596ddc4aa528ccb6c9f9bc514e..92c0efe3786ab67fb6193263ea29adf57a93c1fb 100644
|
| --- a/lib/src/codegen/js_codegen.dart
|
| +++ b/lib/src/codegen/js_codegen.dart
|
| @@ -55,8 +55,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| final TypeRules rules;
|
| final LibraryInfo libraryInfo;
|
|
|
| - /// The global extension method table.
|
| - final HashMap<String, List<InterfaceType>> _extensionMethods;
|
| + /// The global extension type table.
|
| + final HashSet<ClassElement> _extensionTypes;
|
|
|
| /// Information that is precomputed for this library, indicates which fields
|
| /// need storage slots.
|
| @@ -90,7 +90,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| ModuleItemLoadOrder _loader;
|
|
|
| JSCodegenVisitor(AbstractCompiler compiler, this.libraryInfo,
|
| - this._extensionMethods, this._fieldsNeedingStorage)
|
| + this._extensionTypes, this._fieldsNeedingStorage)
|
| : compiler = compiler,
|
| options = compiler.options,
|
| rules = compiler.rules {
|
| @@ -559,6 +559,20 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| new JS.ArrayInitializer(
|
| classElem.interfaces.map(_emitTypeName).toList())
|
| ]));
|
| +
|
| + // If a concrete class implements one of our extensions, we might need to
|
| + // add forwarders.
|
| + var extensions = _extensionsToImplement(classElem);
|
| + if (extensions.isNotEmpty) {
|
| + // Get the dynamic substitution of the extension interface, as we only
|
| + // care about what methods it has, not their implementation.
|
| + var jsTypes = extensions
|
| + .map((e) => _emitTypeName(fillDynamicTypeArgs(e.type, types)));
|
| + body.add(js.statement('dart.implementExtension(#, () => #);', [
|
| + name,
|
| + new JS.ArrayInitializer(jsTypes.toList(growable: false))
|
| + ]));
|
| + }
|
| }
|
|
|
| // Named constructors
|
| @@ -597,7 +611,10 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| inheritedElement.type == element.type) continue;
|
| var unary = node.parameters.parameters.isEmpty;
|
| var memberName = _emitMemberName(name,
|
| - type: cType, unary: unary, isStatic: node.isStatic);
|
| + type: cType,
|
| + unary: unary,
|
| + isStatic: node.isStatic,
|
| + declaration: true);
|
| var parts =
|
| _emitFunctionTypeParts(element.type, dynamicIsBottom: false);
|
| var property =
|
| @@ -645,6 +662,39 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| return _statement(body);
|
| }
|
|
|
| + Set<ClassElement> _extensionsToImplement(ClassElement element) {
|
| + var extensions = new Set<ClassElement>();
|
| + if (_extensionTypes.contains(element)) return extensions;
|
| +
|
| + // Collect all extension types we implement.
|
| + _collectExtensions(element.type, extensions);
|
| + if (extensions.isEmpty) return extensions;
|
| +
|
| + // Filter out extensions implemented by our superclass or mixins.
|
| + var s = element.supertype.element;
|
| + if (!s.isAbstract) extensions.removeAll(_extensionsToImplement(s));
|
| + for (var mixin in element.mixins) {
|
| + var m = mixin.element;
|
| + if (!m.isAbstract) extensions.removeAll(_extensionsToImplement(m));
|
| + }
|
| + return extensions;
|
| + }
|
| +
|
| + /// Collections the type and all supertypes, including interfaces, but
|
| + /// excluding [Object].
|
| + void _collectExtensions(InterfaceType type, Set<ClassElement> types) {
|
| + if (type.isObject) return;
|
| + var element = type.element;
|
| + if (_extensionTypes.contains(element)) types.add(element);
|
| + for (var m in type.mixins.reversed) {
|
| + _collectExtensions(m, types);
|
| + }
|
| + for (var i in type.interfaces) {
|
| + _collectExtensions(i, types);
|
| + }
|
| + _collectExtensions(type.superclass, types);
|
| + }
|
| +
|
| JS.Statement _overrideField(FieldElement e) {
|
| var cls = e.enclosingElement;
|
| return js.statement('dart.virtualField(#, #)', [
|
| @@ -994,7 +1044,10 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| if (params == null) params = [];
|
|
|
| var memberName = _emitMemberName(node.name.name,
|
| - type: type, unary: params.isEmpty, isStatic: node.isStatic);
|
| + type: type,
|
| + unary: params.isEmpty,
|
| + isStatic: node.isStatic,
|
| + declaration: true);
|
| return new JS.Method(memberName, new JS.Fun(params, _visit(node.body)),
|
| isGetter: node.isGetter,
|
| isSetter: node.isSetter,
|
| @@ -1666,8 +1719,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| @override
|
| visitConstructorName(ConstructorName node) {
|
| var typeName = _visit(node.type);
|
| - if (node.name != null || node.staticElement.isFactory) {
|
| - var namedCtor = _constructorName(node.staticElement);
|
| + var element = node.staticElement;
|
| + if (node.name != null || element.isFactory) {
|
| + var namedCtor = _constructorName(element);
|
| return new JS.PropertyAccess(typeName, namedCtor);
|
| }
|
| return typeName;
|
| @@ -1676,9 +1730,22 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| @override
|
| visitInstanceCreationExpression(InstanceCreationExpression node) {
|
| emitNew() {
|
| - var ctor = _visit(node.constructorName);
|
| + JS.Expression ctor;
|
| + bool isFactory = false;
|
| + var element = node.staticElement;
|
| + if (element == null) {
|
| + // TODO(jmesserly): this only happens if we had a static error.
|
| + // Should we generate a throw instead?
|
| + ctor = _visit(node.constructorName.type);
|
| + var ctorName = node.constructorName.name;
|
| + if (ctorName != null) {
|
| + ctor = new JS.PropertyAccess(ctor, _propertyName(ctorName.name));
|
| + }
|
| + } else {
|
| + ctor = _visit(node.constructorName);
|
| + isFactory = element.isFactory;
|
| + }
|
| var args = _visit(node.argumentList);
|
| - var isFactory = node.staticElement.isFactory;
|
| return isFactory ? new JS.Call(ctor, args) : new JS.New(ctor, args);
|
| }
|
| if (node.isConst) return _emitConst(node, emitNew);
|
| @@ -2338,8 +2405,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| emitList() {
|
| JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements));
|
| ParameterizedType type = node.staticType;
|
| - if (type.typeArguments.any((a) => a != types.dynamicType)) {
|
| - list = js.call('dart.setType(#, #)', [list, _emitTypeName(type)]);
|
| + var elementType = type.typeArguments.single;
|
| + if (elementType != types.dynamicType) {
|
| + list = js.call('dart.list(#, #)', [list, _emitTypeName(elementType)]);
|
| }
|
| return list;
|
| }
|
| @@ -2488,8 +2556,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| /// Equality is a bit special, it is generated via the Dart `equals` runtime
|
| /// helper, that checks for null. The user defined method is called '=='.
|
| ///
|
| - JS.Expression _emitMemberName(String name,
|
| - {DartType type, bool unary: false, bool isStatic: false}) {
|
| + JS.Expression _emitMemberName(String name, {DartType type, bool unary: false,
|
| + bool isStatic: false, bool declaration: false}) {
|
|
|
| // Static members skip the rename steps.
|
| if (isStatic) return _propertyName(name);
|
| @@ -2499,9 +2567,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| name, () => _initSymbol(new JS.TemporaryId(name)));
|
| }
|
|
|
| - // Check for extension method:
|
| - var extLibrary = _findExtensionLibrary(name, type);
|
| -
|
| if (name == '[]') {
|
| name = 'get';
|
| } else if (name == '[]=') {
|
| @@ -2510,46 +2575,15 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| name = 'unary-';
|
| }
|
|
|
| - if (extLibrary != null) {
|
| - return _extensionMethodName(name, extLibrary);
|
| + // Dart "extension" methods. Used for JS Array, Boolean, Number, String.
|
| + if (!declaration && _extensionTypes.contains(type.element)) {
|
| + // Special case `length`. We can call it directly.
|
| + if (name != 'length') return js.call('dartx.#', _propertyName(name));
|
| }
|
|
|
| return _propertyName(name);
|
| }
|
|
|
| - LibraryElement _findExtensionLibrary(String name, DartType type) {
|
| - if (type is! InterfaceType) return null;
|
| -
|
| - var extLibrary = null;
|
| - var extensionTypes = _extensionMethods[name];
|
| - if (extensionTypes != null) {
|
| - // Normalize the type to ignore generics.
|
| - type = fillDynamicTypeArgs(type, types);
|
| - for (var t in extensionTypes) {
|
| - if (rules.isSubTypeOf(type, t)) {
|
| - assert(extLibrary == null || extLibrary == t.element.library);
|
| - extLibrary = t.element.library;
|
| - }
|
| - }
|
| - }
|
| - return extLibrary;
|
| - }
|
| -
|
| - JS.Expression _extensionMethodName(String name, LibraryElement library) {
|
| - var extName = '\$$name';
|
| - if (library == currentLibrary) {
|
| - // TODO(jacobr): need to do a better job ensuring that extension method
|
| - // name symbols do not conflict with other symbols before we can let
|
| - // user defined libraries define extension methods.
|
| - if (_extensionMethodNames.add(extName)) {
|
| - _initSymbol(new JS.Identifier(extName));
|
| - _addExport(extName);
|
| - }
|
| - return new JS.Identifier(extName);
|
| - }
|
| - return js.call('#.#', [_libraryName(library), _propertyName(extName)]);
|
| - }
|
| -
|
| bool _externalOrNative(node) =>
|
| node.externalKeyword != null || _functionBody(node) is NativeFunctionBody;
|
|
|
| @@ -2569,35 +2603,31 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| }
|
|
|
| class JSGenerator extends CodeGenerator {
|
| - /// For fast lookup of extension methods, we first check the name, then do a
|
| - /// (possibly expensive) subtype test to see if it matches one of the types
|
| - /// that declares that method.
|
| - final _extensionMethods = new HashMap<String, List<InterfaceType>>();
|
| -
|
| - JSGenerator(AbstractCompiler context) : super(context) {
|
| + final _extensionTypes = new HashSet<ClassElement>();
|
|
|
| + JSGenerator(AbstractCompiler compiler) : super(compiler) {
|
| // TODO(jacobr): determine the the set of types with extension methods from
|
| // the annotations rather than hard coding the list once the analyzer
|
| // supports summaries.
|
| - var extensionTypes = [types.listType, types.iterableType];
|
| - for (var type in extensionTypes) {
|
| - type = fillDynamicTypeArgs(type, rules.provider);
|
| - var e = type.element;
|
| - var names = new HashSet<String>()
|
| - ..addAll(e.methods.map((m) => m.name))
|
| - ..addAll(e.accessors.map((m) => m.name));
|
| - for (var name in names) {
|
| - _extensionMethods.putIfAbsent(name, () => []).add(type);
|
| - }
|
| + var context = compiler.context;
|
| + var src = context.sourceFactory.forUri('dart:_interceptors');
|
| + var interceptors = context.computeLibraryElement(src);
|
| + for (var t in ['JSArray', 'JSString', 'JSInt', 'JSDouble', 'JSBool']) {
|
| + _addExtensionType(interceptors.getType(t).type);
|
| }
|
| }
|
|
|
| - TypeProvider get types => rules.provider;
|
| + void _addExtensionType(InterfaceType t) {
|
| + if (t.isObject || !_extensionTypes.add(t.element)) return;
|
| + t = fillDynamicTypeArgs(t, rules.provider);
|
| + t.interfaces.forEach(_addExtensionType);
|
| + t.mixins.forEach(_addExtensionType);
|
| + _addExtensionType(t.superclass);
|
| + }
|
|
|
| String generateLibrary(LibraryUnit unit, LibraryInfo info) {
|
| var fields = findFieldsNeedingStorage(unit);
|
| - var codegen =
|
| - new JSCodegenVisitor(compiler, info, _extensionMethods, fields);
|
| + var codegen = new JSCodegenVisitor(compiler, info, _extensionTypes, fields);
|
| var module = codegen.emitLibrary(unit);
|
| var dir = path.join(outDir, jsOutputPath(info, root));
|
| return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps);
|
|
|