| 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 * TODO(johnniwinther): The terminology and invariants described below will not | |
| 7 * hold until CL 10905305 has been committed. | |
| 8 * | |
| 9 * This library contains the infrastructure to parse and integrate patch files. | 6 * This library contains the infrastructure to parse and integrate patch files. |
| 10 * | 7 * |
| 11 * Three types of elements can be patched: [LibraryElement], [ClassElement], | 8 * Three types of elements can be patched: [LibraryElement], [ClassElement], |
| 12 * [FunctionElement]. Patches are introduced in patch libraries which are loaded | 9 * [FunctionElement]. Patches are introduced in patch libraries which are loaded |
| 13 * together with the corresponding origin library. Which libraries that are | 10 * together with the corresponding origin library. Which libraries that are |
| 14 * patched is determined by the [dart2jsPatchPath] field of [LibraryInfo] found | 11 * patched is determined by the [dart2jsPatchPath] field of [LibraryInfo] found |
| 15 * in [:lib/_internal/libraries.dart:]. | 12 * in [:lib/_internal/libraries.dart:]. |
| 16 * | 13 * |
| 17 * Patch libraries are parsed like regular library and thus provided with their | 14 * Patch libraries are parsed like regular library and thus provided with their |
| 18 * own elements. These elements which are distinct from the elements from the | 15 * own elements. These elements which are distinct from the elements from the |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 54 * class RegularClass { // A regular class. | 51 * class RegularClass { // A regular class. |
| 55 * void regularMethod() {} // A regular method. | 52 * void regularMethod() {} // A regular method. |
| 56 * } | 53 * } |
| 57 * class PatchedClass { // An origin class. | 54 * class PatchedClass { // An origin class. |
| 58 * int regularField; // A regular field. | 55 * int regularField; // A regular field. |
| 59 * void regularMethod() {} // A regular method. | 56 * void regularMethod() {} // A regular method. |
| 60 * external void patchedMethod(); // An origin method. | 57 * external void patchedMethod(); // An origin method. |
| 61 * } | 58 * } |
| 62 * | 59 * |
| 63 * // In the patch library: | 60 * // In the patch library: |
| 64 * class _GhostClass { // A ghost class. | 61 * class _InjectedClass { // An injected class. |
| 65 * void _ghostMethod() {} // A ghost method. | 62 * void _injectedMethod() {} // An injected method. |
| 66 * } | 63 * } |
| 67 * patch class PatchedClass { // A patch class. | 64 * patch class PatchedClass { // A patch class. |
| 68 * int _ghostField; { // A ghost field. | 65 * int _injectedField; { // An injected field. |
| 69 * patch void patchedMethod() {} // A patch method. | 66 * patch void patchedMethod() {} // A patch method. |
| 70 * } | 67 * } |
| 71 * | 68 * |
| 72 * | 69 * |
| 73 * ## Declaration and Implementation ## | 70 * ## Declaration and Implementation ## |
| 74 * | 71 * |
| 75 * With patches we have two views on elements: as the 'declaration' which | 72 * With patches we have two views on elements: as the 'declaration' which |
| 76 * introduces the entity and defines its interface, and as the 'implementation' | 73 * introduces the entity and defines its interface, and as the 'implementation' |
| 77 * which defines the actual implementation of the entity. | 74 * which defines the actual implementation of the entity. |
| 78 * | 75 * |
| 79 * Every element has a 'declaration' and an 'implementation' element. For | 76 * Every element has a 'declaration' and an 'implementation' element. For |
| 80 * regular and ghost elements these are the same. For origin elements the | 77 * regular and injected elements these are the same. For origin elements the |
| 81 * declaration is the element itself and the implementation is the patch element | 78 * declaration is the element itself and the implementation is the patch element |
| 82 * found through its [:patch:] field. For patch elements the implementation is | 79 * found through its [:patch:] field. For patch elements the implementation is |
| 83 * the element itself and the declaration is the origin element found through | 80 * the element itself and the declaration is the origin element found through |
| 84 * its [:origin:] field. The declaration and implementation of any element is | 81 * its [:origin:] field. The declaration and implementation of any element is |
| 85 * conveniently available through the [Element.declaration] and | 82 * conveniently available through the [Element.declaration] and |
| 86 * [Element.implementation] getters. | 83 * [Element.implementation] getters. |
| 87 * | 84 * |
| 88 * Most patch-related invariants enforced through-out the compiler are defined | 85 * Most patch-related invariants enforced through-out the compiler are defined |
| 89 * in terms of 'declaration' and 'implementation', and tested through the | 86 * in terms of 'declaration' and 'implementation', and tested through the |
| 90 * predicate getters [Element.isDeclaration] and [Element.isImplementation]. | 87 * predicate getters [Element.isDeclaration] and [Element.isImplementation]. |
| 91 * Patch invariants are stated both in comments and as assertions. | 88 * Patch invariants are stated both in comments and as assertions. |
| 92 * | 89 * |
| 93 * | 90 * |
| 94 * ## General invariant guidelines ## | 91 * ## General invariant guidelines ## |
| 95 * | 92 * |
| 96 * For [LibraryElement] we always use declarations. This means the | 93 * For [LibraryElement] we always use declarations. This means the |
| 97 * [Element.getLibrary] method will only return library declarations. Patch | 94 * [Element.getLibrary] method will only return library declarations. Patch |
| 98 * library implementations are only accessed through calls to | 95 * library implementations are only accessed through calls to |
| 99 * [Element.getImplementationLibrary] which is used to setup the correct | 96 * [Element.getImplementationLibrary] which is used to setup the correct |
| 100 * [Element.enclosingElement] relation between patch/ghost elements and the | 97 * [Element.enclosingElement] relation between patch/injected elements and the |
| 101 * patch library. | 98 * patch library. |
| 102 * | 99 * |
| 103 * For [ClassElement] and [FunctionElement] we use declarations for determining | 100 * For [ClassElement] and [FunctionElement] we use declarations for determining |
| 104 * identity and implementations for work based on the AST nodes, such as | 101 * identity and implementations for work based on the AST nodes, such as |
| 105 * resolution, type-checking, type inference, building SSA graphs, etc. | 102 * resolution, type-checking, type inference, building SSA graphs, etc. |
| 106 * - Worklist only contain declaration elements. | 103 * - Worklist only contain declaration elements. |
| 107 * - Most maps and sets use declarations exclusively, and their individual | 104 * - Most maps and sets use declarations exclusively, and their individual |
| 108 * invariants are stated in the field comments. | 105 * invariants are stated in the field comments. |
| 109 * - [TreeElements] only map to patch elements from inside a patch library. | 106 * - [TreeElements] only map to patch elements from inside a patch library. |
| 107 * TODO(johnniwinther): Simplify this invariant to use only declarations in |
| 108 * [TreeElements]. |
| 110 * - Builders shift between declaration and implementation depending on usages. | 109 * - Builders shift between declaration and implementation depending on usages. |
| 111 * - Compile-time constants use constructor implementation exclusively. | 110 * - Compile-time constants use constructor implementation exclusively. |
| 112 * - 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 |
| 113 * element. | 112 * element. |
| 114 */ | 113 */ |
| 115 #library("patchparser"); | 114 #library("patchparser"); |
| 116 | 115 |
| 117 #import("dart:uri"); | 116 #import("dart:uri"); |
| 118 #import("tree/tree.dart", prefix: "tree"); | 117 #import("tree/tree.dart", prefix: "tree"); |
| 119 #import("leg.dart", prefix: 'leg'); // CompilerTask, Compiler. | 118 #import("leg.dart", prefix: 'leg'); // CompilerTask, Compiler. |
| 120 #import("apiimpl.dart"); | 119 #import("apiimpl.dart"); |
| 121 #import("scanner/scannerlib.dart"); // Scanner, Parsers, Listeners | 120 #import("scanner/scannerlib.dart"); // Scanner, Parsers, Listeners |
| 122 #import("elements/elements.dart"); | 121 #import("elements/elements.dart"); |
| 123 #import('util/util.dart'); | 122 #import('util/util.dart'); |
| 124 | 123 |
| 125 class PatchParserTask extends leg.CompilerTask { | 124 class PatchParserTask extends leg.CompilerTask { |
| 126 PatchParserTask(leg.Compiler compiler): super(compiler); | 125 PatchParserTask(leg.Compiler compiler): super(compiler); |
| 127 final String name = "Patching Parser"; | 126 final String name = "Patching Parser"; |
| 128 | 127 |
| 129 /** | 128 /** |
| 130 * Scans a library patch file, applies the method patches and | 129 * Scans a library patch file, applies the method patches and |
| 131 * injections to the library, and returns a list of class | 130 * injections to the library, and returns a list of class |
| 132 * patches. | 131 * patches. |
| 133 */ | 132 */ |
| 134 void patchLibrary(Uri patchUri, LibraryElement library) { | 133 void patchLibrary(Uri patchUri, LibraryElement originLibrary) { |
| 135 leg.Script script = compiler.readScript(patchUri, null); | 134 leg.Script script = compiler.readScript(patchUri, null); |
| 136 CompilationUnitElement compilationUnit = | 135 var patchLibrary = new LibraryElement(script, patchUri, originLibrary); |
| 137 new CompilationUnitElement(script, library); | |
| 138 LinkBuilder<tree.LibraryTag> imports = new LinkBuilder<tree.LibraryTag>(); | 136 LinkBuilder<tree.LibraryTag> imports = new LinkBuilder<tree.LibraryTag>(); |
| 139 compiler.withCurrentElement(compilationUnit, () { | 137 compiler.withCurrentElement(patchLibrary.entryCompilationUnit, () { |
| 140 // This patches the elements of the patch library into [library]. | 138 // This patches the elements of the patch library into [library]. |
| 141 // Injected elements are added directly under the compilation unit. | 139 // Injected elements are added directly under the compilation unit. |
| 142 // Patch elements are stored on the patched functions or classes. | 140 // Patch elements are stored on the patched functions or classes. |
| 143 scanLibraryElements(compilationUnit, imports); | 141 scanLibraryElements(patchLibrary.entryCompilationUnit, imports); |
| 144 }); | 142 }); |
| 145 // After scanning declarations, we handle the import tags in the patch. | 143 // After scanning declarations, we handle the import tags in the patch. |
| 146 // TODO(lrn): These imports end up in the original library and are in | 144 // TODO(lrn): These imports end up in the original library and are in |
| 147 // scope for the original methods too. This should be fixed. | 145 // scope for the original methods too. This should be fixed. |
| 148 for (tree.LibraryTag tag in imports.toLink()) { | 146 for (tree.LibraryTag tag in imports.toLink()) { |
| 149 compiler.scanner.importLibraryFromTag(tag, compilationUnit); | 147 compiler.scanner.importLibraryFromTag(tag, |
| 148 patchLibrary.entryCompilationUnit); |
| 150 } | 149 } |
| 151 } | 150 } |
| 152 | 151 |
| 153 void scanLibraryElements( | 152 void scanLibraryElements( |
| 154 CompilationUnitElement compilationUnit, | 153 CompilationUnitElement compilationUnit, |
| 155 LinkBuilder<tree.LibraryTag> imports) { | 154 LinkBuilder<tree.LibraryTag> imports) { |
| 156 measure(() { | 155 measure(() { |
| 157 // TODO(lrn): Possibly recursively handle #source directives in patch. | 156 // TODO(lrn): Possibly recursively handle #source directives in patch. |
| 158 leg.Script script = compilationUnit.script; | 157 leg.Script script = compilationUnit.script; |
| 159 Token tokens = new StringScanner(script.text).tokenize(); | 158 Token tokens = new StringScanner(script.text).tokenize(); |
| 160 Function idGenerator = compiler.getNextFreeClassId; | 159 Function idGenerator = compiler.getNextFreeClassId; |
| 161 PatchListener patchListener = | 160 PatchListener patchListener = |
| 162 new PatchElementListener(compiler, | 161 new PatchElementListener(compiler, |
| 163 compilationUnit, | 162 compilationUnit, |
| 164 idGenerator, | 163 idGenerator, |
| 165 imports); | 164 imports); |
| 166 new PatchParser(patchListener).parseUnit(tokens); | 165 new PatchParser(patchListener).parseUnit(tokens); |
| 167 }); | 166 }); |
| 168 } | 167 } |
| 169 | 168 |
| 170 tree.ClassNode parsePatchClassNode(PartialClassElement element) { | 169 tree.ClassNode parsePatchClassNode(PartialClassElement element) { |
| 170 // Parse [PartialClassElement] using a "patch"-aware parser instead |
| 171 // of calling its [parseNode] method. |
| 172 if (element.cachedNode != null) return element.cachedNode; |
| 173 |
| 171 return measure(() => compiler.withCurrentElement(element, () { | 174 return measure(() => compiler.withCurrentElement(element, () { |
| 172 // Parse [PartialClassElement] using a "patch"-aware parser instead | 175 PatchMemberListener listener = new PatchMemberListener(compiler, element); |
| 173 // of calling its [parseNode] method. | |
| 174 if (element.cachedNode != null) return element.cachedNode; | |
| 175 PatchMemberListener listener = | |
| 176 new PatchMemberListener(compiler, element); | |
| 177 Parser parser = new PatchClassElementParser(listener); | 176 Parser parser = new PatchClassElementParser(listener); |
| 178 Token token = parser.parseTopLevelDeclaration(element.beginToken); | 177 Token token = parser.parseTopLevelDeclaration(element.beginToken); |
| 179 assert(token === element.endToken.next); | 178 assert(token === element.endToken.next); |
| 180 element.cachedNode = listener.popNode(); | 179 element.cachedNode = listener.popNode(); |
| 181 assert(listener.nodes.isEmpty()); | 180 assert(listener.nodes.isEmpty()); |
| 182 return element.cachedNode; | 181 return element.cachedNode; |
| 183 })); | 182 })); |
| 184 } | 183 } |
| 185 } | 184 } |
| 186 | 185 |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 307 | 306 |
| 308 void addLibraryTag(tree.LibraryTag tag) { | 307 void addLibraryTag(tree.LibraryTag tag) { |
| 309 super.addLibraryTag(tag); | 308 super.addLibraryTag(tag); |
| 310 imports.addLast(tag); | 309 imports.addLast(tag); |
| 311 } | 310 } |
| 312 | 311 |
| 313 void pushElement(Element element) { | 312 void pushElement(Element element) { |
| 314 if (isMemberPatch || (isClassPatch && element is ClassElement)) { | 313 if (isMemberPatch || (isClassPatch && element is ClassElement)) { |
| 315 // Apply patch. | 314 // Apply patch. |
| 316 element.addMetadata(popMetadata()); | 315 element.addMetadata(popMetadata()); |
| 317 LibraryElement library = compilationUnitElement.getLibrary(); | 316 LibraryElement originLibrary = compilationUnitElement.getLibrary(); |
| 318 Element existing = library.localLookup(element.name); | 317 assert(originLibrary.isPatched); |
| 318 Element existing = originLibrary.localLookup(element.name); |
| 319 if (isMemberPatch) { | 319 if (isMemberPatch) { |
| 320 if (element is! FunctionElement) { | 320 if (element is! FunctionElement) { |
| 321 listener.internalErrorOnElement(element, | 321 listener.internalErrorOnElement(element, |
| 322 "Member patch is not a function."); | 322 "Member patch is not a function."); |
| 323 } | 323 } |
| 324 if (existing.kind === ElementKind.ABSTRACT_FIELD) { | 324 if (existing.kind === ElementKind.ABSTRACT_FIELD) { |
| 325 if (!element.isAccessor()) { | 325 if (!element.isAccessor()) { |
| 326 listener.internalErrorOnElement( | 326 listener.internalErrorOnElement( |
| 327 element, "Patching non-accessor with accessor"); | 327 element, "Patching non-accessor with accessor"); |
| 328 } | 328 } |
| 329 AbstractFieldElement field = existing; | 329 AbstractFieldElement field = existing; |
| 330 if (element.isGetter()) { | 330 if (element.isGetter()) { |
| 331 existing = field.getter; | 331 existing = field.getter; |
| 332 } else { | 332 } else { |
| 333 existing = field.setter; | 333 existing = field.setter; |
| 334 } | 334 } |
| 335 } | 335 } |
| 336 if (existing is! FunctionElement) { | 336 if (existing is! FunctionElement) { |
| 337 listener.internalErrorOnElement(element, | 337 listener.internalErrorOnElement(element, |
| 338 "No corresponding method for patch."); | 338 "No corresponding method for patch."); |
| 339 } | 339 } |
| 340 FunctionElement function = existing; | 340 FunctionElement function = existing; |
| 341 if (function.isPatched) { | 341 if (function.isPatched) { |
| 342 listener.internalErrorOnElement( | 342 listener.internalErrorOnElement( |
| 343 element, "Patching the same function more than once."); | 343 element, "Patching the same function more than once."); |
| 344 } | 344 } |
| 345 function.patch = element; | 345 function.patch = element; |
| 346 element.origin = function; |
| 346 } else { | 347 } else { |
| 347 if (existing is! ClassElement) { | 348 if (existing is! ClassElement) { |
| 348 listener.internalErrorOnElement( | 349 listener.internalErrorOnElement( |
| 349 element, "Patching a non-class with a class patch."); | 350 element, "Patching a non-class with a class patch."); |
| 350 } | 351 } |
| 351 ClassElement classElement = existing; | 352 ClassElement classElement = existing; |
| 352 if (classElement.isPatched) { | 353 if (classElement.isPatched) { |
| 353 listener.internalErrorOnElement( | 354 listener.internalErrorOnElement( |
| 354 element, "Patching the same class more than once."); | 355 element, "Patching the same class more than once."); |
| 355 } | 356 } |
| 356 classElement.patch = element; | 357 classElement.patch = element; |
| 358 element.origin = classElement; |
| 357 } | 359 } |
| 358 return; | |
| 359 } | 360 } |
| 360 super.pushElement(element); | 361 super.pushElement(element); |
| 361 } | 362 } |
| 362 } | 363 } |
| 363 | 364 |
| 364 /** | 365 /** |
| 365 * Extension of [MemberListener] for parsing patch class bodies. | 366 * Extension of [MemberListener] for parsing patch class bodies. |
| 366 */ | 367 */ |
| 367 class PatchMemberListener extends MemberListener implements PatchListener { | 368 class PatchMemberListener extends MemberListener implements PatchListener { |
| 368 bool isMemberPatch = false; | 369 bool isMemberPatch = false; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 401 super.addMember(element); | 402 super.addMember(element); |
| 402 } | 403 } |
| 403 } | 404 } |
| 404 | 405 |
| 405 // TODO(ahe): Get rid of this class. | 406 // TODO(ahe): Get rid of this class. |
| 406 class PatchMetadataAnnotation extends MetadataAnnotation { | 407 class PatchMetadataAnnotation extends MetadataAnnotation { |
| 407 final leg.Constant value = null; | 408 final leg.Constant value = null; |
| 408 | 409 |
| 409 PatchMetadataAnnotation() : super(STATE_DONE); | 410 PatchMetadataAnnotation() : super(STATE_DONE); |
| 410 } | 411 } |
| OLD | NEW |