Package com.google.caja.parser.js

Source Code of com.google.caja.parser.js.JsonMLConverter

// Copyright (C) 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      case http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.caja.parser.js;

import com.google.caja.lexer.FilePosition;
import com.google.caja.lexer.Keyword;
import com.google.caja.lexer.SourceBreaks;
import com.google.caja.parser.ParseTreeNode;
import com.google.caja.parser.ParseTreeNodes;
import com.google.caja.parser.ParserBase;
import com.google.caja.util.Lists;
import com.google.caja.util.Maps;
import com.google.javascript.jscomp.jsonml.JsonML;
import com.google.javascript.jscomp.jsonml.TagAttr;
import com.google.javascript.jscomp.jsonml.TagType;

import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
* Translates JsonML into a Caja parse tree.
*
* @author mikesamuel@gmail.com
*/
public final class JsonMLConverter {
  private static final Expression[] NO_EXPRS = new Expression[0];

  private final Map<String, SourceBreaks> breaksPerFile;

  public JsonMLConverter(Map<String, SourceBreaks> breaksPerFile) {
    this.breaksPerFile = Maps.newHashMap(breaksPerFile);
  }

  public ParseTreeNode toNode(JsonML jsonML) {
    return toNode(jsonML, ParseTreeNode.class);
  }

  public <T extends ParseTreeNode> T toNode(JsonML jsonML, Class<T> clazz) {
    ParseTreeNode node = toParseTreeNode(jsonML);
    if (Statement.class == clazz && node instanceof Expression) {
      node = new ExpressionStmt((Expression) node);
    }
    return clazz.cast(node);
  }

