287 lines
8.0 KiB
Java
287 lines
8.0 KiB
Java
/*******************************************************************************
|
|
* Copyright (c) 2013 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 java.util.Collection;
|
|
import java.util.HashSet;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.Vector;
|
|
|
|
import com.ibm.wala.cast.ir.translator.TranslatorToCAst;
|
|
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.impl.CAstControlFlowRecorder;
|
|
import com.ibm.wala.cast.tree.impl.CAstSourcePositionRecorder;
|
|
import com.ibm.wala.util.collections.HashMapFactory;
|
|
import com.ibm.wala.util.debug.Assertions;
|
|
|
|
public interface JavaScriptTranslatorToCAst extends TranslatorToCAst {
|
|
|
|
interface WalkContext<C extends WalkContext<C, T>, T> extends TranslatorToCAst.WalkContext<C, T> {
|
|
|
|
String script();
|
|
|
|
T top();
|
|
|
|
/**
|
|
* Add a name declaration to this context. For variables or constants, n
|
|
* should be a {@link CAstNode#DECL_STMT}, and the initialization of the
|
|
* variable (if any) may occur in a separate assignment. For functions, n
|
|
* should be a {@link CAstNode#FUNCTION_STMT}, including the function body.
|
|
*/
|
|
void addNameDecl(CAstNode n);
|
|
|
|
Collection<CAstNode> getNameDecls();
|
|
|
|
CAstNode getCatchTarget();
|
|
|
|
int setOperation(T node);
|
|
|
|
boolean foundMemberOperation(T node);
|
|
|
|
void copyOperation(T from, T to);
|
|
|
|
}
|
|
|
|
public static class RootContext<C extends WalkContext<C, T>, T> extends TranslatorToCAst.RootContext<C, T> implements WalkContext<C,T> {
|
|
|
|
public String script() { return null; }
|
|
|
|
public T top() {
|
|
Assertions.UNREACHABLE();
|
|
return null;
|
|
}
|
|
|
|
public void addNameDecl(CAstNode v) {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
|
|
public Collection<CAstNode> getNameDecls() {
|
|
Assertions.UNREACHABLE();
|
|
return null;
|
|
}
|
|
|
|
public CAstNode getCatchTarget() {
|
|
Assertions.UNREACHABLE();
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public int setOperation(T node) {
|
|
return -1;
|
|
}
|
|
|
|
@Override
|
|
public boolean foundMemberOperation(T node) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void copyOperation(T from, T to) {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
|
|
}
|
|
|
|
class DelegatingContext<C extends WalkContext<C, T>, T> extends TranslatorToCAst.DelegatingContext<C, T> implements WalkContext<C,T> {
|
|
|
|
protected DelegatingContext(C parent) {
|
|
super(parent);
|
|
}
|
|
|
|
public String script() {
|
|
return parent.script();
|
|
}
|
|
|
|
public T top() {
|
|
return parent.top();
|
|
}
|
|
|
|
public void addNameDecl(CAstNode n) {
|
|
parent.addNameDecl(n);
|
|
}
|
|
|
|
public Collection<CAstNode> getNameDecls() {
|
|
return parent.getNameDecls();
|
|
}
|
|
|
|
public CAstNode getCatchTarget() {
|
|
return parent.getCatchTarget();
|
|
}
|
|
|
|
@Override
|
|
public int setOperation(T node) {
|
|
return parent.setOperation(node);
|
|
}
|
|
|
|
@Override
|
|
public boolean foundMemberOperation(T node) {
|
|
return parent.foundMemberOperation(node);
|
|
}
|
|
|
|
@Override
|
|
public void copyOperation(T from, T to) {
|
|
parent.copyOperation(from, to);
|
|
}
|
|
|
|
}
|
|
|
|
public static class FunctionContext<C extends WalkContext<C, T>, T> extends DelegatingContext<C,T> {
|
|
private final T topNode;
|
|
private final CAstSourcePositionRecorder pos = new CAstSourcePositionRecorder();
|
|
private final CAstControlFlowRecorder cfg = new CAstControlFlowRecorder(pos);
|
|
private final Map<CAstNode, Collection<CAstEntity>> scopedEntities = HashMapFactory.make();
|
|
private final Vector<CAstNode> initializers = new Vector<CAstNode>();
|
|
|
|
protected FunctionContext(C parent, T s) {
|
|
super(parent);
|
|
this.topNode = s;
|
|
}
|
|
|
|
@Override
|
|
public T top() { return topNode; }
|
|
|
|
@Override
|
|
public CAstNode getCatchTarget() { return CAstControlFlowMap.EXCEPTION_TO_EXIT; }
|
|
|
|
@Override
|
|
public void addScopedEntity(CAstNode construct, CAstEntity e) {
|
|
if (! scopedEntities.containsKey(construct)) {
|
|
scopedEntities.put(construct, new HashSet<CAstEntity>(1));
|
|
}
|
|
scopedEntities.get(construct).add(e);
|
|
}
|
|
|
|
@Override
|
|
public Map<CAstNode, Collection<CAstEntity>> getScopedEntities() {
|
|
return scopedEntities;
|
|
}
|
|
|
|
@Override
|
|
public void addNameDecl(CAstNode v) { initializers.add(v); }
|
|
|
|
@Override
|
|
public Collection<CAstNode> getNameDecls() { return initializers; }
|
|
|
|
@Override
|
|
public CAstControlFlowRecorder cfg() { return cfg; }
|
|
|
|
@Override
|
|
public CAstSourcePositionRecorder pos() { return pos; }
|
|
}
|
|
|
|
public static class ScriptContext<C extends WalkContext<C, T>, T> extends FunctionContext<C,T> {
|
|
private final String script;
|
|
|
|
ScriptContext(C parent, T s, String script) {
|
|
super(parent, s);
|
|
this.script = script;
|
|
}
|
|
|
|
public String script() { return script; }
|
|
}
|
|
|
|
public static class TryCatchContext<C extends WalkContext<C, T>, T> extends DelegatingContext<C,T> {
|
|
private final CAstNode catchNode;
|
|
|
|
protected TryCatchContext(C parent, CAstNode catchNode) {
|
|
super(parent);
|
|
this.catchNode = catchNode;
|
|
}
|
|
|
|
public CAstNode getCatchTarget() { return catchNode; }
|
|
}
|
|
|
|
class BreakContext<C extends WalkContext<C, T>, T> extends DelegatingContext<C,T> {
|
|
private final T breakTarget;
|
|
protected final String label;
|
|
|
|
protected BreakContext(C parent, T breakTarget, String label) {
|
|
super(parent);
|
|
this.breakTarget = breakTarget;
|
|
this.label = label;
|
|
}
|
|
|
|
@Override
|
|
public T getBreakFor(String l) {
|
|
return (l == null || l.equals(label))? breakTarget: super.getBreakFor(l);
|
|
}
|
|
}
|
|
|
|
public class LoopContext<C extends WalkContext<C, T>, T> extends BreakContext<C,T> {
|
|
private final T continueTo;
|
|
|
|
protected LoopContext(C parent, T breakTo, T continueTo, String label) {
|
|
super(parent, breakTo, label);
|
|
this.continueTo = continueTo;
|
|
}
|
|
|
|
@Override
|
|
public T getContinueFor(String l) {
|
|
return (l == null || l.equals(label))? continueTo: super.getContinueFor(l);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Used to determine the value to be passed as the 'this' argument for a
|
|
* function call. This is needed since in JavaScript, you can write a call
|
|
* e(...) where e is some arbitrary expression, and in the case where e is a
|
|
* property access like e'.f, we must discover that the value of expression e'
|
|
* is passed as the 'this' parameter.
|
|
*
|
|
* The general strategy is to store the value of the expression passed as the
|
|
* 'this' parameter in baseVar, and then to use baseVar as the actual argument
|
|
* sub-node for the CAst call node
|
|
*/
|
|
public class MemberDestructuringContext<C extends WalkContext<C, T>, T> extends DelegatingContext<C,T> {
|
|
|
|
/**
|
|
* node for which we actually care about what the base pointer is. this
|
|
* helps to handle cases like x.y.f(), where we would like to store x.y in
|
|
* baseVar, but not x when we recurse.
|
|
*/
|
|
private final Set<T> baseFor = new HashSet<T>();
|
|
|
|
private int operationIndex;
|
|
|
|
/**
|
|
* have we discovered a value to be passed as the 'this' parameter?
|
|
*/
|
|
private boolean foundBase = false;
|
|
|
|
protected MemberDestructuringContext(C parent, T initialBaseFor, int operationIndex) {
|
|
super(parent);
|
|
baseFor.add( initialBaseFor );
|
|
this.operationIndex = operationIndex;
|
|
}
|
|
|
|
public int setOperation(T node) {
|
|
if (baseFor.contains( node )) {
|
|
foundBase = true;
|
|
return operationIndex;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
public boolean foundMemberOperation(T node) {
|
|
return foundBase;
|
|
}
|
|
|
|
public void copyOperation(T from, T to) {
|
|
if (baseFor.contains(from)) baseFor.add(to);
|
|
}
|
|
}
|
|
|
|
}
|