package org.rascalmpl.library.experiments.Compiler.RVM.Interpreter; import java.io.PrintWriter; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import org.rascalmpl.interpreter.types.FunctionType; // TODO: remove import: NO import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.repl.CommandExecutor; import org.rascalmpl.value.IInteger; import org.rascalmpl.value.IListWriter; import org.rascalmpl.value.ISourceLocation; import org.rascalmpl.value.IString; import org.rascalmpl.value.IValue; import org.rascalmpl.value.type.Type; import org.rascalmpl.values.ValueFactoryFactory; public class Frame { public final int scopeId; // Name of the scope introduced by this frame public Frame previousCallFrame; // Backpointer to caller public final Frame previousScope; public final Object[] stack; // The local stack public int sp; // Stack pointer /* * Layout of the stack for a function with 'nformals' formals arguments and 'nlocals' local variables: * * stack[0] ... stack[nformals-1] : the given actual parameters * stack[nformals-1] : actual keyword parameters * stack[nformals] : default keyword parameters * stack[nformals+1] ... stack[nlocals-1] : local variables and temporaries * stack[nlocals] ... stack[stackSize-1] : intermediate results during execution * */ int pc; // Program counter in RVM code public ISourceLocation src; // Most recent source location public final Function function; // The function that is being called final boolean isCoroutine; // Is this a coroutine? public Frame(final int scopeId, final Frame previousCallFrame,final int stackSize, final Function function){ this(scopeId, previousCallFrame, previousCallFrame, stackSize, function); } public Frame(final int scopeId, final Frame previousCallFrame, final Frame previousScope, final int stackSize, final Function function){ this(scopeId, previousCallFrame, previousScope, function, new Object[stackSize]); } private Frame(final int scopeId, final Frame previousCallFrame, final Frame previousScope, final Function function, final Object[] stack) { this.scopeId = scopeId; this.previousCallFrame = previousCallFrame; this.previousScope = previousScope; this.stack = stack; this.pc = 0; this.sp = 0; this.src = function.src; this.function = function; this.isCoroutine = function.isCoroutine; } /** * Assumption: scopeIn != -1; */ public Frame getCoroutineFrame(final Function f, final int scopeIn, final int arity, final int sp) { for(Frame env = this; env != null; env = env.previousCallFrame) { if (env.scopeId == f.scopeIn) { return getCoroutineFrame(f, env, arity, sp); } } throw new CompilerError("Could not find a matching scope when computing a nested coroutine instance: " + f.scopeIn, this); } /** * Given a current frame (this), * creates a new frame (frame) to be wrapped inside a coroutine object, * pushes arguments from the current frame's stack to the stack of the new frame (given an arity), * (re)setting the stack pointer of both the current and new frame, and * returns the new frame. */ public Frame getCoroutineFrame(final Function f, final Frame previousScope, final int arity, final int sp) { Frame frame = new Frame(f.scopeId, null, previousScope, f.maxstack, f); if(arity != f.nformals) { throw new CompilerError("Incorrect number of arguments has been passed to create a coroutine instance, expected: " + f.nformals, previousScope); } for (int i = 0; i < arity; i++) { frame.stack[i] = stack[sp - arity + i]; } this.sp = sp - arity; frame.sp = f.getNlocals(); return frame; } /** * Accounts for cases of partial parameter binding, * creates a new frame (frame) (similar to the method above) to be wrapped inside a coroutine object, * that gives a coroutine instance to be immediately initialized (i.e., all the coroutine arguments are assumed to be provided) */ public Frame getCoroutineFrame(final FunctionInstance fun_instance, final int arity, final int sp) { Function f = fun_instance.function; Object[] args = fun_instance.args; Frame frame = new Frame(f.scopeId, this, fun_instance.env, f.maxstack, f); assert fun_instance.next + arity == f.nformals; if(args != null) { for(Object arg : args) { if(arg == null) { break; } frame.stack[frame.sp++] = arg; } } for(int i = 0; i < arity; i++) { frame.stack[frame.sp++] = stack[sp - arity + i]; } this.sp = sp - arity; frame.sp = f.getNlocals(); return frame; } /** * Given a current frame (this), * creates a new frame (frame), * pushes arguments from the current frame's stack to the stack of the new frame, * (re)setting the stack pointer of both the current and the new frame, and * returns the new frame to the caller. */ public Frame getFrame(final Function f, final Frame previousScope, final int arity, final int sp) { Frame frame = new Frame(f.scopeId, this, previousScope, f.maxstack, f); this.sp = frame.pushFunctionArguments(arity, this.stack, sp); return frame; } public Frame getFrame(final Function f, final Frame previousScope, final Object[] args) { Frame frame = new Frame(f.scopeId, this, previousScope, f.maxstack, f); this.sp = frame.pushFunctionArguments(args.length, args, args.length); return frame; } public Frame getFrame(final Function f, final Frame previousScope, final Object[] args,final int arity, final int sp) { Frame frame = new Frame(f.scopeId, this, previousScope, f.maxstack, f); if(args != null) { for(Object arg : args) { if(arg == null) { break; } frame.stack[frame.sp++] = arg; } } if(arity == 0) { this.sp = sp; frame.sp = frame.function.getNlocals(); return frame; } this.sp = frame.pushFunctionArguments(arity, this.stack, sp); return frame; } /** * Given a current frame (this), * pushes arguments from a given stack (stack) to the current frame's stack (given an arity and the number of formal parameters of a function), * resetting the current frame's stack pointer to the number of local variables of its function, and * returns a new stack pointer for the given stack * (in case of the given stack being an array of arguments: stack.length == arity == sp && start == 0). */ private int pushFunctionArguments(final int arity, final Object[] stack, final int sp) { int start = sp - arity; assert start >= 0; if(!function.isVarArgs) { //assert this.sp + arity == function.nformals; for(int i = 0; i < arity; i++){ this.stack[this.sp++] = stack[start + i]; } } else { int posArityMinusOne = function.nformals - 2; // The number of positional arguments minus one for(int i = 0; i < posArityMinusOne; i++) { this.stack[this.sp++] = stack[start + i]; } Type argTypes = ((FunctionType) function.ftype).getArgumentTypes(); if(function.nformals == arity && ((IValue) stack[start + posArityMinusOne]).getType().isSubtypeOf(argTypes.getFieldType(posArityMinusOne))) { this.stack[this.sp++] = stack[start + posArityMinusOne]; } else { IListWriter writer = ValueFactoryFactory.getValueFactory().listWriter(); for(int i = posArityMinusOne; i < arity - 1; i++) { writer.append((IValue) stack[start + i]); } this.stack[this.sp++] = writer.done(); } assert stack[start + arity - 1] instanceof HashMap<?, ?>; this.stack[this.sp++] = stack[start + arity - 1]; // The keyword arguments } this.sp = function.getNlocals(); return start; } public Frame copy() { if(pc != 0) throw new CompilerError("Cannot copy frame when some instructions having already been executed."); Frame newFrame = new Frame(scopeId, previousCallFrame, previousScope, function, stack.clone()); newFrame.sp = sp; return newFrame; } private final int MAXLEN = 200; public int hotEntryPoint; public Frame nextFrame; private String abbrev(String repr) { return (repr.length() < MAXLEN) ? repr : repr.substring(0, 40) + "..."; } public String toString(){ StringBuilder s = new StringBuilder(); // if(src != null){ // s.append("\uE007["); // } s.append(this.function.getPrintableName()).append("("); for(int i = 0; i < function.nformals; i++){ if(i > 0) s.append(", "); String repr; if(stack[i] == null){ repr = "null"; } else if(stack[i] instanceof IValue ) { repr = ((IValue) stack[i]).toString(); } else { repr = stack[i].toString(); int n = repr.lastIndexOf("."); if(n >= 0){ repr = repr.substring(n + 1, repr.length()); } } s.append(abbrev(repr)); // if(src != null){ // s.append("](").append(src).append(")"); // } } // if(function.nformals-1 > 0 && stack[function.nformals-1] instanceof HashMap<?, ?>){ // @SuppressWarnings("unchecked") // HashMap<String, IValue> m = (HashMap<String, IValue>) stack[function.nformals-1]; // if(m.size() > 0){ // for(String key : m.keySet()){ // s.append(", ").append(key).append("=").append(m.get(key)); // } // } // } s.append(")"); if(src != null && !isConsoleMainFrame()){ s.append("\n\t\tat ").append(src); } return s.toString(); } boolean isConsoleMainFrame(){ return src.getPath().contentEquals(CommandExecutor.consoleInputPath) && function.getPrintableName().contains("main"); } private StringBuilder indent(){ int n = 0; Frame prev = previousCallFrame; while(prev != null){ n++; prev = prev.previousCallFrame; } StringBuilder b = new StringBuilder(2 * n + 10); for (int i = 0; i < n; i += 1) { b.append(" "); } return b; } public void printEnter(PrintWriter stdout){ stdout.println(indent().append(this.toString())); stdout.flush(); } public void printLeave(PrintWriter stdout, Object rval){ stdout.println(indent()./*append("\uE007 ").*/append(this.function.getPrintableName()).append(" returns ").append(rval == null ? "null" : abbrev(rval.toString()))); stdout.flush(); } public String getWhere(){ int begin = this.src.getBeginLine(); int end = this.src.getBeginLine(); String line = begin == end ? String.valueOf(begin) : (begin + "-" + end); return this.function.getPrintableName() + ":" + line; } public void printVars(PrintWriter stdout){ Iterator<Entry<IValue, IValue>> iter = function.localNames.entryIterator(); while(iter.hasNext()){ Entry<IValue, IValue> entry = iter.next(); String varName = ((IString) entry.getValue()).getValue(); int varPos = ((IInteger) entry.getKey()).intValue(); Object v = stack[varPos]; if(v != null && !varName.equals("map_of_default_values") && v instanceof IValue && varPos > this.function.nformals){ if(varName.matches("[0-9]+")){ varName = "arg " + varName; } stdout.println("\t" + varName + ": " + abbrev(RascalPrimitive.$value2string((IValue) v))); } } if(stack[function.nformals-1] instanceof HashMap<?, ?>){ @SuppressWarnings("unchecked") HashMap<String,IValue> kwParams = (HashMap<String,IValue>)stack[function.nformals-1]; for(String kwParam : kwParams.keySet()){ IValue v = kwParams.get(kwParam); if(v != null){ stdout.println("\t" + kwParam + "=" + abbrev(RascalPrimitive.$value2string(v)) ); } } } } public Map<String, IValue> getVars(){ HashMap<String, IValue> vars = new HashMap<>(); Iterator<Entry<IValue, IValue>> iter = function.localNames.entryIterator(); while(iter.hasNext()){ Entry<IValue, IValue> entry = iter.next(); String varName = ((IString) entry.getValue()).getValue(); int varPos = ((IInteger) entry.getKey()).intValue(); Object v = stack[varPos]; if(v != null && !varName.equals("map_of_default_values")){ if(varName.matches("[0-9]+")){ varName = "arg " + varName; } vars.put(varName, (IValue) v); } } if(stack[function.nformals-1] instanceof HashMap<?, ?>){ @SuppressWarnings("unchecked") HashMap<String,IValue> kwParams = (HashMap<String,IValue>)stack[function.nformals-1]; for(String kwParam : kwParams.keySet()){ IValue v = kwParams.get(kwParam); if(v != null){ vars.put(kwParam, kwParams.get(kwParam)); } } } return vars; } }