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

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

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
6 * This library contains the infrastructure to parse and integrate patch files.
7 *
8 * Three types of elements can be patched: [LibraryElement], [ClassElement],
9 * [FunctionElement]. Patches are introduced in patch libraries which are loaded
10 * together with the corresponding origin library. Which libraries that are
11 * patched is determined by the dart2jsPatchPath field of LibraryInfo found
12 * in [:lib/_internal/libraries.dart:].
13 *
14 * Patch libraries are parsed like regular library and thus provided with their
15 * own elements. These elements which are distinct from the elements from the
16 * patched library and the relation between patched and patch elements is
17 * established through the [:patch:] and [:origin:] fields found on
18 * [LibraryElement], [ClassElement] and [FunctionElement]. The [:patch:] fields
19 * are set on the patched elements to point to their corresponding patch
20 * element, and the [:origin:] elements are set on the patch elements to point
21 * their corresponding patched elements.
22 *
23 * The fields [Element.isPatched] and [Element.isPatch] can be used to determine
24 * whether the [:patch:] or [:origin:] field, respectively, has been set on an
25 * element, regardless of whether the element is one of the three patchable
26 * element types or not.
27 *
28 * ## Variants of classes and functions ##
29 *
30 * With patches there are four variants of classes and function:
31 *
32 * Regular: A class or function which is not declared in a patch library and
33 * which has no corresponding patch.
34 * Origin: A class or function which is not declared in a patch library and
35 * which has a corresponding patch. Origin functions must use the [:external:]
36 * modifier and can have no body. Origin classes and functions are also
37 * called 'patched'.
38 * Patch: A class or function which is declared in a patch library and which
39 * has a corresponding origin. Both patch classes and patch functions must use
40 * the [:patch:] modifier.
41 * Injected: A class or function (or even field) which is declared in a
42 * patch library and which has no corresponding origin. An injected element
43 * cannot use the [:patch:] modifier. Injected elements are never visible from
44 * outside the patch library in which they have been declared. For this
45 * reason, injected elements are often declared private and therefore called
46 * also called 'patch private'.
47 *
48 * Examples of the variants is shown in the code below:
49 *
50 * // In the origin library:
51 * class RegularClass { // A regular class.
52 * void regularMethod() {} // A regular method.
53 * }
54 * class PatchedClass { // An origin class.
55 * int regularField; // A regular field.
56 * void regularMethod() {} // A regular method.
57 * external void patchedMethod(); // An origin method.
58 * }
59 *
60 * // In the patch library:
61 * class _InjectedClass { // An injected class.
62 * void _injectedMethod() {} // An injected method.
63 * }
64 * @patch class PatchedClass { // A patch class.
65 * int _injectedField; { // An injected field.
66 * @patch void patchedMethod() {} // A patch method.
67 * }
68 *
69 *
70 * ## Declaration and implementation ##
71 *
72 * With patches we have two views on elements: as the 'declaration' which
73 * introduces the entity and defines its interface, and as the 'implementation'
74 * which defines the actual implementation of the entity.
75 *
76 * Every element has a 'declaration' and an 'implementation' element. For
77 * regular and injected elements these are the same. For origin elements the
78 * declaration is the element itself and the implementation is the patch element
79 * found through its [:patch:] field. For patch elements the implementation is
80 * the element itself and the declaration is the origin element found through
81 * its [:origin:] field. The declaration and implementation of any element is
82 * conveniently available through the [Element.declaration] and
83 * [Element.implementation] getters.
84 *
85 * Most patch-related invariants enforced through-out the compiler are defined
86 * in terms of 'declaration' and 'implementation', and tested through the
87 * predicate getters [Element.isDeclaration] and [Element.isImplementation].
88 * Patch invariants are stated both in comments and as assertions.
89 *
90 *
91 * ## General invariant guidelines ##
92 *
93 * For [LibraryElement] we always use declarations. This means the
94 * [Element.getLibrary] method will only return library declarations. Patch
95 * library implementations are only accessed through calls to
96 * [Element.getImplementationLibrary] which is used to setup the correct
97 * [Element.enclosingElement] relation between patch/injected elements and the
98 * patch library.
99 *
100 * For [ClassElement] and [FunctionElement] we use declarations for determining
101 * identity and implementations for work based on the AST nodes, such as
102 * resolution, type-checking, type inference, building SSA graphs, etc.
103 * - Worklist only contain declaration elements.
104 * - Most maps and sets use declarations exclusively, and their individual
105 * invariants are stated in the field comments.
106 * - [tree.TreeElements] only map to patch elements from inside a patch library.
107 * TODO(johnniwinther): Simplify this invariant to use only declarations in
108 * [tree.TreeElements].
109 * - Builders shift between declaration and implementation depending on usages.
110 * - Compile-time constants use constructor implementation exclusively.
111 * - Work on function parameters is performed on the declaration of the function
112 * element.
113 */
114
115 library patchparser;
116
117 import 'dart:async';
118
119 import 'constants/values.dart' show ConstantValue;
120 import 'dart2jslib.dart'
121 show Compiler,
122 CompilerTask,
123 DiagnosticListener,
124 MessageKind,
125 Script;
126 import 'elements/elements.dart';
127 import 'elements/modelx.dart'
128 show LibraryElementX,
129 MetadataAnnotationX,
130 ClassElementX,
131 FunctionElementX;
132 import 'helpers/helpers.dart'; // Included for debug helpers.
133 import 'library_loader.dart' show LibraryLoader;
134 import 'scanner/scannerlib.dart'; // Scanner, Parsers, Listeners
135 import 'util/util.dart';
136
137 class PatchParserTask extends CompilerTask {
138 PatchParserTask(Compiler compiler): super(compiler);
139 final String name = "Patching Parser";
140
141 /**
142 * Scans a library patch file, applies the method patches and
143 * injections to the library, and returns a list of class
144 * patches.
145 */
146 Future patchLibrary(LibraryLoader loader,
147 Uri patchUri, LibraryElement originLibrary) {
148 return compiler.readScript(originLibrary, patchUri)
149 .then((Script script) {
150 var patchLibrary = new LibraryElementX(script, null, originLibrary);
151 return compiler.withCurrentElement(patchLibrary, () {
152 loader.registerNewLibrary(patchLibrary);
153 compiler.withCurrentElement(patchLibrary.entryCompilationUnit, () {
154 // This patches the elements of the patch library into [library].
155 // Injected elements are added directly under the compilation unit.
156 // Patch elements are stored on the patched functions or classes.
157 scanLibraryElements(patchLibrary.entryCompilationUnit);
158 });
159 return loader.processLibraryTags(patchLibrary);
160 });
161 });
162 }
163
164 void scanLibraryElements(CompilationUnitElement compilationUnit) {
165 measure(() {
166 // TODO(johnniwinther): Test that parts and exports are handled correctly.
167 Script script = compilationUnit.script;
168 Token tokens = new Scanner(script.file).tokenize();
169 Function idGenerator = compiler.getNextFreeClassId;
170 Listener patchListener = new PatchElementListener(compiler,
171 compilationUnit,
172 idGenerator);
173 new PartialParser(patchListener).parseUnit(tokens);
174 });
175 }
176
177 void parsePatchClassNode(PartialClassElement element) {
178 // Parse [PartialClassElement] using a "patch"-aware parser instead
179 // of calling its [parseNode] method.
180 if (element.cachedNode != null) return;
181
182 measure(() => compiler.withCurrentElement(element, () {
183 MemberListener listener = new MemberListener(compiler, element);
184 Parser parser = new PatchClassElementParser(listener);
185 Token token = parser.parseTopLevelDeclaration(element.beginToken);
186 assert(identical(token, element.endToken.next));
187 element.cachedNode = listener.popNode();
188 assert(listener.nodes.isEmpty);
189
190 Link<Element> patches = element.localMembers;
191 applyContainerPatch(element.origin, patches);
192 }));
193 }
194
195 void applyContainerPatch(ClassElement originClass,
196 Link<Element> patches) {
197 for (Element patch in patches) {
198 if (!isPatchElement(compiler, patch)) continue;
199
200 Element origin = originClass.localLookup(patch.name);
201 patchElement(compiler, origin, patch);
202 }
203 }
204 }
205
206 /**
207 * Partial parser for patch files that also handles the members of class
208 * declarations.
209 */
210 class PatchClassElementParser extends PartialParser {
211 PatchClassElementParser(Listener listener) : super(listener);
212
213 Token parseClassBody(Token token) => fullParseClassBody(token);
214 }
215
216 /**
217 * Extension of [ElementListener] for parsing patch files.
218 */
219 class PatchElementListener extends ElementListener implements Listener {
220 final Compiler compiler;
221
222 PatchElementListener(Compiler compiler,
223 CompilationUnitElement patchElement,
224 int idGenerator())
225 : this.compiler = compiler,
226 super(compiler, patchElement, idGenerator);
227
228 void pushElement(Element patch) {
229 super.pushElement(patch);
230 if (isPatchElement(compiler, patch)) {
231 LibraryElement originLibrary = compilationUnitElement.library;
232 assert(originLibrary.isPatched);
233 Element origin = originLibrary.localLookup(patch.name);
234 patchElement(listener, origin, patch);
235 }
236 }
237 }
238
239 void patchElement(Compiler compiler,
240 Element origin,
241 Element patch) {
242 if (origin == null) {
243 compiler.reportError(
244 patch, MessageKind.PATCH_NON_EXISTING, {'name': patch.name});
245 return;
246 }
247 if (!(origin.isClass ||
248 origin.isConstructor ||
249 origin.isFunction ||
250 origin.isAbstractField)) {
251 // TODO(ahe): Remove this error when the parser rejects all bad modifiers.
252 compiler.reportError(origin, MessageKind.PATCH_NONPATCHABLE);
253 return;
254 }
255 if (patch.isClass) {
256 tryPatchClass(compiler, origin, patch);
257 } else if (patch.isGetter) {
258 tryPatchGetter(compiler, origin, patch);
259 } else if (patch.isSetter) {
260 tryPatchSetter(compiler, origin, patch);
261 } else if (patch.isConstructor) {
262 tryPatchConstructor(compiler, origin, patch);
263 } else if(patch.isFunction) {
264 tryPatchFunction(compiler, origin, patch);
265 } else {
266 // TODO(ahe): Remove this error when the parser rejects all bad modifiers.
267 compiler.reportError(patch, MessageKind.PATCH_NONPATCHABLE);
268 }
269 }
270
271 void tryPatchClass(Compiler compiler,
272 Element origin,
273 ClassElement patch) {
274 if (!origin.isClass) {
275 compiler.reportError(
276 origin, MessageKind.PATCH_NON_CLASS, {'className': patch.name});
277 compiler.reportInfo(
278 patch, MessageKind.PATCH_POINT_TO_CLASS, {'className': patch.name});
279 return;
280 }
281 patchClass(compiler, origin, patch);
282 }
283
284 void patchClass(Compiler compiler,
285 ClassElementX origin,
286 ClassElementX patch) {
287 if (origin.isPatched) {
288 compiler.internalError(origin,
289 "Patching the same class more than once.");
290 }
291 origin.applyPatch(patch);
292 checkNativeAnnotation(compiler, patch);
293 }
294
295 /// Check whether [cls] has a `@Native(...)` annotation, and if so, set its
296 /// native name from the annotation.
297 checkNativeAnnotation(Compiler compiler, ClassElement cls) {
298 EagerAnnotationHandler.checkAnnotation(compiler, cls,
299 const NativeAnnotationHandler());
300 }
301
302 /// Abstract interface for pre-resolution detection of metadata.
303 ///
304 /// The detection is handled in two steps:
305 /// - match the annotation syntactically and assume that the annotation is valid
306 /// if it looks correct,
307 /// - setup a deferred action to check that the annotation has a valid constant
308 /// value and report an internal error if not.
309 abstract class EagerAnnotationHandler {
310 /// Checks that [annotation] looks like a matching annotation and optionally
311 /// applies actions on [element]. Returns `true` if the annotation matched.
312 bool apply(Compiler compiler,
313 Element element,
314 MetadataAnnotation annotation);
315
316 /// Checks that the annotation value is valid.
317 void validate(Compiler compiler,
318 Element element,
319 MetadataAnnotation annotation,
320 ConstantValue constant);
321
322
323 /// Checks [element] for metadata matching the [handler]. Return `true` if
324 /// matching metadata was found.
325 static bool checkAnnotation(Compiler compiler,
326 Element element,
327 EagerAnnotationHandler handler) {
328 for (Link<MetadataAnnotation> link = element.metadata;
329 !link.isEmpty;
330 link = link.tail) {
331 MetadataAnnotation annotation = link.head;
332 if (handler.apply(compiler, element, annotation)) {
333 // TODO(johnniwinther): Perform this check in
334 // [Compiler.onLibrariesLoaded].
335 compiler.enqueuer.resolution.addDeferredAction(element, () {
336 annotation.ensureResolved(compiler);
337 handler.validate(
338 compiler, element, annotation, annotation.constant.value);
339 });
340 return true;
341 }
342 }
343 return false;
344 }
345 }
346
347 /// Annotation handler for pre-resolution detection of `@Native(...)`
348 /// annotations.
349 class NativeAnnotationHandler implements EagerAnnotationHandler {
350 const NativeAnnotationHandler();
351
352 String getNativeAnnotation(MetadataAnnotation annotation) {
353 if (annotation.beginToken != null &&
354 annotation.beginToken.next.value == 'Native') {
355 // Skipping '@', 'Native', and '('.
356 Token argument = annotation.beginToken.next.next.next;
357 if (argument is StringToken) {
358 return argument.value;
359 }
360 }
361 return null;
362 }
363
364 bool apply(Compiler compiler,
365 Element element,
366 MetadataAnnotation annotation) {
367 if (element.isClass) {
368 String native = getNativeAnnotation(annotation);
369 if (native != null) {
370 ClassElementX declaration = element.declaration;
371 declaration.setNative(native);
372 return true;
373 }
374 }
375 return false;
376 }
377
378 void validate(Compiler compiler,
379 Element element,
380 MetadataAnnotation annotation,
381 ConstantValue constant) {
382 if (constant.computeType(compiler).element !=
383 compiler.nativeAnnotationClass) {
384 compiler.internalError(annotation, 'Invalid @Native(...) annotation.');
385 }
386 }
387 }
388
389 /// Annotation handler for pre-resolution detection of `@patch` annotations.
390 class PatchAnnotationHandler implements EagerAnnotationHandler {
391 const PatchAnnotationHandler();
392
393 bool isPatchAnnotation(MetadataAnnotation annotation) {
394 return annotation.beginToken != null &&
395 annotation.beginToken.next.value == 'patch';
396 }
397
398 bool apply(Compiler compiler,
399 Element element,
400 MetadataAnnotation annotation) {
401 return isPatchAnnotation(annotation);
402 }
403
404 void validate(Compiler compiler,
405 Element element,
406 MetadataAnnotation annotation,
407 ConstantValue constant) {
408 if (constant != compiler.patchConstant) {
409 compiler.internalError(annotation, 'Invalid patch annotation.');
410 }
411 }
412 }
413
414
415 void tryPatchGetter(DiagnosticListener listener,
416 Element origin,
417 FunctionElement patch) {
418 if (!origin.isAbstractField) {
419 listener.reportError(
420 origin, MessageKind.PATCH_NON_GETTER, {'name': origin.name});
421 listener.reportInfo(
422 patch,
423 MessageKind.PATCH_POINT_TO_GETTER, {'getterName': patch.name});
424 return;
425 }
426 AbstractFieldElement originField = origin;
427 if (originField.getter == null) {
428 listener.reportError(
429 origin, MessageKind.PATCH_NO_GETTER, {'getterName': patch.name});
430 listener.reportInfo(
431 patch,
432 MessageKind.PATCH_POINT_TO_GETTER, {'getterName': patch.name});
433 return;
434 }
435 patchFunction(listener, originField.getter, patch);
436 }
437
438 void tryPatchSetter(DiagnosticListener listener,
439 Element origin,
440 FunctionElement patch) {
441 if (!origin.isAbstractField) {
442 listener.reportError(
443 origin, MessageKind.PATCH_NON_SETTER, {'name': origin.name});
444 listener.reportInfo(
445 patch,
446 MessageKind.PATCH_POINT_TO_SETTER, {'setterName': patch.name});
447 return;
448 }
449 AbstractFieldElement originField = origin;
450 if (originField.setter == null) {
451 listener.reportError(
452 origin, MessageKind.PATCH_NO_SETTER, {'setterName': patch.name});
453 listener.reportInfo(
454 patch,
455 MessageKind.PATCH_POINT_TO_SETTER, {'setterName': patch.name});
456 return;
457 }
458 patchFunction(listener, originField.setter, patch);
459 }
460
461 void tryPatchConstructor(DiagnosticListener listener,
462 Element origin,
463 FunctionElement patch) {
464 if (!origin.isConstructor) {
465 listener.reportError(
466 origin,
467 MessageKind.PATCH_NON_CONSTRUCTOR, {'constructorName': patch.name});
468 listener.reportInfo(
469 patch,
470 MessageKind.PATCH_POINT_TO_CONSTRUCTOR,
471 {'constructorName': patch.name});
472 return;
473 }
474 patchFunction(listener, origin, patch);
475 }
476
477 void tryPatchFunction(DiagnosticListener listener,
478 Element origin,
479 FunctionElement patch) {
480 if (!origin.isFunction) {
481 listener.reportError(
482 origin,
483 MessageKind.PATCH_NON_FUNCTION, {'functionName': patch.name});
484 listener.reportInfo(
485 patch,
486 MessageKind.PATCH_POINT_TO_FUNCTION, {'functionName': patch.name});
487 return;
488 }
489 patchFunction(listener, origin, patch);
490 }
491
492 void patchFunction(DiagnosticListener listener,
493 FunctionElementX origin,
494 FunctionElementX patch) {
495 if (!origin.modifiers.isExternal) {
496 listener.reportError(origin, MessageKind.PATCH_NON_EXTERNAL);
497 listener.reportInfo(
498 patch,
499 MessageKind.PATCH_POINT_TO_FUNCTION, {'functionName': patch.name});
500 return;
501 }
502 if (origin.isPatched) {
503 listener.internalError(origin,
504 "Trying to patch a function more than once.");
505 }
506 origin.applyPatch(patch);
507 }
508
509 // TODO(johnniwinther): Add unittest when patch is (real) metadata.
510 bool isPatchElement(Compiler compiler, Element element) {
511 return EagerAnnotationHandler.checkAnnotation(compiler, element,
512 const PatchAnnotationHandler());
513 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698