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 // Skipping '@', 'Native', and '('. |
| 349 Token argument = annotation.beginToken.next.next.next; |
| 350 if (argument is StringToken) { |
| 351 return argument.value; |
| 352 } |
| 353 } |
| 354 return null; |
| 355 } |
| 356 |
| 357 bool apply(leg.Compiler compiler, |
| 358 Element element, |
| 359 MetadataAnnotation annotation) { |
| 360 if (element.isClass) { |
| 361 String native = getNativeAnnotation(annotation); |
| 362 if (native != null) { |
| 363 ClassElementX declaration = element.declaration; |
| 364 declaration.setNative(native); |
| 365 return true; |
| 366 } |
| 367 } |
| 368 return false; |
| 369 } |
| 370 |
| 371 void validate(leg.Compiler compiler, |
| 372 Element element, |
| 373 MetadataAnnotation annotation, |
| 374 leg.Constant constant) { |
| 375 if (constant.computeType(compiler).element != |
| 376 compiler.nativeAnnotationClass) { |
| 377 compiler.internalError(annotation, 'Invalid @Native(...) annotation.'); |
| 378 } |
| 379 } |
| 380 } |
| 381 |
| 382 /// Annotation handler for pre-resolution detection of `@patch` annotations. |
| 383 class PatchAnnotationHandler implements EagerAnnotationHandler { |
| 384 const PatchAnnotationHandler(); |
| 385 |
| 386 bool isPatchAnnotation(MetadataAnnotation annotation) { |
| 387 return annotation.beginToken != null && |
| 388 annotation.beginToken.next.value == 'patch'; |
| 389 } |
| 390 |
| 391 bool apply(leg.Compiler compiler, |
| 392 Element element, |
| 393 MetadataAnnotation annotation) { |
| 394 return isPatchAnnotation(annotation); |
| 395 } |
| 396 |
| 397 void validate(leg.Compiler compiler, |
| 398 Element element, |
| 399 MetadataAnnotation annotation, |
| 400 leg.Constant constant) { |
| 401 if (constant != compiler.patchConstant) { |
| 402 compiler.internalError(annotation, 'Invalid patch annotation.'); |
| 403 } |
| 404 } |
| 405 } |
| 406 |
| 407 |
288 void tryPatchGetter(leg.DiagnosticListener listener, | 408 void tryPatchGetter(leg.DiagnosticListener listener, |
289 Element origin, | 409 Element origin, |
290 FunctionElement patch) { | 410 FunctionElement patch) { |
291 if (!origin.isAbstractField) { | 411 if (!origin.isAbstractField) { |
292 listener.reportError( | 412 listener.reportError( |
293 origin, leg.MessageKind.PATCH_NON_GETTER, {'name': origin.name}); | 413 origin, leg.MessageKind.PATCH_NON_GETTER, {'name': origin.name}); |
294 listener.reportInfo( | 414 listener.reportInfo( |
295 patch, | 415 patch, |
296 leg.MessageKind.PATCH_POINT_TO_GETTER, {'getterName': patch.name}); | 416 leg.MessageKind.PATCH_POINT_TO_GETTER, {'getterName': patch.name}); |
297 return; | 417 return; |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
374 } | 494 } |
375 if (origin.isPatched) { | 495 if (origin.isPatched) { |
376 listener.internalError(origin, | 496 listener.internalError(origin, |
377 "Trying to patch a function more than once."); | 497 "Trying to patch a function more than once."); |
378 } | 498 } |
379 origin.applyPatch(patch); | 499 origin.applyPatch(patch); |
380 } | 500 } |
381 | 501 |
382 // TODO(johnniwinther): Add unittest when patch is (real) metadata. | 502 // TODO(johnniwinther): Add unittest when patch is (real) metadata. |
383 bool isPatchElement(leg.Compiler compiler, Element element) { | 503 bool isPatchElement(leg.Compiler compiler, Element element) { |
384 // TODO(lrn): More checks needed if we introduce metadata for real. | 504 return EagerAnnotationHandler.checkAnnotation(compiler, element, |
385 // In that case, it must have the identifier "native" as metadata. | 505 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 } | 506 } |
OLD | NEW |