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

nl.weeaboo.lua2.LuaRunState Maven / Gradle / Ivy

package nl.weeaboo.lua2;

import java.io.IOException;
import java.io.ObjectInputStream;

import javax.annotation.Nullable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import nl.weeaboo.lua2.io.LuaSerializable;
import nl.weeaboo.lua2.lib.ClassLoaderResourceFinder;
import nl.weeaboo.lua2.lib.ILuaResourceFinder;
import nl.weeaboo.lua2.lib.LuaResource;
import nl.weeaboo.lua2.luajava.ITypeCoercions;
import nl.weeaboo.lua2.stdlib.DebugLib;
import nl.weeaboo.lua2.stdlib.StandardLibrary;
import nl.weeaboo.lua2.vm.LuaClosure;
import nl.weeaboo.lua2.vm.LuaTable;
import nl.weeaboo.lua2.vm.LuaThread;
import nl.weeaboo.lua2.vm.Varargs;

/**
 * Global Lua VM state.
 */
@LuaSerializable
public final class LuaRunState implements ILuaResourceFinder {

    private static final long serialVersionUID = 2L;
    private static final Logger LOG = LoggerFactory.getLogger(LuaRunState.class);

    private static ThreadLocal threadInstance = new ThreadLocal<>();

    private final LuaTable globals = new LuaTable();
    private final LuaTable registry = new LuaTable();
    private final Metatables metatables = new Metatables();
    private final LuaThreadGroup threadGroup;
    private final LuaThread mainThread;

    private boolean destroyed;
    private boolean debugEnabled = true;
    private int instructionCountLimit = 10 * 1000 * 1000;

    private ILuaResourceFinder resourceFinder = new ClassLoaderResourceFinder();
    private ITypeCoercions typeCoercions = ITypeCoercions.getDefault();

    private transient @Nullable LuaThread currentThread;
    private transient int instructionCount;

    @SuppressWarnings("deprecation")
    private LuaRunState() {
        threadGroup = new LuaThreadGroup(this);

        mainThread = LuaThread.createMainThread(this, globals);
        threadGroup.add(mainThread);
    }

    /**
     * Creates a new instance using the default {@link StandardLibrary}.
     *
     * @throws LuaException If no run state could be created.
     * @see #create(StandardLibrary)
     */
    public static LuaRunState create() throws LuaException {
        return create(new StandardLibrary());
    }

    /**
     * Creates a new instance using the supplied standard library.
     *
     * @throws LuaException If no run state could be created.
     */
    public static LuaRunState create(StandardLibrary stdlib) throws LuaException {
        LuaRunState runState = new LuaRunState();
        runState.registerOnThread();
        stdlib.register();
        return runState;
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        registerOnThread();

        in.defaultReadObject();
    }

    /**
     * Destroys this Lua context. This destroys all threads and attempts to close any open resources. After
     * calling this method, any Lua resources created by this context should no longer be used.
     */
    public void destroy() {
        if (destroyed) {
            return;
        }

        LOG.debug("Destroying LuaRunState: {}", this);

        destroyed = true;
        threadGroup.destroy();

        currentThread = null;

        if (threadInstance.get() == this) {
            threadInstance.set(null);
        }
    }

    /**
     * Sets this {@link LuaRunState} as the active Lua context for the current thread.
     */
    public void registerOnThread() {
        if (threadInstance.get() != this) {
            threadInstance.set(this);
            LOG.debug("Registered LuaRunState \"{}\" on thread \"{}\"", this, Thread.currentThread());
        }
    }

    /**
     * Creates a new thread with an empty call stack.
     *
     * @see #newThread(LuaClosure, Varargs)
     */
    public LuaThread newThread() {
        return threadGroup.newThread();
    }

    /**
     * Creates a new Lua thread running the supplied function called with the supplied arguments.
     */
    public LuaThread newThread(LuaClosure func, Varargs args) {
        LuaThread thread = newThread();
        thread.pushPending(func, args);
        return thread;
    }

    /**
     * Runs all threads.
     */
    public void update() {
        if (destroyed) {
            throw new IllegalStateException("Attempted to update a destroyed LuaRunState");
        }

        registerOnThread();
        threadGroup.update();
    }

