# HG changeset patch # User Martin Soch # Date 1359108052 -3600 # Node ID 8e546d108658f6f197c7e257c2a46864eb6e966a # Parent 95753ad6519225a764d0ec8015c1e42ba96ade1a Long arithmetic prototype, Long currently represented by separate JavaScript object with two JS-Numbers. Just few operation implemented to pass tests. Tests under vmtest directory are still failing. diff -r 95753ad65192 -r 8e546d108658 emul/src/main/resources/org/apidesign/vm4brwsr/emul/java_lang_Number.js --- a/emul/src/main/resources/org/apidesign/vm4brwsr/emul/java_lang_Number.js Mon Jan 21 15:57:01 2013 +0100 +++ b/emul/src/main/resources/org/apidesign/vm4brwsr/emul/java_lang_Number.js Fri Jan 25 11:00:52 2013 +0100 @@ -6,4 +6,66 @@ }; Number.prototype.toInt8 = function() { return (this << 24) >> 24; }; -Number.prototype.toInt16 = function() { return (this << 16) >> 16; }; \ No newline at end of file +Number.prototype.toInt16 = function() { return (this << 16) >> 16; }; + +var Long = function(low, hi) { + this.low = low; + this.hi = hi; +}; + +function LongFromNumber(x) { + return new Long(x % 0xFFFFFFFF, Math.floor(x / 0xFFFFFFFF)); +}; + +function MakeLong(x) { + if ((x.hi == undefined) && (x.low == undefined)) { + return LongFromNumber(x); + } + return x; +}; + +Long.prototype.toInt32 = function() { return this.low | 0; }; +Long.prototype.toFP = function() { return this.hi * 0xFFFFFFFF + this.low; }; + +Long.prototype.toString = function() { + return String(this.toFP()); +}; + +Long.prototype.valueOf = function() { + return this.toFP(); +}; + +Long.prototype.compare64 = function(x) { + if (this.hi == x.hi) { + return (this.low == x.low) ? 0 : ((this.low < x.low) ? -1 : 1); + } + return (this.hi < x.hi) ? -1 : 1; +}; + +Long.prototype.add64 = function(x) { + low = this.low + x.low; + carry = 0; + if (low > 0xFFFFFFFF) { + carry = 1; + low -= 0xFFFFFFFF; + } + hi = (this.hi + x.hi + carry) & 0xFFFFFFFF; + return new Long(low, hi); +}; + +Long.prototype.div64 = function(x) { + return LongFromNumber(Math.floor(this.toFP() / x.toFP())); +}; + +Long.prototype.shl64 = function(x) { + if (x > 32) { + hi = (this.low << (x - 32)) & 0xFFFFFFFF; + low = 0; + } else { + hi = (this.hi << x) & 0xFFFFFFFF; + low_reminder = this.low >> x; + hi |= low_reminder; + low = this.low << x; + } + return new Long(low, hi); +}; diff -r 95753ad65192 -r 8e546d108658 vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Mon Jan 21 15:57:01 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Fri Jan 25 11:00:52 2013 +0100 @@ -304,7 +304,7 @@ emit(out, "var @1 = @2;", smapper.pushI(), lmapper.getI(0)); break; case opc_lload_0: - emit(out, "var @1 = @2;", smapper.pushL(), lmapper.getL(0)); + emit(out, "var @1 = MakeLong(@2);", smapper.pushL(), lmapper.getL(0)); break; case opc_fload_0: emit(out, "var @1 = @2;", smapper.pushF(), lmapper.getF(0)); @@ -319,7 +319,7 @@ emit(out, "var @1 = @2;", smapper.pushI(), lmapper.getI(1)); break; case opc_lload_1: - emit(out, "var @1 = @2;", smapper.pushL(), lmapper.getL(1)); + emit(out, "var @1 = MakeLong(@2);", smapper.pushL(), lmapper.getL(1)); break; case opc_fload_1: emit(out, "var @1 = @2;", smapper.pushF(), lmapper.getF(1)); @@ -334,7 +334,7 @@ emit(out, "var @1 = @2;", smapper.pushI(), lmapper.getI(2)); break; case opc_lload_2: - emit(out, "var @1 = @2;", smapper.pushL(), lmapper.getL(2)); + emit(out, "var @1 = MakeLong(@2);", smapper.pushL(), lmapper.getL(2)); break; case opc_fload_2: emit(out, "var @1 = @2;", smapper.pushF(), lmapper.getF(2)); @@ -349,7 +349,7 @@ emit(out, "var @1 = @2;", smapper.pushI(), lmapper.getI(3)); break; case opc_lload_3: - emit(out, "var @1 = @2;", smapper.pushL(), lmapper.getL(3)); + emit(out, "var @1 = MakeLong(@2);", smapper.pushL(), lmapper.getL(3)); break; case opc_fload_3: emit(out, "var @1 = @2;", smapper.pushF(), lmapper.getF(3)); @@ -481,7 +481,7 @@ emit(out, "@1 = @1.add32(@2);", smapper.getI(1), smapper.popI()); break; case opc_ladd: - emit(out, "@1 += @2;", smapper.getL(1), smapper.popL()); + emit(out, "@1 = @1.add64(@2);", smapper.getL(1), smapper.popL()); break; case opc_fadd: emit(out, "@1 += @2;", smapper.getF(1), smapper.popF()); @@ -518,7 +518,7 @@ smapper.getI(1), smapper.popI()); break; case opc_ldiv: - emit(out, "@1 = Math.floor(@1 / @2);", + emit(out, "@1 = @1.div64(@2);", smapper.getL(1), smapper.popL()); break; case opc_fdiv: @@ -573,7 +573,7 @@ emit(out, "@1 <<= @2;", smapper.getI(1), smapper.popI()); break; case opc_lshl: - emit(out, "@1 <<= @2;", smapper.getL(1), smapper.popI()); + emit(out, "@1 = @1.shl64(@2);", smapper.getL(1), smapper.popI()); break; case opc_ishr: emit(out, "@1 >>= @2;", smapper.getI(1), smapper.popI()); @@ -618,7 +618,7 @@ emit(out, "return @1;", smapper.popA()); break; case opc_i2l: - emit(out, "var @2 = @1;", smapper.popI(), smapper.pushL()); + emit(out, "var @2 = new Long(@1, 0);", smapper.popI(), smapper.pushL()); break; case opc_i2f: emit(out, "var @2 = @1;", smapper.popI(), smapper.pushF()); @@ -627,14 +627,14 @@ emit(out, "var @2 = @1;", smapper.popI(), smapper.pushD()); break; case opc_l2i: - emit(out, "var @2 = @1;", smapper.popL(), smapper.pushI()); + emit(out, "var @2 = @1.toInt32();", smapper.popL(), smapper.pushI()); break; // max int check? case opc_l2f: - emit(out, "var @2 = @1;", smapper.popL(), smapper.pushF()); + emit(out, "var @2 = @1.toFP();", smapper.popL(), smapper.pushF()); break; case opc_l2d: - emit(out, "var @2 = @1;", smapper.popL(), smapper.pushD()); + emit(out, "var @2 = @1.toFP();", smapper.popL(), smapper.pushD()); break; case opc_f2d: emit(out, "var @2 = @1;", smapper.popF(), smapper.pushD()); @@ -647,7 +647,7 @@ smapper.popF(), smapper.pushI()); break; case opc_f2l: - emit(out, "var @2 = Math.floor(@1);", + emit(out, "var @2 = LongFromNumber(Math.floor(@1));", smapper.popF(), smapper.pushL()); break; case opc_d2i: @@ -655,7 +655,7 @@ smapper.popD(), smapper.pushI()); break; case opc_d2l: - emit(out, "var @2 = Math.floor(@1);", + emit(out, "var @2 = LongFromNumber(Math.floor(@1));", smapper.popD(), smapper.pushL()); break; case opc_i2b: @@ -680,7 +680,7 @@ emit(out, "var @1 = 0;", smapper.pushD()); break; case opc_lconst_0: - emit(out, "var @1 = 0;", smapper.pushL()); + emit(out, "var @1 = new Long(0,0);", smapper.pushL()); break; case opc_fconst_0: emit(out, "var @1 = 0;", smapper.pushF()); @@ -689,7 +689,7 @@ emit(out, "var @1 = 1;", smapper.pushI()); break; case opc_lconst_1: - emit(out, "var @1 = 1;", smapper.pushL()); + emit(out, "var @1 = new Long(1,0);", smapper.pushL()); break; case opc_fconst_1: emit(out, "var @1 = 1;", smapper.pushF()); @@ -725,11 +725,19 @@ i += 2; String v = encodeConstant(indx); int type = VarType.fromConstantType(jc.getTag(indx)); - emit(out, "var @1 = @2;", smapper.pushT(type), v); + if (type == VarType.LONG) { + final Long lv = new Long(v); + final int low = (int)(lv.longValue() & 0xFFFFFFFF); + final int hi = (int)(lv.longValue() >> 32); + emit(out, "var @1 = new Long(0x@2, 0x@3);", smapper.pushL(), + Integer.toHexString(low), Integer.toHexString(hi)); + } else { + emit(out, "var @1 = @2;", smapper.pushT(type), v); + } break; } case opc_lcmp: - emit(out, "var @3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);", + emit(out, "var @3 = @2.compare64(@1);", smapper.popL(), smapper.popL(), smapper.pushI()); break; case opc_fcmpl: diff -r 95753ad65192 -r 8e546d108658 vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java Mon Jan 21 15:57:01 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java Fri Jan 25 11:00:52 2013 +0100 @@ -157,22 +157,10 @@ } private static void assertExec( - String msg, Class clazz, String method, Object expRes, Object... args) throws Exception { - - Object ret = null; - try { - ret = code.invokeFunction("bck2brwsr"); - ret = code.invokeMethod(ret, "loadClass", clazz.getName()); - ret = code.invokeMethod(ret, method, args); - } catch (ScriptException ex) { - fail("Execution failed in\n" + StaticMethodTest.dumpJS(codeSeq), ex); - } catch (NoSuchMethodException ex) { - fail("Cannot find method in\n" + StaticMethodTest.dumpJS(codeSeq), ex); - } - if (ret == null && expRes == null) { - return; - } - if (expRes.equals(ret)) { + String msg, Class clazz, String method, Object expRes, Object... args) throws Exception + { + Object ret = TestUtils.execCode(code, codeSeq, msg, clazz, method, expRes, args); + if (ret == null) { return; } if (expRes instanceof Double && ret instanceof Double) { diff -r 95753ad65192 -r 8e546d108658 vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java Mon Jan 21 15:57:01 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java Fri Jan 25 11:00:52 2013 +0100 @@ -270,17 +270,8 @@ String msg, Class clazz, String method, Object expRes, Object... args ) throws Exception { - Object ret = null; - try { - ret = toRun.invokeFunction("bck2brwsr"); - ret = toRun.invokeMethod(ret, "loadClass", clazz.getName()); - ret = toRun.invokeMethod(ret, method, args); - } catch (ScriptException ex) { - fail("Execution failed in\n" + dumpJS(theCode), ex); - } catch (NoSuchMethodException ex) { - fail("Cannot find method in\n" + dumpJS(theCode), ex); - } - if (ret == null && expRes == null) { + Object ret = TestUtils.execCode(toRun, theCode, msg, clazz, method, expRes, args); + if (ret == null) { return; } if (expRes != null && expRes.equals(ret)) { diff -r 95753ad65192 -r 8e546d108658 vm/src/test/java/org/apidesign/vm4brwsr/TestUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/TestUtils.java Fri Jan 25 11:00:52 2013 +0100 @@ -0,0 +1,59 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import javax.script.Invocable; +import javax.script.ScriptException; +import static org.testng.Assert.*; + +class TestUtils { + + static Object execCode(Invocable code, CharSequence codeSeq, + String msg, Class clazz, String method, Object expRes, Object... args) + throws Exception + { + Object ret = null; + try { + ret = code.invokeFunction("bck2brwsr"); + ret = code.invokeMethod(ret, "loadClass", clazz.getName()); + ret = code.invokeMethod(ret, method, args); + } catch (ScriptException ex) { + fail("Execution failed in " + StaticMethodTest.dumpJS(codeSeq), ex); + } catch (NoSuchMethodException ex) { + fail("Cannot find method in " + StaticMethodTest.dumpJS(codeSeq), ex); + } + if (ret == null && expRes == null) { + return null; + } + if (expRes.equals(ret)) { + return null; + } + if (expRes instanceof Number) { + // in case of Long it is necessary convert it to number + // since the Long is represented by two numbers in JavaScript + try { + ret = code.invokeFunction("Number", ret); + } catch (ScriptException ex) { + fail("Conversion to number failed in " + StaticMethodTest.dumpJS(codeSeq), ex); + } catch (NoSuchMethodException ex) { + fail("Cannot find global Number(x) function in " + StaticMethodTest.dumpJS(codeSeq), ex); + } + } + return ret; + } +} diff -r 95753ad65192 -r 8e546d108658 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/LongArithmeticTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/LongArithmeticTest.java Fri Jan 25 11:00:52 2013 +0100 @@ -0,0 +1,104 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class LongArithmeticTest { + + private static long add(long x, long y) { + return (x + y); + } + + private static long sub(long x, long y) { + return (x - y); + } + + private static long mul(long x, long y) { + return (x * y); + } + + private static long div(long x, long y) { + return (x / y); + } + + private static long mod(long x, long y) { + return (x % y); + } + + @Compare public long conversion() { + return Long.MAX_VALUE; + } + + /* + @Compare public long addOverflow() { + return add(Long.MAX_VALUE, 1l); + } + + @Compare public long subUnderflow() { + return sub(Long.MIN_VALUE, 1l); + } + + @Compare public long addMaxLongAndMaxLong() { + return add(Long.MAX_VALUE, Long.MAX_VALUE); + } + + @Compare public long subMinLongAndMinLong() { + return sub(Long.MIN_VALUE, Long.MIN_VALUE); + } + + @Compare public long multiplyMaxLong() { + return mul(Long.MAX_VALUE, 2l); + } + + @Compare public long multiplyMaxLongAndMaxLong() { + return mul(Long.MAX_VALUE, Long.MAX_VALUE); + } + + @Compare public long multiplyMinLong() { + return mul(Long.MIN_VALUE, 2l); + } + + @Compare public long multiplyMinLongAndMinLong() { + return mul(Long.MIN_VALUE, Long.MIN_VALUE); + } + + @Compare public long multiplyPrecision() { + return mul(17638l, 1103l); + } + + @Compare public long division() { + return div(1l, 2l); + } + + @Compare public long divisionReminder() { + return mod(1l, 2l); + } + */ + + @Factory + public static Object[] create() { + return VMTest.create(LongArithmeticTest.class); + } +}