From 59b75d5ce01f6d1c1c32fc9164f3e1a7c5681933 Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Sat, 14 Jun 2008 14:40:49 +0000 Subject: [PATCH] fix tail call return value processing --- src/core/org/luaj/vm/LuaState.java | 4 +- src/test/java/org/luaj/vm/LuaJTest.java | 594 ++++++++++++------------ src/test/res/tailcall.lua | 62 +++ version.properties | 2 +- 4 files changed, 366 insertions(+), 296 deletions(-) create mode 100644 src/test/res/tailcall.lua diff --git a/src/core/org/luaj/vm/LuaState.java b/src/core/org/luaj/vm/LuaState.java index 9cc6bd11..2cdee7c7 100644 --- a/src/core/org/luaj/vm/LuaState.java +++ b/src/core/org/luaj/vm/LuaState.java @@ -735,8 +735,8 @@ public class LuaState extends Lua { // adjustTop only for case when call was completed // and number of args > 0. If call completed but // c == 0, leave top to point to end of results - if (this.nresults >= 0) - luaV_adjusttop(base + nresults); + if (ci.nresults >= 0) + luaV_adjusttop(base + ci.nresults); // force restore of base, etc. return; diff --git a/src/test/java/org/luaj/vm/LuaJTest.java b/src/test/java/org/luaj/vm/LuaJTest.java index 3bd12fbc..c90a1635 100644 --- a/src/test/java/org/luaj/vm/LuaJTest.java +++ b/src/test/java/org/luaj/vm/LuaJTest.java @@ -1,4 +1,5 @@ package org.luaj.vm; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -10,328 +11,335 @@ import org.luaj.TestPlatform; import org.luaj.compiler.LuaC; import org.luaj.lib.BaseLib; - public class LuaJTest extends TestCase { - - protected void setUp() throws Exception { - super.setUp(); - Platform.setInstance(new TestPlatform()); - } + protected void setUp() throws Exception { + super.setUp(); + Platform.setInstance(new TestPlatform()); + } - public void testTest1() throws IOException, InterruptedException { - runTest( "test1" ); - } + public void testTest1() throws IOException, InterruptedException { + runTest("test1"); + } - public void testTest2() throws IOException, InterruptedException { - runTest( "test2" ); - } + public void testTest2() throws IOException, InterruptedException { + runTest("test2"); + } - public void testTest3() throws IOException, InterruptedException { - runTest( "test3" ); - } + public void testTest3() throws IOException, InterruptedException { + runTest("test3"); + } - public void testTest4() throws IOException, InterruptedException { - runTest( "test4" ); - } + public void testTest4() throws IOException, InterruptedException { + runTest("test4"); + } - public void testTest5() throws IOException, InterruptedException { - runTest( "test5" ); - } + public void testTest5() throws IOException, InterruptedException { + runTest("test5"); + } - public void testTest6() throws IOException, InterruptedException { - runTest( "test6" ); - } + public void testTest6() throws IOException, InterruptedException { + runTest("test6"); + } - public void testTest7() throws IOException, InterruptedException { - runTest( "test7" ); - } + public void testTest7() throws IOException, InterruptedException { + runTest("test7"); + } - public void testTest8() throws IOException, InterruptedException { - runTest( "test8" ); - } + public void testTest8() throws IOException, InterruptedException { + runTest("test8"); + } - public void testArgtypes() throws IOException, InterruptedException { - runTest( "argtypes" ); - } + public void testArgtypes() throws IOException, InterruptedException { + runTest("argtypes"); + } - public void testAutoload() throws IOException, InterruptedException { - runTest( "autoload" ); - } + public void testAutoload() throws IOException, InterruptedException { + runTest("autoload"); + } - public void testBaseLib() throws IOException, InterruptedException { - runTest( "baselib" ); - } - - public void testBoolean() throws IOException, InterruptedException { - runTest( "boolean" ); - } + public void testBaseLib() throws IOException, InterruptedException { + runTest("baselib"); + } - public void testCalls() throws IOException, InterruptedException { - runTest( "calls" ); - } + public void testBoolean() throws IOException, InterruptedException { + runTest("boolean"); + } - public void testCoercions() throws IOException, InterruptedException { - runTest( "coercions" ); - } - - public void testCoroutines() throws IOException, InterruptedException { - runTest( "coroutines" ); - } - - public void testCompare() throws IOException, InterruptedException { - runTest( "compare" ); - } + public void testCalls() throws IOException, InterruptedException { + runTest("calls"); + } - public void testErrors() throws IOException, InterruptedException { - runTest( "errors" ); - } + public void testCoercions() throws IOException, InterruptedException { + runTest("coercions"); + } - public void testHugeTable() throws IOException, InterruptedException { - runTest( "hugetable" ); - } + public void testCoroutines() throws IOException, InterruptedException { + runTest("coroutines"); + } - public void testLoops() throws IOException, InterruptedException { - runTest( "loops" ); - } - - public void testManyLocals() throws IOException, InterruptedException { - runTest( "manylocals" ); - } + public void testCompare() throws IOException, InterruptedException { + runTest("compare"); + } + public void testErrors() throws IOException, InterruptedException { + runTest("errors"); + } - public void testMathLib() throws IOException, InterruptedException { - runTest( "mathlib" ); - } + public void testHugeTable() throws IOException, InterruptedException { + runTest("hugetable"); + } - public void testMetatables() throws IOException, InterruptedException { - runTest( "metatables" ); - } + public void testLoops() throws IOException, InterruptedException { + runTest("loops"); + } - public void testModule() throws IOException, InterruptedException { - runTest( "module" ); - } + public void testManyLocals() throws IOException, InterruptedException { + runTest("manylocals"); + } - public void testNext() throws IOException, InterruptedException { - runTest( "next" ); - } + public void testMathLib() throws IOException, InterruptedException { + runTest("mathlib"); + } - public void testPcalls() throws IOException, InterruptedException { - runTest( "pcalls" ); - } - - public void testRequire() throws IOException, InterruptedException { - runTest( "require" ); - } - - public void testSelect() throws IOException, InterruptedException { - runTest( "select" ); - } + public void testMetatables() throws IOException, InterruptedException { + runTest("metatables"); + } - public void testSetfenv() throws IOException, InterruptedException { - runTest( "setfenv" ); - } + public void testModule() throws IOException, InterruptedException { + runTest("module"); + } - public void testSetlist() throws IOException, InterruptedException { - runTest( "setlist" ); - } - - public void testSimpleMetatables() throws IOException, InterruptedException { - runTest( "simplemetatables" ); - } - - public void testStack() throws IOException, InterruptedException { - runTest( "stack" ); - } - - public void testStrLib() throws IOException, InterruptedException { - runTest( "strlib" ); - } - - public void testSort() throws IOException, InterruptedException { - runTest( "sort" ); - } + public void testNext() throws IOException, InterruptedException { + runTest("next"); + } - public void testTable() throws IOException, InterruptedException { - runTest( "table" ); - } + public void testPcalls() throws IOException, InterruptedException { + runTest("pcalls"); + } - public void testType() throws IOException, InterruptedException { - runTest( "type" ); - } - - public void testUpvalues() throws IOException, InterruptedException { - runTest( "upvalues" ); - } - - public void testUpvalues2() throws IOException, InterruptedException { - runTest( "upvalues2" ); - } - - public void testUpvalues3() throws IOException, InterruptedException { - runTest( "upvalues3" ); - } - - public void testWeakTable() throws IOException, InterruptedException { - runTest( "weaktable" ); - } + public void testRequire() throws IOException, InterruptedException { + runTest("require"); + } -//*/ - private void runTest( String testName ) throws IOException, InterruptedException { + public void testSelect() throws IOException, InterruptedException { + runTest("select"); + } - // new lua state - LuaState state = Platform.newLuaState(); - - // install the compiler - LuaC.install(); - - // load the file - LPrototype p = loadScriptResource( state, testName ); - p.source = LString.valueOf("stdin"); - - // Replace System.out with a ByteArrayOutputStream - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - BaseLib.redirectOutput( outputStream ); - try { - // create closure and execute - LClosure c = p.newClosure( state._G ); - state.pushlvalue(c); - state.call(0,0); - - final String actualOutput = new String( outputStream.toByteArray() ); - final String expectedOutput = getExpectedOutput( testName ); - - assertEquals( expectedOutput, actualOutput ); - } finally { - BaseLib.restoreStandardOutput(); - outputStream.close(); - } - } - - protected LPrototype loadScriptResource( LuaState state, String name ) throws IOException { - InputStream script = getClass().getResourceAsStream( "/"+name+".luac" ); - if ( script == null ) { - script = getClass().getResourceAsStream( "/"+name+".lua" ); - if ( script == null ) { - fail( "Could not load script for test case: "+name ); - } - } - - try { - // Use "stdin" instead of resource name so that output matches - // standard Lua. - return LoadState.undump(state, script, "stdin"); - } finally { - script.close(); - } - } - - private String getExpectedOutput( final String testName ) throws IOException, InterruptedException { - String expectedOutputName = "/" + testName + "-expected.out"; - InputStream is = getClass().getResourceAsStream( expectedOutputName ); - if ( is != null ) { - try { - return readString( is ); - } finally { - is.close(); - } - } else { - InputStream script; -// script = getClass().getResourceAsStream( "/" + testName + ".luac" ); -// if ( script == null ) { - script = getClass().getResourceAsStream( "/" + testName + ".lua" ); - if ( script == null ) { - fail( "Could not find script for test case: "+testName ); - } -// } - try { - return collectProcessOutput( new String[] { "lua", "-" }, script ); - } finally { - script.close(); - } - } - } - - private String collectProcessOutput( String[] cmd, final InputStream input ) throws IOException, InterruptedException { - Runtime r = Runtime.getRuntime(); - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - final Process p = r.exec( cmd ); - try { - // start a thread to write the given input to the subprocess. - Thread inputCopier = (new Thread() { - public void run() { - try { - OutputStream processStdIn = p.getOutputStream(); - try { - copy( input, processStdIn ); - } finally { - processStdIn.close(); - } - } catch ( IOException e ) { - e.printStackTrace(); - } - } - }); - inputCopier.start(); - - // start another thread to read output from the subprocess. - Thread outputCopier = (new Thread() { - public void run() { - try { - InputStream processStdOut = p.getInputStream(); - try { - copy( processStdOut, baos ); - } finally { - processStdOut.close(); - } - } catch ( IOException ioe ) { - ioe.printStackTrace(); - } - } - }); - outputCopier.start(); - - // start another thread to read output from the subprocess. - Thread errorCopier = (new Thread() { - public void run() { - try { - InputStream processError = p.getErrorStream(); - try { - copy( processError, System.err ); - } finally { - processError.close(); - } - } catch ( IOException ioe ) { - ioe.printStackTrace(); - } - } - }); - errorCopier.start(); - - p.waitFor(); - inputCopier.join(); - outputCopier.join(); - errorCopier.join(); - - return new String( baos.toByteArray() ); - - } finally { - p.destroy(); - } - } - - private String readString( InputStream is ) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - copy( is, baos ); - return new String( baos.toByteArray() ); - } - - private void copy( InputStream is, OutputStream os ) throws IOException { - byte[] buf = new byte[ 1024 ]; - int r; - while ( ( r = is.read( buf ) ) >= 0 ) { - os.write( buf, 0, r ); - } - } + public void testSetfenv() throws IOException, InterruptedException { + runTest("setfenv"); + } + + public void testSetlist() throws IOException, InterruptedException { + runTest("setlist"); + } + + public void testSimpleMetatables() throws IOException, InterruptedException { + runTest("simplemetatables"); + } + + public void testStack() throws IOException, InterruptedException { + runTest("stack"); + } + + public void testStrLib() throws IOException, InterruptedException { + runTest("strlib"); + } + + public void testSort() throws IOException, InterruptedException { + runTest("sort"); + } + + public void testTable() throws IOException, InterruptedException { + runTest("table"); + } + + public void testTailcall() throws IOException, InterruptedException { + runTest("tailcall"); + } + + public void testType() throws IOException, InterruptedException { + runTest("type"); + } + + public void testUpvalues() throws IOException, InterruptedException { + runTest("upvalues"); + } + + public void testUpvalues2() throws IOException, InterruptedException { + runTest("upvalues2"); + } + + public void testUpvalues3() throws IOException, InterruptedException { + runTest("upvalues3"); + } + + public void testWeakTable() throws IOException, InterruptedException { + runTest("weaktable"); + } + + // */ + private void runTest(String testName) throws IOException, + InterruptedException { + + // new lua state + LuaState state = Platform.newLuaState(); + + // install the compiler + LuaC.install(); + + // load the file + LPrototype p = loadScriptResource(state, testName); + p.source = LString.valueOf("stdin"); + + // Replace System.out with a ByteArrayOutputStream + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + BaseLib.redirectOutput(outputStream); + try { + // create closure and execute + LClosure c = p.newClosure(state._G); + state.pushlvalue(c); + state.call(0, 0); + + final String actualOutput = new String(outputStream.toByteArray()); + final String expectedOutput = getExpectedOutput(testName); + + assertEquals(expectedOutput, actualOutput); + } finally { + BaseLib.restoreStandardOutput(); + outputStream.close(); + } + } + + protected LPrototype loadScriptResource(LuaState state, String name) + throws IOException { + InputStream script = getClass().getResourceAsStream( + "/" + name + ".luac"); + if (script == null) { + script = getClass().getResourceAsStream("/" + name + ".lua"); + if (script == null) { + fail("Could not load script for test case: " + name); + } + } + + try { + // Use "stdin" instead of resource name so that output matches + // standard Lua. + return LoadState.undump(state, script, "stdin"); + } finally { + script.close(); + } + } + + private String getExpectedOutput(final String testName) throws IOException, + InterruptedException { + String expectedOutputName = "/" + testName + "-expected.out"; + InputStream is = getClass().getResourceAsStream(expectedOutputName); + if (is != null) { + try { + return readString(is); + } finally { + is.close(); + } + } else { + InputStream script; + // script = getClass().getResourceAsStream( "/" + testName + ".luac" + // ); + // if ( script == null ) { + script = getClass().getResourceAsStream("/" + testName + ".lua"); + if (script == null) { + fail("Could not find script for test case: " + testName); + } + // } + try { + return collectProcessOutput(new String[] { "lua", "-" }, script); + } finally { + script.close(); + } + } + } + + private String collectProcessOutput(String[] cmd, final InputStream input) + throws IOException, InterruptedException { + Runtime r = Runtime.getRuntime(); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final Process p = r.exec(cmd); + try { + // start a thread to write the given input to the subprocess. + Thread inputCopier = (new Thread() { + public void run() { + try { + OutputStream processStdIn = p.getOutputStream(); + try { + copy(input, processStdIn); + } finally { + processStdIn.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + inputCopier.start(); + + // start another thread to read output from the subprocess. + Thread outputCopier = (new Thread() { + public void run() { + try { + InputStream processStdOut = p.getInputStream(); + try { + copy(processStdOut, baos); + } finally { + processStdOut.close(); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + }); + outputCopier.start(); + + // start another thread to read output from the subprocess. + Thread errorCopier = (new Thread() { + public void run() { + try { + InputStream processError = p.getErrorStream(); + try { + copy(processError, System.err); + } finally { + processError.close(); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + }); + errorCopier.start(); + + p.waitFor(); + inputCopier.join(); + outputCopier.join(); + errorCopier.join(); + + return new String(baos.toByteArray()); + + } finally { + p.destroy(); + } + } + + private String readString(InputStream is) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + copy(is, baos); + return new String(baos.toByteArray()); + } + + private void copy(InputStream is, OutputStream os) throws IOException { + byte[] buf = new byte[1024]; + int r; + while ((r = is.read(buf)) >= 0) { + os.write(buf, 0, r); + } + } } diff --git a/src/test/res/tailcall.lua b/src/test/res/tailcall.lua new file mode 100644 index 00000000..096e3354 --- /dev/null +++ b/src/test/res/tailcall.lua @@ -0,0 +1,62 @@ + +function a() + return pcall( function() end ) +end + +function b() + return pcall( function() print 'b' end ) +end + +function c() + return pcall( function() return 'c' end ) +end + +print( pcall( a ) ) +print( pcall( b ) ) +print( pcall( c ) ) + +local function sum(...) + local s = 0 + for i,v in ipairs({...}) do + s = s + v + end + return s +end + +local function f1(n,a,b,c) + local a = a or 0 + local b = b or 0 + local c = c or 0 + if n <= 0 then + return a,a+b,a+b+c + end + return f1(n-1,a,a+b,a+b+c) +end + +local function f2(n,...) + if n <= 0 then + return sum(...) + end + return f2(n-1,n,...) +end + +local function f3(n,...) + if n <= 0 then + return sum(...) + end + return pcall(f3,n-1,n,...) +end + +local function all(f) + for n=0,3 do + t = {} + for m=1,5 do + print( pcall( f, n, unpack(t)) ) + t[m] = m + end + end +end + +all(f1) +all(f2) +all(f3) diff --git a/version.properties b/version.properties index f8be78fe..f2389e51 100644 --- a/version.properties +++ b/version.properties @@ -1 +1 @@ -version: 0.38 +version: 0.39