All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ibm.wala.cast.js.translator.RhinoToAstTranslator Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2002 - 2006 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 */
package com.ibm.wala.cast.js.translator;

import com.ibm.wala.cast.ir.translator.TranslatorToCAst;
import com.ibm.wala.cast.js.html.MappedSourceModule;
import com.ibm.wala.cast.js.ipa.callgraph.JSSSAPropagationCallGraphBuilder;
import com.ibm.wala.cast.js.loader.JavaScriptLoader;
import com.ibm.wala.cast.js.types.JavaScriptTypes;
import com.ibm.wala.cast.tree.CAst;
import com.ibm.wala.cast.tree.CAstAnnotation;
import com.ibm.wala.cast.tree.CAstControlFlowMap;
import com.ibm.wala.cast.tree.CAstEntity;
import com.ibm.wala.cast.tree.CAstNode;
import com.ibm.wala.cast.tree.CAstNodeTypeMap;
import com.ibm.wala.cast.tree.CAstQualifier;
import com.ibm.wala.cast.tree.CAstSourcePositionMap;
import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position;
import com.ibm.wala.cast.tree.CAstType;
import com.ibm.wala.cast.tree.impl.CAstOperator;
import com.ibm.wala.cast.tree.impl.CAstSymbolImpl;
import com.ibm.wala.cast.tree.impl.RangePosition;
import com.ibm.wala.cast.tree.rewrite.CAstRewriter.CopyKey;
import com.ibm.wala.cast.tree.rewrite.CAstRewriter.RewriteContext;
import com.ibm.wala.cast.tree.rewrite.CAstRewriterFactory;
import com.ibm.wala.cast.tree.visit.CAstVisitor;
import com.ibm.wala.cast.util.CAstPattern;
import com.ibm.wala.classLoader.ModuleEntry;
import com.ibm.wala.classLoader.SourceModule;
import com.ibm.wala.core.util.warnings.Warning;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.mozilla.javascript.CompilerEnvirons;
import org.mozilla.javascript.ErrorReporter;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.Kit;
import org.mozilla.javascript.Node;
import org.mozilla.javascript.Parser;
import org.mozilla.javascript.Token;
import org.mozilla.javascript.ast.ArrayComprehension;
import org.mozilla.javascript.ast.ArrayComprehensionLoop;
import org.mozilla.javascript.ast.ArrayLiteral;
import org.mozilla.javascript.ast.Assignment;
import org.mozilla.javascript.ast.AstNode;
import org.mozilla.javascript.ast.AstRoot;
import org.mozilla.javascript.ast.Block;
import org.mozilla.javascript.ast.BreakStatement;
import org.mozilla.javascript.ast.CatchClause;
import org.mozilla.javascript.ast.Comment;
import org.mozilla.javascript.ast.ConditionalExpression;
import org.mozilla.javascript.ast.ContinueStatement;
import org.mozilla.javascript.ast.DoLoop;
import org.mozilla.javascript.ast.ElementGet;
import org.mozilla.javascript.ast.EmptyExpression;
import org.mozilla.javascript.ast.EmptyStatement;
import org.mozilla.javascript.ast.ErrorNode;
import org.mozilla.javascript.ast.ExpressionStatement;
import org.mozilla.javascript.ast.ForInLoop;
import org.mozilla.javascript.ast.ForLoop;
import org.mozilla.javascript.ast.FunctionCall;
import org.mozilla.javascript.ast.FunctionNode;
import org.mozilla.javascript.ast.IfStatement;
import org.mozilla.javascript.ast.InfixExpression;
import org.mozilla.javascript.ast.Jump;
import org.mozilla.javascript.ast.KeywordLiteral;
import org.mozilla.javascript.ast.Label;
import org.mozilla.javascript.ast.LabeledStatement;
import org.mozilla.javascript.ast.LetNode;
import org.mozilla.javascript.ast.Name;
import org.mozilla.javascript.ast.NewExpression;
import org.mozilla.javascript.ast.NumberLiteral;
import org.mozilla.javascript.ast.ObjectLiteral;
import org.mozilla.javascript.ast.ObjectProperty;
import org.mozilla.javascript.ast.ParenthesizedExpression;
import org.mozilla.javascript.ast.PropertyGet;
import org.mozilla.javascript.ast.RegExpLiteral;
import org.mozilla.javascript.ast.ReturnStatement;
import org.mozilla.javascript.ast.Scope;
import org.mozilla.javascript.ast.ScriptNode;
import org.mozilla.javascript.ast.StringLiteral;
import org.mozilla.javascript.ast.SwitchCase;
import org.mozilla.javascript.ast.SwitchStatement;
import org.mozilla.javascript.ast.Symbol;
import org.mozilla.javascript.ast.ThrowStatement;
import org.mozilla.javascript.ast.TryStatement;
import org.mozilla.javascript.ast.UnaryExpression;
import org.mozilla.javascript.ast.UpdateExpression;
import org.mozilla.javascript.ast.VariableDeclaration;
import org.mozilla.javascript.ast.VariableInitializer;
import org.mozilla.javascript.ast.WhileLoop;
import org.mozilla.javascript.ast.WithStatement;
import org.mozilla.javascript.ast.XmlDotQuery;
import org.mozilla.javascript.ast.XmlElemRef;
import org.mozilla.javascript.ast.XmlExpression;
import org.mozilla.javascript.ast.XmlFragment;
import org.mozilla.javascript.ast.XmlLiteral;
import org.mozilla.javascript.ast.XmlMemberGet;
import org.mozilla.javascript.ast.XmlPropRef;
import org.mozilla.javascript.ast.XmlRef;
import org.mozilla.javascript.ast.XmlString;
import org.mozilla.javascript.ast.Yield;