    /**
     * Called by the interpreter on every instruction (if {@link LuaRunState#isDebugEnabled()}).
     *
     * @param pc The current program counter
     * @throws LuaException If an internal assertion fails.
     */
    public void onInstruction(int pc) throws LuaException {
        instructionCount++;
        if (currentThread != null && instructionCount > instructionCountLimit) {
            throw new LuaException("Lua thread instruction limit exceeded (is there an infinite loop somewhere)?");
        }
    }

    /**
     * Returns the {@link LuaRunState} that's registered on the current thread, or {@code null} if none is registered.
     */
    public static LuaRunState getCurrent() {
        return threadInstance.get();
    }

    /**
     * Returns {@code true} if debug mode is enabled (switches on some assertions as well as the {@link DebugLib}).
     */
    public boolean isDebugEnabled() {
        return debugEnabled;
    }

    /**
     * Enables or disables debug mode.
     *
     * @see #isDebugEnabled()
     */
    public void setDebugEnabled(boolean debugEnabled) {
        this.debugEnabled = debugEnabled;
    }

    /**
     * Returns the main thread for this Lua context.
     */
    public LuaThread getMainThread() {
        return mainThread;
    }

    /**
     * Returns the currently running thread, or if no thread is running, the main thread.
     */
    public LuaThread getRunningThread() {
        LuaThread result = currentThread;
        if (result == null) {
            result = mainThread;
        }
        return result;
    }

    /**
     * The Lua globals table {@code _G}.
     */
    public LuaTable getGlobalEnvironment() {
        return globals;
    }

    /**
     * The Lua registry table.
     */
    public LuaTable getRegistry() {
        return registry;
    }

    /**
     * Returns the single-invocation instruction limit. If a threads runs more than this number of instructions
     * without yielding, an error is thrown.
     */
    public int getInstructionCountLimit() {
        return instructionCountLimit;
    }

    /**
     * @see #getInstructionCountLimit()
     */
    public void setInstructionCountLimit(int lim) {
        instructionCountLimit = lim;
    }

    /**
     * @deprecated Meant for internal use only.
     */
    @Deprecated
    public void setRunningThread(LuaThread t) {
        if (currentThread == t) {
            return;
        }

        currentThread = t;
        instructionCount = 0;
        LOG.trace("Set running thread: {}", t);
    }

    /**
     * The load path for Lua's 'require' function.
     * @return The load path, or an empty string if none is set.
     */
    public String getLuaPath() {
        return globals.get("package").get("path").optjstring("");
    }

    /**
     * Sets the load path for Lua's 'require' function.
     */
    public void setLuaPath(String path) {
        globals.get("package").set("path", path);
    }

    /**
     * Allow packages to mark themselves as loaded.
     */
    public void setPackageLoaded(String name, LuaTable value) {
        globals.get("package").get("loaded").set(name, value);
    }

    @Override
    public @Nullable LuaResource findResource(String filename) {
        return resourceFinder.findResource(filename);
    }

    /**
     * Sets the {@link ILuaResourceFinder} used to load script files.
     */
    public void setResourceFinder(ILuaResourceFinder resourceFinder) {
        this.resourceFinder = resourceFinder;
    }

    /**
     * @return {@code true} if there are still running threads.
     */
    public boolean isFinished() {
        return threadGroup.isFinished();
    }

    /**
     * The global metatables for basic types (numbers, strings, etc.)
     */
    public Metatables getMetatables() {
        return metatables;
    }

    /**
     * The global type coercion functions for converting between Java and Lua values.
     */
    public ITypeCoercions getTypeCoercions() {
        return typeCoercions;
    }

    /**
     * @see #getTypeCoercions()
     */
    public void setTypeCoercions(ITypeCoercions typeCoercions) {
        this.typeCoercions = typeCoercions;
    }

    /**
     * The global exception handler is notified when Lua code throws an exception.
     */
    public void setExceptionHandler(ILuaExceptionHandler handler) {
        threadGroup.setExceptionHandler(handler);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy