| 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); | 136 |
| 138 library.addCompilationUnit(compilationUnit); | |
| 139 LinkBuilder<tree.LibraryTag> imports = new LinkBuilder<tree.LibraryTag>(); | 137 LinkBuilder<tree.LibraryTag> imports = new LinkBuilder<tree.LibraryTag>(); |
| 140 compiler.withCurrentElement(compilationUnit, () { | 138 compiler.withCurrentElement(patchLibrary.entryCompilationUnit, () { |
| 141 // This patches the elements of the patch library into [library]. | 139 // This patches the elements of the patch library into [library]. |
| 142 // Injected elements are added directly under the compilation unit. | 140 // Injected elements are added directly under the compilation unit. |
| 143 // Patch elements are stored on the patched functions or classes. | 141 // Patch elements are stored on the patched functions or classes. |
| 144 scanLibraryElements(compilationUnit, imports); | 142 scanLibraryElements(patchLibrary.entryCompilationUnit, imports); |
| 145 }); | 143 }); |
| 146 // After scanning declarations, we handle the import tags in the patch. | 144 // After scanning declarations, we handle the import tags in the patch. |
| 147 // TODO(lrn): These imports end up in the original library and are in | 145 // TODO(lrn): These imports end up in the original library and are in |
| 148 // scope for the original methods too. This should be fixed. | 146 // scope for the original methods too. This should be fixed. |
| 149 for (tree.LibraryTag tag in imports.toLink()) { | 147 for (tree.LibraryTag tag in imports.toLink()) { |
| 150 compiler.scanner.importLibraryFromTag(tag, compilationUnit); | 148 compiler.scanner.importLibraryFromTag(tag, |
| 149 patchLibrary.entryCompilationUnit); |
| 151 } | 150 } |
| 152 } | 151 } |
| 153 | 152 |
| 154 void scanLibraryElements( | 153 void scanLibraryElements( |
| 155 CompilationUnitElement compilationUnit, | 154 CompilationUnitElement compilationUnit, |
| 156 LinkBuilder<tree.LibraryTag> imports) { | 155 LinkBuilder<tree.LibraryTag> imports) { |
| 157 measure(() { | 156 measure(() { |
| 158 // TODO(lrn): Possibly recursively handle #source directives in patch. | 157 // TODO(lrn): Possibly recursively handle #source directives in patch. |
| 159 leg.Script script = compilationUnit.script; | 158 leg.Script script = compilationUnit.script; |
| 160 Token tokens = new StringScanner(script.text).tokenize(); | 159 Token tokens = new StringScanner(script.text).tokenize(); |
| 161 Function idGenerator = compiler.getNextFreeClassId; | 160 Function idGenerator = compiler.getNextFreeClassId; |
| 162 PatchListener patchListener = | 161 PatchListener patchListener = |
| 163 new PatchElementListener(compiler, | 162 new PatchElementListener(compiler, |
| 164 compilationUnit, | 163 compilationUnit, |
| 165 idGenerator, | 164 idGenerator, |
| 166 imports); | 165 imports); |
| 167 new PatchParser(patchListener).parseUnit(tokens); | 166 new PatchParser(patchListener).parseUnit(tokens); |
| 168 }); | 167 }); |
| 169 } | 168 } |
| 170 | 169 |
| 171 tree.ClassNode parsePatchClassNode(PartialClassElement element) { | 170 tree.ClassNode parsePatchClassNode(PartialClassElement element) { |
| 171 // Parse [PartialClassElement] using a "patch"-aware parser instead |
| 172 // of calling its [parseNode] method. |
| 173 if (element.cachedNode != null) return element.cachedNode; |
| 174 |
| 172 return measure(() => compiler.withCurrentElement(element, () { | 175 return measure(() => compiler.withCurrentElement(element, () { |
| 173 // Parse [PartialClassElement] using a "patch"-aware parser instead | 176 PatchMemberListener listener = new PatchMemberListener(compiler, element); |
| 174 // of calling its [parseNode] method. | |
| 175 if (element.cachedNode != null) return element.cachedNode; | |
| 176 PatchMemberListener listener = | |
| 177 new PatchMemberListener(compiler, element); | |
| 178 Parser parser = new PatchClassElementParser(listener); | 177 Parser parser = new PatchClassElementParser(listener); |
| 179 Token token = parser.parseTopLevelDeclaration(element.beginToken); | 178 Token token = parser.parseTopLevelDeclaration(element.beginToken); |
| 180 assert(token === element.endToken.next); | 179 assert(token === element.endToken.next); |
| 181 element.cachedNode = listener.popNode(); | 180 element.cachedNode = listener.popNode(); |
| 182 assert(listener.nodes.isEmpty()); | 181 assert(listener.nodes.isEmpty()); |
| 183 return element.cachedNode; | 182 return element.cachedNode; |
| 184 })); | 183 })); |
| 185 } | 184 } |
| 186 } | 185 } |
| 187 | 186 |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 308 | 307 |
| 309 void addLibraryTag(tree.LibraryTag tag) { | 308 void addLibraryTag(tree.LibraryTag tag) { |
| 310 super.addLibraryTag(tag); | 309 super.addLibraryTag(tag); |
| 311 imports.addLast(tag); | 310 imports.addLast(tag); |
| 312 } | 311 } |
| 313 | 312 |
| 314 void pushElement(Element element) { | 313 void pushElement(Element element) { |
| 315 if (isMemberPatch || (isClassPatch && element is ClassElement)) { | 314 if (isMemberPatch || (isClassPatch && element is ClassElement)) { |
| 316 // Apply patch. | 315 // Apply patch. |
| 317 element.addMetadata(popMetadata()); | 316 element.addMetadata(popMetadata()); |
| 318 LibraryElement library = compilationUnitElement.getLibrary(); | 317 LibraryElement originLibrary = compilationUnitElement.getLibrary(); |
| 319 Element existing = library.localLookup(element.name); | 318 assert(originLibrary.isPatched); |
| 319 Element existing = originLibrary.localLookup(element.name); |
| 320 if (isMemberPatch) { | 320 if (isMemberPatch) { |
| 321 if (element is! FunctionElement) { | 321 if (element is! FunctionElement) { |
| 322 listener.internalErrorOnElement(element, | 322 listener.internalErrorOnElement(element, |
| 323 "Member patch is not a function."); | 323 "Member patch is not a function."); |
| 324 } | 324 } |
| 325 if (existing.kind === ElementKind.ABSTRACT_FIELD) { | 325 if (existing.kind === ElementKind.ABSTRACT_FIELD) { |
| 326 if (!element.isAccessor()) { | 326 if (!element.isAccessor()) { |
| 327 listener.internalErrorOnElement( | 327 listener.internalErrorOnElement( |
| 328 element, "Patching non-accessor with accessor"); | 328 element, "Patching non-accessor with accessor"); |
| 329 } | 329 } |
| 330 AbstractFieldElement field = existing; | 330 AbstractFieldElement field = existing; |
| 331 if (element.isGetter()) { | 331 if (element.isGetter()) { |
| 332 existing = field.getter; | 332 existing = field.getter; |
| 333 } else { | 333 } else { |
| 334 existing = field.setter; | 334 existing = field.setter; |
| 335 } | 335 } |
| 336 } | 336 } |
| 337 if (existing is! FunctionElement) { | 337 if (existing is! FunctionElement) { |
| 338 listener.internalErrorOnElement(element, | 338 listener.internalErrorOnElement(element, |
| 339 "No corresponding method for patch."); | 339 "No corresponding method for patch."); |
| 340 } | 340 } |
| 341 FunctionElement function = existing; | 341 FunctionElement function = existing; |
| 342 if (function.isPatched) { | 342 if (function.isPatched) { |
| 343 listener.internalErrorOnElement( | 343 listener.internalErrorOnElement( |
| 344 element, "Patching the same function more than once."); | 344 element, "Patching the same function more than once."); |
| 345 } | 345 } |
| 346 function.patch = element; | 346 function.patch = element; |
| 347 element.origin = function; |
| 347 } else { | 348 } else { |
| 348 if (existing is! ClassElement) { | 349 if (existing is! ClassElement) { |
| 349 listener.internalErrorOnElement( | 350 listener.internalErrorOnElement( |
| 350 element, "Patching a non-class with a class patch."); | 351 element, "Patching a non-class with a class patch."); |
| 351 } | 352 } |
| 352 ClassElement classElement = existing; | 353 ClassElement classElement = existing; |
| 353 if (classElement.isPatched) { | 354 if (classElement.isPatched) { |
| 354 listener.internalErrorOnElement( | 355 listener.internalErrorOnElement( |
| 355 element, "Patching the same class more than once."); | 356 element, "Patching the same class more than once."); |
| 356 } | 357 } |
| 357 classElement.patch = element; | 358 classElement.patch = element; |
| 359 element.origin = classElement; |
| 358 } | 360 } |
| 359 return; | |
| 360 } | 361 } |
| 361 super.pushElement(element); | 362 super.pushElement(element); |
| 362 } | 363 } |
| 363 } | 364 } |
| 364 | 365 |
| 365 /** | 366 /** |
| 366 * Extension of [MemberListener] for parsing patch class bodies. | 367 * Extension of [MemberListener] for parsing patch class bodies. |
| 367 */ | 368 */ |
| 368 class PatchMemberListener extends MemberListener implements PatchListener { | 369 class PatchMemberListener extends MemberListener implements PatchListener { |
| 369 bool isMemberPatch = false; | 370 bool isMemberPatch = false; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 402 super.addMember(element); | 403 super.addMember(element); |
| 403 } | 404 } |
| 404 } | 405 } |
| 405 | 406 |
| 406 // TODO(ahe): Get rid of this class. | 407 // TODO(ahe): Get rid of this class. |
| 407 class PatchMetadataAnnotation extends MetadataAnnotation { | 408 class PatchMetadataAnnotation extends MetadataAnnotation { |
| 408 final leg.Constant value = null; | 409 final leg.Constant value = null; |
| 409 | 410 |
| 410 PatchMetadataAnnotation() : super(STATE_DONE); | 411 PatchMetadataAnnotation() : super(STATE_DONE); |
| 411 } | 412 } |
| OLD | NEW |