Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library js_backend.native_data; | |
| 6 | |
| 7 import '../common.dart'; | |
| 8 import '../elements/elements.dart' show | |
| 9 ClassElement, | |
| 10 Element, | |
| 11 FunctionElement, | |
| 12 MemberElement; | |
| 13 | |
| 14 class NativeData { | |
|
Siggi Cherem (dart-lang)
2016/03/16 23:40:14
+doc on this class
Johnni Winther
2016/03/17 10:49:37
Done.
| |
| 15 /// The JavaScript names for elements implemented via typed JavaScript | |
| 16 /// interop. | |
| 17 Map<Element, String> jsInteropNames = <Element, String>{}; | |
| 18 | |
| 19 /// The JavaScript names for native JavaScript elements implemented. | |
| 20 Map<Element, String> nativeMemberName = <Element, String>{}; | |
| 21 | |
| 22 /// Tag info for native JavaScript classes names. See | |
| 23 /// [setNativeClassTagInfo]. | |
| 24 Map<ClassElement, String> nativeClassTagInfo = <ClassElement, String>{}; | |
| 25 | |
| 26 /// Returns `true` if [element] is explicitly marked as part of JsInterop. | |
| 27 bool _isJsInterop(Element element) { | |
| 28 return jsInteropNames.containsKey(element.declaration); | |
| 29 } | |
| 30 | |
| 31 /// Returns [element] as an explicit part of JsInterop. The js interop name is | |
| 32 /// expected to be computed later. | |
| 33 void markAsJsInterop(Element element) { | |
| 34 jsInteropNames[element.declaration] = null; | |
| 35 } | |
| 36 | |
| 37 /// Sets the explicit js interop [name] for [element]. | |
| 38 void setJsInteropName(Element element, String name) { | |
| 39 assert(invariant(element, | |
| 40 isJsInterop(element), | |
| 41 message: | |
| 42 'Element $element is not js interop but given a js interop name.')); | |
| 43 jsInteropNames[element.declaration] = name; | |
| 44 } | |
| 45 | |
| 46 /// Returns the explicit js interop name for [element]. | |
| 47 String getJsInteropName(Element element) { | |
| 48 return jsInteropNames[element.declaration]; | |
| 49 } | |
| 50 | |
| 51 /// Returns `true` if [element] is part of JsInterop. | |
| 52 bool isJsInterop(Element element) { | |
| 53 // An function is part of JsInterop in the following cases: | |
| 54 // * It has a jsInteropName annotation | |
| 55 // * It is external member of a class or library tagged as JsInterop. | |
| 56 if (element.isFunction || element.isConstructor || element.isAccessor) { | |
| 57 FunctionElement function = element; | |
| 58 if (!function.isExternal) return false; | |
| 59 | |
| 60 if (_isJsInterop(function)) return true; | |
| 61 if (function.isClassMember) return isJsInterop(function.contextClass); | |
| 62 if (function.isTopLevel) return isJsInterop(function.library); | |
| 63 return false; | |
| 64 } else { | |
| 65 return _isJsInterop(element); | |
| 66 } | |
| 67 } | |
| 68 | |
| 69 /// Returns `true` if the name of [element] is fixed for the generated | |
| 70 /// JavaScript. | |
| 71 bool hasFixedBackendName(Element element) { | |
| 72 return isJsInterop(element) || | |
| 73 nativeMemberName.containsKey(element.declaration); | |
| 74 } | |
| 75 | |
| 76 String _jsNameHelper(Element element) { | |
| 77 String jsInteropName = jsInteropNames[element.declaration]; | |
| 78 assert(invariant(element, | |
| 79 !(_isJsInterop(element) && jsInteropName == null), | |
| 80 message: | |
| 81 'Element $element is js interop but js interop name has not yet ' | |
| 82 'been computed.')); | |
| 83 if (jsInteropName != null && jsInteropName.isNotEmpty) { | |
| 84 return jsInteropName; | |
| 85 } | |
| 86 return element.isLibrary ? 'self' : element.name; | |
| 87 } | |
| 88 | |
| 89 /// Computes the name for [element] to use in the generated JavaScript. This | |
| 90 /// is either given through a native annotation or a js interop annotation. | |
| 91 String getFixedBackendName(Element element) { | |
| 92 String name = nativeMemberName[element.declaration]; | |
| 93 if (name == null && isJsInterop(element)) { | |
| 94 // If an element isJsInterop but _isJsInterop is false that means it is | |
| 95 // considered interop as the parent class is interop. | |
| 96 name = _jsNameHelper( | |
| 97 element.isConstructor ? element.enclosingClass : element); | |
| 98 nativeMemberName[element.declaration] = name; | |
| 99 } | |
| 100 return name; | |
| 101 } | |
| 102 | |
| 103 /// Whether [element] corresponds to a native JavaScript construct either | |
| 104 /// through the native mechanism (`@Native(...)` or the `native` pseudo | |
| 105 /// keyword) which is only allowed for internal libraries or via the typed | |
| 106 /// JavaScriptInterop mechanism which is allowed for user libraries. | |
| 107 bool isNative(Element element) { | |
| 108 if (isJsInterop(element)) return true; | |
| 109 if (element.isClass) { | |
| 110 return nativeClassTagInfo.containsKey(element.declaration); | |
| 111 } else { | |
| 112 return nativeMemberName.containsKey(element.declaration); | |
| 113 } | |
| 114 } | |
| 115 | |
| 116 /// Sets the native [name] for the member [element]. This name is used for | |
| 117 /// [element] in the generated JavaScript. | |
| 118 void setNativeMemberName(MemberElement element, String name) { | |
| 119 // TODO(johnniwinther): Avoid setting this more than once. The enqueuer | |
| 120 // might enqueue [element] several times (before processing it) and computes | |
| 121 // name on each call to `internalAddToWorkList`. | |
| 122 assert(invariant(element, | |
| 123 nativeMemberName[element.declaration] == null || | |
| 124 nativeMemberName[element.declaration] == name, | |
| 125 message: | |
| 126 "Native member name set inconsistently on $element: " | |
| 127 "Existing name '${nativeMemberName[element.declaration]}', " | |
| 128 "new name '$name'.")); | |
| 129 nativeMemberName[element.declaration] = name; | |
| 130 } | |
| 131 | |
| 132 /// Sets the native tag info for [cls]. | |
| 133 /// | |
| 134 /// The tag info string contains comma-separated 'words' which are either | |
| 135 /// dispatch tags (having JavaScript identifier syntax) and directives that | |
| 136 /// begin with `!`. | |
| 137 void setNativeClassTagInfo(ClassElement cls, String tagInfo) { | |
| 138 // TODO(johnniwinther): Assert that this is only called once. The memory | |
| 139 // compiler copies pre-processed elements into a new compiler through | |
| 140 // [Compiler.onLibraryScanned] and thereby causes multiple calls to this | |
| 141 // method. | |
| 142 assert(invariant(cls, | |
| 143 nativeClassTagInfo[cls.declaration] == null || | |
| 144 nativeClassTagInfo[cls.declaration] == tagInfo, | |
| 145 message: | |
| 146 "Native tag info set inconsistently on $cls: " | |
| 147 "Existing tag info '${nativeClassTagInfo[cls.declaration]}', " | |
| 148 "new tag info '$tagInfo'.")); | |
| 149 nativeClassTagInfo[cls.declaration] = tagInfo; | |
| 150 } | |
| 151 | |
| 152 /// Returns the list of native tag words for [cls]. | |
| 153 List<String> getNativeTagsOfClassRaw(ClassElement cls) { | |
| 154 String quotedName = nativeClassTagInfo[cls.declaration]; | |
| 155 return quotedName.substring(1, quotedName.length - 1).split(','); | |
| 156 } | |
| 157 | |
| 158 /// Returns the list of non-directive native tag words for [cls]. | |
| 159 List<String> getNativeTagsOfClass(ClassElement cls) { | |
| 160 return getNativeTagsOfClassRaw(cls).where( | |
| 161 (s) => !s.startsWith('!')).toList(); | |
| 162 } | |
| 163 | |
| 164 /// Returns `true` if [cls] has a `!nonleaf` tag word. | |
| 165 bool hasNativeTagsForcedNonLeaf(ClassElement cls) { | |
| 166 return getNativeTagsOfClassRaw(cls).contains('!nonleaf'); | |
| 167 } | |
| 168 | |
| 169 bool hasNativeName(Element element) { | |
| 170 return nativeMemberName.containsKey(element); | |
| 171 } | |
| 172 } | |
| OLD | NEW |