Long arithmetic prototype, Long currently represented by separate JavaScript object with two JS-Numbers. arithmetic
authorMartin Soch <Martin.Soch@oracle.com>
Fri, 25 Jan 2013 11:00:52 +0100
brancharithmetic
changeset 5828e546d108658
parent 581 95753ad65192
child 593 b42911b78a16
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.
emul/src/main/resources/org/apidesign/vm4brwsr/emul/java_lang_Number.js
vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java
vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java
vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java
vm/src/test/java/org/apidesign/vm4brwsr/TestUtils.java
vmtest/src/test/java/org/apidesign/bck2brwsr/tck/LongArithmeticTest.java
     1.1 --- a/emul/src/main/resources/org/apidesign/vm4brwsr/emul/java_lang_Number.js	Mon Jan 21 15:57:01 2013 +0100
     1.2 +++ b/emul/src/main/resources/org/apidesign/vm4brwsr/emul/java_lang_Number.js	Fri Jan 25 11:00:52 2013 +0100
     1.3 @@ -6,4 +6,66 @@
     1.4  };
     1.5  
     1.6  Number.prototype.toInt8 = function()  { return (this << 24) >> 24; };
     1.7 -Number.prototype.toInt16 = function() { return (this << 16) >> 16; };
     1.8 \ No newline at end of file
     1.9 +Number.prototype.toInt16 = function() { return (this << 16) >> 16; };
    1.10 +
    1.11 +var Long = function(low, hi) {
    1.12 +    this.low = low;
    1.13 +    this.hi = hi;
    1.14 +};
    1.15 +
    1.16 +function LongFromNumber(x) {
    1.17 +    return new Long(x % 0xFFFFFFFF, Math.floor(x / 0xFFFFFFFF));
    1.18 +};
    1.19 +
    1.20 +function MakeLong(x) {
    1.21 +    if ((x.hi == undefined) && (x.low == undefined)) {
    1.22 +        return LongFromNumber(x);
    1.23 +    }
    1.24 +    return x;
    1.25 +};
    1.26 +
    1.27 +Long.prototype.toInt32 = function() { return this.low | 0; };
    1.28 +Long.prototype.toFP = function() { return this.hi * 0xFFFFFFFF + this.low; };
    1.29 +
    1.30 +Long.prototype.toString = function() {
    1.31 +    return String(this.toFP());
    1.32 +};
    1.33 +
    1.34 +Long.prototype.valueOf = function() {
    1.35 +    return this.toFP();
    1.36 +};
    1.37 +
    1.38 +Long.prototype.compare64 = function(x) {
    1.39 +    if (this.hi == x.hi) {
    1.40 +        return (this.low == x.low) ? 0 : ((this.low < x.low) ? -1 : 1);
    1.41 +    }
    1.42 +    return (this.hi < x.hi) ? -1 : 1;
    1.43 +};
    1.44 +
    1.45 +Long.prototype.add64 = function(x) {
    1.46 +    low = this.low + x.low;
    1.47 +    carry = 0;
    1.48 +    if (low > 0xFFFFFFFF) {
    1.49 +        carry = 1;
    1.50 +        low -= 0xFFFFFFFF;
    1.51 +    }
    1.52 +    hi = (this.hi + x.hi + carry) & 0xFFFFFFFF;
    1.53 +    return new Long(low, hi);
    1.54 +};
    1.55 +
    1.56 +Long.prototype.div64 = function(x) {
    1.57 +    return LongFromNumber(Math.floor(this.toFP() / x.toFP()));
    1.58 +};
    1.59 +
    1.60 +Long.prototype.shl64 = function(x) {
    1.61 +    if (x > 32) {
    1.62 +        hi = (this.low << (x - 32)) & 0xFFFFFFFF;
    1.63 +        low = 0;
    1.64 +    } else {
    1.65 +        hi = (this.hi << x) & 0xFFFFFFFF;
    1.66 +        low_reminder = this.low >> x;
    1.67 +        hi |= low_reminder;
    1.68 +        low = this.low << x;
    1.69 +    }
    1.70 +    return new Long(low, hi);
    1.71 +};
     2.1 --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Mon Jan 21 15:57:01 2013 +0100
     2.2 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Fri Jan 25 11:00:52 2013 +0100
     2.3 @@ -304,7 +304,7 @@
     2.4                      emit(out, "var @1 = @2;", smapper.pushI(), lmapper.getI(0));
     2.5                      break;
     2.6                  case opc_lload_0:
     2.7 -                    emit(out, "var @1 = @2;", smapper.pushL(), lmapper.getL(0));
     2.8 +                    emit(out, "var @1 = MakeLong(@2);", smapper.pushL(), lmapper.getL(0));
     2.9                      break;
    2.10                  case opc_fload_0:
    2.11                      emit(out, "var @1 = @2;", smapper.pushF(), lmapper.getF(0));
    2.12 @@ -319,7 +319,7 @@
    2.13                      emit(out, "var @1 = @2;", smapper.pushI(), lmapper.getI(1));
    2.14                      break;
    2.15                  case opc_lload_1:
    2.16 -                    emit(out, "var @1 = @2;", smapper.pushL(), lmapper.getL(1));
    2.17 +                    emit(out, "var @1 = MakeLong(@2);", smapper.pushL(), lmapper.getL(1));
    2.18                      break;
    2.19                  case opc_fload_1:
    2.20                      emit(out, "var @1 = @2;", smapper.pushF(), lmapper.getF(1));
    2.21 @@ -334,7 +334,7 @@
    2.22                      emit(out, "var @1 = @2;", smapper.pushI(), lmapper.getI(2));
    2.23                      break;
    2.24                  case opc_lload_2:
    2.25 -                    emit(out, "var @1 = @2;", smapper.pushL(), lmapper.getL(2));
    2.26 +                    emit(out, "var @1 = MakeLong(@2);", smapper.pushL(), lmapper.getL(2));
    2.27                      break;
    2.28                  case opc_fload_2:
    2.29                      emit(out, "var @1 = @2;", smapper.pushF(), lmapper.getF(2));
    2.30 @@ -349,7 +349,7 @@
    2.31                      emit(out, "var @1 = @2;", smapper.pushI(), lmapper.getI(3));
    2.32                      break;
    2.33                  case opc_lload_3:
    2.34 -                    emit(out, "var @1 = @2;", smapper.pushL(), lmapper.getL(3));
    2.35 +                    emit(out, "var @1 = MakeLong(@2);", smapper.pushL(), lmapper.getL(3));
    2.36                      break;
    2.37                  case opc_fload_3:
    2.38                      emit(out, "var @1 = @2;", smapper.pushF(), lmapper.getF(3));
    2.39 @@ -481,7 +481,7 @@
    2.40                      emit(out, "@1 = @1.add32(@2);", smapper.getI(1), smapper.popI());
    2.41                      break;
    2.42                  case opc_ladd:
    2.43 -                    emit(out, "@1 += @2;", smapper.getL(1), smapper.popL());
    2.44 +                    emit(out, "@1 = @1.add64(@2);", smapper.getL(1), smapper.popL());
    2.45                      break;
    2.46                  case opc_fadd:
    2.47                      emit(out, "@1 += @2;", smapper.getF(1), smapper.popF());
    2.48 @@ -518,7 +518,7 @@
    2.49                           smapper.getI(1), smapper.popI());
    2.50                      break;
    2.51                  case opc_ldiv:
    2.52 -                    emit(out, "@1 = Math.floor(@1 / @2);",
    2.53 +                    emit(out, "@1 = @1.div64(@2);",
    2.54                           smapper.getL(1), smapper.popL());
    2.55                      break;
    2.56                  case opc_fdiv:
    2.57 @@ -573,7 +573,7 @@
    2.58                      emit(out, "@1 <<= @2;", smapper.getI(1), smapper.popI());
    2.59                      break;
    2.60                  case opc_lshl:
    2.61 -                    emit(out, "@1 <<= @2;", smapper.getL(1), smapper.popI());
    2.62 +                    emit(out, "@1 = @1.shl64(@2);", smapper.getL(1), smapper.popI());
    2.63                      break;
    2.64                  case opc_ishr:
    2.65                      emit(out, "@1 >>= @2;", smapper.getI(1), smapper.popI());
    2.66 @@ -618,7 +618,7 @@
    2.67                      emit(out, "return @1;", smapper.popA());
    2.68                      break;
    2.69                  case opc_i2l:
    2.70 -                    emit(out, "var @2 = @1;", smapper.popI(), smapper.pushL());
    2.71 +                    emit(out, "var @2 = new Long(@1, 0);", smapper.popI(), smapper.pushL());
    2.72                      break;
    2.73                  case opc_i2f:
    2.74                      emit(out, "var @2 = @1;", smapper.popI(), smapper.pushF());
    2.75 @@ -627,14 +627,14 @@
    2.76                      emit(out, "var @2 = @1;", smapper.popI(), smapper.pushD());
    2.77                      break;
    2.78                  case opc_l2i:
    2.79 -                    emit(out, "var @2 = @1;", smapper.popL(), smapper.pushI());
    2.80 +                    emit(out, "var @2 = @1.toInt32();", smapper.popL(), smapper.pushI());
    2.81                      break;
    2.82                      // max int check?
    2.83                  case opc_l2f:
    2.84 -                    emit(out, "var @2 = @1;", smapper.popL(), smapper.pushF());
    2.85 +                    emit(out, "var @2 = @1.toFP();", smapper.popL(), smapper.pushF());
    2.86                      break;
    2.87                  case opc_l2d:
    2.88 -                    emit(out, "var @2 = @1;", smapper.popL(), smapper.pushD());
    2.89 +                    emit(out, "var @2 = @1.toFP();", smapper.popL(), smapper.pushD());
    2.90                      break;
    2.91                  case opc_f2d:
    2.92                      emit(out, "var @2 = @1;", smapper.popF(), smapper.pushD());
    2.93 @@ -647,7 +647,7 @@
    2.94                           smapper.popF(), smapper.pushI());
    2.95                      break;
    2.96                  case opc_f2l:
    2.97 -                    emit(out, "var @2 = Math.floor(@1);",
    2.98 +                    emit(out, "var @2 = LongFromNumber(Math.floor(@1));",
    2.99                           smapper.popF(), smapper.pushL());
   2.100                      break;
   2.101                  case opc_d2i:
   2.102 @@ -655,7 +655,7 @@
   2.103                           smapper.popD(), smapper.pushI());
   2.104                      break;
   2.105                  case opc_d2l:
   2.106 -                    emit(out, "var @2 = Math.floor(@1);",
   2.107 +                    emit(out, "var @2 = LongFromNumber(Math.floor(@1));",
   2.108                           smapper.popD(), smapper.pushL());
   2.109                      break;
   2.110                  case opc_i2b:
   2.111 @@ -680,7 +680,7 @@
   2.112                      emit(out, "var @1 = 0;", smapper.pushD());
   2.113                      break;
   2.114                  case opc_lconst_0:
   2.115 -                    emit(out, "var @1 = 0;", smapper.pushL());
   2.116 +                    emit(out, "var @1 = new Long(0,0);", smapper.pushL());
   2.117                      break;
   2.118                  case opc_fconst_0:
   2.119                      emit(out, "var @1 = 0;", smapper.pushF());
   2.120 @@ -689,7 +689,7 @@
   2.121                      emit(out, "var @1 = 1;", smapper.pushI());
   2.122                      break;
   2.123                  case opc_lconst_1:
   2.124 -                    emit(out, "var @1 = 1;", smapper.pushL());
   2.125 +                    emit(out, "var @1 = new Long(1,0);", smapper.pushL());
   2.126                      break;
   2.127                  case opc_fconst_1:
   2.128                      emit(out, "var @1 = 1;", smapper.pushF());
   2.129 @@ -725,11 +725,19 @@
   2.130                      i += 2;
   2.131                      String v = encodeConstant(indx);
   2.132                      int type = VarType.fromConstantType(jc.getTag(indx));
   2.133 -                    emit(out, "var @1 = @2;", smapper.pushT(type), v);
   2.134 +                    if (type == VarType.LONG) {
   2.135 +                        final Long lv = new Long(v);
   2.136 +                        final int low = (int)(lv.longValue() & 0xFFFFFFFF);
   2.137 +                        final int hi = (int)(lv.longValue() >> 32);
   2.138 +                        emit(out, "var @1 = new Long(0x@2, 0x@3);", smapper.pushL(), 
   2.139 +                                Integer.toHexString(low), Integer.toHexString(hi));
   2.140 +                    } else {
   2.141 +                        emit(out, "var @1 = @2;", smapper.pushT(type), v);
   2.142 +                    }
   2.143                      break;
   2.144                  }
   2.145                  case opc_lcmp:
   2.146 -                    emit(out, "var @3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);",
   2.147 +                    emit(out, "var @3 = @2.compare64(@1);",
   2.148                           smapper.popL(), smapper.popL(), smapper.pushI());
   2.149                      break;
   2.150                  case opc_fcmpl:
     3.1 --- a/vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java	Mon Jan 21 15:57:01 2013 +0100
     3.2 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java	Fri Jan 25 11:00:52 2013 +0100
     3.3 @@ -157,22 +157,10 @@
     3.4      }
     3.5  
     3.6      private static void assertExec(
     3.7 -        String msg, Class<?> clazz, String method, Object expRes, Object... args) throws Exception {
     3.8 -
     3.9 -        Object ret = null;
    3.10 -        try {
    3.11 -            ret = code.invokeFunction("bck2brwsr");
    3.12 -            ret = code.invokeMethod(ret, "loadClass", clazz.getName());
    3.13 -            ret = code.invokeMethod(ret, method, args);
    3.14 -        } catch (ScriptException ex) {
    3.15 -            fail("Execution failed in\n" + StaticMethodTest.dumpJS(codeSeq), ex);
    3.16 -        } catch (NoSuchMethodException ex) {
    3.17 -            fail("Cannot find method in\n" + StaticMethodTest.dumpJS(codeSeq), ex);
    3.18 -        }
    3.19 -        if (ret == null && expRes == null) {
    3.20 -            return;
    3.21 -        }
    3.22 -        if (expRes.equals(ret)) {
    3.23 +        String msg, Class<?> clazz, String method, Object expRes, Object... args) throws Exception
    3.24 +    {
    3.25 +        Object ret = TestUtils.execCode(code, codeSeq, msg, clazz, method, expRes, args);
    3.26 +        if (ret == null) {
    3.27              return;
    3.28          }
    3.29          if (expRes instanceof Double && ret instanceof Double) {
     4.1 --- a/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java	Mon Jan 21 15:57:01 2013 +0100
     4.2 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java	Fri Jan 25 11:00:52 2013 +0100
     4.3 @@ -270,17 +270,8 @@
     4.4          String msg, Class clazz, String method, 
     4.5          Object expRes, Object... args
     4.6      ) throws Exception {
     4.7 -        Object ret = null;
     4.8 -        try {
     4.9 -            ret = toRun.invokeFunction("bck2brwsr");
    4.10 -            ret = toRun.invokeMethod(ret, "loadClass", clazz.getName());
    4.11 -            ret = toRun.invokeMethod(ret, method, args);
    4.12 -        } catch (ScriptException ex) {
    4.13 -            fail("Execution failed in\n" + dumpJS(theCode), ex);
    4.14 -        } catch (NoSuchMethodException ex) {
    4.15 -            fail("Cannot find method in\n" + dumpJS(theCode), ex);
    4.16 -        }
    4.17 -        if (ret == null && expRes == null) {
    4.18 +        Object ret = TestUtils.execCode(toRun, theCode, msg, clazz, method, expRes, args);
    4.19 +        if (ret == null) {
    4.20              return;
    4.21          }
    4.22          if (expRes != null && expRes.equals(ret)) {
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/TestUtils.java	Fri Jan 25 11:00:52 2013 +0100
     5.3 @@ -0,0 +1,59 @@
     5.4 +/**
     5.5 + * Back 2 Browser Bytecode Translator
     5.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     5.7 + *
     5.8 + * This program is free software: you can redistribute it and/or modify
     5.9 + * it under the terms of the GNU General Public License as published by
    5.10 + * the Free Software Foundation, version 2 of the License.
    5.11 + *
    5.12 + * This program is distributed in the hope that it will be useful,
    5.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    5.15 + * GNU General Public License for more details.
    5.16 + *
    5.17 + * You should have received a copy of the GNU General Public License
    5.18 + * along with this program. Look for COPYING file in the top folder.
    5.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    5.20 + */
    5.21 +package org.apidesign.vm4brwsr;
    5.22 +
    5.23 +import javax.script.Invocable;
    5.24 +import javax.script.ScriptException;
    5.25 +import static org.testng.Assert.*;
    5.26 +
    5.27 +class TestUtils {
    5.28 +
    5.29 +    static Object execCode(Invocable code, CharSequence codeSeq,
    5.30 +        String msg, Class<?> clazz, String method, Object expRes, Object... args) 
    5.31 +            throws Exception
    5.32 +    {
    5.33 +        Object ret = null;
    5.34 +        try {
    5.35 +            ret = code.invokeFunction("bck2brwsr");
    5.36 +            ret = code.invokeMethod(ret, "loadClass", clazz.getName());
    5.37 +            ret = code.invokeMethod(ret, method, args);
    5.38 +        } catch (ScriptException ex) {
    5.39 +            fail("Execution failed in " + StaticMethodTest.dumpJS(codeSeq), ex);
    5.40 +        } catch (NoSuchMethodException ex) {
    5.41 +            fail("Cannot find method in " + StaticMethodTest.dumpJS(codeSeq), ex);
    5.42 +        }
    5.43 +        if (ret == null && expRes == null) {
    5.44 +            return null;
    5.45 +        }
    5.46 +        if (expRes.equals(ret)) {
    5.47 +            return null;
    5.48 +        }
    5.49 +        if (expRes instanceof Number) {
    5.50 +            // in case of Long it is necessary convert it to number
    5.51 +            // since the Long is represented by two numbers in JavaScript
    5.52 +            try {
    5.53 +                ret = code.invokeFunction("Number", ret);
    5.54 +            } catch (ScriptException ex) {
    5.55 +                fail("Conversion to number failed in " + StaticMethodTest.dumpJS(codeSeq), ex);
    5.56 +            } catch (NoSuchMethodException ex) {
    5.57 +                fail("Cannot find global Number(x) function in " + StaticMethodTest.dumpJS(codeSeq), ex);
    5.58 +            }
    5.59 +        }
    5.60 +        return ret;
    5.61 +    }
    5.62 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/LongArithmeticTest.java	Fri Jan 25 11:00:52 2013 +0100
     6.3 @@ -0,0 +1,104 @@
     6.4 +/**
     6.5 + * Back 2 Browser Bytecode Translator
     6.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     6.7 + *
     6.8 + * This program is free software: you can redistribute it and/or modify
     6.9 + * it under the terms of the GNU General Public License as published by
    6.10 + * the Free Software Foundation, version 2 of the License.
    6.11 + *
    6.12 + * This program is distributed in the hope that it will be useful,
    6.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    6.15 + * GNU General Public License for more details.
    6.16 + *
    6.17 + * You should have received a copy of the GNU General Public License
    6.18 + * along with this program. Look for COPYING file in the top folder.
    6.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    6.20 + */
    6.21 +package org.apidesign.bck2brwsr.tck;
    6.22 +
    6.23 +import org.apidesign.bck2brwsr.vmtest.Compare;
    6.24 +import org.apidesign.bck2brwsr.vmtest.VMTest;
    6.25 +import org.testng.annotations.Factory;
    6.26 +
    6.27 +/**
    6.28 + *
    6.29 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    6.30 + */
    6.31 +public class LongArithmeticTest {
    6.32 +    
    6.33 +    private static long add(long x, long y) {
    6.34 +        return (x + y);
    6.35 +    }
    6.36 +    
    6.37 +    private static long sub(long x, long y) {
    6.38 +        return (x - y);
    6.39 +    }
    6.40 +    
    6.41 +    private static long mul(long x, long y) {
    6.42 +        return (x * y);
    6.43 +    }
    6.44 +    
    6.45 +    private static long div(long x, long y) {
    6.46 +        return (x / y);
    6.47 +    }
    6.48 +    
    6.49 +    private static long mod(long x, long y) {
    6.50 +        return (x % y);
    6.51 +    }
    6.52 +    
    6.53 +    @Compare public long conversion() {
    6.54 +        return Long.MAX_VALUE;
    6.55 +    }
    6.56 +    
    6.57 +    /*
    6.58 +    @Compare public long addOverflow() {
    6.59 +        return add(Long.MAX_VALUE, 1l);
    6.60 +    }
    6.61 +    
    6.62 +    @Compare public long subUnderflow() {
    6.63 +        return sub(Long.MIN_VALUE, 1l);
    6.64 +    }
    6.65 +    
    6.66 +    @Compare public long addMaxLongAndMaxLong() {
    6.67 +        return add(Long.MAX_VALUE, Long.MAX_VALUE);
    6.68 +    }
    6.69 +    
    6.70 +    @Compare public long subMinLongAndMinLong() {
    6.71 +        return sub(Long.MIN_VALUE, Long.MIN_VALUE);
    6.72 +    }
    6.73 +    
    6.74 +    @Compare public long multiplyMaxLong() {
    6.75 +        return mul(Long.MAX_VALUE, 2l);
    6.76 +    }
    6.77 +    
    6.78 +    @Compare public long multiplyMaxLongAndMaxLong() {
    6.79 +        return mul(Long.MAX_VALUE, Long.MAX_VALUE);
    6.80 +    }
    6.81 +    
    6.82 +    @Compare public long multiplyMinLong() {
    6.83 +        return mul(Long.MIN_VALUE, 2l);
    6.84 +    }
    6.85 +    
    6.86 +    @Compare public long multiplyMinLongAndMinLong() {
    6.87 +        return mul(Long.MIN_VALUE, Long.MIN_VALUE);
    6.88 +    }
    6.89 +    
    6.90 +    @Compare public long multiplyPrecision() {
    6.91 +        return mul(17638l, 1103l);
    6.92 +    }
    6.93 +    
    6.94 +    @Compare public long division() {
    6.95 +        return div(1l, 2l);
    6.96 +    }
    6.97 +    
    6.98 +    @Compare public long divisionReminder() {
    6.99 +        return mod(1l, 2l);
   6.100 +    }
   6.101 +    */
   6.102 +    
   6.103 +    @Factory
   6.104 +    public static Object[] create() {
   6.105 +        return VMTest.create(LongArithmeticTest.class);
   6.106 +    }
   6.107 +}