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

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: cleanup 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 /*
floitsch 2014/04/22 16:11:18 debug code.
sra1 2014/04/23 02:33:50 Done.
23 var n = ((expressionTemplates.length == prev) ? '-' : '${expressionTemplat es.length}')
24 .padLeft(5);
25 print('E $n $source');
26 */
27 return template;
28 }
29
30 Template lookupStatementTemplate(String source) {
31 return statementTemplates[source];
32 }
33
34 Template defineStatementTemplate(String source, Node ast) {
35 Template template =
36 new Template(source, ast, isExpression: false, forceCopy: false);
37 statementTemplates[source] = template;
38 /*
floitsch 2014/04/22 16:11:18 ditto.
sra1 2014/04/23 02:33:50 Done.
sra1 2014/04/23 02:33:50 Done.
39 var n = ((statementTemplates.length == prev) ? '-' : '${statementTemplates .length}')
40 .padLeft(5);
41 print(' S $n $source');
42 */
43 return template;
44 }
45 }
46
47 /**
48 */
floitsch 2014/04/22 16:11:18 missing doc.
sra1 2014/04/23 02:33:50 Done.
49 class Template {
50 final String source;
51 final bool isExpression;
52 final bool forceCopy;
53 final Node ast;
54
55 Instantiator instantiator;
56 int positionalArgumentCount = -1;
57 // TODO(sra): Named arguments.
58
59 Template(this.source, this.ast,
60 {this.isExpression: true, this.forceCopy: false}) {
61 _compile();
62 }
63
64 Template.withExpressionResult(this.ast)
65 : source = null, isExpression = true, forceCopy = false {
66 assert(ast is Expression);
67 assert(_checkNoPlaceholders());
68 positionalArgumentCount = 0;
69 instantiator = (arguments) => ast;
70 }
71
72 bool _checkNoPlaceholders() {
73 InstantiatorGeneratorVisitor generator =
74 new InstantiatorGeneratorVisitor(false);
75 generator.compile(ast);
76 return generator.analysis.count == 0;
77 }
78
79 void _compile() {
80 InstantiatorGeneratorVisitor generator =
81 new InstantiatorGeneratorVisitor(forceCopy);
82 instantiator = generator.compile(ast);
83 positionalArgumentCount = generator.analysis.count;
84 }
85
86 Node instantiate(List arguments) {
87 if (arguments is List) {
88 if (arguments.length != positionalArgumentCount) {
89 throw 'Wrong number of template arguments, given ${arguments.length}, '
90 'expected $positionalArgumentCount';
91 }
92 return instantiator(arguments);
93 }
94 // TODO(sra): Add named placeholders and a Map of arguments.
95 throw new UnimplementedError('Template arguments must be a list');
96 }
97 }
98
99 /**
100 * An Instantiator is a Function that generates a JS AST tree or List of
101 * trees. [arguments] is a List for positional templates, or (TODO) Map for
102 * named templates.
103 */
104 typedef Node Instantiator(var arguments);
105
106
107 /**
108 * InstantiatorGeneratorVisitor compiles a tree containing [InterpolatedNode]s
floitsch 2014/04/22 16:11:18 This class compiles ...
sra1 2014/04/23 02:33:50 Done.
109 * into a function that will create a copy of the tree with the interpolated
110 * nodes substituted with provided values.
111 */
112 class InstantiatorGeneratorVisitor implements NodeVisitor<Instantiator> {
113
114 final bool forceCopy;
115
116 InterpolatedNodeAnalysis analysis = new InterpolatedNodeAnalysis();
117
118 /**
119 * The entire tree is cloned if [forceCopy] is true.
120 */
121 InstantiatorGeneratorVisitor(this.forceCopy);
122
123 Instantiator compile(Node node) {
124 analysis.visit(node);
125 Instantiator result = visit(node);
126 return result;
127 }
128
129 static error(String message) {
130 throw message;
131 }
132
133 static Instantiator same(Node node) => (arguments) => node;
134 static Node makeNull(arguments) => null;
135
136 Instantiator visit(Node node) {
137 if (forceCopy || analysis.containsInterpolatedNodes(node)) {
138 return node.accept(this);
139 }
140 return same(node);
141 }
142
143 Instantiator visitNullable(Node node) {
144 if (node == null) return makeNull;
145 return visit(node);
146 }
147
148 Instantiator visitSplayable(Node node) {
149 // TODO(sra): Process immediate [InterpolatedNode]s, permitting splaying.
150 return visit(node);
151 }
152
153 Instantiator visitNode(Node node) {
154 throw 'Unimplemented InstantiatorGeneratorVisitor for $node';
155 }
156
157 static RegExp identiferRE = new RegExp(r'^[A-Za-z_$][A-Za-z_$0-9]*$');
158
159 Instantiator visitInterpolatedExpression(InterpolatedExpression node) {
160 int position = node.name;
161 return (arguments) {
162 var value = arguments[position];
163 if (value is Expression) return value;
164 if (value is String) {
165 if (!identiferRE.hasMatch(value)) error("Not identifier: '$value'");
166 assert(identiferRE.hasMatch(value));
floitsch 2014/04/22 16:11:18 remove assert.
sra1 2014/04/23 02:33:50 Done.
167 return new VariableUse(value);
168 }
169 error('Interpolated value #$position is not an Expression: $value');
170 };
171 }
172
173 Instantiator visitSplayableExpression(Node node) {
174 if (node is InterpolatedExpression) {
175 int position = node.name;
176 return (arguments) {
177 var value = arguments[position];
178 Expression toExpression(item) {
179 if (item is Expression) return item;
180 if (item is String) {
181 assert(identiferRE.hasMatch(item));
182 return new VariableUse(item);
183 }
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;
floitsch 2014/04/22 16:11:18 So we only allow "#" to be a bool or an Expression
sra1 2014/04/23 02:33:50 That has been fixed while you were reviewing (and
326 error('Interpolated value #$position is not an Expression: $value');
327 };
328 }
329 var makeCondition = compileCondition(node.condition);
330 Instantiator makeThen = visit(node.then);
331 Instantiator makeOtherwise = visit(node.otherwise);
332 return (arguments) {
333 var condition = makeCondition(arguments);
334 if (condition is bool) {
335 if (condition == true) {
336 return makeThen(arguments);
337 } else {
338 return makeOtherwise(arguments);
339 }
340 }
341 return new If(
342 condition,
343 makeThen(arguments),
344 makeOtherwise(arguments));
345 };
346 }
347
348 Instantiator visitIfNormal(If node) {
349 Instantiator makeCondition = visit(node.condition);
350 Instantiator makeThen = visit(node.then);
351 Instantiator makeOtherwise = visit(node.otherwise);
352 return (arguments) {
353 return new If(
354 makeCondition(arguments),
355 makeThen(arguments),
356 makeOtherwise(arguments));
357 };
358 }
359
360 Instantiator visitFor(For node) {
361 Instantiator makeInit = visitNullable(node.init);
362 Instantiator makeCondition = visitNullable(node.condition);
363 Instantiator makeUpdate = visitNullable(node.update);
364 Instantiator makeBody = visit(node.body);
365 return (arguments) {
366 return new For(
367 makeInit(arguments), makeCondition(arguments), makeUpdate(arguments),
368 makeBody(arguments));
369 };
370 }
371
372 Instantiator visitForIn(ForIn node) {
373 Instantiator makeLeftHandSide = visit(node.leftHandSide);
374 Instantiator makeObject = visit(node.object);
375 Instantiator makeBody = visit(node.body);
376 return (arguments) {
377 return new ForIn(
378 makeLeftHandSide(arguments),
379 makeObject(arguments),
380 makeBody(arguments));
381 };
382 }
383
384 TODO(String name) {
385 throw new UnimplementedError('${this.runtimeType}.$name');
386 }
387
388 Instantiator visitWhile(While node) => TODO('visitWhile');
389 Instantiator visitDo(Do node) => TODO('visitDo');
390
391 Instantiator visitContinue(Continue node) =>
392 (arguments) => new Continue(node.targetLabel);
393
394 Instantiator visitBreak(Break node) =>
395 (arguments) => new Break(node.targetLabel);
396
397 Instantiator visitReturn(Return node) {
398 Instantiator makeExpression = visitNullable(node.value);
399 return (arguments) => new Return(makeExpression(arguments));
400 }
401
402 Instantiator visitThrow(Throw node) {
403 Instantiator makeExpression = visit(node.expression);
404 return (arguments) => new Throw(makeExpression(arguments));
405 }
406
407
408 Instantiator visitTry(Try node) {
409 Instantiator makeBody = visit(node.body);
410 Instantiator makeCatch = visitNullable(node.catchPart);
411 Instantiator makeFinally = visitNullable(node.finallyPart);
412 return (arguments) => new Try(
413 makeBody(arguments), makeCatch(arguments), makeFinally(arguments));
414 }
415
416 Instantiator visitCatch(Catch node) {
417 Instantiator makeDeclaration = visit(node.declaration);
418 Instantiator makeBody = visit(node.body);
419 return (arguments) => new Catch(
420 makeDeclaration(arguments), makeBody(arguments));
421 }
422
423 Instantiator visitSwitch(Switch node) => TODO('visitSwitch');
424 Instantiator visitCase(Case node) => TODO('visitCase');
425 Instantiator visitDefault(Default node) => TODO('visitDefault');
426
427 Instantiator visitFunctionDeclaration(FunctionDeclaration node) {
428 Instantiator makeName = visit(node.name);
429 Instantiator makeFunction = visit(node.function);
430 return (arguments) =>
431 new FunctionDeclaration(makeName(arguments), makeFunction(arguments));
432 }
433
434 Instantiator visitLabeledStatement(LabeledStatement node) =>
435 TODO('visitLabeledStatement');
436 Instantiator visitLiteralStatement(LiteralStatement node) =>
437 TODO('visitLiteralStatement');
438 Instantiator visitBlob(Blob node) =>
439 TODO('visitBlob');
440 Instantiator visitLiteralExpression(LiteralExpression node) =>
441 TODO('visitLiteralExpression');
442
443 Instantiator visitVariableDeclarationList(VariableDeclarationList node) {
444 List<Instantiator> declarationMakers = node.declarations.map(visit).toList() ;
floitsch 2014/04/22 16:11:18 long line.
445 return (arguments) {
446 List<VariableInitialization> declarations = <VariableInitialization>[];
447 for (Instantiator instantiator in declarationMakers) {
448 var result = instantiator(arguments);
449 declarations.add(result);
450 }
451 return new VariableDeclarationList(declarations);
452 };
453 }
454
455 Instantiator visitSequence(Sequence node) => TODO('visitSequence');
456
457 Instantiator visitAssignment(Assignment node) {
458 Instantiator makeLeftHandSide = visit(node.leftHandSide);
459 Instantiator makeCompoundTarget = visitNullable(node.compoundTarget);
460 Instantiator makeValue = visitNullable(node.value);
461 return (arguments) {
462 return new Assignment._internal(
463 makeLeftHandSide(arguments),
464 makeCompoundTarget(arguments),
465 makeValue(arguments));
466 };
467 }
468
469 Instantiator visitVariableInitialization(VariableInitialization node) {
470 Instantiator makeDeclaration = visit(node.declaration);
471 Instantiator makeValue = visitNullable(node.value);
472 return (arguments) {
473 return new VariableInitialization(
474 makeDeclaration(arguments), makeValue(arguments));
475 };
476 }
477
478 Instantiator visitConditional(Conditional cond) {
479 Instantiator makeCondition = visit(cond.condition);
480 Instantiator makeThen = visit(cond.then);
481 Instantiator makeOtherwise = visit(cond.otherwise);
482 return (arguments) => new Conditional(
483 makeCondition(arguments),
484 makeThen(arguments),
485 makeOtherwise(arguments));
486 }
487
488 Instantiator visitNew(New node) =>
489 handleCallOrNew(node, (target, arguments) => new New(target, arguments));
490
491 Instantiator visitCall(Call node) =>
492 handleCallOrNew(node, (target, arguments) => new Call(target, arguments));
493
494 Instantiator handleCallOrNew(Call node, finish(target, arguments)) {
495 Instantiator makeTarget = visit(node.target);
496 Iterable<Instantiator> argumentMakers =
497 node.arguments.map(visitSplayableExpression).toList();
498
499 return (arguments) {
500 Node target = makeTarget(arguments);
501 List<Expression> callArguments = <Expression>[];
502 for (Instantiator instantiator in argumentMakers) {
503 var result = instantiator(arguments);
504 if (result is Iterable) {
505 callArguments.addAll(result);
506 } else {
507 callArguments.add(result);
508 }
509 }
510 return finish(target, callArguments);
511 };
512 }
513
514 Instantiator visitBinary(Binary node) {
515 Instantiator makeLeft = visit(node.left);
516 Instantiator makeRight = visit(node.right);
517 String op = node.op;
518 return (arguments) =>
519 new Binary(op, makeLeft(arguments), makeRight(arguments));
520 }
521
522 Instantiator visitPrefix(Prefix node) {
523 Instantiator makeOperand = visit(node.argument);
524 String op = node.op;
525 return (arguments) => new Prefix(op, makeOperand(arguments));
526 }
527
528 Instantiator visitPostfix(Postfix node) {
529 Instantiator makeOperand = visit(node.argument);
530 String op = node.op;
531 return (arguments) => new Postfix(op, makeOperand(arguments));
532 }
533
534 Instantiator visitVariableUse(VariableUse node) =>
535 (arguments) => new VariableUse(node.name);
536
537 Instantiator visitThis(This node) => (arguments) => new This();
538
539 Instantiator visitVariableDeclaration(VariableDeclaration node) =>
540 (arguments) => new VariableDeclaration(node.name);
541
542 Instantiator visitParameter(Parameter node) =>
543 (arguments) => new Parameter(node.name);
544
545 Instantiator visitAccess(PropertyAccess node) {
546 Instantiator makeReceiver = visit(node.receiver);
547 Instantiator makeSelector = visit(node.selector);
548 return (arguments) =>
549 new PropertyAccess(makeReceiver(arguments), makeSelector(arguments));
550 }
551
552 Instantiator visitNamedFunction(NamedFunction node) {
553 Instantiator makeDeclaration = visit(node.name);
554 Instantiator makeFunction = visit(node.function);
555 return (arguments) =>
556 new NamedFunction(makeDeclaration(arguments), makeFunction(arguments));
557 }
558
559 Instantiator visitFun(Fun node) {
560 List<Instantiator> paramMakers = node.params.map(visitSplayable).toList();
561 Instantiator makeBody = visit(node.body);
562 // TODO(sra): Avoid copying params if no interpolation or forced copying.
563 return (arguments) {
564 List<Parameter> params = <Parameter>[];
565 for (Instantiator instantiator in paramMakers) {
566 var result = instantiator(arguments);
567 if (result is Iterable) {
568 params.addAll(result);
569 } else {
570 params.add(result);
571 }
572 }
573 Statement body = makeBody(arguments);
574 return new Fun(params, body);
575 };
576 }
577
578 Instantiator visitLiteralBool(LiteralBool node) =>
579 (arguments) => new LiteralBool(node.value);
580
581 Instantiator visitLiteralString(LiteralString node) =>
582 (arguments) => new LiteralString(node.value);
583
584 Instantiator visitLiteralNumber(LiteralNumber node) =>
585 (arguments) => new LiteralNumber(node.value);
586
587 Instantiator visitLiteralNull(LiteralNull node) =>
588 (arguments) => new LiteralNull();
589
590 Instantiator visitArrayInitializer(ArrayInitializer node) {
591 // Assume array has no missing elements.
592 // TODO(sra): Splicing?
593 List<Instantiator> elementMakers = node.elements
594 .map((ArrayElement element) => visit(element.value))
595 .toList();
596 return (arguments) {
597 List<ArrayElement> elements = <ArrayElement>[];
598 void add(Expression value) {
599 elements.add(new ArrayElement(elements.length, value));
600 }
601 for (Instantiator instantiator in elementMakers) {
602 var result = instantiator(arguments);
603 add(result);
604 }
605 return new ArrayInitializer(elements.length, elements);
606 };
607 }
608
609 Instantiator visitArrayElement(ArrayElement node) {
610 throw 'Should not get here'; // Handled in visitArrayInitializer.
floitsch 2014/04/22 16:11:18 make this an internal error.
sra1 2014/04/23 02:33:50 Is there a way to signal an internal error without
floitsch 2014/04/23 07:29:41 Apparently not :( If you have a location you coul
ahe 2014/04/23 08:38:15 If you don't have a location, just throw an except
611 }
612
613 Instantiator visitObjectInitializer(ObjectInitializer node) {
614 List<Instantiator> propertyMakers =
615 node.properties.map(visitSplayable).toList();
616 bool isOneLiner = node.isOneLiner;
617 return (arguments) {
618 List<Property> properties = <Property>[];
619 for (Instantiator instantiator in propertyMakers) {
620 var result = instantiator(arguments);
621 if (result is Iterable) {
622 properties.addAll(result);
623 } else {
624 properties.add(result);
625 }
626 }
627 return new ObjectInitializer(properties, isOneLiner: isOneLiner);
628 };
629 }
630
631 Instantiator visitProperty(Property node) {
632 Instantiator makeName = visit(node.name);
633 Instantiator makeValue = visit(node.value);
634 return (arguments) {
635 return new Property(makeName(arguments), makeValue(arguments));
636 };
637 }
638
639 Instantiator visitRegExpLiteral(RegExpLiteral node) =>
640 (arguments) => new RegExpLiteral(node.pattern);
641
642 Instantiator visitComment(Comment node) => TODO('visitComment');
643 }
644
645 /**
646 * InterpolatedNodeAnalysis extract [InterpolatedNode]s from AST.
647 */
648 class InterpolatedNodeAnalysis extends BaseVisitor {
649 final Set<Node> containsInterpolatedNode = new Set<Node>();
650 final List<InterpolatedNode> interpolatedNodes = <InterpolatedNode>[];
651 int count = 0;
652
653 InterpolatedNodeAnalysis();
654
655 bool containsInterpolatedNodes(Node node) =>
656 containsInterpolatedNode.contains(node);
657
658 void visit(Node node) {
659 node.accept(this);
660 }
661
662 void visitNode(Node node) {
663 int before = count;
664 node.visitChildren(this);
665 if (count != before) containsInterpolatedNode.add(node);
666 return null;
667 }
668
669 visitInterpolatedNode(InterpolatedNode node) {
670 interpolatedNodes.add(node);
671 containsInterpolatedNode.add(node);
672 ++count;
673 }
674 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698