diff --git a/build.gradle b/build.gradle index f15a8a91a..b20e2eaea 100644 --- a/build.gradle +++ b/build.gradle @@ -46,17 +46,29 @@ name = "JEI" url = "http://dvs1.progwml6.com/files/maven" } + maven { + name = "squiddev" + url = "https://dl.bintray.com/squiddev/maven" + } +} + +configurations { + shade + compile.extendsFrom shade } dependencies { deobfProvided "mezz.jei:jei_1.12:4.7.5.86:api" runtime "mezz.jei:jei_1.12:4.7.5.86" + shade 'org.squiddev:Cobalt:0.3.0' } jar { manifest { attributes('FMLAT': 'computercraft_at.cfg') } + + from configurations.shade.collect { it.isDirectory() ? it : zipTree(it) } } import org.ajoberstar.grgit.Grgit diff --git a/deploy.sh b/deploy.sh index 0ce941a6f..c2f1ecf97 100755 --- a/deploy.sh +++ b/deploy.sh @@ -35,12 +35,4 @@ zip -r $OUTPUTJAR api/src/dan200/computercraft > /dev/null cd .. 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." diff --git a/src/main/java/dan200/computercraft/core/computer/Computer.java b/src/main/java/dan200/computercraft/core/computer/Computer.java index 81aac7605..2fc89e8fb 100644 --- a/src/main/java/dan200/computercraft/core/computer/Computer.java +++ b/src/main/java/dan200/computercraft/core/computer/Computer.java @@ -15,7 +15,7 @@ import dan200.computercraft.core.filesystem.FileSystem; import dan200.computercraft.core.filesystem.FileSystemException; 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 java.io.IOException; @@ -621,7 +621,7 @@ private void createAPIs() private void initLua() { // Create the lua machine - ILuaMachine machine = new LuaJLuaMachine( this ); + ILuaMachine machine = new CobaltLuaMachine( this ); // Add the APIs for(ILuaAPI api : m_apis) diff --git a/src/main/java/dan200/computercraft/core/lua/LuaJLuaMachine.java b/src/main/java/dan200/computercraft/core/lua/CobaltLuaMachine.java similarity index 55% rename from src/main/java/dan200/computercraft/core/lua/LuaJLuaMachine.java rename to src/main/java/dan200/computercraft/core/lua/CobaltLuaMachine.java index 114c900a1..53cf76624 100644 --- a/src/main/java/dan200/computercraft/core/lua/LuaJLuaMachine.java +++ b/src/main/java/dan200/computercraft/core/lua/CobaltLuaMachine.java @@ -16,106 +16,130 @@ import dan200.computercraft.core.computer.ITask; import dan200.computercraft.core.computer.MainThread; -import org.luaj.vm2.*; -import org.luaj.vm2.lib.Bit32Lib; -import org.luaj.vm2.lib.OneArgFunction; -import org.luaj.vm2.lib.VarArgFunction; -import org.luaj.vm2.lib.ZeroArgFunction; -import org.luaj.vm2.lib.jse.JsePlatform; +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; +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 java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.Arrays; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; -public class LuaJLuaMachine implements ILuaMachine -{ - private Computer m_computer; +import static org.squiddev.cobalt.Constants.NONE; +import static org.squiddev.cobalt.ValueFactory.valueOf; +import static org.squiddev.cobalt.ValueFactory.varargsOf; - private LuaValue m_globals; - private LuaValue m_loadString; - private LuaValue m_assert; - private LuaValue m_coroutine_create; - private LuaValue m_coroutine_resume; - private LuaValue m_coroutine_yield; - - private LuaValue m_mainRoutine; +public class CobaltLuaMachine implements ILuaMachine +{ + private final Computer m_computer; + + private final LuaState m_state; + private final LuaTable m_globals; + + private LuaThread m_mainRoutine; private String m_eventFilter; private String m_softAbortMessage; private String m_hardAbortMessage; - private Map m_valuesInProgress; - private Map m_objectsInProgress; - - public LuaJLuaMachine( Computer computer ) + public CobaltLuaMachine( Computer computer ) { m_computer = computer; // Create an environment to run in - m_globals = JsePlatform.debugGlobals(); - 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() { + final LuaState state = this.m_state = new LuaState( new AbstractResourceManipulator() + { @Override - public LuaValue call( LuaValue value ) + public InputStream findResource( String filename ) { - final LuaThread thread = native_coroutine_create.call( value ).checkthread(); - 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; + return null; } - }); - - m_coroutine_create = coroutine.get("create"); - m_coroutine_resume = coroutine.get("resume"); - m_coroutine_yield = coroutine.get("yield"); - + } ); + state.debug = new DebugHandler( state ) + { + private int count = 0; + 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 - m_globals.set( "collectgarbage", LuaValue.NIL ); - m_globals.set( "dofile", LuaValue.NIL ); - m_globals.set( "loadfile", LuaValue.NIL ); - m_globals.set( "module", LuaValue.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 ); + m_globals.rawset( "collectgarbage", Constants.NIL ); + m_globals.rawset( "dofile", Constants.NIL ); + m_globals.rawset( "loadfile", Constants.NIL ); + m_globals.rawset( "print", Constants.NIL ); // Add version globals - m_globals.set( "_VERSION", "Lua 5.1" ); - m_globals.set( "_HOST", computer.getAPIEnvironment().getComputerEnvironment().getHostString() ); - m_globals.set( "_CC_DEFAULT_SETTINGS", toValue( ComputerCraft.default_computer_settings ) ); + 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 ) ); 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 @@ -125,7 +149,7 @@ public LuaValue call() { m_softAbortMessage = null; m_hardAbortMessage = null; } - + @Override public void addAPI( ILuaAPI api ) { @@ -134,10 +158,10 @@ public void addAPI( ILuaAPI api ) String[] names = api.getNames(); for( String name : names ) { - m_globals.set( name, table ); + m_globals.rawset( name, table ); } } - + @Override public void loadBios( InputStream bios ) { @@ -146,56 +170,31 @@ public void loadBios( InputStream bios ) { return; } - + try { - // Read the whole bios into a string - String biosText; - 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 ); + LuaFunction value = LoadState.load( m_state, bios, "@bios.lua", m_globals ); + m_mainRoutine = new LuaThread( m_state, value, m_globals ); } - 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 ); if( m_mainRoutine != null ) { - ((LuaThread)m_mainRoutine).abandon(); + m_mainRoutine.abandon(); m_mainRoutine = null; } } } - + @Override public void handleEvent( String eventName, Object[] arguments ) { @@ -208,35 +207,28 @@ public void handleEvent( String eventName, Object[] arguments ) { return; } - + try - { - LuaValue[] resumeArgs; + { + Varargs resumeArgs = Constants.NONE; if( eventName != null ) { - resumeArgs = toValues( arguments, 2 ); - resumeArgs[0] = m_mainRoutine; - resumeArgs[1] = toValue( eventName ); + resumeArgs = varargsOf( valueOf( eventName ), toValues( arguments ) ); } - else - { - resumeArgs = new LuaValue[1]; - resumeArgs[0] = m_mainRoutine; - } - - Varargs results = m_coroutine_resume.invoke( LuaValue.varargsOf( resumeArgs ) ); - if( m_hardAbortMessage != null ) + + Varargs results = m_mainRoutine.resume( resumeArgs ); + if( m_hardAbortMessage != null ) { 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 { - LuaValue filter = results.arg(2); - if( filter.isstring() ) + LuaValue filter = results.arg( 2 ); + if( filter.isString() ) { m_eventFilter = filter.toString(); } @@ -245,16 +237,16 @@ else if( results.arg1().checkboolean() == false ) m_eventFilter = null; } } - - LuaThread mainThread = (LuaThread)m_mainRoutine; - if( mainThread.getStatus().equals("dead") ) + + LuaThread mainThread = m_mainRoutine; + if( mainThread.getStatus().equals( "dead" ) ) { m_mainRoutine = null; } } catch( LuaError e ) { - ((LuaThread)m_mainRoutine).abandon(); + m_mainRoutine.abandon(); m_mainRoutine = null; } finally @@ -264,13 +256,13 @@ else if( results.arg1().checkboolean() == false ) } } - @Override + @Override public void softAbort( String abortMessage ) { m_softAbortMessage = abortMessage; } - @Override + @Override public void hardAbort( String abortMessage ) { m_softAbortMessage = abortMessage; @@ -282,100 +274,88 @@ public boolean saveState( OutputStream output ) { return false; } - + @Override public boolean restoreState( InputStream input ) { return false; } - + @Override public boolean isFinished() { return (m_mainRoutine == null); } - + @Override public void unload() { if( m_mainRoutine != null ) { - LuaThread mainThread = (LuaThread)m_mainRoutine; + LuaThread mainThread = m_mainRoutine; mainThread.abandon(); 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 ) { LuaTable table = new LuaTable(); String[] methods = object.getMethodNames(); - for(int i=0; i= 1 && results[0].equals( "terminate" ) ) + if( results.length >= 1 && results[ 0 ].equals( "terminate" ) ) { throw new LuaException( "Terminated", 0 ); } return results; } - + @Nonnull @Override public Object[] pullEventRaw( String filter ) throws InterruptedException { return yield( new Object[] { filter } ); } - + @Nonnull @Override public Object[] yield( Object[] yieldArgs ) throws InterruptedException { try { - LuaValue[] yieldValues = toValues( yieldArgs, 0 ); - Varargs results = m_coroutine_yield.invoke( LuaValue.varargsOf( yieldValues ) ); + Varargs results = LuaThread.yield( state, toValues( yieldArgs ) ); return toObjects( results, 1 ); } catch( OrphanedThread e ) { throw new InterruptedException(); } + catch( Throwable e ) + { + throw new RuntimeException( e ); + } } @Override @@ -450,10 +430,10 @@ public Object[] executeMainThreadTask( @Nonnull final ILuaTask task ) throws Lua Object[] response = pullEvent( "task_complete" ); 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 ]; - if( (Boolean)response[ 2 ] ) + if( (Boolean) response[ 2 ] ) { // Extract the return values from the event and return them System.arraycopy( response, 3, returnValues, 0, returnValues.length ); @@ -462,9 +442,9 @@ public Object[] executeMainThreadTask( @Nonnull final ILuaTask task ) throws Lua else { // 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 { @@ -481,7 +461,7 @@ public Object[] executeMainThreadTask( @Nonnull final ILuaTask task ) throws Lua catch( InterruptedException e ) { throw new OrphanedThread(); - } + } catch( LuaException e ) { 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 ); } - return LuaValue.varargsOf( toValues( results, 0 ) ); + return toValues( results ); } } ); } @@ -502,192 +482,257 @@ public Object[] executeMainThreadTask( @Nonnull final ILuaTask task ) throws Lua return table; } - private LuaValue toValue( Object object ) + private LuaValue toValue( Object object, Map values ) { if( object == null ) { - return LuaValue.NIL; + return Constants.NIL; } else if( object instanceof Number ) { - double d = ((Number)object).doubleValue(); - return LuaValue.valueOf( d ); + double d = ((Number) object).doubleValue(); + return valueOf( d ); } else if( object instanceof Boolean ) { - boolean b = (Boolean) object; - return LuaValue.valueOf( b ); + return valueOf( (Boolean) object ); } else if( object instanceof String ) { String s = object.toString(); - return LuaValue.valueOf( s ); + return valueOf( s ); } else if( object instanceof byte[] ) { byte[] b = (byte[]) object; - return LuaValue.valueOf( Arrays.copyOf( b, b.length ) ); + return valueOf( Arrays.copyOf( b, b.length ) ); } else if( object instanceof Map ) { // Table: // Start remembering stuff - boolean clearWhenDone = false; - try + if( values == null ) { - if( m_valuesInProgress == null ) - { - m_valuesInProgress = new IdentityHashMap<>(); - clearWhenDone = true; - } - else if( m_valuesInProgress.containsKey( object ) ) - { - return m_valuesInProgress.get( object ); - } - LuaValue table = new LuaTable(); - m_valuesInProgress.put( object, table ); + values = new IdentityHashMap<>(); + } + else if( values.containsKey( object ) ) + { + return values.get( object ); + } + LuaTable table = new LuaTable(); + values.put( object, table ); - // Convert all keys - 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 + // Convert all keys + for( Map.Entry pair : ((Map) object).entrySet() ) { - // Clear (if exiting top level) - if( clearWhenDone ) + LuaValue key = toValue( pair.getKey(), values ); + 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 ) { - return wrapLuaObject( (ILuaObject)object ); + return wrapLuaObject( (ILuaObject) object ); } 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]; - for( int i=0; i objects ) { switch( value.type() ) { - case LuaValue.TNIL: - case LuaValue.TNONE: + case Constants.TNIL: + case Constants.TNONE: { return null; } - case LuaValue.TINT: - case LuaValue.TNUMBER: + case Constants.TINT: + 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 str.tojstring(); + return value.toString(); } - case LuaValue.TTABLE: + case Constants.TTABLE: { // Table: - boolean clearWhenDone = false; - try + // Start remembering stuff + if( objects == null ) { - // Start remembering stuff - if( m_objectsInProgress == null ) - { - m_objectsInProgress = new IdentityHashMap<>(); - clearWhenDone = true; - } - else if( m_objectsInProgress.containsKey( value ) ) - { - return m_objectsInProgress.get( value ); - } - Map 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; + objects = new IdentityHashMap<>(); } - finally + else if( objects.containsKey( value ) ) { - // Clear (if exiting top level) - if( clearWhenDone ) + return objects.get( value ); + } + Map 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: { 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 ]; - for( int n=startIdx; n<=count; ++n ) + for( int n = startIdx; n <= count; ++n ) { int i = n - startIdx; - LuaValue value = values.arg(n); - objects[i] = toObject( value ); + LuaValue value = values.arg( n ); + objects[ i ] = toObject( value, null ); } 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++]; + } + } } diff --git a/src/main/resources/assets/computercraft/lua/bios.lua b/src/main/resources/assets/computercraft/lua/bios.lua index e2b87f076..7bb62d3bd 100644 --- a/src/main/resources/assets/computercraft/lua/bios.lua +++ b/src/main/resources/assets/computercraft/lua/bios.lua @@ -142,39 +142,6 @@ if _VERSION == "Lua 5.3" and not bit32 then ]] )() 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 function os.version() return "CraftOS 1.8" @@ -572,7 +539,7 @@ loadfile = function( _sFile, _tEnv ) end local file = fs.open( _sFile, "r" ) 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() return func, err end