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
|
// 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;
|
||||||
|
|||||||
@@ -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
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