  private ParseTreeNode toParseTreeNode(JsonML jsonML) {
    FilePosition pos = positionFrom(jsonML);
    List<? extends JsonML> children = jsonML.getChildren();
    switch (jsonML.getType()) {
      case ArrayExpr: {
        List<Expression> elements = Lists.newArrayList(children.size());
        for (JsonML child : children) {
          if (child.getType() == TagType.Empty) {
            elements.add(new Elision(pos));
          } else {
            elements.add(toNode(child, Expression.class));
          }
        }
        return new ArrayConstructor(pos, elements);
      }
      case ObjectExpr:
        return new ObjectConstructor(pos, toNodes(children, ObjProperty.class));
      case AssignExpr: case BinaryExpr: case CountExpr: case MemberExpr:
      case UnaryExpr: {
        String symbol = (String) jsonML.getAttribute(TagAttr.OP);
        OperatorType opType;
        if ("()".equals(symbol) || "[]".equals(symbol)) {
          opType = OperatorType.BRACKET;
          symbol = symbol.substring(0, 1);
        } else {
          switch (children.size()) {
            case 1:
              opType = Boolean.FALSE.equals(
                  jsonML.getAttribute(TagAttr.IS_PREFIX))
                  ? OperatorType.POSTFIX : OperatorType.PREFIX;
              break;
            case 2: opType = OperatorType.INFIX; break;
            case 3: opType = OperatorType.TERNARY; break;
            default: throw new AssertionError();
          }
        }
        Operator op = Operator.lookupOperation(symbol, opType);
        Expression[] operands = toNodes(children, Expression.class)
            .toArray(NO_EXPRS);
        if (op == Operator.MEMBER_ACCESS) {
          String member = ((StringLiteral) operands[1]).getUnquotedValue();
          if (ParserBase.isJavascriptIdentifier(member)
              && !Keyword.isKeyword(member)) {
            operands[1] = new Reference(new Identifier(
                operands[1].getFilePosition(), member));
          } else {
            op = Operator.SQUARE_BRACKET;
          }
        }
        return Operation.create(pos, op, operands);
      }
      case CallExpr:
        return Operation.create(
            pos, Operator.FUNCTION_CALL,
            toNodes(children, Expression.class).toArray(NO_EXPRS));
      case ConditionalExpr:
        return Operation.create(
            pos, Operator.TERNARY,
            toNodes(children, Expression.class).toArray(NO_EXPRS));
      case DeleteExpr:
        return Operation.create(
            pos, Operator.DELETE,
            toNodes(children, Expression.class).toArray(NO_EXPRS));
      case EvalExpr: {
        Expression[] operands = new Expression[children.size() + 1];
        toNodes(children, Expression.class).toArray(operands);
        System.arraycopy(operands, 0, operands, 1, children.size());
        operands[0] = new Reference(
            new Identifier(FilePosition.startOf(pos), "eval"));
        return Operation.create(pos, Operator.FUNCTION_CALL, operands);
      }
      case InvokeExpr: {
        Expression obj = toNode(children.get(0), Expression.class);
        Expression key = toNode(children.get(1), Expression.class);
        Operator op = Operator.SQUARE_BRACKET;
        if (".".equals(jsonML.getAttribute(TagAttr.OP))) {
          String name = ((StringLiteral) key).getUnquotedValue();
          if (ParserBase.isJavascriptIdentifier(name)
              && !Keyword.isKeyword(name)) {
            key = new Reference(new Identifier(key.getFilePosition(), name));
            op = Operator.MEMBER_ACCESS;
          }
        }
        List<Expression> operands = Lists.newArrayList(children.size() - 1);
        operands.add(Operation.create(
            FilePosition.span(obj.getFilePosition(), key.getFilePosition()),
            op, obj, key));
        operands.addAll(toNodes(
            children.subList(2, children.size()), Expression.class));
        return Operation.create(
            pos, Operator.FUNCTION_CALL, operands.toArray(NO_EXPRS));
      }
      case LogicalAndExpr:
        return Operation.create(
            pos, Operator.LOGICAL_AND,
            toNodes(children, Expression.class).toArray(NO_EXPRS));
      case LogicalOrExpr:
        return Operation.create(
            pos, Operator.LOGICAL_OR,
            toNodes(children, Expression.class).toArray(NO_EXPRS));
      case NewExpr: {
        return Operation.create(
            pos, Operator.CONSTRUCTOR,
            toNodes(children, Expression.class).toArray(NO_EXPRS));
      }
      case TypeofExpr:
        return Operation.create(
            pos, Operator.TYPEOF,
            toNodes(children, Expression.class).toArray(NO_EXPRS));
      case LiteralExpr: {
        String type = (String) jsonML.getAttribute(TagAttr.TYPE);
        if ("string".equals(type)) {
          return new StringLiteral(
              pos,
              StringLiteral.toQuotedValue(
                  (String) jsonML.getAttribute(TagAttr.VALUE)));
        } else if ("boolean".equals(type)) {
          return new BooleanLiteral(
              pos, (Boolean) jsonML.getAttribute(TagAttr.VALUE));
        } else if ("number".equals(type)) {
          Number number = (Number) jsonML.getAttribute(TagAttr.VALUE);
          if (number instanceof Integer) {
            return new IntegerLiteral(pos, number.intValue());
          } else {
            return new RealLiteral(pos, number.doubleValue());
          }
        } else if ("null".equals(type)) {
          return new NullLiteral(pos);
        } else {
          throw new IllegalArgumentException(type);
        }
      }
      case RegExpExpr: {
        String flags = (String) jsonML.getAttribute(TagAttr.FLAGS);
        if (flags == null) { flags = ""; }
        String regex = "/" + jsonML.getAttribute(TagAttr.BODY) + "/" + flags;
        return new RegexpLiteral(pos, regex);
      }
      case ThisExpr:
        return new Reference(new Identifier(pos, "this"));
      case BlockStmt: case Program:
        return new Block(pos, toNodes(children, Statement.class));
      case BreakStmt: {
        String label = (String) jsonML.getAttribute(TagAttr.LABEL);
        if (label == null) { label = ""; }
        return new BreakStmt(pos, label);
      }
      case ContinueStmt: {
        String label = (String) jsonML.getAttribute(TagAttr.LABEL);
        if (label == null) { label = ""; }
        return new ContinueStmt(pos, label);
      }
      case DebuggerStmt:
        return new DebuggerStmt(pos);
      case DoWhileStmt:
        return new DoWhileLoop(
            pos, "", toNode(children.get(0), Statement.class),
            toNode(children.get(1), Expression.class));
      case EmptyStmt:
        return new Noop(pos);
      case ForInStmt:
        if (children.get(0).getType() == TagType.VarDecl) {
          return new ForEachLoop(
              pos, "", toNode(children.get(0), Declaration.class),
              toNode(children.get(1), Expression.class),
              toNode(children.get(2), Statement.class));
        } else {
          return new ForEachLoop(
              pos, "", toNode(children.get(0), Expression.class),
              toNode(children.get(1), Expression.class),
              toNode(children.get(2), Statement.class));
        }
      case ForStmt:
        return new ForLoop(
            pos, "",
            toNodeOrNoop(children.get(0)),
            toNodeOrTrue(children.get(1)),
            toNodeOrNoop(children.get(2)),
            toNode(children.get(3), Statement.class));
      case FunctionExpr:
        return toFunctionConstructor(jsonML);
      case IdExpr:
        return new Reference(
            new Identifier(pos, (String) jsonML.getAttribute(TagAttr.NAME)));
      case IfStmt: {
        Expression cond = toNode(children.get(0), Expression.class);
        Statement thenClause = toNode(children.get(1), Statement.class);
        Statement elseClause = children.get(2).getType() != TagType.EmptyStmt
            ? toNode(children.get(2), Statement.class) : null;
        List<ParseTreeNode> childNodes = Lists.newArrayList();
        childNodes.add(cond);
        childNodes.add(thenClause);
        if (elseClause != null) {
          if (elseClause instanceof Conditional) {
            childNodes.addAll(elseClause.children());
          } else {
            childNodes.add(elseClause);
          }
        }
        return new Conditional(pos, null, childNodes);
      }
      case LabelledStmt: {
        String label = (String) jsonML.getAttribute(TagAttr.LABEL);
        Statement body = toNode(children.get(0), Statement.class);
        if (body instanceof LabeledStatement
            && !(body instanceof LabeledStmtWrapper)) {
          return ParseTreeNodes.newNodeInstance(
              body.getClass(), body.getFilePosition(), label, body.children());
        } else {
          return new LabeledStmtWrapper(pos, label, body);
        }
      }
      case ReturnStmt:
        if (children.isEmpty()) {
          return new ReturnStmt(pos, null);
        } else {
          return new ReturnStmt(pos, toNode(children.get(0), Expression.class));
        }
      case SwitchStmt:
        return new SwitchStmt(
            pos, "", toNode(children.get(0), Expression.class),
            toNodes(children.subList(1, children.size()), SwitchCase.class));
      case ThrowStmt:
        return new ThrowStmt(pos, toNode(children.get(0), Expression.class));
      case TryStmt: {
        Block body = toNode(children.get(0), Block.class);
        CatchStmt catchStmt = null;
        FinallyStmt finallyStmt = null;
        int i = 1, n = children.size();
        if (children.get(i).getType() == TagType.CatchClause) {
          catchStmt = toNode(children.get(i++), CatchStmt.class);
        } else if (children.get(i).getType() == TagType.Empty) {
          ++i;
        }
        if (i < n) {
          Block block = toNode(children.get(i), Block.class);
          finallyStmt = new FinallyStmt(block.getFilePosition(), block);
        }
        return new TryStmt(pos, body, catchStmt, finallyStmt);
      }
      case WhileStmt:
        return new WhileLoop(
            pos, "", toNode(children.get(0), Expression.class),
            toNode(children.get(1), Statement.class));
      case WithStmt:
        return new WithStmt(
            pos, toNode(children.get(0), Expression.class),
            toNode(children.get(1), Statement.class));
      case FunctionDecl:
        return new FunctionDeclaration(toFunctionConstructor(jsonML));
      case ParamDecl:
        throw new IllegalStateException("Orphaned param");
      case PrologueDecl:
        throw new IllegalStateException("Orphaned prologue");
      case VarDecl: {
        List<Declaration> decls = Lists.newArrayList();
        for (JsonML child : children) {
          FilePosition childPos = positionFrom(child);
          if (child.getType() == TagType.InitPatt) {
            List<JsonML> initPattParts = child.getChildren();
            decls.add(new Declaration(
                childPos, toNode(initPattParts.get(0), Identifier.class),
                toNode(initPattParts.get(1), Expression.class)));
          } else {
            decls.add(new Declaration(
                childPos, toNode(child, Identifier.class), null));
          }
        }
        if (decls.size() == 1) {
          return decls.get(0);
        }
        return new MultiDeclaration(pos, decls);
      }
      case DataProp:
        return new ValueProperty(
            pos,
            StringLiteral.valueOf(
                FilePosition.span(pos, positionFrom(children.get(0))),
                (String) jsonML.getAttribute(TagAttr.NAME)),
            toNode(children.get(0), Expression.class));
      case GetterProp:
        return new GetterProperty(
            pos,
            StringLiteral.valueOf(
                FilePosition.span(pos, positionFrom(children.get(0))),
                (String) jsonML.getAttribute(TagAttr.NAME)),
            toFunctionConstructor(children.get(0)));
      case SetterProp:
        return new SetterProperty(
            pos,
            StringLiteral.valueOf(
                FilePosition.span(pos, positionFrom(children.get(0))),
                (String) jsonML.getAttribute(TagAttr.NAME)),
            toFunctionConstructor(children.get(0)));
      case IdPatt:
        return new Identifier(pos, (String) jsonML.getAttribute(TagAttr.NAME));
      case InitPatt:
        throw new IllegalStateException("Orphaned init patt");
      case Case: {
        Expression value = toNode(children.get(0), Expression.class);
        int n = children.size();
        List<Statement> body = toNodes(children.subList(1, n), Statement.class);
        FilePosition bodyPos = body.isEmpty()
            ? FilePosition.span(
                FilePosition.endOf(value.getFilePosition()), pos)
            : FilePosition.span(
                FilePosition.startOf(body.get(0).getFilePosition()), pos);
        return new CaseStmt(pos, value, new Block(bodyPos, body));
      }
      case DefaultCase: {
        List<Statement> body = toNodes(children, Statement.class);
        FilePosition bodyPos = body.isEmpty()
            ? pos
            : FilePosition.span(
                FilePosition.startOf(body.get(0).getFilePosition()), pos);
        return new DefaultCaseStmt(pos, new Block(bodyPos, body));
      }
      case CatchClause: {
        Identifier exName = toNode(children.get(0), Identifier.class);
        return new CatchStmt(
            pos, new Declaration(exName.getFilePosition(), exName, null),
            toNode(children.get(1), Block.class));
      }
      case Empty:
        throw new IllegalStateException("Orphaned empty");
    }
    return null;
  }

