diff --git a/src/core/org/luaj/vm2/LuaDouble.java b/src/core/org/luaj/vm2/LuaDouble.java index bad3640c..3b598362 100644 --- a/src/core/org/luaj/vm2/LuaDouble.java +++ b/src/core/org/luaj/vm2/LuaDouble.java @@ -75,7 +75,7 @@ public class LuaDouble extends LuaNumber { public boolean equals(Object o) { return o instanceof LuaDouble? ((LuaDouble)o).v == v: false; } // arithmetic equality - public LuaValue eq( LuaValue rhs ) { return rhs.eq_b(v)? TRUE: FALSE; } + public LuaValue eq( LuaValue rhs ) { return rhs.eq_b(v) || rhs.type()==TNUMBER && eqmt_b(rhs)? TRUE: FALSE; } public boolean eq_b( LuaValue rhs ) { return rhs.eq_b(v); } public boolean eq_b( double rhs ) { return v == rhs; } public boolean eq_b( int rhs ) { return v == rhs; } diff --git a/src/core/org/luaj/vm2/LuaInteger.java b/src/core/org/luaj/vm2/LuaInteger.java index 578646c7..9afee4a7 100644 --- a/src/core/org/luaj/vm2/LuaInteger.java +++ b/src/core/org/luaj/vm2/LuaInteger.java @@ -105,7 +105,7 @@ public class LuaInteger extends LuaNumber { public boolean equals(Object o) { return o instanceof LuaInteger? ((LuaInteger)o).v == v: false; } // arithmetic equality - public LuaValue eq( LuaValue rhs ) { return rhs.eq_b(v)? TRUE: FALSE; } + public LuaValue eq( LuaValue rhs ) { return rhs.eq_b(v) || rhs.type()==TNUMBER && eqmt_b(rhs)? TRUE: FALSE; } public boolean eq_b( LuaValue rhs ) { return rhs.eq_b(v); } public boolean eq_b( double rhs ) { return v == rhs; } public boolean eq_b( int rhs ) { return v == rhs; } diff --git a/src/core/org/luaj/vm2/LuaString.java b/src/core/org/luaj/vm2/LuaString.java index 5a90ff95..5ccc0a27 100644 --- a/src/core/org/luaj/vm2/LuaString.java +++ b/src/core/org/luaj/vm2/LuaString.java @@ -313,7 +313,7 @@ public class LuaString extends LuaValue { } public boolean eq_b( LuaValue val ) { - return val.eq_b( this ); + return val == this || (val.type()==TSTRING && val.eq_b(this)); } public static boolean equals( LuaString a, int i, LuaString b, int j, int n ) { diff --git a/src/core/org/luaj/vm2/LuaValue.java b/src/core/org/luaj/vm2/LuaValue.java index f779f6b2..81751369 100644 --- a/src/core/org/luaj/vm2/LuaValue.java +++ b/src/core/org/luaj/vm2/LuaValue.java @@ -179,7 +179,6 @@ 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); } @@ -257,8 +256,8 @@ public class LuaValue extends Varargs { public boolean equals(Object obj) { return this == obj; } // arithmetic equality - public LuaValue eq( LuaValue val ) { return eq_b(val)? TRUE: FALSE; } - public boolean eq_b( LuaValue val ) { return this == val; } + public LuaValue eq( LuaValue val ) { return this == val || eq_b(val)? TRUE: FALSE; } + public boolean eq_b( LuaValue val ) { return this == val || (type() == val.type() && eqmt_b(val)); } public boolean eq_b( LuaTable val ) { return false; } public boolean eq_b( LuaUserdata val ) { return false; } public boolean eq_b( LuaString val ) { return false; } @@ -300,8 +299,11 @@ public class LuaValue extends Varargs { public LuaValue modFrom(double lhs) { return aritherror("modFrom"); } protected LuaValue arithmt(LuaValue tag, LuaValue op2) { LuaValue h = this.metatag(tag); - if ( h.isnil() ) - h = op2.checkmetatag(tag, "attempt to perform arithmetic on "); + if ( h.isnil() ) { + h = op2.metatag(tag); + if ( h.isnil() ) + error( "attempt to perform arithmetic "+tag+" on "+typename()+" and "+op2.typename() ); + } return h.call( this, op2 ); } @@ -336,7 +338,7 @@ public class LuaValue extends Varargs { if ( !h.isnil() && h == op1.metatag(tag) ) return h.call(this, op1); } - return error("attempt to compare "+typename()); + return error("attempt to compare "+tag+" on "+typename()+" and "+op1.typename()); } @@ -354,7 +356,7 @@ public class LuaValue extends Varargs { public LuaValue concatmt(LuaValue rhs) { LuaValue h=metatag(CONCAT); if ( h.isnil() && (h=rhs.metatag(CONCAT)).isnil()) - return (isstring()? rhs: this).concaterror(); + error("attempt to concatenate "+typename()+" and "+rhs.typename()); return h.call(this,rhs); } diff --git a/src/core/org/luaj/vm2/WeakTable.java b/src/core/org/luaj/vm2/WeakTable.java index ed22e510..2023c846 100644 --- a/src/core/org/luaj/vm2/WeakTable.java +++ b/src/core/org/luaj/vm2/WeakTable.java @@ -271,8 +271,7 @@ public class WeakTable extends LuaTable { } public int type() { - illegal("type","weak entry"); - return 0; + return TNONE; } public String typename() { @@ -293,6 +292,10 @@ public class WeakTable extends LuaTable { return weakkey.eq_b(rhs); } + public boolean eq_b( LuaString rhs ) { return false; } + public boolean eq_b( double val ) { return false; } + public boolean eq_b( int val ) { return false; } + public boolean isweaknil() { return weakkey.isweaknil() || weakvalue.isweaknil(); } diff --git a/test/junit/org/luaj/vm2/UnaryBinaryOperatorsTest.java b/test/junit/org/luaj/vm2/UnaryBinaryOperatorsTest.java index 73ac8a13..5bfc5586 100644 --- a/test/junit/org/luaj/vm2/UnaryBinaryOperatorsTest.java +++ b/test/junit/org/luaj/vm2/UnaryBinaryOperatorsTest.java @@ -21,14 +21,12 @@ ******************************************************************************/ package org.luaj.vm2; -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 java.lang.reflect.InvocationTargetException; import junit.framework.TestCase; +import org.luaj.vm2.lib.TwoArgFunction; + /** * Tests of basic unary and binary operators on main value types. */ @@ -212,6 +210,123 @@ public class UnaryBinaryOperatorsTest extends TestCase { assertEquals(LuaValue.NIL.eq(da),LuaValue.FALSE); } + private static final TwoArgFunction RETURN_NIL = new TwoArgFunction() { + public LuaValue call(LuaValue lhs, LuaValue rhs) { + return NIL; + } + }; + + private static final TwoArgFunction RETURN_ONE = new TwoArgFunction() { + public LuaValue call(LuaValue lhs, LuaValue rhs) { + return ONE; + } + }; + + + public void testEqualsMetatag() { + LuaValue tru = LuaValue.TRUE; + LuaValue fal = LuaValue.FALSE; + LuaValue zer = LuaValue.ZERO; + LuaValue one = LuaValue.ONE; + LuaValue abc = LuaValue.valueOf("abcdef").substring(0,3); + LuaValue def = LuaValue.valueOf("abcdef").substring(3,6); + LuaValue pi = LuaValue.valueOf(Math.PI); + LuaValue ee = LuaValue.valueOf(Math.E); + LuaValue tbl = new LuaTable(); + LuaValue tbl2 = new LuaTable(); + LuaValue tbl3 = new LuaTable(); + LuaValue nilb = LuaValue.valueOf( LuaValue.NIL.toboolean() ); + LuaValue oneb = LuaValue.valueOf( LuaValue.ONE.toboolean() ); + assertEquals( LuaValue.FALSE, nilb ); + assertEquals( LuaValue.TRUE, oneb ); + LuaValue smt = LuaString.s_metatable; + try { + // always return nil0 + LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_NIL, } ); + LuaNumber.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_NIL, } ); + LuaString.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_NIL, } ); + tbl.setmetatable(LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_NIL, } )); + tbl2.setmetatable(LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_NIL, } )); + // diff metatag function + tbl3.setmetatable(LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_ONE, } )); + + // same type, same value, does not invoke metatag op + assertEquals( tru, tru.eq(tru) ); + assertEquals( tru, tbl.eq(tbl) ); + assertEquals( tru, one.eq(one) ); + // same type, different value, same metatag op. comparabile via metatag op + assertEquals( nilb, tbl.eq(tbl2) ); + assertEquals( nilb, tbl2.eq(tbl) ); + assertEquals( nilb, tru.eq(fal) ); + assertEquals( nilb, fal.eq(tru) ); + assertEquals( nilb, zer.eq(one) ); + assertEquals( nilb, one.eq(zer) ); + assertEquals( nilb, pi.eq(ee) ); + assertEquals( nilb, ee.eq(pi) ); + assertEquals( nilb, pi.eq(one) ); + assertEquals( nilb, one.eq(pi) ); + assertEquals( nilb, abc.eq(def) ); + assertEquals( nilb, def.eq(abc) ); + // different types. not comparable + assertEquals( fal, fal.eq(tbl) ); + assertEquals( fal, tbl.eq(fal) ); + assertEquals( fal, tbl.eq(one) ); + assertEquals( fal, one.eq(tbl) ); + assertEquals( fal, fal.eq(one) ); + assertEquals( fal, one.eq(fal) ); + assertEquals( fal, abc.eq(one) ); + assertEquals( fal, one.eq(abc) ); + // same type, different metatag ops. not comparable + assertEquals( fal, tbl.eq(tbl3) ); + assertEquals( fal, tbl3.eq(tbl) ); + + // always use right argument + LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_ONE, } ); + LuaNumber.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_ONE, } ); + LuaString.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_ONE, } ); + tbl.setmetatable(LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_ONE, } )); + tbl2.setmetatable(LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_ONE, } )); + // diff metatag function + tbl3.setmetatable(LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_NIL, } )); + + // same type, same value, does not invoke metatag op + assertEquals( tru, tru.eq(tru) ); + assertEquals( tru, tbl.eq(tbl) ); + assertEquals( tru, one.eq(one) ); + // same type, different value, same metatag op. comparabile via metatag op + assertEquals( oneb, tbl.eq(tbl2) ); + assertEquals( oneb, tbl2.eq(tbl) ); + assertEquals( oneb, tru.eq(fal) ); + assertEquals( oneb, fal.eq(tru) ); + assertEquals( oneb, zer.eq(one) ); + assertEquals( oneb, one.eq(zer) ); + assertEquals( oneb, pi.eq(ee) ); + assertEquals( oneb, ee.eq(pi) ); + assertEquals( oneb, pi.eq(one) ); + assertEquals( oneb, one.eq(pi) ); + assertEquals( oneb, abc.eq(def) ); + assertEquals( oneb, def.eq(abc) ); + // different types. not comparable + assertEquals( fal, fal.eq(tbl) ); + assertEquals( fal, tbl.eq(fal) ); + assertEquals( fal, tbl.eq(one) ); + assertEquals( fal, one.eq(tbl) ); + assertEquals( fal, fal.eq(one) ); + assertEquals( fal, one.eq(fal) ); + assertEquals( fal, abc.eq(one) ); + assertEquals( fal, one.eq(abc) ); + // same type, different metatag ops. not comparable + assertEquals( fal, tbl.eq(tbl3) ); + assertEquals( fal, tbl3.eq(tbl) ); + + } finally { + LuaBoolean.s_metatable = null; + LuaNumber.s_metatable = null; + LuaString.s_metatable = smt; + } + } + + public void testAdd() { LuaValue ia=LuaValue.valueOf(111), ib=LuaValue.valueOf(44); LuaValue da=LuaValue.valueOf(55.25), db=LuaValue.valueOf(3.5); @@ -236,7 +351,7 @@ public class UnaryBinaryOperatorsTest extends TestCase { assertEquals(133.125,ia.add(sa).todouble()); assertEquals(133.125,sa.add(ia).todouble()); assertEquals(77.375, da.add(sa).todouble()); - assertEquals(77.375, sa.add(da).todouble()); + assertEquals(77.375, sa.add(da).todouble()); } public void testSub() { @@ -338,6 +453,155 @@ public class UnaryBinaryOperatorsTest extends TestCase { assertEquals(luaMod(1.5,.25), sa.mod(da).todouble()); } + public void testArithErrors() { + LuaValue ia=LuaValue.valueOf(111), ib=LuaValue.valueOf(44); + LuaValue da=LuaValue.valueOf(55.25), db=LuaValue.valueOf(3.5); + LuaValue sa=LuaValue.valueOf("22.125"), sb=LuaValue.valueOf("7.25"); + + String[] ops = { "add", "sub", "mul", "div", "mod", "pow" }; + LuaValue[] vals = { LuaValue.NIL, LuaValue.TRUE, LuaValue.tableOf() }; + LuaValue[] numerics = { LuaValue.valueOf(111), LuaValue.valueOf(55.25), LuaValue.valueOf("22.125") }; + for ( int i=0; i