diff --git a/src/core/org/luaj/vm2/LuaClosure.java b/src/core/org/luaj/vm2/LuaClosure.java index 4b7088bb..df59c8e0 100644 --- a/src/core/org/luaj/vm2/LuaClosure.java +++ b/src/core/org/luaj/vm2/LuaClosure.java @@ -236,14 +236,14 @@ public class LuaClosure extends LuaFunction { b = i>>>23; c = (i>>14)&0x1ff; { - LuaValue r = stack[c-1].concat(stack[c]); - if ( (c-=2) >= b ) { - Buffer sb = r.buffer(); - while ( c>=b ) - sb = stack[c--].concat(sb); - r = sb.value(); + if ( c > b+1 ) { + Buffer sb = stack[c].buffer(); + while ( --c>=b ) + sb = stack[c].concat(sb); + stack[a] = sb.value(); + } else { + stack[a] = stack[c-1].concat(stack[c]); } - stack[a] = r; } continue; diff --git a/src/core/org/luaj/vm2/LuaNumber.java b/src/core/org/luaj/vm2/LuaNumber.java index aa3d1632..878b7a0c 100644 --- a/src/core/org/luaj/vm2/LuaNumber.java +++ b/src/core/org/luaj/vm2/LuaNumber.java @@ -64,6 +64,7 @@ public class LuaNumber extends LuaValue { public LuaValue concat(LuaValue rhs) { return rhs.concatTo(this); } public Buffer concat(Buffer rhs) { return rhs.prepend(this.strvalue()); } + public LuaValue concatTo(LuaValue lhs) { return lhs.concaterror(); } public LuaValue concatTo(LuaNumber lhs) { return strvalue().concatTo(lhs.strvalue()); } public LuaValue concatTo(LuaString lhs) { return strvalue().concatTo(lhs); } diff --git a/src/core/org/luaj/vm2/LuaString.java b/src/core/org/luaj/vm2/LuaString.java index 0d8a596b..94085dc2 100644 --- a/src/core/org/luaj/vm2/LuaString.java +++ b/src/core/org/luaj/vm2/LuaString.java @@ -157,6 +157,7 @@ public class LuaString extends LuaValue { // concatenation public LuaValue concat(LuaValue rhs) { return rhs.concatTo(this); } public Buffer concat(Buffer rhs) { return rhs.prepend(this); } + public LuaValue concatTo(LuaValue lhs) { return lhs.concaterror(); } public LuaValue concatTo(LuaNumber lhs) { return concatTo(lhs.strvalue()); } public LuaValue concatTo(LuaString lhs) { byte[] b = new byte[lhs.m_length+this.m_length]; diff --git a/src/core/org/luaj/vm2/LuaValue.java b/src/core/org/luaj/vm2/LuaValue.java index 3cdcaead..38a9265a 100644 --- a/src/core/org/luaj/vm2/LuaValue.java +++ b/src/core/org/luaj/vm2/LuaValue.java @@ -179,6 +179,7 @@ public class LuaValue extends Varargs { protected LuaValue aritherror(String fun) { throw new LuaError("attempt to perform arithmetic '"+fun+"' on "+typename()); } protected LuaValue compareerror(String rhs) { throw new LuaError("attempt to compare "+typename()+" with "+rhs); } protected LuaValue compareerror(LuaValue rhs) { throw new LuaError("attempt to compare "+typename()+" with "+rhs.typename()); } + protected LuaValue concaterror() { throw new LuaError("attempt to concatenate "+typename()); } // table operations public LuaValue get( LuaValue key ) { return gettable(this,key); } @@ -344,18 +345,17 @@ public class LuaValue extends Varargs { public int strcmp( LuaString rhs ) { error("attempt to compare "+typename()); return 0; } // concatenation - public LuaValue concat(LuaValue rhs) { return this.concatmt(rhs); } - public LuaValue concatTo(LuaNumber lhs) { return lhs.concatmt(this); } - public LuaValue concatTo(LuaString lhs) { return lhs.concatmt(this); } + public LuaValue concat(LuaValue rhs) { return rhs.concatTo(this); } + public LuaValue concatTo(LuaValue lhs) { return lhs.concatmt(this); } + public LuaValue concatTo(LuaNumber lhs) { return concaterror(); } + public LuaValue concatTo(LuaString lhs) { return concaterror(); } public Buffer buffer() { return new Buffer(this); } - public Buffer concat(Buffer rhs) { - return rhs.setvalue(checkmetatag(CONCAT,"attempt to concatenate ").call(this, rhs.value())); - } + public Buffer concat(Buffer rhs) { return rhs.setvalue(concat(rhs.value())); } public LuaValue concatmt(LuaValue rhs) { LuaValue h=metatag(CONCAT); LuaValue v=this; - if ( h.isnil() || (h=(v=rhs).metatag(CONCAT)).isnil()) - v.typerror("attempt to concatenate "); + if ( h.isnil() && (h=(v=rhs).metatag(CONCAT)).isnil()) + return v.concaterror(); return h.call(this,rhs); } diff --git a/test/junit/org/luaj/vm2/UnaryBinaryOperatorsTest.java b/test/junit/org/luaj/vm2/UnaryBinaryOperatorsTest.java index 2b4ec915..2d19b55e 100644 --- a/test/junit/org/luaj/vm2/UnaryBinaryOperatorsTest.java +++ b/test/junit/org/luaj/vm2/UnaryBinaryOperatorsTest.java @@ -25,6 +25,7 @@ import org.luaj.vm2.LuaDouble; import org.luaj.vm2.LuaInteger; import org.luaj.vm2.LuaString; import org.luaj.vm2.LuaValue; +import org.luaj.vm2.lib.TwoArgFunction; import junit.framework.TestCase; @@ -646,4 +647,57 @@ public class UnaryBinaryOperatorsTest extends TestCase { b = def.concat(b); assertEquals( "def123", b.value().tojstring() ); b = abc.concat(b); assertEquals( "abcdef123", b.value().tojstring() ); } + + public void testConcatMetatag() { + LuaValue def = LuaValue.valueOf("abcdefghi").substring(3,6); + LuaValue ghi = LuaValue.valueOf("abcdefghi").substring(6,9); + LuaValue tru = LuaValue.TRUE; + LuaValue fal = LuaValue.FALSE; + LuaValue tbl = new LuaTable(); + LuaValue uda = new LuaUserdata(new Object()); + try { + // always use left argument + LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { + LuaValue.CONCAT, new TwoArgFunction() { + public LuaValue call(LuaValue lhs, LuaValue rhs) { + return lhs; + } + } } ); + assertEquals( tru, tru.concat(tbl) ); + assertEquals( tbl, tbl.concat(tru) ); + assertEquals( tru, tru.concat(new Buffer(tbl)).value() ); + assertEquals( tbl, tbl.concat(new Buffer(tru)).value() ); + assertEquals( fal, fal.concat(tbl.concat(new Buffer(tru))).value() ); + assertEquals( uda, uda.concat(tru.concat(new Buffer(tbl))).value() ); + try { tru.concat(def); fail("did not throw error"); } catch ( LuaError le ) { }; + try { def.concat(tru); fail("did not throw error"); } catch ( LuaError le ) { }; + try { tru.concat(new Buffer(def)).value(); fail("did not throw error"); } catch ( LuaError le ) { }; + try { def.concat(new Buffer(tru)).value(); fail("did not throw error"); } catch ( LuaError le ) { }; + try { fal.concat(def.concat(new Buffer(tru))).value(); fail("did not throw error"); } catch ( LuaError le ) { }; + try { ghi.concat(tru.concat(new Buffer(def))).value(); fail("did not throw error"); } catch ( LuaError le ) { }; + + // always use right argument + LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { + LuaValue.CONCAT, new TwoArgFunction() { + public LuaValue call(LuaValue lhs, LuaValue rhs) { + return rhs; + } + } } ); + assertEquals( tbl, tru.concat(tbl) ); + assertEquals( tru, tbl.concat(tru) ); + assertEquals( tbl, tru.concat(new Buffer(tbl)).value() ); + assertEquals( tru, tbl.concat(new Buffer(tru)).value() ); + assertEquals( tru, uda.concat(tbl.concat(new Buffer(tru))).value() ); + assertEquals( tbl, fal.concat(tru.concat(new Buffer(tbl))).value() ); + try { tru.concat(def); fail("did not throw error"); } catch ( LuaError le ) { }; + try { def.concat(tru); fail("did not throw error"); } catch ( LuaError le ) { }; + try { tru.concat(new Buffer(def)).value(); fail("did not throw error"); } catch ( LuaError le ) { }; + try { def.concat(new Buffer(tru)).value(); fail("did not throw error"); } catch ( LuaError le ) { }; + try { uda.concat(def.concat(new Buffer(tru))).value(); fail("did not throw error"); } catch ( LuaError le ) { }; + try { fal.concat(tru.concat(new Buffer(def))).value(); fail("did not throw error"); } catch ( LuaError le ) { }; + + } finally { + LuaBoolean.s_metatable = null; + } + } }