| 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 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 159 Function idGenerator = compiler.getNextFreeClassId; | 159 Function idGenerator = compiler.getNextFreeClassId; |
| 160 PatchListener patchListener = | 160 PatchListener patchListener = |
| 161 new PatchElementListener(compiler, | 161 new PatchElementListener(compiler, |
| 162 compilationUnit, | 162 compilationUnit, |
| 163 idGenerator, | 163 idGenerator, |
| 164 imports); | 164 imports); |
| 165 new PatchParser(patchListener).parseUnit(tokens); | 165 new PatchParser(patchListener).parseUnit(tokens); |
| 166 }); | 166 }); |
| 167 } | 167 } |
| 168 | 168 |
| 169 tree.ClassNode parsePatchClassNode(PartialClassElement element) { | 169 void parsePatchClassNode(PartialClassElement element) { |
| 170 // Parse [PartialClassElement] using a "patch"-aware parser instead | 170 // Parse [PartialClassElement] using a "patch"-aware parser instead |
| 171 // of calling its [parseNode] method. | 171 // of calling its [parseNode] method. |
| 172 if (element.cachedNode != null) return element.cachedNode; | 172 if (element.cachedNode != null) return; |
| 173 | 173 |
| 174 return measure(() => compiler.withCurrentElement(element, () { | 174 return measure(() => compiler.withCurrentElement(element, () { |
| 175 PatchMemberListener listener = new PatchMemberListener(compiler, element); | 175 PatchMemberListener listener = new PatchMemberListener(compiler, element); |
| 176 Parser parser = new PatchClassElementParser(listener); | 176 Parser parser = new PatchClassElementParser(listener); |
| 177 Token token = parser.parseTopLevelDeclaration(element.beginToken); | 177 Token token = parser.parseTopLevelDeclaration(element.beginToken); |
| 178 assert(token === element.endToken.next); | 178 assert(token === element.endToken.next); |
| 179 element.cachedNode = listener.popNode(); | 179 element.cachedNode = listener.popNode(); |
| 180 assert(listener.nodes.isEmpty()); | 180 assert(listener.nodes.isEmpty()); |
| 181 return element.cachedNode; | 181 |
| 182 Link<Element> patches = element.localMembers; |
| 183 applyContainerPatch(element.origin, patches); |
| 182 })); | 184 })); |
| 183 } | 185 } |
| 186 |
| 187 void applyContainerPatch(ScopeContainerElement original, |
| 188 Link<Element> patches) { |
| 189 while (!patches.isEmpty()) { |
| 190 Element patchElement = patches.head; |
| 191 Element originalElement = original.localLookup(patchElement.name); |
| 192 if (patchElement.isAccessor() && originalElement !== null) { |
| 193 if (originalElement.kind !== ElementKind.ABSTRACT_FIELD) { |
| 194 compiler.internalError( |
| 195 "Cannot patch non-getter/setter with getter/setter", |
| 196 element: originalElement); |
| 197 } |
| 198 AbstractFieldElement originalField = originalElement; |
| 199 if (patchElement.isGetter()) { |
| 200 originalElement = originalField.getter; |
| 201 } else { |
| 202 originalElement = originalField.setter; |
| 203 } |
| 204 } |
| 205 if (originalElement === null) { |
| 206 if (isPatchElement(patchElement)) { |
| 207 compiler.internalError("Cannot patch non-existing member '" |
| 208 "${patchElement.name.slowToString()}'."); |
| 209 } |
| 210 } else { |
| 211 patchMember(originalElement, patchElement); |
| 212 } |
| 213 patches = patches.tail; |
| 214 } |
| 215 } |
| 216 |
| 217 bool isPatchElement(Element element) { |
| 218 // TODO(lrn): More checks needed if we introduce metadata for real. |
| 219 // In that case, it must have the identifier "native" as metadata. |
| 220 for (Link link = element.metadata; !link.isEmpty(); link = link.tail) { |
| 221 if (link.head is PatchMetadataAnnotation) return true; |
| 222 } |
| 223 return false; |
| 224 } |
| 225 |
| 226 void patchMember(Element originalElement, Element patchElement) { |
| 227 // The original library has an element with the same name as the patch |
| 228 // library element. |
| 229 // In this case, the patch library element must be a function marked as |
| 230 // "patch" and it must have the same signature as the function it patches. |
| 231 if (!isPatchElement(patchElement)) { |
| 232 compiler.internalError("Cannot overwrite existing '" |
| 233 "${originalElement.name.slowToString()}' with non-patch."); |
| 234 } |
| 235 if (originalElement is! FunctionElement) { |
| 236 // TODO(lrn): Handle class declarations too. |
| 237 compiler.internalError("Can only patch functions", element: originalElemen
t); |
| 238 } |
| 239 FunctionElement original = originalElement; |
| 240 if (!original.modifiers.isExternal()) { |
| 241 compiler.internalError("Can only patch external functions.", element: orig
inal); |
| 242 } |
| 243 if (patchElement is! FunctionElement || |
| 244 !patchSignatureMatches(original, patchElement)) { |
| 245 compiler.internalError("Can only patch functions with matching signatures"
, |
| 246 element: original); |
| 247 } |
| 248 applyFunctionPatch(original, patchElement); |
| 249 } |
| 250 |
| 251 bool patchSignatureMatches(FunctionElement original, FunctionElement patch) { |
| 252 // TODO(lrn): Check that patches actually match the signature of |
| 253 // the function it's patching. |
| 254 return true; |
| 255 } |
| 256 |
| 257 void applyFunctionPatch(FunctionElement element, |
| 258 FunctionElement patchElement) { |
| 259 if (element.isPatched) { |
| 260 compiler.internalError("Trying to patch a function more than once.", |
| 261 element: element); |
| 262 } |
| 263 if (element.cachedNode !== null) { |
| 264 compiler.internalError("Trying to patch an already compiled function.", |
| 265 element: element); |
| 266 } |
| 267 // Don't just assign the patch field. This also updates the cachedNode. |
| 268 element.setPatch(patchElement); |
| 269 patchElement.origin = element; |
| 270 } |
| 184 } | 271 } |
| 185 | 272 |
| 186 /** | 273 /** |
| 187 * Extension of the [Listener] interface to handle the extra "patch" pseudo- | 274 * Extension of the [Listener] interface to handle the extra "patch" pseudo- |
| 188 * keyword in patch files. | 275 * keyword in patch files. |
| 189 * Patch files shouldn't have a type named "patch". | 276 * Patch files shouldn't have a type named "patch". |
| 190 */ | 277 */ |
| 191 abstract class PatchListener extends Listener { | 278 abstract class PatchListener extends Listener { |
| 192 void beginPatch(Token patch); | 279 void beginPatch(Token patch); |
| 193 void endPatch(Token patch); | 280 void endPatch(Token patch); |
| (...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 402 super.addMember(element); | 489 super.addMember(element); |
| 403 } | 490 } |
| 404 } | 491 } |
| 405 | 492 |
| 406 // TODO(ahe): Get rid of this class. | 493 // TODO(ahe): Get rid of this class. |
| 407 class PatchMetadataAnnotation extends MetadataAnnotation { | 494 class PatchMetadataAnnotation extends MetadataAnnotation { |
| 408 final leg.Constant value = null; | 495 final leg.Constant value = null; |
| 409 | 496 |
| 410 PatchMetadataAnnotation() : super(STATE_DONE); | 497 PatchMetadataAnnotation() : super(STATE_DONE); |
| 411 } | 498 } |
| OLD | NEW |