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 /** | 5 /** |
6 * This library contains the infrastructure to parse and integrate patch files. | 6 * This library contains the infrastructure to parse and integrate patch files. |
7 * | 7 * |
8 * Three types of elements can be patched: [LibraryElement], [ClassElement], | 8 * Three types of elements can be patched: [LibraryElement], [ClassElement], |
9 * [FunctionElement]. Patches are introduced in patch libraries which are loaded | 9 * [FunctionElement]. Patches are introduced in patch libraries which are loaded |
10 * together with the corresponding origin library. Which libraries that are | 10 * together with the corresponding origin library. Which libraries that are |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 * - Builders shift between declaration and implementation depending on usages. | 109 * - Builders shift between declaration and implementation depending on usages. |
110 * - Compile-time constants use constructor implementation exclusively. | 110 * - Compile-time constants use constructor implementation exclusively. |
111 * - Work on function parameters is performed on the declaration of the function | 111 * - Work on function parameters is performed on the declaration of the function |
112 * element. | 112 * element. |
113 */ | 113 */ |
114 | 114 |
115 library dart2js.patchparser; | 115 library dart2js.patchparser; |
116 | 116 |
117 import 'dart:async'; | 117 import 'dart:async'; |
118 | 118 |
119 import 'constants/values.dart' show | 119 import 'constants/values.dart' show ConstantValue; |
120 ConstantValue; | |
121 import 'common.dart'; | 120 import 'common.dart'; |
122 import 'compiler.dart' show | 121 import 'compiler.dart' show Compiler; |
123 Compiler; | 122 import 'common/tasks.dart' show CompilerTask; |
124 import 'common/tasks.dart' show | 123 import 'dart_types.dart' show DartType; |
125 CompilerTask; | |
126 import 'dart_types.dart' show | |
127 DartType; | |
128 import 'elements/elements.dart'; | 124 import 'elements/elements.dart'; |
129 import 'elements/modelx.dart' show | 125 import 'elements/modelx.dart' |
130 BaseFunctionElementX, | 126 show |
131 ClassElementX, | 127 BaseFunctionElementX, |
132 GetterElementX, | 128 ClassElementX, |
133 LibraryElementX, | 129 GetterElementX, |
134 MetadataAnnotationX, | 130 LibraryElementX, |
135 SetterElementX; | 131 MetadataAnnotationX, |
136 import 'js_backend/js_backend.dart' show | 132 SetterElementX; |
137 JavaScriptBackend; | 133 import 'js_backend/js_backend.dart' show JavaScriptBackend; |
138 import 'library_loader.dart' show | 134 import 'library_loader.dart' show LibraryLoader; |
139 LibraryLoader; | 135 import 'options.dart' show ParserOptions; |
140 import 'options.dart' show | 136 import 'parser/listener.dart' show Listener, ParserError; |
141 ParserOptions; | 137 import 'parser/element_listener.dart' show ElementListener; |
142 import 'parser/listener.dart' show | 138 import 'parser/member_listener.dart' show MemberListener; |
143 Listener, | 139 import 'parser/partial_elements.dart' show PartialClassElement; |
144 ParserError; | 140 import 'parser/partial_parser.dart' show PartialParser; |
145 import 'parser/element_listener.dart' show | 141 import 'parser/parser.dart' show Parser; |
146 ElementListener; | 142 import 'scanner/scanner.dart' show Scanner; |
147 import 'parser/member_listener.dart' show | |
148 MemberListener; | |
149 import 'parser/partial_elements.dart' show | |
150 PartialClassElement; | |
151 import 'parser/partial_parser.dart' show | |
152 PartialParser; | |
153 import 'parser/parser.dart' show | |
154 Parser; | |
155 import 'scanner/scanner.dart' show | |
156 Scanner; | |
157 import 'script.dart'; | 143 import 'script.dart'; |
158 import 'tokens/token.dart' show | 144 import 'tokens/token.dart' show StringToken, Token; |
159 StringToken, | |
160 Token; | |
161 | 145 |
162 class PatchParserTask extends CompilerTask { | 146 class PatchParserTask extends CompilerTask { |
163 final String name = "Patching Parser"; | 147 final String name = "Patching Parser"; |
164 final ParserOptions parserOptions; | 148 final ParserOptions parserOptions; |
165 | 149 |
166 PatchParserTask(Compiler compiler, this.parserOptions) : super(compiler); | 150 PatchParserTask(Compiler compiler, this.parserOptions) : super(compiler); |
167 | 151 |
168 /** | 152 /** |
169 * Scans a library patch file, applies the method patches and | 153 * Scans a library patch file, applies the method patches and |
170 * injections to the library, and returns a list of class | 154 * injections to the library, and returns a list of class |
171 * patches. | 155 * patches. |
172 */ | 156 */ |
173 Future patchLibrary(LibraryLoader loader, | 157 Future patchLibrary( |
174 Uri patchUri, LibraryElement originLibrary) { | 158 LibraryLoader loader, Uri patchUri, LibraryElement originLibrary) { |
175 return compiler.readScript(patchUri, originLibrary) | 159 return compiler.readScript(patchUri, originLibrary).then((Script script) { |
176 .then((Script script) { | |
177 var patchLibrary = new LibraryElementX(script, null, originLibrary); | 160 var patchLibrary = new LibraryElementX(script, null, originLibrary); |
178 return reporter.withCurrentElement(patchLibrary, () { | 161 return reporter.withCurrentElement(patchLibrary, () { |
179 loader.registerNewLibrary(patchLibrary); | 162 loader.registerNewLibrary(patchLibrary); |
180 reporter.withCurrentElement(patchLibrary.entryCompilationUnit, () { | 163 reporter.withCurrentElement(patchLibrary.entryCompilationUnit, () { |
181 // This patches the elements of the patch library into [library]. | 164 // This patches the elements of the patch library into [library]. |
182 // Injected elements are added directly under the compilation unit. | 165 // Injected elements are added directly under the compilation unit. |
183 // Patch elements are stored on the patched functions or classes. | 166 // Patch elements are stored on the patched functions or classes. |
184 scanLibraryElements(patchLibrary.entryCompilationUnit); | 167 scanLibraryElements(patchLibrary.entryCompilationUnit); |
185 }); | 168 }); |
186 return loader.processLibraryTags(patchLibrary); | 169 return loader.processLibraryTags(patchLibrary); |
187 }); | 170 }); |
188 }); | 171 }); |
189 } | 172 } |
190 | 173 |
191 void scanLibraryElements(CompilationUnitElement compilationUnit) { | 174 void scanLibraryElements(CompilationUnitElement compilationUnit) { |
192 measure(() { | 175 measure(() { |
193 // TODO(johnniwinther): Test that parts and exports are handled correctly. | 176 // TODO(johnniwinther): Test that parts and exports are handled correctly. |
194 Script script = compilationUnit.script; | 177 Script script = compilationUnit.script; |
195 Token tokens = new Scanner(script.file).tokenize(); | 178 Token tokens = new Scanner(script.file).tokenize(); |
196 Function idGenerator = compiler.getNextFreeClassId; | 179 Function idGenerator = compiler.getNextFreeClassId; |
197 Listener patchListener = new PatchElementListener(compiler, | 180 Listener patchListener = |
198 compilationUnit, | 181 new PatchElementListener(compiler, compilationUnit, idGenerator); |
199 idGenerator); | |
200 try { | 182 try { |
201 new PartialParser(patchListener, parserOptions).parseUnit(tokens); | 183 new PartialParser(patchListener, parserOptions).parseUnit(tokens); |
202 } on ParserError catch (e) { | 184 } on ParserError catch (e) { |
203 // No need to recover from a parser error in platform libraries, user | 185 // No need to recover from a parser error in platform libraries, user |
204 // will never see this if the libraries are tested correctly. | 186 // will never see this if the libraries are tested correctly. |
205 reporter.internalError( | 187 reporter.internalError( |
206 compilationUnit, "Parser error in patch file: $e"); | 188 compilationUnit, "Parser error in patch file: $e"); |
207 } | 189 } |
208 }); | 190 }); |
209 } | 191 } |
210 | 192 |
211 void parsePatchClassNode(PartialClassElement cls) { | 193 void parsePatchClassNode(PartialClassElement cls) { |
212 // Parse [PartialClassElement] using a "patch"-aware parser instead | 194 // Parse [PartialClassElement] using a "patch"-aware parser instead |
213 // of calling its [parseNode] method. | 195 // of calling its [parseNode] method. |
214 if (cls.cachedNode != null) return; | 196 if (cls.cachedNode != null) return; |
215 | 197 |
216 measure(() => reporter.withCurrentElement(cls, () { | 198 measure(() => reporter.withCurrentElement(cls, () { |
217 MemberListener listener = new PatchMemberListener(compiler, cls); | 199 MemberListener listener = new PatchMemberListener(compiler, cls); |
218 Parser parser = new PatchClassElementParser(listener, parserOptions); | 200 Parser parser = new PatchClassElementParser(listener, parserOptions); |
219 try { | 201 try { |
220 Token token = parser.parseTopLevelDeclaration(cls.beginToken); | 202 Token token = parser.parseTopLevelDeclaration(cls.beginToken); |
221 assert(identical(token, cls.endToken.next)); | 203 assert(identical(token, cls.endToken.next)); |
222 } on ParserError catch (e) { | 204 } on ParserError catch (e) { |
223 // No need to recover from a parser error in platform libraries, user | 205 // No need to recover from a parser error in platform libraries, use
r |
224 // will never see this if the libraries are tested correctly. | 206 // will never see this if the libraries are tested correctly. |
225 reporter.internalError( | 207 reporter.internalError(cls, "Parser error in patch file: $e"); |
226 cls, "Parser error in patch file: $e"); | 208 } |
227 } | 209 cls.cachedNode = listener.popNode(); |
228 cls.cachedNode = listener.popNode(); | 210 assert(listener.nodes.isEmpty); |
229 assert(listener.nodes.isEmpty); | 211 })); |
230 })); | |
231 } | 212 } |
232 } | 213 } |
233 | 214 |
234 class PatchMemberListener extends MemberListener { | 215 class PatchMemberListener extends MemberListener { |
235 final Compiler compiler; | 216 final Compiler compiler; |
236 | 217 |
237 PatchMemberListener(Compiler compiler, ClassElement enclosingClass) | 218 PatchMemberListener(Compiler compiler, ClassElement enclosingClass) |
238 : this.compiler = compiler, | 219 : this.compiler = compiler, |
239 super(compiler.parsing.getScannerOptionsFor(enclosingClass), | 220 super(compiler.parsing.getScannerOptionsFor(enclosingClass), |
240 compiler.reporter, | 221 compiler.reporter, enclosingClass); |
241 enclosingClass); | |
242 | 222 |
243 @override | 223 @override |
244 void addMember(Element patch) { | 224 void addMember(Element patch) { |
245 addMetadata(patch); | 225 addMetadata(patch); |
246 | 226 |
247 PatchVersion patchVersion = getPatchVersion(compiler, patch); | 227 PatchVersion patchVersion = getPatchVersion(compiler, patch); |
248 if (patchVersion != null) { | 228 if (patchVersion != null) { |
249 if (patchVersion.isActive(compiler.patchVersion)) { | 229 if (patchVersion.isActive(compiler.patchVersion)) { |
250 Element origin = enclosingClass.origin.localLookup(patch.name); | 230 Element origin = enclosingClass.origin.localLookup(patch.name); |
251 patchElement(compiler, reporter, origin, patch); | 231 patchElement(compiler, reporter, origin, patch); |
(...skipping 20 matching lines...) Expand all Loading... |
272 | 252 |
273 Token parseClassBody(Token token) => fullParseClassBody(token); | 253 Token parseClassBody(Token token) => fullParseClassBody(token); |
274 } | 254 } |
275 | 255 |
276 /** | 256 /** |
277 * Extension of [ElementListener] for parsing patch files. | 257 * Extension of [ElementListener] for parsing patch files. |
278 */ | 258 */ |
279 class PatchElementListener extends ElementListener implements Listener { | 259 class PatchElementListener extends ElementListener implements Listener { |
280 final Compiler compiler; | 260 final Compiler compiler; |
281 | 261 |
282 PatchElementListener(Compiler compiler, | 262 PatchElementListener( |
283 CompilationUnitElement patchElement, | 263 Compiler compiler, CompilationUnitElement patchElement, int idGenerator()) |
284 int idGenerator()) | 264 : this.compiler = compiler, |
285 : this.compiler = compiler, | 265 super(compiler.parsing.getScannerOptionsFor(patchElement), |
286 super(compiler.parsing.getScannerOptionsFor(patchElement), | |
287 compiler.reporter, patchElement, idGenerator); | 266 compiler.reporter, patchElement, idGenerator); |
288 | 267 |
289 @override | 268 @override |
290 void pushElement(Element patch) { | 269 void pushElement(Element patch) { |
291 popMetadata(patch); | 270 popMetadata(patch); |
292 | 271 |
293 PatchVersion patchVersion = getPatchVersion(compiler, patch); | 272 PatchVersion patchVersion = getPatchVersion(compiler, patch); |
294 if (patchVersion != null) { | 273 if (patchVersion != null) { |
295 if (patchVersion.isActive(compiler.patchVersion)) { | 274 if (patchVersion.isActive(compiler.patchVersion)) { |
296 LibraryElement originLibrary = compilationUnitElement.library; | 275 LibraryElement originLibrary = compilationUnitElement.library; |
297 assert(originLibrary.isPatched); | 276 assert(originLibrary.isPatched); |
298 Element origin = originLibrary.localLookup(patch.name); | 277 Element origin = originLibrary.localLookup(patch.name); |
299 patchElement(compiler, reporter, origin, patch); | 278 patchElement(compiler, reporter, origin, patch); |
300 compilationUnitElement.addMember(patch, reporter); | 279 compilationUnitElement.addMember(patch, reporter); |
301 } else { | 280 } else { |
302 // Skip this element. | 281 // Skip this element. |
303 } | 282 } |
304 } else { | 283 } else { |
305 if (Name.isPublicName(patch.name)) { | 284 if (Name.isPublicName(patch.name)) { |
306 reporter.reportErrorMessage(patch, MessageKind.INJECTED_PUBLIC_MEMBER); | 285 reporter.reportErrorMessage(patch, MessageKind.INJECTED_PUBLIC_MEMBER); |
307 } | 286 } |
308 compilationUnitElement.addMember(patch, reporter); | 287 compilationUnitElement.addMember(patch, reporter); |
309 } | 288 } |
310 } | 289 } |
311 } | 290 } |
312 | 291 |
313 void patchElement(Compiler compiler, | 292 void patchElement(Compiler compiler, DiagnosticReporter reporter, |
314 DiagnosticReporter reporter, | 293 Element origin, Element patch) { |
315 Element origin, | |
316 Element patch) { | |
317 if (origin == null) { | 294 if (origin == null) { |
318 reporter.reportErrorMessage( | 295 reporter.reportErrorMessage( |
319 patch, MessageKind.PATCH_NON_EXISTING, {'name': patch.name}); | 296 patch, MessageKind.PATCH_NON_EXISTING, {'name': patch.name}); |
320 return; | 297 return; |
321 } | 298 } |
322 | 299 |
323 if (!(origin.isClass || | 300 if (!(origin.isClass || |
324 origin.isConstructor || | 301 origin.isConstructor || |
325 origin.isFunction || | 302 origin.isFunction || |
326 origin.isAbstractField)) { | 303 origin.isAbstractField)) { |
327 // TODO(ahe): Remove this error when the parser rejects all bad modifiers. | 304 // TODO(ahe): Remove this error when the parser rejects all bad modifiers. |
328 reporter.reportErrorMessage(origin, MessageKind.PATCH_NONPATCHABLE); | 305 reporter.reportErrorMessage(origin, MessageKind.PATCH_NONPATCHABLE); |
329 return; | 306 return; |
330 } | 307 } |
331 if (patch.isClass) { | 308 if (patch.isClass) { |
332 tryPatchClass(compiler, reporter, origin, patch); | 309 tryPatchClass(compiler, reporter, origin, patch); |
333 } else if (patch.isGetter) { | 310 } else if (patch.isGetter) { |
334 tryPatchGetter(reporter, origin, patch); | 311 tryPatchGetter(reporter, origin, patch); |
335 } else if (patch.isSetter) { | 312 } else if (patch.isSetter) { |
336 tryPatchSetter(reporter, origin, patch); | 313 tryPatchSetter(reporter, origin, patch); |
337 } else if (patch.isConstructor) { | 314 } else if (patch.isConstructor) { |
338 tryPatchConstructor(reporter, origin, patch); | 315 tryPatchConstructor(reporter, origin, patch); |
339 } else if(patch.isFunction) { | 316 } else if (patch.isFunction) { |
340 tryPatchFunction(reporter, origin, patch); | 317 tryPatchFunction(reporter, origin, patch); |
341 } else { | 318 } else { |
342 // TODO(ahe): Remove this error when the parser rejects all bad modifiers. | 319 // TODO(ahe): Remove this error when the parser rejects all bad modifiers. |
343 reporter.reportErrorMessage(patch, MessageKind.PATCH_NONPATCHABLE); | 320 reporter.reportErrorMessage(patch, MessageKind.PATCH_NONPATCHABLE); |
344 } | 321 } |
345 } | 322 } |
346 | 323 |
347 void tryPatchClass(Compiler compiler, | 324 void tryPatchClass(Compiler compiler, DiagnosticReporter reporter, |
348 DiagnosticReporter reporter, | 325 Element origin, ClassElement patch) { |
349 Element origin, | |
350 ClassElement patch) { | |
351 if (!origin.isClass) { | 326 if (!origin.isClass) { |
352 reporter.reportError( | 327 reporter.reportError( |
353 reporter.createMessage( | 328 reporter.createMessage( |
354 origin, | 329 origin, MessageKind.PATCH_NON_CLASS, {'className': patch.name}), |
355 MessageKind.PATCH_NON_CLASS, | |
356 {'className': patch.name}), | |
357 <DiagnosticMessage>[ | 330 <DiagnosticMessage>[ |
358 reporter.createMessage( | 331 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_CLASS, |
359 patch, | 332 {'className': patch.name}), |
360 MessageKind.PATCH_POINT_TO_CLASS, | |
361 {'className': patch.name}), | |
362 ]); | 333 ]); |
363 return; | 334 return; |
364 } | 335 } |
365 patchClass(compiler, reporter, origin, patch); | 336 patchClass(compiler, reporter, origin, patch); |
366 } | 337 } |
367 | 338 |
368 void patchClass(Compiler compiler, | 339 void patchClass(Compiler compiler, DiagnosticReporter reporter, |
369 DiagnosticReporter reporter, | 340 ClassElementX origin, ClassElementX patch) { |
370 ClassElementX origin, | |
371 ClassElementX patch) { | |
372 if (origin.isPatched) { | 341 if (origin.isPatched) { |
373 reporter.internalError(origin, | 342 reporter.internalError(origin, "Patching the same class more than once."); |
374 "Patching the same class more than once."); | |
375 } | 343 } |
376 origin.applyPatch(patch); | 344 origin.applyPatch(patch); |
377 checkNativeAnnotation(compiler, patch); | 345 checkNativeAnnotation(compiler, patch); |
378 } | 346 } |
379 | 347 |
380 /// Check whether [cls] has a `@Native(...)` annotation, and if so, set its | 348 /// Check whether [cls] has a `@Native(...)` annotation, and if so, set its |
381 /// native name from the annotation. | 349 /// native name from the annotation. |
382 checkNativeAnnotation(Compiler compiler, ClassElement cls) { | 350 checkNativeAnnotation(Compiler compiler, ClassElement cls) { |
383 EagerAnnotationHandler.checkAnnotation(compiler, cls, | 351 EagerAnnotationHandler.checkAnnotation( |
384 const NativeAnnotationHandler()); | 352 compiler, cls, const NativeAnnotationHandler()); |
385 } | 353 } |
386 | 354 |
387 checkJsInteropAnnotation(Compiler compiler, element) { | 355 checkJsInteropAnnotation(Compiler compiler, element) { |
388 EagerAnnotationHandler.checkAnnotation(compiler, element, | 356 EagerAnnotationHandler.checkAnnotation( |
389 const JsInteropAnnotationHandler()); | 357 compiler, element, const JsInteropAnnotationHandler()); |
390 } | 358 } |
391 | 359 |
392 | |
393 /// Abstract interface for pre-resolution detection of metadata. | 360 /// Abstract interface for pre-resolution detection of metadata. |
394 /// | 361 /// |
395 /// The detection is handled in two steps: | 362 /// The detection is handled in two steps: |
396 /// - match the annotation syntactically and assume that the annotation is valid | 363 /// - match the annotation syntactically and assume that the annotation is valid |
397 /// if it looks correct, | 364 /// if it looks correct, |
398 /// - setup a deferred action to check that the annotation has a valid constant | 365 /// - setup a deferred action to check that the annotation has a valid constant |
399 /// value and report an internal error if not. | 366 /// value and report an internal error if not. |
400 abstract class EagerAnnotationHandler<T> { | 367 abstract class EagerAnnotationHandler<T> { |
401 /// Checks that [annotation] looks like a matching annotation and optionally | 368 /// Checks that [annotation] looks like a matching annotation and optionally |
402 /// applies actions on [element]. Returns a non-null annotation marker if the | 369 /// applies actions on [element]. Returns a non-null annotation marker if the |
403 /// annotation matched and should be validated. | 370 /// annotation matched and should be validated. |
404 T apply(Compiler compiler, | 371 T apply(Compiler compiler, Element element, MetadataAnnotation annotation); |
405 Element element, | |
406 MetadataAnnotation annotation); | |
407 | 372 |
408 /// Checks that the annotation value is valid. | 373 /// Checks that the annotation value is valid. |
409 void validate(Compiler compiler, | 374 void validate(Compiler compiler, Element element, |
410 Element element, | 375 MetadataAnnotation annotation, ConstantValue constant); |
411 MetadataAnnotation annotation, | |
412 ConstantValue constant); | |
413 | |
414 | 376 |
415 /// Checks [element] for metadata matching the [handler]. Return a non-null | 377 /// Checks [element] for metadata matching the [handler]. Return a non-null |
416 /// annotation marker matching metadata was found. | 378 /// annotation marker matching metadata was found. |
417 static checkAnnotation(Compiler compiler, | 379 static checkAnnotation( |
418 Element element, | 380 Compiler compiler, Element element, EagerAnnotationHandler handler) { |
419 EagerAnnotationHandler handler) { | |
420 for (MetadataAnnotation annotation in element.implementation.metadata) { | 381 for (MetadataAnnotation annotation in element.implementation.metadata) { |
421 var result = handler.apply(compiler, element, annotation); | 382 var result = handler.apply(compiler, element, annotation); |
422 if (result != null) { | 383 if (result != null) { |
423 // TODO(johnniwinther): Perform this check in | 384 // TODO(johnniwinther): Perform this check in |
424 // [Compiler.onLibrariesLoaded]. | 385 // [Compiler.onLibrariesLoaded]. |
425 compiler.enqueuer.resolution.addDeferredAction(element, () { | 386 compiler.enqueuer.resolution.addDeferredAction(element, () { |
426 annotation.ensureResolved(compiler.resolution); | 387 annotation.ensureResolved(compiler.resolution); |
427 handler.validate( | 388 handler.validate(compiler, element, annotation, |
428 compiler, element, annotation, | |
429 compiler.constants.getConstantValue(annotation.constant)); | 389 compiler.constants.getConstantValue(annotation.constant)); |
430 }); | 390 }); |
431 return result; | 391 return result; |
432 } | 392 } |
433 } | 393 } |
434 return null; | 394 return null; |
435 } | 395 } |
436 } | 396 } |
437 | 397 |
438 /// Annotation handler for pre-resolution detection of `@Native(...)` | 398 /// Annotation handler for pre-resolution detection of `@Native(...)` |
439 /// annotations. | 399 /// annotations. |
440 class NativeAnnotationHandler implements EagerAnnotationHandler<String> { | 400 class NativeAnnotationHandler implements EagerAnnotationHandler<String> { |
441 const NativeAnnotationHandler(); | 401 const NativeAnnotationHandler(); |
442 | 402 |
443 String getNativeAnnotation(MetadataAnnotation annotation) { | 403 String getNativeAnnotation(MetadataAnnotation annotation) { |
444 if (annotation.beginToken != null && | 404 if (annotation.beginToken != null && |
445 annotation.beginToken.next.value == 'Native') { | 405 annotation.beginToken.next.value == 'Native') { |
446 // Skipping '@', 'Native', and '('. | 406 // Skipping '@', 'Native', and '('. |
447 Token argument = annotation.beginToken.next.next.next; | 407 Token argument = annotation.beginToken.next.next.next; |
448 if (argument is StringToken) { | 408 if (argument is StringToken) { |
449 return argument.value; | 409 return argument.value; |
450 } | 410 } |
451 } | 411 } |
452 return null; | 412 return null; |
453 } | 413 } |
454 | 414 |
455 String apply(Compiler compiler, | 415 String apply( |
456 Element element, | 416 Compiler compiler, Element element, MetadataAnnotation annotation) { |
457 MetadataAnnotation annotation) { | |
458 if (element.isClass) { | 417 if (element.isClass) { |
459 String native = getNativeAnnotation(annotation); | 418 String native = getNativeAnnotation(annotation); |
460 if (native != null) { | 419 if (native != null) { |
461 JavaScriptBackend backend = compiler.backend; | 420 JavaScriptBackend backend = compiler.backend; |
462 backend.nativeData.setNativeClassTagInfo(element, native); | 421 backend.nativeData.setNativeClassTagInfo(element, native); |
463 return native; | 422 return native; |
464 } | 423 } |
465 } | 424 } |
466 return null; | 425 return null; |
467 } | 426 } |
468 | 427 |
469 void validate(Compiler compiler, | 428 void validate(Compiler compiler, Element element, |
470 Element element, | 429 MetadataAnnotation annotation, ConstantValue constant) { |
471 MetadataAnnotation annotation, | |
472 ConstantValue constant) { | |
473 DartType annotationType = constant.getType(compiler.coreTypes); | 430 DartType annotationType = constant.getType(compiler.coreTypes); |
474 if (annotationType.element != compiler.nativeAnnotationClass) { | 431 if (annotationType.element != compiler.nativeAnnotationClass) { |
475 DiagnosticReporter reporter = compiler.reporter; | 432 DiagnosticReporter reporter = compiler.reporter; |
476 reporter.internalError(annotation, 'Invalid @Native(...) annotation.'); | 433 reporter.internalError(annotation, 'Invalid @Native(...) annotation.'); |
477 } | 434 } |
478 } | 435 } |
479 } | 436 } |
480 | 437 |
481 /// Annotation handler for pre-resolution detection of `@JS(...)` | 438 /// Annotation handler for pre-resolution detection of `@JS(...)` |
482 /// annotations. | 439 /// annotations. |
483 class JsInteropAnnotationHandler implements EagerAnnotationHandler<bool> { | 440 class JsInteropAnnotationHandler implements EagerAnnotationHandler<bool> { |
484 const JsInteropAnnotationHandler(); | 441 const JsInteropAnnotationHandler(); |
485 | 442 |
486 bool hasJsNameAnnotation(MetadataAnnotation annotation) => | 443 bool hasJsNameAnnotation(MetadataAnnotation annotation) => |
487 annotation.beginToken != null && annotation.beginToken.next.value == 'JS'; | 444 annotation.beginToken != null && annotation.beginToken.next.value == 'JS'; |
488 | 445 |
489 bool apply(Compiler compiler, | 446 bool apply( |
490 Element element, | 447 Compiler compiler, Element element, MetadataAnnotation annotation) { |
491 MetadataAnnotation annotation) { | |
492 bool hasJsInterop = hasJsNameAnnotation(annotation); | 448 bool hasJsInterop = hasJsNameAnnotation(annotation); |
493 if (hasJsInterop) { | 449 if (hasJsInterop) { |
494 JavaScriptBackend backend = compiler.backend; | 450 JavaScriptBackend backend = compiler.backend; |
495 backend.nativeData.markAsJsInterop(element); | 451 backend.nativeData.markAsJsInterop(element); |
496 } | 452 } |
497 // Due to semantics of apply in the baseclass we have to return null to | 453 // Due to semantics of apply in the baseclass we have to return null to |
498 // indicate that no match was found. | 454 // indicate that no match was found. |
499 return hasJsInterop ? true : null; | 455 return hasJsInterop ? true : null; |
500 } | 456 } |
501 | 457 |
502 @override | 458 @override |
503 void validate(Compiler compiler, | 459 void validate(Compiler compiler, Element element, |
504 Element element, | 460 MetadataAnnotation annotation, ConstantValue constant) { |
505 MetadataAnnotation annotation, | |
506 ConstantValue constant) { | |
507 JavaScriptBackend backend = compiler.backend; | 461 JavaScriptBackend backend = compiler.backend; |
508 if (constant.getType(compiler.coreTypes).element != | 462 if (constant.getType(compiler.coreTypes).element != |
509 backend.helpers.jsAnnotationClass) { | 463 backend.helpers.jsAnnotationClass) { |
510 compiler.reporter.internalError(annotation, 'Invalid @JS(...) annotation.'
); | 464 compiler.reporter |
| 465 .internalError(annotation, 'Invalid @JS(...) annotation.'); |
511 } | 466 } |
512 } | 467 } |
513 } | 468 } |
514 | 469 |
515 /// Annotation handler for pre-resolution detection of `@patch` annotations. | 470 /// Annotation handler for pre-resolution detection of `@patch` annotations. |
516 class PatchAnnotationHandler implements EagerAnnotationHandler<PatchVersion> { | 471 class PatchAnnotationHandler implements EagerAnnotationHandler<PatchVersion> { |
517 const PatchAnnotationHandler(); | 472 const PatchAnnotationHandler(); |
518 | 473 |
519 PatchVersion getPatchVersion(MetadataAnnotation annotation) { | 474 PatchVersion getPatchVersion(MetadataAnnotation annotation) { |
520 if (annotation.beginToken != null) { | 475 if (annotation.beginToken != null) { |
521 if (annotation.beginToken.next.value == 'patch') { | 476 if (annotation.beginToken.next.value == 'patch') { |
522 return const PatchVersion(null); | 477 return const PatchVersion(null); |
523 } else if (annotation.beginToken.next.value == 'patch_full') { | 478 } else if (annotation.beginToken.next.value == 'patch_full') { |
524 return const PatchVersion('full'); | 479 return const PatchVersion('full'); |
525 } else if (annotation.beginToken.next.value == 'patch_lazy') { | 480 } else if (annotation.beginToken.next.value == 'patch_lazy') { |
526 return const PatchVersion('lazy'); | 481 return const PatchVersion('lazy'); |
527 } else if (annotation.beginToken.next.value == 'patch_startup') { | 482 } else if (annotation.beginToken.next.value == 'patch_startup') { |
528 return const PatchVersion('startup'); | 483 return const PatchVersion('startup'); |
529 } | 484 } |
530 } | 485 } |
531 return null; | 486 return null; |
532 } | 487 } |
533 | 488 |
534 @override | 489 @override |
535 PatchVersion apply(Compiler compiler, | 490 PatchVersion apply( |
536 Element element, | 491 Compiler compiler, Element element, MetadataAnnotation annotation) { |
537 MetadataAnnotation annotation) { | |
538 return getPatchVersion(annotation); | 492 return getPatchVersion(annotation); |
539 } | 493 } |
540 | 494 |
541 @override | 495 @override |
542 void validate(Compiler compiler, | 496 void validate(Compiler compiler, Element element, |
543 Element element, | 497 MetadataAnnotation annotation, ConstantValue constant) { |
544 MetadataAnnotation annotation, | |
545 ConstantValue constant) { | |
546 DartType annotationType = constant.getType(compiler.coreTypes); | 498 DartType annotationType = constant.getType(compiler.coreTypes); |
547 if (annotationType.element != compiler.patchAnnotationClass) { | 499 if (annotationType.element != compiler.patchAnnotationClass) { |
548 DiagnosticReporter reporter = compiler.reporter; | 500 DiagnosticReporter reporter = compiler.reporter; |
549 reporter.internalError(annotation, 'Invalid patch annotation.'); | 501 reporter.internalError(annotation, 'Invalid patch annotation.'); |
550 } | 502 } |
551 } | 503 } |
552 } | 504 } |
553 | 505 |
554 | 506 void tryPatchGetter( |
555 void tryPatchGetter(DiagnosticReporter reporter, | 507 DiagnosticReporter reporter, Element origin, FunctionElement patch) { |
556 Element origin, | |
557 FunctionElement patch) { | |
558 if (!origin.isAbstractField) { | 508 if (!origin.isAbstractField) { |
559 reporter.reportError( | 509 reporter.reportError( |
560 reporter.createMessage( | 510 reporter.createMessage( |
561 origin, | 511 origin, MessageKind.PATCH_NON_GETTER, {'name': origin.name}), |
562 MessageKind.PATCH_NON_GETTER, | |
563 {'name': origin.name}), | |
564 <DiagnosticMessage>[ | 512 <DiagnosticMessage>[ |
565 reporter.createMessage( | 513 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_GETTER, |
566 patch, | 514 {'getterName': patch.name}), |
567 MessageKind.PATCH_POINT_TO_GETTER, | |
568 {'getterName': patch.name}), | |
569 ]); | 515 ]); |
570 return; | 516 return; |
571 } | 517 } |
572 AbstractFieldElement originField = origin; | 518 AbstractFieldElement originField = origin; |
573 if (originField.getter == null) { | 519 if (originField.getter == null) { |
574 reporter.reportError( | 520 reporter.reportError( |
575 reporter.createMessage( | 521 reporter.createMessage( |
576 origin, | 522 origin, MessageKind.PATCH_NO_GETTER, {'getterName': patch.name}), |
577 MessageKind.PATCH_NO_GETTER, | |
578 {'getterName': patch.name}), | |
579 <DiagnosticMessage>[ | 523 <DiagnosticMessage>[ |
580 reporter.createMessage( | 524 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_GETTER, |
581 patch, | 525 {'getterName': patch.name}), |
582 MessageKind.PATCH_POINT_TO_GETTER, | |
583 {'getterName': patch.name}), | |
584 ]); | 526 ]); |
585 return; | 527 return; |
586 } | 528 } |
587 GetterElementX getter = originField.getter; | 529 GetterElementX getter = originField.getter; |
588 patchFunction(reporter, getter, patch); | 530 patchFunction(reporter, getter, patch); |
589 } | 531 } |
590 | 532 |
591 void tryPatchSetter(DiagnosticReporter reporter, | 533 void tryPatchSetter( |
592 Element origin, | 534 DiagnosticReporter reporter, Element origin, FunctionElement patch) { |
593 FunctionElement patch) { | |
594 if (!origin.isAbstractField) { | 535 if (!origin.isAbstractField) { |
595 reporter.reportError( | 536 reporter.reportError( |
596 reporter.createMessage( | 537 reporter.createMessage( |
597 origin, | 538 origin, MessageKind.PATCH_NON_SETTER, {'name': origin.name}), |
598 MessageKind.PATCH_NON_SETTER, | |
599 {'name': origin.name}), | |
600 <DiagnosticMessage>[ | 539 <DiagnosticMessage>[ |
601 reporter.createMessage( | 540 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_SETTER, |
602 patch, | 541 {'setterName': patch.name}), |
603 MessageKind.PATCH_POINT_TO_SETTER, | |
604 {'setterName': patch.name}), | |
605 ]); | 542 ]); |
606 return; | 543 return; |
607 } | 544 } |
608 AbstractFieldElement originField = origin; | 545 AbstractFieldElement originField = origin; |
609 if (originField.setter == null) { | 546 if (originField.setter == null) { |
610 reporter.reportError( | 547 reporter.reportError( |
611 reporter.createMessage( | 548 reporter.createMessage( |
612 origin, | 549 origin, MessageKind.PATCH_NO_SETTER, {'setterName': patch.name}), |
613 MessageKind.PATCH_NO_SETTER, | |
614 {'setterName': patch.name}), | |
615 <DiagnosticMessage>[ | 550 <DiagnosticMessage>[ |
616 reporter.createMessage( | 551 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_SETTER, |
617 patch, | 552 {'setterName': patch.name}), |
618 MessageKind.PATCH_POINT_TO_SETTER, | |
619 {'setterName': patch.name}), | |
620 ]); | 553 ]); |
621 return; | 554 return; |
622 } | 555 } |
623 SetterElementX setter = originField.setter; | 556 SetterElementX setter = originField.setter; |
624 patchFunction(reporter, setter, patch); | 557 patchFunction(reporter, setter, patch); |
625 } | 558 } |
626 | 559 |
627 void tryPatchConstructor(DiagnosticReporter reporter, | 560 void tryPatchConstructor( |
628 Element origin, | 561 DiagnosticReporter reporter, Element origin, FunctionElement patch) { |
629 FunctionElement patch) { | |
630 if (!origin.isConstructor) { | 562 if (!origin.isConstructor) { |
631 reporter.reportError( | 563 reporter.reportError( |
632 reporter.createMessage( | 564 reporter.createMessage(origin, MessageKind.PATCH_NON_CONSTRUCTOR, |
633 origin, | |
634 MessageKind.PATCH_NON_CONSTRUCTOR, | |
635 {'constructorName': patch.name}), | 565 {'constructorName': patch.name}), |
636 <DiagnosticMessage>[ | 566 <DiagnosticMessage>[ |
637 reporter.createMessage( | 567 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_CONSTRUCTOR, |
638 patch, | 568 {'constructorName': patch.name}), |
639 MessageKind.PATCH_POINT_TO_CONSTRUCTOR, | |
640 {'constructorName': patch.name}), | |
641 ]); | 569 ]); |
642 return; | 570 return; |
643 } | 571 } |
644 patchFunction(reporter, origin, patch); | 572 patchFunction(reporter, origin, patch); |
645 } | 573 } |
646 | 574 |
647 void tryPatchFunction(DiagnosticReporter reporter, | 575 void tryPatchFunction( |
648 Element origin, | 576 DiagnosticReporter reporter, Element origin, FunctionElement patch) { |
649 FunctionElement patch) { | |
650 if (!origin.isFunction) { | 577 if (!origin.isFunction) { |
651 reporter.reportError( | 578 reporter.reportError( |
652 reporter.createMessage( | 579 reporter.createMessage(origin, MessageKind.PATCH_NON_FUNCTION, |
653 origin, | |
654 MessageKind.PATCH_NON_FUNCTION, | |
655 {'functionName': patch.name}), | 580 {'functionName': patch.name}), |
656 <DiagnosticMessage>[ | 581 <DiagnosticMessage>[ |
657 reporter.createMessage( | 582 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_FUNCTION, |
658 patch, | 583 {'functionName': patch.name}), |
659 MessageKind.PATCH_POINT_TO_FUNCTION, | |
660 {'functionName': patch.name}), | |
661 ]); | 584 ]); |
662 return; | 585 return; |
663 } | 586 } |
664 patchFunction(reporter, origin, patch); | 587 patchFunction(reporter, origin, patch); |
665 } | 588 } |
666 | 589 |
667 void patchFunction(DiagnosticReporter reporter, | 590 void patchFunction(DiagnosticReporter reporter, BaseFunctionElementX origin, |
668 BaseFunctionElementX origin, | 591 BaseFunctionElementX patch) { |
669 BaseFunctionElementX patch) { | |
670 if (!origin.modifiers.isExternal) { | 592 if (!origin.modifiers.isExternal) { |
671 reporter.reportError( | 593 reporter.reportError( |
672 reporter.createMessage(origin, MessageKind.PATCH_NON_EXTERNAL), | 594 reporter.createMessage(origin, MessageKind.PATCH_NON_EXTERNAL), |
673 <DiagnosticMessage>[ | 595 <DiagnosticMessage>[ |
674 reporter.createMessage( | 596 reporter.createMessage(patch, MessageKind.PATCH_POINT_TO_FUNCTION, |
675 patch, | 597 {'functionName': patch.name}), |
676 MessageKind.PATCH_POINT_TO_FUNCTION, | |
677 {'functionName': patch.name}), | |
678 ]); | 598 ]); |
679 return; | 599 return; |
680 } | 600 } |
681 if (origin.isPatched) { | 601 if (origin.isPatched) { |
682 reporter.internalError(origin, | 602 reporter.internalError( |
683 "Trying to patch a function more than once."); | 603 origin, "Trying to patch a function more than once."); |
684 } | 604 } |
685 origin.applyPatch(patch); | 605 origin.applyPatch(patch); |
686 } | 606 } |
687 | 607 |
688 PatchVersion getPatchVersion(Compiler compiler, Element element) { | 608 PatchVersion getPatchVersion(Compiler compiler, Element element) { |
689 return EagerAnnotationHandler.checkAnnotation(compiler, element, | 609 return EagerAnnotationHandler.checkAnnotation( |
690 const PatchAnnotationHandler()); | 610 compiler, element, const PatchAnnotationHandler()); |
691 } | 611 } |
692 | 612 |
693 class PatchVersion { | 613 class PatchVersion { |
694 final String tag; | 614 final String tag; |
695 | 615 |
696 const PatchVersion(this.tag); | 616 const PatchVersion(this.tag); |
697 | 617 |
698 bool isActive(String patchTag) => tag == null || tag == patchTag; | 618 bool isActive(String patchTag) => tag == null || tag == patchTag; |
699 | 619 |
700 String toString() => 'PatchVersion($tag)'; | 620 String toString() => 'PatchVersion($tag)'; |
701 } | 621 } |
OLD | NEW |