fix tail call return value processing
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
62
src/test/res/tailcall.lua
Normal file
62
src/test/res/tailcall.lua
Normal 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)
|
||||
@@ -1 +1 @@
|
||||
version: 0.38
|
||||
version: 0.39
|
||||
|
||||
Reference in New Issue
Block a user