OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library native; | 5 library native; |
6 | 6 |
7 import 'dart:collection' show Queue; | 7 import 'dart:collection' show Queue; |
8 import 'dart2jslib.dart' hide SourceString; | 8 import 'dart2jslib.dart' hide SourceString; |
9 import 'dart_types.dart'; | 9 import 'dart_types.dart'; |
10 import 'elements/elements.dart'; | 10 import 'elements/elements.dart'; |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
79 * exactly one of [unusedClasses], [pendingClasses] and [registeredClasses]. | 79 * exactly one of [unusedClasses], [pendingClasses] and [registeredClasses]. |
80 */ | 80 */ |
81 final Set<ClassElement> nativeClasses = new Set<ClassElement>(); | 81 final Set<ClassElement> nativeClasses = new Set<ClassElement>(); |
82 | 82 |
83 final Set<ClassElement> registeredClasses = new Set<ClassElement>(); | 83 final Set<ClassElement> registeredClasses = new Set<ClassElement>(); |
84 final Set<ClassElement> pendingClasses = new Set<ClassElement>(); | 84 final Set<ClassElement> pendingClasses = new Set<ClassElement>(); |
85 final Set<ClassElement> unusedClasses = new Set<ClassElement>(); | 85 final Set<ClassElement> unusedClasses = new Set<ClassElement>(); |
86 | 86 |
87 bool hasInstantiatedNativeClasses() => !registeredClasses.isEmpty; | 87 bool hasInstantiatedNativeClasses() => !registeredClasses.isEmpty; |
88 | 88 |
| 89 final Set<ClassElement> nativeClassesAndSubclasses = new Set<ClassElement>(); |
| 90 |
| 91 final Map<ClassElement, Set<ClassElement>> nonNativeSubclasses = |
| 92 new Map<ClassElement, Set<ClassElement>>(); |
| 93 |
89 /** | 94 /** |
90 * Records matched constraints ([SpecialType] or [DartType]). Once a type | 95 * Records matched constraints ([SpecialType] or [DartType]). Once a type |
91 * constraint has been matched, there is no need to match it again. | 96 * constraint has been matched, there is no need to match it again. |
92 */ | 97 */ |
93 final Set matchedTypeConstraints = new Set(); | 98 final Set matchedTypeConstraints = new Set(); |
94 | 99 |
95 /// Pending actions. Classes in [pendingClasses] have action thunks in | 100 /// Pending actions. Classes in [pendingClasses] have action thunks in |
96 /// [queue] to register the class. | 101 /// [queue] to register the class. |
97 final queue = new Queue(); | 102 final queue = new Queue(); |
98 bool flushing = false; | 103 bool flushing = false; |
99 | 104 |
100 /// Maps JS foreign calls to their computed native behavior. | 105 /// Maps JS foreign calls to their computed native behavior. |
101 final Map<Node, NativeBehavior> nativeBehaviors = | 106 final Map<Node, NativeBehavior> nativeBehaviors = |
102 new Map<Node, NativeBehavior>(); | 107 new Map<Node, NativeBehavior>(); |
103 | 108 |
104 final Enqueuer world; | 109 final Enqueuer world; |
105 final Compiler compiler; | 110 final Compiler compiler; |
106 final bool enableLiveTypeAnalysis; | 111 final bool enableLiveTypeAnalysis; |
107 | 112 |
108 ClassElement _annotationCreatesClass; | 113 ClassElement _annotationCreatesClass; |
109 ClassElement _annotationReturnsClass; | 114 ClassElement _annotationReturnsClass; |
110 ClassElement _annotationJsNameClass; | 115 ClassElement _annotationJsNameClass; |
111 | 116 |
112 /// Subclasses of [NativeEnqueuerBase] are constructed by the backend. | 117 /// Subclasses of [NativeEnqueuerBase] are constructed by the backend. |
113 NativeEnqueuerBase(this.world, this.compiler, this.enableLiveTypeAnalysis); | 118 NativeEnqueuerBase(this.world, this.compiler, this.enableLiveTypeAnalysis); |
114 | 119 |
115 void processNativeClasses(Iterable<LibraryElement> libraries) { | 120 void processNativeClasses(Iterable<LibraryElement> libraries) { |
116 libraries.forEach(processNativeClassesInLibrary); | 121 libraries.forEach(processNativeClassesInLibrary); |
117 processNativeClassesInLibrary(compiler.isolateHelperLibrary); | 122 processNativeClassesInLibrary(compiler.isolateHelperLibrary); |
| 123 |
| 124 processSubclassesOfNativeClasses(libraries); |
| 125 |
118 if (!enableLiveTypeAnalysis) { | 126 if (!enableLiveTypeAnalysis) { |
119 nativeClasses.forEach((c) => enqueueClass(c, 'forced')); | 127 nativeClasses.forEach((c) => enqueueClass(c, 'forced')); |
120 flushQueue(); | 128 flushQueue(); |
121 } | 129 } |
122 } | 130 } |
123 | 131 |
124 void processNativeClassesInLibrary(LibraryElement library) { | 132 void processNativeClassesInLibrary(LibraryElement library) { |
125 // Use implementation to ensure the inclusion of injected members. | 133 // Use implementation to ensure the inclusion of injected members. |
126 library.implementation.forEachLocalMember((Element element) { | 134 library.implementation.forEachLocalMember((Element element) { |
127 if (element.isClass() && element.isNative()) { | 135 if (element.isClass() && element.isNative()) { |
128 processNativeClass(element); | 136 processNativeClass(element); |
129 } | 137 } |
130 }); | 138 }); |
131 } | 139 } |
132 | 140 |
133 void processNativeClass(ClassElement classElement) { | 141 void processNativeClass(ClassElement classElement) { |
134 nativeClasses.add(classElement); | 142 nativeClasses.add(classElement); |
135 unusedClasses.add(classElement); | 143 unusedClasses.add(classElement); |
136 // Resolve class to ensure the class has valid inheritance info. | 144 // Resolve class to ensure the class has valid inheritance info. |
137 classElement.ensureResolved(compiler); | 145 classElement.ensureResolved(compiler); |
138 } | 146 } |
139 | 147 |
| 148 void processSubclassesOfNativeClasses(Iterable<LibraryElement> libraries) { |
| 149 // Collect potential subclasses, e.g. |
| 150 // |
| 151 // class B extends foo.A {} |
| 152 // |
| 153 // SourceString "A" has a potential subclass B. |
| 154 |
| 155 var potentialExtends = new Map<SourceString, Set<ClassElement>>(); |
| 156 |
| 157 libraries.forEach((library) { |
| 158 library.implementation.forEachLocalMember((element) { |
| 159 if (element.isClass()) { |
| 160 SourceString name = element.name; |
| 161 SourceString extendsName = findExtendsNameOfClass(element); |
| 162 if (extendsName != null) { |
| 163 Set<ClassElement> potentialSubclasses = |
| 164 potentialExtends.putIfAbsent( |
| 165 extendsName, |
| 166 () => new Set<ClassElement>()); |
| 167 potentialSubclasses.add(element); |
| 168 } |
| 169 } |
| 170 }); |
| 171 }); |
| 172 |
| 173 // Resolve all the native classes and any classes that might extend them in |
| 174 // [potentialExtends], and then check that the properly resolved class is in |
| 175 // fact a subclass of a native class. |
| 176 |
| 177 ClassElement nativeSuperclassOf(ClassElement classElement) { |
| 178 if (classElement.isNative()) return classElement; |
| 179 if (classElement.superclass == null) return null; |
| 180 return nativeSuperclassOf(classElement.superclass); |
| 181 } |
| 182 |
| 183 void walkPotentialSubclasses(ClassElement element) { |
| 184 if (nativeClassesAndSubclasses.contains(element)) return; |
| 185 element.ensureResolved(compiler); |
| 186 ClassElement nativeSuperclass = nativeSuperclassOf(element); |
| 187 if (nativeSuperclass != null) { |
| 188 nativeClassesAndSubclasses.add(element); |
| 189 if (!element.isNative()) { |
| 190 nonNativeSubclasses.putIfAbsent(nativeSuperclass, |
| 191 () => new Set<ClassElement>()) |
| 192 .add(element); |
| 193 } |
| 194 Set<ClassElement> potentialSubclasses = potentialExtends[element.name]; |
| 195 if (potentialSubclasses != null) { |
| 196 potentialSubclasses.forEach(walkPotentialSubclasses); |
| 197 } |
| 198 } |
| 199 } |
| 200 |
| 201 nativeClasses.forEach(walkPotentialSubclasses); |
| 202 |
| 203 nativeClasses.addAll(nativeClassesAndSubclasses); |
| 204 unusedClasses.addAll(nativeClassesAndSubclasses); |
| 205 } |
| 206 |
| 207 /** |
| 208 * Returns the source string of the class named in the extends clause, or |
| 209 * `null` if there is no extends clause. |
| 210 */ |
| 211 SourceString findExtendsNameOfClass(ClassElement classElement) { |
| 212 // "class B extends A ... {}" --> "A" |
| 213 // "class B extends foo.A ... {}" --> "A" |
| 214 // "class B<T> extends foo.A<T,T> with M1, M2 ... {}" --> "A" |
| 215 |
| 216 // We want to avoid calling classElement.parseNode on every class. Doing so |
| 217 // will slightly increase parse time and size and cause compiler errors and |
| 218 // warnings to me emitted in more unused code. |
| 219 |
| 220 // An alternative to this code is to extend the API of ClassElement to |
| 221 // expose the name of the extended element. |
| 222 |
| 223 // Pattern match the above cases in the token stream. |
| 224 // [abstract] class X extends [id.]* id |
| 225 |
| 226 Token skipTypeParameters(Token token) { |
| 227 BeginGroupToken beginGroupToken = token; |
| 228 Token endToken = beginGroupToken.endGroup; |
| 229 return endToken.next; |
| 230 //for (;;) { |
| 231 // token = token.next; |
| 232 // if (token.stringValue == '>') return token.next; |
| 233 // if (token.stringValue == '<') return skipTypeParameters(token); |
| 234 //} |
| 235 } |
| 236 |
| 237 SourceString scanForExtendsName(Token token) { |
| 238 if (token.stringValue == 'abstract') token = token.next; |
| 239 if (token.stringValue != 'class') return null; |
| 240 token = token.next; |
| 241 if (!token.isIdentifier()) return null; |
| 242 token = token.next; |
| 243 // class F<X extends B<X>> extends ... |
| 244 if (token.stringValue == '<') { |
| 245 token = skipTypeParameters(token); |
| 246 } |
| 247 if (token.stringValue != 'extends') return null; |
| 248 token = token.next; |
| 249 Token id = token; |
| 250 while (token.kind != EOF_TOKEN) { |
| 251 token = token.next; |
| 252 if (token.stringValue != '.') break; |
| 253 token = token.next; |
| 254 if (!token.isIdentifier()) return null; |
| 255 id = token; |
| 256 } |
| 257 // Should be at '{', 'with', 'implements', '<' or 'native'. |
| 258 return id.value; |
| 259 } |
| 260 |
| 261 return compiler.withCurrentElement(classElement, () { |
| 262 return scanForExtendsName(classElement.position()); |
| 263 }); |
| 264 } |
| 265 |
140 ClassElement get annotationCreatesClass { | 266 ClassElement get annotationCreatesClass { |
141 findAnnotationClasses(); | 267 findAnnotationClasses(); |
142 return _annotationCreatesClass; | 268 return _annotationCreatesClass; |
143 } | 269 } |
144 | 270 |
145 ClassElement get annotationReturnsClass { | 271 ClassElement get annotationReturnsClass { |
146 findAnnotationClasses(); | 272 findAnnotationClasses(); |
147 return _annotationReturnsClass; | 273 return _annotationReturnsClass; |
148 } | 274 } |
149 | 275 |
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
378 staticUse(name) => world.registerStaticUse(compiler.findHelper(name)); | 504 staticUse(name) => world.registerStaticUse(compiler.findHelper(name)); |
379 | 505 |
380 staticUse(const SourceString('dynamicFunction')); | 506 staticUse(const SourceString('dynamicFunction')); |
381 staticUse(const SourceString('dynamicSetMetadata')); | 507 staticUse(const SourceString('dynamicSetMetadata')); |
382 staticUse(const SourceString('defineProperty')); | 508 staticUse(const SourceString('defineProperty')); |
383 staticUse(const SourceString('toStringForNativeObject')); | 509 staticUse(const SourceString('toStringForNativeObject')); |
384 staticUse(const SourceString('hashCodeForNativeObject')); | 510 staticUse(const SourceString('hashCodeForNativeObject')); |
385 staticUse(const SourceString('convertDartClosureToJS')); | 511 staticUse(const SourceString('convertDartClosureToJS')); |
386 staticUse(const SourceString('defineNativeMethods')); | 512 staticUse(const SourceString('defineNativeMethods')); |
387 staticUse(const SourceString('defineNativeMethodsNonleaf')); | 513 staticUse(const SourceString('defineNativeMethodsNonleaf')); |
| 514 staticUse(const SourceString('defineNativeMethodsExtended')); |
388 // TODO(9577): Registering `defineNativeMethodsFinish` seems redundant with | 515 // TODO(9577): Registering `defineNativeMethodsFinish` seems redundant with |
389 // respect to the registering in the backend. When the backend registering | 516 // respect to the registering in the backend. When the backend registering |
390 // is removed as part of fixing Issue 9577, this can be the sole place | 517 // is removed as part of fixing Issue 9577, this can be the sole place |
391 // registering this methods. | 518 // registering this methods. |
392 staticUse(const SourceString('defineNativeMethodsFinish')); | 519 staticUse(const SourceString('defineNativeMethodsFinish')); |
393 | 520 |
394 addNativeExceptions(); | 521 addNativeExceptions(); |
395 } | 522 } |
396 | 523 |
397 addNativeExceptions() { | 524 addNativeExceptions() { |
(...skipping 618 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1016 'native "..." syntax is restricted to functions with zero parameters', | 1143 'native "..." syntax is restricted to functions with zero parameters', |
1017 node: nativeBody); | 1144 node: nativeBody); |
1018 } | 1145 } |
1019 LiteralString jsCode = nativeBody.asLiteralString(); | 1146 LiteralString jsCode = nativeBody.asLiteralString(); |
1020 builder.push(new HForeign.statement( | 1147 builder.push(new HForeign.statement( |
1021 new js.LiteralStatement(jsCode.dartString.slowToString()), | 1148 new js.LiteralStatement(jsCode.dartString.slowToString()), |
1022 <HInstruction>[], | 1149 <HInstruction>[], |
1023 new SideEffects())); | 1150 new SideEffects())); |
1024 } | 1151 } |
1025 } | 1152 } |
OLD | NEW |