EMMA Coverage Report (generated Mon Nov 01 16:48:29 PDT 2010)
[all classes][com.google.caja.parser.js]

COVERAGE SUMMARY FOR SOURCE FILE [JsonMLConverter.java]

nameclass, %method, %block, %line, %
JsonMLConverter.java100% (2/2)91%  (10/11)92%  (1760/1920)92%  (193.3/211)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class JsonMLConverter100% (1/1)90%  (9/10)93%  (1449/1558)92%  (193.4/211)
<static initializer> 100% (1/1)100% (4/4)100% (1/1)
JsonMLConverter (Map): void 100% (1/1)100% (7/7)100% (3/3)
positionFrom (JsonML): FilePosition 100% (1/1)39%  (12/31)40%  (2/5)
toFunctionConstructor (JsonML): FunctionConstructor 100% (1/1)100% (98/98)100% (16/16)
toNode (JsonML): ParseTreeNode 0%   (0/1)0%   (0/5)0%   (0/1)
toNode (JsonML, Class): ParseTreeNode 100% (1/1)100% (21/21)100% (4/4)
toNodeOrNoop (JsonML): Statement 100% (1/1)59%  (10/17)67%  (2/3)
toNodeOrTrue (JsonML): Expression 100% (1/1)56%  (10/18)67%  (2/3)
toNodes (List, Class): List 100% (1/1)100% (90/90)100% (17/17)
toParseTreeNode (JsonML): ParseTreeNode 100% (1/1)94%  (1197/1267)93%  (146.4/158)
     
class JsonMLConverter$1100% (1/1)100% (1/1)86%  (311/362)85%  (0.8/1)
<static initializer> 100% (1/1)86%  (311/362)85%  (0.8/1)

