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,328 +11,335 @@ 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 {
runTest( "boolean" );
}
public void testCalls() throws IOException, InterruptedException { public void testBoolean() throws IOException, InterruptedException {
runTest( "calls" ); runTest("boolean");
} }
public void testCoercions() throws IOException, InterruptedException { public void testCalls() throws IOException, InterruptedException {
runTest( "coercions" ); runTest("calls");
} }
public void testCoroutines() throws IOException, InterruptedException {
runTest( "coroutines" );
}
public void testCompare() throws IOException, InterruptedException {
runTest( "compare" );
}
public void testErrors() throws IOException, InterruptedException { public void testCoercions() throws IOException, InterruptedException {
runTest( "errors" ); runTest("coercions");
} }
public void testHugeTable() throws IOException, InterruptedException { public void testCoroutines() throws IOException, InterruptedException {
runTest( "hugetable" ); runTest("coroutines");
} }
public void testLoops() throws IOException, InterruptedException { public void testCompare() throws IOException, InterruptedException {
runTest( "loops" ); runTest("compare");
} }
public void testManyLocals() throws IOException, InterruptedException {
runTest( "manylocals" );
}
public void testErrors() throws IOException, InterruptedException {
runTest("errors");
}
public void testMathLib() throws IOException, InterruptedException { public void testHugeTable() throws IOException, InterruptedException {
runTest( "mathlib" ); runTest("hugetable");
} }
public void testMetatables() throws IOException, InterruptedException { public void testLoops() throws IOException, InterruptedException {
runTest( "metatables" ); runTest("loops");
} }
public void testModule() throws IOException, InterruptedException { public void testManyLocals() throws IOException, InterruptedException {
runTest( "module" ); runTest("manylocals");
} }
public void testNext() throws IOException, InterruptedException { public void testMathLib() throws IOException, InterruptedException {
runTest( "next" ); runTest("mathlib");
} }
public void testPcalls() throws IOException, InterruptedException { public void testMetatables() throws IOException, InterruptedException {
runTest( "pcalls" ); runTest("metatables");
} }
public void testRequire() throws IOException, InterruptedException {
runTest( "require" );
}
public void testSelect() throws IOException, InterruptedException {
runTest( "select" );
}
public void testSetfenv() throws IOException, InterruptedException { public void testModule() throws IOException, InterruptedException {
runTest( "setfenv" ); runTest("module");
} }
public void testSetlist() throws IOException, InterruptedException { public void testNext() throws IOException, InterruptedException {
runTest( "setlist" ); runTest("next");
} }
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 { public void testPcalls() throws IOException, InterruptedException {
runTest( "table" ); runTest("pcalls");
} }
public void testType() throws IOException, InterruptedException { public void testRequire() throws IOException, InterruptedException {
runTest( "type" ); runTest("require");
} }
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 testSelect() throws IOException, InterruptedException {
private void runTest( String testName ) throws IOException, InterruptedException { runTest("select");
}
// new lua state public void testSetfenv() throws IOException, InterruptedException {
LuaState state = Platform.newLuaState(); runTest("setfenv");
}
// install the compiler
LuaC.install(); public void testSetlist() throws IOException, InterruptedException {
runTest("setlist");
// load the file }
LPrototype p = loadScriptResource( state, testName );
p.source = LString.valueOf("stdin"); public void testSimpleMetatables() throws IOException, InterruptedException {
runTest("simplemetatables");
// Replace System.out with a ByteArrayOutputStream }
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BaseLib.redirectOutput( outputStream ); public void testStack() throws IOException, InterruptedException {
try { runTest("stack");
// create closure and execute }
LClosure c = p.newClosure( state._G );
state.pushlvalue(c); public void testStrLib() throws IOException, InterruptedException {
state.call(0,0); runTest("strlib");
}
final String actualOutput = new String( outputStream.toByteArray() );
final String expectedOutput = getExpectedOutput( testName ); public void testSort() throws IOException, InterruptedException {
runTest("sort");
assertEquals( expectedOutput, actualOutput ); }
} finally {
BaseLib.restoreStandardOutput(); public void testTable() throws IOException, InterruptedException {
outputStream.close(); runTest("table");
} }
}
public void testTailcall() throws IOException, InterruptedException {
protected LPrototype loadScriptResource( LuaState state, String name ) throws IOException { runTest("tailcall");
InputStream script = getClass().getResourceAsStream( "/"+name+".luac" ); }
if ( script == null ) {
script = getClass().getResourceAsStream( "/"+name+".lua" ); public void testType() throws IOException, InterruptedException {
if ( script == null ) { runTest("type");
fail( "Could not load script for test case: "+name ); }
}
} public void testUpvalues() throws IOException, InterruptedException {
runTest("upvalues");
try { }
// Use "stdin" instead of resource name so that output matches
// standard Lua. public void testUpvalues2() throws IOException, InterruptedException {
return LoadState.undump(state, script, "stdin"); runTest("upvalues2");
} finally { }
script.close();
} public void testUpvalues3() throws IOException, InterruptedException {
} runTest("upvalues3");
}
private String getExpectedOutput( final String testName ) throws IOException, InterruptedException {
String expectedOutputName = "/" + testName + "-expected.out"; public void testWeakTable() throws IOException, InterruptedException {
InputStream is = getClass().getResourceAsStream( expectedOutputName ); runTest("weaktable");
if ( is != null ) { }
try {
return readString( is ); // */
} finally { private void runTest(String testName) throws IOException,
is.close(); InterruptedException {
}
} else { // new lua state
InputStream script; LuaState state = Platform.newLuaState();
// script = getClass().getResourceAsStream( "/" + testName + ".luac" );
// if ( script == null ) { // install the compiler
script = getClass().getResourceAsStream( "/" + testName + ".lua" ); LuaC.install();
if ( script == null ) {
fail( "Could not find script for test case: "+testName ); // load the file
} LPrototype p = loadScriptResource(state, testName);
// } p.source = LString.valueOf("stdin");
try {
return collectProcessOutput( new String[] { "lua", "-" }, script ); // Replace System.out with a ByteArrayOutputStream
} finally { ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
script.close(); BaseLib.redirectOutput(outputStream);
} try {
} // create closure and execute
} LClosure c = p.newClosure(state._G);
state.pushlvalue(c);
private String collectProcessOutput( String[] cmd, final InputStream input ) throws IOException, InterruptedException { state.call(0, 0);
Runtime r = Runtime.getRuntime();
final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final String actualOutput = new String(outputStream.toByteArray());
final Process p = r.exec( cmd ); final String expectedOutput = getExpectedOutput(testName);
try {
// start a thread to write the given input to the subprocess. assertEquals(expectedOutput, actualOutput);
Thread inputCopier = (new Thread() { } finally {
public void run() { BaseLib.restoreStandardOutput();
try { outputStream.close();
OutputStream processStdIn = p.getOutputStream(); }
try { }
copy( input, processStdIn );
} finally { protected LPrototype loadScriptResource(LuaState state, String name)
processStdIn.close(); throws IOException {
} InputStream script = getClass().getResourceAsStream(
} catch ( IOException e ) { "/" + name + ".luac");
e.printStackTrace(); if (script == null) {
} script = getClass().getResourceAsStream("/" + name + ".lua");
} if (script == null) {
}); fail("Could not load script for test case: " + name);
inputCopier.start(); }
}
// start another thread to read output from the subprocess.
Thread outputCopier = (new Thread() { try {
public void run() { // Use "stdin" instead of resource name so that output matches
try { // standard Lua.
InputStream processStdOut = p.getInputStream(); return LoadState.undump(state, script, "stdin");
try { } finally {
copy( processStdOut, baos ); script.close();
} finally { }
processStdOut.close(); }
}
} catch ( IOException ioe ) { private String getExpectedOutput(final String testName) throws IOException,
ioe.printStackTrace(); InterruptedException {
} String expectedOutputName = "/" + testName + "-expected.out";
} InputStream is = getClass().getResourceAsStream(expectedOutputName);
}); if (is != null) {
outputCopier.start(); try {
return readString(is);
// start another thread to read output from the subprocess. } finally {
Thread errorCopier = (new Thread() { is.close();
public void run() { }
try { } else {
InputStream processError = p.getErrorStream(); InputStream script;
try { // script = getClass().getResourceAsStream( "/" + testName + ".luac"
copy( processError, System.err ); // );
} finally { // if ( script == null ) {
processError.close(); script = getClass().getResourceAsStream("/" + testName + ".lua");
} if (script == null) {
} catch ( IOException ioe ) { fail("Could not find script for test case: " + testName);
ioe.printStackTrace(); }
} // }
} try {
}); return collectProcessOutput(new String[] { "lua", "-" }, script);
errorCopier.start(); } finally {
script.close();
p.waitFor(); }
inputCopier.join(); }
outputCopier.join(); }
errorCopier.join();
private String collectProcessOutput(String[] cmd, final InputStream input)
return new String( baos.toByteArray() ); throws IOException, InterruptedException {
Runtime r = Runtime.getRuntime();
} finally { final ByteArrayOutputStream baos = new ByteArrayOutputStream();
p.destroy(); final Process p = r.exec(cmd);
} try {
} // start a thread to write the given input to the subprocess.
Thread inputCopier = (new Thread() {
private String readString( InputStream is ) throws IOException { public void run() {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); try {
copy( is, baos ); OutputStream processStdIn = p.getOutputStream();
return new String( baos.toByteArray() ); try {
} copy(input, processStdIn);
} finally {
private void copy( InputStream is, OutputStream os ) throws IOException { processStdIn.close();
byte[] buf = new byte[ 1024 ]; }
int r; } catch (IOException e) {
while ( ( r = is.read( buf ) ) >= 0 ) { e.printStackTrace();
os.write( buf, 0, r ); }
} }
} });
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);
}
}
} }

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