public class RhinoToAstTranslator implements TranslatorToCAst {

  /**
   * a dummy name to use for standard function calls, only used to distinguish them from constructor
   * calls
   */
  public static final String STANDARD_CALL_FN_NAME = "do";

  /** name used for constructor calls, used to distinguish them from standard function calls */
  public static final String CTOR_CALL_FN_NAME = "ctor";

  private final boolean DEBUG = false;

  /**
   * shared interface for all objects storing contextual information during the Rhino AST traversal
   */
  private interface WalkContext extends JavaScriptTranslatorToCAst.WalkContext {}

  /** default implementation of WalkContext; methods do nothing / return null */
  private static class RootContext extends JavaScriptTranslatorToCAst.RootContext
      implements WalkContext {}

  /** context used for function / script declarations */
  private static class FunctionContext
      extends JavaScriptTranslatorToCAst.FunctionContext implements WalkContext {
    FunctionContext(WalkContext parent, Node s) {
      super(parent, s);
    }
  }

  /** context used for top-level script declarations */
  private static class ScriptContext extends FunctionContext {
    private final String script;

    ScriptContext(WalkContext parent, ScriptNode s, String script) {
      super(parent, s);
      this.script = script;
    }

    @Override
    public String script() {
      return script;
    }
  }

  private static class MemberDestructuringContext
      extends JavaScriptTranslatorToCAst.MemberDestructuringContext
      implements WalkContext {

    protected MemberDestructuringContext(
        WalkContext parent, Node initialBaseFor, int operationIndex) {
      super(parent, initialBaseFor, operationIndex);
    }
  }

  private static class BreakContext
      extends JavaScriptTranslatorToCAst.BreakContext implements WalkContext {

    @Override
    public WalkContext getParent() {
      return (WalkContext) super.getParent();
    }

    BreakContext(WalkContext parent, Node breakTo, String label) {
      super(parent, breakTo, label);
    }
  }

  private static class LoopContext extends TranslatorToCAst.LoopContext
      implements WalkContext {

    LoopContext(WalkContext parent, Node breakTo, Node continueTo, String label) {
      super(parent, breakTo, continueTo, label);
    }

    @Override
    public WalkContext getParent() {
      return (WalkContext) super.getParent();
    }
  }

  private static class TryCatchContext extends TranslatorToCAst.TryCatchContext
      implements WalkContext {

    TryCatchContext(WalkContext parent, CAstNode catchNode) {
      super(parent, catchNode);
    }

    @Override
    public WalkContext getParent() {
      return (WalkContext) super.getParent();
    }
  }

