Chromium Code Reviews| Index: pkg/dev_compiler/lib/src/compiler/code_generator.dart |
| diff --git a/pkg/dev_compiler/lib/src/compiler/code_generator.dart b/pkg/dev_compiler/lib/src/compiler/code_generator.dart |
| index 1f3a6f30c1b63e75227d297fe185ace66ffdc08a..aadac672dc3cdd0e4b221d237017dff1c79b193f 100644 |
| --- a/pkg/dev_compiler/lib/src/compiler/code_generator.dart |
| +++ b/pkg/dev_compiler/lib/src/compiler/code_generator.dart |
| @@ -382,81 +382,51 @@ class CodeGenerator extends Object |
| return new JS.ObjectInitializer(properties, multiline: true); |
| } |
| - List<String> _getJSName(Element e) { |
| - if (e.library == null || |
| - findAnnotation(e.library, isPublicJSAnnotation) == null) { |
| - return null; |
| - } |
| + String _getJSNameWithoutGlobal(Element e) { |
|
vsm
2017/06/30 18:08:47
This will still support something like:
@JS('foo.
Jennifer Messerly
2017/07/05 21:17:16
exactly, it just skips the redundant step. I belie
|
| + if (e.library == null || !_isJSNative(e.library)) return null; |
| + bool isJS = |
| + e is PropertyAccessorElement && e.variable is TopLevelVariableElement |
| + ? e.isExternal |
| + : _isJSNative(e); |
|
vsm
2017/06/30 18:08:47
Maybe this should e.isExternal || _isJSNative(e) ?
Jennifer Messerly
2017/07/05 21:17:16
That's what I was wondering too. This is exactly t
|
| + if (!isJS) return null; |
| var libraryJSName = getAnnotationName(e.library, isPublicJSAnnotation); |
| - var libraryPrefix = <String>[]; |
| - if (libraryJSName != null && libraryJSName.isNotEmpty) { |
| - libraryPrefix.addAll(libraryJSName.split('.')); |
| - } |
| - |
| - var original = e; |
| - var variableElement = e; |
| - if (original is PropertyAccessorElement) { |
| - variableElement = original.variable; |
| - if (original.isSynthetic) e = variableElement; |
| - } |
| - |
| - String elementJSName; |
| - if (findAnnotation(e, isPublicJSAnnotation) != null) { |
| - elementJSName = getAnnotationName(e, isPublicJSAnnotation) ?? ''; |
| - } |
| - |
| - if (variableElement is TopLevelVariableElement) { |
| - elementJSName = _jsInteropStaticMemberName(original); |
| - } |
| - if (elementJSName == null) return null; |
| - |
| - var elementJSParts = <String>[]; |
| - if (elementJSName.isNotEmpty) { |
| - elementJSParts.addAll(elementJSName.split('.')); |
| - } else { |
| - elementJSParts.add(e.name); |
| - } |
| - |
| - return libraryPrefix..addAll(elementJSParts); |
| + var jsName = getAnnotationName(e, isPublicJSAnnotation); |
| + jsName ??= e is PropertyAccessorElement ? e.variable.name : e.name; |
| + return libraryJSName != null ? '$libraryJSName.$jsName' : jsName; |
| } |
| JS.Expression _emitJSInterop(Element e) { |
| - var jsName = _getJSName(e); |
| - if (jsName == null) return null; |
| - var fullName = ['global']..addAll(jsName); |
| - JS.Expression access = _runtimeModule; |
| - for (var part in fullName) { |
| - access = new JS.PropertyAccess(access, js.string(part)); |
| - } |
| - return access; |
| + var jsName = _getJSNameWithoutGlobal(e); |
| + return jsName != null ? _emitJSInteropForJSName(jsName) : null; |
| } |
| - String _jsInteropStaticMemberName(Element e) { |
| - if (e?.library == null || |
| - findAnnotation(e.library, isPublicJSAnnotation) == null) { |
| - return null; |
| - } |
| - if (e is ExecutableElement && e.isExternal) { |
| - return getAnnotationName(e, isPublicJSAnnotation) ?? |
| - (e is PropertyAccessorElement ? e.variable : e).name; |
| + JS.Expression _emitJSInteropForJSName(String name) { |
| + var access = _callHelper('global'); |
| + for (var part in name.split('.')) { |
| + access = new JS.PropertyAccess(access, js.escapedString(part, "'")); |
| } |
| - if (e is PropertyInducingElement && e.getter.isExternal) { |
| - return getAnnotationName(e, isPublicJSAnnotation) ?? e.name; |
| - } |
| - return null; |
| + return access; |
| } |
| - String _emitJSInteropStaticMemberName(Element e) { |
| - var name = _jsInteropStaticMemberName(e); |
| + JS.Expression _emitJSInteropStaticMemberName(Element e) { |
| + if (e?.library == null || !_isJSNative(e.library)) return null; |
| + bool isJS = e is ExecutableElement && e.isExternal || |
| + e is PropertyInducingElement && e.getter.isExternal; |
| + if (!isJS) return null; |
| + var name = getAnnotationName(e, isPublicJSAnnotation); |
| // We do not support statics names with JS annotations containing dots. |
| // See https://github.com/dart-lang/sdk/issues/27926 |
| - if (name != null && name.contains('.')) { |
| - throw new UnsupportedError( |
| - 'We do not support JS annotations containing dots on static members. ' |
| - 'See https://github.com/dart-lang/sdk/issues/27926'); |
| + if (name != null) { |
| + if (name.contains('.')) { |
| + throw new UnsupportedError( |
| + 'We do not support JS annotations containing dots on static members. ' |
| + 'See https://github.com/dart-lang/sdk/issues/27926'); |
| + } |
| + } else { |
| + name = e is PropertyAccessorElement ? e.variable.name : e.name; |
| } |
| - return name; |
| + return js.escapedString(name, "'"); |
| } |
| /// Flattens blocks in [items] to a single list. |
| @@ -849,7 +819,7 @@ class CodeGenerator extends Object |
| return _statement(block); |
| } |
| - JS.Statement _emitJsType(Element e) { |
| + JS.Statement _emitJSType(Element e) { |
| var jsTypeName = getAnnotationName(e, isJSAnnotation); |
| if (jsTypeName == null || jsTypeName == e.name) return null; |
| @@ -868,7 +838,7 @@ class CodeGenerator extends Object |
| if (findAnnotation(classElem, isPublicJSAnnotation) != null) return null; |
| // If this is a JavaScript type, emit it now and then exit. |
| - var jsTypeDef = _emitJsType(classElem); |
| + var jsTypeDef = _emitJSType(classElem); |
| if (jsTypeDef != null) return jsTypeDef; |
| var ctors = <ConstructorDeclaration>[]; |
| @@ -1691,13 +1661,10 @@ class CodeGenerator extends Object |
| JS.Statement _setBaseClass(ClassElement classElem, JS.Expression className, |
| List<String> jsPeerNames, List<JS.Statement> body) { |
| var typeFormals = classElem.typeParameters; |
| - if (jsPeerNames.isNotEmpty && typeFormals.isNotEmpty) { |
| - for (var peer in jsPeerNames) { |
| - // TODO(jmesserly): we should just extend Array in the first place |
| - var newBaseClass = _callHelper('global.#', [peer]); |
| - body.add(_callHelperStatement( |
| - 'setExtensionBaseClass(#, #);', [className, newBaseClass])); |
| - } |
| + if (jsPeerNames.length == 1 && typeFormals.isNotEmpty) { |
| + var newBaseClass = _callHelper('global.#', jsPeerNames[0]); |
| + body.add(_callHelperStatement( |
| + 'setExtensionBaseClass(#, #);', [className, newBaseClass])); |
|
vsm
2017/06/30 18:08:47
Ha - I was just trying to figure why JSArray's pro
Jennifer Messerly
2017/07/05 21:17:16
yup! it's pretty crazy ... hope it's a tad better
|
| } else if (_hasDeferredSupertype.contains(classElem)) { |
| // TODO(vsm): consider just threading the deferred supertype through |
| // instead of recording classElem in a set on the class and recomputing |
| @@ -2752,9 +2719,7 @@ class CodeGenerator extends Object |
| JS.Expression _emitSimpleIdentifier(SimpleIdentifier node) { |
| var accessor = resolutionMap.staticElementForIdentifier(node); |
| if (accessor == null) { |
| - return _callHelper( |
| - 'throw(Error("compile error: unresolved identifier: " + #))', |
| - js.escapedString(node.name ?? '<null>')); |
| + return _throwUnsafe('unresolved identifier: ' + (node.name ?? '<null>')); |
| } |
| // Get the original declaring element. If we had a property accessor, this |
| @@ -2799,17 +2764,10 @@ class CodeGenerator extends Object |
| // For instance members, we add implicit-this. |
| // For method tear-offs, we ensure it's a bound method. |
| - var tearOff = element is MethodElement && !inInvocationContext(node); |
| - if (tearOff) { |
| - // To be safe always use the symbolized name when binding on a native |
| - // class as bind assumes the name will match the name class sigatures |
| - // which is symbolized for native classes. |
| - var safeName = _emitMemberName(name, |
| - isStatic: isStatic, |
| - type: type, |
| - element: accessor, |
| - alwaysSymbolizeNative: true); |
| - return _callHelper('bind(this, #)', safeName); |
| + if (element is MethodElement && |
| + !inInvocationContext(node) && |
| + !_isJSNative(element.enclosingElement)) { |
| + return _callHelper('bind(this, #)', member); |
| } |
| return js.call('this.#', member); |
| } |
| @@ -3072,7 +3030,6 @@ class CodeGenerator extends Object |
| _declareBeforeUse(element); |
| } |
| - var interop = _emitJSInterop(element); |
| // Type parameters don't matter as JS interop types cannot be reified. |
| // We have to use lazy JS types because until we have proper module |
| // loading for JS libraries bundled with Dart libraries, we will sometimes |
| @@ -3086,14 +3043,13 @@ class CodeGenerator extends Object |
| // object to represent MyType. |
| // Anonymous JS types do not have a corresponding concrete JS type so we |
| // have to use a helper to define them. |
| - if (interop != null) { |
| - if (_isObjectLiteral(element)) { |
| - return _callHelper( |
| - 'lazyAnonymousJSType(#)', js.string(element.displayName)); |
| - } else { |
| - return _callHelper('lazyJSType(() => #, #)', |
| - [interop, js.string(_getJSName(element).join('.'))]); |
| - } |
| + if (_isObjectLiteral(element)) { |
| + return _callHelper('anonymousJSType(#)', js.escapedString(element.name)); |
| + } |
| + var jsName = _getJSNameWithoutGlobal(element); |
| + if (jsName != null) { |
| + return _callHelper('lazyJSType(() => #, #)', |
| + [_emitJSInteropForJSName(jsName), js.escapedString(jsName)]); |
| } |
| // TODO(jmesserly): like constants, should we hoist function types out of |
| @@ -3128,7 +3084,7 @@ class CodeGenerator extends Object |
| jsArgs = []; |
| } |
| if (jsArgs != null) { |
| - var genericName = _emitTopLevelName(element, suffix: '\$'); |
| + var genericName = _emitTopLevelNameNoInterop(element, suffix: '\$'); |
| var typeRep = js.call('#(#)', [genericName, jsArgs]); |
| return nameType |
| ? _typeTable.nameType(type, typeRep, hoistType: hoistType) |
| @@ -3140,15 +3096,14 @@ class CodeGenerator extends Object |
| } |
| JS.PropertyAccess _emitTopLevelName(Element e, {String suffix: ''}) { |
| - var interop = _emitJSInterop(e); |
| - if (interop != null) return interop; |
| - return _emitTopLevelNameNoInterop(e, suffix: suffix); |
| + return _emitJSInterop(e) ?? _emitTopLevelNameNoInterop(e, suffix: suffix); |
| } |
| JS.PropertyAccess _emitTopLevelNameNoInterop(Element e, {String suffix: ''}) { |
| - String name = getJSExportName(e) + suffix; |
| + var name = getJSExportName(e) ?? |
| + (e is PropertyAccessorElement ? e.variable.name : e.name); |
|
vsm
2017/06/30 18:08:47
Consider factoring out - this guarded expr seems f
Jennifer Messerly
2017/07/05 21:17:16
Done.
|
| return new JS.PropertyAccess( |
| - emitLibraryName(e.library), _propertyName(name)); |
| + emitLibraryName(e.library), _propertyName(name + suffix)); |
| } |
| @override |
| @@ -3446,15 +3401,13 @@ class CodeGenerator extends Object |
| JS.Expression _getSuperHelper(Element member, JS.Expression jsName) { |
| var jsMethod = _superHelpers.putIfAbsent(member.name, () { |
| if (member is PropertyAccessorElement) { |
| - var field = member.variable as FieldElement; |
| - var name = field.name; |
| var isSetter = member.isSetter; |
| var fn = js.call( |
| isSetter |
| ? 'function(x) { super[#] = x; }' |
| : 'function() { return super[#]; }', |
| [jsName]); |
| - return new JS.Method(new JS.TemporaryId(name), fn, |
| + return new JS.Method(new JS.TemporaryId(member.variable.name), fn, |
| isGetter: !isSetter, isSetter: isSetter); |
| } else { |
| var method = member as MethodElement; |
| @@ -3490,7 +3443,7 @@ class CodeGenerator extends Object |
| var element = node.methodName.staticElement; |
| bool isStatic = element is ExecutableElement && element.isStatic; |
| var name = node.methodName.name; |
| - var memberName = |
| + var jsName = |
| _emitMemberName(name, type: type, isStatic: isStatic, element: element); |
| JS.Expression jsTarget = _emitTarget(target, element, isStatic); |
| @@ -3500,19 +3453,19 @@ class CodeGenerator extends Object |
| _emitDynamicOperationName('dgsend'), |
| jsTarget, |
| new JS.ArrayInitializer(typeArgs), |
| - memberName, |
| + jsName, |
| args |
| ]); |
| } else { |
| return _callHelper('#(#, #, #)', |
| - [_emitDynamicOperationName('dsend'), jsTarget, memberName, args]); |
| + [_emitDynamicOperationName('dsend'), jsTarget, jsName, args]); |
| } |
| } |
| if (_isObjectMemberCall(target, name)) { |
| assert(typeArgs == null); // Object methods don't take type args. |
| return _callHelper('#(#, #)', [name, jsTarget, args]); |
| } |
| - jsTarget = _emitTargetAccess(jsTarget, memberName, element); |
| + jsTarget = _emitTargetAccess(jsTarget, jsName, element); |
| if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); |
| return new JS.Call(jsTarget, args); |
| @@ -3616,7 +3569,7 @@ class CodeGenerator extends Object |
| var args = node.argumentList.arguments; |
| // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` |
| var code = args[1]; |
| - List<AstNode> templateArgs; |
| + List<Expression> templateArgs; |
| String source; |
| if (code is StringInterpolation) { |
| if (args.length > 2) { |
| @@ -3659,6 +3612,27 @@ class CodeGenerator extends Object |
| } |
| } |
| + JS.Expression visitTemplateArg(Expression arg) { |
| + if (arg is InvocationExpression) { |
| + var e = arg is MethodInvocation |
| + ? arg.methodName.staticElement |
| + : (arg as FunctionExpressionInvocation).staticElement; |
| + if (e?.name == 'getGenericClass' && |
| + e.library.name == 'dart._runtime' && |
| + arg.argumentList.arguments.length == 1) { |
| + var typeArg = arg.argumentList.arguments[0]; |
| + if (typeArg is SimpleIdentifier) { |
| + var typeElem = typeArg.staticElement; |
| + if (typeElem is TypeDefiningElement && |
| + typeElem.type is ParameterizedType) { |
| + return _emitTopLevelNameNoInterop(typeElem, suffix: '\$'); |
| + } |
| + } |
| + } |
| + } |
| + return _visit(arg); |
| + } |
| + |
| // TODO(rnystrom): The JS() calls are almost never nested, and probably |
| // really shouldn't be, but there are at least a couple of calls in the |
| // HTML library where an argument to JS() is itself a JS() call. If those |
| @@ -3667,12 +3641,11 @@ class CodeGenerator extends Object |
| // wrapped Type object. |
| var wasInForeignJS = _isInForeignJS; |
| _isInForeignJS = true; |
| - |
| - var template = js.parseForeignJS(source); |
| - var result = template.instantiate(_visitList(templateArgs)); |
| - |
| + var jsArgs = templateArgs.map(visitTemplateArg).toList(); |
| _isInForeignJS = wasInForeignJS; |
| + var result = js.parseForeignJS(source).instantiate(jsArgs); |
| + |
| // `throw` is emitted as a statement by `parseForeignJS`. |
| assert(result is JS.Expression || node.parent is ExpressionStatement); |
| return result; |
| @@ -3999,13 +3972,8 @@ class CodeGenerator extends Object |
| bool isFactory = false; |
| bool isNative = false; |
| if (element == null) { |
| - ctor = _callHelper( |
| - 'throw(Error("compile error: unresolved constructor: " ' |
| - '+ # + "." + #))', |
| - [ |
| - js.escapedString(type?.name ?? '<null>'), |
| - js.escapedString(name?.name ?? '<unnamed>') |
| - ]); |
| + ctor = _throwUnsafe('unresolved constructor: ${type?.name ?? '<null>'}' |
| + '.${name?.name ?? '<unnamed>'}'); |
| } else { |
| ctor = _emitConstructorName(element, type, name); |
| isFactory = element.isFactory; |
| @@ -4026,13 +3994,13 @@ class CodeGenerator extends Object |
| return emitNew(); |
| } |
| - bool _isObjectLiteral(ClassElement classElem) { |
| - return findAnnotation(classElem, isPublicJSAnnotation) != null && |
| + bool _isObjectLiteral(Element classElem) { |
| + return _isJSNative(classElem) && |
| findAnnotation(classElem, isJSAnonymousAnnotation) != null; |
| } |
| - bool _isJSNative(ClassElement classElem) => |
| - findAnnotation(classElem, isPublicJSAnnotation) != null; |
| + bool _isJSNative(Element e) => |
| + findAnnotation(e, isPublicJSAnnotation) != null; |
| JS.Expression _emitObjectLiteral(ArgumentList argumentList) { |
| var args = _emitArgumentList(argumentList); |
| @@ -4290,20 +4258,20 @@ class CodeGenerator extends Object |
| if (expr.value >= low && expr.value <= high) return expr.value; |
| return null; |
| } |
| - int finishIdentifier(SimpleIdentifier identifier) { |
| - Element staticElement = identifier.staticElement; |
| - if (staticElement is PropertyAccessorElement && staticElement.isGetter) { |
| - PropertyInducingElement variable = staticElement.variable; |
| - int value = variable?.computeConstantValue()?.toIntValue(); |
| - if (value != null && value >= low && value <= high) return value; |
| - } |
| - return null; |
| - } |
| + Identifier id; |
| if (expr is SimpleIdentifier) { |
| - return finishIdentifier(expr); |
| + id = expr; |
| } else if (expr is PrefixedIdentifier && !expr.isDeferred) { |
| - return finishIdentifier(expr.identifier); |
| + id = expr.identifier; |
| + } else { |
| + return null; |
| + } |
| + var elmement = id.staticElement; |
|
vsm
2017/06/30 18:08:47
elmement -> element
Jennifer Messerly
2017/07/05 21:17:16
Done.
|
| + if (elmement is PropertyAccessorElement && elmement.isGetter) { |
| + var variable = elmement.variable; |
| + int value = variable?.computeConstantValue()?.toIntValue(); |
| + if (value != null && value >= low && value <= high) return value; |
| } |
| return null; |
| } |
| @@ -4771,11 +4739,11 @@ class CodeGenerator extends Object |
| var typeArgs = _getTypeArgs(accessor, resultType); |
| bool isStatic = field is ClassMemberElement && field.isStatic; |
| - var name = _emitMemberName(memberName, |
| + var jsName = _emitMemberName(memberName, |
| type: getStaticType(target), isStatic: isStatic, element: accessor); |
| if (isDynamicInvoke(target)) { |
| return _callHelper('#(#, #)', |
| - [_emitDynamicOperationName('dload'), _visit(target), name]); |
| + [_emitDynamicOperationName('dload'), _visit(target), jsName]); |
| } |
| var jsTarget = _emitTarget(target, accessor, isStatic); |
| @@ -4791,39 +4759,27 @@ class CodeGenerator extends Object |
| } |
| JS.Expression result; |
| - if (accessor is MethodElement && !isStatic) { |
| - // Tear-off methods: explicitly bind it. |
| - // To be safe always use the symbolized name when binding on a native |
| - // class as bind assumes the name will match the name class signatures |
| - // which is symbolized for native classes. |
| - var safeName = _emitMemberName(memberName, |
| - type: getStaticType(target), |
| - isStatic: isStatic, |
| - element: accessor, |
| - alwaysSymbolizeNative: true); |
| + if (_isObjectMemberCall(target, memberName)) { |
| + if (_isObjectMethod(memberName)) { |
| + result = _callHelper('bind(#, #)', [jsTarget, jsName]); |
| + } else { |
| + result = _callHelper('#(#)', [memberName, jsTarget]); |
| + } |
| + } else if (accessor is MethodElement && |
| + !isStatic && |
| + !_isJSNative(accessor.enclosingElement)) { |
| if (isSuper) { |
| result = _callHelper('bind(this, #, #)', |
| - [safeName, _emitTargetAccess(jsTarget, name, accessor)]); |
| - } else if (_isObjectMemberCall(target, memberName)) { |
| - var fn = js.call( |
| - memberName == 'noSuchMethod' |
| - ? 'function(i) { return #.#(this, i); }' |
| - : 'function() { return #.#(this); }', |
| - [_runtimeModule, memberName]); |
| - result = _callHelper( |
| - 'bind(#, #, #)', [jsTarget, _propertyName(memberName), fn]); |
| + [jsName, _emitTargetAccess(jsTarget, jsName, accessor)]); |
| } else { |
| - result = _callHelper('bind(#, #)', [jsTarget, safeName]); |
| + result = _callHelper('bind(#, #)', [jsTarget, jsName]); |
| } |
| - } else if (_isObjectMemberCall(target, memberName)) { |
| - result = _callHelper('#(#)', [memberName, jsTarget]); |
| } else { |
| - result = _emitTargetAccess(jsTarget, name, accessor); |
| - } |
| - if (typeArgs == null) { |
| - return result; |
| + result = _emitTargetAccess(jsTarget, jsName, accessor); |
| } |
| - return _callHelper('gbind(#, #)', [result, typeArgs]); |
| + return typeArgs == null |
| + ? result |
| + : _callHelper('gbind(#, #)', [result, typeArgs]); |
| } |
| JS.LiteralString _emitDynamicOperationName(String name) => |
| @@ -5334,11 +5290,12 @@ class CodeGenerator extends Object |
| /// Unlike call sites, we always have an element available, so we can use it |
| /// directly rather than computing the relevant options for [_emitMemberName]. |
| JS.Expression _declareMemberName(ExecutableElement e, {bool useExtension}) { |
| - var name = (e is PropertyAccessorElement) ? e.variable.name : e.name; |
| + var name = e is PropertyAccessorElement ? e.variable.name : e.name; |
| return _emitMemberName(name, |
| isStatic: e.isStatic, |
| useExtension: |
| - useExtension ?? _extensionTypes.isNativeClass(e.enclosingElement)); |
| + useExtension ?? _extensionTypes.isNativeClass(e.enclosingElement), |
| + element: e); |
| } |
| /// This handles member renaming for private names and operators. |
| @@ -5385,11 +5342,18 @@ class CodeGenerator extends Object |
| {DartType type, |
| bool isStatic: false, |
| bool useExtension, |
| - bool alwaysSymbolizeNative: false, |
| Element element}) { |
| // Static members skip the rename steps and may require JS interop renames. |
| if (isStatic) { |
| - return _propertyName(_emitJSInteropStaticMemberName(element) ?? name); |
| + return _emitJSInteropStaticMemberName(element) ?? _propertyName(name); |
| + } |
| + |
| + // We allow some (illegal in Dart) member names to be used in our private |
| + // SDK code. These renames need to be included at every declaration, |
| + // including overrides in subclasses. |
| + if (element != null) { |
| + var runtimeName = getJSExportName(element); |
| + if (runtimeName != null) return _propertyName(runtimeName); |
| } |
| if (name.startsWith('_')) { |
| @@ -5416,15 +5380,7 @@ class CodeGenerator extends Object |
| var result = _propertyName(name); |
| - if (useExtension == null) { |
| - // Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
| - var baseType = type; |
| - while (baseType is TypeParameterType) { |
| - baseType = (baseType.element as TypeParameterElement).bound; |
| - } |
| - useExtension = baseType is InterfaceType && |
| - _isSymbolizedMember(baseType, name, alwaysSymbolizeNative); |
| - } |
| + useExtension ??= _isSymbolizedMember(type, name); |
| return useExtension |
| ? js.call('#.#', [_extensionSymbolsModule, result]) |
| @@ -5461,33 +5417,38 @@ class CodeGenerator extends Object |
| /// Note, this is an underlying assumption here that, if another native type |
| /// subtypes this one, it also forwards this member to its underlying native |
| /// one without renaming. |
| - bool _isSymbolizedMember( |
| - InterfaceType type, String name, bool alwaysSymbolizeNative) { |
| + bool _isSymbolizedMember(DartType type, String name) { |
| // Object members are handled separately. |
| if (isObjectMember(name)) { |
| return false; |
| } |
| - var element = type.element; |
| - if (_extensionTypes.isNativeClass(element)) { |
| - var member = _lookupForwardedMember(element, name); |
| - |
| - // Fields on a native class are implicitly native. |
| - // Methods/getters/setters are marked external/native. |
| - if (member is FieldElement || |
| - member is ExecutableElement && member.isExternal) { |
| - var jsName = getAnnotationName(member, isJsName); |
| - return alwaysSymbolizeNative || (jsName != null && jsName != name); |
| - } else { |
| - // Non-external members must be symbolized. |
| - return true; |
| + while (type is TypeParameterType) { |
| + type = (type as TypeParameterType).bound; |
| + } |
| + if (type is InterfaceType) { |
| + var element = type.element; |
| + if (_extensionTypes.isNativeClass(element)) { |
| + var member = _lookupForwardedMember(element, name); |
| + |
| + // Fields on a native class are implicitly native. |
| + // Methods/getters/setters are marked external/native. |
| + if (member is FieldElement || |
| + member is ExecutableElement && member.isExternal) { |
| + var jsName = getAnnotationName(member, isJsName); |
| + return jsName != null && jsName != name; |
| + } else { |
| + // Non-external members must be symbolized. |
| + return true; |
| + } |
| } |
| + // If the receiver *may* be a native type (i.e., an interface allowed to |
| + // be implemented by a native class), conservatively symbolize - we don't |
| + // whether it'll be implemented via forwarding. |
|
vsm
2017/06/30 18:08:48
"whether" -> "know whether"
Jennifer Messerly
2017/07/05 21:17:16
oh funny, we must've missed this in the original C
|
| + // TODO(vsm): Consider CHA here to be less conservative. |
| + return _extensionTypes.isNativeInterface(element); |
| } |
| - // If the receiver *may* be a native type (i.e., an interface allowed to |
| - // be implemented by a native class), conservatively symbolize - we don't |
| - // whether it'll be implemented via forwarding. |
| - // TODO(vsm): Consider CHA here to be less conservative. |
| - return _extensionTypes.isNativeInterface(element); |
| + return false; |
| } |
| JS.TemporaryId _emitPrivateNameSymbol(LibraryElement library, String name) { |
| @@ -5551,6 +5512,9 @@ class CodeGenerator extends Object |
| return false; |
| } |
| + bool _isObjectMethod(String name) => |
| + name == 'toString' || name == 'noSuchMethod'; |
| + |
| // TODO(leafp): Various analyzer pieces computed similar things. |
| // Share this logic somewhere? |
| DartType _getExpectedReturnType(ExecutableElement element) { |
| @@ -5609,6 +5573,9 @@ class CodeGenerator extends Object |
| return js.statement('#.$code', args); |
| } |
| + JS.Expression _throwUnsafe(String message) => _callHelper( |
| + 'throw(Error(#))', js.escapedString("compile error: $message")); |
| + |
| _unreachable(AstNode node) { |
| throw new UnsupportedError( |
| 'tried to generate an unreachable node: `$node`'); |