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

Side by Side Diff: pkg/compiler/lib/src/patch_parser.dart

Issue 886773004: Supported versioned patching. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 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 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
128 show LibraryElementX, 128 show LibraryElementX,
129 MetadataAnnotationX, 129 MetadataAnnotationX,
130 ClassElementX, 130 ClassElementX,
131 FunctionElementX; 131 FunctionElementX;
132 import 'helpers/helpers.dart'; // Included for debug helpers. 132 import 'helpers/helpers.dart'; // Included for debug helpers.
133 import 'library_loader.dart' show LibraryLoader; 133 import 'library_loader.dart' show LibraryLoader;
134 import 'scanner/scannerlib.dart'; // Scanner, Parsers, Listeners 134 import 'scanner/scannerlib.dart'; // Scanner, Parsers, Listeners
135 import 'util/util.dart'; 135 import 'util/util.dart';
136 136
137 class PatchParserTask extends CompilerTask { 137 class PatchParserTask extends CompilerTask {
138 final String name = "Patching Parser";
139
138 PatchParserTask(Compiler compiler): super(compiler); 140 PatchParserTask(Compiler compiler): super(compiler);
139 final String name = "Patching Parser";
140 141
141 /** 142 /**
142 * Scans a library patch file, applies the method patches and 143 * Scans a library patch file, applies the method patches and
143 * injections to the library, and returns a list of class 144 * injections to the library, and returns a list of class
144 * patches. 145 * patches.
145 */ 146 */
146 Future patchLibrary(LibraryLoader loader, 147 Future patchLibrary(LibraryLoader loader,
147 Uri patchUri, LibraryElement originLibrary) { 148 Uri patchUri, LibraryElement originLibrary) {
148 return compiler.readScript(originLibrary, patchUri) 149 return compiler.readScript(originLibrary, patchUri)
149 .then((Script script) { 150 .then((Script script) {
(...skipping 24 matching lines...) Expand all
174 new PartialParser(patchListener).parseUnit(tokens); 175 new PartialParser(patchListener).parseUnit(tokens);
175 } on ParserError catch (e) { 176 } on ParserError catch (e) {
176 // No need to recover from a parser error in platform libraries, user 177 // No need to recover from a parser error in platform libraries, user
177 // will never see this if the libraries are tested correctly. 178 // will never see this if the libraries are tested correctly.
178 compiler.internalError( 179 compiler.internalError(
179 compilationUnit, "Parser error in patch file: $e"); 180 compilationUnit, "Parser error in patch file: $e");
180 } 181 }
181 }); 182 });
182 } 183 }
183 184
184 void parsePatchClassNode(PartialClassElement element) { 185 void parsePatchClassNode(PartialClassElement cls) {
185 // Parse [PartialClassElement] using a "patch"-aware parser instead 186 // Parse [PartialClassElement] using a "patch"-aware parser instead
186 // of calling its [parseNode] method. 187 // of calling its [parseNode] method.
187 if (element.cachedNode != null) return; 188 if (cls.cachedNode != null) return;
188 189
189 measure(() => compiler.withCurrentElement(element, () { 190 measure(() => compiler.withCurrentElement(cls, () {
190 MemberListener listener = new MemberListener(compiler, element); 191 MemberListener listener = new PatchMemberListener(compiler, cls);
191 Parser parser = new PatchClassElementParser(listener); 192 Parser parser = new PatchClassElementParser(listener);
192 try { 193 try {
193 Token token = parser.parseTopLevelDeclaration(element.beginToken); 194 Token token = parser.parseTopLevelDeclaration(cls.beginToken);
194 assert(identical(token, element.endToken.next)); 195 assert(identical(token, cls.endToken.next));
195 } on ParserError catch (e) { 196 } on ParserError catch (e, s) {
196 // No need to recover from a parser error in platform libraries, user 197 // No need to recover from a parser error in platform libraries, user
197 // will never see this if the libraries are tested correctly. 198 // will never see this if the libraries are tested correctly.
198 compiler.internalError( 199 compiler.internalError(
199 element, "Parser error in patch file: $e"); 200 cls, "Parser error in patch file: $e");
200 } 201 }
201 element.cachedNode = listener.popNode(); 202 cls.cachedNode = listener.popNode();
202 assert(listener.nodes.isEmpty); 203 assert(listener.nodes.isEmpty);
203
204 Link<Element> patches = element.localMembers;
205 applyContainerPatch(element.origin, patches);
206 })); 204 }));
207 } 205 }
206 }
208 207
209 void applyContainerPatch(ClassElement originClass, 208 class PatchMemberListener extends MemberListener {
210 Link<Element> patches) { 209 final Compiler compiler;
211 for (Element patch in patches) {
212 if (!isPatchElement(compiler, patch)) continue;
213 210
214 Element origin = originClass.localLookup(patch.name); 211 PatchMemberListener(Compiler compiler, ClassElement enclosingClass)
215 patchElement(compiler, origin, patch); 212 : this.compiler = compiler,
213 super(compiler, enclosingClass);
214
215 @override
216 void addMember(Element patch) {
217 addMetadata(patch);
218
219 PatchVersion patchVersion = getPatchVersion(compiler, patch);
220 if (patchVersion != null) {
221 if (patchVersion.isActive(compiler.patchVersion)) {
222 Element origin = enclosingClass.origin.localLookup(patch.name);
223 patchElement(compiler, origin, patch);
224 enclosingClass.addMember(patch, listener);
225 } else {
226 // Skip this element.
227 }
228 } else {
229 enclosingClass.addMember(patch, listener);
216 } 230 }
217 } 231 }
218 } 232 }
219 233
220 /** 234 /**
221 * Partial parser for patch files that also handles the members of class 235 * Partial parser for patch files that also handles the members of class
222 * declarations. 236 * declarations.
223 */ 237 */
224 class PatchClassElementParser extends PartialParser { 238 class PatchClassElementParser extends PartialParser {
225 PatchClassElementParser(Listener listener) : super(listener); 239 PatchClassElementParser(Listener listener) : super(listener);
226 240
227 Token parseClassBody(Token token) => fullParseClassBody(token); 241 Token parseClassBody(Token token) => fullParseClassBody(token);
228 } 242 }
229 243
230 /** 244 /**
231 * Extension of [ElementListener] for parsing patch files. 245 * Extension of [ElementListener] for parsing patch files.
232 */ 246 */
233 class PatchElementListener extends ElementListener implements Listener { 247 class PatchElementListener extends ElementListener implements Listener {
234 final Compiler compiler; 248 final Compiler compiler;
235 249
236 PatchElementListener(Compiler compiler, 250 PatchElementListener(Compiler compiler,
237 CompilationUnitElement patchElement, 251 CompilationUnitElement patchElement,
238 int idGenerator()) 252 int idGenerator())
239 : this.compiler = compiler, 253 : this.compiler = compiler,
240 super(compiler, patchElement, idGenerator); 254 super(compiler, patchElement, idGenerator);
241 255
256 @override
242 void pushElement(Element patch) { 257 void pushElement(Element patch) {
243 super.pushElement(patch); 258 popMetadata(patch);
244 if (isPatchElement(compiler, patch)) { 259
245 LibraryElement originLibrary = compilationUnitElement.library; 260 PatchVersion patchVersion = getPatchVersion(compiler, patch);
246 assert(originLibrary.isPatched); 261 if (patchVersion != null) {
247 Element origin = originLibrary.localLookup(patch.name); 262 if (patchVersion.isActive(compiler.patchVersion)) {
248 patchElement(listener, origin, patch); 263 LibraryElement originLibrary = compilationUnitElement.library;
264 assert(originLibrary.isPatched);
265 Element origin = originLibrary.localLookup(patch.name);
266 patchElement(listener, origin, patch);
267 compilationUnitElement.addMember(patch, listener);
268 } else {
269 // Skip this element.
270 }
271 } else {
272 compilationUnitElement.addMember(patch, listener);
249 } 273 }
250 } 274 }
251 } 275 }
252 276
253 void patchElement(Compiler compiler, 277 void patchElement(Compiler compiler,
254 Element origin, 278 Element origin,
255 Element patch) { 279 Element patch) {
256 if (origin == null) { 280 if (origin == null) {
257 compiler.reportError( 281 compiler.reportError(
258 patch, MessageKind.PATCH_NON_EXISTING, {'name': patch.name}); 282 patch, MessageKind.PATCH_NON_EXISTING, {'name': patch.name});
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
313 const NativeAnnotationHandler()); 337 const NativeAnnotationHandler());
314 } 338 }
315 339
316 /// Abstract interface for pre-resolution detection of metadata. 340 /// Abstract interface for pre-resolution detection of metadata.
317 /// 341 ///
318 /// The detection is handled in two steps: 342 /// The detection is handled in two steps:
319 /// - match the annotation syntactically and assume that the annotation is valid 343 /// - match the annotation syntactically and assume that the annotation is valid
320 /// if it looks correct, 344 /// if it looks correct,
321 /// - setup a deferred action to check that the annotation has a valid constant 345 /// - setup a deferred action to check that the annotation has a valid constant
322 /// value and report an internal error if not. 346 /// value and report an internal error if not.
323 abstract class EagerAnnotationHandler { 347 abstract class EagerAnnotationHandler<T> {
324 /// Checks that [annotation] looks like a matching annotation and optionally 348 /// Checks that [annotation] looks like a matching annotation and optionally
325 /// applies actions on [element]. Returns `true` if the annotation matched. 349 /// applies actions on [element]. Returns a non-null annotation marker if the
326 bool apply(Compiler compiler, 350 /// annotation matched and should be validated.
327 Element element, 351 T apply(Compiler compiler,
328 MetadataAnnotation annotation); 352 Element element,
353 MetadataAnnotation annotation);
329 354
330 /// Checks that the annotation value is valid. 355 /// Checks that the annotation value is valid.
331 void validate(Compiler compiler, 356 void validate(Compiler compiler,
332 Element element, 357 Element element,
333 MetadataAnnotation annotation, 358 MetadataAnnotation annotation,
334 ConstantValue constant); 359 ConstantValue constant);
335 360
336 361
337 /// Checks [element] for metadata matching the [handler]. Return `true` if 362 /// Checks [element] for metadata matching the [handler]. Return a non-null
338 /// matching metadata was found. 363 /// annotation marker matching metadata was found.
339 static bool checkAnnotation(Compiler compiler, 364 static checkAnnotation(Compiler compiler,
340 Element element, 365 Element element,
341 EagerAnnotationHandler handler) { 366 EagerAnnotationHandler handler) {
342 for (Link<MetadataAnnotation> link = element.metadata; 367 for (Link<MetadataAnnotation> link = element.metadata;
343 !link.isEmpty; 368 !link.isEmpty;
344 link = link.tail) { 369 link = link.tail) {
345 MetadataAnnotation annotation = link.head; 370 MetadataAnnotation annotation = link.head;
346 if (handler.apply(compiler, element, annotation)) { 371 var result = handler.apply(compiler, element, annotation);
372 if (result != null) {
347 // TODO(johnniwinther): Perform this check in 373 // TODO(johnniwinther): Perform this check in
348 // [Compiler.onLibrariesLoaded]. 374 // [Compiler.onLibrariesLoaded].
349 compiler.enqueuer.resolution.addDeferredAction(element, () { 375 compiler.enqueuer.resolution.addDeferredAction(element, () {
350 annotation.ensureResolved(compiler); 376 annotation.ensureResolved(compiler);
351 handler.validate( 377 handler.validate(
352 compiler, element, annotation, annotation.constant.value); 378 compiler, element, annotation, annotation.constant.value);
353 }); 379 });
354 return true; 380 return result;
355 } 381 }
356 } 382 }
357 return false; 383 return null;
358 } 384 }
359 } 385 }
360 386
361 /// Annotation handler for pre-resolution detection of `@Native(...)` 387 /// Annotation handler for pre-resolution detection of `@Native(...)`
362 /// annotations. 388 /// annotations.
363 class NativeAnnotationHandler implements EagerAnnotationHandler { 389 class NativeAnnotationHandler implements EagerAnnotationHandler<String> {
364 const NativeAnnotationHandler(); 390 const NativeAnnotationHandler();
365 391
366 String getNativeAnnotation(MetadataAnnotation annotation) { 392 String getNativeAnnotation(MetadataAnnotation annotation) {
367 if (annotation.beginToken != null && 393 if (annotation.beginToken != null &&
368 annotation.beginToken.next.value == 'Native') { 394 annotation.beginToken.next.value == 'Native') {
369 // Skipping '@', 'Native', and '('. 395 // Skipping '@', 'Native', and '('.
370 Token argument = annotation.beginToken.next.next.next; 396 Token argument = annotation.beginToken.next.next.next;
371 if (argument is StringToken) { 397 if (argument is StringToken) {
372 return argument.value; 398 return argument.value;
373 } 399 }
374 } 400 }
375 return null; 401 return null;
376 } 402 }
377 403
378 bool apply(Compiler compiler, 404 String apply(Compiler compiler,
379 Element element, 405 Element element,
380 MetadataAnnotation annotation) { 406 MetadataAnnotation annotation) {
381 if (element.isClass) { 407 if (element.isClass) {
382 String native = getNativeAnnotation(annotation); 408 String native = getNativeAnnotation(annotation);
383 if (native != null) { 409 if (native != null) {
384 ClassElementX declaration = element.declaration; 410 ClassElementX declaration = element.declaration;
385 declaration.setNative(native); 411 declaration.setNative(native);
386 return true; 412 return native;
387 } 413 }
388 } 414 }
389 return false; 415 return null;
390 } 416 }
391 417
392 void validate(Compiler compiler, 418 void validate(Compiler compiler,
393 Element element, 419 Element element,
394 MetadataAnnotation annotation, 420 MetadataAnnotation annotation,
395 ConstantValue constant) { 421 ConstantValue constant) {
396 if (constant.getType(compiler.coreTypes).element != 422 if (constant.getType(compiler.coreTypes).element !=
397 compiler.nativeAnnotationClass) { 423 compiler.nativeAnnotationClass) {
398 compiler.internalError(annotation, 'Invalid @Native(...) annotation.'); 424 compiler.internalError(annotation, 'Invalid @Native(...) annotation.');
399 } 425 }
400 } 426 }
401 } 427 }
402 428
403 /// Annotation handler for pre-resolution detection of `@patch` annotations. 429 /// Annotation handler for pre-resolution detection of `@patch` annotations.
404 class PatchAnnotationHandler implements EagerAnnotationHandler { 430 class PatchAnnotationHandler implements EagerAnnotationHandler<PatchVersion> {
405 const PatchAnnotationHandler(); 431 const PatchAnnotationHandler();
406 432
407 bool isPatchAnnotation(MetadataAnnotation annotation) { 433 PatchVersion getPatchVersion(MetadataAnnotation annotation) {
408 return annotation.beginToken != null && 434 if (annotation.beginToken != null) {
409 annotation.beginToken.next.value == 'patch'; 435 if (annotation.beginToken.next.value == 'patch') {
436 return const PatchVersion(null);
437 } else if (annotation.beginToken.next.value == 'patch_old') {
438 return const PatchVersion('old');
439 } else if (annotation.beginToken.next.value == 'patch_new') {
440 return const PatchVersion('new');
441 }
442 }
443 return null;
410 } 444 }
411 445
412 bool apply(Compiler compiler, 446 @override
413 Element element, 447 PatchVersion apply(Compiler compiler,
414 MetadataAnnotation annotation) { 448 Element element,
415 return isPatchAnnotation(annotation); 449 MetadataAnnotation annotation) {
450 return getPatchVersion(annotation);
416 } 451 }
417 452
453 @override
418 void validate(Compiler compiler, 454 void validate(Compiler compiler,
419 Element element, 455 Element element,
420 MetadataAnnotation annotation, 456 MetadataAnnotation annotation,
421 ConstantValue constant) { 457 ConstantValue constant) {
422 if (constant != compiler.patchConstant) { 458 if (constant.getType(compiler.coreTypes).element !=
459 compiler.patchAnnotationClass) {
423 compiler.internalError(annotation, 'Invalid patch annotation.'); 460 compiler.internalError(annotation, 'Invalid patch annotation.');
424 } 461 }
425 } 462 }
426 } 463 }
427 464
428 465
429 void tryPatchGetter(DiagnosticListener listener, 466 void tryPatchGetter(DiagnosticListener listener,
430 Element origin, 467 Element origin,
431 FunctionElement patch) { 468 FunctionElement patch) {
432 if (!origin.isAbstractField) { 469 if (!origin.isAbstractField) {
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
513 MessageKind.PATCH_POINT_TO_FUNCTION, {'functionName': patch.name}); 550 MessageKind.PATCH_POINT_TO_FUNCTION, {'functionName': patch.name});
514 return; 551 return;
515 } 552 }
516 if (origin.isPatched) { 553 if (origin.isPatched) {
517 listener.internalError(origin, 554 listener.internalError(origin,
518 "Trying to patch a function more than once."); 555 "Trying to patch a function more than once.");
519 } 556 }
520 origin.applyPatch(patch); 557 origin.applyPatch(patch);
521 } 558 }
522 559
523 // TODO(johnniwinther): Add unittest when patch is (real) metadata. 560 PatchVersion getPatchVersion(Compiler compiler, Element element) {
524 bool isPatchElement(Compiler compiler, Element element) {
525 return EagerAnnotationHandler.checkAnnotation(compiler, element, 561 return EagerAnnotationHandler.checkAnnotation(compiler, element,
526 const PatchAnnotationHandler()); 562 const PatchAnnotationHandler());
527 } 563 }
564
565 class PatchVersion {
566 final String tag;
567
568 const PatchVersion(this.tag);
569
570 bool isActive(String patchTag) => tag == null || tag == patchTag;
571
572 String toString() => 'PatchVersion($tag)';
573 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698