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

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

Issue 338103004: Use metadata for patching. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 6 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 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
54 * class PatchedClass { // An origin class. 54 * class PatchedClass { // An origin class.
55 * int regularField; // A regular field. 55 * int regularField; // A regular field.
56 * void regularMethod() {} // A regular method. 56 * void regularMethod() {} // A regular method.
57 * external void patchedMethod(); // An origin method. 57 * external void patchedMethod(); // An origin method.
58 * } 58 * }
59 * 59 *
60 * // In the patch library: 60 * // In the patch library:
61 * class _InjectedClass { // An injected class. 61 * class _InjectedClass { // An injected class.
62 * void _injectedMethod() {} // An injected method. 62 * void _injectedMethod() {} // An injected method.
63 * } 63 * }
64 * patch class PatchedClass { // A patch class. 64 * @patch class PatchedClass { // A patch class.
65 * int _injectedField; { // An injected field. 65 * int _injectedField; { // An injected field.
66 * patch void patchedMethod() {} // A patch method. 66 * @patch void patchedMethod() {} // A patch method.
67 * } 67 * }
68 * 68 *
69 * 69 *
70 * ## Declaration and implementation ## 70 * ## Declaration and implementation ##
71 * 71 *
72 * With patches we have two views on elements: as the 'declaration' which 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' 73 * introduces the entity and defines its interface, and as the 'implementation'
74 * which defines the actual implementation of the entity. 74 * which defines the actual implementation of the entity.
75 * 75 *
76 * Every element has a 'declaration' and an 'implementation' element. For 76 * Every element has a 'declaration' and an 'implementation' element. For
(...skipping 34 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:async'; 117 import 'dart:async';
118 118
119 import "tree/tree.dart" as tree; 119 import "tree/tree.dart" as tree;
120 import "dart2jslib.dart" as leg; // CompilerTask, Compiler. 120 import "dart2jslib.dart" as leg; // CompilerTask, Compiler.
121 import "helpers/helpers.dart";
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" 124 import "elements/modelx.dart"
124 show LibraryElementX, 125 show LibraryElementX,
125 MetadataAnnotationX, 126 MetadataAnnotationX,
126 ClassElementX, 127 ClassElementX,
127 FunctionElementX; 128 FunctionElementX;
128 import 'util/util.dart'; 129 import 'util/util.dart';
129 130
130 class PatchParserTask extends leg.CompilerTask { 131 class PatchParserTask extends leg.CompilerTask {
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
162 } 163 }
163 164
164 void scanLibraryElements( 165 void scanLibraryElements(
165 CompilationUnitElement compilationUnit, 166 CompilationUnitElement compilationUnit,
166 LinkBuilder<tree.LibraryTag> imports) { 167 LinkBuilder<tree.LibraryTag> imports) {
167 measure(() { 168 measure(() {
168 // TODO(lrn): Possibly recursively handle 'part' directives in patch. 169 // TODO(lrn): Possibly recursively handle 'part' directives in patch.
169 leg.Script script = compilationUnit.script; 170 leg.Script script = compilationUnit.script;
170 Token tokens = new Scanner(script.file).tokenize(); 171 Token tokens = new Scanner(script.file).tokenize();
171 Function idGenerator = compiler.getNextFreeClassId; 172 Function idGenerator = compiler.getNextFreeClassId;
172 PatchListener patchListener = 173 Listener patchListener = new PatchElementListener(compiler,
173 new PatchElementListener(compiler, 174 compilationUnit,
174 compilationUnit, 175 idGenerator,
175 idGenerator, 176 imports);
176 imports); 177 new PartialParser(patchListener).parseUnit(tokens);
177 new PatchParser(patchListener).parseUnit(tokens);
178 }); 178 });
179 } 179 }
180 180
181 void parsePatchClassNode(PartialClassElement element) { 181 void parsePatchClassNode(PartialClassElement element) {
182 // Parse [PartialClassElement] using a "patch"-aware parser instead 182 // Parse [PartialClassElement] using a "patch"-aware parser instead
183 // of calling its [parseNode] method. 183 // of calling its [parseNode] method.
184 if (element.cachedNode != null) return; 184 if (element.cachedNode != null) return;
185 185
186 measure(() => compiler.withCurrentElement(element, () { 186 measure(() => compiler.withCurrentElement(element, () {
187 PatchMemberListener listener = new PatchMemberListener(compiler, element); 187 MemberListener listener = new MemberListener(compiler, element);
188 Parser parser = new PatchClassElementParser(listener); 188 Parser parser = new PatchClassElementParser(listener);
189 Token token = parser.parseTopLevelDeclaration(element.beginToken); 189 Token token = parser.parseTopLevelDeclaration(element.beginToken);
190 assert(identical(token, element.endToken.next)); 190 assert(identical(token, element.endToken.next));
191 element.cachedNode = listener.popNode(); 191 element.cachedNode = listener.popNode();
192 assert(listener.nodes.isEmpty); 192 assert(listener.nodes.isEmpty);
193 193
194 Link<Element> patches = element.localMembers; 194 Link<Element> patches = element.localMembers;
195 applyContainerPatch(element.origin, patches); 195 applyContainerPatch(element.origin, patches);
196 })); 196 }));
197 } 197 }
198 198
199 void applyContainerPatch(ClassElement originClass, 199 void applyContainerPatch(ClassElement originClass,
200 Link<Element> patches) { 200 Link<Element> patches) {
201 for (Element patch in patches) { 201 for (Element patch in patches) {
202 if (!isPatchElement(patch)) continue; 202 if (!isPatchElement(compiler, patch)) continue;
203 203
204 Element origin = originClass.localLookup(patch.name); 204 Element origin = originClass.localLookup(patch.name);
205 patchElement(compiler, origin, patch); 205 patchElement(compiler, origin, patch);
206 } 206 }
207 } 207 }
208 } 208 }
209 209
210 /** 210 /**
211 * Extension of the [Listener] interface to handle the extra "patch" pseudo-
212 * keyword in patch files.
213 * Patch files shouldn't have a type named "patch".
214 */
215 abstract class PatchListener extends Listener {
216 void beginPatch(Token patch);
217 void endPatch(Token patch);
218 }
219
220 /**
221 * Partial parser that extends the top-level and class grammars to allow the
222 * word "patch" in front of some declarations.
223 */
224 class PatchParser extends PartialParser {
225 PatchParser(PatchListener listener) : super(listener);
226
227 PatchListener get patchListener => listener;
228
229 bool isPatch(Token token) => token.value == 'patch';
230
231 /**
232 * Parse top-level declarations, and allow "patch" in front of functions
233 * and classes.
234 */
235 Token parseTopLevelDeclaration(Token token) {
236 if (!isPatch(token)) {
237 return super.parseTopLevelDeclaration(token);
238 }
239 Token patch = token;
240 token = token.next;
241 String value = token.stringValue;
242 if (identical(value, 'interface')
243 || identical(value, 'typedef')
244 || identical(value, '#')
245 || identical(value, 'abstract')) {
246 // At the top level, you can only patch functions and classes.
247 // Patch classes and functions can't be marked abstract.
248 return listener.unexpected(patch);
249 }
250 patchListener.beginPatch(patch);
251 token = super.parseTopLevelDeclaration(token);
252 patchListener.endPatch(patch);
253 return token;
254 }
255
256 /**
257 * Parse a class member.
258 * If the member starts with "patch", it's a member override.
259 * Only methods can be overridden, including constructors, getters and
260 * setters, but not fields. If "patch" occurs in front of a field, the error
261 * is caught elsewhere.
262 */
263 Token parseMember(Token token) {
264 if (!isPatch(token)) {
265 return super.parseMember(token);
266 }
267 Token patch = token;
268 patchListener.beginPatch(patch);
269 token = super.parseMember(token.next);
270 patchListener.endPatch(patch);
271 return token;
272 }
273 }
274
275 /**
276 * Partial parser for patch files that also handles the members of class 211 * Partial parser for patch files that also handles the members of class
277 * declarations. 212 * declarations.
278 */ 213 */
279 class PatchClassElementParser extends PatchParser { 214 class PatchClassElementParser extends PartialParser {
280 PatchClassElementParser(PatchListener listener) : super(listener); 215 PatchClassElementParser(Listener listener) : super(listener);
281 216
282 Token parseClassBody(Token token) => fullParseClassBody(token); 217 Token parseClassBody(Token token) => fullParseClassBody(token);
283 } 218 }
284 219
285 /** 220 /**
286 * Extension of [ElementListener] for parsing patch files. 221 * Extension of [ElementListener] for parsing patch files.
287 */ 222 */
288 class PatchElementListener extends ElementListener implements PatchListener { 223 class PatchElementListener extends ElementListener implements Listener {
224 final leg.Compiler compiler;
289 final LinkBuilder<tree.LibraryTag> imports; 225 final LinkBuilder<tree.LibraryTag> imports;
290 bool isMemberPatch = false;
291 bool isClassPatch = false;
292 226
293 PatchElementListener(leg.DiagnosticListener listener, 227 PatchElementListener(leg.Compiler compiler,
294 CompilationUnitElement patchElement, 228 CompilationUnitElement patchElement,
295 int idGenerator(), 229 int idGenerator(),
296 this.imports) 230 this.imports)
297 : super(listener, patchElement, idGenerator); 231 : this.compiler = compiler,
298 232 super(compiler, patchElement, idGenerator);
299 MetadataAnnotation popMetadataHack() {
300 // TODO(ahe): Remove this method.
301 popNode(); // Discard null.
302 return new PatchMetadataAnnotation();
303 }
304
305 void beginPatch(Token token) {
306 if (identical(token.next.stringValue, "class")) {
307 isClassPatch = true;
308 } else {
309 isMemberPatch = true;
310 }
311 handleIdentifier(token);
312 }
313
314 void endPatch(Token token) {
315 if (identical(token.next.stringValue, "class")) {
316 isClassPatch = false;
317 } else {
318 isMemberPatch = false;
319 }
320 }
321 233
322 /** 234 /**
323 * Allow script tags (import only, the parser rejects the rest for now) in 235 * Allow script tags (import only, the parser rejects the rest for now) in
324 * patch files. The import tags will be added to the library. 236 * patch files. The import tags will be added to the library.
325 */ 237 */
326 bool allowLibraryTags() => true; 238 bool allowLibraryTags() => true;
327 239
328 void addLibraryTag(tree.LibraryTag tag) { 240 void addLibraryTag(tree.LibraryTag tag) {
329 super.addLibraryTag(tag); 241 super.addLibraryTag(tag);
330 imports.addLast(tag); 242 imports.addLast(tag);
331 } 243 }
332 244
333 void pushElement(Element patch) { 245 void pushElement(Element patch) {
334 if (isMemberPatch || (isClassPatch && patch is ClassElement)) { 246 super.pushElement(patch);
335 // Apply patch. 247 if (isPatchElement(compiler, patch)) {
336 patch.addMetadata(popMetadataHack());
337 LibraryElement originLibrary = compilationUnitElement.library; 248 LibraryElement originLibrary = compilationUnitElement.library;
338 assert(originLibrary.isPatched); 249 assert(originLibrary.isPatched);
339 Element origin = originLibrary.localLookup(patch.name); 250 Element origin = originLibrary.localLookup(patch.name);
340 patchElement(listener, origin, patch); 251 patchElement(listener, origin, patch);
341 } 252 }
342 super.pushElement(patch);
343 } 253 }
344 } 254 }
345 255
346 /**
347 * Extension of [MemberListener] for parsing patch class bodies.
348 */
349 class PatchMemberListener extends MemberListener implements PatchListener {
350 bool isMemberPatch = false;
351 bool isClassPatch = false;
352 PatchMemberListener(leg.DiagnosticListener listener,
353 Element enclosingElement)
354 : super(listener, enclosingElement);
355
356 MetadataAnnotation popMetadataHack() {
357 // TODO(ahe): Remove this method.
358 popNode(); // Discard null.
359 return new PatchMetadataAnnotation();
360 }
361
362 void beginPatch(Token token) {
363 if (identical(token.next.stringValue, "class")) {
364 isClassPatch = true;
365 } else {
366 isMemberPatch = true;
367 }
368 handleIdentifier(token);
369 }
370
371 void endPatch(Token token) {
372 if (identical(token.next.stringValue, "class")) {
373 isClassPatch = false;
374 } else {
375 isMemberPatch = false;
376 }
377 }
378
379 void addMember(Element element) {
380 if (isMemberPatch || (isClassPatch && element is ClassElement)) {
381 element.addMetadata(popMetadataHack());
382 }
383 super.addMember(element);
384 }
385 }
386
387 // TODO(ahe): Get rid of this class.
388 class PatchMetadataAnnotation extends MetadataAnnotationX {
389 final leg.Constant value = null;
390
391 PatchMetadataAnnotation() : super(STATE_DONE);
392
393 tree.Node parseNode(leg.DiagnosticListener listener) => null;
394
395 Token get beginToken => null;
396 Token get endToken => null;
397 }
398
399 void patchElement(leg.DiagnosticListener listener, 256 void patchElement(leg.DiagnosticListener listener,
400 Element origin, 257 Element origin,
401 Element patch) { 258 Element patch) {
402 if (origin == null) { 259 if (origin == null) {
403 listener.reportError( 260 listener.reportError(
404 patch, leg.MessageKind.PATCH_NON_EXISTING, {'name': patch.name}); 261 patch, leg.MessageKind.PATCH_NON_EXISTING, {'name': patch.name});
405 return; 262 return;
406 } 263 }
407 if (!(origin.isClass || 264 if (!(origin.isClass ||
408 origin.isConstructor || 265 origin.isConstructor ||
409 origin.isFunction || 266 origin.isFunction ||
410 origin.isAbstractField)) { 267 origin.isAbstractField)) {
411 // TODO(ahe): Remove this error when the parser rejects all bad modifiers. 268 // TODO(ahe): Remove this error when the parser rejects all bad modifiers.
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
539 return; 396 return;
540 } 397 }
541 if (origin.isPatched) { 398 if (origin.isPatched) {
542 listener.internalError(origin, 399 listener.internalError(origin,
543 "Trying to patch a function more than once."); 400 "Trying to patch a function more than once.");
544 } 401 }
545 origin.applyPatch(patch); 402 origin.applyPatch(patch);
546 } 403 }
547 404
548 // TODO(johnniwinther): Add unittest when patch is (real) metadata. 405 // TODO(johnniwinther): Add unittest when patch is (real) metadata.
549 bool isPatchElement(Element element) { 406 bool isPatchElement(leg.Compiler compiler, Element element) {
550 // TODO(lrn): More checks needed if we introduce metadata for real. 407 // TODO(lrn): More checks needed if we introduce metadata for real.
551 // In that case, it must have the identifier "native" as metadata. 408 // In that case, it must have the identifier "native" as metadata.
552 for (Link link = element.metadata; !link.isEmpty; link = link.tail) { 409 for (Link<MetadataAnnotation> link = element.metadata;
553 if (link.head is PatchMetadataAnnotation) return true; 410 !link.isEmpty;
411 link = link.tail) {
412 MetadataAnnotation annotation = link.head;
413 if (annotation.beginToken != null &&
414 annotation.beginToken.next.value == 'patch') {
415 // TODO(johnniwinther): Perform this check in
416 // [Compiler.onLibrariesLoaded].
417 compiler.enqueuer.resolution.addDeferredAction(element, () {
418 annotation.ensureResolved(compiler);
419 if (annotation.value != compiler.patchConstant) {
420 compiler.internalError(annotation, 'Invalid patch annotation.');
421 }
422 });
423 return true;
424 }
554 } 425 }
555 return false; 426 return false;
556 } 427 }
OLDNEW
« no previous file with comments | « sdk/lib/_internal/compiler/implementation/compiler.dart ('k') | sdk/lib/_internal/lib/async_patch.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698