Chromium Code Reviews| 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 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } | |
| OLD | NEW |