2017-05-07 00:18:59 +00:00
|
|
|
/*
|
2017-05-01 13:32:39 +00:00
|
|
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
2017-05-13 18:20:39 +00:00
|
|
|
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
|
2017-05-01 13:32:39 +00:00
|
|
|
* Send enquiries to dratcliffe@gmail.com
|
|
|
|
*/
|
|
|
|
|
|
|
|
package dan200.computercraft.core.lua;
|
|
|
|
|
|
|
|
import dan200.computercraft.ComputerCraft;
|
|
|
|
import dan200.computercraft.api.lua.ILuaContext;
|
|
|
|
import dan200.computercraft.api.lua.ILuaObject;
|
|
|
|
import dan200.computercraft.api.lua.ILuaTask;
|
|
|
|
import dan200.computercraft.api.lua.LuaException;
|
|
|
|
import dan200.computercraft.core.apis.ILuaAPI;
|
|
|
|
import dan200.computercraft.core.computer.Computer;
|
|
|
|
import dan200.computercraft.core.computer.ITask;
|
|
|
|
import dan200.computercraft.core.computer.MainThread;
|
2017-05-01 17:28:17 +00:00
|
|
|
import org.squiddev.cobalt.*;
|
|
|
|
import org.squiddev.cobalt.compiler.CompileException;
|
|
|
|
import org.squiddev.cobalt.compiler.LoadState;
|
|
|
|
import org.squiddev.cobalt.debug.DebugFrame;
|
|
|
|
import org.squiddev.cobalt.debug.DebugHandler;
|
|
|
|
import org.squiddev.cobalt.debug.DebugState;
|
2017-05-01 17:57:10 +00:00
|
|
|
import org.squiddev.cobalt.function.LibFunction;
|
2017-05-01 17:28:17 +00:00
|
|
|
import org.squiddev.cobalt.function.LuaFunction;
|
|
|
|
import org.squiddev.cobalt.function.VarArgFunction;
|
|
|
|
import org.squiddev.cobalt.lib.*;
|
|
|
|
import org.squiddev.cobalt.lib.platform.AbstractResourceManipulator;
|
2017-05-01 13:32:39 +00:00
|
|
|
|
2017-05-06 23:07:42 +00:00
|
|
|
import javax.annotation.Nonnull;
|
2017-05-01 17:28:17 +00:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.OutputStream;
|
2017-05-12 21:49:44 +00:00
|
|
|
import java.util.Arrays;
|
2017-05-01 13:32:39 +00:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.IdentityHashMap;
|
|
|
|
import java.util.Map;
|
|
|
|
|
2017-05-01 17:28:17 +00:00
|
|
|
import static org.squiddev.cobalt.Constants.NONE;
|
|
|
|
import static org.squiddev.cobalt.ValueFactory.valueOf;
|
|
|
|
import static org.squiddev.cobalt.ValueFactory.varargsOf;
|
|
|
|
|
|
|
|
public class CobaltLuaMachine implements ILuaMachine
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
private final Computer m_computer;
|
|
|
|
|
|
|
|
private final LuaState m_state;
|
|
|
|
private final LuaTable m_globals;
|
|
|
|
|
|
|
|
private LuaThread m_mainRoutine;
|
2017-05-01 14:48:44 +00:00
|
|
|
private String m_eventFilter;
|
|
|
|
private String m_softAbortMessage;
|
|
|
|
private String m_hardAbortMessage;
|
2017-05-01 13:32:39 +00:00
|
|
|
|
2017-05-01 17:28:17 +00:00
|
|
|
public CobaltLuaMachine( Computer computer )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-01 13:32:39 +00:00
|
|
|
m_computer = computer;
|
|
|
|
|
2017-05-01 14:48:44 +00:00
|
|
|
// Create an environment to run in
|
2017-05-01 17:28:17 +00:00
|
|
|
final LuaState state = this.m_state = new LuaState( new AbstractResourceManipulator()
|
|
|
|
{
|
2017-05-01 14:48:44 +00:00
|
|
|
@Override
|
2017-05-01 17:28:17 +00:00
|
|
|
public InputStream findResource( String filename )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
state.debug = new DebugHandler( state )
|
|
|
|
{
|
|
|
|
private int count = 0;
|
2017-05-01 17:46:28 +00:00
|
|
|
private boolean hasSoftAbort;
|
2017-05-01 17:28:17 +00:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onInstruction( DebugState ds, DebugFrame di, int pc, Varargs extras, int top ) throws LuaError
|
|
|
|
{
|
|
|
|
int count = ++this.count;
|
|
|
|
if( count > 100000 )
|
|
|
|
{
|
|
|
|
if( m_hardAbortMessage != null ) LuaThread.yield( state, NONE );
|
|
|
|
this.count = 0;
|
|
|
|
}
|
2017-05-01 17:46:28 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
handleSoftAbort();
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
|
|
|
super.onInstruction( ds, di, pc, extras, top );
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void poll() throws LuaError
|
|
|
|
{
|
|
|
|
if( m_hardAbortMessage != null ) LuaThread.yield( state, NONE );
|
2017-05-01 17:46:28 +00:00
|
|
|
handleSoftAbort();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleSoftAbort() throws LuaError {
|
|
|
|
// If the soft abort has been cleared then we can reset our flags and continue.
|
|
|
|
String message = m_softAbortMessage;
|
|
|
|
if (message == null) {
|
|
|
|
hasSoftAbort = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hasSoftAbort && m_hardAbortMessage == null) {
|
|
|
|
// If we have fired our soft abort, but we haven't been hard aborted then everything is OK.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
hasSoftAbort = true;
|
|
|
|
throw new LuaError(message);
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
m_globals = new LuaTable();
|
|
|
|
state.setupThread( m_globals );
|
|
|
|
|
|
|
|
// Add basic libraries
|
|
|
|
m_globals.load( state, new BaseLib() );
|
|
|
|
m_globals.load( state, new TableLib() );
|
|
|
|
m_globals.load( state, new StringLib() );
|
|
|
|
m_globals.load( state, new MathLib() );
|
|
|
|
m_globals.load( state, new CoroutineLib() );
|
2017-11-14 21:48:47 +00:00
|
|
|
m_globals.load( state, new Bit32Lib() );
|
2017-11-15 11:57:52 +00:00
|
|
|
if( ComputerCraft.debug_enable ) m_globals.load( state, new DebugLib() );
|
2017-05-01 17:28:17 +00:00
|
|
|
|
2017-05-01 17:57:10 +00:00
|
|
|
// Register custom load/loadstring provider which automatically adds prefixes.
|
|
|
|
LibFunction.bind( state, m_globals, PrefixLoader.class, new String[]{ "load", "loadstring" } );
|
|
|
|
|
2017-05-01 14:48:44 +00:00
|
|
|
// Remove globals we don't want to expose
|
2017-05-01 17:28:17 +00:00
|
|
|
m_globals.rawset( "collectgarbage", Constants.NIL );
|
|
|
|
m_globals.rawset( "dofile", Constants.NIL );
|
|
|
|
m_globals.rawset( "loadfile", Constants.NIL );
|
|
|
|
m_globals.rawset( "print", Constants.NIL );
|
2017-05-01 13:32:39 +00:00
|
|
|
|
|
|
|
// Add version globals
|
2017-05-01 17:28:17 +00:00
|
|
|
m_globals.rawset( "_VERSION", valueOf( "Lua 5.1" ) );
|
|
|
|
m_globals.rawset( "_HOST", valueOf( computer.getAPIEnvironment().getComputerEnvironment().getHostString() ) );
|
|
|
|
m_globals.rawset( "_CC_DEFAULT_SETTINGS", valueOf( ComputerCraft.default_computer_settings ) );
|
2017-05-01 13:32:39 +00:00
|
|
|
if( ComputerCraft.disable_lua51_features )
|
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
m_globals.rawset( "_CC_DISABLE_LUA51_FEATURES", Constants.TRUE );
|
2017-05-01 13:32:39 +00:00
|
|
|
}
|
|
|
|
|
2017-05-01 14:48:44 +00:00
|
|
|
// Our main function will go here
|
|
|
|
m_mainRoutine = null;
|
|
|
|
m_eventFilter = null;
|
2017-05-01 13:32:39 +00:00
|
|
|
|
2017-05-01 14:48:44 +00:00
|
|
|
m_softAbortMessage = null;
|
|
|
|
m_hardAbortMessage = null;
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
2017-05-01 14:48:44 +00:00
|
|
|
@Override
|
|
|
|
public void addAPI( ILuaAPI api )
|
|
|
|
{
|
|
|
|
// Add the methods of an API to the global table
|
|
|
|
LuaTable table = wrapLuaObject( api );
|
|
|
|
String[] names = api.getNames();
|
2017-05-06 23:42:00 +00:00
|
|
|
for( String name : names )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
m_globals.rawset( name, table );
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
2017-05-01 14:48:44 +00:00
|
|
|
@Override
|
|
|
|
public void loadBios( InputStream bios )
|
|
|
|
{
|
|
|
|
// Begin executing a file (ie, the bios)
|
|
|
|
if( m_mainRoutine != null )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
2017-05-01 14:48:44 +00:00
|
|
|
try
|
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
LuaFunction value = LoadState.load( m_state, bios, "@bios.lua", m_globals );
|
|
|
|
m_mainRoutine = new LuaThread( m_state, value, m_globals );
|
|
|
|
}
|
|
|
|
catch( CompileException e )
|
|
|
|
{
|
|
|
|
if( m_mainRoutine != null )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
m_mainRoutine.abandon();
|
|
|
|
m_mainRoutine = null;
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
catch( IOException e )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-13 21:26:35 +00:00
|
|
|
ComputerCraft.log.warn( "Could not load bios.lua ", e );
|
2017-05-01 14:48:44 +00:00
|
|
|
if( m_mainRoutine != null )
|
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
m_mainRoutine.abandon();
|
2017-05-01 14:48:44 +00:00
|
|
|
m_mainRoutine = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
2017-05-01 14:48:44 +00:00
|
|
|
@Override
|
|
|
|
public void handleEvent( String eventName, Object[] arguments )
|
|
|
|
{
|
|
|
|
if( m_mainRoutine == null )
|
|
|
|
{
|
|
|
|
return;
|
2017-05-01 13:32:39 +00:00
|
|
|
}
|
|
|
|
|
2017-05-01 14:48:44 +00:00
|
|
|
if( m_eventFilter != null && eventName != null && !eventName.equals( m_eventFilter ) && !eventName.equals( "terminate" ) )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
2017-05-01 14:48:44 +00:00
|
|
|
try
|
2017-05-01 17:28:17 +00:00
|
|
|
{
|
|
|
|
Varargs resumeArgs = Constants.NONE;
|
2017-05-01 14:48:44 +00:00
|
|
|
if( eventName != null )
|
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
resumeArgs = varargsOf( valueOf( eventName ), toValues( arguments ) );
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
|
|
|
Varargs results = m_mainRoutine.resume( resumeArgs );
|
|
|
|
if( m_hardAbortMessage != null )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
|
|
|
throw new LuaError( m_hardAbortMessage );
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
else if( !results.first().checkBoolean() )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
throw new LuaError( results.arg( 2 ).checkString() );
|
2017-05-01 13:32:39 +00:00
|
|
|
}
|
|
|
|
else
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
LuaValue filter = results.arg( 2 );
|
|
|
|
if( filter.isString() )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
|
|
|
m_eventFilter = filter.toString();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_eventFilter = null;
|
|
|
|
}
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
|
|
|
LuaThread mainThread = m_mainRoutine;
|
|
|
|
if( mainThread.getStatus().equals( "dead" ) )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
|
|
|
m_mainRoutine = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch( LuaError e )
|
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
m_mainRoutine.abandon();
|
2017-05-01 14:48:44 +00:00
|
|
|
m_mainRoutine = null;
|
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
m_softAbortMessage = null;
|
|
|
|
m_hardAbortMessage = null;
|
|
|
|
}
|
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
|
2017-05-01 17:28:17 +00:00
|
|
|
@Override
|
2017-05-01 14:48:44 +00:00
|
|
|
public void softAbort( String abortMessage )
|
|
|
|
{
|
|
|
|
m_softAbortMessage = abortMessage;
|
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
|
2017-05-01 17:28:17 +00:00
|
|
|
@Override
|
2017-05-01 14:48:44 +00:00
|
|
|
public void hardAbort( String abortMessage )
|
|
|
|
{
|
|
|
|
m_softAbortMessage = abortMessage;
|
|
|
|
m_hardAbortMessage = abortMessage;
|
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
|
2017-05-01 14:48:44 +00:00
|
|
|
@Override
|
|
|
|
public boolean saveState( OutputStream output )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
2017-05-01 14:48:44 +00:00
|
|
|
@Override
|
|
|
|
public boolean restoreState( InputStream input )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
2017-05-01 14:48:44 +00:00
|
|
|
@Override
|
|
|
|
public boolean isFinished()
|
|
|
|
{
|
|
|
|
return (m_mainRoutine == null);
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
2017-05-01 14:48:44 +00:00
|
|
|
@Override
|
|
|
|
public void unload()
|
|
|
|
{
|
|
|
|
if( m_mainRoutine != null )
|
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
LuaThread mainThread = m_mainRoutine;
|
2017-05-01 14:48:44 +00:00
|
|
|
mainThread.abandon();
|
|
|
|
m_mainRoutine = null;
|
|
|
|
}
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
2017-05-01 14:48:44 +00:00
|
|
|
private LuaTable wrapLuaObject( ILuaObject object )
|
|
|
|
{
|
|
|
|
LuaTable table = new LuaTable();
|
|
|
|
String[] methods = object.getMethodNames();
|
2017-05-01 17:28:17 +00:00
|
|
|
for( int i = 0; i < methods.length; ++i )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
if( methods[ i ] != null )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
|
|
|
final int method = i;
|
|
|
|
final ILuaObject apiObject = object;
|
2017-05-01 17:28:17 +00:00
|
|
|
final String methodName = methods[ i ];
|
|
|
|
table.rawset( methodName, new VarArgFunction()
|
|
|
|
{
|
2017-05-01 14:48:44 +00:00
|
|
|
@Override
|
2017-05-01 17:28:17 +00:00
|
|
|
public Varargs invoke( final LuaState state, Varargs _args ) throws LuaError
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
|
|
|
Object[] arguments = toObjects( _args, 1 );
|
2017-05-06 23:42:00 +00:00
|
|
|
Object[] results;
|
2017-05-01 14:48:44 +00:00
|
|
|
try
|
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
results = apiObject.callMethod( new ILuaContext()
|
|
|
|
{
|
2017-05-06 23:07:42 +00:00
|
|
|
@Nonnull
|
2017-05-01 14:48:44 +00:00
|
|
|
@Override
|
|
|
|
public Object[] pullEvent( String filter ) throws LuaException, InterruptedException
|
|
|
|
{
|
|
|
|
Object[] results = pullEventRaw( filter );
|
2017-05-01 17:28:17 +00:00
|
|
|
if( results.length >= 1 && results[ 0 ].equals( "terminate" ) )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
|
|
|
throw new LuaException( "Terminated", 0 );
|
|
|
|
}
|
|
|
|
return results;
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
2017-05-06 23:07:42 +00:00
|
|
|
@Nonnull
|
2017-05-01 14:48:44 +00:00
|
|
|
@Override
|
|
|
|
public Object[] pullEventRaw( String filter ) throws InterruptedException
|
|
|
|
{
|
|
|
|
return yield( new Object[] { filter } );
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
2017-05-06 23:07:42 +00:00
|
|
|
@Nonnull
|
2017-05-01 14:48:44 +00:00
|
|
|
@Override
|
|
|
|
public Object[] yield( Object[] yieldArgs ) throws InterruptedException
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
Varargs results = LuaThread.yield( state, toValues( yieldArgs ) );
|
2017-05-01 14:48:44 +00:00
|
|
|
return toObjects( results, 1 );
|
|
|
|
}
|
|
|
|
catch( OrphanedThread e )
|
|
|
|
{
|
|
|
|
throw new InterruptedException();
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
catch( Throwable e )
|
|
|
|
{
|
|
|
|
throw new RuntimeException( e );
|
|
|
|
}
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
|
|
|
|
@Override
|
2017-05-06 23:07:42 +00:00
|
|
|
public long issueMainThreadTask( @Nonnull final ILuaTask task ) throws LuaException
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
|
|
|
// Issue command
|
|
|
|
final long taskID = MainThread.getUniqueTaskID();
|
|
|
|
final ITask iTask = new ITask()
|
|
|
|
{
|
|
|
|
@Override
|
|
|
|
public Computer getOwner()
|
|
|
|
{
|
|
|
|
return m_computer;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void execute()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Object[] results = task.execute();
|
|
|
|
if( results != null )
|
|
|
|
{
|
|
|
|
Object[] eventArguments = new Object[ results.length + 2 ];
|
|
|
|
eventArguments[ 0 ] = taskID;
|
|
|
|
eventArguments[ 1 ] = true;
|
2017-05-06 23:42:00 +00:00
|
|
|
System.arraycopy( results, 0, eventArguments, 2, results.length );
|
2017-05-01 13:32:39 +00:00
|
|
|
m_computer.queueEvent( "task_complete", eventArguments );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_computer.queueEvent( "task_complete", new Object[] { taskID, true } );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch( LuaException e )
|
|
|
|
{
|
|
|
|
m_computer.queueEvent( "task_complete", new Object[] {
|
|
|
|
taskID, false, e.getMessage()
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
catch( Throwable t )
|
|
|
|
{
|
2017-05-16 14:59:09 +00:00
|
|
|
if( ComputerCraft.logPeripheralErrors )
|
|
|
|
{
|
|
|
|
ComputerCraft.log.error( "Error running task", t );
|
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
m_computer.queueEvent( "task_complete", new Object[] {
|
|
|
|
taskID, false, "Java Exception Thrown: " + t.toString()
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if( MainThread.queueTask( iTask ) )
|
|
|
|
{
|
|
|
|
return taskID;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw new LuaException( "Task limit exceeded" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-05-06 23:07:42 +00:00
|
|
|
public Object[] executeMainThreadTask( @Nonnull final ILuaTask task ) throws LuaException, InterruptedException
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
|
|
|
// Issue task
|
|
|
|
final long taskID = issueMainThreadTask( task );
|
|
|
|
|
|
|
|
// Wait for response
|
|
|
|
while( true )
|
|
|
|
{
|
|
|
|
Object[] response = pullEvent( "task_complete" );
|
|
|
|
if( response.length >= 3 && response[ 1 ] instanceof Number && response[ 2 ] instanceof Boolean )
|
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
if( ((Number) response[ 1 ]).intValue() == taskID )
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
|
|
|
Object[] returnValues = new Object[ response.length - 3 ];
|
2017-05-01 17:28:17 +00:00
|
|
|
if( (Boolean) response[ 2 ] )
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
|
|
|
// Extract the return values from the event and return them
|
2017-05-06 23:42:00 +00:00
|
|
|
System.arraycopy( response, 3, returnValues, 0, returnValues.length );
|
2017-05-01 13:32:39 +00:00
|
|
|
return returnValues;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Extract the error message from the event and raise it
|
2017-05-01 17:28:17 +00:00
|
|
|
if( response.length >= 4 && response[ 3 ] instanceof String )
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
throw new LuaException( (String) response[ 3 ] );
|
2017-05-01 13:32:39 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw new LuaException();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2017-05-01 14:48:44 +00:00
|
|
|
}, method, arguments );
|
|
|
|
}
|
|
|
|
catch( InterruptedException e )
|
|
|
|
{
|
|
|
|
throw new OrphanedThread();
|
2017-05-01 17:28:17 +00:00
|
|
|
}
|
2017-05-01 14:48:44 +00:00
|
|
|
catch( LuaException e )
|
|
|
|
{
|
|
|
|
throw new LuaError( e.getMessage(), e.getLevel() );
|
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
catch( Throwable t )
|
|
|
|
{
|
2017-05-16 14:59:09 +00:00
|
|
|
if( ComputerCraft.logPeripheralErrors )
|
|
|
|
{
|
|
|
|
ComputerCraft.log.error( "Error calling " + methodName + " on " + apiObject, t );
|
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
throw new LuaError( "Java Exception Thrown: " + t.toString(), 0 );
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
return toValues( results );
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return table;
|
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
|
2017-05-01 17:28:17 +00:00
|
|
|
private LuaValue toValue( Object object, Map<Object, LuaValue> values )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
|
|
|
if( object == null )
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
return Constants.NIL;
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
else if( object instanceof Number )
|
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
double d = ((Number) object).doubleValue();
|
|
|
|
return valueOf( d );
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
else if( object instanceof Boolean )
|
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
return valueOf( (Boolean) object );
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
else if( object instanceof String )
|
|
|
|
{
|
2017-05-01 14:48:44 +00:00
|
|
|
String s = object.toString();
|
2017-05-01 17:28:17 +00:00
|
|
|
return valueOf( s );
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-12 21:49:44 +00:00
|
|
|
else if( object instanceof byte[] )
|
|
|
|
{
|
|
|
|
byte[] b = (byte[]) object;
|
2017-05-01 17:28:17 +00:00
|
|
|
return valueOf( Arrays.copyOf( b, b.length ) );
|
2017-05-12 21:49:44 +00:00
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
else if( object instanceof Map )
|
|
|
|
{
|
|
|
|
// Table:
|
|
|
|
// Start remembering stuff
|
2017-05-01 17:28:17 +00:00
|
|
|
if( values == null )
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
values = new IdentityHashMap<>();
|
2017-05-01 13:32:39 +00:00
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
else if( values.containsKey( object ) )
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
return values.get( object );
|
2017-05-01 13:32:39 +00:00
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
LuaTable table = new LuaTable();
|
|
|
|
values.put( object, table );
|
|
|
|
|
|
|
|
// Convert all keys
|
|
|
|
for( Map.Entry<?, ?> pair : ((Map<?, ?>) object).entrySet() )
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
LuaValue key = toValue( pair.getKey(), values );
|
|
|
|
LuaValue value = toValue( pair.getValue(), values );
|
|
|
|
if( !key.isNil() && !value.isNil() )
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
table.rawset( key, value );
|
2017-05-01 13:32:39 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
return table;
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
else if( object instanceof ILuaObject )
|
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
return wrapLuaObject( (ILuaObject) object );
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
else
|
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
return Constants.NIL;
|
|
|
|
}
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
|
2017-05-01 17:28:17 +00:00
|
|
|
private Varargs toValues( Object[] objects )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
if( objects == null || objects.length == 0 )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
return Constants.NONE;
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
|
|
|
LuaValue[] values = new LuaValue[ objects.length ];
|
|
|
|
for( int i = 0; i < values.length; ++i )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
Object object = objects[ i ];
|
|
|
|
values[ i ] = toValue( object, null );
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
return varargsOf( values );
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
|
2017-05-01 17:28:17 +00:00
|
|
|
private static Object toObject( LuaValue value, Map<LuaValue, Object> objects )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
|
|
|
switch( value.type() )
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
case Constants.TNIL:
|
|
|
|
case Constants.TNONE:
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
case Constants.TINT:
|
|
|
|
case Constants.TNUMBER:
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
return value.toDouble();
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
case Constants.TBOOLEAN:
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
return value.toBoolean();
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
case Constants.TSTRING:
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
return value.toString();
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
case Constants.TTABLE:
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-01 13:32:39 +00:00
|
|
|
// Table:
|
2017-05-01 17:28:17 +00:00
|
|
|
// Start remembering stuff
|
|
|
|
if( objects == null )
|
|
|
|
{
|
|
|
|
objects = new IdentityHashMap<>();
|
|
|
|
}
|
|
|
|
else if( objects.containsKey( value ) )
|
|
|
|
{
|
|
|
|
return objects.get( value );
|
|
|
|
}
|
|
|
|
Map<Object, Object> table = new HashMap<>();
|
|
|
|
objects.put( value, table );
|
|
|
|
|
|
|
|
LuaTable luaTable = (LuaTable) value;
|
|
|
|
|
|
|
|
// Convert all keys
|
|
|
|
LuaValue k = Constants.NIL;
|
|
|
|
while( true )
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
Varargs keyValue;
|
|
|
|
try
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
keyValue = luaTable.next( k );
|
2017-05-01 13:32:39 +00:00
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
catch( LuaError luaError )
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
break;
|
2017-05-01 13:32:39 +00:00
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
k = keyValue.first();
|
|
|
|
if( k.isNil() )
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
break;
|
2017-05-01 13:32:39 +00:00
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
|
|
|
LuaValue v = keyValue.arg( 2 );
|
|
|
|
Object keyObject = toObject( k, objects );
|
|
|
|
Object valueObject = toObject( v, objects );
|
|
|
|
if( keyObject != null && valueObject != null )
|
2017-05-01 13:32:39 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
table.put( keyObject, valueObject );
|
2017-05-01 13:32:39 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
return table;
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
}
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
2017-05-01 17:28:17 +00:00
|
|
|
|
|
|
|
private static Object[] toObjects( Varargs values, int startIdx )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
2017-05-01 17:28:17 +00:00
|
|
|
int count = values.count();
|
2017-05-01 14:48:44 +00:00
|
|
|
Object[] objects = new Object[ count - startIdx + 1 ];
|
2017-05-01 17:28:17 +00:00
|
|
|
for( int n = startIdx; n <= count; ++n )
|
2017-05-01 14:48:44 +00:00
|
|
|
{
|
|
|
|
int i = n - startIdx;
|
2017-05-01 17:28:17 +00:00
|
|
|
LuaValue value = values.arg( n );
|
|
|
|
objects[ i ] = toObject( value, null );
|
2017-05-01 14:48:44 +00:00
|
|
|
}
|
|
|
|
return objects;
|
|
|
|
}
|
2017-05-01 17:57:10 +00:00
|
|
|
|
|
|
|
private static class PrefixLoader extends VarArgFunction
|
|
|
|
{
|
|
|
|
private static final LuaString FUNCTION_STR = valueOf( "function" );
|
|
|
|
private static final LuaString EQ_STR = valueOf( "=" );
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Varargs invoke( LuaState state, Varargs args ) throws LuaError
|
|
|
|
{
|
|
|
|
switch (opcode)
|
|
|
|
{
|
|
|
|
case 0: // "load", // ( func [,chunkname] ) -> chunk | nil, msg
|
|
|
|
{
|
|
|
|
LuaValue func = args.arg( 1 ).checkFunction();
|
|
|
|
LuaString chunkname = args.arg( 2 ).optLuaString( FUNCTION_STR );
|
|
|
|
if( !chunkname.startsWith( '@' ) && !chunkname.startsWith( '=' ) )
|
|
|
|
{
|
|
|
|
chunkname = OperationHelper.concat( EQ_STR, chunkname );
|
|
|
|
}
|
|
|
|
return BaseLib.loadStream( state, new StringInputStream( state, func ), chunkname );
|
|
|
|
}
|
|
|
|
case 1: // "loadstring", // ( string [,chunkname] ) -> chunk | nil, msg
|
|
|
|
{
|
|
|
|
LuaString script = args.arg( 1 ).checkLuaString();
|
|
|
|
LuaString chunkname = args.arg( 2 ).optLuaString( script );
|
|
|
|
if( !chunkname.startsWith( '@' ) && !chunkname.startsWith( '=' ) )
|
|
|
|
{
|
|
|
|
chunkname = OperationHelper.concat( EQ_STR, chunkname );
|
|
|
|
}
|
|
|
|
return BaseLib.loadStream( state, script.toInputStream(), chunkname );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static class StringInputStream extends InputStream
|
|
|
|
{
|
|
|
|
private final LuaState state;
|
|
|
|
private final LuaValue func;
|
|
|
|
private byte[] bytes;
|
|
|
|
private int offset, remaining = 0;
|
|
|
|
|
|
|
|
public StringInputStream( LuaState state, LuaValue func )
|
|
|
|
{
|
|
|
|
this.state = state;
|
|
|
|
this.func = func;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int read() throws IOException
|
|
|
|
{
|
|
|
|
if( remaining <= 0 )
|
|
|
|
{
|
|
|
|
LuaValue s;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
s = OperationHelper.call( state, func );
|
|
|
|
} catch (LuaError e)
|
|
|
|
{
|
|
|
|
throw new IOException( e );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( s.isNil() )
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
LuaString ls;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
ls = s.strvalue();
|
|
|
|
} catch (LuaError e)
|
|
|
|
{
|
|
|
|
throw new IOException( e );
|
|
|
|
}
|
|
|
|
bytes = ls.bytes;
|
|
|
|
offset = ls.offset;
|
|
|
|
remaining = ls.length;
|
|
|
|
if( remaining <= 0 )
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
--remaining;
|
|
|
|
return bytes[offset++];
|
|
|
|
}
|
|
|
|
}
|
2017-05-01 13:32:39 +00:00
|
|
|
}
|