package org.rascalmpl.library.experiments.Compiler.RVM.Interpreter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.SoftReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;
import java.util.regex.Matcher;
import org.rascalmpl.debug.IRascalMonitor;
import org.rascalmpl.interpreter.IEvaluatorContext;
import org.rascalmpl.interpreter.ITestResultListener;
import org.rascalmpl.interpreter.asserts.ImplementationError;
import org.rascalmpl.interpreter.control_exceptions.Throw;
import org.rascalmpl.interpreter.result.util.MemoizationCache;
import org.rascalmpl.interpreter.types.DefaultRascalTypeVisitor;
import org.rascalmpl.interpreter.types.FunctionType;
import org.rascalmpl.interpreter.types.RascalType;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.java2rascal.RascalFunctionInvocationHandler;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.observers.IFrameObserver;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.observers.NullFrameObserver;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.traverse.DescendantDescriptor;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.traverse.Traverse;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.traverse.Traverse.DIRECTION;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.traverse.Traverse.FIXEDPOINT;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.traverse.Traverse.PROGRESS;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.traverse.Traverse.REBUILD;
import org.rascalmpl.value.IBool;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IDateTime;
import org.rascalmpl.value.IInteger;
import org.rascalmpl.value.IList;
import org.rascalmpl.value.IListWriter;
import org.rascalmpl.value.IMap;
import org.rascalmpl.value.IMapWriter;
import org.rascalmpl.value.INode;
import org.rascalmpl.value.INumber;
import org.rascalmpl.value.IRational;
import org.rascalmpl.value.IReal;
import org.rascalmpl.value.ISet;
import org.rascalmpl.value.ISetWriter;
import org.rascalmpl.value.ISourceLocation;
import org.rascalmpl.value.IString;
import org.rascalmpl.value.ITuple;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeFactory;
import org.rascalmpl.values.ValueFactoryFactory;
public abstract class RVMCore {
public final IValueFactory vf;
protected final TypeFactory tf;
protected final static IBool Rascal_TRUE = ValueFactoryFactory.getValueFactory().bool(true);
protected final static IBool Rascal_FALSE = ValueFactoryFactory.getValueFactory().bool(false);
protected final IString NOVALUE;
protected Function[] functionStore;
protected Map<String, Integer> functionMap;
protected ArrayList<Type> constructorStore;
protected final Map<String, Integer> constructorMap;
// Function overloading
protected final Map<String, Integer> resolver;
protected final OverloadedFunction[] overloadedStore;
private Map<String, OverloadedFunction> overloadedStoreMap;
protected IFrameObserver frameObserver;
public final static Function noCompanionFunction = new Function("noCompanionFunction", null, null, null, 0, 0, false, false, null, null, 0, false, 0, 0, null, null, 0);
public static final HashMap<String, IValue> emptyKeywordMap = new HashMap<>(0);
protected PrintWriter stdout;
protected PrintWriter stderr;
// Management of active coroutines
protected Stack<Coroutine> activeCoroutines = new Stack<>();
protected Frame ccf = null; // The start frame of the current active coroutine (coroutine's main function)
protected Frame cccf = null; // The candidate coroutine's start frame; used by the guard semantics
protected RascalExecutionContext rex;
public Map<IValue, IValue> moduleVariables;
List<ClassLoader> classLoaders;
private final Map<Class<?>, Object> instanceCache;
private final Map<String, Class<?>> classCache;
private final Types types;
public static RVMCore readFromFileAndInitialize(ISourceLocation rvmBinaryLocation, RascalExecutionContext rex) throws IOException{
RVMExecutable rvmExecutable = RVMExecutable.read(rvmBinaryLocation, rex.getTypeStore());
return ExecutionTools.initializedRVM(rvmExecutable, rex);
}
public static IValue readFromFileAndExecuteProgram(ISourceLocation rvmBinaryLocation, Map<String, IValue> keywordArguments, RascalExecutionContext rex) throws Exception{
RVMExecutable rvmExecutable = RVMExecutable.read(rvmBinaryLocation, rex.getTypeStore());
return ExecutionTools.executeProgram(rvmExecutable, keywordArguments, rex);
}
// An exhausted coroutine instance
public static Coroutine exhausted = new Coroutine(null) {
@Override
public void next(Frame previousCallFrame) {
throw new CompilerError("Attempt to activate an exhausted coroutine instance.");
}
@Override
public void suspend(Frame current) {
throw new CompilerError("Attempt to suspend an exhausted coroutine instance.");
}
@Override
public boolean isInitialized() {
return true;
}
@Override
public boolean hasNext() {
return false;
}
@Override
public Coroutine copy() {
throw new CompilerError("Attempt to copy an exhausted coroutine instance.");
}
};
public RVMCore(RVMExecutable rvmExec, RascalExecutionContext rex){
this.rex = rex;
rex.setRVM(this);
rex.setFullModuleName(rvmExec.getModuleName());
this.instanceCache = new HashMap<Class<?>, Object>();
this.classCache = new HashMap<String, Class<?>>();
this.classLoaders = rex.getClassLoaders();
this.vf = rex.getValueFactory();
tf = TypeFactory.getInstance();
this.stdout = rex.getStdOut();
this.stderr = rex.getStdErr();
NOVALUE = vf.string("$no-value$");
moduleVariables = new HashMap<IValue,IValue>();
this.functionStore = rvmExec.getFunctionStore();
this.functionMap = rvmExec.getFunctionMap();
this.resolver = rvmExec.getResolver();
this.overloadedStore = rvmExec.getOverloadedStore();
mappifyOverloadedStore();
this.constructorStore = rvmExec.getConstructorStore();
this.constructorMap = rvmExec.getConstructorMap();
this.types = new Types(vf);
IFrameObserver observer = rex.getFrameObserver();
this.frameObserver = (observer == null) ? NullFrameObserver.getInstance() : observer;
}
public void shutdown(){
frameObserver.report();
}
public Map<IValue, IValue> getModuleVariables() { return moduleVariables; }
public void updateModuleVariable(IValue name, IValue newVal){
IValue oldVal = moduleVariables.get(name);
if(oldVal != null && !oldVal.getType().comparable(newVal.getType())){
throw new CompilerError("Module variable " + name + " initalized with incompatible value " + newVal + " was " + oldVal);
}
moduleVariables.put(name, newVal);
}
void mappifyOverloadedStore(){
overloadedStoreMap = new HashMap<>();
for(OverloadedFunction of : overloadedStore){
String name = of.getName();
if(overloadedStoreMap.containsKey(name)){
OverloadedFunction previous = overloadedStoreMap.get(name);
if(of.getFunctions().length > previous.getFunctions().length){
overloadedStoreMap.put(name, of);
}
} else {
overloadedStoreMap.put(name, of);
}
}
}
/**
* Create an object which implements the provided interface (that is
* usually created by ApiGen.generate) by forwarding calls
* to its methods directly to Rascal functions.
*
* This works for interfaces which contain a specific kind of methods:
* * each method should have a name which maps to a Rascal function name which
* is loaded into the current RVMCore
* * the arity of the method should be equal to at least one of the Rascal
* functions with the given name
* * all arguments of the methods should have a type which is a sub-type of IValue
* * the return type of the methods should be either IValue or void
*
* @param interf the interface which is to be implemented
* @return an object which implements this interface by forwarding to Rascal functions
*/
public <T> T asInterface(Class<T> interf) {
return interf.cast(Proxy.newProxyInstance(RVMCore.class.getClassLoader(), new Class<?> [] { interf }, new RascalFunctionInvocationHandler(this, interf)));
}
public PrintWriter getStdErr() { return rex.getStdErr(); }
public PrintWriter getStdOut() { return rex.getStdOut(); }
IRascalMonitor getMonitor() {return rex.getMonitor();}
public IFrameObserver getFrameObserver() {
return frameObserver;
}
public void setFrameObserver(IFrameObserver observer){
frameObserver = observer;
rex.setFrameObserver(observer);
}
protected String getFunctionName(int n) {
for(String fname : functionMap.keySet()) {
if(functionMap.get(fname) == n) {
return fname;
}
}
throw new CompilerError("Undefined function index " + n);
}
public String findVarName(Frame cf, int s, int pos){
for (Frame fr = cf; fr != null; fr = fr.previousScope) {
if (fr.scopeId == s) {
return findLocalName(fr, pos);
}
}
return "** unknown variable **";
}
public String findLocalName(Frame cf, int pos){
IString name = ((IString) cf.function.localNames.get(vf.integer(pos)));
return (name != null) ? name.getValue() : "** unknown variable **";
}
public Function getFunction(String name, Type returnType, Type argumentTypes){
next:
for(Function f : functionStore){
if(f.name.contains("/" + name + "(") && f.ftype instanceof FunctionType){
FunctionType ft = (FunctionType) f.ftype;
int arity = argumentTypes.getArity();
if(returnType.equals(ft.getReturnType()) && arity == ft.getArgumentTypes().getArity()){
for(int i = 0; i < arity; i++){ // ignore field names
if(!argumentTypes.getFieldType(i).equals(ft.getArgumentTypes().getFieldType(i))){
continue next;
}
}
return f;
}
}
}
return null;
}
private ArrayList<Integer> getFunctionByNameAndArity(String name, int arity){
ArrayList<Integer> functions = new ArrayList<>();
for(int n = 0; n < functionStore.length; n++){
Function f = functionStore[n];
if(f.name.contains("/" + name + "(") && f.ftype instanceof FunctionType){
FunctionType ft = (FunctionType) f.ftype;
if(ft.getArgumentTypes().getArity() == arity){
functions.add(n);
}
}
}
return functions;
}
public OverloadedFunction getOverloadedFunctionByNameAndArity(String name, int arity) throws NoSuchRascalFunction {
// System.err.println("getFirstOverloadedFunctionByNameAndArity: " + name + ", " + arity);
OverloadedFunction of = overloadedStoreMap.get(name);
Type tp = null;
//System.err.println(of);
ArrayList<Integer> filteredAlts = new ArrayList<>();
if(of != null){
for (int alt : of.getFunctions()) {
//System.err.println("alt: " + functionStore[alt].ftype);
if (functionStore[alt].ftype.getArity() == arity) {
tp = functionStore[alt].ftype;
filteredAlts.add(alt);
}
}
if(filteredAlts.size() > 0){
int falts[] = new int[filteredAlts.size()];
for(int i = 0; i < falts.length; i++){
falts[i] = filteredAlts.get(i);
}
return new OverloadedFunction(name, tp, falts, new int[] { }, "");
}
}
filteredAlts = getFunctionByNameAndArity(name, arity);
if(filteredAlts.size() > 0){
tp = tf.tupleType(tf.valueType()); // arbitrary!
int falts[] = new int[filteredAlts.size()];
for(int i = 0; i < falts.length; i++){
falts[i] = filteredAlts.get(i);
}
return new OverloadedFunction(name, tp, falts, new int[] { }, "");
}
// ?? why are constructors not part of overloaded functions?
for(int i = 0; i < constructorStore.size(); i++){
tp = constructorStore.get(i);
if (tp.getName().equals(name) && tp.getArity() == arity) {
return new OverloadedFunction(name, tp, new int[]{}, new int[] { i }, "");
}
}
throw new NoSuchRascalFunction(name);
}
// deprecated
public OverloadedFunction getOverloadedFunction(String signature) throws NoSuchRascalFunction{
OverloadedFunction result = null;
String name = types.getFunctionName(signature);
Type funType = types.getFunctionType(signature);
FunctionType ft = (FunctionType) funType;
int arity = ft.getArity();
for(OverloadedFunction of : overloadedStore){
if(of.matchesNameAndSignature(name, funType)){
if(result == null){
result = of;
} else {
if(result.getFunctions().length < of.getFunctions().length ||
result.getConstructors().length < of.getConstructors().length){
result = of;
}
}
}
}
if(result != null){
return result;
}
ArrayList<Integer> filteredAlts = getFunctionByNameAndArity(name, arity);
if(filteredAlts.size() > 0){
Type tp = tf.tupleType(tf.valueType()); // arbitrary!
int falts[] = new int[filteredAlts.size()];
for(int i = 0; i < falts.length; i++){
falts[i] = filteredAlts.get(i);
}
return new OverloadedFunction(name, tp, falts, new int[] { }, "");
}
for(int i = 0; i < constructorStore.size(); i++){
Type tp = constructorStore.get(i);
if(name.equals(tp.getName()) && ft.getFieldTypes().comparable(funType.getFieldTypes())
&& ft.getReturnType().comparable(tp.getAbstractDataType())
){
return new OverloadedFunction(name, tp, new int[]{}, new int[] { i }, "");
}
}
// No overloaded function or constructor matched, only nonterminals remain ...
for(int i = 0; i < functionStore.length; i++){
Function fun = functionStore[i];
if(fun.name.contains("/" + name + "(")){
if(fun.ftype instanceof FunctionType){
FunctionType tp = (FunctionType) fun.ftype;
if(tp.comparable(ft)){
return new OverloadedFunction(name, tp, new int[]{ i }, new int[] { }, "");
}
}
}
}
throw new NoSuchRascalFunction(signature);
}
public Function getCompanionDefaultsFunction(String name, Type ftype){
all:
for(Function f : functionStore){
//if(f.name.contains("companion")) System.err.println("getCompanionDefaultsFunction " + f.name);
if(f.name.contains("companion-defaults") &&
f.name.contains("::" + name + "(")){
FunctionType ft = (FunctionType) f.ftype;
if(ftype.getAbstractDataType().equals(ft.getReturnType())){
if(ftype.isAbstractData()){
//System.err.println("getCompanionDefaultsFunction1: " + name + ", " + ftype);
return f;
}
if(ftype.getFieldTypes().getArity() == ft.getArgumentTypes().getArity()){
for(int i = 0; i < ftype.getFieldTypes().getArity(); i++){
if(!ftype.getFieldType(i).equals(ft.getArgumentTypes().getFieldType(i))){
continue all;
}
}
//System.err.println("getCompanionDefaultsFunction2: " + name + ", " + ftype);
return f;
}
}
}
}
//System.err.println("getCompanionDefaultsFunction3: " + name + ", " + ftype);
return noCompanionFunction;
}
public Function getCompanionFieldDefaultFunction(Type adtType, String fieldName){
String key = adtType.toString() + "::" + fieldName + "-companion-default";
for(Function f : functionStore){
if(f.name.equals(key)){
return f;
}
}
return noCompanionFunction;
}
public Frame makeFrameForVisit(FunctionInstance func){
return new Frame(func.function.scopeId, null, func.env, func.function.maxstack, func.function);
}
/**
* Execute a single, not-overloaded, function
*
* @param uid_func Internal function name
* @param posArgs Positional arguments
* @param kwArgs Keyword arguments
* @return
*/
public Object executeRVMFunction(String uid_func, IValue[] posArgs, Map<String,IValue> kwArgs){
// Assumption here is that the function called is not a nested one and does not use global variables
Function func = functionStore[functionMap.get(uid_func)];
return executeRVMFunction(func, posArgs, kwArgs);
}
/**
* Execute an OverloadedFunction
* @param func OverloadedFunction
* @param posArgs Arguments (last one is a map with keyword arguments)
* @param kwArgs Keyword arguments
* @return Result of function execution
*/
public IValue executeRVMFunction(OverloadedFunction func, IValue[] posArgs, Map<String, IValue> kwArgs){
if(func.getFunctions().length > 0){
Function firstFunc = functionStore[func.getFunctions()[0]];
Frame root = new Frame(func.scopeIn, null, null, func.getArity()+2, firstFunc);
OverloadedFunctionInstance ofi = OverloadedFunctionInstance.computeOverloadedFunctionInstance(func.functions, func.constructors, root, func.scopeIn, functionStore, constructorStore, this);
return executeRVMFunction(ofi, posArgs, kwArgs);
} else {
Type cons = constructorStore.get(func.getConstructors()[0]);
return vf.constructor(cons, posArgs, kwArgs);
}
}
public IList executeTests(ITestResultListener testResultListener, RascalExecutionContext rex){
IListWriter w = vf.listWriter();
for(Function f : functionStore){
if(f.isTest){
w.append(f.executeTest(testResultListener, rex));
}
}
return w.done();
}
/************************************************************************************/
/* Abstract methods to execute RVMFunctions and RVMPrograms */
/************************************************************************************/
/**
* Execute a single, not-overloaded, function
*
* @param func Function
* @param posArgs Positional arguments
* @param kwArgs Keyword arguments
* @return Result of function execution
*/
abstract public Object executeRVMFunction(Function func, IValue[] posArgs, Map<String,IValue> kwArgs);
/**
* Execute a FunctionInstance
* @param func Function instance
* @param posArgs Arguments (last one is a map with keyword arguments)
* @param kwArgs Keyword arguments
* @return Result of function execution
*/
abstract public IValue executeRVMFunction(FunctionInstance func, IValue[] posArgs, Map<String, IValue> kwArgs);
/**
* Execute an OverloadedFunctionInstance
* @param func OverloadedFunctionInstance
* @param posArgs Positional arguments
* @param kwArgs Keyword arguments
* @return Result of function execution
*/
abstract public IValue executeRVMFunction(OverloadedFunctionInstance func, IValue[] posArgs, Map<String, IValue> kwArgs);
/**
* Execute a function during a visit
* @param root Frame in which the function will be executed
* @return Result of function execution
*/
abstract public IValue executeRVMFunctionInVisit(Frame root);
/**
* Execute a main program in a Rascal module
* @param moduleName Name of the module
* @param uid_main Internal name of the main function
* @param posArgs Positional arguments
* @param kwArgs Keyword arguments
* @return Result of executing main function
*/
abstract public IValue executeRVMProgram(String moduleName, String uid_main, IValue[] posArgs, Map<String,IValue> kwArgs);
/**
* Narrow an Object as occurring on the RVM runtime stack to an IValue that can be returned.
* Note that various non-IValues can occur:
* - Coroutine
* - Reference
* - FunctionInstance
* - Object[] (is converted to an IList)
* @param result to be returned
* @return converted result or an exception
*/
protected IValue narrow(Object result){
if(result instanceof Integer) {
return vf.integer((Integer)result);
}
if(result instanceof IValue) {
return (IValue) result;
}
if(result instanceof Thrown) {
((Thrown) result).printStackTrace(stdout);
return vf.string(((Thrown) result).toString());
}
if(result instanceof Object[]) {
IListWriter w = vf.listWriter();
Object[] lst = (Object[]) result;
for(int i = 0; i < lst.length; i++){
w.append(narrow(lst[i]));
}
return w.done();
}
if(result == null){
return null;
}
throw new CompilerError("Cannot convert object back to IValue: " + result);
}
/**
* Represent any object that can occur on the RVM stack stack as string
* @param some stack object
* @return its string representation
*/
@SuppressWarnings("rawtypes")
protected static String asString(Object o){
if(o == null)
return "null";
if(o instanceof Integer)
return ((Integer)o).toString() + " [Java]";
if(o instanceof String)
return ((String)o) + " [Java]";
if(o instanceof IValue)
return ((IValue) o).toString() +" [IValue]";
if(o instanceof Type)
return ((Type) o).toString() + " [Type]";
if(o instanceof Object[]){
StringBuilder w = new StringBuilder();
Object[] lst = (Object[]) o;
w.append("[");
for(int i = 0; i < lst.length; i++){
w.append(asString(lst[i]));
if(i < lst.length - 1)
w.append(", ");
}
w.append("]");
return w.toString() + " [Object[]]";
}
if(o instanceof Coroutine){
if(((Coroutine)o).frame != null && ((Coroutine)o).frame.function != null){
return "Coroutine[" + ((Coroutine)o).frame.function.getName() + "]";
} else {
return "Coroutine[**no name**]";
}
}
if(o instanceof Function){
return "Function[" + ((Function)o).getName() + "]";
}
if(o instanceof FunctionInstance){
return "Function[" + ((FunctionInstance)o).function.getName() + "]";
}
if(o instanceof OverloadedFunctionInstance) {
OverloadedFunctionInstance of = (OverloadedFunctionInstance) o;
String alts = "";
for(Integer fun : of.getFunctions()) {
alts = alts + fun + "; ";
}
return "OverloadedFunction[ alts: " + alts + "]";
}
if(o instanceof Reference){
Reference ref = (Reference) o;
return "Reference[" + ref.stack + ", " + ref.pos + "]";
}
if(o instanceof IListWriter){
return "ListWriter[" + ((IListWriter) o).toString() + "]";
}
if(o instanceof ISetWriter){
return "SetWriter[" + ((ISetWriter) o).toString() + "]";
}
if(o instanceof IMapWriter){
return "MapWriter[" + ((IMapWriter) o).toString() + "]";
}
if(o instanceof Matcher){
return "Matcher[" + ((Matcher) o).pattern() + "]";
}
if(o instanceof Thrown) {
return "THROWN[ " + asString(((Thrown) o).getValue()) + " ]";
}
if(o instanceof StringBuilder){
return "StringBuilder[" + ((StringBuilder) o).toString() + "]";
}
if(o instanceof HashSet){
return "HashSet[" + ((HashSet<?>) o).toString() + "]";
}
if(o instanceof Map){
return "Map[" + ((Map<?, ?>) o).toString() + "]";
}
if(o instanceof HashMap){
return "HashMap[" + ((HashMap<?, ?>) o).toString() + "]";
}
if(o instanceof Map.Entry){
return "Map.Entry[" + ((Map.Entry) o).toString() + "]";
}
return o.getClass().getName();
//throw new CompilerError("asString cannot convert: " + o);
}
public String asString(Object o, int w){
String repr = asString(o);
return (repr.length() < w) ? repr : repr.substring(0, w) + "...";
}
/********************************************************************************/
/* Auxiliary functions that implement specific instructions and are */
/* used both by RVM interpreter and generated JVM bytecod */
/********************************************************************************/
// LOAD/PUSH VAR
protected Object LOADVAR(final Frame cf, final int varScope, final int pos){
return CodeBlock.isMaxArg2(pos) ? LOADVARMODULE(cf, varScope) : LOADVARSCOPED(cf, varScope, pos);
}
protected Object LOADVARMODULE(final Frame cf, final int varScope){
return moduleVariables.get(cf.function.constantStore[varScope]); // TODO: undefined case
}
protected Object LOADVARSCOPED(final Frame cf, final int varScope, final int pos){
for (Frame fr = cf.previousScope; fr != null; fr = fr.previousScope) {
if (fr.scopeId == varScope) {
return fr.stack[pos]; // TODO: undefined case
}
}
throw new CompilerError("LOADVAR cannot find matching scope: " + varScope + " from scope " + cf.scopeId, cf);
}
protected int PUSHVAR(final Object[] stack, int sp, final Frame cf, final int varScope, final int pos){
stack[sp++] = CodeBlock.isMaxArg2(pos) ? LOADVARMODULE(cf, varScope) : LOADVARSCOPED(cf, varScope, pos);
return sp;
}
protected int PUSHVARMODULE(final Object[] stack, int sp, final Frame cf, final int varScope){
stack[sp++] = LOADVARMODULE(cf, varScope);
return sp;
}
protected int PUSHVARSCOPED(final Object[] stack, int sp, final Frame cf, final int varScope, final int pos){
stack[sp++] = LOADVARSCOPED(cf, varScope, pos);
return sp;
}
// LOAD/PUSH VARREF
protected Object LOADVARREF(final Frame cf, final int varScope, final int pos){
return CodeBlock.isMaxArg2(pos) ? LOADVARREFMODULE(cf, varScope) : LOADVARREFSCOPED(cf, varScope, pos);
}
protected Object LOADVARREFMODULE(final Frame cf, final int varScope){
return moduleVariables.get(cf.function.constantStore[varScope]);
}
protected Object LOADVARREFSCOPED(final Frame cf, final int varScope, final int pos){
for (Frame fr = cf.previousScope; fr != null; fr = fr.previousScope) {
if (fr.scopeId == varScope) {
return new Reference(fr.stack, pos);
}
}
throw new CompilerError("LOADVARREF cannot find matching scope: " + varScope + " from scope " + cf.scopeId, cf);
}
protected int PUSHVARREF(final Object[] stack, int sp, final Frame cf, final int varScope, final int pos){
stack[sp++] = CodeBlock.isMaxArg2(pos) ? LOADVARREFMODULE(cf, varScope) : LOADVARREFSCOPED(cf, varScope, pos);
return sp;
}
protected int PUSHVARREFMODULE(final Object[] stack, int sp, final Frame cf, final int varScope){
stack[sp++] = LOADVARREFMODULE(cf, varScope);
return sp;
}
protected int PUSHVARREFSCOPED(final Object[] stack, int sp, final Frame cf, final int varScope, final int pos){
stack[sp++] = LOADVARREFSCOPED(cf, varScope, pos);
return sp;
}
// LOAD/PUSH VARDEREF
protected Object LOADVARDEREF(Frame cf, int varScope, int pos){
for (Frame fr = cf.previousScope; fr != null; fr = fr.previousScope) {
if (fr.scopeId == varScope) {
Reference ref = (Reference) fr.stack[pos];
return ref.stack[ref.pos];
}
}
throw new CompilerError("LOADVARDEREF cannot find matching scope: " + varScope, cf);
}
protected int PUSHVARDEREF(final Object[] stack, int sp, final Frame cf, final int varScope, final int pos){
stack[sp++] = LOADVARDEREF(cf, varScope, pos);
return sp;
}
// STOREVAR
protected void STOREVAR(final Frame cf, final int varScope, final int pos, final Object accu){
if(CodeBlock.isMaxArg2(pos)){
STOREVARMODULE(cf, varScope, accu);
} else {
STOREVARSCOPED(cf, varScope, pos, accu);
}
}
protected void STOREVARMODULE(final Frame cf, final int varScope, final Object accu){
IValue mvar = cf.function.constantStore[varScope];
moduleVariables.put(mvar, (IValue)accu);
return;
}
protected void STOREVARSCOPED(Frame cf, int varScope, int pos, Object accu){
for (Frame fr = cf.previousScope; fr != null; fr = fr.previousScope) {
if (fr.scopeId == varScope) {
// TODO: We need to re-consider how to guarantee safe use of both Java objects and IValues
fr.stack[pos] = accu;
return;
}
}
throw new CompilerError("STOREVAR cannot find matching scope: " + varScope + " from scope " + cf.scopeId, cf);
}
// RESETVAR
protected void RESETVAR(Frame cf, int varScope, int pos){
if(CodeBlock.isMaxArg2(pos)){
IValue mvar = cf.function.constantStore[varScope];
moduleVariables.put(mvar, null);
return;
}
for (Frame fr = cf.previousScope; fr != null; fr = fr.previousScope) {
if (fr.scopeId == varScope) {
// TODO: We need to re-consider how to guarantee safe use of both Java objects and IValues
fr.stack[pos] = null;
return;
}
}
throw new CompilerError("RESETVAR cannot find matching scope: " + varScope + " from scope " + cf.scopeId, cf);
}
// UNWRAPTHROWNVAR
protected int UNWRAPTHROWNVAR(final Object[] stack, final int sp, final Frame cf, final int varScope, final int pos){
return CodeBlock.isMaxArg2(pos) ? UNWRAPTHROWNVARMODULE(stack, sp, cf, varScope)
: UNWRAPTHROWNVARSCOPED(stack, sp, cf, varScope, pos);
}
protected int UNWRAPTHROWNVARMODULE(final Object[] stack, final int sp, final Frame cf, final int varScope){
IValue mvar = cf.function.constantStore[varScope];
moduleVariables.put(mvar, (IValue)stack[sp - 1]);
return sp;
}
protected int UNWRAPTHROWNVARSCOPED(final Object[] stack, int sp, final Frame cf, final int varScope, final int pos){
for (Frame fr = cf; fr != null; fr = fr.previousScope) {
if (fr.scopeId == varScope) {
// TODO: We need to re-consider how to guarantee safe use of both Java objects and IValues
fr.stack[pos] = ((Thrown) stack[--sp]).getValue();
return sp;
}
}
throw new CompilerError("UNWRAPTHROWNVAR cannot find matching scope: " + varScope, cf);
}
protected void STOREVARDEREF(Frame cf, int varScope, int pos, Object accu){
for (Frame fr = cf.previousScope; fr != null; fr = fr.previousScope) {
if (fr.scopeId == varScope) {
Reference ref = (Reference) fr.stack[pos];
ref.stack[ref.pos] = accu;
}
}
throw new CompilerError("STOREVARDEREF cannot find matching scope: " + varScope + " from scope " + cf.scopeId, cf);
}
// STORELOCKWP
@SuppressWarnings("unchecked")
protected void STORELOCKWP(final Object[] stack, Frame cf, int iname, Object accu){
String name = ((IString) cf.function.codeblock.getConstantValue(iname)).getValue();
Map<String, IValue> kargs = (Map<String, IValue>) stack[cf.function.nformals - 1];
if(kargs == emptyKeywordMap){
System.err.println("Creating new kw map while updating: " + name);
kargs = new HashMap<>();
stack[cf.function.nformals - 1] = kargs;
}
kargs.put(name, (IValue) accu);
}
// LOAD/PUSH VARKWP
@SuppressWarnings("unchecked")
protected Object LOADVARKWP(final Frame cf, final int varScope, final int iname){
String name = ((IString) cf.function.codeblock.getConstantValue(iname)).getValue();
for(Frame f = cf.previousScope; f != null; f = f.previousCallFrame) {
if (f.scopeId == varScope) {
if(f.function.nformals > 0){
Object okargs = f.stack[f.function.nformals - 1];
if(okargs instanceof Map<?,?>){ // Not all frames provide kwargs, i.e. generated PHI functions.
Map<String, IValue> kargs = (Map<String,IValue>) okargs;
if(kargs.containsKey(name)) {
IValue val = kargs.get(name);
//if(val.getType().isSubtypeOf(defaultValue.getKey())) {
return val;
//}
}
Map<String, Entry<Type, IValue>> defaults = (Map<String, Map.Entry<Type, IValue>>) f.stack[f.function.nformals];
if(defaults.containsKey(name)) {
Entry<Type, IValue> defaultValue = defaults.get(name);
//if(val.getType().isSubtypeOf(defaultValue.getKey())) {
return defaultValue.getValue();
//}
}
}
}
}
}
throw new CompilerError("LOADVARKWP cannot find matching scope: " + varScope + " from scope " + cf.scopeId, cf);
}
protected int PUSHVARKWP(final Object[] stack, int sp, final Frame cf, final int varScope, final int iname){
stack[sp++] = LOADVARKWP(cf, varScope, iname);
return sp;
}
// STOREVARKWP
@SuppressWarnings("unchecked")
protected void STOREVARKWP(final Frame cf, final int varScope, final int iname, final Object accu){
String name = ((IString) cf.function.codeblock.getConstantValue(iname)).getValue();
IValue val = (IValue) accu;
for(Frame f = cf.previousScope; f != null; f = f.previousCallFrame) {
if (f.scopeId == varScope) {
if(f.function.nformals > 0){
Object okargs = f.stack[f.function.nformals - 1];
if(okargs instanceof Map<?,?>){ // Not all frames provide kwargs, i.e. generated PHI functions.
Map<String, IValue> kargs = (Map<String,IValue>) f.stack[f.function.nformals - 1];
if(kargs.containsKey(name)) {
val = kargs.get(name);
//if(val.getType().isSubtypeOf(defaultValue.getKey())) {
if(kargs == emptyKeywordMap){
System.err.println("Creating new kw map while updating: " + name);
kargs = new HashMap<>();
f.stack[f.function.nformals - 1] = kargs;
}
kargs.put(name, val);
return;
//}
}
Map<String, Entry<Type, IValue>> defaults = (Map<String, Map.Entry<Type, IValue>>) f.stack[f.function.nformals];
if(defaults.containsKey(name)) {
//Entry<Type, IValue> defaultValue = defaults.get(name);
//if(val.getType().isSubtypeOf(defaultValue.getKey())) {
kargs.put(name,val);
return;
//}
}
}
}
}
}
throw new CompilerError("STOREVARKWP cannot find matching scope: " + varScope + " from scope " + cf.scopeId, cf);
}
// LOAD/PUSH LOCKWP
@SuppressWarnings("unchecked")
protected Object LOADLOCKWP(final Object[] stack, final Frame cf, final int iname){
String name = ((IString) cf.function.codeblock.getConstantValue(iname)).getValue();
Map<String, Map.Entry<Type, IValue>> defaults = (Map<String, Map.Entry<Type, IValue>>) stack[cf.function.nformals];
Map.Entry<Type, IValue> defaultValue = defaults.get(name);
Frame f = cf;
// TODO: UNCOMMENT TO GET KEYWORD PARAMETER PROPAGATION
//for(Frame f = cf; f != null; f = f.previousCallFrame) {
int nf = f.function.nformals;
if(nf > 0){ // Some generated functions have zero args, i.e. EQUIVALENCE
Object okargs = f.stack[nf - 1];
if(okargs instanceof Map<?,?>){ // Not all frames provide kwargs, i.e. generated PHI functions.
Map<String, IValue> kargs = (Map<String,IValue>) okargs;
if(kargs.containsKey(name)) {
IValue val = kargs.get(name);
if(val.getType().isSubtypeOf(defaultValue.getKey())) {
return val;
}
}
}
}
//}
return defaultValue.getValue();
}
protected int PUSHLOCKWP(final Object[] stack, int sp, final Frame cf, final int iname){
stack[sp++] = LOADLOCKWP(stack, cf, iname);
return sp;
}
// CALLCONSTR
@SuppressWarnings("unchecked")
protected int CALLCONSTR(final Object[] stack, int sp, final int iconstructor, final int arity){
Type constructor = constructorStore.get(iconstructor);
IValue[] args = new IValue[constructor.getArity()];
java.util.Map<String,IValue> kwargs;
@SuppressWarnings("unused")
Type type = (Type) stack[--sp]; // TODO: emove from instruction
kwargs = (java.util.Map<String,IValue>) stack[--sp];
for(int i = 0; i < constructor.getArity(); i++) {
args[constructor.getArity() - 1 - i] = (IValue) stack[--sp];
}
stack[sp++] = vf.constructor(constructor, args, kwargs);
return sp;
}
protected int PRINTLN(Object[] stack, int sp, int arity){
StringBuilder w = new StringBuilder();
for(int i = arity - 1; i >= 0; i--){
String str = (stack[sp - 1 - i] instanceof IString) ? ((IString) stack[sp - 1 - i]).toString() : asString(stack[sp - 1 - i]);
w.append(str).append(" ");
}
stdout.println(w.toString());
sp = sp - arity + 1;
return sp;
}
public int VISIT(Object[] stack, int sp, boolean direction, boolean progress, boolean fixedpoint, boolean rebuild){
FunctionInstance phi = (FunctionInstance)stack[sp - 8];
IValue subject = (IValue) stack[sp - 7];
Reference refMatched = (Reference) stack[sp - 6];
Reference refChanged = (Reference) stack[sp - 5];
Reference refLeaveVisit = (Reference) stack[sp - 4];
Reference refBegin = (Reference) stack[sp - 3];
Reference refEnd = (Reference) stack[sp - 2];
DescendantDescriptor descriptor = (DescendantDescriptor) stack[sp - 1];
DIRECTION tr_direction = direction ? DIRECTION.BottomUp : DIRECTION.TopDown;
PROGRESS tr_progress = progress ? PROGRESS.Continuing : PROGRESS.Breaking;
FIXEDPOINT tr_fixedpoint = fixedpoint ? FIXEDPOINT.Yes : FIXEDPOINT.No;
REBUILD tr_rebuild = rebuild ? REBUILD.Yes :REBUILD.No;
IValue res = new Traverse(vf).traverse(tr_direction, tr_progress, tr_fixedpoint, tr_rebuild, subject, phi, refMatched, refChanged, refLeaveVisit, refBegin, refEnd, this, descriptor);
stack[sp - 8] = res;
sp -= 7;
// Trick: we return a negative sp to force a function return;
return ((IBool)refLeaveVisit.getValue()).getValue() ? -sp : sp;
}
@SuppressWarnings("unchecked")
public int CHECKMEMO(Object[] stack, int sp, Frame cf){;
Function fun = cf.function;
MemoizationCache<IValue> cache = fun.memoization == null ? null : fun.memoization.get();
if(cache == null){
cache = new MemoizationCache<>();
fun.memoization = new SoftReference<>(cache);
}
int nformals = fun.nformals;
IValue[] args = new IValue[nformals - 1];
for(int i = 0; i < nformals - 1; i++){
args[i] = (IValue) stack[i];
}
IValue result = cache.getStoredResult(args, (Map<String,IValue>)stack[nformals - 1]);
if(result == null){
return sp + 1;
}
stack[sp++] = result;
return -sp; // Trick: we return a negative sp to force a function return;
}
public Class<?> getJavaClass(String className){
Class<?> clazz = classCache.get(className);
if(clazz != null){
return clazz;
}
try {
clazz = this.getClass().getClassLoader().loadClass(className);
} catch(ClassNotFoundException e1) {
// If the class is not found, try other class loaders
for(ClassLoader loader : this.classLoaders) {
try {
clazz = loader.loadClass(className);
break;
} catch(ClassNotFoundException e2) {
;
}
}
}
if(clazz == null) {
throw new CompilerError("Class " + className + " not found");
}
classCache.put(className, clazz);
return clazz;
}
public Object getJavaClassInstance(Class<?> clazz){
Object instance = instanceCache.get(clazz);
if (instance != null){
return instance;
}
try {
Constructor<?> constructor = clazz.getConstructor(IValueFactory.class);
instance = constructor.newInstance(vf);
instanceCache.put(clazz, instance);
return instance;
} catch (IllegalArgumentException | InstantiationException | IllegalAccessException | InvocationTargetException | SecurityException | NoSuchMethodException e) {
throw new ImplementationError(e.getMessage(), e);
}
}
Method getJavaMethod(Class<?> clazz, String methodName, String className, Type parameterTypes, Type keywordTypes, int reflect) {
try {
return clazz.getMethod(methodName, makeJavaTypes(methodName, className, parameterTypes, keywordTypes, reflect));
}
catch (NoSuchMethodException | SecurityException e) {
throw new CompilerError("could not find Java method", e);
}
}
int callJavaMethod(String methodName, String className, Type parameterTypes, Type keywordTypes, int reflect, Object[] stack, int sp) throws Throw, Throwable {
Class<?> clazz = getJavaClass(className);
Method m = getJavaMethod(clazz, methodName, className, parameterTypes, keywordTypes, reflect);
return callJavaMethod(clazz, m, parameterTypes, keywordTypes, reflect, stack, sp);
}
int callJavaMethod(Class<?> clazz, Method m, Type parameterTypes, Type keywordTypes, int reflect, Object[] stack, int sp) throws Throw, Throwable {
try {
Object instance = getJavaClassInstance(clazz);
int arity = parameterTypes.getArity();
int kwArity = keywordTypes.getArity();
int kwMaps = kwArity > 0 ? 2 : 0;
Object[] parameters = new Object[arity + kwArity + reflect];
for(int i = arity - 1; i >= 0; i--){
parameters[i] = stack[sp - arity - kwMaps + i];
}
if(kwArity > 0){
@SuppressWarnings("unchecked")
Map<String, IValue> kwMap = (Map<String, IValue>) stack[sp - 2];
@SuppressWarnings("unchecked")
Map<String, Map.Entry<Type, IValue>> kwDefaultMap = (Map<String, Map.Entry<Type, IValue>>) stack[sp - 1];
for(int i = arity + kwArity - 1; i >= arity; i--){
String key = keywordTypes.getFieldName(i - arity);
IValue val = kwMap.get(key);
if(val == null){
val = kwDefaultMap.get(key).getValue();
}
parameters[i] = val;
}
}
if(reflect == 1) {
parameters[arity + kwArity] = converted.contains(clazz.getName() + "." + m.getName()) ? this.rex : null /*this.getEvaluatorContext()*/; // TODO: remove CTX
}
stack[sp - arity - kwMaps] = m.invoke(instance, parameters);
return sp - arity - kwMaps + 1;
}
catch (SecurityException | IllegalAccessException | IllegalArgumentException e) {
throw new CompilerError("could not call Java method", e);
}
catch (InvocationTargetException e) {
if(e.getTargetException() instanceof Throw) {
throw (Throw) e.getTargetException();
}
if(e.getTargetException() instanceof Thrown){
throw (Thrown) e.getTargetException();
}
throw e.getTargetException();
}
}
// Reflective methods where the CTX arguments has already been replaced by a REX argument
private static HashSet<String> converted = new HashSet<String>(Arrays.asList(
"org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.ParsingTools.parseFragment",
"org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.ExecuteProgram.executeProgram",
"org.rascalmpl.library.experiments.Compiler.CoverageCompiled.startCoverage",
"org.rascalmpl.library.experiments.Compiler.CoverageCompiled.stopCoverage",
"org.rascalmpl.library.experiments.Compiler.CoverageCompiled.getCoverage",
"org.rascalmpl.library.experiments.Compiler.ProfileCompiled.startProfile",
"org.rascalmpl.library.experiments.Compiler.ProfileCompiled.stopProfile",
"org.rascalmpl.library.experiments.Compiler.ProfileCompiled.getProfile",
"org.rascalmpl.library.experiments.Compiler.ProfileCompiled.reportProfile",
"org.rascalmpl.library.lang.csv.IOCompiled.readCSV",
"org.rascalmpl.library.lang.csv.IOCompiled.getCSVType",
"org.rascalmpl.library.lang.csv.IOCompiled.writeCSV",
"org.rascalmpl.library.lang.json.IOCompiled.fromJSON",
"org.rascalmpl.library.PreludeCompiled.delAnnotation",
"org.rascalmpl.library.PreludeCompiled.delAnnotations",
"org.rascalmpl.library.PreludeCompiled.implode",
"org.rascalmpl.library.PreludeCompiled.parse",
"org.rascalmpl.library.PreludeCompiled.print",
"org.rascalmpl.library.PreludeCompiled.println",
"org.rascalmpl.library.PreludeCompiled.iprint",
"org.rascalmpl.library.PreludeCompiled.iprintln",
"org.rascalmpl.library.PreludeCompiled.rprint",
"org.rascalmpl.library.PreludeCompiled.rprintln",
"org.rascalmpl.library.PreludeCompiled.sort",
"org.rascalmpl.library.util.MonitorCompiled.startJob",
"org.rascalmpl.library.util.MonitorCompiled.event",
"org.rascalmpl.library.util.MonitorCompiled.endJob",
"org.rascalmpl.library.util.MonitorCompiled.todo",
"org.rascalmpl.library.util.ReflectiveCompiled.parseModule",
"org.rascalmpl.library.util.ReflectiveCompiled.getModuleLocation",
"org.rascalmpl.library.util.ReflectiveCompiled.getSearchPathLocation",
"org.rascalmpl.library.util.ReflectiveCompiled.inCompiledMode",
"org.rascalmpl.library.util.ReflectiveCompiled.parseNamedModuleWithSpaces",
"org.rascalmpl.library.util.ReflectiveCompiled.diff",
"org.rascalmpl.library.util.ReflectiveCompiled.watch",
"org.rascalmpl.library.util.WebserverCompiled.serve"
/*
* TODO:
* cobra::util::outputlogger::startLog
* cobra::util::outputlogger::getLog
* cobra::quickcheck::_quickcheck
* cobra::quickcheck::arbitrary
*
* experiments::resource::Resource::registerResource
* experiments::resource::Resource::getTypedResource
* experiments::resource::Resource::generateTypedInterfaceInternal
* experiments::vis2::vega::Vega::color
*
* lang::aterm::IO::readTextATermFile
* lang::aterm::IO::writeTextATermFile
*
* lang::html::IO::readHTMLFile
*
* lang::java::m3::AST::setEnvironmentOptions
* lang::java::m3::AST::createAstFromFile
* lang::java::m3::AST::createAstFromString
* lang::java::m3::Core::createM3FromFile
* lang::java::m3::Core::createM3FromFile
* lang::java::m3::Core::createM3FromJarClass
*
* lang::jvm::run::RunClassFile::runClassFile
* lang::jvm::transform::SerializeClass::serialize
*
* lang::rsf::IO::readRSF
* lang::rsf::IO::getRSFTypes
* lang::rsf::IO::readRSFRelation
*
* lang::yaml::Model::loadYAML
* lang::yaml::Model::dumpYAML
*
* resource::jdbc::JDBC::registerJDBCClass
* util::tasks::Manager
* util::Eval
* util::Monitor
* vis::Figure::color
*
* Traversal::getTraversalContext
*
* **eclipse**
* util::Editors
* util::FastPrint
* util::HtmlDisplay
* util::IDE
* util::ResourceMarkers
* vis::Render
* vis::RenderSWT
*
*/
));
private static Class<?>[] makeJavaTypes(String methodName, String className, Type parameterTypes, Type keywordTypes, int reflect){
JavaClasses javaClasses = new JavaClasses();
int arity = parameterTypes.getArity();
int kwArity = keywordTypes.getArity();
Class<?>[] jtypes = new Class<?>[arity + kwArity + reflect];
int i = 0;
while(i < parameterTypes.getArity()){
jtypes[i] = parameterTypes.getFieldType(i).accept(javaClasses);
i++;
}
while(i < arity + kwArity){
jtypes[i] = keywordTypes.getFieldType(i - arity).accept(javaClasses);
i++;
}
if(reflect == 1) {
jtypes[arity + kwArity] = converted.contains(className + "." + methodName)
? RascalExecutionContext.class
: IEvaluatorContext.class; // TODO: remove CTX
}
return jtypes;
}
private static class JavaClasses extends DefaultRascalTypeVisitor<Class<?>, RuntimeException> {
public JavaClasses() {
super(IValue.class);
}
@Override
public Class<?> visitNonTerminal(RascalType type)
throws RuntimeException {
return IConstructor.class;
}
@Override
public Class<?> visitBool(org.rascalmpl.value.type.Type boolType) {
return IBool.class;
}
@Override
public Class<?> visitReal(org.rascalmpl.value.type.Type type) {
return IReal.class;
}
@Override
public Class<?> visitInteger(org.rascalmpl.value.type.Type type) {
return IInteger.class;
}
@Override
public Class<?> visitRational(org.rascalmpl.value.type.Type type) {
return IRational.class;
}
@Override
public Class<?> visitNumber(org.rascalmpl.value.type.Type type) {
return INumber.class;
}
@Override
public Class<?> visitList(org.rascalmpl.value.type.Type type) {
return IList.class;
}
@Override
public Class<?> visitMap(org.rascalmpl.value.type.Type type) {
return IMap.class;
}
@Override
public Class<?> visitAlias(org.rascalmpl.value.type.Type type) {
return type.getAliased().accept(this);
}
@Override
public Class<?> visitAbstractData(org.rascalmpl.value.type.Type type) {
return IConstructor.class;
}
@Override
public Class<?> visitSet(org.rascalmpl.value.type.Type type) {
return ISet.class;
}
@Override
public Class<?> visitSourceLocation(org.rascalmpl.value.type.Type type) {
return ISourceLocation.class;
}
@Override
public Class<?> visitString(org.rascalmpl.value.type.Type type) {
return IString.class;
}
@Override
public Class<?> visitNode(org.rascalmpl.value.type.Type type) {
return INode.class;
}
@Override
public Class<?> visitConstructor(org.rascalmpl.value.type.Type type) {
return IConstructor.class;
}
@Override
public Class<?> visitTuple(org.rascalmpl.value.type.Type type) {
return ITuple.class;
}
@Override
public Class<?> visitValue(org.rascalmpl.value.type.Type type) {
return IValue.class;
}
@Override
public Class<?> visitVoid(org.rascalmpl.value.type.Type type) {
return null;
}
@Override
public Class<?> visitParameter(org.rascalmpl.value.type.Type parameterType) {
return parameterType.getBound().accept(this);
}
@Override
public Class<?> visitDateTime(Type type) {
return IDateTime.class;
}
}
}