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 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
111 * - 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 |
112 * element. | 112 * element. |
113 */ | 113 */ |
114 | 114 |
115 library patchparser; | 115 library patchparser; |
116 | 116 |
117 import "dart:uri"; | 117 import "dart:uri"; |
118 import "tree/tree.dart" as tree; | 118 import "tree/tree.dart" as tree; |
119 import "dart2jslib.dart" as leg; // CompilerTask, Compiler. | 119 import "dart2jslib.dart" as leg; // CompilerTask, Compiler. |
120 import "apiimpl.dart"; | 120 import "apiimpl.dart"; |
121 import "../compiler.dart" as api; | |
121 import "scanner/scannerlib.dart"; // Scanner, Parsers, Listeners | 122 import "scanner/scannerlib.dart"; // Scanner, Parsers, Listeners |
122 import "elements/elements.dart"; | 123 import "elements/elements.dart"; |
123 import "elements/modelx.dart" show LibraryElementX, MetadataAnnotationX; | 124 import "elements/modelx.dart" show LibraryElementX, MetadataAnnotationX; |
124 import 'util/util.dart'; | 125 import 'util/util.dart'; |
125 | 126 |
126 class PatchParserTask extends leg.CompilerTask { | 127 class PatchParserTask extends leg.CompilerTask { |
127 PatchParserTask(leg.Compiler compiler): super(compiler); | 128 PatchParserTask(leg.Compiler compiler): super(compiler); |
128 final String name = "Patching Parser"; | 129 final String name = "Patching Parser"; |
129 | 130 |
130 /** | 131 /** |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
185 Token token = parser.parseTopLevelDeclaration(element.beginToken); | 186 Token token = parser.parseTopLevelDeclaration(element.beginToken); |
186 assert(identical(token, element.endToken.next)); | 187 assert(identical(token, element.endToken.next)); |
187 element.cachedNode = listener.popNode(); | 188 element.cachedNode = listener.popNode(); |
188 assert(listener.nodes.isEmpty); | 189 assert(listener.nodes.isEmpty); |
189 | 190 |
190 Link<Element> patches = element.localMembers; | 191 Link<Element> patches = element.localMembers; |
191 applyContainerPatch(element.origin, patches); | 192 applyContainerPatch(element.origin, patches); |
192 })); | 193 })); |
193 } | 194 } |
194 | 195 |
195 void applyContainerPatch(ScopeContainerElement original, | 196 void applyContainerPatch(ClassElement originClass, |
196 Link<Element> patches) { | 197 Link<Element> patches) { |
197 while (!patches.isEmpty) { | 198 for (Element patch in patches) { |
198 Element patchElement = patches.head; | 199 if (!_isPatchElement(patch)) continue; |
199 Element originalElement = original.localLookup(patchElement.name); | 200 |
200 if (patchElement.isAccessor() && originalElement != null) { | 201 Element origin = originClass.localLookup(patch.name); |
201 if (!identical(originalElement.kind, ElementKind.ABSTRACT_FIELD)) { | 202 _patchElement(compiler, origin, patch); |
202 compiler.internalError( | |
203 "Cannot patch non-getter/setter with getter/setter", | |
204 element: originalElement); | |
205 } | |
206 AbstractFieldElement originalField = originalElement; | |
207 if (patchElement.isGetter()) { | |
208 originalElement = originalField.getter; | |
209 } else { | |
210 originalElement = originalField.setter; | |
211 } | |
212 } | |
213 if (originalElement == null) { | |
214 if (isPatchElement(patchElement)) { | |
215 compiler.internalError("Cannot patch non-existing member '" | |
216 "${patchElement.name.slowToString()}'."); | |
217 } | |
218 } else { | |
219 patchMember(originalElement, patchElement); | |
220 } | |
221 patches = patches.tail; | |
222 } | 203 } |
223 } | 204 } |
224 | |
225 bool isPatchElement(Element element) { | |
226 // TODO(lrn): More checks needed if we introduce metadata for real. | |
227 // In that case, it must have the identifier "native" as metadata. | |
228 for (Link link = element.metadata; !link.isEmpty; link = link.tail) { | |
229 if (link.head is PatchMetadataAnnotation) return true; | |
230 } | |
231 return false; | |
232 } | |
233 | |
234 void patchMember(Element originalElement, Element patchElement) { | |
235 // The original library has an element with the same name as the patch | |
236 // library element. | |
237 // In this case, the patch library element must be a function marked as | |
238 // "patch" and it must have the same signature as the function it patches. | |
239 if (!isPatchElement(patchElement)) { | |
240 compiler.internalError("Cannot overwrite existing '" | |
241 "${originalElement.name.slowToString()}' with non-patch."); | |
242 } | |
243 if (originalElement is! FunctionElement) { | |
244 // TODO(lrn): Handle class declarations too. | |
245 compiler.internalError("Can only patch functions", element: originalElemen t); | |
246 } | |
247 FunctionElement original = originalElement; | |
248 if (!original.modifiers.isExternal()) { | |
249 compiler.internalError("Can only patch external functions.", element: orig inal); | |
250 } | |
251 if (patchElement is! FunctionElement || | |
252 !patchSignatureMatches(original, patchElement)) { | |
253 compiler.internalError("Can only patch functions with matching signatures" , | |
254 element: original); | |
255 } | |
256 applyFunctionPatch(original, patchElement); | |
257 } | |
258 | |
259 bool patchSignatureMatches(FunctionElement original, FunctionElement patch) { | |
260 // TODO(lrn): Check that patches actually match the signature of | |
261 // the function it's patching. | |
262 return true; | |
263 } | |
264 | |
265 void applyFunctionPatch(FunctionElement element, | |
266 FunctionElement patchElement) { | |
267 if (element.isPatched) { | |
268 compiler.internalError("Trying to patch a function more than once.", | |
269 element: element); | |
270 } | |
271 if (element.cachedNode != null) { | |
272 compiler.internalError("Trying to patch an already compiled function.", | |
273 element: element); | |
274 } | |
275 // Don't just assign the patch field. This also updates the cachedNode. | |
276 element.setPatch(patchElement); | |
277 patchElement.origin = element; | |
278 } | |
279 } | 205 } |
280 | 206 |
281 /** | 207 /** |
282 * Extension of the [Listener] interface to handle the extra "patch" pseudo- | 208 * Extension of the [Listener] interface to handle the extra "patch" pseudo- |
283 * keyword in patch files. | 209 * keyword in patch files. |
284 * Patch files shouldn't have a type named "patch". | 210 * Patch files shouldn't have a type named "patch". |
285 */ | 211 */ |
286 abstract class PatchListener extends Listener { | 212 abstract class PatchListener extends Listener { |
287 void beginPatch(Token patch); | 213 void beginPatch(Token patch); |
288 void endPatch(Token patch); | 214 void endPatch(Token patch); |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
397 * Allow script tags (import only, the parser rejects the rest for now) in | 323 * Allow script tags (import only, the parser rejects the rest for now) in |
398 * patch files. The import tags will be added to the library. | 324 * patch files. The import tags will be added to the library. |
399 */ | 325 */ |
400 bool allowLibraryTags() => true; | 326 bool allowLibraryTags() => true; |
401 | 327 |
402 void addLibraryTag(tree.LibraryTag tag) { | 328 void addLibraryTag(tree.LibraryTag tag) { |
403 super.addLibraryTag(tag); | 329 super.addLibraryTag(tag); |
404 imports.addLast(tag); | 330 imports.addLast(tag); |
405 } | 331 } |
406 | 332 |
407 void pushElement(Element element) { | 333 void pushElement(Element patch) { |
408 if (isMemberPatch || (isClassPatch && element is ClassElement)) { | 334 if (isMemberPatch || (isClassPatch && patch is ClassElement)) { |
409 // Apply patch. | 335 // Apply patch. |
410 element.addMetadata(popMetadata()); | 336 patch.addMetadata(popMetadata()); |
411 LibraryElement originLibrary = compilationUnitElement.getLibrary(); | 337 LibraryElement originLibrary = compilationUnitElement.getLibrary(); |
412 assert(originLibrary.isPatched); | 338 assert(originLibrary.isPatched); |
413 Element existing = originLibrary.localLookup(element.name); | 339 Element origin = originLibrary.localLookup(patch.name); |
414 if (isMemberPatch) { | 340 _patchElement(listener, origin, patch); |
415 if (element is! FunctionElement) { | |
416 listener.internalErrorOnElement(element, | |
417 "Member patch is not a function."); | |
418 } | |
419 FunctionElement functionElement = element; | |
420 if (identical(existing.kind, ElementKind.ABSTRACT_FIELD)) { | |
421 if (!element.isAccessor()) { | |
422 listener.internalErrorOnElement( | |
423 functionElement, "Patching non-accessor with accessor"); | |
424 } | |
425 AbstractFieldElement field = existing; | |
426 if (functionElement.isGetter()) { | |
427 existing = field.getter; | |
428 } else { | |
429 existing = field.setter; | |
430 } | |
431 } | |
432 if (existing is! FunctionElement) { | |
433 listener.internalErrorOnElement(functionElement, | |
434 "No corresponding method for patch."); | |
435 } | |
436 FunctionElement existingFunction = existing; | |
437 if (existingFunction.isPatched) { | |
438 listener.internalErrorOnElement( | |
439 functionElement, "Patching the same function more than once."); | |
440 } | |
441 existingFunction.patch = functionElement; | |
442 functionElement.origin = existingFunction; | |
443 } else { | |
444 assert(leg.invariant(element, element is ClassElement)); | |
445 ClassElement classElement = element; | |
446 if (existing is! ClassElement) { | |
447 listener.internalErrorOnElement( | |
448 classElement, "Patching a non-class with a class patch."); | |
449 } | |
450 ClassElement existingClass = existing; | |
451 if (existingClass.isPatched) { | |
452 listener.internalErrorOnElement( | |
453 classElement, "Patching the same class more than once."); | |
454 } | |
455 existingClass.patch = classElement; | |
456 classElement.origin = existingClass; | |
457 } | |
458 } | 341 } |
459 super.pushElement(element); | 342 super.pushElement(patch); |
460 } | 343 } |
461 } | 344 } |
462 | 345 |
463 /** | 346 /** |
464 * Extension of [MemberListener] for parsing patch class bodies. | 347 * Extension of [MemberListener] for parsing patch class bodies. |
465 */ | 348 */ |
466 class PatchMemberListener extends MemberListener implements PatchListener { | 349 class PatchMemberListener extends MemberListener implements PatchListener { |
467 bool isMemberPatch = false; | 350 bool isMemberPatch = false; |
468 bool isClassPatch = false; | 351 bool isClassPatch = false; |
469 PatchMemberListener(leg.DiagnosticListener listener, | 352 PatchMemberListener(leg.DiagnosticListener listener, |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
502 } | 385 } |
503 | 386 |
504 // TODO(ahe): Get rid of this class. | 387 // TODO(ahe): Get rid of this class. |
505 class PatchMetadataAnnotation extends MetadataAnnotationX { | 388 class PatchMetadataAnnotation extends MetadataAnnotationX { |
506 final leg.Constant value = null; | 389 final leg.Constant value = null; |
507 | 390 |
508 PatchMetadataAnnotation() : super(STATE_DONE); | 391 PatchMetadataAnnotation() : super(STATE_DONE); |
509 | 392 |
510 Token get beginToken => null; | 393 Token get beginToken => null; |
511 } | 394 } |
395 | |
396 void _patchElement(leg.DiagnosticListener listener, | |
397 Element origin, | |
398 Element patch) { | |
399 if (origin == null) { | |
400 listener.reportMessage( | |
401 listener.spanFromSpannable(patch), | |
402 leg.MessageKind.PATCH_NON_EXISTING.error({'name': patch.name}), | |
403 api.Diagnostic.ERROR); | |
404 return; | |
405 } | |
406 if (!(patch.isClass() || | |
407 patch.isConstructor() || | |
408 patch.isFunction() || | |
409 patch.isAccessor())) { | |
410 listener.reportMessage( | |
411 listener.spanFromSpannable(patch), | |
412 leg.MessageKind.PATCH_NONPATCHABLE.error(), | |
413 api.Diagnostic.ERROR); | |
414 return; | |
415 } | |
416 if (!(origin.isClass() || | |
417 origin.isConstructor() || | |
418 origin.isFunction() || | |
419 origin.isAbstractField())) { | |
420 listener.reportMessage( | |
421 listener.spanFromSpannable(origin), | |
422 leg.MessageKind.PATCH_NONPATCHABLE.error(), | |
423 api.Diagnostic.ERROR); | |
424 return; | |
425 } | |
426 if (patch.isClass()) { | |
427 _tryPatchClass(listener, origin, patch); | |
428 } else if (patch.isGetter()) { | |
429 _tryPatchGetter(listener, origin, patch); | |
430 } else if (patch.isSetter()) { | |
431 _tryPatchSetter(listener, origin, patch); | |
432 } else if (patch.isConstructor()) { | |
433 _tryPatchConstructor(listener, origin, patch); | |
434 } else { | |
435 assert(patch.isFunction()); | |
436 _tryPatchFunction(listener, origin, patch); | |
437 } | |
438 } | |
439 | |
440 void _tryPatchClass(leg.DiagnosticListener listener, | |
441 Element origin, | |
442 ClassElement patch) { | |
443 if (!origin.isClass()) { | |
444 listener.reportMessage( | |
445 listener.spanFromSpannable(origin), | |
446 leg.MessageKind.PATCH_NON_CLASS.error({'className': patch.name}), | |
447 api.Diagnostic.ERROR); | |
448 listener.reportMessage( | |
449 listener.spanFromSpannable(patch), | |
450 leg.MessageKind.PATCH_POINT_TO_CLASS.error({'className': patch.name}), | |
451 api.Diagnostic.INFO); | |
452 return; | |
453 } | |
454 _patchClass(listener, origin, patch); | |
455 } | |
456 | |
457 void _patchClass(leg.DiagnosticListener listener, | |
458 ClassElement origin, | |
459 ClassElement patch) { | |
460 if (origin.isPatched) { | |
461 listener.internalErrorOnElement( | |
462 origin, "Patching the same class more than once."); | |
463 } | |
464 origin.patch = patch; | |
465 patch.origin = origin; | |
466 } | |
467 | |
468 void _tryPatchGetter(leg.DiagnosticListener listener, | |
469 Element origin, | |
470 FunctionElement patch) { | |
471 if (!origin.isAbstractField()) { | |
472 listener.reportMessage( | |
473 listener.spanFromSpannable(origin), | |
474 leg.MessageKind.PATCH_NON_GETTER.error({'name': origin.name}), | |
475 api.Diagnostic.ERROR); | |
476 listener.reportMessage( | |
477 listener.spanFromSpannable(patch), | |
478 leg.MessageKind.PATCH_POINT_TO_GETTER.error({'getterName': patch.name}), | |
479 api.Diagnostic.INFO); | |
480 return; | |
481 } | |
482 AbstractFieldElement originField = origin; | |
483 if (originField.getter == null) { | |
484 listener.reportMessage( | |
485 listener.spanFromSpannable(origin), | |
486 leg.MessageKind.PATCH_NO_GETTER.error({'getterName': patch.name}), | |
487 api.Diagnostic.ERROR); | |
488 listener.reportMessage( | |
489 listener.spanFromSpannable(patch), | |
490 leg.MessageKind.PATCH_POINT_TO_GETTER.error({'getterName': patch.name}), | |
491 api.Diagnostic.INFO); | |
492 return; | |
493 } | |
494 _patchFunction(listener, originField.getter, patch); | |
495 } | |
496 | |
497 void _tryPatchSetter(leg.DiagnosticListener listener, | |
498 Element origin, | |
499 FunctionElement patch) { | |
500 if (!origin.isAbstractField()) { | |
501 listener.reportMessage( | |
502 listener.spanFromSpannable(origin), | |
503 leg.MessageKind.PATCH_NON_SETTER.error({'name': origin.name}), | |
504 api.Diagnostic.ERROR); | |
505 listener.reportMessage( | |
506 listener.spanFromSpannable(patch), | |
507 leg.MessageKind.PATCH_POINT_TO_SETTER.error({'setterName': patch.name}), | |
508 api.Diagnostic.INFO); | |
509 return; | |
510 } | |
511 AbstractFieldElement originField = origin; | |
512 if (originField.setter == null) { | |
513 listener.reportMessage( | |
514 listener.spanFromSpannable(origin), | |
515 leg.MessageKind.PATCH_NO_SETTER.error({'setterName': patch.name}), | |
516 api.Diagnostic.ERROR); | |
517 listener.reportMessage( | |
518 listener.spanFromSpannable(patch), | |
519 leg.MessageKind.PATCH_POINT_TO_SETTER.error({'setterName': patch.name}), | |
520 api.Diagnostic.INFO); | |
521 return; | |
522 } | |
523 _patchFunction(listener, originField.setter, patch); | |
524 } | |
525 | |
526 void _tryPatchConstructor(leg.DiagnosticListener listener, | |
527 Element origin, | |
528 FunctionElement patch) { | |
529 if (!origin.isConstructor()) { | |
530 listener.reportMessage( | |
531 listener.spanFromSpannable(origin), | |
532 leg.MessageKind.PATCH_NON_CONSTRUCTOR.error( | |
533 {'constructorName': patch.name}), | |
534 api.Diagnostic.ERROR); | |
535 listener.reportMessage( | |
536 listener.spanFromSpannable(patch), | |
537 leg.MessageKind.PATCH_POINT_TO_CONSTRUCTOR.error( | |
538 {'constructorName': patch.name}), | |
539 api.Diagnostic.INFO); | |
540 return; | |
541 } | |
542 _patchFunction(listener, origin, patch); | |
543 } | |
544 | |
545 void _tryPatchFunction(leg.DiagnosticListener listener, | |
546 Element origin, | |
547 FunctionElement patch) { | |
548 if (!origin.isFunction()) { | |
549 listener.reportMessage( | |
550 listener.spanFromSpannable(origin), | |
551 leg.MessageKind.PATCH_NON_FUNCTION.error({'functionName': patch.name}), | |
552 api.Diagnostic.ERROR); | |
553 listener.reportMessage( | |
554 listener.spanFromSpannable(patch), | |
555 leg.MessageKind.PATCH_POINT_TO_FUNCTION.error( | |
556 {'functionName': patch.name}), | |
557 api.Diagnostic.INFO); | |
558 return; | |
559 } | |
560 _patchFunction(listener, origin, patch); | |
561 } | |
562 | |
563 void _patchFunction(leg.DiagnosticListener listener, | |
564 FunctionElement origin, | |
565 FunctionElement patch) { | |
566 if (!origin.modifiers.isExternal()) { | |
567 listener.reportMessage( | |
568 listener.spanFromSpannable(origin), | |
569 leg.MessageKind.PATCH_NON_EXTERNAL.error(), | |
570 api.Diagnostic.ERROR); | |
571 listener.reportMessage( | |
572 listener.spanFromSpannable(patch), | |
573 leg.MessageKind.PATCH_POINT_TO_FUNCTION.error( | |
574 {'functionName': patch.name}), | |
575 api.Diagnostic.INFO); | |
576 return; | |
577 } | |
578 if (origin.isPatched) { | |
579 listener.internalErrorOnElement(origin, | |
580 "Trying to patch a function more than once."); | |
581 } | |
582 if (origin.cachedNode != null) { | |
583 listener.internalErrorOnElement(origin, | |
584 "Trying to patch an already compiled function."); | |
585 } | |
586 // Don't just assign the patch field. This also updates the cachedNode. | |
587 origin.setPatch(patch); | |
588 patch.origin = origin; | |
589 } | |
590 | |
591 bool _isPatchElement(Element element) { | |
592 // TODO(lrn): More checks needed if we introduce metadata for real. | |
ngeoffray
2013/02/04 09:03:47
I think you can remove TODO or file a bug to repla
| |
593 // In that case, it must have the identifier "native" as metadata. | |
594 for (Link link = element.metadata; !link.isEmpty; link = link.tail) { | |
595 if (link.head is PatchMetadataAnnotation) return true; | |
596 } | |
597 return false; | |
598 } | |
OLD | NEW |