Merge pull request #163 from SquidDev-CC/ComputerCraft/feature/cobalt

Replace LuaJ with Cobalt
This commit is contained in:
SquidDev 2017-11-14 21:48:47 +00:00
commit 540e2e25aa
5 changed files with 353 additions and 337 deletions

View File

@ -46,17 +46,29 @@
name = "JEI" name = "JEI"
url = "http://dvs1.progwml6.com/files/maven" url = "http://dvs1.progwml6.com/files/maven"
} }
maven {
name = "squiddev"
url = "https://dl.bintray.com/squiddev/maven"
}
}
configurations {
shade
compile.extendsFrom shade
} }
dependencies { dependencies {
deobfProvided "mezz.jei:jei_1.12:4.7.5.86:api" deobfProvided "mezz.jei:jei_1.12:4.7.5.86:api"
runtime "mezz.jei:jei_1.12:4.7.5.86" runtime "mezz.jei:jei_1.12:4.7.5.86"
shade 'org.squiddev:Cobalt:0.3.0'
} }
jar { jar {
manifest { manifest {
attributes('FMLAT': 'computercraft_at.cfg') attributes('FMLAT': 'computercraft_at.cfg')
} }
from configurations.shade.collect { it.isDirectory() ? it : zipTree(it) }
} }
import org.ajoberstar.grgit.Grgit import org.ajoberstar.grgit.Grgit

View File

@ -35,12 +35,4 @@ zip -r $OUTPUTJAR api/src/dan200/computercraft > /dev/null
cd .. cd ..
rm -rf deploy/api rm -rf deploy/api
echo "Adding LuaJ to deployment..."
mkdir deploy/luaj
cd deploy/luaj
jar xf ../../libs/luaj-jse-2.0.3.jar
zip -r ../$OUTPUTJAR org > /dev/null
cd ../..
rm -rf deploy/luaj
echo "Done." echo "Done."

View File