  private static String operationReceiverName(int operationIndex) {
    return "$$destructure$rcvr" + operationIndex;
  }

  private CAstNode operationReceiverVar(int operationIndex) {
    return Ast.makeNode(CAstNode.VAR, Ast.makeConstant(operationReceiverName(operationIndex)));
  }

  private static String operationElementName(int operationIndex) {
    return "$$destructure$elt" + operationIndex;
  }

  private CAstNode operationElementVar(int operationIndex) {
    return Ast.makeNode(CAstNode.VAR, Ast.makeConstant(operationElementName(operationIndex)));
  }

  private static CAstNode translateOpcode(int nodeType) {
    switch (nodeType) {
      case Token.POS:
      case Token.ADD:
      case Token.ASSIGN_ADD:
        return CAstOperator.OP_ADD;
      case Token.DIV:
      case Token.ASSIGN_DIV:
        return CAstOperator.OP_DIV;
      case Token.ASSIGN_LSH:
      case Token.LSH:
        return CAstOperator.OP_LSH;
      case Token.MOD:
      case Token.ASSIGN_MOD:
        return CAstOperator.OP_MOD;
      case Token.MUL:
      case Token.ASSIGN_MUL:
        return CAstOperator.OP_MUL;
      case Token.RSH:
      case Token.ASSIGN_RSH:
        return CAstOperator.OP_RSH;
      case Token.SUB:
      case Token.NEG:
      case Token.ASSIGN_SUB:
        return CAstOperator.OP_SUB;
      case Token.URSH:
      case Token.ASSIGN_URSH:
        return CAstOperator.OP_URSH;
      case Token.BITAND:
      case Token.ASSIGN_BITAND:
        return CAstOperator.OP_BIT_AND;
      case Token.BITOR:
      case Token.ASSIGN_BITOR:
        return CAstOperator.OP_BIT_OR;
      case Token.BITXOR:
      case Token.ASSIGN_BITXOR:
        return CAstOperator.OP_BIT_XOR;

      case Token.EQ:
      case Token.IFEQ:
        return CAstOperator.OP_EQ;
      case Token.SHEQ:
        return CAstOperator.OP_STRICT_EQ;
      case Token.GE:
        return CAstOperator.OP_GE;
      case Token.GT:
        return CAstOperator.OP_GT;
      case Token.LE:
        return CAstOperator.OP_LE;
      case Token.LT:
        return CAstOperator.OP_LT;
      case Token.NE:
      case Token.IFNE:
        return CAstOperator.OP_NE;
      case Token.SHNE:
        return CAstOperator.OP_STRICT_NE;

      case Token.BITNOT:
        return CAstOperator.OP_BITNOT;
      case Token.NOT:
        return CAstOperator.OP_NOT;

      default:
        Assertions.UNREACHABLE();
        return null;
    }
  }

  private CAstNode makeBuiltinNew(String typeName) {
    return Ast.makeNode(CAstNode.NEW, Ast.makeConstant(typeName));
  }

  private CAstNode handleNew(WalkContext context, String globalName, List arguments) {
    return handleNew(context, readName(context, null, globalName), arguments);
  }

  private CAstNode handleNew(WalkContext context, CAstNode value, List arguments) {
    return makeCtorCall(value, arguments, context);
  }

  private static boolean isPrologueScript(WalkContext context) {
    return JavaScriptLoader.bootstrapFileNames.contains(context.script());
  }

  private static Node getCallTarget(FunctionCall n) {
    return n.getTarget();
  }

  /** is n a call to "primitive" within our synthetic modeling code? */
  private static boolean isPrimitiveCall(WalkContext context, FunctionCall n) {
    return isPrologueScript(context)
        && n.getType() == Token.CALL
        && getCallTarget(n).getType() == Token.NAME
        && getCallTarget(n).getString().equals("primitive");
  }

  private static Node getNewTarget(NewExpression n) {
    return n.getTarget();
  }

