mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-12-12 11:10:29 +00:00
[skip ci] Waluaigi POC
See 12ef20d0fc/README.md
for the performance numbers, which are pretty untenable.
This commit is contained in:
parent
53dd15a213
commit
7e74725de7
@ -104,6 +104,7 @@ sourceSets {
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
maven {
|
||||
name "SquidDev"
|
||||
@ -130,6 +131,7 @@ dependencies {
|
||||
compileOnly 'com.google.auto.service:auto-service:1.0-rc7'
|
||||
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'
|
||||
|
||||
shade 'cc.tweaked.waluaigi:waluaigi:1.0-SNAPSHOT'
|
||||
shade 'org.squiddev:Cobalt:0.5.1-SNAPSHOT'
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
|
||||
|
@ -13,9 +13,9 @@ import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||
import dan200.computercraft.core.apis.*;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||
import dan200.computercraft.core.lua.CobaltLuaMachine;
|
||||
import dan200.computercraft.core.lua.ILuaMachine;
|
||||
import dan200.computercraft.core.lua.MachineResult;
|
||||
import dan200.computercraft.core.lua.waluaigi.WaluaigiLuaMachine;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.tracking.Tracking;
|
||||
import dan200.computercraft.shared.util.Colour;
|
||||
@ -386,7 +386,7 @@ final class ComputerExecutor
|
||||
}
|
||||
|
||||
// Create the lua machine
|
||||
ILuaMachine machine = new CobaltLuaMachine( computer, timeout );
|
||||
ILuaMachine machine = new WaluaigiLuaMachine( computer, timeout );
|
||||
|
||||
// Add the APIs. We unwrap them (yes, this is horrible) to get access to the underlying object.
|
||||
for( ILuaAPI api : apis ) machine.addAPI( api instanceof ApiWrapper ? ((ApiWrapper) api).getDelegate() : api );
|
||||
|
@ -10,7 +10,6 @@ import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.core.asm.LuaMethod;
|
||||
import dan200.computercraft.core.asm.ObjectSource;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.MainThread;
|
||||
import dan200.computercraft.core.computer.TimeoutState;
|
||||
import dan200.computercraft.core.tracking.Tracking;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
@ -53,7 +52,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
private final Computer computer;
|
||||
private final TimeoutState timeout;
|
||||
private final TimeoutDebugHandler debug;
|
||||
private final ILuaContext context = new CobaltLuaContext();
|
||||
private final ILuaContext context;
|
||||
|
||||
private LuaState state;
|
||||
private LuaTable globals;
|
||||
@ -65,6 +64,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
{
|
||||
this.computer = computer;
|
||||
this.timeout = timeout;
|
||||
this.context = new LuaContext( computer );
|
||||
debug = new TimeoutDebugHandler();
|
||||
|
||||
// Create an environment to run in
|
||||
@ -509,53 +509,6 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
}
|
||||
}
|
||||
|
||||
private class CobaltLuaContext implements ILuaContext
|
||||
{
|
||||
@Override
|
||||
public long issueMainThreadTask( @Nonnull final ILuaTask task ) throws LuaException
|
||||
{
|
||||
// Issue command
|
||||
final long taskID = MainThread.getUniqueTaskID();
|
||||
final Runnable iTask = () -> {
|
||||
try
|
||||
{
|
||||
Object[] results = task.execute();
|
||||
if( results != null )
|
||||
{
|
||||
Object[] eventArguments = new Object[results.length + 2];
|
||||
eventArguments[0] = taskID;
|
||||
eventArguments[1] = true;
|
||||
System.arraycopy( results, 0, eventArguments, 2, results.length );
|
||||
computer.queueEvent( "task_complete", eventArguments );
|
||||
}
|
||||
else
|
||||
{
|
||||
computer.queueEvent( "task_complete", new Object[] { taskID, true } );
|
||||
}
|
||||
}
|
||||
catch( LuaException e )
|
||||
{
|
||||
computer.queueEvent( "task_complete", new Object[] { taskID, false, e.getMessage() } );
|
||||
}
|
||||
catch( Throwable t )
|
||||
{
|
||||
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error running task", t );
|
||||
computer.queueEvent( "task_complete", new Object[] {
|
||||
taskID, false, "Java Exception Thrown: " + t,
|
||||
} );
|
||||
}
|
||||
};
|
||||
if( computer.queueMainThread( iTask ) )
|
||||
{
|
||||
return taskID;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new LuaException( "Task limit exceeded" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class HardAbortError extends Error
|
||||
{
|
||||
private static final long serialVersionUID = 7954092008586367501L;
|
||||
|
64
src/main/java/dan200/computercraft/core/lua/LuaContext.java
Normal file
64
src/main/java/dan200/computercraft/core/lua/LuaContext.java
Normal file
@ -0,0 +1,64 @@
|
||||
package dan200.computercraft.core.lua;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ILuaTask;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.MainThread;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class LuaContext implements ILuaContext
|
||||
{
|
||||
private final Computer computer;
|
||||
|
||||
public LuaContext( Computer computer )
|
||||
{
|
||||
this.computer = computer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long issueMainThreadTask( @Nonnull final ILuaTask task ) throws LuaException
|
||||
{
|
||||
// Issue command
|
||||
final long taskID = MainThread.getUniqueTaskID();
|
||||
final Runnable iTask = () -> {
|
||||
try
|
||||
{
|
||||
Object[] results = task.execute();
|
||||
if( results != null )
|
||||
{
|
||||
Object[] eventArguments = new Object[results.length + 2];
|
||||
eventArguments[0] = taskID;
|
||||
eventArguments[1] = true;
|
||||
System.arraycopy( results, 0, eventArguments, 2, results.length );
|
||||
computer.queueEvent( "task_complete", eventArguments );
|
||||
}
|
||||
else
|
||||
{
|
||||
computer.queueEvent( "task_complete", new Object[] { taskID, true } );
|
||||
}
|
||||
}
|
||||
catch( LuaException e )
|
||||
{
|
||||
computer.queueEvent( "task_complete", new Object[] { taskID, false, e.getMessage() } );
|
||||
}
|
||||
catch( Throwable t )
|
||||
{
|
||||
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error running task", t );
|
||||
computer.queueEvent( "task_complete", new Object[] {
|
||||
taskID, false, "Java Exception Thrown: " + t,
|
||||
} );
|
||||
}
|
||||
};
|
||||
if( computer.queueMainThread( iTask ) )
|
||||
{
|
||||
return taskID;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new LuaException( "Task limit exceeded" );
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,457 @@
|
||||
package dan200.computercraft.core.lua.waluaigi;
|
||||
|
||||
import cc.tweaked.waluaigi.*;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.core.asm.LuaMethod;
|
||||
import dan200.computercraft.core.asm.ObjectSource;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.TimeoutState;
|
||||
import dan200.computercraft.core.lua.LuaContext;
|
||||
import dan200.computercraft.core.lua.MachineResult;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue;
|
||||
import it.unimi.dsi.fastutil.ints.IntPriorityQueue;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
class LuaState implements ExecutionEnvironment
|
||||
{
|
||||
private static final int INITIAL_MEMORY = 1 * 1024 * 1024;
|
||||
private static final int STRING_BUFFER_SIZE = 1024;
|
||||
private static final IDynamicLuaObject[] EMPTY_OBJECTS = new IDynamicLuaObject[0];
|
||||
|
||||
private final ILuaContext context;
|
||||
private final Lua lua;
|
||||
private final Memory memory;
|
||||
|
||||
private final TimeoutState timeout;
|
||||
private int insnCount = 0;
|
||||
|
||||
private IDynamicLuaObject[] objects = EMPTY_OBJECTS;
|
||||
private final IntPriorityQueue freeSlots = new IntArrayFIFOQueue( 0 );
|
||||
private int nextSlot = 0;
|
||||
|
||||
private final int mainState;
|
||||
private final int childState;
|
||||
|
||||
private final int intBox;
|
||||
private final int stringBuffer;
|
||||
|
||||
private String eventFilter;
|
||||
|
||||
public LuaState( int maxMemory, Computer computer, TimeoutState timeout )
|
||||
{
|
||||
this.context = new LuaContext( computer );
|
||||
this.timeout = timeout;
|
||||
this.memory = new Memory( INITIAL_MEMORY, maxMemory );
|
||||
this.lua = LuaFactory.create( this, memory );
|
||||
|
||||
this.intBox = lua.malloc( 4 );
|
||||
this.stringBuffer = lua.malloc( STRING_BUFFER_SIZE );
|
||||
this.mainState = lua.waluaigi_new_state();
|
||||
this.childState = lua.lua_newthread( mainState );
|
||||
}
|
||||
|
||||
public boolean isOpen()
|
||||
{
|
||||
return mainState != 0 && childState != 0 && intBox != 0 && stringBuffer != 0;
|
||||
}
|
||||
|
||||
public int allocate( IDynamicLuaObject object )
|
||||
{
|
||||
if( !freeSlots.isEmpty() )
|
||||
{
|
||||
int slot = freeSlots.dequeueInt();
|
||||
objects[slot] = object;
|
||||
return slot;
|
||||
}
|
||||
|
||||
if( nextSlot >= objects.length )
|
||||
{
|
||||
objects = objects == EMPTY_OBJECTS ? new IDynamicLuaObject[16] : Arrays.copyOf( objects, objects.length * 2 );
|
||||
}
|
||||
|
||||
int slot = nextSlot++;
|
||||
objects[slot] = object;
|
||||
return slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void free( int object )
|
||||
{
|
||||
if( object < 0 || object >= objects.length || objects[object] == null ) return;
|
||||
objects[object] = null;
|
||||
freeSlots.enqueue( object );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int invoke( int luaState, int object, int method )
|
||||
{
|
||||
IDynamicLuaObject luaObject;
|
||||
if( object < 0 || object >= objects.length || (luaObject = objects[object]) == null )
|
||||
{
|
||||
pushString( luaState, "Invalid object" );
|
||||
lua.lua_error( luaState );
|
||||
throw new UnreachableException();
|
||||
}
|
||||
|
||||
int nargs = lua.lua_gettop( luaState );
|
||||
Object[] args = new Object[nargs];
|
||||
for( int i = 0; i < nargs; i++ ) args[i] = fromLua( luaState, i + 1 );
|
||||
|
||||
MethodResult result;
|
||||
try
|
||||
{
|
||||
result = luaObject.callMethod( context, method, new ObjectArguments( args ) );
|
||||
}
|
||||
catch( LuaException e )
|
||||
{
|
||||
lua.luaL_where( luaState, e.getLevel() );
|
||||
pushString( luaState, e.getMessage() );
|
||||
lua.lua_concat( luaState, 2 );
|
||||
lua.lua_error( luaState );
|
||||
throw new UnreachableException();
|
||||
}
|
||||
|
||||
if( result.getCallback() != null ) throw new IllegalStateException( "NYI: Yields" );
|
||||
|
||||
Object[] values = result.getResult();
|
||||
if( values == null ) return 0;
|
||||
|
||||
for( Object value : values ) pushObject( luaState, value );
|
||||
return values.length;
|
||||
}
|
||||
|
||||
private Object fromLua( int luaState, int slot )
|
||||
{
|
||||
int type = lua.lua_type( luaState, slot );
|
||||
switch( type )
|
||||
{
|
||||
case LuaConstants.LUA_TNIL:
|
||||
return null;
|
||||
case LuaConstants.LUA_TBOOLEAN:
|
||||
return lua.lua_toboolean( luaState, slot ) != 0;
|
||||
case LuaConstants.LUA_TLIGHTUSERDATA:
|
||||
return null;
|
||||
case LuaConstants.LUA_TNUMBER:
|
||||
return lua.lua_tonumberx( luaState, slot, 0 );
|
||||
case LuaConstants.LUA_TSTRING:
|
||||
return getString( luaState, slot );
|
||||
case LuaConstants.LUA_TTABLE:
|
||||
ComputerCraft.log.warn( "NYI: conversion from table" );
|
||||
// TODO: Conversion fom tables
|
||||
return null;
|
||||
case LuaConstants.LUA_TFUNCTION:
|
||||
case LuaConstants.LUA_TUSERDATA:
|
||||
case LuaConstants.LUA_TTHREAD:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void addAPI( ILuaAPI api )
|
||||
{
|
||||
int mainState = childState;
|
||||
pushLuaObject( mainState, api, true );
|
||||
for( String name : api.getNames() )
|
||||
{
|
||||
lua.lua_pushvalue( mainState, -1 );
|
||||
setShortString( name ); // TODO: Support long strings.
|
||||
lua.lua_setglobal( mainState, stringBuffer );
|
||||
}
|
||||
|
||||
lua.lua_settop( mainState, -2 );
|
||||
}
|
||||
|
||||
public MachineResult handleEvent( @Nullable String eventName, @Nullable Object[] arguments )
|
||||
{
|
||||
ComputerCraft.log.warn( "Resuming computer {} vs {}", eventName, eventFilter );
|
||||
|
||||
if( eventFilter != null && eventName != null && !eventName.equals( eventFilter ) && !eventName.equals( "terminate" ) )
|
||||
{
|
||||
return MachineResult.OK;
|
||||
}
|
||||
|
||||
// If the soft abort has been cleared then we can reset our flag.
|
||||
timeout.refresh();
|
||||
|
||||
int args = 0;
|
||||
if( eventName != null )
|
||||
{
|
||||
args++;
|
||||
pushString( childState, eventName );
|
||||
}
|
||||
if( arguments != null )
|
||||
{
|
||||
args += arguments.length;
|
||||
for( Object object : arguments ) pushObject( childState, object );
|
||||
}
|
||||
|
||||
int result = lua.lua_resume( childState, 0, args, intBox );
|
||||
switch( result )
|
||||
{
|
||||
case LuaConstants.LUA_OK:
|
||||
{
|
||||
ComputerCraft.log.warn( "Coroutine finished for no reason!" );
|
||||
return MachineResult.GENERIC_ERROR;
|
||||
}
|
||||
case LuaConstants.LUA_YIELD:
|
||||
{
|
||||
int nargs = memory.getInt( intBox );
|
||||
eventFilter = nargs > 0 && lua.lua_type( childState, 1 ) == LuaConstants.LUA_TSTRING
|
||||
? getString( childState, 1 )
|
||||
: null;
|
||||
lua.lua_settop( childState, -nargs - 1 );
|
||||
ComputerCraft.log.warn( "Computer yielded OK with {}", eventFilter );
|
||||
return MachineResult.OK;
|
||||
}
|
||||
default:
|
||||
{
|
||||
String error = getString( childState, -1 );
|
||||
ComputerCraft.log.warn( "Top level coroutine errored ({})", error );
|
||||
return MachineResult.error( error );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean pushDynamicObject( int luaState, IDynamicLuaObject object )
|
||||
{
|
||||
lua.waluaigi_pushobject( luaState, allocate( object ) );
|
||||
|
||||
String[] methods = Objects.requireNonNull( object.getMethodNames(), "Methods cannot be null" );
|
||||
for( int i = 0; i < methods.length; i++ )
|
||||
{
|
||||
pushString( luaState, methods[i] );
|
||||
lua.lua_pushvalue( luaState, -2 );
|
||||
lua.waluaigi_pushfunction( luaState, i );
|
||||
lua.lua_rawset( luaState, -4 );
|
||||
}
|
||||
|
||||
lua.lua_settop( luaState, -2 );
|
||||
|
||||
return methods.length > 0;
|
||||
}
|
||||
|
||||
private void pushLuaObject( int luaState, Object object, boolean force )
|
||||
{
|
||||
boolean hasMethods = false;
|
||||
|
||||
lua.lua_createtable( luaState, 0, 0 );
|
||||
|
||||
if( object instanceof IDynamicLuaObject )
|
||||
{
|
||||
hasMethods = pushDynamicObject( luaState, (IDynamicLuaObject) object );
|
||||
}
|
||||
|
||||
hasMethods |= pushDynamicObject( luaState, new WrappedLuaObject( object, LuaMethod.GENERATOR.getMethods( object.getClass() ) ) );
|
||||
if( object instanceof ObjectSource )
|
||||
{
|
||||
for( Object extra : ((ObjectSource) object).getExtra() )
|
||||
{
|
||||
hasMethods |= pushDynamicObject( luaState, new WrappedLuaObject( extra, LuaMethod.GENERATOR.getMethods( extra.getClass() ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
if( hasMethods ) return;
|
||||
|
||||
ComputerCraft.log.warn( "Received unknown type '{}'.", object.getClass().getName() );
|
||||
if( !force )
|
||||
{
|
||||
lua.lua_settop( luaState, -2 );
|
||||
lua.lua_pushnil( luaState );
|
||||
}
|
||||
}
|
||||
|
||||
private void pushObject( int luaState, Object object )
|
||||
{
|
||||
if( object == null )
|
||||
{
|
||||
lua.lua_pushnil( luaState );
|
||||
}
|
||||
else if( object instanceof Long || object instanceof Integer )
|
||||
{
|
||||
lua.lua_pushinteger( luaState, ((Number) object).longValue() );
|
||||
}
|
||||
else if( object instanceof Number )
|
||||
{
|
||||
lua.lua_pushnumber( luaState, ((Number) object).doubleValue() );
|
||||
}
|
||||
else if( object instanceof Boolean )
|
||||
{
|
||||
lua.lua_pushboolean( luaState, (Boolean) object ? 1 : 0 );
|
||||
}
|
||||
else if( object instanceof String )
|
||||
{
|
||||
pushString( luaState, (String) object );
|
||||
}
|
||||
else if( object instanceof byte[] )
|
||||
{
|
||||
byte[] b = (byte[]) object;
|
||||
pushString( luaState, b, 0, b.length );
|
||||
}
|
||||
else if( object instanceof ByteBuffer )
|
||||
{
|
||||
pushString( luaState, (ByteBuffer) object );
|
||||
}
|
||||
else if( object instanceof IDynamicLuaObject )
|
||||
{
|
||||
pushLuaObject( luaState, object, true );
|
||||
}
|
||||
else if( object instanceof Map )
|
||||
{
|
||||
Map<?, ?> map = (Map<?, ?>) object;
|
||||
lua.lua_createtable( luaState, 0, map.size() );
|
||||
|
||||
for( Map.Entry<?, ?> pair : map.entrySet() )
|
||||
{
|
||||
pushObject( luaState, pair.getKey() );
|
||||
pushObject( luaState, pair.getValue() );
|
||||
lua.lua_rawset( luaState, -3 );
|
||||
}
|
||||
}
|
||||
else if( object instanceof Collection )
|
||||
{
|
||||
Collection<?> collection = (Collection<?>) object;
|
||||
lua.lua_createtable( luaState, collection.size(), 0 );
|
||||
|
||||
int i = 0;
|
||||
for( Object obj : collection )
|
||||
{
|
||||
pushObject( luaState, obj );
|
||||
lua.lua_rawseti( luaState, -2, ++i );
|
||||
}
|
||||
}
|
||||
else if( object instanceof Object[] )
|
||||
{
|
||||
Object[] collection = (Object[]) object;
|
||||
lua.lua_createtable( luaState, collection.length, 0 );
|
||||
|
||||
for( int i = 0; i < collection.length; i++ )
|
||||
{
|
||||
pushObject( luaState, collection[i] );
|
||||
lua.lua_rawseti( luaState, -2, i + 1 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pushLuaObject( luaState, object, false );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeoutMode getTimeout()
|
||||
{
|
||||
if( timeout.isHardAborted() ) return TimeoutMode.HALT;
|
||||
|
||||
if( (insnCount = (insnCount + 1) & 127) == 0 )
|
||||
{
|
||||
timeout.refresh();
|
||||
if( timeout.isSoftAborted() ) return TimeoutMode.ERROR;
|
||||
if( timeout.isPaused() ) return TimeoutMode.PAUSE;
|
||||
}
|
||||
|
||||
return TimeoutMode.OK;
|
||||
}
|
||||
|
||||
public MachineResult loadString( ByteBuffer contents, String name )
|
||||
{
|
||||
int contentsLen = contents.remaining();
|
||||
int contentsLoc = lua.malloc( contentsLen );
|
||||
memory.put( contentsLoc, contents );
|
||||
|
||||
setShortString( name );
|
||||
|
||||
int result = lua.luaL_loadbufferx( childState, contentsLoc, contentsLen, stringBuffer, 0 );
|
||||
lua.free( contentsLoc );
|
||||
|
||||
return result == 0 ? MachineResult.OK : MachineResult.error( "Cannot parse bios.lua: " + getString( childState, 1 ) );
|
||||
}
|
||||
|
||||
private void setShortString( String string )
|
||||
{
|
||||
int length = string.length();
|
||||
if( length >= STRING_BUFFER_SIZE ) throw new IllegalArgumentException( "String must be <1024 characters" );
|
||||
memory.putString( stringBuffer, string );
|
||||
memory.put( stringBuffer + string.length(), (byte) 0 );
|
||||
}
|
||||
|
||||
public void pushString( int luaState, String string )
|
||||
{
|
||||
int length = string.length();
|
||||
if( length <= STRING_BUFFER_SIZE )
|
||||
{
|
||||
memory.putString( stringBuffer, string );
|
||||
lua.lua_pushlstring( luaState, stringBuffer, length );
|
||||
}
|
||||
else
|
||||
{
|
||||
int position = lua.malloc( length + 1 );
|
||||
if( position == 0 ) throw new OutOfMemoryException( "Cannot allocate string" );
|
||||
|
||||
memory.putString( position, string );
|
||||
lua.lua_pushlstring( luaState, position, length );
|
||||
lua.free( position );
|
||||
}
|
||||
}
|
||||
|
||||
public void pushString( int luaState, ByteBuffer buffer )
|
||||
{
|
||||
int length = buffer.remaining();
|
||||
if( length <= STRING_BUFFER_SIZE )
|
||||
{
|
||||
memory.put( stringBuffer, buffer );
|
||||
lua.lua_pushlstring( luaState, stringBuffer, length );
|
||||
}
|
||||
else
|
||||
{
|
||||
int position = lua.malloc( length );
|
||||
if( position == 0 ) throw new OutOfMemoryException( "Cannot allocate string" );
|
||||
|
||||
memory.put( position, buffer );
|
||||
lua.lua_pushlstring( luaState, position, length );
|
||||
lua.free( position );
|
||||
}
|
||||
}
|
||||
|
||||
public void pushString( int luaState, byte[] bytes, int offset, int length )
|
||||
{
|
||||
if( length <= STRING_BUFFER_SIZE )
|
||||
{
|
||||
memory.put( stringBuffer, bytes, offset, length );
|
||||
lua.lua_pushlstring( luaState, stringBuffer, length );
|
||||
}
|
||||
else
|
||||
{
|
||||
int position = lua.malloc( length );
|
||||
if( position == 0 ) throw new OutOfMemoryException( "Cannot allocate string" );
|
||||
|
||||
memory.put( position, bytes, offset, length );
|
||||
lua.lua_pushlstring( luaState, position, length );
|
||||
lua.free( position );
|
||||
}
|
||||
}
|
||||
|
||||
public String getString( int luaState, int slot )
|
||||
{
|
||||
int stringPos = lua.luaL_checklstring( luaState, slot, intBox );
|
||||
if( stringPos == 0 ) throw new NullPointerException( "luaL_checklstring returned 0" );
|
||||
return memory.getString( stringPos, memory.getInt( intBox ) );
|
||||
}
|
||||
|
||||
public ByteBuffer getStringAsBuffer( int luaState, int slot )
|
||||
{
|
||||
int stringPos = lua.luaL_checklstring( luaState, slot, intBox );
|
||||
if( stringPos == 0 ) throw new NullPointerException( "luaL_checklstring returned 0" );
|
||||
|
||||
int length = memory.getInt( intBox );
|
||||
ByteBuffer buffer = ByteBuffer.allocate( length );
|
||||
memory.get( stringPos, buffer );
|
||||
buffer.flip();
|
||||
return buffer;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package dan200.computercraft.core.lua.waluaigi;
|
||||
|
||||
class UnreachableException extends IllegalStateException
|
||||
{
|
||||
public UnreachableException()
|
||||
{
|
||||
super( "Unreachable code" );
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package dan200.computercraft.core.lua.waluaigi;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.TimeoutState;
|
||||
import dan200.computercraft.core.lua.ILuaMachine;
|
||||
import dan200.computercraft.core.lua.MachineResult;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class WaluaigiLuaMachine implements ILuaMachine
|
||||
{
|
||||
private final Computer computer;
|
||||
private final TimeoutState timeout;
|
||||
private LuaState state;
|
||||
|
||||
public WaluaigiLuaMachine( Computer computer, TimeoutState timeout )
|
||||
{
|
||||
this.computer = computer;
|
||||
this.timeout = timeout;
|
||||
this.state = new LuaState( 128 * 1024 * 1024, computer, timeout );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAPI( @Nonnull ILuaAPI api )
|
||||
{
|
||||
if( state != null && state.isOpen() ) state.addAPI( api );
|
||||
}
|
||||
|
||||
@Override
|
||||
public MachineResult loadBios( @Nonnull InputStream bios )
|
||||
{
|
||||
if( state == null || !state.isOpen() ) return MachineResult.error( "Could not create Lua state" );
|
||||
|
||||
byte[] bytes;
|
||||
try
|
||||
{
|
||||
bytes = ByteStreams.toByteArray( bios );
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
ComputerCraft.log.error( "Error reading bios", e );
|
||||
return MachineResult.error( "Could not load bios" );
|
||||
}
|
||||
|
||||
MachineResult load = state.loadString( ByteBuffer.wrap( bytes ), "@bios.lua" );
|
||||
if( load.isError() ) return load;
|
||||
|
||||
return MachineResult.OK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MachineResult handleEvent( @Nullable String eventName, @Nullable Object[] arguments )
|
||||
{
|
||||
if( state == null || !state.isOpen() ) return MachineResult.error( "Machine is not open" );
|
||||
return state.handleEvent( eventName, arguments );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
state = null;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package dan200.computercraft.core.lua.waluaigi;
|
||||
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.core.asm.LuaMethod;
|
||||
import dan200.computercraft.core.asm.NamedMethod;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
class WrappedLuaObject implements IDynamicLuaObject
|
||||
{
|
||||
private final Object object;
|
||||
private final List<NamedMethod<LuaMethod>> methods;
|
||||
|
||||
public WrappedLuaObject( Object object, List<NamedMethod<LuaMethod>> methods )
|
||||
{
|
||||
this.object = object;
|
||||
this.methods = methods;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
// TODO: Make this less allocation happy.
|
||||
String[] methods = new String[this.methods.size()];
|
||||
int i = 0;
|
||||
for( NamedMethod<LuaMethod> method : this.methods ) methods[i++] = method.getName();
|
||||
return methods;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public MethodResult callMethod( @Nonnull ILuaContext context, int method, @Nonnull IArguments arguments ) throws LuaException
|
||||
{
|
||||
if( method < 0 || method >= methods.size() ) throw new LuaException( "Unknown method" );
|
||||
return methods.get( method ).getMethod().apply( object, context, arguments );
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ local expect
|
||||
|
||||
do
|
||||
local h = fs.open("rom/modules/main/cc/expect.lua", "r")
|
||||
local f, err = loadstring(h.readAll(), "@expect.lua")
|
||||
local f, err = load(h.readAll(), "@expect.lua")
|
||||
h.close()
|
||||
|
||||
if not f then error(err) end
|
||||
|
Loading…
Reference in New Issue
Block a user