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

nl.weeaboo.lua2.vm.StackFrame Maven / Gradle / Ivy

package nl.weeaboo.lua2.vm;

import static nl.weeaboo.lua2.vm.LuaConstants.NONE;
import static nl.weeaboo.lua2.vm.LuaNil.NIL;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Arrays;

import javax.annotation.Nullable;

import nl.weeaboo.lua2.io.LuaSerializable;

@LuaSerializable
final class StackFrame implements Externalizable {

    enum Status {
        FRESH, RUNNING, FINISHED, CLOSED
    }

    // --- Uses manual serialization, don't add variables ---
    Status status;
    LuaFunction func;  //The function that's being called
    String functionName; // The name of 'func' at the place where it's being called from
    Varargs args;      //The args given
    Varargs varargs;   //The varargs part of the arguments given

    StackFrame parent; //Link to calling context
    int parentCount;   //Number of parents
    int returnBase;    //Stack offset in parent to write return values to
    int returnCount;   //Number of return values to write in parent stack

    LuaValue[] stack = LuaConstants.NOVALS;
    UpValue[] openups = UpValue.NOUPVALUES;
    Varargs v;
    int top;
    int pc;
    // --- Uses manual serialization, don't add variables ---

    @Deprecated
    public StackFrame() {
    }

    static StackFrame newInstance(LuaFunction func, Varargs args, String functionName,
            StackFrame parent, int returnBase, int returnCount) {

        StackFrame frame = new StackFrame();
        frame.prepareCall(func, args, functionName, parent, returnBase, returnCount);
        return frame;
    }

    /** Closes every frame in the callstack. */
    static void releaseCallstack(StackFrame frame) {
        while (frame != null) {
            StackFrame parent = frame.parent;
            frame.close();
            frame = parent;
        }
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(status);
        out.writeObject(func);
        out.writeUTF(functionName);
        out.writeObject(args);
        out.writeObject(varargs);

        out.writeObject(stack);
        out.writeObject(openups);
        out.writeObject(v);
        out.writeInt(top);
        out.writeInt(pc);

        out.writeObject(parent);
        out.writeInt(parentCount);
        out.writeInt(returnBase);
        out.writeInt(returnCount);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        status = (Status)in.readObject();
        func = (LuaClosure)in.readObject();
        functionName = in.readUTF();
        args = (Varargs)in.readObject();
        varargs = (Varargs)in.readObject();

        stack = (LuaValue[])in.readObject();
        openups = (UpValue[])in.readObject();
        v = (Varargs)in.readObject();
        top = in.readInt();
        pc = in.readInt();

        parent = (StackFrame)in.readObject();
        parentCount = in.readInt();
        returnBase = in.readInt();
        returnCount = in.readInt();
    }

    public void close() {
        status = Status.CLOSED;

        closeUpValues();

        stack = LuaConstants.NOVALS;
    }

    public void closeUpValues() {
        for (int u = openups.length; --u >= 0;) {
            if (openups[u] != null) {
                openups[u].close();
                openups[u] = null;
            }
        }
    }

    private void resetExecutionState(int minStackSize) {
        if (stack.length < minStackSize) {
            stack = new LuaValue[minStackSize];
        }
        Arrays.fill(stack, NIL);

        // (re)size upValue array
        if (openups.length < stack.length) {
            openups = new UpValue[stack.length];
        } else {
            Arrays.fill(openups, null);
        }

        v = NONE;
        top = 0;
        pc = 0;
    }

    public int size() {
        return parentCount + 1;
    }

    public @Nullable LuaFunction getCallstackFunction(int level) {
        StackFrame sf = getStackFrame(level);
        if (sf == null) {
            return null;
        }
        return sf.func;
    }

    public @Nullable StackFrame getStackFrame(int level) {
        StackFrame sf = this;
        while (--level >= 1) {
            sf = sf.parent;
            if (sf == null) {
                return null;
            }
        }
        return sf;
    }

    private static @Nullable Prototype getPrototype(LuaFunction func) {
        if (func.isclosure()) {
            return func.checkclosure().getPrototype();
        } else {
            return null;
        }
    }

    public final void prepareCall(LuaFunction func, Varargs args, String functionName,
            StackFrame parent, int returnBase, int returnCount) {

        final Prototype p = getPrototype(func);

        this.status = Status.FRESH;
        this.func = func;
        this.functionName = functionName;

        this.parent = parent;
        this.parentCount = (parent != null ? parent.size() : 0);
        this.returnBase = returnBase;
        this.returnCount = returnCount;

        if (p == null) {
            resetExecutionState(0);
        } else {
            resetExecutionState(p.maxstacksize);
        }

        setArgs(args);
    }

    public void setArgs(Varargs args) {
        final Prototype p = getPrototype(func);

        this.args = args;
        this.varargs = extractVarargs(p, args);

        if (p != null) {
            //Push params on stack
            for (int i = 0; i < p.numparams; i++) {
                stack[i] = args.arg(i + 1);
            }
            if (p.isVararg >= Lua.VARARG_NEEDSARG) {
                stack[p.numparams] = new LuaTable(args.subargs(p.numparams + 1));
            }
        }
    }

    public final void prepareTailcall(LuaFunction func, Varargs args, String functionName) {
        closeUpValues(); // We're clobbering the stack, save the upvalues first

        final Prototype p = getPrototype(func);

        // Don't change status

        this.func = func;
        this.functionName = functionName;
        this.args = args;
        this.varargs = extractVarargs(p, args);

        // Don't change parent

        if (p == null) {
            resetExecutionState(0);
        } else {
            resetExecutionState(p.maxstacksize);

            // Push params on stack
            for (int i = 0; i < p.numparams; i++) {
                stack[top + i] = args.arg(i + 1);
            }
            if (p.isVararg >= Lua.VARARG_NEEDSARG) {
                stack[p.numparams] = new LuaTable(args.subargs(p.numparams + 1));
            }
        }
    }

    private static Varargs extractVarargs(Prototype p, Varargs args) {
        if (p == null || p.isVararg == 0) {
            return NONE;
        }
        return args.subargs(p.numparams + 1);
    }

    @Override
    public String toString() {
        return "StackFrame[" + func + ", args=" + args + "]";
    }

    public void setReturnedValues(Varargs args) {
        // Push args on the stack
        LuaClosure closure = func.checkclosure();
        Prototype p = closure.getPrototype();
        if (pc <= 0) {
            v = args;
            return;
        }

        int i = p.code[pc - 1];
        int opcode = (i & 0x3f);
        if (opcode != Lua.OP_CALL && opcode != Lua.OP_TAILCALL) {
            v = args;
            return;
        }

        // Yielded from a Lua function call -- push return values on the stack
        int a = ((i >> 6) & 0xff);
        int c = (i >> 14) & 0x1ff;
        LuaInterpreter.pushReturnValues(this, args, a, c);
    }

    public LuaValue getLocalValue(int index) {
        return stack[index - 1];
    }

    public void setLocalValue(int index, LuaValue value) {
        stack[index - 1] = value;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy