fix tail call return value processing

This commit is contained in:
James Roseborough
2008-06-14 14:40:49 +00:00
parent a9b8cc5655
commit 59b75d5ce0
4 changed files with 366 additions and 296 deletions

View File

@@ -735,8 +735,8 @@ public class LuaState extends Lua {
// adjustTop only for case when call was completed // adjustTop only for case when call was completed
// and number of args > 0. If call completed but // and number of args > 0. If call completed but
// c == 0, leave top to point to end of results // c == 0, leave top to point to end of results
if (this.nresults >= 0) if (ci.nresults >= 0)
luaV_adjusttop(base + nresults); luaV_adjusttop(base + ci.nresults);
// force restore of base, etc. // force restore of base, etc.
return; return;

View File

@@ -1,4 +1,5 @@
package org.luaj.vm; package org.luaj.vm;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -10,174 +11,176 @@ import org.luaj.TestPlatform;
import org.luaj.compiler.LuaC; import org.luaj.compiler.LuaC;
import org.luaj.lib.BaseLib; import org.luaj.lib.BaseLib;
public class LuaJTest extends TestCase { public class LuaJTest extends TestCase {
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
Platform.setInstance(new TestPlatform()); Platform.setInstance(new TestPlatform());
} }
public void testTest1() throws IOException, InterruptedException { public void testTest1() throws IOException, InterruptedException {
runTest( "test1" ); runTest("test1");
} }
public void testTest2() throws IOException, InterruptedException { public void testTest2() throws IOException, InterruptedException {
runTest( "test2" ); runTest("test2");
} }
public void testTest3() throws IOException, InterruptedException { public void testTest3() throws IOException, InterruptedException {
runTest( "test3" ); runTest("test3");
} }
public void testTest4() throws IOException, InterruptedException { public void testTest4() throws IOException, InterruptedException {
runTest( "test4" ); runTest("test4");
} }
public void testTest5() throws IOException, InterruptedException { public void testTest5() throws IOException, InterruptedException {
runTest( "test5" ); runTest("test5");
} }
public void testTest6() throws IOException, InterruptedException { public void testTest6() throws IOException, InterruptedException {
runTest( "test6" ); runTest("test6");
} }
public void testTest7() throws IOException, InterruptedException { public void testTest7() throws IOException, InterruptedException {
runTest( "test7" ); runTest("test7");
} }
public void testTest8() throws IOException, InterruptedException { public void testTest8() throws IOException, InterruptedException {
runTest( "test8" ); runTest("test8");
} }
public void testArgtypes() throws IOException, InterruptedException { public void testArgtypes() throws IOException, InterruptedException {
runTest( "argtypes" ); runTest("argtypes");
} }
public void testAutoload() throws IOException, InterruptedException { public void testAutoload() throws IOException, InterruptedException {
runTest( "autoload" ); runTest("autoload");
} }
public void testBaseLib() throws IOException, InterruptedException { public void testBaseLib() throws IOException, InterruptedException {
runTest( "baselib" ); runTest("baselib");
} }
public void testBoolean() throws IOException, InterruptedException { public void testBoolean() throws IOException, InterruptedException {
runTest( "boolean" ); runTest("boolean");
} }
public void testCalls() throws IOException, InterruptedException { public void testCalls() throws IOException, InterruptedException {
runTest( "calls" ); runTest("calls");
} }
public void testCoercions() throws IOException, InterruptedException { public void testCoercions() throws IOException, InterruptedException {
runTest( "coercions" ); runTest("coercions");
} }
public void testCoroutines() throws IOException, InterruptedException { public void testCoroutines() throws IOException, InterruptedException {
runTest( "coroutines" ); runTest("coroutines");
} }
public void testCompare() throws IOException, InterruptedException { public void testCompare() throws IOException, InterruptedException {
runTest( "compare" ); runTest("compare");
} }
public void testErrors() throws IOException, InterruptedException { public void testErrors() throws IOException, InterruptedException {
runTest( "errors" ); runTest("errors");
} }
public void testHugeTable() throws IOException, InterruptedException { public void testHugeTable() throws IOException, InterruptedException {
runTest( "hugetable" ); runTest("hugetable");
} }
public void testLoops() throws IOException, InterruptedException { public void testLoops() throws IOException, InterruptedException {
runTest( "loops" ); runTest("loops");
} }
public void testManyLocals() throws IOException, InterruptedException { public void testManyLocals() throws IOException, InterruptedException {
runTest( "manylocals" ); runTest("manylocals");
} }
public void testMathLib() throws IOException, InterruptedException { public void testMathLib() throws IOException, InterruptedException {
runTest( "mathlib" ); runTest("mathlib");
} }
public void testMetatables() throws IOException, InterruptedException { public void testMetatables() throws IOException, InterruptedException {
runTest( "metatables" ); runTest("metatables");
} }
public void testModule() throws IOException, InterruptedException { public void testModule() throws IOException, InterruptedException {
runTest( "module" ); runTest("module");
} }
public void testNext() throws IOException, InterruptedException { public void testNext() throws IOException, InterruptedException {
runTest( "next" ); runTest("next");
} }
public void testPcalls() throws IOException, InterruptedException { public void testPcalls() throws IOException, InterruptedException {
runTest( "pcalls" ); runTest("pcalls");
} }
public void testRequire() throws IOException, InterruptedException { public void testRequire() throws IOException, InterruptedException {
runTest( "require" ); runTest("require");
} }
public void testSelect() throws IOException, InterruptedException { public void testSelect() throws IOException, InterruptedException {
runTest( "select" ); runTest("select");
} }
public void testSetfenv() throws IOException, InterruptedException { public void testSetfenv() throws IOException, InterruptedException {
runTest( "setfenv" ); runTest("setfenv");
} }
public void testSetlist() throws IOException, InterruptedException { public void testSetlist() throws IOException, InterruptedException {
runTest( "setlist" ); runTest("setlist");
} }
public void testSimpleMetatables() throws IOException, InterruptedException { public void testSimpleMetatables() throws IOException, InterruptedException {
runTest( "simplemetatables" ); runTest("simplemetatables");
} }
public void testStack() throws IOException, InterruptedException { public void testStack() throws IOException, InterruptedException {
runTest( "stack" ); runTest("stack");
} }
public void testStrLib() throws IOException, InterruptedException { public void testStrLib() throws IOException, InterruptedException {
runTest( "strlib" ); runTest("strlib");
} }
public void testSort() throws IOException, InterruptedException { public void testSort() throws IOException, InterruptedException {
runTest( "sort" ); runTest("sort");
} }
public void testTable() throws IOException, InterruptedException { public void testTable() throws IOException, InterruptedException {
runTest( "table" ); runTest("table");
}
public void testTailcall() throws IOException, InterruptedException {
runTest("tailcall");
} }
public void testType() throws IOException, InterruptedException { public void testType() throws IOException, InterruptedException {
runTest( "type" ); runTest("type");
} }
public void testUpvalues() throws IOException, InterruptedException { public void testUpvalues() throws IOException, InterruptedException {
runTest( "upvalues" ); runTest("upvalues");
} }
public void testUpvalues2() throws IOException, InterruptedException { public void testUpvalues2() throws IOException, InterruptedException {
runTest( "upvalues2" ); runTest("upvalues2");
} }
public void testUpvalues3() throws IOException, InterruptedException { public void testUpvalues3() throws IOException, InterruptedException {
runTest( "upvalues3" ); runTest("upvalues3");
} }
public void testWeakTable() throws IOException, InterruptedException { public void testWeakTable() throws IOException, InterruptedException {
runTest( "weaktable" ); runTest("weaktable");
} }
//*/ // */
private void runTest( String testName ) throws IOException, InterruptedException { private void runTest(String testName) throws IOException,
InterruptedException {
// new lua state // new lua state
LuaState state = Platform.newLuaState(); LuaState state = Platform.newLuaState();
@@ -186,34 +189,36 @@ public class LuaJTest extends TestCase {
LuaC.install(); LuaC.install();
// load the file // load the file
LPrototype p = loadScriptResource( state, testName ); LPrototype p = loadScriptResource(state, testName);
p.source = LString.valueOf("stdin"); p.source = LString.valueOf("stdin");
// Replace System.out with a ByteArrayOutputStream // Replace System.out with a ByteArrayOutputStream
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BaseLib.redirectOutput( outputStream ); BaseLib.redirectOutput(outputStream);
try { try {
// create closure and execute // create closure and execute
LClosure c = p.newClosure( state._G ); LClosure c = p.newClosure(state._G);
state.pushlvalue(c); state.pushlvalue(c);
state.call(0,0); state.call(0, 0);
final String actualOutput = new String( outputStream.toByteArray() ); final String actualOutput = new String(outputStream.toByteArray());
final String expectedOutput = getExpectedOutput( testName ); final String expectedOutput = getExpectedOutput(testName);
assertEquals( expectedOutput, actualOutput ); assertEquals(expectedOutput, actualOutput);
} finally { } finally {
BaseLib.restoreStandardOutput(); BaseLib.restoreStandardOutput();
outputStream.close(); outputStream.close();
} }
} }
protected LPrototype loadScriptResource( LuaState state, String name ) throws IOException { protected LPrototype loadScriptResource(LuaState state, String name)
InputStream script = getClass().getResourceAsStream( "/"+name+".luac" ); throws IOException {
if ( script == null ) { InputStream script = getClass().getResourceAsStream(
script = getClass().getResourceAsStream( "/"+name+".lua" ); "/" + name + ".luac");
if ( script == null ) { if (script == null) {
fail( "Could not load script for test case: "+name ); script = getClass().getResourceAsStream("/" + name + ".lua");
if (script == null) {
fail("Could not load script for test case: " + name);
} }
} }
@@ -226,36 +231,39 @@ public class LuaJTest extends TestCase {
} }
} }
private String getExpectedOutput( final String testName ) throws IOException, InterruptedException { private String getExpectedOutput(final String testName) throws IOException,
InterruptedException {
String expectedOutputName = "/" + testName + "-expected.out"; String expectedOutputName = "/" + testName + "-expected.out";
InputStream is = getClass().getResourceAsStream( expectedOutputName ); InputStream is = getClass().getResourceAsStream(expectedOutputName);
if ( is != null ) { if (is != null) {
try { try {
return readString( is ); return readString(is);
} finally { } finally {
is.close(); is.close();
} }
} else { } else {
InputStream script; InputStream script;
// script = getClass().getResourceAsStream( "/" + testName + ".luac" ); // script = getClass().getResourceAsStream( "/" + testName + ".luac"
// if ( script == null ) { // );
script = getClass().getResourceAsStream( "/" + testName + ".lua" ); // if ( script == null ) {
if ( script == null ) { script = getClass().getResourceAsStream("/" + testName + ".lua");
fail( "Could not find script for test case: "+testName ); if (script == null) {
fail("Could not find script for test case: " + testName);
} }
// } // }
try { try {
return collectProcessOutput( new String[] { "lua", "-" }, script ); return collectProcessOutput(new String[] { "lua", "-" }, script);
} finally { } finally {
script.close(); script.close();
} }
} }
} }
private String collectProcessOutput( String[] cmd, final InputStream input ) throws IOException, InterruptedException { private String collectProcessOutput(String[] cmd, final InputStream input)
throws IOException, InterruptedException {
Runtime r = Runtime.getRuntime(); Runtime r = Runtime.getRuntime();
final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final Process p = r.exec( cmd ); final Process p = r.exec(cmd);
try { try {
// start a thread to write the given input to the subprocess. // start a thread to write the given input to the subprocess.
Thread inputCopier = (new Thread() { Thread inputCopier = (new Thread() {
@@ -263,11 +271,11 @@ public class LuaJTest extends TestCase {
try { try {
OutputStream processStdIn = p.getOutputStream(); OutputStream processStdIn = p.getOutputStream();
try { try {
copy( input, processStdIn ); copy(input, processStdIn);
} finally { } finally {
processStdIn.close(); processStdIn.close();
} }
} catch ( IOException e ) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@@ -280,11 +288,11 @@ public class LuaJTest extends TestCase {
try { try {
InputStream processStdOut = p.getInputStream(); InputStream processStdOut = p.getInputStream();
try { try {
copy( processStdOut, baos ); copy(processStdOut, baos);
} finally { } finally {
processStdOut.close(); processStdOut.close();
} }
} catch ( IOException ioe ) { } catch (IOException ioe) {
ioe.printStackTrace(); ioe.printStackTrace();
} }
} }
@@ -297,11 +305,11 @@ public class LuaJTest extends TestCase {
try { try {
InputStream processError = p.getErrorStream(); InputStream processError = p.getErrorStream();
try { try {
copy( processError, System.err ); copy(processError, System.err);
} finally { } finally {
processError.close(); processError.close();
} }
} catch ( IOException ioe ) { } catch (IOException ioe) {
ioe.printStackTrace(); ioe.printStackTrace();
} }
} }
@@ -313,24 +321,24 @@ public class LuaJTest extends TestCase {
outputCopier.join(); outputCopier.join();
errorCopier.join(); errorCopier.join();
return new String( baos.toByteArray() ); return new String(baos.toByteArray());
} finally { } finally {
p.destroy(); p.destroy();
} }
} }
private String readString( InputStream is ) throws IOException { private String readString(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
copy( is, baos ); copy(is, baos);
return new String( baos.toByteArray() ); return new String(baos.toByteArray());
} }
private void copy( InputStream is, OutputStream os ) throws IOException { private void copy(InputStream is, OutputStream os) throws IOException {
byte[] buf = new byte[ 1024 ]; byte[] buf = new byte[1024];
int r; int r;
while ( ( r = is.read( buf ) ) >= 0 ) { while ((r = is.read(buf)) >= 0) {
os.write( buf, 0, r ); os.write(buf, 0, r);
} }
} }

62
src/test/res/tailcall.lua Normal file
View File

@@ -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)

View File

@@ -1 +1 @@
version: 0.38 version: 0.39