Chromium Code Reviews| 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 |