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