Split js interop registration into library/class/member elements
R=sigmund@google.com Review-Url: https://codereview.chromium.org/2731163002 .
This commit is contained in:
@@ -39,7 +39,9 @@ class JavaScriptBackendSerialization implements BackendSerialization {
|
||||
deserializer = new JavaScriptBackendDeserializer(nativeData);
|
||||
}
|
||||
|
||||
const Key JS_INTEROP_NAME = const Key('jsInteropName');
|
||||
const Key JS_INTEROP_LIBRARY_NAME = const Key('jsInteropLibraryName');
|
||||
const Key JS_INTEROP_CLASS_NAME = const Key('jsInteropClassName');
|
||||
const Key JS_INTEROP_MEMBER_NAME = const Key('jsInteropMemberName');
|
||||
const Key NATIVE_MEMBER_NAME = const Key('nativeMemberName');
|
||||
const Key NATIVE_CLASS_TAG_INFO = const Key('nativeClassTagInfo');
|
||||
const Key NATIVE_METHOD_BEHAVIOR = const Key('nativeMethodBehavior');
|
||||
@@ -58,9 +60,17 @@ class JavaScriptBackendSerializer implements SerializerPlugin {
|
||||
return encoder ??= createEncoder(_BACKEND_DATA_TAG);
|
||||
}
|
||||
|
||||
String jsInteropName = nativeData.jsInteropNames[element];
|
||||
if (jsInteropName != null) {
|
||||
getEncoder().setString(JS_INTEROP_NAME, jsInteropName);
|
||||
String jsInteropLibraryName = nativeData.jsInteropLibraryNames[element];
|
||||
if (jsInteropLibraryName != null) {
|
||||
getEncoder().setString(JS_INTEROP_LIBRARY_NAME, jsInteropLibraryName);
|
||||
}
|
||||
String jsInteropClassName = nativeData.jsInteropClassNames[element];
|
||||
if (jsInteropClassName != null) {
|
||||
getEncoder().setString(JS_INTEROP_CLASS_NAME, jsInteropClassName);
|
||||
}
|
||||
String jsInteropMemberName = nativeData.jsInteropMemberNames[element];
|
||||
if (jsInteropMemberName != null) {
|
||||
getEncoder().setString(JS_INTEROP_MEMBER_NAME, jsInteropMemberName);
|
||||
}
|
||||
String nativeMemberName = nativeData.nativeMemberName[element];
|
||||
if (nativeMemberName != null) {
|
||||
@@ -107,10 +117,20 @@ class JavaScriptBackendDeserializer implements DeserializerPlugin {
|
||||
void onElement(Element element, ObjectDecoder getDecoder(String tag)) {
|
||||
ObjectDecoder decoder = getDecoder(_BACKEND_DATA_TAG);
|
||||
if (decoder != null) {
|
||||
String jsInteropName =
|
||||
decoder.getString(JS_INTEROP_NAME, isOptional: true);
|
||||
if (jsInteropName != null) {
|
||||
nativeData.jsInteropNames[element] = jsInteropName;
|
||||
String jsInteropLibraryName =
|
||||
decoder.getString(JS_INTEROP_LIBRARY_NAME, isOptional: true);
|
||||
if (jsInteropLibraryName != null) {
|
||||
nativeData.jsInteropLibraryNames[element] = jsInteropLibraryName;
|
||||
}
|
||||
String jsInteropClassName =
|
||||
decoder.getString(JS_INTEROP_CLASS_NAME, isOptional: true);
|
||||
if (jsInteropClassName != null) {
|
||||
nativeData.jsInteropClassNames[element] = jsInteropClassName;
|
||||
}
|
||||
String jsInteropMemberName =
|
||||
decoder.getString(JS_INTEROP_MEMBER_NAME, isOptional: true);
|
||||
if (jsInteropMemberName != null) {
|
||||
nativeData.jsInteropMemberNames[element] = jsInteropMemberName;
|
||||
}
|
||||
String nativeMemberName =
|
||||
decoder.getString(NATIVE_MEMBER_NAME, isOptional: true);
|
||||
|
||||
@@ -13,7 +13,7 @@ abstract class _MinifiedFieldNamer implements Namer {
|
||||
// The inheritance scope based naming might not yield a name. For instance,
|
||||
// this could be because the field belongs to a mixin. In such a case this
|
||||
// will return `null` and a normal field name has to be used.
|
||||
jsAst.Name _minifiedInstanceFieldPropertyName(Element element) {
|
||||
jsAst.Name _minifiedInstanceFieldPropertyName(FieldElement element) {
|
||||
if (_nativeData.hasFixedBackendName(element)) {
|
||||
return new StringBackedName(_nativeData.getFixedBackendName(element));
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ class FrequencyBasedNamer extends Namer
|
||||
}
|
||||
|
||||
@override
|
||||
jsAst.Name instanceFieldPropertyName(Element element) {
|
||||
jsAst.Name instanceFieldPropertyName(FieldElement element) {
|
||||
jsAst.Name proposed = _minifiedInstanceFieldPropertyName(element);
|
||||
if (proposed != null) {
|
||||
return proposed;
|
||||
|
||||
@@ -16,9 +16,9 @@ import '../elements/elements.dart'
|
||||
ClassElement,
|
||||
Element,
|
||||
FieldElement,
|
||||
FunctionElement,
|
||||
LibraryElement,
|
||||
ParameterElement,
|
||||
MemberElement,
|
||||
MethodElement,
|
||||
MetadataAnnotation;
|
||||
import '../js/js.dart' as jsAst;
|
||||
@@ -57,8 +57,10 @@ class JsInteropAnalysis {
|
||||
_inCodegen = true;
|
||||
}
|
||||
|
||||
void processJsInteropAnnotation(Element e) {
|
||||
for (MetadataAnnotation annotation in e.implementation.metadata) {
|
||||
/// Resolves the metadata of [element] and returns the name of the `JS(...)`
|
||||
/// annotation for js interop, if found.
|
||||
String processJsInteropAnnotation(Element element) {
|
||||
for (MetadataAnnotation annotation in element.implementation.metadata) {
|
||||
// TODO(johnniwinther): Avoid processing unresolved elements.
|
||||
if (annotation.constant == null) continue;
|
||||
ConstantValue constant =
|
||||
@@ -67,18 +69,19 @@ class JsInteropAnalysis {
|
||||
ConstructedConstantValue constructedConstant = constant;
|
||||
if (constructedConstant.type.element == helpers.jsAnnotationClass) {
|
||||
ConstantValue value = constructedConstant.fields[nameField];
|
||||
String name;
|
||||
if (value.isString) {
|
||||
StringConstantValue stringValue = value;
|
||||
backend.nativeDataBuilder
|
||||
.setJsInteropName(e, stringValue.primitiveValue.slowToString());
|
||||
name = stringValue.primitiveValue.slowToString();
|
||||
} else {
|
||||
// TODO(jacobr): report a warning if the value is not a String.
|
||||
backend.nativeDataBuilder.setJsInteropName(e, '');
|
||||
name = '';
|
||||
}
|
||||
enabledJsInterop = true;
|
||||
return;
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool hasAnonymousAnnotation(Element element) {
|
||||
@@ -94,7 +97,7 @@ class JsInteropAnalysis {
|
||||
});
|
||||
}
|
||||
|
||||
void _checkFunctionParameters(FunctionElement fn) {
|
||||
void _checkFunctionParameters(MethodElement fn) {
|
||||
if (fn.hasFunctionSignature &&
|
||||
fn.functionSignature.optionalParametersAreNamed) {
|
||||
backend.reporter.reportErrorMessage(
|
||||
@@ -105,17 +108,31 @@ class JsInteropAnalysis {
|
||||
}
|
||||
|
||||
void processJsInteropAnnotationsInLibrary(LibraryElement library) {
|
||||
processJsInteropAnnotation(library);
|
||||
String libraryName = processJsInteropAnnotation(library);
|
||||
if (libraryName != null) {
|
||||
backend.nativeDataBuilder.setJsInteropLibraryName(library, libraryName);
|
||||
}
|
||||
library.implementation.forEachLocalMember((Element element) {
|
||||
processJsInteropAnnotation(element);
|
||||
if (element is MethodElement) {
|
||||
if (!backend.nativeData.isJsInteropMember(element)) return;
|
||||
_checkFunctionParameters(element);
|
||||
if (element is MemberElement) {
|
||||
String memberName = processJsInteropAnnotation(element);
|
||||
if (memberName != null) {
|
||||
backend.nativeDataBuilder.setJsInteropMemberName(element, memberName);
|
||||
}
|
||||
|
||||
if (element is MethodElement) {
|
||||
if (!backend.nativeData.isJsInteropMember(element)) return;
|
||||
_checkFunctionParameters(element);
|
||||
}
|
||||
}
|
||||
|
||||
if (!element.isClass) return;
|
||||
|
||||
ClassElement classElement = element;
|
||||
String className = processJsInteropAnnotation(classElement);
|
||||
if (className != null) {
|
||||
backend.nativeDataBuilder
|
||||
.setJsInteropClassName(classElement, className);
|
||||
}
|
||||
if (!backend.nativeData.isJsInteropClass(classElement)) return;
|
||||
|
||||
// Skip classes that are completely unreachable. This should only happen
|
||||
@@ -133,13 +150,17 @@ class JsInteropAnalysis {
|
||||
});
|
||||
}
|
||||
|
||||
classElement.forEachMember((ClassElement classElement, Element member) {
|
||||
processJsInteropAnnotation(member);
|
||||
classElement
|
||||
.forEachMember((ClassElement classElement, MemberElement member) {
|
||||
String memberName = processJsInteropAnnotation(member);
|
||||
if (memberName != null) {
|
||||
backend.nativeDataBuilder.setJsInteropMemberName(element, memberName);
|
||||
}
|
||||
|
||||
if (!member.isSynthesized &&
|
||||
backend.nativeData.isJsInteropClass(classElement) &&
|
||||
member is FunctionElement) {
|
||||
FunctionElement fn = member;
|
||||
member is MethodElement) {
|
||||
MethodElement fn = member;
|
||||
if (!fn.isExternal &&
|
||||
!fn.isAbstract &&
|
||||
!fn.isConstructor &&
|
||||
|
||||
@@ -292,7 +292,7 @@ class MinifyNamer extends Namer
|
||||
}
|
||||
|
||||
@override
|
||||
jsAst.Name instanceFieldPropertyName(Element element) {
|
||||
jsAst.Name instanceFieldPropertyName(FieldElement element) {
|
||||
jsAst.Name proposed = _minifiedInstanceFieldPropertyName(element);
|
||||
if (proposed != null) {
|
||||
return proposed;
|
||||
|
||||
@@ -778,36 +778,6 @@ class Namer {
|
||||
return invocationName(new Selector.fromElement(method));
|
||||
}
|
||||
|
||||
String _jsNameHelper(Element e) {
|
||||
String jsInteropName = _nativeData.getJsInteropName(e);
|
||||
if (jsInteropName != null && jsInteropName.isNotEmpty) return jsInteropName;
|
||||
return e.isLibrary ? 'self' : _nativeData.getUnescapedJSInteropName(e.name);
|
||||
}
|
||||
|
||||
/// Returns a JavaScript path specifying the context in which
|
||||
/// [element.fixedBackendName] should be evaluated. Only applicable for
|
||||
/// elements using typed JavaScript interop.
|
||||
/// For example: fixedBackendPath for the static method createMap in the
|
||||
/// Map class of the goog.map JavaScript library would have path
|
||||
/// "goog.maps.Map".
|
||||
String fixedBackendMethodPath(MethodElement element) {
|
||||
return _fixedBackendPath(element);
|
||||
}
|
||||
|
||||
String _fixedBackendPath(Element element) {
|
||||
if (!_nativeData.isJsInterop(element)) return null;
|
||||
if (element.isInstanceMember) return 'this';
|
||||
if (element.isConstructor) return _fixedBackendPath(element.enclosingClass);
|
||||
if (element.isLibrary) return 'self';
|
||||
var sb = new StringBuffer();
|
||||
sb..write(_jsNameHelper(element.library));
|
||||
|
||||
if (element.enclosingClass != null && element.enclosingClass != element) {
|
||||
sb..write('.')..write(_jsNameHelper(element.enclosingClass));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/// Returns the annotated name for a variant of `call`.
|
||||
/// The result has the form:
|
||||
///
|
||||
|
||||
@@ -10,7 +10,6 @@ import '../elements/elements.dart'
|
||||
ClassElement,
|
||||
Element,
|
||||
FieldElement,
|
||||
FunctionElement,
|
||||
LibraryElement,
|
||||
MemberElement,
|
||||
MethodElement;
|
||||
@@ -63,11 +62,17 @@ abstract class NativeData extends NativeClassData {
|
||||
|
||||
/// Returns `true` if the name of [element] is fixed for the generated
|
||||
/// JavaScript.
|
||||
bool hasFixedBackendName(Element element);
|
||||
bool hasFixedBackendName(MemberElement element);
|
||||
|
||||
/// Computes the name for [element] to use in the generated JavaScript. This
|
||||
/// is either given through a native annotation or a js interop annotation.
|
||||
String getFixedBackendName(Entity entity);
|
||||
String getFixedBackendName(MemberEntity element);
|
||||
|
||||
/// Computes the name prefix for [element] to use in the generated JavaScript.
|
||||
///
|
||||
/// For static and top-level members and constructors this is based on the
|
||||
/// JavaScript names for the library and/or the enclosing class.
|
||||
String getFixedBackendMethodPath(MethodElement element);
|
||||
|
||||
/// Returns the list of non-directive native tag words for [cls].
|
||||
List<String> getNativeTagsOfClass(ClassElement cls);
|
||||
@@ -75,22 +80,21 @@ abstract class NativeData extends NativeClassData {
|
||||
/// Returns `true` if [cls] has a `!nonleaf` tag word.
|
||||
bool hasNativeTagsForcedNonLeaf(ClassElement cls);
|
||||
|
||||
/// Returns `true` if [element] is part of JsInterop.
|
||||
///
|
||||
/// Deprecated: Use [isJsInteropLibrary], [isJsInteropClass] or
|
||||
/// [isJsInteropMember] instead.
|
||||
@deprecated
|
||||
bool isJsInterop(Element element);
|
||||
|
||||
/// Returns `true` if [element] is a JsInterop method.
|
||||
bool isJsInteropMember(MethodElement element);
|
||||
bool isJsInteropMember(MemberEntity element);
|
||||
|
||||
/// Returns the explicit js interop name for [element].
|
||||
String getJsInteropName(Element element);
|
||||
/// Returns the explicit js interop name for library [element].
|
||||
String getJsInteropLibraryName(LibraryElement element);
|
||||
|
||||
/// Returns the explicit js interop name for class [element].
|
||||
String getJsInteropClassName(ClassElement element);
|
||||
|
||||
/// Returns the explicit js interop name for member [element].
|
||||
String getJsInteropMemberName(MemberElement element);
|
||||
|
||||
/// Apply JS$ escaping scheme to convert possible escaped Dart names into
|
||||
/// JS names.
|
||||
String getUnescapedJSInteropName(String name);
|
||||
String computeUnescapedJSInteropName(String name);
|
||||
}
|
||||
|
||||
abstract class NativeClassDataBuilder {
|
||||
@@ -131,15 +135,24 @@ abstract class NativeDataBuilder {
|
||||
/// [element] in the generated JavaScript.
|
||||
void setNativeMemberName(MemberElement element, String name);
|
||||
|
||||
/// Sets the explicit js interop [name] for [element].
|
||||
void setJsInteropName(Element element, String name);
|
||||
/// Sets the explicit js interop [name] for the library [element].
|
||||
void setJsInteropLibraryName(LibraryElement element, String name);
|
||||
|
||||
/// Sets the explicit js interop [name] for the class [element].
|
||||
void setJsInteropClassName(ClassElement element, String name);
|
||||
|
||||
/// Sets the explicit js interop [name] for the member [element].
|
||||
void setJsInteropMemberName(MemberElement element, String name);
|
||||
}
|
||||
|
||||
class NativeDataImpl
|
||||
implements NativeData, NativeDataBuilder, NativeClassDataBuilder {
|
||||
/// The JavaScript names for elements implemented via typed JavaScript
|
||||
/// interop.
|
||||
Map<Element, String> jsInteropNames = <Element, String>{};
|
||||
Map<LibraryElement, String> jsInteropLibraryNames =
|
||||
<LibraryElement, String>{};
|
||||
Map<ClassElement, String> jsInteropClassNames = <ClassElement, String>{};
|
||||
Map<MemberElement, String> jsInteropMemberNames = <MemberElement, String>{};
|
||||
|
||||
/// The JavaScript names for native JavaScript elements implemented.
|
||||
Map<Element, String> nativeMemberName = <Element, String>{};
|
||||
@@ -165,125 +178,183 @@ class NativeDataImpl
|
||||
static const String _jsInteropEscapePrefix = r'JS$';
|
||||
|
||||
/// Returns `true` if [element] is explicitly marked as part of JsInterop.
|
||||
bool _isJsInterop(Element element) {
|
||||
return jsInteropNames.containsKey(element.declaration);
|
||||
bool _isJsInteropLibrary(LibraryElement element) {
|
||||
return jsInteropLibraryNames.containsKey(element);
|
||||
}
|
||||
|
||||
/// Marks [element] as an explicit part of JsInterop. The js interop name is
|
||||
/// expected to be computed later.
|
||||
void markAsJsInterop(Element element) {
|
||||
jsInteropNames[element.declaration] = null;
|
||||
/// Returns `true` if [element] is explicitly marked as part of JsInterop.
|
||||
bool _isJsInteropClass(ClassElement element) {
|
||||
return jsInteropClassNames.containsKey(element);
|
||||
}
|
||||
|
||||
/// Returns `true` if [element] is explicitly marked as part of JsInterop.
|
||||
bool _isJsInteropMember(MemberElement element) {
|
||||
return jsInteropMemberNames.containsKey(element);
|
||||
}
|
||||
|
||||
@override
|
||||
void markAsJsInteropLibrary(LibraryElement element) {
|
||||
markAsJsInterop(element);
|
||||
jsInteropLibraryNames[element] = null;
|
||||
}
|
||||
|
||||
@override
|
||||
void markAsJsInteropClass(ClassElement element) {
|
||||
markAsJsInterop(element);
|
||||
jsInteropClassNames[element] = null;
|
||||
}
|
||||
|
||||
@override
|
||||
void markAsJsInteropMember(MemberElement element) {
|
||||
markAsJsInterop(element);
|
||||
jsInteropMemberNames[element] = null;
|
||||
}
|
||||
|
||||
/// Sets the explicit js interop [name] for [element].
|
||||
void setJsInteropName(Element element, String name) {
|
||||
assert(invariant(element, isJsInterop(element),
|
||||
/// Sets the explicit js interop [name] for the library [element].
|
||||
void setJsInteropLibraryName(LibraryElement element, String name) {
|
||||
assert(invariant(element, _isJsInteropLibrary(element),
|
||||
message:
|
||||
'Element $element is not js interop but given a js interop name.'));
|
||||
jsInteropNames[element.declaration] = name;
|
||||
'Library $element is not js interop but given a js interop name.'));
|
||||
jsInteropLibraryNames[element] = name;
|
||||
}
|
||||
|
||||
/// Returns the explicit js interop name for [element].
|
||||
String getJsInteropName(Element element) {
|
||||
return jsInteropNames[element.declaration];
|
||||
/// Sets the explicit js interop [name] for the class [element].
|
||||
void setJsInteropClassName(ClassElement element, String name) {
|
||||
assert(invariant(element, _isJsInteropClass(element),
|
||||
message:
|
||||
'Class $element is not js interop but given a js interop name.'));
|
||||
jsInteropClassNames[element] = name;
|
||||
}
|
||||
|
||||
/// Returns `true` if [element] is part of JsInterop.
|
||||
bool isJsInterop(Element element) {
|
||||
// An function is part of JsInterop in the following cases:
|
||||
// * It has a jsInteropName annotation
|
||||
// * It is external member of a class or library tagged as JsInterop.
|
||||
if (element.isFunction || element.isConstructor || element.isAccessor) {
|
||||
FunctionElement function = element;
|
||||
if (!function.isExternal) return false;
|
||||
/// Sets the explicit js interop [name] for the member [element].
|
||||
void setJsInteropMemberName(MemberElement element, String name) {
|
||||
assert(invariant(element, _isJsInteropMember(element),
|
||||
message:
|
||||
'Member $element is not js interop but given a js interop name.'));
|
||||
jsInteropMemberNames[element] = name;
|
||||
}
|
||||
|
||||
if (_isJsInterop(function)) return true;
|
||||
if (function.isClassMember) return isJsInterop(function.contextClass);
|
||||
if (function.isTopLevel) return isJsInterop(function.library);
|
||||
return false;
|
||||
} else {
|
||||
return _isJsInterop(element);
|
||||
}
|
||||
/// Returns the explicit js interop name for library [element].
|
||||
String getJsInteropLibraryName(LibraryElement element) {
|
||||
return jsInteropLibraryNames[element];
|
||||
}
|
||||
|
||||
/// Returns the explicit js interop name for class [element].
|
||||
String getJsInteropClassName(ClassElement element) {
|
||||
return jsInteropClassNames[element];
|
||||
}
|
||||
|
||||
/// Returns the explicit js interop name for member [element].
|
||||
String getJsInteropMemberName(MemberElement element) {
|
||||
return jsInteropMemberNames[element];
|
||||
}
|
||||
|
||||
/// Returns `true` if [element] is a JsInterop library.
|
||||
bool isJsInteropLibrary(LibraryElement element) => isJsInterop(element);
|
||||
bool isJsInteropLibrary(LibraryElement element) =>
|
||||
_isJsInteropLibrary(element);
|
||||
|
||||
/// Returns `true` if [element] is a JsInterop class.
|
||||
bool isJsInteropClass(ClassElement element) => isJsInterop(element);
|
||||
bool isJsInteropClass(ClassElement element) => _isJsInteropClass(element);
|
||||
|
||||
/// Returns `true` if [element] is a JsInterop method.
|
||||
bool isJsInteropMember(MethodElement element) => isJsInterop(element);
|
||||
bool isJsInteropMember(MemberElement element) {
|
||||
if (element.isFunction || element.isConstructor || element.isAccessor) {
|
||||
MethodElement function = element;
|
||||
if (!function.isExternal) return false;
|
||||
|
||||
if (_isJsInteropMember(function)) return true;
|
||||
if (function.isClassMember) {
|
||||
return _isJsInteropClass(function.enclosingClass);
|
||||
}
|
||||
if (function.isTopLevel) {
|
||||
return _isJsInteropLibrary(function.library);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return _isJsInteropMember(element);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the name of [element] is fixed for the generated
|
||||
/// JavaScript.
|
||||
bool hasFixedBackendName(Element element) {
|
||||
return isJsInterop(element) ||
|
||||
bool hasFixedBackendName(MemberElement element) {
|
||||
return isJsInteropMember(element) ||
|
||||
nativeMemberName.containsKey(element.declaration);
|
||||
}
|
||||
|
||||
String _jsNameHelper(Element element) {
|
||||
String jsInteropName = jsInteropNames[element.declaration];
|
||||
assert(invariant(element, !(_isJsInterop(element) && jsInteropName == null),
|
||||
message:
|
||||
'Element $element is js interop but js interop name has not yet '
|
||||
'been computed.'));
|
||||
if (jsInteropName != null && jsInteropName.isNotEmpty) {
|
||||
return jsInteropName;
|
||||
}
|
||||
return element.isLibrary ? 'self' : getUnescapedJSInteropName(element.name);
|
||||
}
|
||||
|
||||
/// Computes the name for [element] to use in the generated JavaScript. This
|
||||
/// is either given through a native annotation or a js interop annotation.
|
||||
String getFixedBackendName(Entity entity) {
|
||||
// TODO(johnniwinther): Remove this assignment from [Entity] to [Element]
|
||||
// when `.declaration` is no longer needed.
|
||||
Element element = entity;
|
||||
String getFixedBackendName(MemberElement element) {
|
||||
String name = nativeMemberName[element.declaration];
|
||||
if (name == null && isJsInterop(element)) {
|
||||
if (name == null && isJsInteropMember(element)) {
|
||||
// If an element isJsInterop but _isJsInterop is false that means it is
|
||||
// considered interop as the parent class is interop.
|
||||
name = _jsNameHelper(
|
||||
element.isConstructor ? element.enclosingClass : element);
|
||||
name = element.isConstructor
|
||||
? _jsClassNameHelper(element.enclosingClass)
|
||||
: _jsMemberNameHelper(element);
|
||||
nativeMemberName[element.declaration] = name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/// Whether [element] corresponds to a native JavaScript construct either
|
||||
/// through the native mechanism (`@Native(...)` or the `native` pseudo
|
||||
/// keyword) which is only allowed for internal libraries or via the typed
|
||||
/// JavaScriptInterop mechanism which is allowed for user libraries.
|
||||
bool isNative(Element element) {
|
||||
if (isJsInterop(element)) return true;
|
||||
if (element.isClass) {
|
||||
return nativeClassTagInfo.containsKey(element.declaration);
|
||||
} else {
|
||||
return nativeMemberName.containsKey(element.declaration);
|
||||
String _jsLibraryNameHelper(LibraryElement element) {
|
||||
String jsInteropName = getJsInteropLibraryName(element);
|
||||
if (jsInteropName != null && jsInteropName.isNotEmpty) return jsInteropName;
|
||||
return 'self';
|
||||
}
|
||||
|
||||
String _jsClassNameHelper(ClassElement element) {
|
||||
String jsInteropName = getJsInteropClassName(element);
|
||||
if (jsInteropName != null && jsInteropName.isNotEmpty) return jsInteropName;
|
||||
return computeUnescapedJSInteropName(element.name);
|
||||
}
|
||||
|
||||
String _jsMemberNameHelper(MemberElement element) {
|
||||
String jsInteropName = jsInteropMemberNames[element];
|
||||
assert(invariant(element,
|
||||
!(jsInteropMemberNames.containsKey(element) && jsInteropName == null),
|
||||
message:
|
||||
'Member $element is js interop but js interop name has not yet '
|
||||
'been computed.'));
|
||||
if (jsInteropName != null && jsInteropName.isNotEmpty) {
|
||||
return jsInteropName;
|
||||
}
|
||||
return computeUnescapedJSInteropName(element.name);
|
||||
}
|
||||
|
||||
/// Returns a JavaScript path specifying the context in which
|
||||
/// [element.fixedBackendName] should be evaluated. Only applicable for
|
||||
/// elements using typed JavaScript interop.
|
||||
/// For example: fixedBackendPath for the static method createMap in the
|
||||
/// Map class of the goog.map JavaScript library would have path
|
||||
/// "goog.maps.Map".
|
||||
String getFixedBackendMethodPath(MethodElement element) {
|
||||
if (!isJsInteropMember(element)) return null;
|
||||
if (element.isInstanceMember) return 'this';
|
||||
if (element.isConstructor) {
|
||||
return _fixedBackendClassPath(element.enclosingClass);
|
||||
}
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.write(_jsLibraryNameHelper(element.library));
|
||||
if (element.enclosingClass != null) {
|
||||
sb..write('.')..write(_jsClassNameHelper(element.enclosingClass));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
String _fixedBackendClassPath(ClassElement element) {
|
||||
if (!isJsInteropClass(element)) return null;
|
||||
return _jsLibraryNameHelper(element.library);
|
||||
}
|
||||
|
||||
/// Returns `true` if [cls] is a native class.
|
||||
bool isNativeClass(ClassElement element) => isNative(element);
|
||||
bool isNativeClass(ClassElement element) {
|
||||
if (isJsInteropClass(element)) return true;
|
||||
return nativeClassTagInfo.containsKey(element);
|
||||
}
|
||||
|
||||
/// Returns `true` if [element] is a native member of a native class.
|
||||
bool isNativeMember(MemberElement element) => isNative(element);
|
||||
bool isNativeMember(MemberElement element) {
|
||||
if (isJsInteropMember(element)) return true;
|
||||
return nativeMemberName.containsKey(element);
|
||||
}
|
||||
|
||||
/// Returns `true` if [element] or any of its superclasses is native.
|
||||
bool isNativeOrExtendsNative(ClassElement element) {
|
||||
@@ -390,7 +461,7 @@ class NativeDataImpl
|
||||
|
||||
/// Apply JS$ escaping scheme to convert possible escaped Dart names into
|
||||
/// JS names.
|
||||
String getUnescapedJSInteropName(String name) {
|
||||
String computeUnescapedJSInteropName(String name) {
|
||||
return name.startsWith(_jsInteropEscapePrefix)
|
||||
? name.substring(_jsInteropEscapePrefix.length)
|
||||
: name;
|
||||
|
||||
@@ -339,7 +339,8 @@ class NativeEmitter {
|
||||
// and library that uses typed JavaScript interop will create only 1
|
||||
// unique template.
|
||||
receiver = js
|
||||
.uncachedExpressionTemplate(namer.fixedBackendMethodPath(member))
|
||||
.uncachedExpressionTemplate(
|
||||
nativeData.getFixedBackendMethodPath(member))
|
||||
.instantiate([]);
|
||||
} else {
|
||||
receiver = js('this');
|
||||
|
||||
@@ -354,7 +354,7 @@ class ProgramBuilder {
|
||||
if (e is ClassElement && backend.nativeData.isJsInteropClass(e)) {
|
||||
e.declaration.forEachMember((_, Element member) {
|
||||
var jsName =
|
||||
backend.nativeData.getUnescapedJSInteropName(member.name);
|
||||
backend.nativeData.computeUnescapedJSInteropName(member.name);
|
||||
if (!member.isInstanceMember) return;
|
||||
if (member.isGetter || member.isField || member.isFunction) {
|
||||
var selectors = worldBuilder.getterInvocationsByName(member.name);
|
||||
|
||||
@@ -1067,7 +1067,7 @@ class SsaBuilder extends ast.Visitor
|
||||
ast.Send call = link.head;
|
||||
assert(ast.Initializers.isSuperConstructorCall(call) ||
|
||||
ast.Initializers.isConstructorRedirect(call));
|
||||
FunctionElement target = elements[call].implementation;
|
||||
ConstructorElement target = elements[call];
|
||||
CallStructure callStructure =
|
||||
elements.getSelector(call).callStructure;
|
||||
Link<ast.Node> arguments = call.arguments;
|
||||
@@ -2490,8 +2490,8 @@ class SsaBuilder extends ast.Visitor
|
||||
* Invariant: [element] must be an implementation element.
|
||||
*/
|
||||
List<HInstruction> makeStaticArgumentList(CallStructure callStructure,
|
||||
Link<ast.Node> arguments, FunctionElement element) {
|
||||
assert(invariant(element, element.isImplementation));
|
||||
Link<ast.Node> arguments, MethodElement element) {
|
||||
assert(invariant(element, element.isDeclaration));
|
||||
|
||||
HInstruction compileArgument(ast.Node argument) {
|
||||
visit(argument);
|
||||
@@ -2501,7 +2501,7 @@ class SsaBuilder extends ast.Visitor
|
||||
return Elements.makeArgumentsList<HInstruction>(
|
||||
callStructure,
|
||||
arguments,
|
||||
element,
|
||||
element.implementation,
|
||||
compileArgument,
|
||||
backend.nativeData.isJsInteropMember(element)
|
||||
? handleConstantForOptionalParameterJsInterop
|
||||
@@ -3069,8 +3069,8 @@ class SsaBuilder extends ast.Visitor
|
||||
Selector selector = elements.getSelector(node);
|
||||
assert(invariant(node, selector.applies(method.implementation),
|
||||
message: "$selector does not apply to ${method.implementation}"));
|
||||
List<HInstruction> inputs = makeStaticArgumentList(
|
||||
selector.callStructure, node.arguments, method.implementation);
|
||||
List<HInstruction> inputs =
|
||||
makeStaticArgumentList(selector.callStructure, node.arguments, method);
|
||||
push(buildInvokeSuper(selector, method, inputs, sourceInformation));
|
||||
}
|
||||
|
||||
@@ -3402,7 +3402,7 @@ class SsaBuilder extends ast.Visitor
|
||||
inputs.add(graph.addConstantNull(closedWorld));
|
||||
}
|
||||
inputs.addAll(makeStaticArgumentList(
|
||||
callStructure, send.arguments, constructorImplementation));
|
||||
callStructure, send.arguments, constructorImplementation.declaration));
|
||||
|
||||
TypeMask elementType = computeType(constructor);
|
||||
if (isFixedListConstructorCall) {
|
||||
@@ -3562,9 +3562,9 @@ class SsaBuilder extends ast.Visitor
|
||||
|
||||
/// Generate an invocation to the static or top level [function].
|
||||
void generateStaticFunctionInvoke(
|
||||
ast.Send node, FunctionElement function, CallStructure callStructure) {
|
||||
List<HInstruction> inputs = makeStaticArgumentList(
|
||||
callStructure, node.arguments, function.implementation);
|
||||
ast.Send node, MethodElement function, CallStructure callStructure) {
|
||||
List<HInstruction> inputs =
|
||||
makeStaticArgumentList(callStructure, node.arguments, function);
|
||||
|
||||
pushInvokeStatic(node, function, inputs,
|
||||
sourceInformation:
|
||||
@@ -4026,7 +4026,7 @@ class SsaBuilder extends ast.Visitor
|
||||
if (argument != null) {
|
||||
filteredArguments.add(argument);
|
||||
var jsName =
|
||||
backend.nativeData.getUnescapedJSInteropName(parameter.name);
|
||||
backend.nativeData.computeUnescapedJSInteropName(parameter.name);
|
||||
parameterNameMap[jsName] = new js.InterpolatedExpression(positions++);
|
||||
}
|
||||
i++;
|
||||
@@ -4045,7 +4045,8 @@ class SsaBuilder extends ast.Visitor
|
||||
..sourceInformation = sourceInformation;
|
||||
}
|
||||
var target = new HForeignCode(
|
||||
js.js.parseForeignJS("${backend.namer.fixedBackendMethodPath(element)}."
|
||||
js.js.parseForeignJS(
|
||||
"${backend.nativeData.getFixedBackendMethodPath(element)}."
|
||||
"${backend.nativeData.getFixedBackendName(element)}"),
|
||||
commonMasks.dynamicType,
|
||||
<HInstruction>[]);
|
||||
|
||||
@@ -206,13 +206,20 @@ class GlobalTypeInferenceResults {
|
||||
var key = (element is SynthesizedCallMethodElementX)
|
||||
? element.memberContext
|
||||
: element;
|
||||
bool isJsInterop = false;
|
||||
if (element is MemberElement) {
|
||||
isJsInterop = _compiler.backend.nativeData.isJsInteropMember(element);
|
||||
} else if (element is ClassElement) {
|
||||
// TODO(johnniwinther): Can we meet classes here?
|
||||
isJsInterop = _compiler.backend.nativeData.isJsInteropClass(element);
|
||||
}
|
||||
return _elementResults.putIfAbsent(
|
||||
element,
|
||||
() => new GlobalTypeInferenceElementResultImpl(
|
||||
element,
|
||||
_inferrer.inferrer.inTreeData[key],
|
||||
_inferrer,
|
||||
_compiler.backend.nativeData.isJsInterop(element),
|
||||
isJsInterop,
|
||||
dynamicType));
|
||||
}
|
||||
|
||||
|
||||
@@ -50,8 +50,20 @@ Future checkNativeData(Uri uri, {bool verbose: false}) async {
|
||||
NativeDataImpl nativeData1 = backend1.nativeData;
|
||||
NativeDataImpl nativeData2 = backend2.nativeData;
|
||||
|
||||
checkMaps(nativeData1.jsInteropNames, nativeData2.jsInteropNames,
|
||||
"NativeData.jsInteropNames", areElementsEquivalent, equality,
|
||||
checkMaps(
|
||||
nativeData1.jsInteropLibraryNames,
|
||||
nativeData2.jsInteropLibraryNames,
|
||||
"NativeData.jsInteropLibraryNames",
|
||||
areElementsEquivalent,
|
||||
equality,
|
||||
verbose: verbose);
|
||||
|
||||
checkMaps(nativeData1.jsInteropClassNames, nativeData2.jsInteropClassNames,
|
||||
"NativeData.jsInteropClassNames", areElementsEquivalent, equality,
|
||||
verbose: verbose);
|
||||
|
||||
checkMaps(nativeData1.jsInteropMemberNames, nativeData2.jsInteropMemberNames,
|
||||
"NativeData.jsInteropMemberNames", areElementsEquivalent, equality,
|
||||
verbose: verbose);
|
||||
|
||||
checkMaps(nativeData1.nativeMemberName, nativeData2.nativeMemberName,
|
||||
|
||||
Reference in New Issue
Block a user