Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(491)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/patch_parser.dart

Issue 11864010: Improve checking of patches. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rebased Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698