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

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

Powered by Google App Engine
This is Rietveld 408576698