@ -15,7 +15,7 @@
import dan200.computercraft.core.filesystem.FileSystem; import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.filesystem.FileSystemException; import dan200.computercraft.core.filesystem.FileSystemException;
import dan200.computercraft.core.lua.ILuaMachine; import dan200.computercraft.core.lua.ILuaMachine;
import dan200.computercraft.core.lua.LuaJLuaMachine; import dan200.computercraft.core.lua.CobaltLuaMachine;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import java.io.IOException; import java.io.IOException;
@ -621,7 +621,7 @@ private void createAPIs()
private void initLua() private void initLua()
{ {
// Create the lua machine // Create the lua machine
ILuaMachine machine = new LuaJLuaMachine( this ); ILuaMachine machine = new CobaltLuaMachine( this );
// Add the APIs // Add the APIs
for(ILuaAPI api : m_apis) for(ILuaAPI api : m_apis)

View File

@ -16,106 +16,130 @@
import dan200.computercraft.core.computer.ITask; import dan200.computercraft.core.computer.ITask;
import dan200.computercraft.core.computer.MainThread; import dan200.computercraft.core.computer.MainThread;
import org.luaj.vm2.*; import org.squiddev.cobalt.*;
import org.luaj.vm2.lib.Bit32Lib; import org.squiddev.cobalt.compiler.CompileException;
import org.luaj.vm2.lib.OneArgFunction; import org.squiddev.cobalt.compiler.LoadState;
import org.luaj.vm2.lib.VarArgFunction; import org.squiddev.cobalt.debug.DebugFrame;
import org.luaj.vm2.lib.ZeroArgFunction; import org.squiddev.cobalt.debug.DebugHandler;
import org.luaj.vm2.lib.jse.JsePlatform; import org.squiddev.cobalt.debug.DebugState;
import org.squiddev.cobalt.function.LibFunction;
import org.squiddev.cobalt.function.LuaFunction;
import org.squiddev.cobalt.function.VarArgFunction;
import org.squiddev.cobalt.lib.*;
import org.squiddev.cobalt.lib.platform.AbstractResourceManipulator;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.*; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.Map; import java.util.Map;
public class LuaJLuaMachine implements ILuaMachine import static org.squiddev.cobalt.Constants.NONE;
{ import static org.squiddev.cobalt.ValueFactory.valueOf;
private Computer m_computer; import static org.squiddev.cobalt.ValueFactory.varargsOf;
private LuaValue m_globals; public class CobaltLuaMachine implements ILuaMachine
private LuaValue m_loadString; {
private LuaValue m_assert; private final Computer m_computer;
private LuaValue m_coroutine_create;
private LuaValue m_coroutine_resume; private final LuaState m_state;
private LuaValue m_coroutine_yield; private final LuaTable m_globals;
private LuaValue m_mainRoutine; private LuaThread m_mainRoutine;
private String m_eventFilter; private String m_eventFilter;
private String m_softAbortMessage; private String m_softAbortMessage;
private String m_hardAbortMessage; private String m_hardAbortMessage;
private Map<Object, LuaValue> m_valuesInProgress; public CobaltLuaMachine( Computer computer )
private Map<LuaValue, Object> m_objectsInProgress;
public LuaJLuaMachine( Computer computer )
{ {
m_computer = computer; m_computer = computer;
// Create an environment to run in // Create an environment to run in
m_globals = JsePlatform.debugGlobals(); final LuaState state = this.m_state = new LuaState( new AbstractResourceManipulator()
m_globals.load( new Bit32Lib() ); {
m_loadString = m_globals.get("loadstring");
m_assert = m_globals.get("assert");
LuaValue coroutine = m_globals.get("coroutine");
final LuaValue native_coroutine_create = coroutine.get("create");
LuaValue debug = m_globals.get("debug");
final LuaValue debug_sethook = debug.get("sethook");
coroutine.set("create", new OneArgFunction() {
@Override @Override
public LuaValue call( LuaValue value ) public InputStream findResource( String filename )
{ {
final LuaThread thread = native_coroutine_create.call( value ).checkthread(); return null;
debug_sethook.invoke( new LuaValue[] {
thread,
new ZeroArgFunction() {
@Override
public LuaValue call() {
String hardAbortMessage = m_hardAbortMessage;
if( hardAbortMessage != null )
{
LuaThread.yield(LuaValue.NIL);
}
return LuaValue.NIL;
}
},
LuaValue.NIL,
LuaValue.valueOf(100000)
} );
return thread;
} }
}); } );
state.debug = new DebugHandler( state )
m_coroutine_create = coroutine.get("create"); {
m_coroutine_resume = coroutine.get("resume"); private int count = 0;
m_coroutine_yield = coroutine.get("yield"); private boolean hasSoftAbort;
@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;
}
else
{
handleSoftAbort();
}
super.onInstruction( ds, di, pc, extras, top );
}
@Override
public void poll() throws LuaError
{
if( m_hardAbortMessage != null ) LuaThread.yield( state, NONE );
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);
}
};
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() );
m_globals.load( state, new Bit32Lib() );
// Register custom load/loadstring provider which automatically adds prefixes.
LibFunction.bind( state, m_globals, PrefixLoader.class, new String[]{ "load", "loadstring" } );
// Remove globals we don't want to expose // Remove globals we don't want to expose
m_globals.set( "collectgarbage", LuaValue.NIL ); m_globals.rawset( "collectgarbage", Constants.NIL );
m_globals.set( "dofile", LuaValue.NIL ); m_globals.rawset( "dofile", Constants.NIL );
m_globals.set( "loadfile", LuaValue.NIL ); m_globals.rawset( "loadfile", Constants.NIL );
m_globals.set( "module", LuaValue.NIL ); m_globals.rawset( "print", Constants.NIL );
m_globals.set( "require", LuaValue.NIL );
m_globals.set( "package", LuaValue.NIL );
m_globals.set( "io", LuaValue.NIL );
m_globals.set( "os", LuaValue.NIL );
m_globals.set( "print", LuaValue.NIL );
m_globals.set( "luajava", LuaValue.NIL );
m_globals.set( "debug", LuaValue.NIL );
m_globals.set( "newproxy", LuaValue.NIL );
m_globals.set( "__inext", LuaValue.NIL );
// Add version globals // Add version globals
m_globals.set( "_VERSION", "Lua 5.1" ); m_globals.rawset( "_VERSION", valueOf( "Lua 5.1" ) );
m_globals.set( "_HOST", computer.getAPIEnvironment().getComputerEnvironment().getHostString() ); m_globals.rawset( "_HOST", valueOf( computer.getAPIEnvironment().getComputerEnvironment().getHostString() ) );
m_globals.set( "_CC_DEFAULT_SETTINGS", toValue( ComputerCraft.default_computer_settings ) ); m_globals.rawset( "_CC_DEFAULT_SETTINGS", valueOf( ComputerCraft.default_computer_settings ) );
if( ComputerCraft.disable_lua51_features ) if( ComputerCraft.disable_lua51_features )
{ {
m_globals.set( "_CC_DISABLE_LUA51_FEATURES", toValue( true ) ); m_globals.rawset( "_CC_DISABLE_LUA51_FEATURES", Constants.TRUE );
} }
// Our main function will go here // Our main function will go here
@ -125,7 +149,7 @@ public LuaValue call() {
m_softAbortMessage = null; m_softAbortMessage = null;
m_hardAbortMessage = null; m_hardAbortMessage = null;
} }
@Override @Override
public void addAPI( ILuaAPI api ) public void addAPI( ILuaAPI api )
{ {
@ -134,10 +158,10 @@ public void addAPI( ILuaAPI api )
String[] names = api.getNames(); String[] names = api.getNames();
for( String name : names ) for( String name : names )
{ {
m_globals.set( name, table ); m_globals.rawset( name, table );
} }
} }
@Override @Override
public void loadBios( InputStream bios ) public void loadBios( InputStream bios )
{ {
@ -146,56 +170,31 @@ public void loadBios( InputStream bios )
{ {
return; return;
} }
try try
{ {
// Read the whole bios into a string LuaFunction value = LoadState.load( m_state, bios, "@bios.lua", m_globals );
String biosText; m_mainRoutine = new LuaThread( m_state, value, m_globals );
try
{
InputStreamReader isr;
try
{
isr = new InputStreamReader( bios, "UTF-8" );
}
catch( UnsupportedEncodingException e )
{
isr = new InputStreamReader( bios );
}
BufferedReader reader = new BufferedReader( isr );
StringBuilder fileText = new StringBuilder( "" );
String line = reader.readLine();
while( line != null ) {
fileText.append( line );
line = reader.readLine();
if( line != null ) {
fileText.append( "\n" );
}
}
biosText = fileText.toString();
}
catch( IOException e )
{
throw new LuaError( "Could not read file" );
}
// Load it
LuaValue program = m_assert.call( m_loadString.call(
toValue( biosText ), toValue( "bios.lua" )
));
m_mainRoutine = m_coroutine_create.call( program );
} }
catch( LuaError e ) catch( CompileException e )
{
if( m_mainRoutine != null )
{
m_mainRoutine.abandon();
m_mainRoutine = null;
}
}
catch( IOException e )
{ {
ComputerCraft.log.warn( "Could not load bios.lua ", e ); ComputerCraft.log.warn( "Could not load bios.lua ", e );
if( m_mainRoutine != null ) if( m_mainRoutine != null )
{ {
((LuaThread)m_mainRoutine).abandon(); m_mainRoutine.abandon();
m_mainRoutine = null; m_mainRoutine = null;
} }
} }
} }
@Override @Override
public void handleEvent( String eventName, Object[] arguments ) public void handleEvent( String eventName, Object[] arguments )
{ {
@ -208,35 +207,28 @@ public void handleEvent( String eventName, Object[] arguments )
{ {
return; return;
} }
try try
{ {
LuaValue[] resumeArgs; Varargs resumeArgs = Constants.NONE;
if( eventName != null ) if( eventName != null )
{ {
resumeArgs = toValues( arguments, 2 ); resumeArgs = varargsOf( valueOf( eventName ), toValues( arguments ) );
resumeArgs[0] = m_mainRoutine;
resumeArgs[1] = toValue( eventName );
} }
else
{ Varargs results = m_mainRoutine.resume( resumeArgs );
resumeArgs = new LuaValue[1]; if( m_hardAbortMessage != null )
resumeArgs[0] = m_mainRoutine;
}
Varargs results = m_coroutine_resume.invoke( LuaValue.varargsOf( resumeArgs ) );
if( m_hardAbortMessage != null )
{ {
throw new LuaError( m_hardAbortMessage ); throw new LuaError( m_hardAbortMessage );
} }
else if( results.arg1().checkboolean() == false ) else if( !results.first().checkBoolean() )
{ {
throw new LuaError( results.arg(2).checkstring().toString() ); throw new LuaError( results.arg( 2 ).checkString() );
} }
else else
{ {
LuaValue filter = results.arg(2); LuaValue filter = results.arg( 2 );
if( filter.isstring() ) if( filter.isString() )
{ {
m_eventFilter = filter.toString(); m_eventFilter = filter.toString();
} }
@ -245,16 +237,16 @@ else if( results.arg1().checkboolean() == false )
m_eventFilter = null; m_eventFilter = null;
} }
} }
LuaThread mainThread = (LuaThread)m_mainRoutine; LuaThread mainThread = m_mainRoutine;
if( mainThread.getStatus().equals("dead") ) if( mainThread.getStatus().equals( "dead" ) )
{ {
m_mainRoutine = null; m_mainRoutine = null;
} }
} }
catch( LuaError e ) catch( LuaError e )
{ {
((LuaThread)m_mainRoutine).abandon(); m_mainRoutine.abandon();
m_mainRoutine = null; m_mainRoutine = null;
} }
finally finally
@ -264,13 +256,13 @@ else if( results.arg1().checkboolean() == false )
} }
} }
@Override @Override
public void softAbort( String abortMessage ) public void softAbort( String abortMessage )
{ {
m_softAbortMessage = abortMessage; m_softAbortMessage = abortMessage;
} }
@Override @Override
public void hardAbort( String abortMessage ) public void hardAbort( String abortMessage )
{ {
m_softAbortMessage = abortMessage; m_softAbortMessage = abortMessage;
@ -282,100 +274,88 @@ public boolean saveState( OutputStream output )
{ {
return false; return false;
} }
@Override @Override
public boolean restoreState( InputStream input ) public boolean restoreState( InputStream input )
{ {
return false; return false;
} }
@Override @Override
public boolean isFinished() public boolean isFinished()
{ {
return (m_mainRoutine == null); return (m_mainRoutine == null);
} }
@Override @Override
public void unload() public void unload()
{ {
if( m_mainRoutine != null ) if( m_mainRoutine != null )
{ {
LuaThread mainThread = (LuaThread)m_mainRoutine; LuaThread mainThread = m_mainRoutine;
mainThread.abandon(); mainThread.abandon();
m_mainRoutine = null; m_mainRoutine = null;
} }
} }
private void tryAbort() throws LuaError
{
// while( m_stopped )
// {
// m_coroutine_yield.call();
// }
String abortMessage = m_softAbortMessage;
if( abortMessage != null )
{
m_softAbortMessage = null;
m_hardAbortMessage = null;
throw new LuaError( abortMessage );
}
}
private LuaTable wrapLuaObject( ILuaObject object ) private LuaTable wrapLuaObject( ILuaObject object )
{ {
LuaTable table = new LuaTable(); LuaTable table = new LuaTable();
String[] methods = object.getMethodNames(); String[] methods = object.getMethodNames();
for(int i=0; i<methods.length; ++i ) for( int i = 0; i < methods.length; ++i )
{ {
if( methods[i] != null ) if( methods[ i ] != null )
{ {
final int method = i; final int method = i;
final ILuaObject apiObject = object; final ILuaObject apiObject = object;
final String methodName = methods[i]; final String methodName = methods[ i ];
table.set( methodName, new VarArgFunction() { table.rawset( methodName, new VarArgFunction()
{
@Override @Override
public Varargs invoke( Varargs _args ) public Varargs invoke( final LuaState state, Varargs _args ) throws LuaError
{ {
tryAbort();
Object[] arguments = toObjects( _args, 1 ); Object[] arguments = toObjects( _args, 1 );
Object[] results; Object[] results;
try try
{ {
results = apiObject.callMethod( new ILuaContext() { results = apiObject.callMethod( new ILuaContext()
{
@Nonnull @Nonnull
@Override @Override
public Object[] pullEvent( String filter ) throws LuaException, InterruptedException public Object[] pullEvent( String filter ) throws LuaException, InterruptedException
{ {
Object[] results = pullEventRaw( filter ); Object[] results = pullEventRaw( filter );
if( results.length >= 1 && results[0].equals( "terminate" ) ) if( results.length >= 1 && results[ 0 ].equals( "terminate" ) )
{ {
throw new LuaException( "Terminated", 0 ); throw new LuaException( "Terminated", 0 );
} }
return results; return results;
} }
@Nonnull @Nonnull
@Override @Override
public Object[] pullEventRaw( String filter ) throws InterruptedException public Object[] pullEventRaw( String filter ) throws InterruptedException
{ {
return yield( new Object[] { filter } ); return yield( new Object[] { filter } );
} }
@Nonnull @Nonnull
@Override @Override
public Object[] yield( Object[] yieldArgs ) throws InterruptedException public Object[] yield( Object[] yieldArgs ) throws InterruptedException
{ {
try try
{ {
LuaValue[] yieldValues = toValues( yieldArgs, 0 ); Varargs results = LuaThread.yield( state, toValues( yieldArgs ) );
Varargs results = m_coroutine_yield.invoke( LuaValue.varargsOf( yieldValues ) );
return toObjects( results, 1 ); return toObjects( results, 1 );
} }
catch( OrphanedThread e ) catch( OrphanedThread e )
{ {
throw new InterruptedException(); throw new InterruptedException();
} }
catch( Throwable e )
{
throw new RuntimeException( e );
}
} }
@Override @Override
@ -450,10 +430,10 @@ public Object[] executeMainThreadTask( @Nonnull final ILuaTask task ) throws Lua
Object[] response = pullEvent( "task_complete" ); Object[] response = pullEvent( "task_complete" );
if( response.length >= 3 && response[ 1 ] instanceof Number && response[ 2 ] instanceof Boolean ) if( response.length >= 3 && response[ 1 ] instanceof Number && response[ 2 ] instanceof Boolean )
{ {
if( ( (Number)response[ 1 ] ).intValue() == taskID ) if( ((Number) response[ 1 ]).intValue() == taskID )
{ {
Object[] returnValues = new Object[ response.length - 3 ]; Object[] returnValues = new Object[ response.length - 3 ];
if( (Boolean)response[ 2 ] ) if( (Boolean) response[ 2 ] )
{ {
// Extract the return values from the event and return them // Extract the return values from the event and return them
System.arraycopy( response, 3, returnValues, 0, returnValues.length ); System.arraycopy( response, 3, returnValues, 0, returnValues.length );
@ -462,9 +442,9 @@ public Object[] executeMainThreadTask( @Nonnull final ILuaTask task ) throws Lua
else else
{ {
// Extract the error message from the event and raise it // Extract the error message from the event and raise it
if( response.length >= 4 && response[3] instanceof String ) if( response.length >= 4 && response[ 3 ] instanceof String )
{ {
throw new LuaException( (String)response[ 3 ] ); throw new LuaException( (String) response[ 3 ] );
} }
else else
{ {
@ -481,7 +461,7 @@ public Object[] executeMainThreadTask( @Nonnull final ILuaTask task ) throws Lua
catch( InterruptedException e ) catch( InterruptedException e )
{ {
throw new OrphanedThread(); throw new OrphanedThread();
} }
catch( LuaException e ) catch( LuaException e )
{ {
throw new LuaError( e.getMessage(), e.getLevel() ); throw new LuaError( e.getMessage(), e.getLevel() );
@ -494,7 +474,7 @@ public Object[] executeMainThreadTask( @Nonnull final ILuaTask task ) throws Lua
} }
throw new LuaError( "Java Exception Thrown: " + t.toString(), 0 ); throw new LuaError( "Java Exception Thrown: " + t.toString(), 0 );
} }
return LuaValue.varargsOf( toValues( results, 0 ) ); return toValues( results );
} }
} ); } );
} }
@ -502,192 +482,257 @@ public Object[] executeMainThreadTask( @Nonnull final ILuaTask task ) throws Lua
return table; return table;
} }
private LuaValue toValue( Object object ) private LuaValue toValue( Object object, Map<Object, LuaValue> values )
{ {
if( object == null ) if( object == null )
{ {
return LuaValue.NIL; return Constants.NIL;
} }
else if( object instanceof Number ) else if( object instanceof Number )
{ {
double d = ((Number)object).doubleValue(); double d = ((Number) object).doubleValue();
return LuaValue.valueOf( d ); return valueOf( d );
} }
else if( object instanceof Boolean ) else if( object instanceof Boolean )
{ {
boolean b = (Boolean) object; return valueOf( (Boolean) object );
return LuaValue.valueOf( b );
} }
else if( object instanceof String ) else if( object instanceof String )
{ {
String s = object.toString(); String s = object.toString();
return LuaValue.valueOf( s ); return valueOf( s );
} }
else if( object instanceof byte[] ) else if( object instanceof byte[] )
{ {
byte[] b = (byte[]) object; byte[] b = (byte[]) object;
return LuaValue.valueOf( Arrays.copyOf( b, b.length ) ); return valueOf( Arrays.copyOf( b, b.length ) );
} }
else if( object instanceof Map ) else if( object instanceof Map )
{ {
// Table: // Table:
// Start remembering stuff // Start remembering stuff
boolean clearWhenDone = false; if( values == null )
try
{ {
if( m_valuesInProgress == null ) values = new IdentityHashMap<>();
{ }
m_valuesInProgress = new IdentityHashMap<>(); else if( values.containsKey( object ) )
clearWhenDone = true; {
} return values.get( object );
else if( m_valuesInProgress.containsKey( object ) ) }
{ LuaTable table = new LuaTable();
return m_valuesInProgress.get( object ); values.put( object, table );
}
LuaValue table = new LuaTable();
m_valuesInProgress.put( object, table );
// Convert all keys // Convert all keys
for( Map.Entry<?, ?> pair : ((Map<?, ?>) object).entrySet() ) for( Map.Entry<?, ?> pair : ((Map<?, ?>) object).entrySet() )
{
LuaValue key = toValue( pair.getKey() );
LuaValue value = toValue( pair.getValue() );
if( !key.isnil() && !value.isnil() )
{
table.set( key, value );
}
}
return table;
}
finally
{ {
// Clear (if exiting top level) LuaValue key = toValue( pair.getKey(), values );
if( clearWhenDone ) LuaValue value = toValue( pair.getValue(), values );
if( !key.isNil() && !value.isNil() )
{ {
m_valuesInProgress = null; table.rawset( key, value );
} }
} }
return table;
} }
else if( object instanceof ILuaObject ) else if( object instanceof ILuaObject )
{ {
return wrapLuaObject( (ILuaObject)object ); return wrapLuaObject( (ILuaObject) object );
} }
else else
{ {
return LuaValue.NIL; return Constants.NIL;
} }
} }
private LuaValue[] toValues( Object[] objects, int leaveEmpty ) private Varargs toValues( Object[] objects )
{ {
if( objects == null || objects.length == 0 ) if( objects == null || objects.length == 0 )
{ {
return new LuaValue[ leaveEmpty ]; return Constants.NONE;
} }
LuaValue[] values = new LuaValue[objects.length + leaveEmpty]; LuaValue[] values = new LuaValue[ objects.length ];
for( int i=0; i<values.length; ++i ) for( int i = 0; i < values.length; ++i )
{ {
if( i < leaveEmpty ) Object object = objects[ i ];
{ values[ i ] = toValue( object, null );
values[i] = null;
continue;
}
Object object = objects[i - leaveEmpty];
values[i] = toValue( object );
} }
return values; return varargsOf( values );
} }
private Object toObject( LuaValue value ) private static Object toObject( LuaValue value, Map<LuaValue, Object> objects )
{ {
switch( value.type() ) switch( value.type() )
{ {
case LuaValue.TNIL: case Constants.TNIL:
case LuaValue.TNONE: case Constants.TNONE:
{ {
return null; return null;
} }
case LuaValue.TINT: case Constants.TINT:
case LuaValue.TNUMBER: case Constants.TNUMBER:
{ {
return value.todouble(); return value.toDouble();
} }
case LuaValue.TBOOLEAN: case Constants.TBOOLEAN:
{ {
return value.toboolean(); return value.toBoolean();
} }
case LuaValue.TSTRING: case Constants.TSTRING:
{ {
LuaString str = value.checkstring(); return value.toString();
return str.tojstring();
} }
case LuaValue.TTABLE: case Constants.TTABLE:
{ {
// Table: // Table:
boolean clearWhenDone = false; // Start remembering stuff
try if( objects == null )
{ {
// Start remembering stuff objects = new IdentityHashMap<>();
if( m_objectsInProgress == null )
{
m_objectsInProgress = new IdentityHashMap<>();
clearWhenDone = true;
}
else if( m_objectsInProgress.containsKey( value ) )
{
return m_objectsInProgress.get( value );
}
Map<Object, Object> table = new HashMap<>();
m_objectsInProgress.put( value, table );
// Convert all keys
LuaValue k = LuaValue.NIL;
while( true )
{
Varargs keyValue = value.next( k );
k = keyValue.arg1();
if( k.isnil() )
{
break;
}
LuaValue v = keyValue.arg(2);
Object keyObject = toObject(k);
Object valueObject = toObject(v);
if( keyObject != null && valueObject != null )
{
table.put( keyObject, valueObject );
}
}
return table;
} }
finally else if( objects.containsKey( value ) )
{ {
// Clear (if exiting top level) return objects.get( value );
if( clearWhenDone ) }
Map<Object, Object> table = new HashMap<>();
objects.put( value, table );
LuaTable luaTable = (LuaTable) value;
// Convert all keys
LuaValue k = Constants.NIL;
while( true )
{
Varargs keyValue;
try
{ {
m_objectsInProgress = null; keyValue = luaTable.next( k );
}
catch( LuaError luaError )
{
break;
}
k = keyValue.first();
if( k.isNil() )
{
break;
}
LuaValue v = keyValue.arg( 2 );
Object keyObject = toObject( k, objects );
Object valueObject = toObject( v, objects );
if( keyObject != null && valueObject != null )
{
table.put( keyObject, valueObject );
} }
} }
return table;
} }
default: default:
{ {
return null; return null;
} }
} }
} }
private Object[] toObjects( Varargs values, int startIdx ) private static Object[] toObjects( Varargs values, int startIdx )
{ {
int count = values.narg(); int count = values.count();
Object[] objects = new Object[ count - startIdx + 1 ]; Object[] objects = new Object[ count - startIdx + 1 ];
for( int n=startIdx; n<=count; ++n ) for( int n = startIdx; n <= count; ++n )
{ {
int i = n - startIdx; int i = n - startIdx;
LuaValue value = values.arg(n); LuaValue value = values.arg( n );
objects[i] = toObject( value ); objects[ i ] = toObject( value, null );
} }
return objects; return objects;
} }
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++];
}
}
} }

View File

@ -142,39 +142,6 @@ if _VERSION == "Lua 5.3" and not bit32 then
]] )() ]] )()
end end
if string.find( _HOST, "ComputerCraft" ) == 1 then
-- Prevent access to metatables or environments of strings, as these are global between all computers
local nativegetmetatable = getmetatable
local nativeerror = error
local nativetype = type
local string_metatable = nativegetmetatable("")
function getmetatable( t )
local mt = nativegetmetatable( t )
if mt == string_metatable then
nativeerror( "Attempt to access string metatable", 2 )
else
return mt
end
end
if _VERSION == "Lua 5.1" and not _CC_DISABLE_LUA51_FEATURES then
local string_env = nativegetfenv(("").gsub)
function getfenv( env )
if env == nil then
env = 2
elseif nativetype( env ) == "number" and env > 0 then
env = env + 1
end
local fenv = nativegetfenv(env)
if fenv == string_env then
--nativeerror( "Attempt to access string metatable", 2 )
return nativegetfenv( 0 )
else
return fenv
end
end
end
end
-- Install lua parts of the os api -- Install lua parts of the os api
function os.version() function os.version()
return "CraftOS 1.8" return "CraftOS 1.8"
@ -572,7 +539,7 @@ loadfile = function( _sFile, _tEnv )
end end
local file = fs.open( _sFile, "r" ) local file = fs.open( _sFile, "r" )
if file then if file then
local func, err = load( file.readAll(), fs.getName( _sFile ), "t", _tEnv ) local func, err = load( file.readAll(), "@" .. fs.getName( _sFile ), "t", _tEnv )
file.close() file.close()
return func, err return func, err
end end