| 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 6f2c2d1cd8dedcb5c0079afd775497b6c1cd194a..4e5b3a264740a048f8c496e55e37fd343a54f118 100644
|
| --- a/pkg/dev_compiler/lib/src/compiler/code_generator.dart
|
| +++ b/pkg/dev_compiler/lib/src/compiler/code_generator.dart
|
| @@ -391,81 +391,61 @@ 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;
|
| - }
|
| -
|
| + /// If [e] is a property accessor element, this returns the
|
| + /// (possibly synthetic) field that corresponds to it, otherwise returns [e].
|
| + Element _getNonAccessorElement(Element e) =>
|
| + e is PropertyAccessorElement ? e.variable : e;
|
| +
|
| + /// Returns the name of [e] but removes trailing `=` from setter names.
|
| + // TODO(jmesserly): it would be nice if Analyzer had something like this.
|
| + // `Element.displayName` is close, but it also normalizes operator names in
|
| + // a way we don't want.
|
| + String _getElementName(Element e) => _getNonAccessorElement(e).name;
|
| +
|
| + bool _isExternal(Element e) =>
|
| + e is ExecutableElement && e.isExternal ||
|
| + e is PropertyInducingElement &&
|
| + (e.getter.isExternal || e.setter.isExternal);
|
| +
|
| + bool _isJSElement(Element e) =>
|
| + e?.library != null &&
|
| + _isJSNative(e.library) &&
|
| + (_isExternal(e) || e is ClassElement && _isJSNative(e));
|
| +
|
| + String _getJSNameWithoutGlobal(Element e) {
|
| + if (!_isJSElement(e)) 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) ?? _getElementName(e);
|
| + 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 ? _emitJSInteropForGlobal(jsName) : null;
|
| }
|
|
|
| - String _jsInteropStaticMemberName(Element e) {
|
| - if (e?.library == null ||
|
| - findAnnotation(e.library, isPublicJSAnnotation) == null) {
|
| - return null;
|
| + JS.Expression _emitJSInteropForGlobal(String name) {
|
| + var access = _callHelper('global');
|
| + for (var part in name.split('.')) {
|
| + access = new JS.PropertyAccess(access, js.escapedString(part, "'"));
|
| }
|
| - if (e is ExecutableElement && e.isExternal) {
|
| - return getAnnotationName(e, isPublicJSAnnotation) ??
|
| - (e is PropertyAccessorElement ? e.variable : e).name;
|
| - }
|
| - 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);
|
| - // 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');
|
| + JS.Expression _emitJSInteropStaticMemberName(Element e) {
|
| + if (!_isJSElement(e)) return null;
|
| + var name = getAnnotationName(e, isPublicJSAnnotation);
|
| + if (name != null) {
|
| + if (name.contains('.')) {
|
| + throw new UnsupportedError(
|
| + 'static members do not support "." in their names. '
|
| + 'See https://github.com/dart-lang/sdk/issues/27926');
|
| + }
|
| + } else {
|
| + name = _getElementName(e);
|
| }
|
| - return name;
|
| + return js.escapedString(name, "'");
|
| }
|
|
|
| /// Flattens blocks in [items] to a single list.
|
| @@ -857,7 +837,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;
|
|
|
| @@ -876,7 +856,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>[];
|
| @@ -1699,13 +1679,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]));
|
| } 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
|
| @@ -2760,9 +2737,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
|
| @@ -2807,17 +2782,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);
|
| }
|
| @@ -3080,7 +3048,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
|
| @@ -3094,14 +3061,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(() => #, #)',
|
| + [_emitJSInteropForGlobal(jsName), js.escapedString(jsName)]);
|
| }
|
|
|
| // TODO(jmesserly): like constants, should we hoist function types out of
|
| @@ -3136,7 +3102,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)
|
| @@ -3148,15 +3114,13 @@ 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) ?? _getElementName(e);
|
| return new JS.PropertyAccess(
|
| - emitLibraryName(e.library), _propertyName(name));
|
| + emitLibraryName(e.library), _propertyName(name + suffix));
|
| }
|
|
|
| @override
|
| @@ -3454,15 +3418,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;
|
| @@ -3498,7 +3460,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);
|
| @@ -3508,19 +3470,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);
|
| @@ -3677,7 +3639,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) {
|
| @@ -3720,6 +3682,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
|
| @@ -3728,12 +3711,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;
|
| @@ -4060,13 +4042,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;
|
| @@ -4087,13 +4064,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);
|
| @@ -4380,20 +4357,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 element = id.staticElement;
|
| + if (element is PropertyAccessorElement && element.isGetter) {
|
| + var variable = element.variable;
|
| + int value = variable?.computeConstantValue()?.toIntValue();
|
| + if (value != null && value >= low && value <= high) return value;
|
| }
|
| return null;
|
| }
|
| @@ -4839,17 +4816,16 @@ class CodeGenerator extends Object
|
| Expression target, SimpleIdentifier memberId, DartType resultType) {
|
| var accessor = memberId.staticElement;
|
| // If `member` is a getter/setter, get the corresponding
|
| - var field =
|
| - accessor is PropertyAccessorElement ? accessor.variable : accessor;
|
| + var field = _getNonAccessorElement(accessor);
|
| String memberName = memberId.name;
|
| 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);
|
| @@ -4865,39 +4841,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);
|
| + result = _emitTargetAccess(jsTarget, jsName, accessor);
|
| }
|
| - if (typeArgs == null) {
|
| - return result;
|
| - }
|
| - return _callHelper('gbind(#, #)', [result, typeArgs]);
|
| + return typeArgs == null
|
| + ? result
|
| + : _callHelper('gbind(#, #)', [result, typeArgs]);
|
| }
|
|
|
| JS.LiteralString _emitDynamicOperationName(String name) =>
|
| @@ -5410,11 +5374,11 @@ 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;
|
| - return _emitMemberName(name,
|
| + return _emitMemberName(_getElementName(e),
|
| 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.
|
| @@ -5461,11 +5425,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('_')) {
|
| @@ -5492,15 +5463,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])
|
| @@ -5537,33 +5500,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
|
| + // know whether it'll be implemented via forwarding.
|
| + // 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) {
|
| @@ -5617,6 +5585,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) {
|
| @@ -5675,6 +5646,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`');
|
|
|