  private FilePosition positionFrom(JsonML jsonML) {
    SourceBreaks breaks = breaksPerFile.get(jsonML.getAttribute(TagAttr.SOURCE));
    if (breaks == null) { return FilePosition.UNKNOWN; }
    int pos = (Integer) jsonML.getAttribute(TagAttr.OPAQUE_POSITION);
    int start = pos >>> 16;
    return breaks.toFilePosition(start, (pos & 0xfff) + start);
  }

  private Expression toNodeOrTrue(JsonML jsonML) {
    if (jsonML.getType() == TagType.Empty) {
      return new BooleanLiteral(positionFrom(jsonML), true);
    } else {
      return toNode(jsonML, Expression.class);
    }
  }

  private Statement toNodeOrNoop(JsonML jsonML) {
    if (jsonML.getType() == TagType.Empty) {
      return new Noop(positionFrom(jsonML));
    } else {
      return toNode(jsonML, Statement.class);
    }
  }

  private <T extends ParseTreeNode> List<T> toNodes(
      List<? extends JsonML> jsonMLs, Class<T> clazz) {
    int n = jsonMLs.size();
    if (n == 0) { return Collections.emptyList(); }
    List<T> out = Lists.newArrayList(n);
    int i = 0;
    if (jsonMLs.get(0).getType() == TagType.PrologueDecl) {
      FilePosition start = null;
      FilePosition end;
      List<Directive> directives = Lists.newArrayList();
      do {
        JsonML decl = jsonMLs.get(i++);
        end = positionFrom(decl);
        if (start == null) { start = end; }
        directives.add(new Directive(
            end, (String) decl.getAttribute(TagAttr.DIRECTIVE)));
      } while (i < n && jsonMLs.get(i).getType() == TagType.PrologueDecl);
      DirectivePrologue directive = new DirectivePrologue(
          FilePosition.span(start, end), null, directives);
      out.add(clazz.cast(directive));
    }
    while (i < n) {
      out.add(toNode(jsonMLs.get(i++), clazz));
    }
    return out;
  }

  private FunctionConstructor toFunctionConstructor(JsonML jsonML) {
    FilePosition pos = positionFrom(jsonML);
    List<? extends JsonML> children = jsonML.getChildren();
    JsonML name = children.get(0);
    Identifier nameNode;
    if (name.getType() == TagType.Empty) {
      nameNode = new Identifier(positionFrom(name), null);
    } else {
      nameNode = toNode(name, Identifier.class);
    }
    List<FormalParam> params = Lists.newArrayList();
    for (JsonML param : children.get(1).getChildren()) {
      params.add(new FormalParam(toNode(param, Identifier.class)));
    }
    FilePosition blockEnd = FilePosition.endOf(pos);
    FilePosition blockStart = blockEnd;
    List<Statement> bodyParts = toNodes(
        children.subList(2, children.size()), Statement.class);
    if (!bodyParts.isEmpty()) {
      blockStart = bodyParts.get(0).getFilePosition();
    }
    Block body = new Block(FilePosition.span(blockStart, blockEnd), bodyParts);
    return new FunctionConstructor(pos, nameNode, params, body);
  }
}
TOP

Related Classes of com.google.caja.parser.js.JsonMLConverter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.