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

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 (again) 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
« no previous file with comments | « no previous file | sdk/lib/_internal/compiler/implementation/resolution/members.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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(popMetadataHack()); 336 patch.addMetadata(popMetadataHack());
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 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 Token get endToken => null; 394 Token get endToken => null;
512 } 395 }
396
397 void patchElement(leg.DiagnosticListener listener,
398 Element origin,
399 Element patch) {
400 if (origin == null) {
401 listener.reportMessage(
402 listener.spanFromSpannable(patch),
403 leg.MessageKind.PATCH_NON_EXISTING.error({'name': patch.name}),
404 api.Diagnostic.ERROR);
405 return;
406 }
407 if (!(origin.isClass() ||
408 origin.isConstructor() ||
409 origin.isFunction() ||
410 origin.isAbstractField())) {
411 listener.reportMessage(
412 listener.spanFromSpannable(origin),
413 leg.MessageKind.PATCH_NONPATCHABLE.error(),
414 api.Diagnostic.ERROR);
415 return;
416 }
417 if (patch.isClass()) {
418 tryPatchClass(listener, origin, patch);
419 } else if (patch.isGetter()) {
420 tryPatchGetter(listener, origin, patch);
421 } else if (patch.isSetter()) {
422 tryPatchSetter(listener, origin, patch);
423 } else if (patch.isConstructor()) {
424 tryPatchConstructor(listener, origin, patch);
425 } else if(patch.isFunction()) {
426 tryPatchFunction(listener, origin, patch);
427 } else {
428 listener.reportMessage(
429 listener.spanFromSpannable(patch),
430 leg.MessageKind.PATCH_NONPATCHABLE.error(),
431 api.Diagnostic.ERROR);
432 }
433 }
434
435 void tryPatchClass(leg.DiagnosticListener listener,
436 Element origin,
437 ClassElement patch) {
438 if (!origin.isClass()) {
439 listener.reportMessage(
440 listener.spanFromSpannable(origin),
441 leg.MessageKind.PATCH_NON_CLASS.error({'className': patch.name}),
442 api.Diagnostic.ERROR);
443 listener.reportMessage(
444 listener.spanFromSpannable(patch),
445 leg.MessageKind.PATCH_POINT_TO_CLASS.error({'className': patch.name}),
446 api.Diagnostic.INFO);
447 return;
448 }
449 patchClass(listener, origin, patch);
450 }
451
452 void patchClass(leg.DiagnosticListener listener,
453 ClassElement origin,
454 ClassElement patch) {
455 if (origin.isPatched) {
456 listener.internalErrorOnElement(
457 origin, "Patching the same class more than once.");
458 }
459 // TODO(johnniwinther): Change to functions on the ElementX class.
460 origin.patch = patch;
461 patch.origin = origin;
462 }
463
464 void tryPatchGetter(leg.DiagnosticListener listener,
465 Element origin,
466 FunctionElement patch) {
467 if (!origin.isAbstractField()) {
468 listener.reportMessage(
469 listener.spanFromSpannable(origin),
470 leg.MessageKind.PATCH_NON_GETTER.error({'name': origin.name}),
471 api.Diagnostic.ERROR);
472 listener.reportMessage(
473 listener.spanFromSpannable(patch),
474 leg.MessageKind.PATCH_POINT_TO_GETTER.error({'getterName': patch.name}),
475 api.Diagnostic.INFO);
476 return;
477 }
478 AbstractFieldElement originField = origin;
479 if (originField.getter == null) {
480 listener.reportMessage(
481 listener.spanFromSpannable(origin),
482 leg.MessageKind.PATCH_NO_GETTER.error({'getterName': patch.name}),
483 api.Diagnostic.ERROR);
484 listener.reportMessage(
485 listener.spanFromSpannable(patch),
486 leg.MessageKind.PATCH_POINT_TO_GETTER.error({'getterName': patch.name}),
487 api.Diagnostic.INFO);
488 return;
489 }
490 patchFunction(listener, originField.getter, patch);
491 }
492
493 void tryPatchSetter(leg.DiagnosticListener listener,
494 Element origin,
495 FunctionElement patch) {
496 if (!origin.isAbstractField()) {
497 listener.reportMessage(
498 listener.spanFromSpannable(origin),
499 leg.MessageKind.PATCH_NON_SETTER.error({'name': origin.name}),
500 api.Diagnostic.ERROR);
501 listener.reportMessage(
502 listener.spanFromSpannable(patch),
503 leg.MessageKind.PATCH_POINT_TO_SETTER.error({'setterName': patch.name}),
504 api.Diagnostic.INFO);
505 return;
506 }
507 AbstractFieldElement originField = origin;
508 if (originField.setter == null) {
509 listener.reportMessage(
510 listener.spanFromSpannable(origin),
511 leg.MessageKind.PATCH_NO_SETTER.error({'setterName': patch.name}),
512 api.Diagnostic.ERROR);
513 listener.reportMessage(
514 listener.spanFromSpannable(patch),
515 leg.MessageKind.PATCH_POINT_TO_SETTER.error({'setterName': patch.name}),
516 api.Diagnostic.INFO);
517 return;
518 }
519 patchFunction(listener, originField.setter, patch);
520 }
521
522 void tryPatchConstructor(leg.DiagnosticListener listener,
523 Element origin,
524 FunctionElement patch) {
525 if (!origin.isConstructor()) {
526 listener.reportMessage(
527 listener.spanFromSpannable(origin),
528 leg.MessageKind.PATCH_NON_CONSTRUCTOR.error(
529 {'constructorName': patch.name}),
530 api.Diagnostic.ERROR);
531 listener.reportMessage(
532 listener.spanFromSpannable(patch),
533 leg.MessageKind.PATCH_POINT_TO_CONSTRUCTOR.error(
534 {'constructorName': patch.name}),
535 api.Diagnostic.INFO);
536 return;
537 }
538 patchFunction(listener, origin, patch);
539 }
540
541 void tryPatchFunction(leg.DiagnosticListener listener,
542 Element origin,
543 FunctionElement patch) {
544 if (!origin.isFunction()) {
545 listener.reportMessage(
546 listener.spanFromSpannable(origin),
547 leg.MessageKind.PATCH_NON_FUNCTION.error({'functionName': patch.name}),
548 api.Diagnostic.ERROR);
549 listener.reportMessage(
550 listener.spanFromSpannable(patch),
551 leg.MessageKind.PATCH_POINT_TO_FUNCTION.error(
552 {'functionName': patch.name}),
553 api.Diagnostic.INFO);
554 return;
555 }
556 patchFunction(listener, origin, patch);
557 }
558
559 void patchFunction(leg.DiagnosticListener listener,
560 FunctionElement origin,
561 FunctionElement patch) {
562 if (!origin.modifiers.isExternal()) {
563 listener.reportMessage(
564 listener.spanFromSpannable(origin),
565 leg.MessageKind.PATCH_NON_EXTERNAL.error(),
566 api.Diagnostic.ERROR);
567 listener.reportMessage(
568 listener.spanFromSpannable(patch),
569 leg.MessageKind.PATCH_POINT_TO_FUNCTION.error(
570 {'functionName': patch.name}),
571 api.Diagnostic.INFO);
572 return;
573 }
574 if (origin.isPatched) {
575 listener.internalErrorOnElement(origin,
576 "Trying to patch a function more than once.");
577 }
578 if (origin.cachedNode != null) {
579 listener.internalErrorOnElement(origin,
580 "Trying to patch an already compiled function.");
581 }
582 // Don't just assign the patch field. This also updates the cachedNode.
583 // TODO(johnniwinther): Change to functions on the ElementX class.
584 origin.setPatch(patch);
585 patch.origin = origin;
586 }
587
588 // TODO(johnniwinther): Add unittest when patch is (real) metadata.
589 bool isPatchElement(Element element) {
590 // TODO(lrn): More checks needed if we introduce metadata for real.
591 // In that case, it must have the identifier "native" as metadata.
592 for (Link link = element.metadata; !link.isEmpty; link = link.tail) {
593 if (link.head is PatchMetadataAnnotation) return true;
594 }
595 return false;
596 }
OLDNEW
« no previous file with comments | « no previous file | sdk/lib/_internal/compiler/implementation/resolution/members.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698