| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * This library contains the infrastructure to parse and integrate patch files. | |
| 7 * | |
| 8 * Three types of elements can be patched: [LibraryElement], [ClassElement], | |
| 9 * [FunctionElement]. Patches are introduced in patch libraries which are loaded | |
| 10 * together with the corresponding origin library. Which libraries that are | |
| 11 * patched is determined by the dart2jsPatchPath field of LibraryInfo found | |
| 12 * in [:lib/_internal/libraries.dart:]. | |
| 13 * | |
| 14 * Patch libraries are parsed like regular library and thus provided with their | |
| 15 * own elements. These elements which are distinct from the elements from the | |
| 16 * patched library and the relation between patched and patch elements is | |
| 17 * established through the [:patch:] and [:origin:] fields found on | |
| 18 * [LibraryElement], [ClassElement] and [FunctionElement]. The [:patch:] fields | |
| 19 * are set on the patched elements to point to their corresponding patch | |
| 20 * element, and the [:origin:] elements are set on the patch elements to point | |
| 21 * their corresponding patched elements. | |
| 22 * | |
| 23 * The fields [Element.isPatched] and [Element.isPatch] can be used to determine | |
| 24 * whether the [:patch:] or [:origin:] field, respectively, has been set on an | |
| 25 * element, regardless of whether the element is one of the three patchable | |
| 26 * element types or not. | |
| 27 * | |
| 28 * ## Variants of classes and functions ## | |
| 29 * | |
| 30 * With patches there are four variants of classes and function: | |
| 31 * | |
| 32 * Regular: A class or function which is not declared in a patch library and | |
| 33 * which has no corresponding patch. | |
| 34 * Origin: A class or function which is not declared in a patch library and | |
| 35 * which has a corresponding patch. Origin functions must use the [:external:] | |
| 36 * modifier and can have no body. Origin classes and functions are also | |
| 37 * called 'patched'. | |
| 38 * Patch: A class or function which is declared in a patch library and which | |
| 39 * has a corresponding origin. Both patch classes and patch functions must use | |
| 40 * the [:patch:] modifier. | |
| 41 * Injected: A class or function (or even field) which is declared in a | |
| 42 * patch library and which has no corresponding origin. An injected element | |
| 43 * cannot use the [:patch:] modifier. Injected elements are never visible from | |
| 44 * outside the patch library in which they have been declared. For this | |
| 45 * reason, injected elements are often declared private and therefore called | |
| 46 * also called 'patch private'. | |
| 47 * | |
| 48 * Examples of the variants is shown in the code below: | |
| 49 * | |
| 50 * // In the origin library: | |
| 51 * class RegularClass { // A regular class. | |
| 52 * void regularMethod() {} // A regular method. | |
| 53 * } | |
| 54 * class PatchedClass { // An origin class. | |
| 55 * int regularField; // A regular field. | |
| 56 * void regularMethod() {} // A regular method. | |
| 57 * external void patchedMethod(); // An origin method. | |
| 58 * } | |
| 59 * | |
| 60 * // In the patch library: | |
| 61 * class _InjectedClass { // An injected class. | |
| 62 * void _injectedMethod() {} // An injected method. | |
| 63 * } | |
| 64 * @patch class PatchedClass { // A patch class. | |
| 65 * int _injectedField; { // An injected field. | |
| 66 * @patch void patchedMethod() {} // A patch method. | |
| 67 * } | |
| 68 * | |
| 69 * | |
| 70 * ## Declaration and implementation ## | |
| 71 * | |
| 72 * With patches we have two views on elements: as the 'declaration' which | |
| 73 * introduces the entity and defines its interface, and as the 'implementation' | |
| 74 * which defines the actual implementation of the entity. | |
| 75 * | |
| 76 * Every element has a 'declaration' and an 'implementation' element. For | |
| 77 * regular and injected elements these are the same. For origin elements the | |
| 78 * declaration is the element itself and the implementation is the patch element | |
| 79 * found through its [:patch:] field. For patch elements the implementation is | |
| 80 * the element itself and the declaration is the origin element found through | |
| 81 * its [:origin:] field. The declaration and implementation of any element is | |
| 82 * conveniently available through the [Element.declaration] and | |
| 83 * [Element.implementation] getters. | |
| 84 * | |
| 85 * Most patch-related invariants enforced through-out the compiler are defined | |
| 86 * in terms of 'declaration' and 'implementation', and tested through the | |
| 87 * predicate getters [Element.isDeclaration] and [Element.isImplementation]. | |
| 88 * Patch invariants are stated both in comments and as assertions. | |
| 89 * | |
| 90 * | |
| 91 * ## General invariant guidelines ## | |
| 92 * | |
| 93 * For [LibraryElement] we always use declarations. This means the | |
| 94 * [Element.getLibrary] method will only return library declarations. Patch | |
| 95 * library implementations are only accessed through calls to | |
| 96 * [Element.getImplementationLibrary] which is used to setup the correct | |
| 97 * [Element.enclosingElement] relation between patch/injected elements and the | |
| 98 * patch library. | |
| 99 * | |
| 100 * For [ClassElement] and [FunctionElement] we use declarations for determining | |
| 101 * identity and implementations for work based on the AST nodes, such as | |
| 102 * resolution, type-checking, type inference, building SSA graphs, etc. | |
| 103 * - Worklist only contain declaration elements. | |
| 104 * - Most maps and sets use declarations exclusively, and their individual | |
| 105 * invariants are stated in the field comments. | |
| 106 * - [tree.TreeElements] only map to patch elements from inside a patch library. | |
| 107 * TODO(johnniwinther): Simplify this invariant to use only declarations in | |
| 108 * [tree.TreeElements]. | |
| 109 * - Builders shift between declaration and implementation depending on usages. | |
| 110 * - Compile-time constants use constructor implementation exclusively. | |
| 111 * - Work on function parameters is performed on the declaration of the function | |
| 112 * element. | |
| 113 */ | |
| 114 | |
| 115 library patchparser; | |
| 116 | |
| 117 import 'dart:async'; | |
| 118 | |
| 119 import 'constants/values.dart' show ConstantValue; | |
| 120 import 'dart2jslib.dart' | |
| 121 show Compiler, | |
| 122 CompilerTask, | |
| 123 DiagnosticListener, | |
| 124 MessageKind, | |
| 125 Script; | |
| 126 import 'elements/elements.dart'; | |
| 127 import 'elements/modelx.dart' | |
| 128 show LibraryElementX, | |
| 129 MetadataAnnotationX, | |
| 130 ClassElementX, | |
| 131 FunctionElementX; | |
| 132 import 'helpers/helpers.dart'; // Included for debug helpers. | |
| 133 import 'library_loader.dart' show LibraryLoader; | |
| 134 import 'scanner/scannerlib.dart'; // Scanner, Parsers, Listeners | |
| 135 import 'util/util.dart'; | |
| 136 | |
| 137 class PatchParserTask extends CompilerTask { | |
| 138 PatchParserTask(Compiler compiler): super(compiler); | |
| 139 final String name = "Patching Parser"; | |
| 140 | |
| 141 /** | |
| 142 * Scans a library patch file, applies the method patches and | |
| 143 * injections to the library, and returns a list of class | |
| 144 * patches. | |
| 145 */ | |
| 146 Future patchLibrary(LibraryLoader loader, | |
| 147 Uri patchUri, LibraryElement originLibrary) { | |
| 148 return compiler.readScript(originLibrary, patchUri) | |
| 149 .then((Script script) { | |
| 150 var patchLibrary = new LibraryElementX(script, null, originLibrary); | |
| 151 return compiler.withCurrentElement(patchLibrary, () { | |
| 152 loader.registerNewLibrary(patchLibrary); | |
| 153 compiler.withCurrentElement(patchLibrary.entryCompilationUnit, () { | |
| 154 // This patches the elements of the patch library into [library]. | |
| 155 // Injected elements are added directly under the compilation unit. | |
| 156 // Patch elements are stored on the patched functions or classes. | |
| 157 scanLibraryElements(patchLibrary.entryCompilationUnit); | |
| 158 }); | |
| 159 return loader.processLibraryTags(patchLibrary); | |
| 160 }); | |
| 161 }); | |
| 162 } | |
| 163 | |
| 164 void scanLibraryElements(CompilationUnitElement compilationUnit) { | |
| 165 measure(() { | |
| 166 // TODO(johnniwinther): Test that parts and exports are handled correctly. | |
| 167 Script script = compilationUnit.script; | |
| 168 Token tokens = new Scanner(script.file).tokenize(); | |
| 169 Function idGenerator = compiler.getNextFreeClassId; | |
| 170 Listener patchListener = new PatchElementListener(compiler, | |
| 171 compilationUnit, | |
| 172 idGenerator); | |
| 173 new PartialParser(patchListener).parseUnit(tokens); | |
| 174 }); | |
| 175 } | |
| 176 | |
| 177 void parsePatchClassNode(PartialClassElement element) { | |
| 178 // Parse [PartialClassElement] using a "patch"-aware parser instead | |
| 179 // of calling its [parseNode] method. | |
| 180 if (element.cachedNode != null) return; | |
| 181 | |
| 182 measure(() => compiler.withCurrentElement(element, () { | |
| 183 MemberListener listener = new MemberListener(compiler, element); | |
| 184 Parser parser = new PatchClassElementParser(listener); | |
| 185 Token token = parser.parseTopLevelDeclaration(element.beginToken); | |
| 186 assert(identical(token, element.endToken.next)); | |
| 187 element.cachedNode = listener.popNode(); | |
| 188 assert(listener.nodes.isEmpty); | |
| 189 | |
| 190 Link<Element> patches = element.localMembers; | |
| 191 applyContainerPatch(element.origin, patches); | |
| 192 })); | |
| 193 } | |
| 194 | |
| 195 void applyContainerPatch(ClassElement originClass, | |
| 196 Link<Element> patches) { | |
| 197 for (Element patch in patches) { | |
| 198 if (!isPatchElement(compiler, patch)) continue; | |
| 199 | |
| 200 Element origin = originClass.localLookup(patch.name); | |
| 201 patchElement(compiler, origin, patch); | |
| 202 } | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 /** | |
| 207 * Partial parser for patch files that also handles the members of class | |
| 208 * declarations. | |
| 209 */ | |
| 210 class PatchClassElementParser extends PartialParser { | |
| 211 PatchClassElementParser(Listener listener) : super(listener); | |
| 212 | |
| 213 Token parseClassBody(Token token) => fullParseClassBody(token); | |
| 214 } | |
| 215 | |
| 216 /** | |
| 217 * Extension of [ElementListener] for parsing patch files. | |
| 218 */ | |
| 219 class PatchElementListener extends ElementListener implements Listener { | |
| 220 final Compiler compiler; | |
| 221 | |
| 222 PatchElementListener(Compiler compiler, | |
| 223 CompilationUnitElement patchElement, | |
| 224 int idGenerator()) | |
| 225 : this.compiler = compiler, | |
| 226 super(compiler, patchElement, idGenerator); | |
| 227 | |
| 228 void pushElement(Element patch) { | |
| 229 super.pushElement(patch); | |
| 230 if (isPatchElement(compiler, patch)) { | |
| 231 LibraryElement originLibrary = compilationUnitElement.library; | |
| 232 assert(originLibrary.isPatched); | |
| 233 Element origin = originLibrary.localLookup(patch.name); | |
| 234 patchElement(listener, origin, patch); | |
| 235 } | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 void patchElement(Compiler compiler, | |
| 240 Element origin, | |
| 241 Element patch) { | |
| 242 if (origin == null) { | |
| 243 compiler.reportError( | |
| 244 patch, MessageKind.PATCH_NON_EXISTING, {'name': patch.name}); | |
| 245 return; | |
| 246 } | |
| 247 if (!(origin.isClass || | |
| 248 origin.isConstructor || | |
| 249 origin.isFunction || | |
| 250 origin.isAbstractField)) { | |
| 251 // TODO(ahe): Remove this error when the parser rejects all bad modifiers. | |
| 252 compiler.reportError(origin, MessageKind.PATCH_NONPATCHABLE); | |
| 253 return; | |
| 254 } | |
| 255 if (patch.isClass) { | |
| 256 tryPatchClass(compiler, origin, patch); | |
| 257 } else if (patch.isGetter) { | |
| 258 tryPatchGetter(compiler, origin, patch); | |
| 259 } else if (patch.isSetter) { | |
| 260 tryPatchSetter(compiler, origin, patch); | |
| 261 } else if (patch.isConstructor) { | |
| 262 tryPatchConstructor(compiler, origin, patch); | |
| 263 } else if(patch.isFunction) { | |
| 264 tryPatchFunction(compiler, origin, patch); | |
| 265 } else { | |
| 266 // TODO(ahe): Remove this error when the parser rejects all bad modifiers. | |
| 267 compiler.reportError(patch, MessageKind.PATCH_NONPATCHABLE); | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 void tryPatchClass(Compiler compiler, | |
| 272 Element origin, | |
| 273 ClassElement patch) { | |
| 274 if (!origin.isClass) { | |
| 275 compiler.reportError( | |
| 276 origin, MessageKind.PATCH_NON_CLASS, {'className': patch.name}); | |
| 277 compiler.reportInfo( | |
| 278 patch, MessageKind.PATCH_POINT_TO_CLASS, {'className': patch.name}); | |
| 279 return; | |
| 280 } | |
| 281 patchClass(compiler, origin, patch); | |
| 282 } | |
| 283 | |
| 284 void patchClass(Compiler compiler, | |
| 285 ClassElementX origin, | |
| 286 ClassElementX patch) { | |
| 287 if (origin.isPatched) { | |
| 288 compiler.internalError(origin, | |
| 289 "Patching the same class more than once."); | |
| 290 } | |
| 291 origin.applyPatch(patch); | |
| 292 checkNativeAnnotation(compiler, patch); | |
| 293 } | |
| 294 | |
| 295 /// Check whether [cls] has a `@Native(...)` annotation, and if so, set its | |
| 296 /// native name from the annotation. | |
| 297 checkNativeAnnotation(Compiler compiler, ClassElement cls) { | |
| 298 EagerAnnotationHandler.checkAnnotation(compiler, cls, | |
| 299 const NativeAnnotationHandler()); | |
| 300 } | |
| 301 | |
| 302 /// Abstract interface for pre-resolution detection of metadata. | |
| 303 /// | |
| 304 /// The detection is handled in two steps: | |
| 305 /// - match the annotation syntactically and assume that the annotation is valid | |
| 306 /// if it looks correct, | |
| 307 /// - setup a deferred action to check that the annotation has a valid constant | |
| 308 /// value and report an internal error if not. | |
| 309 abstract class EagerAnnotationHandler { | |
| 310 /// Checks that [annotation] looks like a matching annotation and optionally | |
| 311 /// applies actions on [element]. Returns `true` if the annotation matched. | |
| 312 bool apply(Compiler compiler, | |
| 313 Element element, | |
| 314 MetadataAnnotation annotation); | |
| 315 | |
| 316 /// Checks that the annotation value is valid. | |
| 317 void validate(Compiler compiler, | |
| 318 Element element, | |
| 319 MetadataAnnotation annotation, | |
| 320 ConstantValue constant); | |
| 321 | |
| 322 | |
| 323 /// Checks [element] for metadata matching the [handler]. Return `true` if | |
| 324 /// matching metadata was found. | |
| 325 static bool checkAnnotation(Compiler compiler, | |
| 326 Element element, | |
| 327 EagerAnnotationHandler handler) { | |
| 328 for (Link<MetadataAnnotation> link = element.metadata; | |
| 329 !link.isEmpty; | |
| 330 link = link.tail) { | |
| 331 MetadataAnnotation annotation = link.head; | |
| 332 if (handler.apply(compiler, element, annotation)) { | |
| 333 // TODO(johnniwinther): Perform this check in | |
| 334 // [Compiler.onLibrariesLoaded]. | |
| 335 compiler.enqueuer.resolution.addDeferredAction(element, () { | |
| 336 annotation.ensureResolved(compiler); | |
| 337 handler.validate( | |
| 338 compiler, element, annotation, annotation.constant.value); | |
| 339 }); | |
| 340 return true; | |
| 341 } | |
| 342 } | |
| 343 return false; | |
| 344 } | |
| 345 } | |
| 346 | |
| 347 /// Annotation handler for pre-resolution detection of `@Native(...)` | |
| 348 /// annotations. | |
| 349 class NativeAnnotationHandler implements EagerAnnotationHandler { | |
| 350 const NativeAnnotationHandler(); | |
| 351 | |
| 352 String getNativeAnnotation(MetadataAnnotation annotation) { | |
| 353 if (annotation.beginToken != null && | |
| 354 annotation.beginToken.next.value == 'Native') { | |
| 355 // Skipping '@', 'Native', and '('. | |
| 356 Token argument = annotation.beginToken.next.next.next; | |
| 357 if (argument is StringToken) { | |
| 358 return argument.value; | |
| 359 } | |
| 360 } | |
| 361 return null; | |
| 362 } | |
| 363 | |
| 364 bool apply(Compiler compiler, | |
| 365 Element element, | |
| 366 MetadataAnnotation annotation) { | |
| 367 if (element.isClass) { | |
| 368 String native = getNativeAnnotation(annotation); | |
| 369 if (native != null) { | |
| 370 ClassElementX declaration = element.declaration; | |
| 371 declaration.setNative(native); | |
| 372 return true; | |
| 373 } | |
| 374 } | |
| 375 return false; | |
| 376 } | |
| 377 | |
| 378 void validate(Compiler compiler, | |
| 379 Element element, | |
| 380 MetadataAnnotation annotation, | |
| 381 ConstantValue constant) { | |
| 382 if (constant.computeType(compiler).element != | |
| 383 compiler.nativeAnnotationClass) { | |
| 384 compiler.internalError(annotation, 'Invalid @Native(...) annotation.'); | |
| 385 } | |
| 386 } | |
| 387 } | |
| 388 | |
| 389 /// Annotation handler for pre-resolution detection of `@patch` annotations. | |
| 390 class PatchAnnotationHandler implements EagerAnnotationHandler { | |
| 391 const PatchAnnotationHandler(); | |
| 392 | |
| 393 bool isPatchAnnotation(MetadataAnnotation annotation) { | |
| 394 return annotation.beginToken != null && | |
| 395 annotation.beginToken.next.value == 'patch'; | |
| 396 } | |
| 397 | |
| 398 bool apply(Compiler compiler, | |
| 399 Element element, | |
| 400 MetadataAnnotation annotation) { | |
| 401 return isPatchAnnotation(annotation); | |
| 402 } | |
| 403 | |
| 404 void validate(Compiler compiler, | |
| 405 Element element, | |
| 406 MetadataAnnotation annotation, | |
| 407 ConstantValue constant) { | |
| 408 if (constant != compiler.patchConstant) { | |
| 409 compiler.internalError(annotation, 'Invalid patch annotation.'); | |
| 410 } | |
| 411 } | |
| 412 } | |
| 413 | |
| 414 | |
| 415 void tryPatchGetter(DiagnosticListener listener, | |
| 416 Element origin, | |
| 417 FunctionElement patch) { | |
| 418 if (!origin.isAbstractField) { | |
| 419 listener.reportError( | |
| 420 origin, MessageKind.PATCH_NON_GETTER, {'name': origin.name}); | |
| 421 listener.reportInfo( | |
| 422 patch, | |
| 423 MessageKind.PATCH_POINT_TO_GETTER, {'getterName': patch.name}); | |
| 424 return; | |
| 425 } | |
| 426 AbstractFieldElement originField = origin; | |
| 427 if (originField.getter == null) { | |
| 428 listener.reportError( | |
| 429 origin, MessageKind.PATCH_NO_GETTER, {'getterName': patch.name}); | |
| 430 listener.reportInfo( | |
| 431 patch, | |
| 432 MessageKind.PATCH_POINT_TO_GETTER, {'getterName': patch.name}); | |
| 433 return; | |
| 434 } | |
| 435 patchFunction(listener, originField.getter, patch); | |
| 436 } | |
| 437 | |
| 438 void tryPatchSetter(DiagnosticListener listener, | |
| 439 Element origin, | |
| 440 FunctionElement patch) { | |
| 441 if (!origin.isAbstractField) { | |
| 442 listener.reportError( | |
| 443 origin, MessageKind.PATCH_NON_SETTER, {'name': origin.name}); | |
| 444 listener.reportInfo( | |
| 445 patch, | |
| 446 MessageKind.PATCH_POINT_TO_SETTER, {'setterName': patch.name}); | |
| 447 return; | |
| 448 } | |
| 449 AbstractFieldElement originField = origin; | |
| 450 if (originField.setter == null) { | |
| 451 listener.reportError( | |
| 452 origin, MessageKind.PATCH_NO_SETTER, {'setterName': patch.name}); | |
| 453 listener.reportInfo( | |
| 454 patch, | |
| 455 MessageKind.PATCH_POINT_TO_SETTER, {'setterName': patch.name}); | |
| 456 return; | |
| 457 } | |
| 458 patchFunction(listener, originField.setter, patch); | |
| 459 } | |
| 460 | |
| 461 void tryPatchConstructor(DiagnosticListener listener, | |
| 462 Element origin, | |
| 463 FunctionElement patch) { | |
| 464 if (!origin.isConstructor) { | |
| 465 listener.reportError( | |
| 466 origin, | |
| 467 MessageKind.PATCH_NON_CONSTRUCTOR, {'constructorName': patch.name}); | |
| 468 listener.reportInfo( | |
| 469 patch, | |
| 470 MessageKind.PATCH_POINT_TO_CONSTRUCTOR, | |
| 471 {'constructorName': patch.name}); | |
| 472 return; | |
| 473 } | |
| 474 patchFunction(listener, origin, patch); | |
| 475 } | |
| 476 | |
| 477 void tryPatchFunction(DiagnosticListener listener, | |
| 478 Element origin, | |
| 479 FunctionElement patch) { | |
| 480 if (!origin.isFunction) { | |
| 481 listener.reportError( | |
| 482 origin, | |
| 483 MessageKind.PATCH_NON_FUNCTION, {'functionName': patch.name}); | |
| 484 listener.reportInfo( | |
| 485 patch, | |
| 486 MessageKind.PATCH_POINT_TO_FUNCTION, {'functionName': patch.name}); | |
| 487 return; | |
| 488 } | |
| 489 patchFunction(listener, origin, patch); | |
| 490 } | |
| 491 | |
| 492 void patchFunction(DiagnosticListener listener, | |
| 493 FunctionElementX origin, | |
| 494 FunctionElementX patch) { | |
| 495 if (!origin.modifiers.isExternal) { | |
| 496 listener.reportError(origin, MessageKind.PATCH_NON_EXTERNAL); | |
| 497 listener.reportInfo( | |
| 498 patch, | |
| 499 MessageKind.PATCH_POINT_TO_FUNCTION, {'functionName': patch.name}); | |
| 500 return; | |
| 501 } | |
| 502 if (origin.isPatched) { | |
| 503 listener.internalError(origin, | |
| 504 "Trying to patch a function more than once."); | |
| 505 } | |
| 506 origin.applyPatch(patch); | |
| 507 } | |
| 508 | |
| 509 // TODO(johnniwinther): Add unittest when patch is (real) metadata. | |
| 510 bool isPatchElement(Compiler compiler, Element element) { | |
| 511 return EagerAnnotationHandler.checkAnnotation(compiler, element, | |
| 512 const PatchAnnotationHandler()); | |
| 513 } | |
| OLD | NEW |