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

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

Issue 237583014: JS templates (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: merge Created 6 years, 8 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
(Empty)
1 // Copyright (c) 2014, 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 part of js;
6
7 class TemplateManager {
8 Map<String, Template> expressionTemplates = new Map<String, Template>();
9 Map<String, Template> statementTemplates = new Map<String, Template>();
10
11 TemplateManager();
12
13
14 Template lookupExpressionTemplate(String source) {
15 return expressionTemplates[source];
16 }
17
18 Template defineExpressionTemplate(String source, Node ast) {
19 Template template =
20 new Template(source, ast, isExpression: true, forceCopy: false);
21 expressionTemplates[source] = template;
22 return template;
23 }
24
25 Template lookupStatementTemplate(String source) {
26 return statementTemplates[source];
27 }
28
29 Template defineStatementTemplate(String source, Node ast) {
30 Template template =
31 new Template(source, ast, isExpression: false, forceCopy: false);
32 statementTemplates[source] = template;
33 return template;
34 }
35 }
36
37 /**
38 * A Template is created with JavaScript AST containing placeholders (interface
39 * InterpolatedNode). The [instantiate] method creates an AST that looks like
40 * the original with the placeholders replaced by the arguments to
41 * [instantiate].
42 */
43 class Template {
44 final String source;
45 final bool isExpression;
46 final bool forceCopy;
47 final Node ast;
48
49 Instantiator instantiator;
50 int positionalArgumentCount = -1;
51 // TODO(sra): Named arguments.
52
53 Template(this.source, this.ast,
54 {this.isExpression: true, this.forceCopy: false}) {
55 _compile();
56 }
57
58 Template.withExpressionResult(this.ast)
59 : source = null, isExpression = true, forceCopy = false {
60 assert(ast is Expression);
61 assert(_checkNoPlaceholders());
62 positionalArgumentCount = 0;
63 instantiator = (arguments) => ast;
64 }
65
66 Template.withStatementResult(this.ast)
67 : source = null, isExpression = false, forceCopy = false {
68 assert(ast is Statement);
69 assert(_checkNoPlaceholders());
70 positionalArgumentCount = 0;
71 instantiator = (arguments) => ast;
72 }
73
74 bool _checkNoPlaceholders() {
75 InstantiatorGeneratorVisitor generator =
76 new InstantiatorGeneratorVisitor(false);
77 generator.compile(ast);
78 return generator.analysis.count == 0;
79 }
80
81 void _compile() {
82 InstantiatorGeneratorVisitor generator =
83 new InstantiatorGeneratorVisitor(forceCopy);
84 instantiator = generator.compile(ast);
85 positionalArgumentCount = generator.analysis.count;
86 }
87
88 Node instantiate(List arguments) {
89 if (arguments is List) {
90 if (arguments.length != positionalArgumentCount) {
91 throw 'Wrong number of template arguments, given ${arguments.length}, '
92 'expected $positionalArgumentCount';
93 }
94 return instantiator(arguments);
95 }
96 // TODO(sra): Add named placeholders and a Map of arguments.
97 throw new UnimplementedError('Template arguments must be a list');
98 }
99 }
100
101 /**
102 * An Instantiator is a Function that generates a JS AST tree or List of
103 * trees. [arguments] is a List for positional templates, or (TODO) Map for
104 * named templates.
105 */
106 typedef Node Instantiator(var arguments);
107
108
109 /**
110 * InstantiatorGeneratorVisitor compiles a template. This class compiles a tree
111 * containing [InterpolatedNode]s into a function that will create a copy of the
112 * tree with the interpolated nodes substituted with provided values.
113 */
114 class InstantiatorGeneratorVisitor implements NodeVisitor<Instantiator> {
115
116 final bool forceCopy;
117
118 InterpolatedNodeAnalysis analysis = new InterpolatedNodeAnalysis();
119
120 /**
121 * The entire tree is cloned if [forceCopy] is true.
122 */
123 InstantiatorGeneratorVisitor(this.forceCopy);
124
125 Instantiator compile(Node node) {
126 analysis.visit(node);
127 Instantiator result = visit(node);
128 return result;
129 }
130
131 static error(String message) {
132 throw message;
133 }
134
135 static Instantiator same(Node node) => (arguments) => node;
136 static Node makeNull(arguments) => null;
137
138 Instantiator visit(Node node) {
139 if (forceCopy || analysis.containsInterpolatedNodes(node)) {
140 return node.accept(this);
141 }
142 return same(node);
143 }
144
145 Instantiator visitNullable(Node node) {
146 if (node == null) return makeNull;
147 return visit(node);
148 }
149
150 Instantiator visitSplayable(Node node) {
151 // TODO(sra): Process immediate [InterpolatedNode]s, permitting splaying.
152 return visit(node);
153 }
154
155 Instantiator visitNode(Node node) {
156 throw 'Unimplemented InstantiatorGeneratorVisitor for $node';
157 }
158
159 static RegExp identiferRE = new RegExp(r'^[A-Za-z_$][A-Za-z_$0-9]*$');
160
161 static Expression convertStringToVariableUse(String value) {
162 assert(identiferRE.hasMatch(value));
163 return new VariableUse(value);
164 }
165
166 Instantiator visitInterpolatedExpression(InterpolatedExpression node) {
167 int position = node.name;
168 return (arguments) {
169 var value = arguments[position];
170 if (value is Expression) return value;
171 if (value is String) return convertStringToVariableUse(value);;
172 error('Interpolated value #$position is not an Expression: $value');
173 };
174 }
175
176 Instantiator visitSplayableExpression(Node node) {
177 if (node is InterpolatedExpression) {
178 int position = node.name;
179 return (arguments) {
180 var value = arguments[position];
181 Expression toExpression(item) {
182 if (item is Expression) return item;
183 if (item is String) return convertStringToVariableUse(item);
184 error('Interpolated value #$position is not '
185 'an Expression or List of Expressions: $value');
186 }
187 if (value is Iterable) return value.map(toExpression);
188 return toExpression(value);
189 };
190 }
191 return visit(node);
192 }
193
194 Instantiator visitInterpolatedLiteral(InterpolatedLiteral node) {
195 int position = node.name;
196 return (arguments) {
197 var value = arguments[position];
198 if (value is Literal) return value;
199 error('Interpolated value #$position is not a Literal: '
200 '$value (${value.runtimeType})');
201 };
202 }
203
204 Instantiator visitInterpolatedParameter(InterpolatedParameter node) {
205 int position = node.name;
206 return (arguments) {
207 var value = arguments[position];
208
209 Parameter toParameter(item) {
210 if (item is Parameter) return item;
211 if (item is String) return new Parameter(item);
212 error('Interpolated value #$position is not a Parameter or '
213 'List of Parameters: $value (a ${value.runtimeType})');
214 }
215 if (value is Iterable) return value.map(toParameter);
216 return toParameter(value);
217 };
218 }
219
220 Instantiator visitInterpolatedSelector(InterpolatedSelector node) {
221 // A selector is an expression, as in `a[selector]`.
222 // A String argument converted into a LiteralString, so `a.#` with argument
223 // 'foo' generates `a["foo"]` which prints as `a.foo`.
224 int position = node.name;
225 return (arguments) {
226 var value = arguments[position];
227 if (value is Expression) return value;
228 if (value is String) return new LiteralString('"$value"');
229 error('Interpolated value #$position is not a selector: $value');
230 };
231 }
232
233 Instantiator visitInterpolatedStatement(InterpolatedStatement node) {
234 int position = node.name;
235 return (arguments) {
236 var value = arguments[position];
237 if (value is Node) return value.toStatement();
238 error('Interpolated value #$position is not a Statement: $value');
239 };
240 }
241
242 Instantiator visitSplayableStatement(Node node) {
243 if (node is InterpolatedStatement) {
244 int position = node.name;
245 return (arguments) {
246 var value = arguments[position];
247 Statement toStatement(item) {
248 if (item is Statement) return item;
249 if (item is Expression) return item.toStatement();;
250 error('Interpolated value #$position is not '
251 'a Statement or List of Statements: $value');
252 }
253 if (value is Iterable) return value.map(toStatement);
254 return toStatement(value);
255 };
256 }
257 return visit(node);
258 }
259
260 Instantiator visitProgram(Program node) {
261 List instantiators = node.body.map(visitSplayableStatement).toList();
262 return (arguments) {
263 List<Statement> statements = <Statement>[];
264 void add(node) {
265 if (node is EmptyStatement) return;
266 if (node is Iterable) {
267 statements.addAll(node);
268 } else {
269 statements.add(node.toStatement());
270 }
271 }
272 for (Instantiator instantiator in instantiators) {
273 add(instantiator(arguments));
274 }
275 return new Program(statements);
276 };
277 }
278
279 Instantiator visitBlock(Block node) {
280 List instantiators = node.statements.map(visitSplayableStatement).toList();
281 return (arguments) {
282 List<Statement> statements = <Statement>[];
283 void add(node) {
284 if (node is EmptyStatement) return;
285 if (node is Iterable) {
286 statements.addAll(node);
287 } else if (node is Block) {
288 statements.addAll(node.statements);
289 } else {
290 statements.add(node.toStatement());
291 }
292 }
293 for (Instantiator instantiator in instantiators) {
294 add(instantiator(arguments));
295 }
296 return new Block(statements);
297 };
298 }
299
300 Instantiator visitExpressionStatement(ExpressionStatement node) {
301 Instantiator buildExpression = visit(node.expression);
302 return (arguments) {
303 return buildExpression(arguments).toStatement();
304 };
305 }
306
307 Instantiator visitEmptyStatement(EmptyStatement node) =>
308 (arguments) => new EmptyStatement();
309
310 Instantiator visitIf(If node) {
311 if (node.condition is InterpolatedExpression) {
312 return visitIfConditionalCompilation(node);
313 } else {
314 return visitIfNormal(node);
315 }
316 }
317
318 Instantiator visitIfConditionalCompilation(If node) {
319 // Special version of visitInterpolatedExpression that permits bools.
320 compileCondition(InterpolatedExpression node) {
321 int position = node.name;
322 return (arguments) {
323 var value = arguments[position];
324 if (value is bool) return value;
325 if (value is Expression) return value;
326 if (value is String) return convertStringToVariableUse(value);;
327 error('Interpolated value #$position is not an Expression: $value');
328 };
329 }
330 var makeCondition = compileCondition(node.condition);
331 Instantiator makeThen = visit(node.then);
332 Instantiator makeOtherwise = visit(node.otherwise);
333 return (arguments) {
334 var condition = makeCondition(arguments);
335 if (condition is bool) {
336 if (condition == true) {
337 return makeThen(arguments);
338 } else {
339 return makeOtherwise(arguments);
340 }
341 }
342 return new If(
343 condition,
344 makeThen(arguments),
345 makeOtherwise(arguments));
346 };
347 }
348
349 Instantiator visitIfNormal(If node) {
350 Instantiator makeCondition = visit(node.condition);
351 Instantiator makeThen = visit(node.then);
352 Instantiator makeOtherwise = visit(node.otherwise);
353 return (arguments) {
354 return new If(
355 makeCondition(arguments),
356 makeThen(arguments),
357 makeOtherwise(arguments));
358 };
359 }
360
361 Instantiator visitFor(For node) {
362 Instantiator makeInit = visitNullable(node.init);
363 Instantiator makeCondition = visitNullable(node.condition);
364 Instantiator makeUpdate = visitNullable(node.update);
365 Instantiator makeBody = visit(node.body);
366 return (arguments) {
367 return new For(
368 makeInit(arguments), makeCondition(arguments), makeUpdate(arguments),
369 makeBody(arguments));
370 };
371 }
372
373 Instantiator visitForIn(ForIn node) {
374 Instantiator makeLeftHandSide = visit(node.leftHandSide);
375 Instantiator makeObject = visit(node.object);
376 Instantiator makeBody = visit(node.body);
377 return (arguments) {
378 return new ForIn(
379 makeLeftHandSide(arguments),
380 makeObject(arguments),
381 makeBody(arguments));
382 };
383 }
384
385 TODO(String name) {
386 throw new UnimplementedError('${this.runtimeType}.$name');
387 }
388
389 Instantiator visitWhile(While node) => TODO('visitWhile');
390 Instantiator visitDo(Do node) => TODO('visitDo');
391
392 Instantiator visitContinue(Continue node) =>
393 (arguments) => new Continue(node.targetLabel);
394
395 Instantiator visitBreak(Break node) =>
396 (arguments) => new Break(node.targetLabel);
397
398 Instantiator visitReturn(Return node) {
399 Instantiator makeExpression = visitNullable(node.value);
400 return (arguments) => new Return(makeExpression(arguments));
401 }
402
403 Instantiator visitThrow(Throw node) {
404 Instantiator makeExpression = visit(node.expression);
405 return (arguments) => new Throw(makeExpression(arguments));
406 }
407
408
409 Instantiator visitTry(Try node) {
410 Instantiator makeBody = visit(node.body);
411 Instantiator makeCatch = visitNullable(node.catchPart);
412 Instantiator makeFinally = visitNullable(node.finallyPart);
413 return (arguments) => new Try(
414 makeBody(arguments), makeCatch(arguments), makeFinally(arguments));
415 }
416
417 Instantiator visitCatch(Catch node) {
418 Instantiator makeDeclaration = visit(node.declaration);
419 Instantiator makeBody = visit(node.body);
420 return (arguments) => new Catch(
421 makeDeclaration(arguments), makeBody(arguments));
422 }
423
424 Instantiator visitSwitch(Switch node) => TODO('visitSwitch');
425 Instantiator visitCase(Case node) => TODO('visitCase');
426 Instantiator visitDefault(Default node) => TODO('visitDefault');
427
428 Instantiator visitFunctionDeclaration(FunctionDeclaration node) {
429 Instantiator makeName = visit(node.name);
430 Instantiator makeFunction = visit(node.function);
431 return (arguments) =>
432 new FunctionDeclaration(makeName(arguments), makeFunction(arguments));
433 }
434
435 Instantiator visitLabeledStatement(LabeledStatement node) =>
436 TODO('visitLabeledStatement');
437 Instantiator visitLiteralStatement(LiteralStatement node) =>
438 TODO('visitLiteralStatement');
439 Instantiator visitBlob(Blob node) =>
440 TODO('visitBlob');
441 Instantiator visitLiteralExpression(LiteralExpression node) =>
442 TODO('visitLiteralExpression');
443
444 Instantiator visitVariableDeclarationList(VariableDeclarationList node) {
445 List<Instantiator> declarationMakers =
446 node.declarations.map(visit).toList();
447 return (arguments) {
448 List<VariableInitialization> declarations = <VariableInitialization>[];
449 for (Instantiator instantiator in declarationMakers) {
450 var result = instantiator(arguments);
451 declarations.add(result);
452 }
453 return new VariableDeclarationList(declarations);
454 };
455 }
456
457 Instantiator visitSequence(Sequence node) => TODO('visitSequence');
458
459 Instantiator visitAssignment(Assignment node) {
460 Instantiator makeLeftHandSide = visit(node.leftHandSide);
461 Instantiator makeCompoundTarget = visitNullable(node.compoundTarget);
462 Instantiator makeValue = visitNullable(node.value);
463 return (arguments) {
464 return new Assignment._internal(
465 makeLeftHandSide(arguments),
466 makeCompoundTarget(arguments),
467 makeValue(arguments));
468 };
469 }
470
471 Instantiator visitVariableInitialization(VariableInitialization node) {
472 Instantiator makeDeclaration = visit(node.declaration);
473 Instantiator makeValue = visitNullable(node.value);
474 return (arguments) {
475 return new VariableInitialization(
476 makeDeclaration(arguments), makeValue(arguments));
477 };
478 }
479
480 Instantiator visitConditional(Conditional cond) {
481 Instantiator makeCondition = visit(cond.condition);
482 Instantiator makeThen = visit(cond.then);
483 Instantiator makeOtherwise = visit(cond.otherwise);
484 return (arguments) => new Conditional(
485 makeCondition(arguments),
486 makeThen(arguments),
487 makeOtherwise(arguments));
488 }
489
490 Instantiator visitNew(New node) =>
491 handleCallOrNew(node, (target, arguments) => new New(target, arguments));
492
493 Instantiator visitCall(Call node) =>
494 handleCallOrNew(node, (target, arguments) => new Call(target, arguments));
495
496 Instantiator handleCallOrNew(Call node, finish(target, arguments)) {
497 Instantiator makeTarget = visit(node.target);
498 Iterable<Instantiator> argumentMakers =
499 node.arguments.map(visitSplayableExpression).toList();
500
501 return (arguments) {
502 Node target = makeTarget(arguments);
503 List<Expression> callArguments = <Expression>[];
504 for (Instantiator instantiator in argumentMakers) {
505 var result = instantiator(arguments);
506 if (result is Iterable) {
507 callArguments.addAll(result);
508 } else {
509 callArguments.add(result);
510 }
511 }
512 return finish(target, callArguments);
513 };
514 }
515
516 Instantiator visitBinary(Binary node) {
517 Instantiator makeLeft = visit(node.left);
518 Instantiator makeRight = visit(node.right);
519 String op = node.op;
520 return (arguments) =>
521 new Binary(op, makeLeft(arguments), makeRight(arguments));
522 }
523
524 Instantiator visitPrefix(Prefix node) {
525 Instantiator makeOperand = visit(node.argument);
526 String op = node.op;
527 return (arguments) => new Prefix(op, makeOperand(arguments));
528 }
529
530 Instantiator visitPostfix(Postfix node) {
531 Instantiator makeOperand = visit(node.argument);
532 String op = node.op;
533 return (arguments) => new Postfix(op, makeOperand(arguments));
534 }
535
536 Instantiator visitVariableUse(VariableUse node) =>
537 (arguments) => new VariableUse(node.name);
538
539 Instantiator visitThis(This node) => (arguments) => new This();
540
541 Instantiator visitVariableDeclaration(VariableDeclaration node) =>
542 (arguments) => new VariableDeclaration(node.name);
543
544 Instantiator visitParameter(Parameter node) =>
545 (arguments) => new Parameter(node.name);
546
547 Instantiator visitAccess(PropertyAccess node) {
548 Instantiator makeReceiver = visit(node.receiver);
549 Instantiator makeSelector = visit(node.selector);
550 return (arguments) =>
551 new PropertyAccess(makeReceiver(arguments), makeSelector(arguments));
552 }
553
554 Instantiator visitNamedFunction(NamedFunction node) {
555 Instantiator makeDeclaration = visit(node.name);
556 Instantiator makeFunction = visit(node.function);
557 return (arguments) =>
558 new NamedFunction(makeDeclaration(arguments), makeFunction(arguments));
559 }
560
561 Instantiator visitFun(Fun node) {
562 List<Instantiator> paramMakers = node.params.map(visitSplayable).toList();
563 Instantiator makeBody = visit(node.body);
564 // TODO(sra): Avoid copying params if no interpolation or forced copying.
565 return (arguments) {
566 List<Parameter> params = <Parameter>[];
567 for (Instantiator instantiator in paramMakers) {
568 var result = instantiator(arguments);
569 if (result is Iterable) {
570 params.addAll(result);
571 } else {
572 params.add(result);
573 }
574 }
575 Statement body = makeBody(arguments);
576 return new Fun(params, body);
577 };
578 }
579
580 Instantiator visitLiteralBool(LiteralBool node) =>
581 (arguments) => new LiteralBool(node.value);
582
583 Instantiator visitLiteralString(LiteralString node) =>
584 (arguments) => new LiteralString(node.value);
585
586 Instantiator visitLiteralNumber(LiteralNumber node) =>
587 (arguments) => new LiteralNumber(node.value);
588
589 Instantiator visitLiteralNull(LiteralNull node) =>
590 (arguments) => new LiteralNull();
591
592 Instantiator visitArrayInitializer(ArrayInitializer node) {
593 // Assume array has no missing elements.
594 // TODO(sra): Splicing?
595 List<Instantiator> elementMakers = node.elements
596 .map((ArrayElement element) => visit(element.value))
597 .toList();
598 return (arguments) {
599 List<ArrayElement> elements = <ArrayElement>[];
600 void add(Expression value) {
601 elements.add(new ArrayElement(elements.length, value));
602 }
603 for (Instantiator instantiator in elementMakers) {
604 var result = instantiator(arguments);
605 add(result);
606 }
607 return new ArrayInitializer(elements.length, elements);
608 };
609 }
610
611 Instantiator visitArrayElement(ArrayElement node) {
612 throw 'Should not get here'; // Handled in visitArrayInitializer.
613 }
614
615 Instantiator visitObjectInitializer(ObjectInitializer node) {
616 List<Instantiator> propertyMakers =
617 node.properties.map(visitSplayable).toList();
618 bool isOneLiner = node.isOneLiner;
619 return (arguments) {
620 List<Property> properties = <Property>[];
621 for (Instantiator instantiator in propertyMakers) {
622 var result = instantiator(arguments);
623 if (result is Iterable) {
624 properties.addAll(result);
625 } else {
626 properties.add(result);
627 }
628 }
629 return new ObjectInitializer(properties, isOneLiner: isOneLiner);
630 };
631 }
632
633 Instantiator visitProperty(Property node) {
634 Instantiator makeName = visit(node.name);
635 Instantiator makeValue = visit(node.value);
636 return (arguments) {
637 return new Property(makeName(arguments), makeValue(arguments));
638 };
639 }
640
641 Instantiator visitRegExpLiteral(RegExpLiteral node) =>
642 (arguments) => new RegExpLiteral(node.pattern);
643
644 Instantiator visitComment(Comment node) => TODO('visitComment');
645 }
646
647 /**
648 * InterpolatedNodeAnalysis extract [InterpolatedNode]s from AST.
649 */
650 class InterpolatedNodeAnalysis extends BaseVisitor {
651 final Set<Node> containsInterpolatedNode = new Set<Node>();
652 final List<InterpolatedNode> interpolatedNodes = <InterpolatedNode>[];
653 int count = 0;
654
655 InterpolatedNodeAnalysis();
656
657 bool containsInterpolatedNodes(Node node) =>
658 containsInterpolatedNode.contains(node);
659
660 void visit(Node node) {
661 node.accept(this);
662 }
663
664 void visitNode(Node node) {
665 int before = count;
666 node.visitChildren(this);
667 if (count != before) containsInterpolatedNode.add(node);
668 return null;
669 }
670
671 visitInterpolatedNode(InterpolatedNode node) {
672 interpolatedNodes.add(node);
673 containsInterpolatedNode.add(node);
674 ++count;
675 }
676 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698