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 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
223 super.pushElement(patch); | 223 super.pushElement(patch); |
224 if (isPatchElement(compiler, patch)) { | 224 if (isPatchElement(compiler, patch)) { |
225 LibraryElement originLibrary = compilationUnitElement.library; | 225 LibraryElement originLibrary = compilationUnitElement.library; |
226 assert(originLibrary.isPatched); | 226 assert(originLibrary.isPatched); |
227 Element origin = originLibrary.localLookup(patch.name); | 227 Element origin = originLibrary.localLookup(patch.name); |
228 patchElement(listener, origin, patch); | 228 patchElement(listener, origin, patch); |
229 } | 229 } |
230 } | 230 } |
231 } | 231 } |
232 | 232 |
233 void patchElement(leg.DiagnosticListener listener, | 233 void patchElement(leg.Compiler compiler, |
234 Element origin, | 234 Element origin, |
235 Element patch) { | 235 Element patch) { |
236 if (origin == null) { | 236 if (origin == null) { |
237 listener.reportError( | 237 compiler.reportError( |
238 patch, leg.MessageKind.PATCH_NON_EXISTING, {'name': patch.name}); | 238 patch, leg.MessageKind.PATCH_NON_EXISTING, {'name': patch.name}); |
239 return; | 239 return; |
240 } | 240 } |
241 if (!(origin.isClass || | 241 if (!(origin.isClass || |
242 origin.isConstructor || | 242 origin.isConstructor || |
243 origin.isFunction || | 243 origin.isFunction || |
244 origin.isAbstractField)) { | 244 origin.isAbstractField)) { |
245 // TODO(ahe): Remove this error when the parser rejects all bad modifiers. | 245 // TODO(ahe): Remove this error when the parser rejects all bad modifiers. |
246 listener.reportError(origin, leg.MessageKind.PATCH_NONPATCHABLE); | 246 compiler.reportError(origin, leg.MessageKind.PATCH_NONPATCHABLE); |
247 return; | 247 return; |
248 } | 248 } |
249 if (patch.isClass) { | 249 if (patch.isClass) { |
250 tryPatchClass(listener, origin, patch); | 250 tryPatchClass(compiler, origin, patch); |
251 } else if (patch.isGetter) { | 251 } else if (patch.isGetter) { |
252 tryPatchGetter(listener, origin, patch); | 252 tryPatchGetter(compiler, origin, patch); |
253 } else if (patch.isSetter) { | 253 } else if (patch.isSetter) { |
254 tryPatchSetter(listener, origin, patch); | 254 tryPatchSetter(compiler, origin, patch); |
255 } else if (patch.isConstructor) { | 255 } else if (patch.isConstructor) { |
256 tryPatchConstructor(listener, origin, patch); | 256 tryPatchConstructor(compiler, origin, patch); |
257 } else if(patch.isFunction) { | 257 } else if(patch.isFunction) { |
258 tryPatchFunction(listener, origin, patch); | 258 tryPatchFunction(compiler, origin, patch); |
259 } else { | 259 } else { |
260 // TODO(ahe): Remove this error when the parser rejects all bad modifiers. | 260 // TODO(ahe): Remove this error when the parser rejects all bad modifiers. |
261 listener.reportError(patch, leg.MessageKind.PATCH_NONPATCHABLE); | 261 compiler.reportError(patch, leg.MessageKind.PATCH_NONPATCHABLE); |
262 } | 262 } |
263 } | 263 } |
264 | 264 |
265 void tryPatchClass(leg.DiagnosticListener listener, | 265 void tryPatchClass(leg.Compiler compiler, |
266 Element origin, | 266 Element origin, |
267 ClassElement patch) { | 267 ClassElement patch) { |
268 if (!origin.isClass) { | 268 if (!origin.isClass) { |
269 listener.reportError( | 269 compiler.reportError( |
270 origin, leg.MessageKind.PATCH_NON_CLASS, {'className': patch.name}); | 270 origin, leg.MessageKind.PATCH_NON_CLASS, {'className': patch.name}); |
271 listener.reportInfo( | 271 compiler.reportInfo( |
272 patch, leg.MessageKind.PATCH_POINT_TO_CLASS, {'className': patch.name}); | 272 patch, leg.MessageKind.PATCH_POINT_TO_CLASS, {'className': patch.name}); |
273 return; | 273 return; |
274 } | 274 } |
275 patchClass(listener, origin, patch); | 275 patchClass(compiler, origin, patch); |
276 } | 276 } |
277 | 277 |
278 void patchClass(leg.DiagnosticListener listener, | 278 void patchClass(leg.Compiler compiler, |
279 ClassElementX origin, | 279 ClassElementX origin, |
280 ClassElementX patch) { | 280 ClassElementX patch) { |
281 if (origin.isPatched) { | 281 if (origin.isPatched) { |
282 listener.internalError(origin, | 282 compiler.internalError(origin, |
283 "Patching the same class more than once."); | 283 "Patching the same class more than once."); |
284 } | 284 } |
285 origin.applyPatch(patch); | 285 origin.applyPatch(patch); |
286 checkNativeAnnotation(compiler, patch); | |
286 } | 287 } |
287 | 288 |
289 /// Check whether [cls] has a `@Native(...)` annotation, and if so, set its | |
290 /// native name from the annotation. | |
291 checkNativeAnnotation(leg.Compiler compiler, ClassElement cls) { | |
292 EagerAnnotationHandler.checkAnnotation(compiler, cls, | |
293 const NativeAnnotationHandler()); | |
294 } | |
295 | |
296 /// Abstract interface for pre-resolution detection of metadata. | |
297 /// | |
298 /// The detection is handled in two steps: | |
299 /// - match the annotation syntactically and assume that the annotation is valid | |
300 /// if it looks correct, | |
301 /// - setup a deferred action to check that the annotation has a valid constant | |
302 /// value and report an internal error if not. | |
303 abstract class EagerAnnotationHandler { | |
304 /// Checks that [annotation] looks like a matching annotation and optionally | |
305 /// applies actions on [element]. Returns `true` if the annotation matched. | |
306 bool apply(leg.Compiler compiler, | |
307 Element element, | |
308 MetadataAnnotation annotation); | |
309 | |
310 /// Checks that the annotation value is valid. | |
311 void validate(leg.Compiler compiler, | |
312 Element element, | |
313 MetadataAnnotation annotation, | |
314 leg.Constant constant); | |
315 | |
316 | |
317 /// Checks [element] for metadata matching the [handler]. Return `true` if | |
318 /// matching metadata was found. | |
319 static bool checkAnnotation(leg.Compiler compiler, | |
320 Element element, | |
321 EagerAnnotationHandler handler) { | |
322 for (Link<MetadataAnnotation> link = element.metadata; | |
323 !link.isEmpty; | |
324 link = link.tail) { | |
325 MetadataAnnotation annotation = link.head; | |
326 if (handler.apply(compiler, element, annotation)) { | |
327 // TODO(johnniwinther): Perform this check in | |
328 // [Compiler.onLibrariesLoaded]. | |
329 compiler.enqueuer.resolution.addDeferredAction(element, () { | |
330 annotation.ensureResolved(compiler); | |
331 handler.validate(compiler, element, annotation, annotation.value); | |
332 }); | |
333 return true; | |
334 } | |
335 } | |
336 return false; | |
337 } | |
338 } | |
339 | |
340 /// Annotation handler for pre-resolution detection of `@Native(...)` | |
341 /// annotations. | |
342 class NativeAnnotationHandler implements EagerAnnotationHandler { | |
343 const NativeAnnotationHandler(); | |
344 | |
345 String getNativeAnnotation(MetadataAnnotation annotation) { | |
346 if (annotation.beginToken != null && | |
347 annotation.beginToken.next.value == 'Native') { | |
348 Token argument = annotation.beginToken.next.next.next; | |
floitsch
2014/07/14 19:32:59
Comment what tokens are skipped.
Johnni Winther
2014/07/18 08:59:32
Done.
| |
349 if (argument is StringToken) { | |
350 return argument.value; | |
351 } | |
352 } | |
353 return null; | |
354 } | |
355 | |
356 bool apply(leg.Compiler compiler, | |
357 Element element, | |
358 MetadataAnnotation annotation) { | |
359 if (element.isClass) { | |
360 String native = getNativeAnnotation(annotation); | |
361 if (native != null) { | |
362 ClassElementX declaration = element.declaration; | |
363 declaration.setNative(native); | |
364 return true; | |
365 } | |
366 } | |
367 return false; | |
368 } | |
369 | |
370 void validate(leg.Compiler compiler, | |
371 Element element, | |
372 MetadataAnnotation annotation, | |
373 leg.Constant constant) { | |
374 if (constant.computeType(compiler).element != | |
375 compiler.nativeAnnotationClass) { | |
376 compiler.internalError(annotation, 'Invalid @Native(...) annotation.'); | |
377 } | |
378 } | |
379 } | |
380 | |
381 /// Annotation handler for pre-resolution detection of `@patch` annotations. | |
382 class PatchAnnotationHandler implements EagerAnnotationHandler { | |
383 const PatchAnnotationHandler(); | |
384 | |
385 bool isPatchAnnotation(MetadataAnnotation annotation) { | |
386 return annotation.beginToken != null && | |
387 annotation.beginToken.next.value == 'patch'; | |
388 } | |
389 | |
390 bool apply(leg.Compiler compiler, | |
391 Element element, | |
392 MetadataAnnotation annotation) { | |
393 return isPatchAnnotation(annotation); | |
394 } | |
395 | |
396 void validate(leg.Compiler compiler, | |
397 Element element, | |
398 MetadataAnnotation annotation, | |
399 leg.Constant constant) { | |
400 if (constant != compiler.patchConstant) { | |
401 compiler.internalError(annotation, 'Invalid patch annotation.'); | |
402 } | |
403 } | |
404 } | |
405 | |
406 | |
288 void tryPatchGetter(leg.DiagnosticListener listener, | 407 void tryPatchGetter(leg.DiagnosticListener listener, |
289 Element origin, | 408 Element origin, |
290 FunctionElement patch) { | 409 FunctionElement patch) { |
291 if (!origin.isAbstractField) { | 410 if (!origin.isAbstractField) { |
292 listener.reportError( | 411 listener.reportError( |
293 origin, leg.MessageKind.PATCH_NON_GETTER, {'name': origin.name}); | 412 origin, leg.MessageKind.PATCH_NON_GETTER, {'name': origin.name}); |
294 listener.reportInfo( | 413 listener.reportInfo( |
295 patch, | 414 patch, |
296 leg.MessageKind.PATCH_POINT_TO_GETTER, {'getterName': patch.name}); | 415 leg.MessageKind.PATCH_POINT_TO_GETTER, {'getterName': patch.name}); |
297 return; | 416 return; |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
374 } | 493 } |
375 if (origin.isPatched) { | 494 if (origin.isPatched) { |
376 listener.internalError(origin, | 495 listener.internalError(origin, |
377 "Trying to patch a function more than once."); | 496 "Trying to patch a function more than once."); |
378 } | 497 } |
379 origin.applyPatch(patch); | 498 origin.applyPatch(patch); |
380 } | 499 } |
381 | 500 |
382 // TODO(johnniwinther): Add unittest when patch is (real) metadata. | 501 // TODO(johnniwinther): Add unittest when patch is (real) metadata. |
383 bool isPatchElement(leg.Compiler compiler, Element element) { | 502 bool isPatchElement(leg.Compiler compiler, Element element) { |
384 // TODO(lrn): More checks needed if we introduce metadata for real. | 503 return EagerAnnotationHandler.checkAnnotation(compiler, element, |
385 // In that case, it must have the identifier "native" as metadata. | 504 const PatchAnnotationHandler()); |
386 for (Link<MetadataAnnotation> link = element.metadata; | |
387 !link.isEmpty; | |
388 link = link.tail) { | |
389 MetadataAnnotation annotation = link.head; | |
390 if (annotation.beginToken != null && | |
391 annotation.beginToken.next.value == 'patch') { | |
392 // TODO(johnniwinther): Perform this check in | |
393 // [Compiler.onLibrariesLoaded]. | |
394 compiler.enqueuer.resolution.addDeferredAction(element, () { | |
395 annotation.ensureResolved(compiler); | |
396 if (annotation.value != compiler.patchConstant) { | |
397 compiler.internalError(annotation, 'Invalid patch annotation.'); | |
398 } | |
399 }); | |
400 return true; | |
401 } | |
402 } | |
403 return false; | |
404 } | 505 } |
OLD | NEW |