rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_Number.js
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 25 Jan 2016 06:40:40 +0100
changeset 1857 f1344425bcb1
parent 1800 65cab8539582
child 1858 4dea14fafc31
permissions -rw-r--r--
Some operations are faster when included in the generated code rather than dispatched to Number.prototype
     1 // empty line needed here
     2 
     3 (function(numberPrototype) {
     4     function add32(x, y) {
     5         return (x + y) | 0;
     6     };
     7     numberPrototype.mul32 = function(x) {
     8         return (((this * (x >> 16)) << 16) + this * (x & 0xFFFF)) | 0;
     9     };
    10     numberPrototype.div32 = function(x) {
    11         if (x === 0) {
    12             __handleDivByZero();
    13         }
    14 
    15         return (this / x) | 0;
    16     }
    17 
    18     numberPrototype.mod32 = function(x) {
    19         if (x === 0) {
    20             __handleDivByZero();
    21         }
    22 
    23         return (this % x);
    24     }
    25 
    26     var __m32 = 0xFFFFFFFF;
    27 
    28     numberPrototype.next32 = function(low) {
    29         if (this === 0) {
    30             return low;
    31         }
    32         var l = new Number(low);
    33         l.hi = this | 0;
    34         return l;
    35     };
    36 
    37     numberPrototype.high32 = function() {
    38         return this.hi ? this.hi : (Math.floor(this / (__m32 + 1))) | 0;
    39     };
    40     numberPrototype.toFP = function() {
    41         return this.hi ? this.hi * (__m32 + 1) + this : this;
    42     };
    43     numberPrototype.toLong = function() {
    44         var hi = (this / (__m32 + 1)) | 0;
    45         var low = (this % (__m32 + 1)) | 0;
    46         if (low < 0) {
    47             low += __m32 + 1;
    48         }
    49 
    50         if (this < 0) {
    51             hi -= 1;
    52         }
    53 
    54         return hi.next32(low);
    55     };
    56 
    57     numberPrototype.toExactString = function() {
    58         if (this.hi) {
    59             // check for Long.MIN_VALUE
    60             if ((this.hi == (0x80000000 | 0)) && (this == 0)) {
    61                 return '-9223372036854775808';
    62             }
    63             var res = 0;
    64             var a = [6, 9, 2, 7, 6, 9, 4, 9, 2, 4];
    65             var s = '';
    66             var digit;
    67             var neg = this.hi < 0;
    68             if (neg) {
    69                 var x = this.neg64();
    70                 var hi = x.hi;
    71                 var low = x;
    72             } else {
    73                 var hi = this.hi;
    74                 var low = this;
    75             }
    76             for (var i = 0; i < a.length; i++) {
    77                 res += hi * a[i];
    78                 var low_digit = low % 10;
    79                 digit = (res % 10) + low_digit;
    80 
    81                 low = Math.floor(low / 10);
    82                 res = Math.floor(res / 10);
    83 
    84                 if (digit >= 10) {
    85                     digit -= 10;
    86                     res++;
    87                 }
    88                 s = String(digit).concat(s);
    89             }
    90             s = String(res).concat(s).replace(/^0+/, '');
    91             return (neg ? '-' : '').concat(s);
    92         }
    93         return String(this);
    94     };
    95 
    96     numberPrototype.add64 = function(x) {
    97         var low = this + x;
    98         carry = 0;
    99         if (low > __m32) {
   100             carry = 1;
   101             low -= (__m32 + 1);
   102         }
   103         var hi = (this.high32() + x.high32() + carry) | 0;
   104         return hi.next32(low);
   105     };
   106 
   107     numberPrototype.sub64 = function(x) {
   108         var low = this - x;
   109         carry = 0;
   110         if (low < 0) {
   111             carry = 1;
   112             low += (__m32 + 1);
   113         }
   114         var hi = (this.high32() - x.high32() - carry) | 0;
   115         return hi.next32(low);
   116     };
   117 
   118     numberPrototype.mul64 = function(x) {
   119         var low = this.mul32(x);
   120         low += (low < 0) ? (__m32 + 1) : 0;
   121         // first count upper 32 bits of (this.low * x.low)
   122         var hi_hi = 0;
   123         var hi_low = 0;
   124         var m = 1;
   125         for (var i = 0; i < 32; i++) {
   126             if (x & m) {
   127                 hi_hi += this >>> 16;
   128                 hi_low += this & 0xFFFF
   129             }
   130             hi_low >>= 1;
   131             hi_low += (hi_hi & 1) ? 0x8000 : 0;
   132             hi_hi >>= 1;
   133             m <<= 1;
   134         }
   135         var hi = (hi_hi << 16) + hi_low;
   136 
   137         var m1 = this.high32().mul32(x);
   138         var m2 = this.mul32(x.high32());
   139         hi = add32(add32(hi, m1), m2);
   140 
   141         return hi.next32(low);
   142     };
   143 
   144     numberPrototype.and64 = function(x) {
   145         var low = this & x;
   146         low += (low < 0) ? (__m32 + 1) : 0;
   147         if (this.hi && x.hi) {
   148             var hi = this.hi & x.hi;
   149             return hi.next32(low);
   150         }
   151         ;
   152         return low;
   153     };
   154 
   155     numberPrototype.or64 = function(x) {
   156         var low = this | x;
   157         low += (low < 0) ? (__m32 + 1) : 0;
   158         if (this.hi || x.hi) {
   159             var hi = this.hi | x.hi;
   160             return hi.next32(low);
   161         }
   162         ;
   163         return low;
   164     };
   165 
   166     numberPrototype.xor64 = function(x) {
   167         var low = this ^ x;
   168         low += (low < 0) ? (__m32 + 1) : 0;
   169         if (this.hi || x.hi) {
   170             var hi = this.hi ^ x.hi;
   171             return hi.next32(low);
   172         }
   173         ;
   174         return low;
   175     };
   176 
   177     numberPrototype.shl64 = function(x) {
   178         x &= 0x3f;
   179         if (x == 0) return this;
   180         if (x >= 32) {
   181             var hi = this << (x - 32);
   182             return hi.next32(0);
   183         } else {
   184             var hi = this.high32() << x;
   185             var low_reminder = this >> (32 - x);
   186             hi |= low_reminder;
   187             var low = this << x;
   188             low += (low < 0) ? (__m32 + 1) : 0;
   189             return hi.next32(low);
   190         }
   191     };
   192 
   193     numberPrototype.shr64 = function(x) {
   194         x &= 0x3f;
   195         if (x == 0) return this;
   196         if (x >= 32) {
   197             var low = this.high32() >> (x - 32);
   198             low += (low < 0) ? (__m32 + 1) : 0;
   199             return low;
   200         } else {
   201             var low = this >>> x;
   202             var hi_reminder = this.high32() << (32 - x);
   203             low |= hi_reminder;
   204             low += (low < 0) ? (__m32 + 1) : 0;
   205             var hi = this.high32() >> x;
   206             return hi.next32(low);
   207         }
   208     };
   209 
   210     numberPrototype.ushr64 = function(x) {
   211         x &= 0x3f;
   212         if (x == 0) return this;
   213         if (x >= 32) {
   214             var low = this.high32() >>> (x - 32);
   215             low += (low < 0) ? (__m32 + 1) : 0;
   216             return low;
   217         } else {
   218             var low = this >>> x;
   219             var hi_reminder = this.high32() << (32 - x);
   220             low |= hi_reminder;
   221             low += (low < 0) ? (__m32 + 1) : 0;
   222             var hi = this.high32() >>> x;
   223             return hi.next32(low);
   224         }
   225     };
   226 
   227     numberPrototype.compare64 = function(x) {
   228         if (this.high32() === x.high32()) {
   229             return (this < x) ? -1 : ((this > x) ? 1 : 0);
   230         }
   231         return (this.high32() < x.high32()) ? -1 : 1;
   232     };
   233 
   234     numberPrototype.neg64 = function() {
   235         var hi = this.high32();
   236         var low = this;
   237         if ((hi === 0) && (low < 0)) {
   238             return -low;
   239         }
   240         hi = ~hi;
   241         low = ~low;
   242         low += (low < 0) ? (__m32 + 1) : 0;
   243         var ret = hi.next32(low);
   244         return ret.add64(1);
   245     };
   246     
   247     function __handleDivByZero() {
   248         var exception = new vm.java_lang_ArithmeticException;
   249         vm.java_lang_ArithmeticException(false).constructor
   250           .cons__VLjava_lang_String_2.call(exception, "/ by zero");
   251 
   252         throw exception;
   253     }
   254 
   255     function __Int64(hi32, lo32) {
   256         this.hi32 = hi32 | 0;
   257         this.lo32 = lo32 | 0;
   258 
   259         this.get32 = function(bitIndex) {
   260             var v0;
   261             var v1;
   262             bitIndex += 32;
   263             var selector = bitIndex >>> 5;
   264             switch (selector) {
   265                 case 0:
   266                     v0 = 0;
   267                     v1 = this.lo32;
   268                     break;
   269                 case 1:
   270                     v0 = this.lo32;
   271                     v1 = this.hi32;
   272                     break;
   273                 case 2:
   274                     v0 = this.hi32;
   275                     v1 = 0;
   276                     break
   277                 default:
   278                     return 0;
   279             }
   280 
   281             var shift = bitIndex & 31;
   282             if (shift === 0) {
   283                 return v0;
   284             }
   285 
   286             return (v1 << (32 - shift)) | (v0 >>> shift);
   287         }
   288 
   289         this.get16 = function(bitIndex) {
   290             return this.get32(bitIndex) & 0xffff;
   291         }
   292 
   293         this.set16 = function(bitIndex, value) {
   294             bitIndex += 32;
   295             var shift = bitIndex & 15;
   296             var svalue = (value & 0xffff) << shift; 
   297             var smask = 0xffff << shift;
   298             var selector = bitIndex >>> 4;
   299             switch (selector) {
   300                 case 0:
   301                     break;
   302                 case 1:
   303                     this.lo32 = (this.lo32 & ~(smask >>> 16))
   304                                     | (svalue >>> 16);
   305                     break;
   306                 case 2:
   307                     this.lo32 = (this.lo32 & ~smask) | svalue;
   308                     break;
   309                 case 3:
   310                     this.lo32 = (this.lo32 & ~(smask << 16))
   311                                     | (svalue << 16);
   312                     this.hi32 = (this.hi32 & ~(smask >>> 16))
   313                                     | (svalue >>> 16);
   314                     break;
   315                 case 4:
   316                     this.hi32 = (this.hi32 & ~smask) | svalue;
   317                     break;
   318                 case 5:
   319                     this.hi32 = (this.hi32 & ~(smask << 16))
   320                                     | (svalue << 16);
   321                     break;
   322             }
   323         }
   324 
   325         this.getDigit = function(index, shift) {
   326             return this.get16((index << 4) - shift);
   327         }
   328 
   329         this.getTwoDigits = function(index, shift) {
   330             return this.get32(((index - 1) << 4) - shift);
   331         }
   332 
   333         this.setDigit = function(index, shift, value) {
   334             this.set16((index << 4) - shift, value);
   335         }
   336 
   337         this.countSignificantDigits = function() {
   338             var sd;
   339             var remaining;
   340 
   341             if (this.hi32 === 0) {
   342                 if (this.lo32 === 0) {
   343                     return 0;
   344                 }
   345 
   346                 sd = 2;
   347                 remaining = this.lo32;
   348             } else {
   349                 sd = 4;
   350                 remaining = this.hi32;
   351             }
   352 
   353             if (remaining < 0) {
   354                 return sd;
   355             }
   356 
   357             return (remaining < 65536) ? sd - 1 : sd;
   358         }
   359         
   360         this.toNumber = function() {
   361             var lo32 = this.lo32;
   362             if (lo32 < 0) {
   363                 lo32 += 0x100000000;
   364             }
   365 
   366             return this.hi32.next32(lo32);
   367         }
   368     }
   369 
   370     function __countLeadingZeroes16(number) {
   371         var nlz = 0;
   372 
   373         if (number < 256) {
   374             nlz += 8;
   375             number <<= 8;
   376         }
   377 
   378         if (number < 4096) {
   379             nlz += 4;
   380             number <<= 4;
   381         }
   382 
   383         if (number < 16384) {
   384             nlz += 2;
   385             number <<= 2;
   386         }
   387 
   388         return (number < 32768) ? nlz + 1 : nlz;
   389     }
   390     
   391     // q = u / v; r = u - q * v;
   392     // v != 0
   393     function __div64(q, r, u, v) {
   394         var m = u.countSignificantDigits();
   395         var n = v.countSignificantDigits();
   396 
   397         q.hi32 = q.lo32 = 0;
   398 
   399         if (n === 1) {
   400             // v has single digit
   401             var vd = v.getDigit(0, 0);
   402             var carry = 0;
   403             for (var i = m - 1; i >= 0; --i) {
   404                 var ui = (carry << 16) | u.getDigit(i, 0);
   405                 if (ui < 0) {
   406                     ui += 0x100000000;
   407                 }
   408                 var qi = (ui / vd) | 0;
   409                 q.setDigit(i, 0, qi);
   410                 carry = ui - qi * vd;
   411             }
   412 
   413             r.hi32 = 0;
   414             r.lo32 = carry;
   415             return;
   416         }
   417 
   418         r.hi32 = u.hi32;  
   419         r.lo32 = u.lo32;
   420 
   421         if (m < n) {
   422             return;
   423         }
   424 
   425         // Normalize
   426         var nrm = __countLeadingZeroes16(v.getDigit(n - 1, 0));
   427 
   428         var vd1 = v.getDigit(n - 1, nrm);                
   429         var vd0 = v.getDigit(n - 2, nrm);
   430         for (var j = m - n; j >= 0; --j) {
   431             // Calculate qj estimate
   432             var ud21 = r.getTwoDigits(j + n, nrm);
   433             var ud2 = ud21 >>> 16;
   434             if (ud21 < 0) {
   435                 ud21 += 0x100000000;
   436             }
   437 
   438             var qest = (ud2 === vd1) ? 0xFFFF : ((ud21 / vd1) | 0);
   439             var rest = ud21 - qest * vd1;
   440 
   441             // 0 <= (qest - qj) <= 2
   442 
   443             // Refine qj estimate
   444             var ud0 = r.getDigit(j + n - 2, nrm);
   445             while ((qest * vd0) > ((rest * 0x10000) + ud0)) {
   446                 --qest;
   447                 rest += vd1;
   448             }
   449 
   450             // 0 <= (qest - qj) <= 1
   451             
   452             // Multiply and subtract
   453             var carry = 0;
   454             for (var i = 0; i < n; ++i) {
   455                 var vi = qest * v.getDigit(i, nrm);
   456                 var ui = r.getDigit(i + j, nrm) - carry - (vi & 0xffff);
   457                 r.setDigit(i + j, nrm, ui);
   458                 carry = (vi >>> 16) - (ui >> 16);
   459             }
   460             var uj = ud2 - carry;
   461 
   462             if (uj < 0) {
   463                 // qest - qj = 1
   464 
   465                 // Add back
   466                 --qest;
   467                 var carry = 0;
   468                 for (var i = 0; i < n; ++i) {
   469                     var ui = r.getDigit(i + j, nrm) + v.getDigit(i, nrm)
   470                                  + carry;
   471                     r.setDigit(i + j, nrm, ui);
   472                     carry = ui >> 16;
   473                 }
   474                 uj += carry;
   475             }
   476 
   477             q.setDigit(j, 0, qest);
   478             r.setDigit(j + n, nrm, uj);
   479         }
   480     }
   481 
   482     numberPrototype.div64 = function(x) {
   483         var negateResult = false;
   484         var u, v;
   485 
   486         if ((this.high32() & 0x80000000) != 0) {
   487             u = this.neg64();
   488             negateResult = !negateResult;
   489         } else {
   490             u = this;        
   491         }
   492 
   493         if ((x.high32() & 0x80000000) != 0) {
   494             v = x.neg64();
   495             negateResult = !negateResult;
   496         } else {
   497             v = x;
   498         }
   499 
   500         if ((v == 0) && (v.high32() === 0)) {
   501             __handleDivByZero();
   502         }
   503 
   504         if (u.high32() === 0) {
   505             if (v.high32() === 0) {
   506                 var result = (u / v) | 0;
   507                 return negateResult ? result.neg64() : result; 
   508             }
   509 
   510             return 0;
   511         }
   512 
   513         var u64 = new __Int64(u.high32(), u);
   514         var v64 = new __Int64(v.high32(), v);
   515         var q64 = new __Int64(0, 0);
   516         var r64 = new __Int64(0, 0);
   517 
   518         __div64(q64, r64, u64, v64);
   519 
   520         var result = q64.toNumber();
   521         return negateResult ? result.neg64() : result; 
   522     }
   523 
   524     numberPrototype.mod64 = function(x) {
   525         var negateResult = false;
   526         var u, v;
   527         
   528         if ((this.high32() & 0x80000000) != 0) {
   529             u = this.neg64();
   530             negateResult = !negateResult;
   531         } else {
   532             u = this;        
   533         }
   534 
   535         if ((x.high32() & 0x80000000) != 0) {
   536             v = x.neg64();
   537         } else {
   538             v = x;
   539         }
   540 
   541         if ((v == 0) && (v.high32() === 0)) {
   542             __handleDivByZero();
   543         }
   544 
   545         if (u.high32() === 0) {
   546             var result = (v.high32() === 0) ? (u % v) : u;
   547             return negateResult ? result.neg64() : result; 
   548         }
   549 
   550         var u64 = new __Int64(u.high32(), u);
   551         var v64 = new __Int64(v.high32(), v);
   552         var q64 = new __Int64(0, 0);
   553         var r64 = new __Int64(0, 0);
   554 
   555         __div64(q64, r64, u64, v64);
   556 
   557         var result = r64.toNumber();
   558         return negateResult ? result.neg64() : result; 
   559     }
   560 })(Number.prototype);
   561 
   562 vm.java_lang_Number(false);