package org.reldb.rel.v0.vm; import org.reldb.rel.exceptions.ExceptionSemantic; import org.reldb.rel.v0.debuginfo.DebugInfo; import org.reldb.rel.v0.generator.Generator; import org.reldb.rel.v0.types.*; import org.reldb.rel.v0.values.*; import org.reldb.rel.v0.vm.instructions.core.OpNop; /** Run-time context for operator execution. Allows access to relevant parameters and variables. */ public class Context { // Operand stack size private final static int operandstacksize = 128; private Instruction[] code; // current set of instructions private int instructionPointer; // instruction pointer private Context[] contextDisplay; // this context's view of static scope private Cell[] variables; // variables private Value[] arguments; // arguments private Value[] operandStack; // operand stack private int stackPointer; // operand stack pointer private Context caller; // context that spawned this one private VirtualMachine vm; // VM in which this context lives private Generator generator; // Generator that owns this context private int depth; // static scope depth private boolean running = false; /** Create a non-executable root (depth=0) context. */ public Context(Generator generator, VirtualMachine vm) { this.generator = generator; this.vm = vm; caller = null; depth = 0; // Set up scope display contextDisplay = new Context[depth + 1]; contextDisplay[depth] = this; // Allocate an operand stack. operandStack = new Value[operandstacksize]; // Point to the executable code. code = null; // Initialise the instruction and stack pointers. instructionPointer = 0; stackPointer = 0; } /** Create a context for an operator invocation, where the variables and parms referenced are in a specified context */ private Context(Context caller, Operator operator, Context varparms) { this.generator = caller.generator; this.vm = caller.vm; this.caller = caller; depth = operator.getDepth(); // Set up scope display contextDisplay = new Context[Math.max(depth + 1, varparms.contextDisplay.length)]; System.arraycopy(varparms.contextDisplay, 0, contextDisplay, 0, varparms.contextDisplay.length); // Add this context to the scope display contextDisplay[depth] = this; // Allocate space for variables. if (operator.getVariableCount() > 0) variables = new Cell[operator.getVariableCount()]; // Adjust the caller context's stack pointer to remove the arguments from its stack // and move them to this context. This ensures continuations (such as TupleIteratorS) will // work, because we no longer need to refer to the caller's operand stack. int parmCount = operator.getParameterCount(); if (parmCount > 0) { arguments = new Value[parmCount]; caller.stackPointer -= parmCount; System.arraycopy(caller.operandStack, caller.stackPointer, arguments, 0, parmCount); } // Allocate an operand stack. operandStack = new Value[operandstacksize]; // Point to the executable code. code = operator.getExecutableCode(); // Initialise the instruction and stack pointers. instructionPointer = 0; stackPointer = 0; // Allow it to run running = true; } /** Create a context for an operator invocation. */ Context(Context caller, Operator operator) { this(caller, operator, caller); } private void dumpstack() { if (variables != null) { System.out.println("Variables:"); for (int i=0; i<variables.length; i++) { System.out.print("V[" + i + "] = "); if (variables[i] == null) System.out.println("uninitialised"); else System.out.println(variables[i]); } } System.out.println("Stack:"); for (int i=0; i<stackPointer; i++) { System.out.print("S[" + i + "] = "); if (operandStack[i] == null) System.out.println("uninitialised"); else System.out.println(operandStack[i]); } } /** Dump this context. */ public void dump(String prompt) { System.out.println("----------" + prompt + "----------"); System.out.println("Arguments:"); for (int i=0; i <= depth; i++) { int offset = 0; if (contextDisplay != null && contextDisplay[i] != null && contextDisplay[i].arguments != null) { for (Value v: contextDisplay[i].arguments) System.out.println("[" + i + " " + (offset++) + "] " + v); } else System.out.println("[" + i + " 0] none"); } dumpstack(); System.out.println("Context ID: " + this); System.out.print("Depth: " + depth); (new Dumper()).dumpMachineCode(code); System.out.println(); System.out.println("--------------------"); } /** Get the currently-executing instruction. */ public final Instruction getCurrentInstruction() { if (code == null) { Instruction i = new OpNop(); i.setDebugInfo(new DebugInfo("unknown location")); return i; } return code[instructionPointer - 1]; } /** Get the virtual machine upon which this Context is running. */ public final VirtualMachine getVirtualMachine() { return vm; } /** Get the Generator that owns this Context */ public final Generator getGenerator() { return generator; } private final void execute() { vm.setCurrentContext(this); while (instructionPointer < code.length && running) code[instructionPointer++].execute(this); vm.setCurrentContext(caller); } public void halt() { running = false; } // Invoke user-defined operator in its own context, i.e., call it. public final void call(Operator operator) { (new Context(this, operator)).execute(); } // Invoke user-defined operator in its own context, using a specified parent context for variable/parm scope. public final void call(Operator operator, Context varparmContext) { (new Context(this, operator, varparmContext)).execute(); } public final void doReturn() { instructionPointer = code.length; } public final void doReturnValue() { caller.push(pop()); doReturn(); } /** Go to a given instruction. */ public final void jump(int newIP) { instructionPointer = newIP; } /** Return Value on top of the stack */ public final Value peek() { return operandStack[stackPointer - 1]; } /** Return n values on top of the stack */ public Value[] peek(int n) { Value[] values = new Value[n]; System.arraycopy(operandStack, stackPointer - n, values, 0, n); return values; } final Context getCaller() { return caller; } final int getStackCount() { return stackPointer; } /** Push a Value onto the operand stack. */ public final void push(Value v) { operandStack[stackPointer++] = v; } /** Pop a Value from the operand stack. */ public final Value pop() { return operandStack[--stackPointer]; } // User-defined native function // POP(n) - Value // PUSH - Value public final void userFunction(NativeFunction function, int parmCount) { Value arguments[] = new Value[parmCount]; for (int i=0; i<parmCount; i++) arguments[parmCount - i - 1] = pop(); try { push(function.evaluate(arguments)); } catch (Throwable t) { throw new ExceptionSemantic(t.getMessage(), t); } } // User-defined native procedure // POP(n) - Value public final void userProcedure(NativeProcedure procedure, int parmCount) { Value arguments[] = new Value[parmCount]; for (int i=0; i<parmCount; i++) arguments[parmCount - i - 1] = pop(); try { procedure.execute(arguments); } catch (Throwable t) { throw new ExceptionSemantic(t.getMessage(), t); } } /** Operator to set a parameter's argument. * * RT: * POP - value */ public final void parmSet(int depth, int offset) { contextDisplay[depth].arguments[offset] = pop(); } /** Operator to get a parameter's argument. * RT: * PUSH - value */ public final void parmGet(int depth, int offset) { push(contextDisplay[depth].arguments[offset]); } /** Operator to set value of a local variable. * RT: * POP - value */ public final void varSet(int depth, int offset) { contextDisplay[depth].variables[offset].setValue(generator, pop()); } /** Operator to get value of a local variable. * RT: * PUSH - value */ public final void varGet(int depth, int offset) { push(contextDisplay[depth].variables[offset].getValue(generator)); } /** Operator to define a Cell. * RT: * */ public final void varSetCell(int depth, int offset, Cell cell) { contextDisplay[depth].variables[offset] = cell; } /** Operator to obtain the Cell at a given slot. * RT: * */ public final Cell varGetCell(int depth, int offset) { return contextDisplay[depth].variables[offset]; } /** Conditional Jump operator. * * POP - ValueBoolean * */ public final void branchIfTrue(int jumpTo) { if (pop().booleanValue()) jump(jumpTo); } /** Conditional Jump operator. * * POP - ValueBoolean * */ public final void branchIfFalse(int jumpTo) { if (!pop().booleanValue()) jump(jumpTo); } // Push tuple literal // POP(n) - Value // PUSH - Value (ValueTuple) public final void pushTupleLiteral(int attributeCount) { stackPointer -= attributeCount; Value[] rawTuple = new Value[attributeCount]; System.arraycopy(operandStack, stackPointer, rawTuple, 0, attributeCount); push(new ValueTuple(generator, rawTuple)); } // Extract an attribute of a tuple to the stack. // POP - Value (ValueTuple) // PUSH - Value public final void tupleGetAttribute(int index) { Value tuple[] = ((ValueTuple)pop()).getValues(); push(tuple[index]); } // Set an attribute of a tuple from the topmost value on the stack. // POP - Value (ValueTuple) // POP - Value // PUSH - Value (ValueTuple) public final void tupleSetAttribute(int index) { Value tuple[] = ((ValueTuple)pop()).getValues(); tuple[index] = pop(); push(new ValueTuple(generator, tuple)); } // Project the ValueTuple on the stack using the provided AttributeMap. // POP - Value (ValueTuple) // PUSH - Value (ValueTuple) public final void tupleProject(AttributeMap map) { push(((ValueTuple)pop()).project(map)); } // Join the ValueTuples on the stack. No checks are done to see if the tuples share // attributes in common. It is assumed that they do not, and the result is simply // the concatenation of one tuple onto another. // // POP - Value (ValueTuple) // POP - Value (ValueTuple) // PUSH - Value (ValueTuple) public final void tupleJoinDisjoint() { Value v2 = pop(); push(((ValueTuple)pop()).joinDisjoint((ValueTuple)v2)); } // Join the ValueTuples on the stack. Checks are done to ensure that // merging attributes are equal. An exception is thrown if they are not. // // POP - Value (ValueTuple) // POP - Value (ValueTuple) // PUSH - Value (ValueTuple) public final void tupleJoin(JoinMap map) { Value v2 = pop(); push(((ValueTuple)pop()).joinChecked(map, (ValueTuple)v2)); } // Insert tuple in ValueRelationLiteral. // // POP - ValueTuple // POP - ValueRelationLiteral // PUSH - ValueRelationLiteral public final void relationLiteralInsertTuple() { ValueTuple tuple = (ValueTuple)pop(); ((ValueRelationLiteral)peek()).insert(tuple); } // Push literal // PUSH - Value public final void pushLiteral(Value literal) { push(literal); } // Duplicate value on top of stack public final void duplicate() { push(peek()); } // Duplicate value under topmost on stack. Topmost remains unchanged. public final void duplicateUnder() { Value v = pop(); push(peek()); push(v); } // Swap values on top of stack public final void swap() { Value v1 = pop(); Value v2 = pop(); push(v1); push(v2); } // EXACTLY // POP - n // POP(countOfValues times) - Value // PUSH - Value public final void exactly(Generator generator, int countOfValues) { long n = pop().longValue(); while (countOfValues-- > 0) { if (pop().booleanValue()) n--; } push(ValueBoolean.select(generator, n == 0)); } // AVERAGE // POP(countOfValues times) - Value // PUSH - ValueRational public final void average(Generator generator, int countOfValues) { double total = 0; for (int i=0; i<countOfValues; i++) total += pop().doubleValue(); push(ValueRational.select(generator, total / (double)countOfValues)); } // || // POP - Value // POP - Value // PUSH - ValueString public final void concatenate(Generator generator) { Value v2 = pop(); push(ValueCharacter.select(generator, pop().stringValue() + v2.stringValue())); } }