  private static boolean isPrimitiveCreation(WalkContext context, NewExpression n) {
    Node target = getNewTarget(n);
    return isPrologueScript(context)
        && n.getType() == Token.NEW
        && target.getType() == Token.NAME
        && target.getString().equals("Primitives");
  }

  private CAstNode makeCall(
      CAstNode fun, CAstNode thisptr, List args, WalkContext context) {
    return makeCall(fun, thisptr, args, context, STANDARD_CALL_FN_NAME);
  }

  private CAstNode makeCtorCall(CAstNode thisptr, List args, WalkContext context) {
    return makeCall(thisptr, null, args, context, CTOR_CALL_FN_NAME);
  }

  private CAstNode makeCall(
      CAstNode fun, CAstNode thisptr, List args, WalkContext context, String callee) {
    int children = (args == null) ? 0 : args.size();

    // children of CAst CALL node are the expression that evaluates to the
    // function, followed by a name (either STANDARD_CALL_FN_NAME or
    // CTOR_CALL_FN_NAME), followed by the actual
    // parameters
    int nargs = (thisptr == null) ? children + 2 : children + 3;
    List arguments = new ArrayList<>(nargs);
    arguments.add(fun);
    // assert callee.equals(STANDARD_CALL_FN_NAME) || callee.equals(CTOR_CALL_FN_NAME);
    arguments.add(Ast.makeConstant(callee));
    if (thisptr != null) arguments.add(thisptr);
    if (args != null) {
      arguments.addAll(args);
    }

    CAstNode call = Ast.makeNode(CAstNode.CALL, arguments);

    context.cfg().map(call, call);
    if (context.getCatchTarget() != null) {
      context.cfg().add(call, context.getCatchTarget(), null);
    }

    return call;
  }

  /** Used to represent a script or function in the CAst; see walkEntity(). */
  private class ScriptOrFnEntity implements CAstEntity {
    private final String[] arguments;

    private final String name;

    private final int kind;

    private final Map> subs;

    private final CAstNode ast;

    private final CAstControlFlowMap map;

    private final CAstSourcePositionMap pos;

    private final Position entityPosition;

    private final Position namePosition;

    private final Position[] paramPositions;

    private ScriptOrFnEntity(
        AstNode n,
        Map> subs,
        CAstNode ast,
        CAstControlFlowMap map,
        CAstSourcePositionMap pos,
        String name) {
      this.name = name;
      this.entityPosition = pos.getPosition(ast);

      if (n instanceof FunctionNode) {
        FunctionNode f = (FunctionNode) n;
        namePosition = makePosition(f.getFunctionName());
        f.flattenSymbolTable(false);
        int i = 0;
        // The name of the function is declared within the scope of the function itself if it is a
        // function expression.  Otherwise, the name is declared in the same scope as the function
        // declaration.
        boolean isFunctionExpression =
            f.getFunctionType() == FunctionNode.FUNCTION_EXPRESSION
                || f.getFunctionType() == FunctionNode.FUNCTION_EXPRESSION_STATEMENT;
        arguments = new String[f.getParamCount() + 2];
        if (isFunctionExpression) {
          arguments[i++] = name;
        } else {
          // Obfuscate the name, so any references within the method to the actual function name
          // become lexical accesses.  We still need to keep the argument since in the WALA IR for
          // JavaScript calls, the function value itself is always passed as the first argument.
          arguments[i++] = "__WALA__int3rnal__fn__" + name;
        }
        arguments[i++] = "this";
        for (int j = 0; j < f.getParamCount(); j++) {
          arguments[i++] = f.getParamOrVarName(j);
        }

        List params = f.getParams();
        paramPositions = new Position[params.size()];
        for (int pi = 0; pi < params.size(); pi++) {
          paramPositions[pi] = makePosition(params.get(pi));
        }

      } else {
        paramPositions = new Position[0];
        arguments = new String[0];
        namePosition = null;
      }
      kind = (n instanceof FunctionNode) ? CAstEntity.FUNCTION_ENTITY : CAstEntity.SCRIPT_ENTITY;
      this.subs = subs;
      this.ast = ast;
      this.map = map;
      this.pos = pos;
    }

    @Override
    public String toString() {
      return "';
    }

    @Override
    public String getName() {
      return name;
    }