1// Copyright (C) 2010 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      case http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14 
15package com.google.caja.parser.js;
16 
17import com.google.caja.lexer.FilePosition;
18import com.google.caja.lexer.Keyword;
19import com.google.caja.lexer.SourceBreaks;
20import com.google.caja.parser.ParseTreeNode;
21import com.google.caja.parser.ParseTreeNodes;
22import com.google.caja.parser.ParserBase;
23import com.google.caja.util.Lists;
24import com.google.caja.util.Maps;
25import com.google.javascript.jscomp.jsonml.JsonML;
26import com.google.javascript.jscomp.jsonml.TagAttr;
27import com.google.javascript.jscomp.jsonml.TagType;
28 
29import java.util.Collections;
30import java.util.List;
31import java.util.Map;
32 
33/**
34 * Translates JsonML into a Caja parse tree.
35 *
36 * @author mikesamuel@gmail.com
37 */
38public final class JsonMLConverter {
39  private static final Expression[] NO_EXPRS = new Expression[0];
40 
41  private final Map<String, SourceBreaks> breaksPerFile;
42 
43  public JsonMLConverter(Map<String, SourceBreaks> breaksPerFile) {
44    this.breaksPerFile = Maps.newHashMap(breaksPerFile);
45  }
46 
47  public ParseTreeNode toNode(JsonML jsonML) {
48    return toNode(jsonML, ParseTreeNode.class);
49  }
50 
51  public <T extends ParseTreeNode> T toNode(JsonML jsonML, Class<T> clazz) {
52    ParseTreeNode node = toParseTreeNode(jsonML);
53    if (Statement.class == clazz && node instanceof Expression) {
54      node = new ExpressionStmt((Expression) node);
55    }
56    return clazz.cast(node);
57  }
58 
59  private ParseTreeNode toParseTreeNode(JsonML jsonML) {
60    FilePosition pos = positionFrom(jsonML);
61    List<? extends JsonML> children = jsonML.getChildren();
62    switch (jsonML.getType()) {
63      case ArrayExpr: {
64        List<Expression> elements = Lists.newArrayList(children.size());
65        for (JsonML child : children) {
66          if (child.getType() == TagType.Empty) {
67            elements.add(new Elision(pos));
68          } else {
69            elements.add(toNode(child, Expression.class));
70          }
71        }
72        return new ArrayConstructor(pos, elements);
73      }
74      case ObjectExpr:
75        return new ObjectConstructor(pos, toNodes(children, ObjProperty.class));
76      case AssignExpr: case BinaryExpr: case CountExpr: case MemberExpr:
77      case UnaryExpr: {
78        String symbol = (String) jsonML.getAttribute(TagAttr.OP);
79        OperatorType opType;
80        if ("()".equals(symbol) || "[]".equals(symbol)) {
81          opType = OperatorType.BRACKET;
82          symbol = symbol.substring(0, 1);
83        } else {
84          switch (children.size()) {
85            case 1:
86              opType = Boolean.FALSE.equals(
87                  jsonML.getAttribute(TagAttr.IS_PREFIX))
88                  ? OperatorType.POSTFIX : OperatorType.PREFIX;
89              break;
90            case 2: opType = OperatorType.INFIX; break;
91            case 3: opType = OperatorType.TERNARY; break;
92            default: throw new AssertionError();
93          }
94        }
95        Operator op = Operator.lookupOperation(symbol, opType);
96        Expression[] operands = toNodes(children, Expression.class)
97            .toArray(NO_EXPRS);
98        if (op == Operator.MEMBER_ACCESS) {
99          String member = ((StringLiteral) operands[1]).getUnquotedValue();
100          if (ParserBase.isJavascriptIdentifier(member)
101              && !Keyword.isKeyword(member)) {
102            operands[1] = new Reference(new Identifier(
103                operands[1].getFilePosition(), member));
104          } else {
105            op = Operator.SQUARE_BRACKET;
106          }
107        }
108        return Operation.create(pos, op, operands);
109      }
110      case CallExpr:
111        return Operation.create(
112            pos, Operator.FUNCTION_CALL,
113            toNodes(children, Expression.class).toArray(NO_EXPRS));
114      case ConditionalExpr:
115        return Operation.create(
116            pos, Operator.TERNARY,
117            toNodes(children, Expression.class).toArray(NO_EXPRS));
118      case DeleteExpr:
119        return Operation.create(
120            pos, Operator.DELETE,
121            toNodes(children, Expression.class).toArray(NO_EXPRS));
122      case EvalExpr: {
123        Expression[] operands = new Expression[children.size() + 1];
124        toNodes(children, Expression.class).toArray(operands);
125        System.arraycopy(operands, 0, operands, 1, children.size());
126        operands[0] = new Reference(
127            new Identifier(FilePosition.startOf(pos), "eval"));
128        return Operation.create(pos, Operator.FUNCTION_CALL, operands);
129      }
130      case InvokeExpr: {
131        Expression obj = toNode(children.get(0), Expression.class);
132        Expression key = toNode(children.get(1), Expression.class);
133        Operator op = Operator.SQUARE_BRACKET;
134        if (".".equals(jsonML.getAttribute(TagAttr.OP))) {
135          String name = ((StringLiteral) key).getUnquotedValue();
136          if (ParserBase.isJavascriptIdentifier(name)
137              && !Keyword.isKeyword(name)) {
138            key = new Reference(new Identifier(key.getFilePosition(), name));
139            op = Operator.MEMBER_ACCESS;
140          }
141        }
142        List<Expression> operands = Lists.newArrayList(children.size() - 1);
143        operands.add(Operation.create(
144            FilePosition.span(obj.getFilePosition(), key.getFilePosition()),
145            op, obj, key));
146        operands.addAll(toNodes(
147            children.subList(2, children.size()), Expression.class));
148        return Operation.create(
149            pos, Operator.FUNCTION_CALL, operands.toArray(NO_EXPRS));
150      }
151      case LogicalAndExpr:
152        return Operation.create(
153            pos, Operator.LOGICAL_AND,
154            toNodes(children, Expression.class).toArray(NO_EXPRS));
155      case LogicalOrExpr:
156        return Operation.create(
157            pos, Operator.LOGICAL_OR,
158            toNodes(children, Expression.class).toArray(NO_EXPRS));
159      case NewExpr: {
160        int n = children.size();
161        Expression ctor = toNode(children.get(0), Expression.class);
162        List<Expression> operands = Lists.newArrayList(n);
163        operands.add(Operation.create(
164            ctor.getFilePosition(), Operator.CONSTRUCTOR, ctor));
165        operands.addAll(toNodes(children.subList(1, n), Expression.class));
166        return Operation.create(
167            pos, Operator.FUNCTION_CALL, operands.toArray(NO_EXPRS));
168      }
169      case TypeofExpr:
170        return Operation.create(
171            pos, Operator.TYPEOF,
172            toNodes(children, Expression.class).toArray(NO_EXPRS));
173      case LiteralExpr: {
174        String type = (String) jsonML.getAttribute(TagAttr.TYPE);
175        if ("string".equals(type)) {
176          return new StringLiteral(
177              pos,
178              StringLiteral.toQuotedValue(
179                  (String) jsonML.getAttribute(TagAttr.VALUE)));
180        } else if ("boolean".equals(type)) {
181          return new BooleanLiteral(
182              pos, (Boolean) jsonML.getAttribute(TagAttr.VALUE));
183        } else if ("number".equals(type)) {
184          Number number = (Number) jsonML.getAttribute(TagAttr.VALUE);
185          if (number instanceof Integer) {
186            return new IntegerLiteral(pos, number.intValue());
187          } else {
188            return new RealLiteral(pos, number.doubleValue());
189          }
190        } else if ("null".equals(type)) {
191          return new NullLiteral(pos);
192        } else {
193          throw new IllegalArgumentException(type);
194        }
195      }
196      case RegExpExpr: {
197        String flags = (String) jsonML.getAttribute(TagAttr.FLAGS);
198        if (flags == null) { flags = ""; }
199        String regex = "/" + jsonML.getAttribute(TagAttr.BODY) + "/" + flags;
200        return new RegexpLiteral(pos, regex);
201      }
202      case ThisExpr:
203        return new Reference(new Identifier(pos, "this"));
204      case BlockStmt: case Program:
205        return new Block(pos, toNodes(children, Statement.class));
206      case BreakStmt: {
207        String label = (String) jsonML.getAttribute(TagAttr.LABEL);
208        if (label == null) { label = ""; }
209        return new BreakStmt(pos, label);
210      }
211      case ContinueStmt: {
212        String label = (String) jsonML.getAttribute(TagAttr.LABEL);
213        if (label == null) { label = ""; }
214        return new ContinueStmt(pos, label);
215      }
216      case DebuggerStmt:
217        return new DebuggerStmt(pos);
218      case DoWhileStmt:
219        return new DoWhileLoop(
220            pos, "", toNode(children.get(0), Statement.class),
221            toNode(children.get(1), Expression.class));
222      case EmptyStmt:
223        return new Noop(pos);
224      case ForInStmt:
225        if (children.get(0).getType() == TagType.VarDecl) {
226          return new ForEachLoop(
227              pos, "", toNode(children.get(0), Declaration.class),
228              toNode(children.get(1), Expression.class),
229              toNode(children.get(2), Statement.class));
230        } else {
231          return new ForEachLoop(
232              pos, "", toNode(children.get(0), Expression.class),
233              toNode(children.get(1), Expression.class),
234              toNode(children.get(2), Statement.class));
235        }
236      case ForStmt:
237        return new ForLoop(
238            pos, "",
239            toNodeOrNoop(children.get(0)),
240            toNodeOrTrue(children.get(1)),
241            toNodeOrNoop(children.get(2)),
242            toNode(children.get(3), Statement.class));
243      case FunctionExpr:
244        return toFunctionConstructor(jsonML);
245      case IdExpr:
246        return new Reference(
247            new Identifier(pos, (String) jsonML.getAttribute(TagAttr.NAME)));
248      case IfStmt: {
249        Expression cond = toNode(children.get(0), Expression.class);
250        Statement thenClause = toNode(children.get(1), Statement.class);
251        Statement elseClause = children.get(2).getType() != TagType.EmptyStmt
252            ? toNode(children.get(2), Statement.class) : null;
253        List<ParseTreeNode> childNodes = Lists.newArrayList();
254        childNodes.add(cond);
255        childNodes.add(thenClause);
256        if (elseClause != null) {
257          if (elseClause instanceof Conditional) {
258            childNodes.addAll(elseClause.children());
259          } else {
260            childNodes.add(elseClause);
261          }
262        }
263        return new Conditional(pos, null, childNodes);
264      }
265      case LabelledStmt: {
266        String label = (String) jsonML.getAttribute(TagAttr.LABEL);
267        Statement body = toNode(children.get(0), Statement.class);
268        if (body instanceof LabeledStatement
269            && !(body instanceof LabeledStmtWrapper)) {
270          return ParseTreeNodes.newNodeInstance(
271              body.getClass(), body.getFilePosition(), label, body.children());
272        } else {
273          return new LabeledStmtWrapper(pos, label, body);
274        }
275      }
276      case ReturnStmt:
277        if (children.isEmpty()) {
278          return new ReturnStmt(pos, null);
279        } else {
280          return new ReturnStmt(pos, toNode(children.get(0), Expression.class));
281        }
282      case SwitchStmt:
283        return new SwitchStmt(
284            pos, "", toNode(children.get(0), Expression.class),
285            toNodes(children.subList(1, children.size()), SwitchCase.class));
286      case ThrowStmt:
287        return new ThrowStmt(pos, toNode(children.get(0), Expression.class));
288      case TryStmt: {
289        Block body = toNode(children.get(0), Block.class);
290        CatchStmt catchStmt = null;
291        FinallyStmt finallyStmt = null;
292        int i = 1, n = children.size();
293        if (children.get(i).getType() == TagType.CatchClause) {
294          catchStmt = toNode(children.get(i++), CatchStmt.class);
295        } else if (children.get(i).getType() == TagType.Empty) {
296          ++i;
297        }
298        if (i < n) {
299          Block block = toNode(children.get(i), Block.class);
300          finallyStmt = new FinallyStmt(block.getFilePosition(), block);
301        }
302        return new TryStmt(pos, body, catchStmt, finallyStmt);
303      }
304      case WhileStmt:
305        return new WhileLoop(
306            pos, "", toNode(children.get(0), Expression.class),
307            toNode(children.get(1), Statement.class));
308      case WithStmt:
309        return new WithStmt(
310            pos, toNode(children.get(0), Expression.class),
311            toNode(children.get(1), Statement.class));
312      case FunctionDecl:
313        return new FunctionDeclaration(toFunctionConstructor(jsonML));
314      case ParamDecl:
315        throw new IllegalStateException("Orphaned param");
316      case PrologueDecl:
317        throw new IllegalStateException("Orphaned prologue");
318      case VarDecl: {
319        List<Declaration> decls = Lists.newArrayList();
320        for (JsonML child : children) {
321          FilePosition childPos = positionFrom(child);
322          if (child.getType() == TagType.InitPatt) {
323            List<JsonML> initPattParts = child.getChildren();
324            decls.add(new Declaration(
325                childPos, toNode(initPattParts.get(0), Identifier.class),
326                toNode(initPattParts.get(1), Expression.class)));
327          } else {
328            decls.add(new Declaration(
329                childPos, toNode(child, Identifier.class), null));
330          }
331        }
332        if (decls.size() == 1) {
333          return decls.get(0);
334        }
335        return new MultiDeclaration(pos, decls);
336      }
337      case DataProp:
338        return new ValueProperty(
339            pos,
340            StringLiteral.valueOf(
341                FilePosition.span(pos, positionFrom(children.get(0))),
342                (String) jsonML.getAttribute(TagAttr.NAME)),
343            toNode(children.get(0), Expression.class));
344      case GetterProp:
345        return new GetterProperty(
346            pos,
347            StringLiteral.valueOf(
348                FilePosition.span(pos, positionFrom(children.get(0))),
349                (String) jsonML.getAttribute(TagAttr.NAME)),
350            toFunctionConstructor(children.get(0)));
351      case SetterProp:
352        return new SetterProperty(
353            pos,
354            StringLiteral.valueOf(
355                FilePosition.span(pos, positionFrom(children.get(0))),
356                (String) jsonML.getAttribute(TagAttr.NAME)),
357            toFunctionConstructor(children.get(0)));
358      case IdPatt:
359        return new Identifier(pos, (String) jsonML.getAttribute(TagAttr.NAME));
360      case InitPatt:
361        throw new IllegalStateException("Orphaned init patt");
362      case Case: {
363        Expression value = toNode(children.get(0), Expression.class);
364        int n = children.size();
365        List<Statement> body = toNodes(children.subList(1, n), Statement.class);
366        FilePosition bodyPos = body.isEmpty()
367            ? FilePosition.span(
368                FilePosition.endOf(value.getFilePosition()), pos)
369            : FilePosition.span(
370                FilePosition.startOf(body.get(0).getFilePosition()), pos);
371        return new CaseStmt(pos, value, new Block(bodyPos, body));
372      }
373      case DefaultCase: {
374        List<Statement> body = toNodes(children, Statement.class);
375        FilePosition bodyPos = body.isEmpty()
376            ? pos
377            : FilePosition.span(
378                FilePosition.startOf(body.get(0).getFilePosition()), pos);
379        return new DefaultCaseStmt(pos, new Block(bodyPos, body));
380      }
381      case CatchClause: {
382        Identifier exName = toNode(children.get(0), Identifier.class);
383        return new CatchStmt(
384            pos, new Declaration(exName.getFilePosition(), exName, null),
385            toNode(children.get(1), Block.class));
386      }
387      case Empty:
388        throw new IllegalStateException("Orphaned empty");
389    }
390    return null;
391  }
392 
393  private FilePosition positionFrom(JsonML jsonML) {
394    SourceBreaks breaks = breaksPerFile.get(jsonML.getAttribute(TagAttr.SOURCE));
395    if (breaks == null) { return FilePosition.UNKNOWN; }
396    int pos = (Integer) jsonML.getAttribute(TagAttr.OPAQUE_POSITION);
397    int start = pos >>> 16;
398    return breaks.toFilePosition(start, (pos & 0xfff) + start);
399  }
400 
401  private Expression toNodeOrTrue(JsonML jsonML) {
402    if (jsonML.getType() == TagType.Empty) {
403      return new BooleanLiteral(positionFrom(jsonML), true);
404    } else {
405      return toNode(jsonML, Expression.class);
406    }
407  }
408 
409  private Statement toNodeOrNoop(JsonML jsonML) {
410    if (jsonML.getType() == TagType.Empty) {
411      return new Noop(positionFrom(jsonML));
412    } else {
413      return toNode(jsonML, Statement.class);
414    }
415  }
416 
417  private <T extends ParseTreeNode> List<T> toNodes(
418      List<? extends JsonML> jsonMLs, Class<T> clazz) {
419    int n = jsonMLs.size();
420    if (n == 0) { return Collections.emptyList(); }
421    List<T> out = Lists.newArrayList(n);
422    int i = 0;
423    if (jsonMLs.get(0).getType() == TagType.PrologueDecl) {
424      FilePosition start = null;
425      FilePosition end;
426      List<Directive> directives = Lists.newArrayList();
427      do {
428        JsonML decl = jsonMLs.get(i++);
429        end = positionFrom(decl);
430        if (start == null) { start = end; }
431        directives.add(new Directive(
432            end, (String) decl.getAttribute(TagAttr.DIRECTIVE)));
433      } while (i < n && jsonMLs.get(i).getType() == TagType.PrologueDecl);
434      DirectivePrologue directive = new DirectivePrologue(
435          FilePosition.span(start, end), null, directives);
436      out.add(clazz.cast(directive));
437    }
438    while (i < n) {
439      out.add(toNode(jsonMLs.get(i++), clazz));
440    }
441    return out;
442  }
443 
444  private FunctionConstructor toFunctionConstructor(JsonML jsonML) {
445    FilePosition pos = positionFrom(jsonML);
446    List<? extends JsonML> children = jsonML.getChildren();
447    JsonML name = children.get(0);
448    Identifier nameNode;
449    if (name.getType() == TagType.Empty) {
450      nameNode = new Identifier(positionFrom(name), null);
451    } else {
452      nameNode = toNode(name, Identifier.class);
453    }
454    List<FormalParam> params = Lists.newArrayList();
455    for (JsonML param : children.get(1).getChildren()) {
456      params.add(new FormalParam(toNode(param, Identifier.class)));
457    }
458    FilePosition blockEnd = FilePosition.endOf(pos);
459    FilePosition blockStart = blockEnd;
460    List<Statement> bodyParts = toNodes(
461        children.subList(2, children.size()), Statement.class);
462    if (!bodyParts.isEmpty()) {
463      blockStart = bodyParts.get(0).getFilePosition();
464    }
465    Block body = new Block(FilePosition.span(blockStart, blockEnd), bodyParts);
466    return new FunctionConstructor(pos, nameNode, params, body);
467  }
468}

[all classes][com.google.caja.parser.js]
EMMA 2.0.5312 (C) Vladimir Roubtsov