    @Override
    public String getSignature() {
      Assertions.UNREACHABLE();
      return null;
    }

    @Override
    public int getKind() {
      return kind;
    }

    @Override
    public String[] getArgumentNames() {
      return arguments;
    }

    @Override
    public CAstNode[] getArgumentDefaults() {
      return new CAstNode[0];
    }

    @Override
    public int getArgumentCount() {
      return arguments.length;
    }

    @Override
    public Map> getAllScopedEntities() {
      return Collections.unmodifiableMap(subs);
    }

    @Override
    public Iterator getScopedEntities(CAstNode construct) {
      if (subs.containsKey(construct)) return subs.get(construct).iterator();
      else return EmptyIterator.instance();
    }

    @Override
    public CAstNode getAST() {
      return ast;
    }

    @Override
    public CAstControlFlowMap getControlFlow() {
      return map;
    }

    @Override
    public CAstSourcePositionMap getSourceMap() {
      return pos;
    }

    @Override
    public CAstSourcePositionMap.Position getPosition() {
      return entityPosition;
    }

    @Override
    public CAstNodeTypeMap getNodeTypeMap() {
      return null;
    }

    @Override
    public Collection getAnnotations() {
      return null;
    }

    @Override
    public Collection getQualifiers() {
      Assertions.UNREACHABLE("JuliansUnnamedCAstEntity$2.getQualifiers()");
      return null;
    }

    @Override
    public CAstType getType() {
      return JSAstTranslator.Any;
    }

    @Override
    public Position getPosition(int arg) {
      return paramPositions[arg];
    }

    @Override
    public Position getNamePosition() {
      return namePosition;
    }
  }

  private CAstEntity walkEntity(
      final AstNode n, List body, String name, WalkContext child) {
    final List stmts;

    // add variable / constant / function declarations, if any
    if (!child.getNameDecls().isEmpty()) {
      // new first statement will be a block declaring all names.
      stmts = new ArrayList<>(body.size() + 1);
      stmts.add(
          child.getNameDecls().size() == 1
              ? child.getNameDecls().iterator().next()
              : Ast.makeNode(CAstNode.BLOCK_STMT, child.getNameDecls()));
      stmts.addAll(body);

    } else stmts = new ArrayList<>(body);

    final CAstNode ast = noteSourcePosition(child, Ast.makeNode(CAstNode.BLOCK_STMT, stmts), n);
    final CAstControlFlowMap map = child.cfg();
    final CAstSourcePositionMap pos = child.pos();

    // not sure if we need this copy --MS
    final Map> subs =
        HashMapFactory.make(child.getScopedEntities());

    return new ScriptOrFnEntity(n, subs, ast, map, pos, name);
  }

  private Position makePosition(AstNode n) {
    if (n != null) {
      URL url = ((SourceModule) sourceModule).getURL();
      int line = n.getLineno();
      Position pos =
          new RangePosition(
              url, line, n.getAbsolutePosition(), n.getAbsolutePosition() + n.getLength());

      if (sourceModule instanceof MappedSourceModule) {
        Position np = ((MappedSourceModule) sourceModule).getMapping().getIncludedPosition(pos);
        if (np != null) {
          return np;
        }
      }

      return pos;
    } else {
      return null;
    }
  }

  protected CAstNode noteSourcePosition(WalkContext context, CAstNode n, AstNode p) {
    if (p.getLineno() != -1 && context.pos().getPosition(n) == null) {
      pushSourcePosition(context, n, makePosition(p));
    }
    return n;
  }

  private CAstNode readName(WalkContext context, AstNode node, String name) {
    CAstNode cn = makeVarRef(name);
    if (node != null) {
      context.cfg().map(node, cn);
    } else {
      context.cfg().map(cn, cn);
    }
    CAstNode target = context.getCatchTarget();
    context
        .cfg()
        .add(
            cn,
            Objects.requireNonNullElse(target, CAstControlFlowMap.EXCEPTION_TO_EXIT),
            JavaScriptTypes.ReferenceError);
    return cn;
  }

  private static List




© 2015 - 2024 Weber Informatics LLC | Privacy Policy