Skip to content

Instantly share code, notes, and snippets.

@jvilk
Created September 5, 2013 18:18
Show Gist options
  • Save jvilk/6453990 to your computer and use it in GitHub Desktop.
Save jvilk/6453990 to your computer and use it in GitHub Desktop.
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
define('src/gLong',["require", "exports"], function(require, exports) {
/**
* @fileoverview Defines a Long class for representing a 64-bit two's-complement
* integer value, which faithfully simulates the behavior of a Java "long". This
* implementation is derived from LongLib in GWT.
*
*/
var gLong = (function () {
/**
* Constructs a 64-bit two's-complement integer, given its low and high 32-bit
* values as *signed* integers. See the from* functions below for more
* convenient ways of constructing Longs.
*
* The internal representation of a long is the two given signed, 32-bit values.
* We use 32-bit pieces because these are the size of integers on which
* Javascript performs bit-operations. For operations like addition and
* multiplication, we split each number into 16-bit pieces, which can easily be
* multiplied within Javascript's floating-point representation without overflow
* or change in sign.
*
* In the algorithms below, we frequently reduce the negative case to the
* positive case by negating the input(s) and then post-processing the result.
* Note that we must ALWAYS check specially whether those values are MIN_VALUE
* (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as
* a positive number, it overflows back into a negative). Not handling this
* case would often result in infinite recursion.
*
* @param {number} low The low (signed) 32 bits of the long.
* @param {number} high The high (signed) 32 bits of the long.
* @constructor
*/
function gLong(low, high) {
this.low_ = low | 0;
this.high_ = high | 0;
}
gLong.fromInt = /**
* Returns a Long representing the given (32-bit) integer value.
* @param {number} value The 32-bit integer in question.
* @return {!gLong} The corresponding Long value.
*/
function (value) {
if (-128 <= value && value < 128) {
var cachedObj = gLong.IntCache_[value];
if (cachedObj) {
return cachedObj;
}
}
var obj = new gLong(value, value < 0 ? -1 : 0);
if (-128 <= value && value < 128) {
gLong.IntCache_[value] = obj;
}
return obj;
};
gLong.fromNumber = /**
* Returns a Long representing the given value, provided that it is a finite
* number. Otherwise, zero is returned.
* @param {number} value The number in question.
* @return {!gLong} The corresponding Long value.
*/
function (value) {
if (isNaN(value) || !isFinite(value)) {
return gLong.ZERO;
} else if (value <= -gLong.TWO_PWR_63_DBL_) {
return gLong.MIN_VALUE;
} else if (value + 1 >= gLong.TWO_PWR_63_DBL_) {
return gLong.MAX_VALUE;
} else if (value < 0) {
return gLong.fromNumber(-value).negate();
} else {
return new gLong((value % gLong.TWO_PWR_32_DBL_) | 0, (value / gLong.TWO_PWR_32_DBL_) | 0);
}
};
gLong.fromBits = /**
* Returns a Long representing the 64-bit integer that comes by concatenating
* the given high and low bits. Each is assumed to use 32 bits.
* @param {number} lowBits The low 32-bits.
* @param {number} highBits The high 32-bits.
* @return {!gLong} The corresponding Long value.
*/
function (lowBits, highBits) {
return new gLong(lowBits, highBits);
};
gLong.fromString = /**
* Returns a Long representation of the given string, written using the given
* radix.
* @param {string} str The textual representation of the Long.
* @param {number=} opt_radix The radix in which the text is written.
* @return {!gLong} The corresponding Long value.
*/
function (str, opt_radix) {
if (str.length == 0) {
throw Error('number format error: empty string');
}
var radix = opt_radix || 10;
if (radix < 2 || 36 < radix) {
throw Error('radix out of range: ' + radix);
}
if (str.charAt(0) == '-') {
return gLong.fromString(str.substring(1), radix).negate();
} else if (str.indexOf('-') >= 0) {
throw Error('number format error: interior "-" character: ' + str);
}
// Do several (8) digits each time through the loop, so as to
// minimize the calls to the very expensive emulated div.
var radixToPower = gLong.fromNumber(Math.pow(radix, 8));
var result = gLong.ZERO;
for (var i = 0; i < str.length; i += 8) {
var size = Math.min(8, str.length - i);
var value = parseInt(str.substring(i, i + size), radix);
if (size < 8) {
var power = gLong.fromNumber(Math.pow(radix, size));
result = result.multiply(power).add(gLong.fromNumber(value));
} else {
result = result.multiply(radixToPower);
result = result.add(gLong.fromNumber(value));
}
}
return result;
};
/** @return {number} The value, assuming it is a 32-bit integer. */
gLong.prototype.toInt = function () {
return this.low_;
};
/** @return {number} The closest floating-point representation to this value. */
gLong.prototype.toNumber = function () {
return this.high_ * gLong.TWO_PWR_32_DBL_ + this.getLowBitsUnsigned();
};
/**
* @param {number=} opt_radix The radix in which the text should be written.
* @return {string} The textual representation of this value.
*/
gLong.prototype.toString = function (opt_radix) {
var radix = opt_radix || 10;
if (radix < 2 || 36 < radix) {
throw Error('radix out of range: ' + radix);
}
if (this.isZero()) {
return '0';
}
if (this.isNegative()) {
if (this.equals(gLong.MIN_VALUE)) {
// We need to change the Long value before it can be negated, so we remove
// the bottom-most digit in this base and then recurse to do the rest.
var radixLong = gLong.fromNumber(radix);
var div = this.div(radixLong);
var rem = div.multiply(radixLong).subtract(this);
return div.toString(radix) + rem.toInt().toString(radix);
} else {
return '-' + this.negate().toString(radix);
}
}
// Do several (6) digits each time through the loop, so as to
// minimize the calls to the very expensive emulated div.
var radixToPower = gLong.fromNumber(Math.pow(radix, 6));
var rem = this;
var result = '';
while (true) {
var remDiv = rem.div(radixToPower);
var intval = rem.subtract(remDiv.multiply(radixToPower)).toInt();
var digits = intval.toString(radix);
rem = remDiv;
if (rem.isZero()) {
return digits + result;
} else {
while (digits.length < 6) {
digits = '0' + digits;
}
result = '' + digits + result;
}
}
};
/** @return {number} The high 32-bits as a signed value. */
gLong.prototype.getHighBits = function () {
return this.high_;
};
/** @return {number} The low 32-bits as a signed value. */
gLong.prototype.getLowBits = function () {
return this.low_;
};
/** @return {number} The low 32-bits as an unsigned value. */
gLong.prototype.getLowBitsUnsigned = function () {
return (this.low_ >= 0) ? this.low_ : gLong.TWO_PWR_32_DBL_ + this.low_;
};
/**
* @return {number} Returns the number of bits needed to represent the absolute
* value of this Long.
*/
gLong.prototype.getNumBitsAbs = function () {
if (this.isNegative()) {
if (this.equals(gLong.MIN_VALUE)) {
return 64;
} else {
return this.negate().getNumBitsAbs();
}
} else {
var val = this.high_ != 0 ? this.high_ : this.low_;
for (var bit = 31; bit > 0; bit--) {
if ((val & (1 << bit)) != 0) {
break;
}
}
return this.high_ != 0 ? bit + 33 : bit + 1;
}
};
/** @return {boolean} Whether this value is zero. */
gLong.prototype.isZero = function () {
return this.high_ == 0 && this.low_ == 0;
};
/** @return {boolean} Whether this value is negative. */
gLong.prototype.isNegative = function () {
return this.high_ < 0;
};
/** @return {boolean} Whether this value is odd. */
gLong.prototype.isOdd = function () {
return (this.low_ & 1) == 1;
};
/**
* @param {gLong} other Long to compare against.
* @return {boolean} Whether this Long equals the other.
*/
gLong.prototype.equals = function (other) {
return (this.high_ == other.high_) && (this.low_ == other.low_);
};
/**
* @param {gLong} other Long to compare against.
* @return {boolean} Whether this Long does not equal the other.
*/
gLong.prototype.notEquals = function (other) {
return (this.high_ != other.high_) || (this.low_ != other.low_);
};
/**
* @param {gLong} other Long to compare against.
* @return {boolean} Whether this Long is less than the other.
*/
gLong.prototype.lessThan = function (other) {
return this.compare(other) < 0;
};
/**
* @param {gLong} other Long to compare against.
* @return {boolean} Whether this Long is less than or equal to the other.
*/
gLong.prototype.lessThanOrEqual = function (other) {
return this.compare(other) <= 0;
};
/**
* @param {gLong} other Long to compare against.
* @return {boolean} Whether this Long is greater than the other.
*/
gLong.prototype.greaterThan = function (other) {
return this.compare(other) > 0;
};
/**
* @param {gLong} other Long to compare against.
* @return {boolean} Whether this Long is greater than or equal to the other.
*/
gLong.prototype.greaterThanOrEqual = function (other) {
return this.compare(other) >= 0;
};
/**
* Compares this Long with the given one.
* @param {gLong} other Long to compare against.
* @return {number} 0 if they are the same, 1 if the this is greater, and -1
* if the given one is greater.
*/
gLong.prototype.compare = function (other) {
if (this.equals(other)) {
return 0;
}
var thisNeg = this.isNegative();
var otherNeg = other.isNegative();
if (thisNeg && !otherNeg) {
return -1;
}
if (!thisNeg && otherNeg) {
return 1;
}
if (this.subtract(other).isNegative()) {
return -1;
} else {
return 1;
}
};
/** @return {!gLong} The negation of this value. */
gLong.prototype.negate = function () {
if (this.equals(gLong.MIN_VALUE)) {
return gLong.MIN_VALUE;
} else {
return this.not().add(gLong.ONE);
}
};
/**
* Returns the sum of this and the given Long.
* @param {gLong} other Long to add to this one.
* @return {!gLong} The sum of this and the given Long.
*/
gLong.prototype.add = function (other) {
// Divide each number into 4 chunks of 16 bits, and then sum the chunks.
var a48 = this.high_ >>> 16;
var a32 = this.high_ & 0xFFFF;
var a16 = this.low_ >>> 16;
var a00 = this.low_ & 0xFFFF;
var b48 = other.high_ >>> 16;
var b32 = other.high_ & 0xFFFF;
var b16 = other.low_ >>> 16;
var b00 = other.low_ & 0xFFFF;
var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
c00 += a00 + b00;
c16 += c00 >>> 16;
c00 &= 0xFFFF;
c16 += a16 + b16;
c32 += c16 >>> 16;
c16 &= 0xFFFF;
c32 += a32 + b32;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c48 += a48 + b48;
c48 &= 0xFFFF;
return gLong.fromBits((c16 << 16) | c00, (c48 << 16) | c32);
};
/**
* Returns the difference of this and the given Long.
* @param {gLong} other Long to subtract from this.
* @return {!gLong} The difference of this and the given Long.
*/
gLong.prototype.subtract = function (other) {
return this.add(other.negate());
};
/**
* Returns the product of this and the given long.
* @param {gLong} other Long to multiply with this.
* @return {!gLong} The product of this and the other.
*/
gLong.prototype.multiply = function (other) {
if (this.isZero()) {
return gLong.ZERO;
} else if (other.isZero()) {
return gLong.ZERO;
}
if (this.equals(gLong.MIN_VALUE)) {
return other.isOdd() ? gLong.MIN_VALUE : gLong.ZERO;
} else if (other.equals(gLong.MIN_VALUE)) {
return this.isOdd() ? gLong.MIN_VALUE : gLong.ZERO;
}
if (this.isNegative()) {
if (other.isNegative()) {
return this.negate().multiply(other.negate());
} else {
return this.negate().multiply(other).negate();
}
} else if (other.isNegative()) {
return this.multiply(other.negate()).negate();
}
if (this.lessThan(gLong.TWO_PWR_24_) && other.lessThan(gLong.TWO_PWR_24_)) {
return gLong.fromNumber(this.toNumber() * other.toNumber());
}
// Divide each long into 4 chunks of 16 bits, and then add up 4x4 products.
// We can skip products that would overflow.
var a48 = this.high_ >>> 16;
var a32 = this.high_ & 0xFFFF;
var a16 = this.low_ >>> 16;
var a00 = this.low_ & 0xFFFF;
var b48 = other.high_ >>> 16;
var b32 = other.high_ & 0xFFFF;
var b16 = other.low_ >>> 16;
var b00 = other.low_ & 0xFFFF;
var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
c00 += a00 * b00;
c16 += c00 >>> 16;
c00 &= 0xFFFF;
c16 += a16 * b00;
c32 += c16 >>> 16;
c16 &= 0xFFFF;
c16 += a00 * b16;
c32 += c16 >>> 16;
c16 &= 0xFFFF;
c32 += a32 * b00;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c32 += a16 * b16;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c32 += a00 * b32;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48;
c48 &= 0xFFFF;
return gLong.fromBits((c16 << 16) | c00, (c48 << 16) | c32);
};
/**
* Returns this Long divided by the given one.
* @param {gLong} other Long by which to divide.
* @return {!gLong} This Long divided by the given one.
*/
gLong.prototype.div = function (other) {
if (other.isZero()) {
throw Error('division by zero');
} else if (this.isZero()) {
return gLong.ZERO;
}
if (this.equals(gLong.MIN_VALUE)) {
if (other.equals(gLong.ONE) || other.equals(gLong.NEG_ONE)) {
return gLong.MIN_VALUE;
} else if (other.equals(gLong.MIN_VALUE)) {
return gLong.ONE;
} else {
// At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|.
var halfThis = this.shiftRight(1);
var l_approx = halfThis.div(other).shiftLeft(1);
if (l_approx.equals(gLong.ZERO)) {
return other.isNegative() ? gLong.ONE : gLong.NEG_ONE;
} else {
var rem = this.subtract(other.multiply(l_approx));
var result = l_approx.add(rem.div(other));
return result;
}
}
} else if (other.equals(gLong.MIN_VALUE)) {
return gLong.ZERO;
}
if (this.isNegative()) {
if (other.isNegative()) {
return this.negate().div(other.negate());
} else {
return this.negate().div(other).negate();
}
} else if (other.isNegative()) {
return this.div(other.negate()).negate();
}
// Repeat the following until the remainder is less than other: find a
// floating-point that approximates remainder / other *from below*, add this
// into the result, and subtract it from the remainder. It is critical that
// the approximate value is less than or equal to the real value so that the
// remainder never becomes negative.
var res = gLong.ZERO;
var rem = this;
while (rem.greaterThanOrEqual(other)) {
// Approximate the result of division. This may be a little greater or
// smaller than the actual value.
var approx = Math.max(1, Math.floor(rem.toNumber() / other.toNumber()));
// We will tweak the approximate result by changing it in the 48-th digit or
// the smallest non-fractional digit, whichever is larger.
var log2 = Math.ceil(Math.log(approx) / Math.LN2);
var delta = 1;
if (log2 > 48)
delta = Math.pow(2, log2 - 48);
// Decrease the approximation until it is smaller than the remainder. Note
// that if it is too large, the product overflows and is negative.
var approxRes = gLong.fromNumber(approx);
var approxRem = approxRes.multiply(other);
while (approxRem.isNegative() || approxRem.greaterThan(rem)) {
approx -= delta;
approxRes = gLong.fromNumber(approx);
approxRem = approxRes.multiply(other);
}
if (approxRes.isZero()) {
approxRes = gLong.ONE;
}
res = res.add(approxRes);
rem = rem.subtract(approxRem);
}
return res;
};
/**
* Returns this Long modulo the given one.
* @param {gLong} other Long by which to mod.
* @return {!gLong} This Long modulo the given one.
*/
gLong.prototype.modulo = function (other) {
return this.subtract(this.div(other).multiply(other));
};
/** @return {!gLong} The bitwise-NOT of this value. */
gLong.prototype.not = function () {
return gLong.fromBits(~this.low_, ~this.high_);
};
/**
* Returns the bitwise-AND of this Long and the given one.
* @param {gLong} other The Long with which to AND.
* @return {!gLong} The bitwise-AND of this and the other.
*/
gLong.prototype.and = function (other) {
return gLong.fromBits(this.low_ & other.low_, this.high_ & other.high_);
};
/**
* Returns the bitwise-OR of this Long and the given one.
* @param {gLong} other The Long with which to OR.
* @return {!gLong} The bitwise-OR of this and the other.
*/
gLong.prototype.or = function (other) {
return gLong.fromBits(this.low_ | other.low_, this.high_ | other.high_);
};
/**
* Returns the bitwise-XOR of this Long and the given one.
* @param {gLong} other The Long with which to XOR.
* @return {!gLong} The bitwise-XOR of this and the other.
*/
gLong.prototype.xor = function (other) {
return gLong.fromBits(this.low_ ^ other.low_, this.high_ ^ other.high_);
};
/**
* Returns this Long with bits shifted to the left by the given amount.
* @param {number} numBits The number of bits by which to shift.
* @return {!gLong} This shifted to the left by the given amount.
*/
gLong.prototype.shiftLeft = function (numBits) {
numBits &= 63;
if (numBits == 0) {
return this;
} else {
var low = this.low_;
if (numBits < 32) {
var high = this.high_;
return gLong.fromBits(low << numBits, (high << numBits) | (low >>> (32 - numBits)));
} else {
return gLong.fromBits(0, low << (numBits - 32));
}
}
};
/**
* Returns this Long with bits shifted to the right by the given amount.
* @param {number} numBits The number of bits by which to shift.
* @return {!gLong} This shifted to the right by the given amount.
*/
gLong.prototype.shiftRight = function (numBits) {
numBits &= 63;
if (numBits == 0) {
return this;
} else {
var high = this.high_;
if (numBits < 32) {
var low = this.low_;
return gLong.fromBits((low >>> numBits) | (high << (32 - numBits)), high >> numBits);
} else {
return gLong.fromBits(high >> (numBits - 32), high >= 0 ? 0 : -1);
}
}
};
/**
* Returns this Long with bits shifted to the right by the given amount, with
* the new top bits matching the current sign bit.
* @param {number} numBits The number of bits by which to shift.
* @return {!gLong} This shifted to the right by the given amount, with
* zeros placed into the new leading bits.
*/
gLong.prototype.shiftRightUnsigned = function (numBits) {
numBits &= 63;
if (numBits == 0) {
return this;
} else {
var high = this.high_;
if (numBits < 32) {
var low = this.low_;
return gLong.fromBits((low >>> numBits) | (high << (32 - numBits)), high >>> numBits);
} else if (numBits == 32) {
return gLong.fromBits(high, 0);
} else {
return gLong.fromBits(high >>> (numBits - 32), 0);
}
}
};
gLong.IntCache_ = {};
gLong.TWO_PWR_16_DBL_ = 1 << 16;
gLong.TWO_PWR_24_DBL_ = 1 << 24;
gLong.TWO_PWR_32_DBL_ = gLong.TWO_PWR_16_DBL_ * gLong.TWO_PWR_16_DBL_;
gLong.TWO_PWR_31_DBL_ = gLong.TWO_PWR_32_DBL_ / 2;
gLong.TWO_PWR_48_DBL_ = gLong.TWO_PWR_32_DBL_ * gLong.TWO_PWR_16_DBL_;
gLong.TWO_PWR_64_DBL_ = gLong.TWO_PWR_32_DBL_ * gLong.TWO_PWR_32_DBL_;
gLong.TWO_PWR_63_DBL_ = gLong.TWO_PWR_64_DBL_ / 2;
gLong.ZERO = gLong.fromInt(0);
gLong.ONE = gLong.fromInt(1);
gLong.NEG_ONE = gLong.fromInt(-1);
gLong.MAX_VALUE = gLong.fromBits(0xFFFFFFFF, 0x7FFFFFFF);
gLong.MIN_VALUE = gLong.fromBits(0, 0x80000000);
gLong.TWO_PWR_24_ = gLong.fromInt(gLong.TWO_PWR_24_DBL_);
return gLong;
})();
return gLong;
});
define('src/util',["require", "exports", './gLong'], function(require, exports, __gLong__) {
var gLong = __gLong__;
// default module: util
exports.INT_MAX = Math.pow(2, 31) - 1;
exports.INT_MIN = -exports.INT_MAX - 1;
exports.FLOAT_POS_INFINITY = Math.pow(2, 128);
exports.FLOAT_NEG_INFINITY = -1 * exports.FLOAT_POS_INFINITY;
exports.FLOAT_POS_INFINITY_AS_INT = 0x7F800000;
exports.FLOAT_NEG_INFINITY_AS_INT = -8388608;
exports.FLOAT_NaN_AS_INT = 0x7fc00000;
if (Math['imul'] == null) {
Math['imul'] = function (a, b) {
// polyfill from https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/imul
var ah = (a >>> 16) & 0xffff;
var al = a & 0xffff;
var bh = (b >>> 16) & 0xffff;
var bl = b & 0xffff;
// the shift by 0 fixes the sign on the high part, and the |0 prevents
// overflow on the high part.
return (al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0;
};
}
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement, fromIndex) {
if (this == null) {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
if (len === 0) {
return -1;
}
var n = 0;
if (fromIndex !== undefined) {
n = Number(fromIndex);
if (n != n) {
n = 0;
} else if (n != 0 && n != Infinity && n != -Infinity) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {
return -1;
}
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
for (; k < len; k++) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
};
}
// Creates and initializes *JavaScript* array to *val* in each element slot.
// Like memset, but for arrays.
function arrayset(len, val) {
var array = new Array(len);
for (var i = 0; i < len; i++) {
array[i] = val;
}
return array;
}
exports.arrayset = arrayset;
function int_mod(rs, a, b) {
if (b === 0) {
var err_cls = rs.get_bs_class('Ljava/lang/ArithmeticException;');
rs.java_throw(err_cls, '/ by zero');
}
return a % b;
}
exports.int_mod = int_mod;
function int_div(rs, a, b) {
if (b === 0) {
var err_cls = rs.get_bs_class('Ljava/lang/ArithmeticException;');
rs.java_throw(err_cls, '/ by zero');
}
if (a === exports.INT_MIN && b === -1) {
return a;
}
return (a / b) | 0;
}
exports.int_div = int_div;
function long_mod(rs, a, b) {
if (b.isZero()) {
var err_cls = rs.get_bs_class('Ljava/lang/ArithmeticException;');
rs.java_throw(err_cls, '/ by zero');
}
return a.modulo(b);
}
exports.long_mod = long_mod;
function long_div(rs, a, b) {
if (b.isZero()) {
var err_cls = rs.get_bs_class('Ljava/lang/ArithmeticException;');
rs.java_throw(err_cls, '/ by zero');
}
return a.div(b);
}
exports.long_div = long_div;
function float2int(a) {
if (a > exports.INT_MAX) {
return exports.INT_MAX;
} else if (a < exports.INT_MIN) {
return exports.INT_MIN;
} else {
return a | 0;
}
}
exports.float2int = float2int;
function intbits2float(int32) {
if (typeof Int32Array !== "undefined") {
var i_view = new Int32Array([int32]);
var f_view = new Float32Array(i_view.buffer);
return f_view[0];
}
if (int32 === exports.FLOAT_POS_INFINITY_AS_INT) {
return Number.POSITIVE_INFINITY;
} else if (int32 === exports.FLOAT_NEG_INFINITY_AS_INT) {
return Number.NEGATIVE_INFINITY;
}
var sign = (int32 & 0x80000000) >>> 31;
var exponent = (int32 & 0x7F800000) >>> 23;
var significand = int32 & 0x007FFFFF;
var value;
if (exponent === 0) {
value = Math.pow(-1, sign) * significand * Math.pow(2, -149);
} else {
value = Math.pow(-1, sign) * (1 + significand * Math.pow(2, -23)) * Math.pow(2, exponent - 127);
}
if (value < exports.FLOAT_NEG_INFINITY || value > exports.FLOAT_POS_INFINITY) {
value = NaN;
}
return value;
}
exports.intbits2float = intbits2float;
function longbits2double(uint32_a, uint32_b) {
if (typeof Uint32Array !== "undefined") {
var i_view = new Uint32Array(2);
i_view[0] = uint32_b;
i_view[1] = uint32_a;
var d_view = new Float64Array(i_view.buffer);
return d_view[0];
}
var sign = (uint32_a & 0x80000000) >>> 31;
var exponent = (uint32_a & 0x7FF00000) >>> 20;
var significand = exports.lshift(uint32_a & 0x000FFFFF, 32) + uint32_b;
if (exponent === 0 && significand === 0) {
return 0;
}
if (exponent === 2047) {
if (significand === 0) {
if (sign === 1) {
return Number.NEGATIVE_INFINITY;
}
return Number.POSITIVE_INFINITY;
} else {
return NaN;
}
}
if (exponent === 0)
return Math.pow(-1, sign) * significand * Math.pow(2, -1074);
return Math.pow(-1, sign) * (1 + significand * Math.pow(2, -52)) * Math.pow(2, exponent - 1023);
}
exports.longbits2double = longbits2double;
// Call this ONLY on the result of two non-NaN numbers.
function wrap_float(a) {
if (a > 3.40282346638528860e+38) {
return Number.POSITIVE_INFINITY;
}
if (0 < a && a < 1.40129846432481707e-45) {
return 0;
}
if (a < -3.40282346638528860e+38) {
return Number.NEGATIVE_INFINITY;
}
if (0 > a && a > -1.40129846432481707e-45) {
return 0;
}
return a;
}
exports.wrap_float = wrap_float;
function cmp(a, b) {
if (a === b) {
return 0;
}
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
// this will occur if either a or b is NaN
return null;
}
exports.cmp = cmp;
// implements x<<n without the braindead javascript << operator
// (see http://stackoverflow.com/questions/337355/javascript-bitwise-shift-of-long-long-number)
function lshift(x, n) {
return x * Math.pow(2, n);
}
exports.lshift = lshift;
function read_uint(bytes) {
var n = bytes.length - 1;
// sum up the byte values shifted left to the right alignment.
var sum = 0;
for (var i = 0; i <= n; i++) {
sum += exports.lshift(bytes[i], 8 * (n - i));
}
return sum;
}
exports.read_uint = read_uint;
// Convert :count chars starting from :offset in a Java character array into a JS string
function chars2js_str(jvm_carr, offset, count) {
var off = offset || 0;
return exports.bytes2str(jvm_carr.array).substr(off, count);
}
exports.chars2js_str = chars2js_str;
function bytestr_to_array(bytecode_string) {
var rv = [];
for (var i = 0; i < bytecode_string.length; i++) {
rv.push(bytecode_string.charCodeAt(i) & 0xFF);
}
return rv;
}
exports.bytestr_to_array = bytestr_to_array;
function array_to_bytestr(bytecode_array) {
// XXX: We'd like to use String.fromCharCode(bytecode_array...)
// but that fails on Webkit with arrays longer than 2^31. See issue #129 for details.
var rv = '';
for (var i = 0; i < bytecode_array.length; i++) {
rv += String.fromCharCode(bytecode_array[i]);
}
return rv;
}
exports.array_to_bytestr = array_to_bytestr;
function parse_flags(flag_byte) {
return {
"public": (flag_byte & 0x1) > 0,
"private": (flag_byte & 0x2) > 0,
"protected": (flag_byte & 0x4) > 0,
"static": (flag_byte & 0x8) > 0,
"final": (flag_byte & 0x10) > 0,
"synchronized": (flag_byte & 0x20) > 0,
"super": (flag_byte & 0x20) > 0,
"volatile": (flag_byte & 0x40) > 0,
"transient": (flag_byte & 0x80) > 0,
"native": (flag_byte & 0x100) > 0,
"interface": (flag_byte & 0x200) > 0,
"abstract": (flag_byte & 0x400) > 0,
"strict": (flag_byte & 0x800) > 0
};
}
exports.parse_flags = parse_flags;
function escaper(c) {
var args = [];
for (var _i = 0; _i < (arguments.length - 1); _i++) {
args[_i] = arguments[_i + 1];
}
switch (c) {
case "\n":
return "\\n";
case "\r":
return "\\r";
case "\t":
return "\\t";
case "\v":
return "\\v";
case "\f":
return "\\f";
default:
return c;
}
}
function escape_whitespace(str) {
return str.replace(/\s/g, escaper);
}
exports.escape_whitespace = escape_whitespace;
// if :entry is a reference, display its referent in a comment
function format_extra_info(entry) {
var type = entry.type;
var info = typeof entry.deref === "function" ? entry.deref() : void 0;
if (!info) {
return "";
}
switch (type) {
case 'Method':
case 'InterfaceMethod':
return "\t// " + info["class"] + "." + info.sig;
case 'Field':
return "\t// " + info["class"] + "." + info.name + ":" + info.type;
case 'NameAndType':
return "// " + info.name + ":" + info.type;
default:
if (exports.is_string(info)) {
return "\t// " + exports.escape_whitespace(info);
}
}
}
exports.format_extra_info = format_extra_info;
var BytesArray = (function () {
function BytesArray(buffer, start, end) {
this.buffer = buffer;
this.start = start != null ? start : 0;
this.end = end != null ? end : this.buffer.length;
this._index = 0;
}
BytesArray.prototype.rewind = function () {
this._index = 0;
};
BytesArray.prototype.pos = function () {
return this._index;
};
BytesArray.prototype.skip = function (bytes_count) {
this._index += bytes_count;
};
BytesArray.prototype.has_bytes = function () {
return this.start + this._index < this.end;
};
BytesArray.prototype.get_uint = function (bytes_count) {
var readIndex = this.start + this._index;
this._index += bytes_count;
switch (bytes_count) {
case 1:
return this.buffer.readUInt8(readIndex);
case 2:
return this.buffer.readUInt16BE(readIndex);
case 4:
return this.buffer.readUInt32BE(readIndex);
default:
this._index -= bytes_count;
throw new Error('Cannot read a uint of size ' + bytes_count);
}
};
BytesArray.prototype.get_int = function (bytes_count) {
var bytes_to_set = 32 - bytes_count * 8;
return this.get_uint(bytes_count) << bytes_to_set >> bytes_to_set;
};
BytesArray.prototype.read = function (bytes_count) {
var rv = [];
for (var i = this.start + this._index; i < this.start + this._index + bytes_count; i++) {
rv.push(this.buffer.readUInt8(i));
}
this._index += bytes_count;
return rv;
};
BytesArray.prototype.peek = function () {
return this.buffer.readUInt8(this.start + this._index);
};
BytesArray.prototype.size = function () {
return this.end - this.start - this._index;
};
BytesArray.prototype.splice = function (len) {
var arr = new BytesArray(this.buffer, this.start + this._index, this.start + this._index + len);
this._index += len;
return arr;
};
BytesArray.prototype.slice = function (len) {
var rv = this.buffer.slice(this.start + this._index, this.start + this._index + len);
this._index += len;
return rv;
};
return BytesArray;
})();
exports.BytesArray = BytesArray;
function initial_value(type_str) {
if (type_str === 'J')
return gLong.ZERO;
var c = type_str[0];
if (c === '[' || c === 'L')
return null;
return 0;
}
exports.initial_value = initial_value;
function is_string(obj) {
return typeof obj === 'string' || obj instanceof String;
}
exports.is_string = is_string;
// Java classes are represented internally using slashes as delimiters.
// These helper functions convert between the two representations.
function ext_classname(str) {
return exports.descriptor2typestr(str).replace(/\//g, '.');
}
exports.ext_classname = ext_classname;
function int_classname(str) {
return exports.typestr2descriptor(str).replace(/\./g, '/');
}
exports.int_classname = int_classname;
function verify_int_classname(str) {
var array_nesting = str.match(/^\[*/)[0].length;
if (array_nesting > 255) {
return false;
}
if (array_nesting > 0) {
str = str.slice(array_nesting);
}
if (str[0] === 'L') {
if (str[str.length - 1] !== ';') {
return false;
}
str = str.slice(1, -1);
}
if (str in exports.internal2external) {
return true;
}
if (str.match(/\/{2,}/)) {
return false;
}
var parts = str.split('/');
for (var i = 0; i < parts.length; i++) {
if (parts[i].match(/[^$_a-z0-9]/i)) {
return false;
}
}
return true;
}
exports.verify_int_classname = verify_int_classname;
exports.internal2external = {
B: 'byte',
C: 'char',
D: 'double',
F: 'float',
I: 'int',
J: 'long',
S: 'short',
V: 'void',
Z: 'boolean'
};
exports.external2internal = {};
for (var k in exports.internal2external) {
exports.external2internal[exports.internal2external[k]] = k;
}
// Get the component type of an array type string.
// Cut off the [L and ; for arrays of classes.
function get_component_type(type_str) {
return type_str.slice(1);
}
exports.get_component_type = get_component_type;
function is_array_type(type_str) {
return type_str[0] === '[';
}
exports.is_array_type = is_array_type;
function is_primitive_type(type_str) {
return type_str in exports.internal2external;
}
exports.is_primitive_type = is_primitive_type;
function is_reference_type(type_str) {
return type_str[0] === 'L';
}
exports.is_reference_type = is_reference_type;
// Converts type descriptors into standardized internal type strings.
// Ljava/lang/Class; => java/lang/Class Reference types
// [Ljava/lang/Class; is unchanged Array types
// C => char Primitive types
function descriptor2typestr(type_str) {
var c = type_str[0];
if (c in exports.internal2external)
return exports.internal2external[c];
if (c === 'L')
return type_str.slice(1, -1);
if (c === '[')
return type_str;
throw new Error("Unrecognized type string: " + type_str);
}
exports.descriptor2typestr = descriptor2typestr;
// Takes a character array of concatenated type descriptors and returns/removes the first one.
function carr2descriptor(carr) {
var c = carr.shift();
if (c == null)
return null;
if (exports.internal2external[c] !== void 0)
return c;
if (c === 'L') {
var rv = 'L';
while ((c = carr.shift()) !== ';') {
rv += c;
}
return rv + ';';
}
if (c === '[')
return "[" + exports.carr2descriptor(carr);
// no match
carr.unshift(c);
throw new Error("Unrecognized descriptor: " + carr.join(''));
}
exports.carr2descriptor = carr2descriptor;
// Converts internal type strings into type descriptors. Reverse of descriptor2typestr.
function typestr2descriptor(type_str) {
var c = type_str[0];
if (exports.external2internal[type_str] !== void 0) {
return exports.external2internal[type_str];
} else if (c === '[') {
return type_str;
} else {
return "L" + type_str + ";";
}
}
exports.typestr2descriptor = typestr2descriptor;
// Parse Java's pseudo-UTF-8 strings. (spec 4.4.7)
function bytes2str(bytes, null_terminate) {
var y;
var z;
var idx = 0;
var rv = '';
while (idx < bytes.length) {
var x = bytes[idx++] & 0xff;
if (null_terminate && x == 0) {
break;
}
rv += String.fromCharCode(x <= 0x7f ? x : x <= 0xdf ? (y = bytes[idx++], ((x & 0x1f) << 6) + (y & 0x3f)) : (y = bytes[idx++], z = bytes[idx++], ((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f)));
}
return rv;
}
exports.bytes2str = bytes2str;
function last(array) {
return array[array.length - 1];
}
exports.last = last;
var SafeMap = (function () {
function SafeMap() {
this.cache = Object.create(null);
}
SafeMap.prototype.get = function (key) {
if (this.cache[key] != null) {
return this.cache[key];
}
if (key == '__proto__' && this.proto_cache !== undefined) {
return this.proto_cache;
}
return undefined;
};
SafeMap.prototype.has = function (key) {
return this.get(key) !== void 0;
};
SafeMap.prototype.set = function (key, value) {
if (key != '__proto__') {
this.cache[key] = value;
} else {
this.proto_cache = value;
}
};
return SafeMap;
})();
exports.SafeMap = SafeMap;
});
define('src/logging',["require", "exports", './gLong'], function(require, exports, __gLong__) {
var gLong = __gLong__;
// default module: logging
function debug_var(e) {
if (e === null) {
return '!';
} else if (e === void 0) {
return 'undef';
} else if (e.ref != null) {
return "*" + e.ref;
} else if (e instanceof gLong) {
return e + "L";
}
return e;
}
// used for debugging the stack and local variables
function debug_vars(arr) {
return arr.map(debug_var);
}
exports.debug_vars = debug_vars;
// log levels
// TODO: turn this into an enum, if possible
exports.VTRACE = 10;
exports.TRACE = 9;
exports.DEBUG = 5;
exports.ERROR = 1;
exports.log_level = exports.ERROR;
function log(level, msgs) {
if (level <= exports.log_level) {
var msg = msgs.join(' ');
if (level == 1) {
console.error(msg);
} else {
console.log(msg);
}
}
}
function vtrace() {
var msgs = [];
for (var _i = 0; _i < (arguments.length - 0); _i++) {
msgs[_i] = arguments[_i + 0];
}
log(exports.VTRACE, msgs);
}
exports.vtrace = vtrace;
function trace() {
var msgs = [];
for (var _i = 0; _i < (arguments.length - 0); _i++) {
msgs[_i] = arguments[_i + 0];
}
log(exports.TRACE, msgs);
}
exports.trace = trace;
function debug() {
var msgs = [];
for (var _i = 0; _i < (arguments.length - 0); _i++) {
msgs[_i] = arguments[_i + 0];
}
log(exports.DEBUG, msgs);
}
exports.debug = debug;
function error() {
var msgs = [];
for (var _i = 0; _i < (arguments.length - 0); _i++) {
msgs[_i] = arguments[_i + 0];
}
log(exports.ERROR, msgs);
}
exports.error = error;
});
// Underscore.js 1.5.1
// http://underscorejs.org
// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
(function() {
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `global` on the server.
var root = this;
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// Establish the object that gets returned to break out of a loop iteration.
var breaker = {};
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
var
push = ArrayProto.push,
slice = ArrayProto.slice,
concat = ArrayProto.concat,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
// Create a safe reference to the Underscore object for use below.
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object via a string identifier,
// for Closure Compiler "advanced" mode.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
// Current version.
_.VERSION = '1.5.1';
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, l = obj.length; i < l; i++) {
if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
for (var key in obj) {
if (_.has(obj, key)) {
if (iterator.call(context, obj[key], key, obj) === breaker) return;
}
}
}
};
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
each(obj, function(value, index, list) {
results.push(iterator.call(context, value, index, list));
});
return results;
};
var reduceError = 'Reduce of empty array with no initial value';
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
}
each(obj, function(value, index, list) {
if (!initial) {
memo = value;
initial = true;
} else {
memo = iterator.call(context, memo, value, index, list);
}
});
if (!initial) throw new TypeError(reduceError);
return memo;
};
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
var length = obj.length;
if (length !== +length) {
var keys = _.keys(obj);
length = keys.length;
}
each(obj, function(value, index, list) {
index = keys ? keys[--length] : --length;
if (!initial) {
memo = obj[index];
initial = true;
} else {
memo = iterator.call(context, memo, obj[index], index, list);
}
});
if (!initial) throw new TypeError(reduceError);
return memo;
};
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, iterator, context) {
var result;
any(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) {
result = value;
return true;
}
});
return result;
};
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
each(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) results.push(value);
});
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function(obj, iterator, context) {
return _.filter(obj, function(value, index, list) {
return !iterator.call(context, value, index, list);
}, context);
};
// Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _.some = _.any = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
each(obj, function(value, index, list) {
if (result || (result = iterator.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if the array or object contains a given value (using `===`).
// Aliased as `include`.
_.contains = _.include = function(obj, target) {
if (obj == null) return false;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
return any(obj, function(value) {
return value === target;
});
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
var isFunc = _.isFunction(method);
return _.map(obj, function(value) {
return (isFunc ? method : value[method]).apply(value, args);
});
};
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) {
return _.map(obj, function(value){ return value[key]; });
};
// Convenience version of a common use case of `filter`: selecting only objects
// containing specific `key:value` pairs.
_.where = function(obj, attrs, first) {
if (_.isEmpty(attrs)) return first ? void 0 : [];
return _[first ? 'find' : 'filter'](obj, function(value) {
for (var key in attrs) {
if (attrs[key] !== value[key]) return false;
}
return true;
});
};
// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
_.findWhere = function(obj, attrs) {
return _.where(obj, attrs, true);
};
// Return the maximum element or (element-based computation).
// Can't optimize arrays of integers longer than 65,535 elements.
// See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
_.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.max.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return -Infinity;
var result = {computed : -Infinity, value: -Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed > result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Return the minimum element (or element-based computation).
_.min = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.min.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return Infinity;
var result = {computed : Infinity, value: Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed < result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Shuffle an array.
_.shuffle = function(obj) {
var rand;
var index = 0;
var shuffled = [];
each(obj, function(value) {
rand = _.random(index++);
shuffled[index - 1] = shuffled[rand];
shuffled[rand] = value;
});
return shuffled;
};
// An internal function to generate lookup iterators.
var lookupIterator = function(value) {
return _.isFunction(value) ? value : function(obj){ return obj[value]; };
};
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function(obj, value, context) {
var iterator = lookupIterator(value);
return _.pluck(_.map(obj, function(value, index, list) {
return {
value : value,
index : index,
criteria : iterator.call(context, value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria;
var b = right.criteria;
if (a !== b) {
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
return left.index < right.index ? -1 : 1;
}), 'value');
};
// An internal function used for aggregate "group by" operations.
var group = function(obj, value, context, behavior) {
var result = {};
var iterator = lookupIterator(value == null ? _.identity : value);
each(obj, function(value, index) {
var key = iterator.call(context, value, index, obj);
behavior(result, key, value);
});
return result;
};
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = function(obj, value, context) {
return group(obj, value, context, function(result, key, value) {
(_.has(result, key) ? result[key] : (result[key] = [])).push(value);
});
};
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
_.countBy = function(obj, value, context) {
return group(obj, value, context, function(result, key) {
if (!_.has(result, key)) result[key] = 0;
result[key]++;
});
};
// Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator, context) {
iterator = iterator == null ? _.identity : lookupIterator(iterator);
var value = iterator.call(context, obj);
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >>> 1;
iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
}
return low;
};
// Safely create a real, live array from anything iterable.
_.toArray = function(obj) {
if (!obj) return [];
if (_.isArray(obj)) return slice.call(obj);
if (obj.length === +obj.length) return _.map(obj, _.identity);
return _.values(obj);
};
// Return the number of elements in an object.
_.size = function(obj) {
if (obj == null) return 0;
return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
};
// Array Functions
// ---------------
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head` and `take`. The **guard** check
// allows it to work with `_.map`.
_.first = _.head = _.take = function(array, n, guard) {
if (array == null) return void 0;
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
};
// Returns everything but the last entry of the array. Especially useful on
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N. The **guard** check allows it to work with
// `_.map`.
_.initial = function(array, n, guard) {
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
};
// Get the last element of an array. Passing **n** will return the last N
// values in the array. The **guard** check allows it to work with `_.map`.
_.last = function(array, n, guard) {
if (array == null) return void 0;
if ((n != null) && !guard) {
return slice.call(array, Math.max(array.length - n, 0));
} else {
return array[array.length - 1];
}
};
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
// Especially useful on the arguments object. Passing an **n** will return
// the rest N values in the array. The **guard**
// check allows it to work with `_.map`.
_.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, (n == null) || guard ? 1 : n);
};
// Trim out all falsy values from an array.
_.compact = function(array) {
return _.filter(array, _.identity);
};
// Internal implementation of a recursive `flatten` function.
var flatten = function(input, shallow, output) {
if (shallow && _.every(input, _.isArray)) {
return concat.apply(output, input);
}
each(input, function(value) {
if (_.isArray(value) || _.isArguments(value)) {
shallow ? push.apply(output, value) : flatten(value, shallow, output);
} else {
output.push(value);
}
});
return output;
};
// Return a completely flattened version of an array.
_.flatten = function(array, shallow) {
return flatten(array, shallow, []);
};
// Return a version of the array that does not contain the specified value(s).
_.without = function(array) {
return _.difference(array, slice.call(arguments, 1));
};
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iterator, context) {
if (_.isFunction(isSorted)) {
context = iterator;
iterator = isSorted;
isSorted = false;
}
var initial = iterator ? _.map(array, iterator, context) : array;
var results = [];
var seen = [];
each(initial, function(value, index) {
if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
seen.push(value);
results.push(array[index]);
}
});
return results;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
return _.uniq(_.flatten(arguments, true));
};
// Produce an array that contains every item shared between all the
// passed-in arrays.
_.intersection = function(array) {
var rest = slice.call(arguments, 1);
return _.filter(_.uniq(array), function(item) {
return _.every(rest, function(other) {
return _.indexOf(other, item) >= 0;
});
});
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
return _.filter(array, function(value){ return !_.contains(rest, value); });
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function() {
var length = _.max(_.pluck(arguments, "length").concat(0));
var results = new Array(length);
for (var i = 0; i < length; i++) {
results[i] = _.pluck(arguments, '' + i);
}
return results;
};
// Converts lists into objects. Pass either a single array of `[key, value]`
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values.
_.object = function(list, values) {
if (list == null) return {};
var result = {};
for (var i = 0, l = list.length; i < l; i++) {
if (values) {
result[list[i]] = values[i];
} else {
result[list[i][0]] = list[i][1];
}
}
return result;
};
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
// we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) {
if (array == null) return -1;
var i = 0, l = array.length;
if (isSorted) {
if (typeof isSorted == 'number') {
i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
} else {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
for (; i < l; i++) if (array[i] === item) return i;
return -1;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item, from) {
if (array == null) return -1;
var hasIndex = from != null;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
}
var i = (hasIndex ? from : array.length);
while (i--) if (array[i] === item) return i;
return -1;
};
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
_.range = function(start, stop, step) {
if (arguments.length <= 1) {
stop = start || 0;
start = 0;
}
step = arguments[2] || 1;
var len = Math.max(Math.ceil((stop - start) / step), 0);
var idx = 0;
var range = new Array(len);
while(idx < len) {
range[idx++] = start;
start += step;
}
return range;
};
// Function (ahem) Functions
// ------------------
// Reusable constructor function for prototype setting.
var ctor = function(){};
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
// available.
_.bind = function(func, context) {
var args, bound;
if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
ctor.prototype = null;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
// Partially apply a function by creating a version that has had some of its
// arguments pre-filled, without changing its dynamic `this` context.
_.partial = function(func) {
var args = slice.call(arguments, 1);
return function() {
return func.apply(this, args.concat(slice.call(arguments)));
};
};
// Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it.
_.bindAll = function(obj) {
var funcs = slice.call(arguments, 1);
if (funcs.length === 0) throw new Error("bindAll must be passed function names");
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
return obj;
};
// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
var memo = {};
hasher || (hasher = _.identity);
return function() {
var key = hasher.apply(this, arguments);
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
};
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function(){ return func.apply(null, args); }, wait);
};
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = function(func) {
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
};
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
_.throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
options || (options = {});
var later = function() {
previous = options.leading === false ? 0 : new Date;
timeout = null;
result = func.apply(context, args);
};
return function() {
var now = new Date;
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var result;
var timeout = null;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) result = func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) result = func.apply(context, args);
return result;
};
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
memo = func.apply(this, arguments);
func = null;
return memo;
};
};
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_.wrap = function(func, wrapper) {
return function() {
var args = [func];
push.apply(args, arguments);
return wrapper.apply(this, args);
};
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
var funcs = arguments;
return function() {
var args = arguments;
for (var i = funcs.length - 1; i >= 0; i--) {
args = [funcs[i].apply(this, args)];
}
return args[0];
};
};
// Returns a function that will only be executed after being called N times.
_.after = function(times, func) {
return function() {
if (--times < 1) {
return func.apply(this, arguments);
}
};
};
// Object Functions
// ----------------
// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = nativeKeys || function(obj) {
if (obj !== Object(obj)) throw new TypeError('Invalid object');
var keys = [];
for (var key in obj) if (_.has(obj, key)) keys.push(key);
return keys;
};
// Retrieve the values of an object's properties.
_.values = function(obj) {
var values = [];
for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);
return values;
};
// Convert an object into a list of `[key, value]` pairs.
_.pairs = function(obj) {
var pairs = [];
for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);
return pairs;
};
// Invert the keys and values of an object. The values must be serializable.
_.invert = function(obj) {
var result = {};
for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;
return result;
};
// Return a sorted list of the function names available on the object.
// Aliased as `methods`
_.functions = _.methods = function(obj) {
var names = [];
for (var key in obj) {
if (_.isFunction(obj[key])) names.push(key);
}
return names.sort();
};
// Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
obj[prop] = source[prop];
}
}
});
return obj;
};
// Return a copy of the object only containing the whitelisted properties.
_.pick = function(obj) {
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
each(keys, function(key) {
if (key in obj) copy[key] = obj[key];
});
return copy;
};
// Return a copy of the object without the blacklisted properties.
_.omit = function(obj) {
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
for (var key in obj) {
if (!_.contains(keys, key)) copy[key] = obj[key];
}
return copy;
};
// Fill in a given object with default properties.
_.defaults = function(obj) {
each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
if (obj[prop] === void 0) obj[prop] = source[prop];
}
}
});
return obj;
};
// Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
// Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
_.tap = function(obj, interceptor) {
interceptor(obj);
return obj;
};
// Internal recursive comparison function for `isEqual`.
var eq = function(a, b, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
if (a === b) return a !== 0 || 1 / a == 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// Unwrap any wrapped objects.
if (a instanceof _) a = a._wrapped;
if (b instanceof _) b = b._wrapped;
// Compare `[[Class]]` names.
var className = toString.call(a);
if (className != toString.call(b)) return false;
switch (className) {
// Strings, numbers, dates, and booleans are compared by value.
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return a == String(b);
case '[object Number]':
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
// other numeric values.
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return +a == +b;
// RegExps are compared by their source patterns and flags.
case '[object RegExp]':
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
}
if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = aStack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (aStack[length] == a) return bStack[length] == b;
}
// Objects with different constructors are not equivalent, but `Object`s
// from different frames are.
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
_.isFunction(bCtor) && (bCtor instanceof bCtor))) {
return false;
}
// Add the first object to the stack of traversed objects.
aStack.push(a);
bStack.push(b);
var size = 0, result = true;
// Recursively compare objects and arrays.
if (className == '[object Array]') {
// Compare array lengths to determine if a deep comparison is necessary.
size = a.length;
result = size == b.length;
if (result) {
// Deep compare the contents, ignoring non-numeric properties.
while (size--) {
if (!(result = eq(a[size], b[size], aStack, bStack))) break;
}
}
} else {
// Deep compare objects.
for (var key in a) {
if (_.has(a, key)) {
// Count the expected number of properties.
size++;
// Deep compare each member.
if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
}
}
// Ensure that both objects contain the same number of properties.
if (result) {
for (key in b) {
if (_.has(b, key) && !(size--)) break;
}
result = !size;
}
}
// Remove the first object from the stack of traversed objects.
aStack.pop();
bStack.pop();
return result;
};
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {
return eq(a, b, [], []);
};
// Is a given array, string, or object empty?
// An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) {
if (obj == null) return true;
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
for (var key in obj) if (_.has(obj, key)) return false;
return true;
};
// Is a given value a DOM element?
_.isElement = function(obj) {
return !!(obj && obj.nodeType === 1);
};
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) == '[object Array]';
};
// Is a given variable an object?
_.isObject = function(obj) {
return obj === Object(obj);
};
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
_['is' + name] = function(obj) {
return toString.call(obj) == '[object ' + name + ']';
};
});
// Define a fallback version of the method in browsers (ahem, IE), where
// there isn't any inspectable "Arguments" type.
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return !!(obj && _.has(obj, 'callee'));
};
}
// Optimize `isFunction` if appropriate.
if (typeof (/./) !== 'function') {
_.isFunction = function(obj) {
return typeof obj === 'function';
};
}
// Is a given object a finite number?
_.isFinite = function(obj) {
return isFinite(obj) && !isNaN(parseFloat(obj));
};
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
_.isNaN = function(obj) {
return _.isNumber(obj) && obj != +obj;
};
// Is a given value a boolean?
_.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};
// Is a given value equal to null?
_.isNull = function(obj) {
return obj === null;
};
// Is a given variable undefined?
_.isUndefined = function(obj) {
return obj === void 0;
};
// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
// Utility Functions
// -----------------
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
// previous owner. Returns a reference to the Underscore object.
_.noConflict = function() {
root._ = previousUnderscore;
return this;
};
// Keep the identity function around for default iterators.
_.identity = function(value) {
return value;
};
// Run a function **n** times.
_.times = function(n, iterator, context) {
var accum = Array(Math.max(0, n));
for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
return accum;
};
// Return a random integer between min and max (inclusive).
_.random = function(min, max) {
if (max == null) {
max = min;
min = 0;
}
return min + Math.floor(Math.random() * (max - min + 1));
};
// List of HTML entities for escaping.
var entityMap = {
escape: {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
'/': '&#x2F;'
}
};
entityMap.unescape = _.invert(entityMap.escape);
// Regexes containing the keys and values listed immediately above.
var entityRegexes = {
escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
};
// Functions for escaping and unescaping strings to/from HTML interpolation.
_.each(['escape', 'unescape'], function(method) {
_[method] = function(string) {
if (string == null) return '';
return ('' + string).replace(entityRegexes[method], function(match) {
return entityMap[method][match];
});
};
});
// If the value of the named `property` is a function then invoke it with the
// `object` as context; otherwise, return it.
_.result = function(object, property) {
if (object == null) return void 0;
var value = object[property];
return _.isFunction(value) ? value.call(object) : value;
};
// Add your own custom functions to the Underscore object.
_.mixin = function(obj) {
each(_.functions(obj), function(name){
var func = _[name] = obj[name];
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return result.call(this, func.apply(_, args));
};
});
};
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function(prefix) {
var id = ++idCounter + '';
return prefix ? prefix + id : id;
};
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /(.)^/;
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(text, data, settings) {
var render;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
var matcher = new RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
// Compile the template source, escaping string literals appropriately.
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset)
.replace(escaper, function(match) { return '\\' + escapes[match]; });
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
}
if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
}
if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
index = offset + match.length;
return match;
});
source += "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + "return __p;\n";
try {
render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
if (data) return render(data, _);
var template = function(data) {
return render.call(this, data, _);
};
// Provide the compiled function source as a convenience for precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
return template;
};
// Add a "chain" function, which will delegate to the wrapper.
_.chain = function(obj) {
return _(obj).chain();
};
// OOP
// ---------------
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
// Helper function to continue chaining intermediate results.
var result = function(obj) {
return this._chain ? _(obj).chain() : obj;
};
// Add all of the Underscore functions to the wrapper object.
_.mixin(_);
// Add all mutator Array functions to the wrapper.
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
var obj = this._wrapped;
method.apply(obj, arguments);
if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
return result.call(this, obj);
};
});
// Add all accessor Array functions to the wrapper.
each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
return result.call(this, method.apply(this._wrapped, arguments));
};
});
_.extend(_.prototype, {
// Start chaining a wrapped Underscore object.
chain: function() {
this._chain = true;
return this;
},
// Extracts the result from a wrapped and chained object.
value: function() {
return this._wrapped;
}
});
}).call(this);
define("vendor/underscore/underscore", (function (global) {
return function () {
var ret, fn;
return ret || global._;
};
}(this)));
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
define('src/exceptions',["require", "exports", './logging', "../vendor/underscore/underscore"], function(require, exports, __logging__) {
/// <amd-dependency path="../vendor/underscore/underscore" />
var underscore = require('../vendor/underscore/underscore');
var logging = __logging__;
var debug = logging.debug;
var HaltException = (function () {
function HaltException(exit_code) {
this.exit_code = exit_code;
}
HaltException.prototype.toplevel_catch_handler = function () {
if (this.exit_code !== 0) {
return logging.error("\nExited with code " + this.exit_code);
}
};
return HaltException;
})();
exports.HaltException = HaltException;
exports.ReturnException = 'RETURNEXCEPTION';
var YieldException = (function () {
function YieldException(condition) {
this.condition = condition;
}
return YieldException;
})();
exports.YieldException = YieldException;
var YieldIOException = (function (_super) {
__extends(YieldIOException, _super);
function YieldIOException() {
_super.apply(this, arguments);
}
return YieldIOException;
})(YieldException);
exports.YieldIOException = YieldIOException;
var JavaException = (function () {
function JavaException(exception) {
this.exception = exception;
}
JavaException.prototype.method_catch_handler = function (rs, cf, top_of_stack) {
var _this = this;
var method = cf.method;
if (!top_of_stack && method.has_bytecode) {
cf.pc -= 3;
var op;
while (!(cf.pc <= 0 || (((op = method.code.opcodes[cf.pc]) != null) && op.name.match(/^invoke/)))) {
--cf.pc;
}
}
if (cf["native"]) {
if (cf.error != null) {
cf.runner = function () {
return cf.error(_this);
};
return true;
}
return false;
}
var exception_handlers = method.code.exception_handlers;
var ecls = this.exception.cls;
var handler = underscore.find(exception_handlers, function (eh) {
// XXX: Kludge. If the class is not loaded, then it is not possible for this to be the correct exception handler
return (eh.start_pc <= cf.pc && cf.pc < eh.end_pc) && (method.cls.loader.get_resolved_class(eh.catch_type, true) != null) && (eh.catch_type === "<any>" || ecls.is_castable(method.cls.loader.get_resolved_class(eh.catch_type)));
});
if (handler != null) {
debug("caught " + this.exception.cls.get_type() + " in " + method.full_signature() + " as subclass of " + handler.catch_type);
cf.stack = [this.exception];
cf.pc = handler.handler_pc;
return true;
}
// abrupt method invocation completion
debug("exception not caught, terminating " + method.full_signature());
return false;
};
JavaException.prototype.toplevel_catch_handler = function (rs) {
debug("\nUncaught " + this.exception.cls.get_type());
var msg = this.exception.get_field(rs, 'Ljava/lang/Throwable;detailMessage');
if (msg != null) {
debug("\t" + msg.jvm2js_str());
}
rs.push2(rs.curr_thread, this.exception);
var thread_cls = rs.get_bs_class('Ljava/lang/Thread;');
var dispatch_method = thread_cls.method_lookup(rs, 'dispatchUncaughtException(Ljava/lang/Throwable;)V');
dispatch_method.setup_stack(rs);
};
return JavaException;
})();
exports.JavaException = JavaException;
});
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
define('src/java_object',["require", "exports", './gLong', './util', "../vendor/underscore/underscore"], function(require, exports, __gLong__, __util__) {
/// <amd-dependency path="../vendor/underscore/underscore" />
var underscore = require('../vendor/underscore/underscore');
var gLong = __gLong__;
var util = __util__;
var JavaArray = (function () {
function JavaArray(rs, cls, obj) {
this.cls = cls;
this.ref = rs.high_oref++;
this.array = obj;
}
JavaArray.prototype.clone = function (rs) {
// note: we don't clone the type, because they're effectively immutable
return new JavaArray(rs, this.cls, underscore.clone(this.array));
};
JavaArray.prototype.get_field_from_offset = function (rs, offset) {
return this.array[offset.toInt()];
};
JavaArray.prototype.set_field_from_offset = function (rs, offset, value) {
this.array[offset.toInt()] = value;
};
JavaArray.prototype.toString = function () {
if (this.array.length <= 10) {
return "<" + this.cls.get_type() + " [" + this.array + "] (*" + this.ref + ")>";
}
return "<" + this.cls.get_type() + " of length " + this.array.length + " (*" + this.ref + ")>";
};
JavaArray.prototype.serialize = function (visited) {
if (visited[this.ref]) {
return "<*" + this.ref + ">";
}
visited[this.ref] = true;
function elem_serializer(f) {
if (!f)
return f;
if (typeof f.serialize !== "function")
return f;
return f.serialize(visited);
}
return {
'type': this.cls.get_type(),
'ref': this.ref,
'array': this.array.map(elem_serializer)
};
};
return JavaArray;
})();
exports.JavaArray = JavaArray;
var JavaObject = (function () {
function JavaObject(rs, cls, obj) {
this.cls = cls;
if (obj == null) {
obj = {};
}
this.ref = rs.high_oref++;
// Use default fields as a prototype.
this.fields = Object.create(this.cls.get_default_fields());
for (var field in obj) {
if (obj.hasOwnProperty(field)) {
this.fields[field] = obj[field];
}
}
}
JavaObject.prototype.clone = function (rs) {
// note: we don't clone the type, because they're effectively immutable
return new JavaObject(rs, this.cls, underscore.clone(this.fields));
};
JavaObject.prototype.set_field = function (rs, name, val) {
if (this.fields[name] !== undefined) {
this.fields[name] = val;
} else {
rs.java_throw(this.cls.loader.get_initialized_class('Ljava/lang/NoSuchFieldError;'), name);
}
};
JavaObject.prototype.get_field = function (rs, name) {
if (this.fields[name] !== undefined) {
return this.fields[name];
}
return rs.java_throw(this.cls.loader.get_initialized_class('Ljava/lang/NoSuchFieldError;'), name);
};
JavaObject.prototype.get_field_from_offset = function (rs, offset) {
var f = this._get_field_from_offset(rs, this.cls, offset.toInt());
if (f.field.access_flags['static']) {
return f.cls_obj.static_get(rs, f.field.name);
}
return this.get_field(rs, f.cls + f.field.name);
};
JavaObject.prototype._get_field_from_offset = function (rs, cls, offset) {
var classname = cls.get_type();
while (cls != null) {
var jco_ref = cls.get_class_object(rs).ref;
var f = cls.get_fields()[offset - jco_ref];
if (f != null) {
return {
field: f,
cls: cls.get_type(),
cls_obj: cls
};
}
cls = cls.get_super_class();
}
return rs.java_throw(this.cls.loader.get_initialized_class('Ljava/lang/NullPointerException;'), "field " + offset + " doesn't exist in class " + classname);
};
JavaObject.prototype.set_field_from_offset = function (rs, offset, value) {
var f = this._get_field_from_offset(rs, this.cls, offset.toInt());
if (f.field.access_flags['static']) {
f.cls_obj.static_put(rs, f.field.name, value);
} else {
this.set_field(rs, f.cls + f.field.name, value);
}
};
JavaObject.prototype.toString = function () {
if (this.cls.get_type() === 'Ljava/lang/String;')
return "<" + this.cls.get_type() + " '" + (this.jvm2js_str()) + "' (*" + this.ref + ")>";
return "<" + this.cls.get_type() + " (*" + this.ref + ")>";
};
JavaObject.prototype.serialize = function (visited) {
if (this.ref in visited) {
return "<*" + this.ref + ">";
}
visited[this.ref] = true;
var fields = {};
var _ref2 = this.fields;
for (var k in this.fields) {
var field = this.fields[k];
if (field && field.serialize) {
fields[k] = field.serialize(visited);
} else {
fields[k] = field;
}
}
return {
type: this.cls.get_type(),
ref: this.ref,
fields: fields
};
};
// Convert a Java String object into an equivalent JS one.
JavaObject.prototype.jvm2js_str = function () {
return util.chars2js_str(this.fields['Ljava/lang/String;value'], this.fields['Ljava/lang/String;offset'], this.fields['Ljava/lang/String;count']);
};
return JavaObject;
})();
exports.JavaObject = JavaObject;
var JavaThreadObject = (function (_super) {
__extends(JavaThreadObject, _super);
function JavaThreadObject(rs, cls, obj) {
if (cls == null) {
cls = {
get_type: (function () {
return 'Ljava/lang/Thread;';
}),
loader: rs.get_bs_cl(),
get_default_fields: (function () {
return null;
})
};
this.fake = true;
} else {
this.fake = false;
}
_super.call(this, rs, cls, obj);
this.$isAlive = true;
this.wakeup_time = null;
this.$park_count = 0;
this.$park_timeout = Infinity;
this.$meta_stack = rs.construct_callstack();
}
JavaThreadObject.prototype.clone = function (rs) {
return new JavaThreadObject(rs, this.cls, underscore.clone(this.fields));
};
return JavaThreadObject;
})(JavaObject);
exports.JavaThreadObject = JavaThreadObject;
var JavaClassObject = (function (_super) {
__extends(JavaClassObject, _super);
function JavaClassObject(rs, $cls) {
_super.call(this, rs, rs.get_bs_cl().get_resolved_class('Ljava/lang/Class;'));
this.$cls = $cls;
}
JavaClassObject.prototype.toString = function () {
return "<Class " + this.$cls.get_type() + " (*" + this.ref + ")>";
};
return JavaClassObject;
})(JavaObject);
exports.JavaClassObject = JavaClassObject;
// Each JavaClassLoaderObject is a unique ClassLoader.
var JavaClassLoaderObject = (function (_super) {
__extends(JavaClassLoaderObject, _super);
function JavaClassLoaderObject(rs, cls) {
_super.call(this, rs, cls);
this.$loader = rs.construct_cl(this);
}
JavaClassLoaderObject.prototype.serialize = function (visited) {
if (visited[this.ref]) {
return "<*" + this.ref + ">";
}
visited[this.ref] = true;
var fields = {};
for (var k in this.fields) {
var f = this.fields[k];
if (!f || (typeof f.serialize !== "function"))
fields[k] = f;
else
fields[k] = f.serialize(visited);
}
var loaded = {};
for (var type in this.$loader.loaded_classes) {
var vcls = this.$loader.loaded_classes[type];
loaded[type + "(" + vcls.getLoadState() + ")"] = vcls.loader.serialize(visited);
}
return {
type: this.cls.get_type(),
ref: this.ref,
fields: fields,
loaded: loaded
};
};
return JavaClassLoaderObject;
})(JavaObject);
exports.JavaClassLoaderObject = JavaClassLoaderObject;
function thread_name(rs, thread) {
return util.chars2js_str(thread.get_field(rs, 'Ljava/lang/Thread;name'));
}
exports.thread_name = thread_name;
});
define('src/jvm',["require", "exports", './logging'], function(require, exports, __logging__) {
///<reference path='../vendor/node.d.ts' />
var logging = __logging__;
var fs = typeof node !== "undefined" ? node.fs : require('fs');
var path = typeof node !== "undefined" ? node.path : require('path');
var trace = logging.trace;
var error = logging.error;
exports.show_NYI_natives = false;
exports.dump_state = false;
var vendor_path = typeof node !== "undefined" ? '/sys/vendor' : path.resolve(__dirname, '../vendor');
exports.system_properties;
function reset_system_properties() {
exports.system_properties = {
'java.class.path': [],
'java.home': "" + vendor_path + "/java_home",
'sun.boot.class.path': "" + vendor_path + "/classes",
'file.encoding': 'UTF-8',
'java.vendor': 'Doppio',
'java.version': '1.6',
'java.vendor.url': 'https://github.com/int3/doppio',
'java.class.version': '50.0',
'java.specification.version': '1.6',
'line.separator': '\n',
'file.separator': '/',
'path.separator': ':',
'user.dir': path.resolve('.'),
'user.home': '.',
'user.name': 'DoppioUser',
'os.name': 'doppio',
'os.arch': 'js',
'os.version': '0',
'java.vm.name': 'Doppio 64-bit VM',
'java.vm.vendor': 'Doppio Inc.',
'java.awt.headless': (typeof node === "undefined" || node === null).toString(),
'java.awt.graphicsenv': 'classes.awt.CanvasGraphicsEnvironment',
'useJavaUtilZip': 'true',
'jline.terminal': 'jline.UnsupportedTerminal'
};
}
exports.reset_system_properties = reset_system_properties;
// Read in a binary classfile asynchronously. Return an array of bytes.
function read_classfile(cls, cb, failure_cb) {
cls = cls.slice(1, -1);
var cpath = exports.system_properties['java.class.path'];
function try_get(i) {
fs.readFile(cpath[i] + cls + '.class', function (err, data) {
if (err) {
if (i + 1 == cpath.length) {
failure_cb(function () {
throw new Error("Error: No file found for class " + cls);
});
} else {
try_get(i + 1);
}
} else {
cb(data);
}
});
}
// We could launch them all at once, but we would need to ensure that we use
// the working version that occurs first in the classpath.
try_get(0);
}
exports.read_classfile = read_classfile;
function set_classpath(jcl_path, classpath) {
var dirs = classpath.split(':');
dirs.push(jcl_path);
var tmp_classpath = [];
for (var i = 0; i < dirs.length; i++) {
var cp = path.normalize(dirs[i]);
if (cp.charAt(cp.length - 1) !== '/') {
cp += '/';
}
// XXX: I'm not checking.
// if (fs.existsSync(cp))
tmp_classpath.push(cp);
}
this.system_properties['java.class.path'] = tmp_classpath;
}
exports.set_classpath = set_classpath;
function run_class(rs, class_name, cmdline_args, done_cb) {
var class_descriptor = "L" + class_name + ";";
var main_sig = 'main([Ljava/lang/String;)V';
var main_method = null;
function run_main() {
trace("run_main");
rs.run_until_finished((function () {
rs.async_op(function (resume_cb, except_cb) {
rs.get_bs_cl().initialize_class(rs, class_descriptor, function (cls) {
rs.init_args(cmdline_args);
return rs.run_until_finished(function () {
main_method = cls.method_lookup(rs, main_sig);
if (main_method != null) {
return;
}
return rs.async_op(function (resume_cb, except_cb) {
cls.resolve_method(rs, main_sig, function (m) {
main_method = m;
return except_cb(function () {
});
}, except_cb);
});
}, true, function (success) {
if (!(success && (main_method != null))) {
if (typeof done_cb === "function") {
done_cb(success);
}
}
return rs.run_until_finished((function () {
return main_method.setup_stack(rs);
}), false, function (success) {
if (typeof done_cb === "function") {
done_cb(success && !rs.unusual_termination);
}
});
});
}, except_cb);
});
}), true, done_cb);
}
;
function run_program() {
trace("run_program");
rs.run_until_finished((function () {
rs.init_threads();
}), true, function (success) {
if (!success) {
return;
}
if (rs.system_initialized != null) {
run_main();
} else {
rs.run_until_finished((function () {
rs.init_system_class();
}), true, function (success) {
if (!success) {
return;
}
run_main();
});
}
});
}
;
return rs.run_until_finished(function () {
return rs.async_op(function (resume_cb, except_cb) {
return rs.preinitialize_core_classes(run_program, function (e) {
// Error during preinitialization? Abort abort abort!
e();
});
});
}, true, function () {
});
}
exports.run_class = run_class;
exports.reset_system_properties();
});
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
define('src/ConstantPool',["require", "exports", './gLong', './util'], function(require, exports, __gLong__, __util__) {
var gLong = __gLong__;
var util = __util__;
var SimpleReference = (function () {
function SimpleReference(constant_pool, value) {
this.constant_pool = constant_pool;
this.value = value;
}
SimpleReference.from_bytes = function (bytes_array, constant_pool) {
var value = bytes_array.get_uint(2);
return new this(constant_pool, value);
};
SimpleReference.prototype.deref = function () {
var pool_obj = this.constant_pool[this.value];
return (typeof pool_obj.deref === "function" ? pool_obj.deref() : void 0) || pool_obj.value;
};
SimpleReference.size = 1;
return SimpleReference;
})();
exports.SimpleReference = SimpleReference;
var ClassReference = (function (_super) {
__extends(ClassReference, _super);
function ClassReference() {
_super.apply(this, arguments);
this.type = 'class';
}
ClassReference.prototype.deref = function () {
var pool_obj = this.constant_pool[this.value];
if (typeof pool_obj.deref === "function") {
return pool_obj.deref();
}
return util.typestr2descriptor(pool_obj.value);
};
return ClassReference;
})(SimpleReference);
exports.ClassReference = ClassReference;
var StringReference = (function (_super) {
__extends(StringReference, _super);
function StringReference(constant_pool, value) {
_super.call(this, constant_pool, value);
this.type = 'String';
}
return StringReference;
})(SimpleReference);
exports.StringReference = StringReference;
var AbstractMethodFieldReference = (function () {
function AbstractMethodFieldReference(constant_pool, value) {
this.constant_pool = constant_pool;
this.value = value;
}
AbstractMethodFieldReference.from_bytes = function (bytes_array, constant_pool) {
var class_ref = ClassReference.from_bytes(bytes_array, constant_pool);
var sig = SimpleReference.from_bytes(bytes_array, constant_pool);
return new this(constant_pool, {
class_ref: class_ref,
sig: sig
});
};
AbstractMethodFieldReference.prototype.deref = function () {
var sig = this.value.sig.deref();
return {
"class": this.value.class_ref.deref(),
sig: sig.name + sig.type
};
};
AbstractMethodFieldReference.size = 1;
return AbstractMethodFieldReference;
})();
exports.AbstractMethodFieldReference = AbstractMethodFieldReference;
var MethodReference = (function (_super) {
__extends(MethodReference, _super);
function MethodReference() {
_super.apply(this, arguments);
this.type = 'Method';
}
return MethodReference;
})(AbstractMethodFieldReference);
exports.MethodReference = MethodReference;
var InterfaceMethodReference = (function (_super) {
__extends(InterfaceMethodReference, _super);
function InterfaceMethodReference() {
_super.apply(this, arguments);
this.type = 'InterfaceMethod';
}
return InterfaceMethodReference;
})(AbstractMethodFieldReference);
exports.InterfaceMethodReference = InterfaceMethodReference;
var FieldReference = (function (_super) {
__extends(FieldReference, _super);
function FieldReference() {
_super.apply(this, arguments);
this.type = 'Field';
}
FieldReference.prototype.deref = function () {
var sig = this.value.sig.deref();
return {
"class": this.value.class_ref.deref(),
name: sig.name,
type: sig.type
};
};
return FieldReference;
})(AbstractMethodFieldReference);
exports.FieldReference = FieldReference;
var MethodSignature = (function () {
function MethodSignature(constant_pool, value) {
this.type = 'NameAndType';
this.constant_pool = constant_pool;
this.value = value;
}
MethodSignature.from_bytes = function (bytes_array, constant_pool) {
var meth_ref = StringReference.from_bytes(bytes_array, constant_pool);
var type_ref = StringReference.from_bytes(bytes_array, constant_pool);
return new this(constant_pool, {
meth_ref: meth_ref,
type_ref: type_ref
});
};
MethodSignature.prototype.deref = function () {
return {
name: this.value.meth_ref.deref(),
type: this.value.type_ref.deref()
};
};
MethodSignature.size = 1;
return MethodSignature;
})();
exports.MethodSignature = MethodSignature;
var ConstString = (function () {
function ConstString(value) {
this.type = 'Asciz';
this.value = value;
}
ConstString.from_bytes = function (bytes_array) {
var strlen = bytes_array.get_uint(2);
var value = util.bytes2str(bytes_array.read(strlen));
return new this(value);
};
ConstString.size = 1;
return ConstString;
})();
exports.ConstString = ConstString;
var ConstInt32 = (function () {
function ConstInt32(value) {
this.type = 'int';
this.value = value;
}
ConstInt32.from_bytes = function (bytes_array) {
var uint32 = bytes_array.get_uint(4);
var value = -(1 + ~uint32);
return new this(value);
};
ConstInt32.size = 1;
return ConstInt32;
})();
exports.ConstInt32 = ConstInt32;
var ConstFloat = (function () {
function ConstFloat(value) {
this.type = 'float';
this.value = value;
}
ConstFloat.from_bytes = function (bytes_array) {
var uint32 = bytes_array.get_uint(4);
var value = util.intbits2float(uint32 | 0);
return new this(value);
};
ConstFloat.size = 1;
return ConstFloat;
})();
exports.ConstFloat = ConstFloat;
var ConstLong = (function () {
function ConstLong(value) {
this.type = 'long';
this.value = value;
}
ConstLong.from_bytes = function (bytes_array) {
var high = bytes_array.get_uint(4);
var low = bytes_array.get_uint(4);
var value = gLong.fromBits(low, high);
return new this(value);
};
ConstLong.size = 2;
return ConstLong;
})();
exports.ConstLong = ConstLong;
var ConstDouble = (function () {
function ConstDouble(value) {
this.type = 'double';
this.value = value;
}
ConstDouble.from_bytes = function (bytes_array) {
var uint32_a = bytes_array.get_uint(4);
var uint32_b = bytes_array.get_uint(4);
return new this(util.longbits2double(uint32_a, uint32_b));
};
ConstDouble.size = 2;
return ConstDouble;
})();
exports.ConstDouble = ConstDouble;
var ConstantPool = (function () {
function ConstantPool() {
}
ConstantPool.prototype.parse = function (bytes_array) {
var constant_tags;
constant_tags = {
1: ConstString,
3: ConstInt32,
4: ConstFloat,
5: ConstLong,
6: ConstDouble,
7: ClassReference,
8: StringReference,
9: FieldReference,
10: MethodReference,
11: InterfaceMethodReference,
12: MethodSignature
};
this.cp_count = bytes_array.get_uint(2);
this.constant_pool = {};
var idx = 1;
while (idx < this.cp_count) {
var tag = bytes_array.get_uint(1);
if (!((1 <= tag && tag <= 12))) {
throw "invalid tag: " + tag;
}
var pool_obj = constant_tags[tag].from_bytes(bytes_array, this.constant_pool);
this.constant_pool[idx] = pool_obj;
idx += constant_tags[tag].size;
}
return bytes_array;
};
ConstantPool.prototype.get = function (idx) {
var _ref = this.constant_pool[idx];
if (_ref != null) {
return _ref;
} else {
throw new Error("Invalid constant_pool reference: " + idx);
}
};
ConstantPool.prototype.each = function (fn) {
var _results = [];
for (var i = 0, _ref = this.cp_count; i < _ref; ++i) {
if (i in this.constant_pool) {
_results.push(fn(i, this.constant_pool[i]));
}
}
return _results;
};
return ConstantPool;
})();
exports.ConstantPool = ConstantPool;
});
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
define('src/opcodes',["require", "exports", './gLong', './util', './exceptions', './java_object'], function(require, exports, __gLong__, __util__, __exceptions__, __java_object__) {
var gLong = __gLong__;
var util = __util__;
var exceptions = __exceptions__;
var JavaException = exceptions.JavaException;
var ReturnException = exceptions.ReturnException;
var java_object = __java_object__;
var JavaObject = java_object.JavaObject;
var JavaArray = java_object.JavaArray;
var thread_name = java_object.thread_name;
var JavaClassLoaderObject = java_object.JavaClassLoaderObject;
var Opcode = (function () {
function Opcode(name, byte_count, execute) {
this.name = name;
this.byte_count = byte_count || 0;
this.execute = execute || this._execute;
// Backup so we can reset caching between JVM invocations.
this.orig_execute = this.execute;
}
Opcode.prototype.take_args = function (code_array, constant_pool) {
this.args = [];
for (var i = 0; i < this.byte_count; i++) {
this.args.push(code_array.get_uint(1));
}
};
// called to provide opcode annotations for disassembly and vtrace
Opcode.prototype.annotate = function (idx, pool) {
return '';
};
// Used to reset any cached information between JVM invocations.
Opcode.prototype.reset_cache = function () {
if (this.execute !== this.orig_execute) {
this.execute = this.orig_execute;
}
};
Opcode.prototype._execute = function (rs) {
throw new Error("ERROR: Unimplemented opcode.");
};
// Increments the PC properly by the given offset.
// Subtracts the byte_count and 1 before setting the offset so that the outer
// loop can be simple.
Opcode.prototype.inc_pc = function (rs, offset) {
return rs.inc_pc(offset - 1 - this.byte_count);
};
Opcode.prototype.goto_pc = function (rs, new_pc) {
return rs.goto_pc(new_pc - 1 - this.byte_count);
};
return Opcode;
})();
exports.Opcode = Opcode;
var FieldOpcode = (function (_super) {
__extends(FieldOpcode, _super);
function FieldOpcode(name, execute) {
_super.call(this, name, 2, execute);
}
FieldOpcode.prototype.take_args = function (code_array, constant_pool) {
this.field_spec_ref = code_array.get_uint(2);
this.field_spec = constant_pool.get(this.field_spec_ref).deref();
};
FieldOpcode.prototype.annotate = function (idx, pool) {
var info = util.format_extra_info(pool.get(this.field_spec_ref));
return "\t#" + this.field_spec_ref + ";" + info;
};
return FieldOpcode;
})(Opcode);
exports.FieldOpcode = FieldOpcode;
var ClassOpcode = (function (_super) {
__extends(ClassOpcode, _super);
function ClassOpcode(name, execute) {
_super.call(this, name, 2, execute);
}
ClassOpcode.prototype.take_args = function (code_array, constant_pool) {
this.class_ref = code_array.get_uint(2);
this['class'] = constant_pool.get(this.class_ref).deref();
};
ClassOpcode.prototype.annotate = function (idx, pool) {
var info = util.format_extra_info(pool.get(this.class_ref));
return "\t#" + this.class_ref + ";" + info;
};
return ClassOpcode;
})(Opcode);
exports.ClassOpcode = ClassOpcode;
var InvokeOpcode = (function (_super) {
__extends(InvokeOpcode, _super);
function InvokeOpcode(name) {
_super.call(this, name, 2);
}
InvokeOpcode.prototype.take_args = function (code_array, constant_pool) {
this.method_spec_ref = code_array.get_uint(2);
this.method_spec = constant_pool.get(this.method_spec_ref).deref();
};
InvokeOpcode.prototype.annotate = function (idx, pool) {
var info = util.format_extra_info(pool.get(this.method_spec_ref));
return "\t#" + this.method_spec_ref + ";" + info;
};
InvokeOpcode.prototype._execute = function (rs) {
var cls = rs.get_class(this.method_spec["class"], true);
if (cls != null) {
var my_sf = rs.curr_frame();
var m = cls.method_lookup(rs, this.method_spec.sig);
if (m != null) {
if (m.setup_stack(rs) != null) {
my_sf.pc += 1 + this.byte_count;
return false;
}
} else {
var sig = this.method_spec.sig;
rs.async_op(function (resume_cb, except_cb) {
cls.resolve_method(rs, sig, (function () {
return resume_cb(undefined, undefined, true, false);
}), except_cb);
});
}
} else {
// Initialize our class and rerun opcode.
var classname = this.method_spec["class"];
rs.async_op(function (resume_cb, except_cb) {
rs.get_cl().initialize_class(rs, classname, (function () {
return resume_cb(undefined, undefined, true, false);
}), except_cb);
});
}
};
return InvokeOpcode;
})(Opcode);
exports.InvokeOpcode = InvokeOpcode;
function get_param_word_size(signature) {
var state = 'name';
var size = 0;
for (var i = 0; i < signature.length; i++) {
var c = signature[i];
switch (state) {
case 'name':
if (c === '(')
state = 'type';
break;
case 'type':
if (c === ')')
return size;
if (c === 'J' || c === 'D') {
size += 2;
} else {
++size;
}
if (c === 'L') {
state = 'class';
} else if (c === '[') {
state = 'array';
}
break;
case 'class':
if (c === ';')
state = 'type';
break;
case 'array':
if (c === 'L') {
state = 'class';
} else if (c !== '[') {
state = 'type';
}
}
}
}
var DynInvokeOpcode = (function (_super) {
__extends(DynInvokeOpcode, _super);
function DynInvokeOpcode() {
_super.apply(this, arguments);
}
DynInvokeOpcode.prototype.take_args = function (code_array, constant_pool) {
_super.prototype.take_args.call(this, code_array, constant_pool);
if (this.name === 'invokeinterface') {
this.count = code_array.get_uint(1);
code_array.skip(1);
this.byte_count += 2;
} else {
this.count = 1 + get_param_word_size(this.method_spec.sig);
}
this.cache = Object.create(null);
};
DynInvokeOpcode.prototype.annotate = function (idx, pool) {
var info = util.format_extra_info(pool.get(this.method_spec_ref));
var extra = '';
if (this.name === 'invokeinterface')
extra = ', ' + this.count;
return "\t#" + this.method_spec_ref + extra + ";" + info;
};
DynInvokeOpcode.prototype._execute = function (rs) {
var cls = rs.get_class(this.method_spec["class"], true);
if (cls != null) {
var my_sf = rs.curr_frame();
var stack = my_sf.stack;
var obj = stack[stack.length - this.count];
var cls_obj = rs.check_null(obj).cls;
var m = cls_obj.method_lookup(rs, this.method_spec.sig);
if (m != null) {
if (m.setup_stack(rs) != null) {
my_sf.pc += 1 + this.byte_count;
return false;
}
} else {
var sig = this.method_spec.sig;
rs.async_op(function (resume_cb, except_cb) {
cls_obj.resolve_method(rs, sig, (function () {
return resume_cb(undefined, undefined, true, false);
}), except_cb);
});
}
} else {
// Initialize our class and rerun opcode.
var classname = this.method_spec["class"];
rs.async_op(function (resume_cb, except_cb) {
rs.get_cl().initialize_class(rs, classname, (function () {
return resume_cb(undefined, undefined, true, false);
}), except_cb);
});
}
};
return DynInvokeOpcode;
})(InvokeOpcode);
exports.DynInvokeOpcode = DynInvokeOpcode;
var LoadConstantOpcode = (function (_super) {
__extends(LoadConstantOpcode, _super);
function LoadConstantOpcode() {
_super.apply(this, arguments);
}
LoadConstantOpcode.prototype.take_args = function (code_array, constant_pool) {
this.constant_ref = code_array.get_uint(this.byte_count);
this.constant = constant_pool.get(this.constant_ref);
var ctype = this.constant.type;
if (ctype === 'String' || ctype === 'class') {
this.str_constant = constant_pool.get(this.constant.value);
}
};
LoadConstantOpcode.prototype.annotate = function (idx, pool) {
var ctype = this.constant.type;
var anno = "\t#" + this.constant_ref + ";\t// " + this.constant.type + " ";
if (ctype === 'String' || ctype === 'class')
return anno + util.escape_whitespace(this.constant.deref());
return anno + this.constant.value;
};
LoadConstantOpcode.prototype._execute = function (rs) {
switch (this.constant.type) {
case 'String':
rs.push(rs.init_string(this.str_constant.value, true));
break;
case 'class':
// XXX: Make this rewrite itself to cache the jclass object.
// Fetch the jclass object and push it on to the stack. Do not rerun
// this opcode.
var cdesc = util.typestr2descriptor(this.str_constant.value);
rs.async_op(function (resume_cb, except_cb) {
rs.get_cl().resolve_class(rs, cdesc, (function (cls) {
return resume_cb(cls.get_class_object(rs), undefined, true);
}), except_cb);
});
break;
default:
if (this.name === 'ldc2_w')
rs.push2(this.constant.value, null);
else
rs.push(this.constant.value);
}
return true;
};
return LoadConstantOpcode;
})(Opcode);
exports.LoadConstantOpcode = LoadConstantOpcode;
var BranchOpcode = (function (_super) {
__extends(BranchOpcode, _super);
function BranchOpcode(name, execute) {
_super.call(this, name, 2, execute);
}
BranchOpcode.prototype.take_args = function (code_array, constant_pool) {
this.offset = code_array.get_int(this.byte_count);
};
BranchOpcode.prototype.annotate = function (idx, pool) {
return "\t" + (idx + this.offset);
};
return BranchOpcode;
})(Opcode);
exports.BranchOpcode = BranchOpcode;
var GotoOpcode = (function (_super) {
__extends(GotoOpcode, _super);
function GotoOpcode(name, byte_count) {
_super.call(this, name);
this.byte_count = byte_count;
}
GotoOpcode.prototype._execute = function (rs) {
this.inc_pc(rs, this.offset);
return true;
};
return GotoOpcode;
})(BranchOpcode);
exports.GotoOpcode = GotoOpcode;
var JSROpcode = (function (_super) {
__extends(JSROpcode, _super);
function JSROpcode() {
_super.apply(this, arguments);
}
JSROpcode.prototype._execute = function (rs) {
rs.push(rs.curr_pc() + this.byte_count + 1);
this.inc_pc(rs, this.offset);
return true;
};
return JSROpcode;
})(GotoOpcode);
exports.JSROpcode = JSROpcode;
var UnaryBranchOpcode = (function (_super) {
__extends(UnaryBranchOpcode, _super);
function UnaryBranchOpcode(name, cmp) {
_super.call(this, name);
this.cmp = cmp;
}
UnaryBranchOpcode.prototype._execute = function (rs) {
if (this.cmp(rs.pop())) {
this.inc_pc(rs, this.offset);
}
return true;
};
return UnaryBranchOpcode;
})(BranchOpcode);
exports.UnaryBranchOpcode = UnaryBranchOpcode;
var BinaryBranchOpcode = (function (_super) {
__extends(BinaryBranchOpcode, _super);
function BinaryBranchOpcode(name, cmp) {
_super.call(this, name);
this.cmp = cmp;
}
BinaryBranchOpcode.prototype._execute = function (rs) {
var v2 = rs.pop();
var v1 = rs.pop();
if (this.cmp(v1, v2)) {
this.inc_pc(rs, this.offset);
}
return true;
};
return BinaryBranchOpcode;
})(BranchOpcode);
exports.BinaryBranchOpcode = BinaryBranchOpcode;
var PushOpcode = (function (_super) {
__extends(PushOpcode, _super);
function PushOpcode() {
_super.apply(this, arguments);
}
PushOpcode.prototype.take_args = function (code_array, constant_pool) {
this.value = code_array.get_int(this.byte_count);
};
PushOpcode.prototype.annotate = function (idx, pool) {
return "\t" + this.value;
};
PushOpcode.prototype._execute = function (rs) {
rs.push(this.value);
return true;
};
return PushOpcode;
})(Opcode);
exports.PushOpcode = PushOpcode;
var IIncOpcode = (function (_super) {
__extends(IIncOpcode, _super);
function IIncOpcode() {
_super.apply(this, arguments);
}
IIncOpcode.prototype.take_args = function (code_array, constant_pool, wide) {
var arg_size;
if (wide) {
this.name += "_w";
arg_size = 2;
this.byte_count = 5;
} else {
arg_size = 1;
this.byte_count = 2;
}
this.index = code_array.get_uint(arg_size);
this["const"] = code_array.get_int(arg_size);
};
IIncOpcode.prototype.annotate = function (idx, pool) {
return "\t" + this.index + ", " + this["const"];
};
IIncOpcode.prototype._execute = function (rs) {
var v = rs.cl(this.index) + this["const"];
rs.put_cl(this.index, v | 0);
return true;
};
return IIncOpcode;
})(Opcode);
exports.IIncOpcode = IIncOpcode;
var LoadOpcode = (function (_super) {
__extends(LoadOpcode, _super);
function LoadOpcode() {
_super.apply(this, arguments);
}
LoadOpcode.prototype.take_args = function (code_array, constant_pool) {
// sneaky hack, works for name =~ /.load_\d/
this.var_num = parseInt(this.name[6]);
};
LoadOpcode.prototype._execute = function (rs) {
rs.push(rs.cl(this.var_num));
return true;
};
return LoadOpcode;
})(Opcode);
exports.LoadOpcode = LoadOpcode;
// For category 2 types.
var LoadOpcode2 = (function (_super) {
__extends(LoadOpcode2, _super);
function LoadOpcode2() {
_super.apply(this, arguments);
}
LoadOpcode2.prototype._execute = function (rs) {
rs.push2(rs.cl(this.var_num), null);
return true;
};
return LoadOpcode2;
})(LoadOpcode);
exports.LoadOpcode2 = LoadOpcode2;
var LoadVarOpcode = (function (_super) {
__extends(LoadVarOpcode, _super);
function LoadVarOpcode() {
_super.apply(this, arguments);
}
LoadVarOpcode.prototype.take_args = function (code_array, constant_pool, wide) {
if (wide) {
this.name += "_w";
this.byte_count = 3;
this.var_num = code_array.get_uint(2);
} else {
this.byte_count = 1;
this.var_num = code_array.get_uint(1);
}
};
LoadVarOpcode.prototype.annotate = function (idx, pool) {
return "\t" + this.var_num;
};
return LoadVarOpcode;
})(LoadOpcode);
exports.LoadVarOpcode = LoadVarOpcode;
var LoadVarOpcode2 = (function (_super) {
__extends(LoadVarOpcode2, _super);
function LoadVarOpcode2() {
_super.apply(this, arguments);
}
LoadVarOpcode2.prototype._execute = function (rs) {
rs.push2(rs.cl(this.var_num), null);
return true;
};
return LoadVarOpcode2;
})(LoadVarOpcode);
exports.LoadVarOpcode2 = LoadVarOpcode2;
var StoreOpcode = (function (_super) {
__extends(StoreOpcode, _super);
function StoreOpcode() {
_super.apply(this, arguments);
}
StoreOpcode.prototype.take_args = function (code_array, constant_pool) {
// sneaky hack, works for name =~ /.store_\d/
this.var_num = parseInt(this.name[7]);
};
StoreOpcode.prototype._execute = function (rs) {
rs.put_cl(this.var_num, rs.pop());
return true;
};
return StoreOpcode;
})(Opcode);
exports.StoreOpcode = StoreOpcode;
// For category 2 types.
var StoreOpcode2 = (function (_super) {
__extends(StoreOpcode2, _super);
function StoreOpcode2() {
_super.apply(this, arguments);
}
StoreOpcode2.prototype._execute = function (rs) {
rs.put_cl2(this.var_num, rs.pop2());
return true;
};
return StoreOpcode2;
})(StoreOpcode);
exports.StoreOpcode2 = StoreOpcode2;
var StoreVarOpcode = (function (_super) {
__extends(StoreVarOpcode, _super);
function StoreVarOpcode() {
_super.apply(this, arguments);
}
StoreVarOpcode.prototype.take_args = function (code_array, constant_pool, wide) {
if (wide) {
this.name += "_w";
this.byte_count = 3;
this.var_num = code_array.get_uint(2);
} else {
this.byte_count = 1;
this.var_num = code_array.get_uint(1);
}
};
StoreVarOpcode.prototype.annotate = function (idx, pool) {
return "\t" + this.var_num;
};
return StoreVarOpcode;
})(StoreOpcode);
exports.StoreVarOpcode = StoreVarOpcode;
var StoreVarOpcode2 = (function (_super) {
__extends(StoreVarOpcode2, _super);
function StoreVarOpcode2() {
_super.apply(this, arguments);
}
StoreVarOpcode2.prototype._execute = function (rs) {
rs.put_cl2(this.var_num, rs.pop2());
return true;
};
return StoreVarOpcode2;
})(LoadVarOpcode);
exports.StoreVarOpcode2 = StoreVarOpcode2;
var LookupSwitchOpcode = (function (_super) {
__extends(LookupSwitchOpcode, _super);
function LookupSwitchOpcode() {
_super.apply(this, arguments);
}
LookupSwitchOpcode.prototype.annotate = function (idx, pool) {
var rv = "{\n";
for (var match in this.offsets) {
var offset = this.offsets[match];
rv += ("\t\t" + match + ": " + (idx + offset) + ";\n");
}
return rv + "\t\tdefault: " + (idx + this._default) + " }";
};
LookupSwitchOpcode.prototype.take_args = function (code_array, constant_pool) {
// account for padding that ensures alignment
var padding_size = (4 - code_array.pos() % 4) % 4;
code_array.skip(padding_size);
this._default = code_array.get_int(4);
var npairs = code_array.get_int(4);
this.offsets = {};
for (var i = 0; i < npairs; ++i) {
var match = code_array.get_int(4);
this.offsets[match] = code_array.get_int(4);
}
this.byte_count = padding_size + 8 * (npairs + 1);
};
LookupSwitchOpcode.prototype._execute = function (rs) {
var offset = this.offsets[rs.pop()];
if (offset) {
this.inc_pc(rs, offset);
} else {
this.inc_pc(rs, this._default);
}
return true;
};
return LookupSwitchOpcode;
})(BranchOpcode);
exports.LookupSwitchOpcode = LookupSwitchOpcode;
var TableSwitchOpcode = (function (_super) {
__extends(TableSwitchOpcode, _super);
function TableSwitchOpcode() {
_super.apply(this, arguments);
}
TableSwitchOpcode.prototype.take_args = function (code_array, constant_pool) {
// account for padding that ensures alignment
var padding_size = (4 - code_array.pos() % 4) % 4;
code_array.skip(padding_size);
this._default = code_array.get_int(4);
var low = code_array.get_int(4);
var high = code_array.get_int(4);
this.offsets = {};
var total_offsets = high - low + 1;
for (var i = 0; i < total_offsets; ++i) {
this.offsets[low + i] = code_array.get_int(4);
}
this.byte_count = padding_size + 12 + 4 * total_offsets;
};
return TableSwitchOpcode;
})(LookupSwitchOpcode);
exports.TableSwitchOpcode = TableSwitchOpcode;
var NewArray_arr_types = {
4: 'Z',
5: 'C',
6: 'F',
7: 'D',
8: 'B',
9: 'S',
10: 'I',
11: 'J'
};
var NewArrayOpcode = (function (_super) {
__extends(NewArrayOpcode, _super);
function NewArrayOpcode(name) {
_super.call(this, name, 1);
}
NewArrayOpcode.prototype.take_args = function (code_array, constant_pool) {
this.element_type = NewArray_arr_types[code_array.get_uint(1)];
};
NewArrayOpcode.prototype.annotate = function (idx, pool) {
return "\t" + util.internal2external[this.element_type];
};
NewArrayOpcode.prototype._execute = function (rs) {
rs.push(rs.heap_newarray(this.element_type, rs.pop()));
return true;
};
return NewArrayOpcode;
})(Opcode);
exports.NewArrayOpcode = NewArrayOpcode;
var MultiArrayOpcode = (function (_super) {
__extends(MultiArrayOpcode, _super);
function MultiArrayOpcode(name) {
_super.call(this, name, 3);
}
MultiArrayOpcode.prototype.take_args = function (code_array, constant_pool) {
this.class_ref = code_array.get_uint(2);
this.class_descriptor = constant_pool.get(this.class_ref).deref();
this.dim = code_array.get_uint(1);
};
MultiArrayOpcode.prototype.annotate = function (idx, pool) {
return "\t#" + this.class_ref + ", " + this.dim + ";";
};
MultiArrayOpcode.prototype._execute = function (rs) {
var _this = this;
var cls = rs.get_class(this.class_descriptor, true);
if (cls == null) {
rs.async_op(function (resume_cb, except_cb) {
rs.get_cl().initialize_class(rs, _this.class_descriptor, (function (class_file) {
return resume_cb(undefined, undefined, true, false);
}), except_cb);
});
return true;
}
// cls is loaded. Create a new execute function to avoid this overhead.
var new_execute = function (rs) {
var counts = rs.curr_frame().stack.splice(-this.dim, this.dim);
rs.push(rs.heap_multinewarray(_this.class_descriptor, counts));
};
new_execute.call(this, rs);
this.execute = new_execute;
return true;
};
return MultiArrayOpcode;
})(Opcode);
exports.MultiArrayOpcode = MultiArrayOpcode;
var ArrayLoadOpcode = (function (_super) {
__extends(ArrayLoadOpcode, _super);
function ArrayLoadOpcode() {
_super.apply(this, arguments);
}
ArrayLoadOpcode.prototype._execute = function (rs) {
var idx = rs.pop();
var obj = rs.check_null(rs.pop());
var len = obj.array.length;
if (idx < 0 || idx >= len) {
var err_cls = rs.get_bs_class('Ljava/lang/ArrayIndexOutOfBoundsException;');
rs.java_throw(err_cls, idx + " not in length " + len + " array of type " + obj.cls.get_type());
}
rs.push(obj.array[idx]);
if (this.name[0] === 'l' || this.name[0] === 'd') {
rs.push(null);
}
return true;
};
return ArrayLoadOpcode;
})(Opcode);
exports.ArrayLoadOpcode = ArrayLoadOpcode;
var ArrayStoreOpcode = (function (_super) {
__extends(ArrayStoreOpcode, _super);
function ArrayStoreOpcode() {
_super.apply(this, arguments);
}
ArrayStoreOpcode.prototype._execute = function (rs) {
var value = (this.name[0] === 'l' || this.name[0] === 'd') ? rs.pop2() : rs.pop();
var idx = rs.pop();
var obj = rs.check_null(rs.pop());
var len = obj.array.length;
if (idx < 0 || idx >= len) {
var err_cls = rs.get_bs_class('Ljava/lang/ArrayIndexOutOfBoundsException;');
rs.java_throw(err_cls, idx + " not in length " + len + " array of type " + obj.cls.get_type());
}
obj.array[idx] = value;
return true;
};
return ArrayStoreOpcode;
})(Opcode);
exports.ArrayStoreOpcode = ArrayStoreOpcode;
var ReturnOpcode = (function (_super) {
__extends(ReturnOpcode, _super);
function ReturnOpcode() {
_super.apply(this, arguments);
}
ReturnOpcode.prototype._execute = function (rs) {
var cf = rs.meta_stack().pop();
rs.push(cf.stack[0]);
rs.should_return = true;
return true;
};
return ReturnOpcode;
})(Opcode);
exports.ReturnOpcode = ReturnOpcode;
var ReturnOpcode2 = (function (_super) {
__extends(ReturnOpcode2, _super);
function ReturnOpcode2() {
_super.apply(this, arguments);
}
ReturnOpcode2.prototype._execute = function (rs) {
var cf = rs.meta_stack().pop();
rs.push2(cf.stack[0], null);
rs.should_return = true;
return true;
};
return ReturnOpcode2;
})(Opcode);
exports.ReturnOpcode2 = ReturnOpcode2;
var VoidReturnOpcode = (function (_super) {
__extends(VoidReturnOpcode, _super);
function VoidReturnOpcode() {
_super.apply(this, arguments);
}
VoidReturnOpcode.prototype._execute = function (rs) {
rs.meta_stack().pop();
rs.should_return = true;
return true;
};
return VoidReturnOpcode;
})(Opcode);
exports.VoidReturnOpcode = VoidReturnOpcode;
function monitorenter(rs, monitor, inst) {
if (monitor == null) {
rs.java_throw(rs.get_bs_class('Ljava/lang/NullPointerException;'), 'Cannot enter a null monitor.');
}
var locked_thread = rs.lock_refs[monitor.ref];
if (locked_thread != null) {
if (locked_thread === rs.curr_thread) {
// increment lock counter, to only unlock at zero
rs.lock_counts[monitor.ref]++;
} else {
if (inst != null) {
inst.inc_pc(rs, 1);
} else {
rs.inc_pc(1);
}
// dummy, to be popped by rs.yield
rs.meta_stack().push({});
rs.wait(monitor);
return false;
}
} else {
// this lock not held by any thread
rs.lock_refs[monitor.ref] = rs.curr_thread;
rs.lock_counts[monitor.ref] = 1;
}
return true;
}
exports.monitorenter = monitorenter;
function monitorexit(rs, monitor) {
var locked_thread = rs.lock_refs[monitor.ref];
if (locked_thread == null)
return;
if (locked_thread === rs.curr_thread) {
rs.lock_counts[monitor.ref]--;
if (rs.lock_counts[monitor.ref] === 0) {
delete rs.lock_refs[monitor.ref];
if (rs.waiting_threads[monitor.ref] != null) {
rs.waiting_threads[monitor.ref] = [];
}
}
} else {
var err_cls = rs.get_bs_class('Ljava/lang/IllegalMonitorStateException;');
rs.java_throw(err_cls, "Thread " + thread_name(rs, rs.curr_thread) + " tried to monitorexit on lock held by thread " + thread_name(rs, locked_thread) + ".");
}
}
exports.monitorexit = monitorexit;
// These objects are used as prototypes for the parsed instructions in the classfile.
// Opcodes are in order, indexed by their binary representation.
exports.opcodes = [
new Opcode('nop', 0, function (rs) {
}),
new Opcode('aconst_null', 0, (function (rs) {
return rs.push(null);
})),
new Opcode('iconst_m1', 0, (function (rs) {
return rs.push(-1);
})),
new Opcode('iconst_0', 0, (function (rs) {
return rs.push(0);
})),
new Opcode('iconst_1', 0, (function (rs) {
return rs.push(1);
})),
new Opcode('iconst_2', 0, (function (rs) {
return rs.push(2);
})),
new Opcode('iconst_3', 0, (function (rs) {
return rs.push(3);
})),
new Opcode('iconst_4', 0, (function (rs) {
return rs.push(4);
})),
new Opcode('iconst_5', 0, (function (rs) {
return rs.push(5);
})),
new Opcode('lconst_0', 0, (function (rs) {
return rs.push2(gLong.ZERO, null);
})),
new Opcode('lconst_1', 0, (function (rs) {
return rs.push2(gLong.ONE, null);
})),
new Opcode('fconst_0', 0, (function (rs) {
return rs.push(0);
})),
new Opcode('fconst_1', 0, (function (rs) {
return rs.push(1);
})),
new Opcode('fconst_2', 0, (function (rs) {
return rs.push(2);
})),
new Opcode('dconst_0', 0, (function (rs) {
return rs.push2(0, null);
})),
new Opcode('dconst_1', 0, (function (rs) {
return rs.push2(1, null);
})),
new PushOpcode('bipush', 1),
new PushOpcode('sipush', 2),
new LoadConstantOpcode('ldc', 1),
new LoadConstantOpcode('ldc_w', 2),
new LoadConstantOpcode('ldc2_w', 2),
new LoadVarOpcode('iload'),
new LoadVarOpcode2('lload'),
new LoadVarOpcode('fload'),
new LoadVarOpcode2('dload'),
new LoadVarOpcode('aload'),
new LoadOpcode('iload_0'),
new LoadOpcode('iload_1'),
new LoadOpcode('iload_2'),
new LoadOpcode('iload_3'),
new LoadOpcode2('lload_0'),
new LoadOpcode2('lload_1'),
new LoadOpcode2('lload_2'),
new LoadOpcode2('lload_3'),
new LoadOpcode('fload_0'),
new LoadOpcode('fload_1'),
new LoadOpcode('fload_2'),
new LoadOpcode('fload_3'),
new LoadOpcode2('dload_0'),
new LoadOpcode2('dload_1'),
new LoadOpcode2('dload_2'),
new LoadOpcode2('dload_3'),
new LoadOpcode('aload_0'),
new LoadOpcode('aload_1'),
new LoadOpcode('aload_2'),
new LoadOpcode('aload_3'),
new ArrayLoadOpcode('iaload'),
new ArrayLoadOpcode('laload'),
new ArrayLoadOpcode('faload'),
new ArrayLoadOpcode('daload'),
new ArrayLoadOpcode('aaload'),
new ArrayLoadOpcode('baload'),
new ArrayLoadOpcode('caload'),
new ArrayLoadOpcode('saload'),
new StoreVarOpcode('istore'),
new StoreVarOpcode2('lstore'),
new StoreVarOpcode('fstore'),
new StoreVarOpcode2('dstore'),
new StoreVarOpcode('astore'),
new StoreOpcode('istore_0'),
new StoreOpcode('istore_1'),
new StoreOpcode('istore_2'),
new StoreOpcode('istore_3'),
new StoreOpcode2('lstore_0'),
new StoreOpcode2('lstore_1'),
new StoreOpcode2('lstore_2'),
new StoreOpcode2('lstore_3'),
new StoreOpcode('fstore_0'),
new StoreOpcode('fstore_1'),
new StoreOpcode('fstore_2'),
new StoreOpcode('fstore_3'),
new StoreOpcode2('dstore_0'),
new StoreOpcode2('dstore_1'),
new StoreOpcode2('dstore_2'),
new StoreOpcode2('dstore_3'),
new StoreOpcode('astore_0'),
new StoreOpcode('astore_1'),
new StoreOpcode('astore_2'),
new StoreOpcode('astore_3'),
new ArrayStoreOpcode('iastore'),
new ArrayStoreOpcode('lastore'),
new ArrayStoreOpcode('fastore'),
new ArrayStoreOpcode('dastore'),
new ArrayStoreOpcode('aastore'),
new ArrayStoreOpcode('bastore'),
new ArrayStoreOpcode('castore'),
new ArrayStoreOpcode('sastore'),
new Opcode('pop', 0, (function (rs) {
return rs.pop();
})),
new Opcode('pop2', 0, (function (rs) {
return rs.pop2();
})),
new Opcode('dup', 0, function (rs) {
var v = rs.pop();
rs.push2(v, v);
}),
new Opcode('dup_x1', 0, function (rs) {
var v1 = rs.pop();
var v2 = rs.pop();
rs.push_array([v1, v2, v1]);
}),
new Opcode('dup_x2', 0, function (rs) {
var v1 = rs.pop();
var v2 = rs.pop();
var v3 = rs.pop();
rs.push_array([v1, v3, v2, v1]);
}),
new Opcode('dup2', 0, function (rs) {
var v1 = rs.pop();
var v2 = rs.pop();
rs.push_array([v2, v1, v2, v1]);
}),
new Opcode('dup2_x1', 0, function (rs) {
var v1 = rs.pop();
var v2 = rs.pop();
var v3 = rs.pop();
rs.push_array([v2, v1, v3, v2, v1]);
}),
new Opcode('dup2_x2', 0, function (rs) {
var v1 = rs.pop();
var v2 = rs.pop();
var v3 = rs.pop();
var v4 = rs.pop();
rs.push_array([v2, v1, v4, v3, v2, v1]);
}),
new Opcode('swap', 0, function (rs) {
var v1 = rs.pop();
var v2 = rs.pop();
rs.push2(v2, v1);
}),
new Opcode('iadd', 0, (function (rs) {
return rs.push((rs.pop() + rs.pop()) | 0);
})),
new Opcode('ladd', 0, (function (rs) {
return rs.push2(rs.pop2().add(rs.pop2()), null);
})),
new Opcode('fadd', 0, (function (rs) {
return rs.push(util.wrap_float(rs.pop() + rs.pop()));
})),
new Opcode('dadd', 0, (function (rs) {
return rs.push2(rs.pop2() + rs.pop2(), null);
})),
new Opcode('isub', 0, (function (rs) {
return rs.push((-rs.pop() + rs.pop()) | 0);
})),
new Opcode('lsub', 0, (function (rs) {
return rs.push2(rs.pop2().negate().add(rs.pop2()), null);
})),
new Opcode('fsub', 0, (function (rs) {
return rs.push(util.wrap_float(-rs.pop() + rs.pop()));
})),
new Opcode('dsub', 0, (function (rs) {
return rs.push2(-rs.pop2() + rs.pop2(), null);
})),
new Opcode('imul', 0, (function (rs) {
return rs.push(Math['imul'](rs.pop(), rs.pop()));
})),
new Opcode('lmul', 0, (function (rs) {
return rs.push2(rs.pop2().multiply(rs.pop2()), null);
})),
new Opcode('fmul', 0, (function (rs) {
return rs.push(util.wrap_float(rs.pop() * rs.pop()));
})),
new Opcode('dmul', 0, (function (rs) {
return rs.push2(rs.pop2() * rs.pop2(), null);
})),
new Opcode('idiv', 0, function (rs) {
var v = rs.pop();
rs.push(util.int_div(rs, rs.pop(), v));
}),
new Opcode('ldiv', 0, function (rs) {
var v = rs.pop2();
rs.push2(util.long_div(rs, rs.pop2(), v), null);
}),
new Opcode('fdiv', 0, function (rs) {
var a = rs.pop();
rs.push(util.wrap_float(rs.pop() / a));
}),
new Opcode('ddiv', 0, function (rs) {
var v = rs.pop2();
rs.push2(rs.pop2() / v, null);
}),
new Opcode('irem', 0, function (rs) {
var v2 = rs.pop();
rs.push(util.int_mod(rs, rs.pop(), v2));
}),
new Opcode('lrem', 0, function (rs) {
var v2 = rs.pop2();
rs.push2(util.long_mod(rs, rs.pop2(), v2), null);
}),
new Opcode('frem', 0, function (rs) {
var b = rs.pop();
rs.push(rs.pop() % b);
}),
new Opcode('drem', 0, function (rs) {
var v2 = rs.pop2();
rs.push2(rs.pop2() % v2, null);
}),
new Opcode('ineg', 0, (function (rs) {
return rs.push(-rs.pop() | 0);
})),
new Opcode('lneg', 0, (function (rs) {
return rs.push2(rs.pop2().negate(), null);
})),
new Opcode('fneg', 0, (function (rs) {
return rs.push(-rs.pop());
})),
new Opcode('dneg', 0, (function (rs) {
return rs.push2(-rs.pop2(), null);
})),
new Opcode('ishl', 0, function (rs) {
var s = rs.pop();
rs.push(rs.pop() << s);
}),
new Opcode('lshl', 0, function (rs) {
var s = rs.pop();
rs.push2(rs.pop2().shiftLeft(gLong.fromInt(s)), null);
}),
new Opcode('ishr', 0, function (rs) {
var s = rs.pop();
rs.push(rs.pop() >> s);
}),
new Opcode('lshr', 0, function (rs) {
var s = rs.pop();
rs.push2(rs.pop2().shiftRight(gLong.fromInt(s)), null);
}),
new Opcode('iushr', 0, function (rs) {
var s = rs.pop();
rs.push(rs.pop() >>> s);
}),
new Opcode('lushr', 0, function (rs) {
var s = rs.pop();
rs.push2(rs.pop2().shiftRightUnsigned(gLong.fromInt(s)), null);
}),
new Opcode('iand', 0, (function (rs) {
return rs.push(rs.pop() & rs.pop());
})),
new Opcode('land', 0, (function (rs) {
return rs.push2(rs.pop2().and(rs.pop2()), null);
})),
new Opcode('ior', 0, (function (rs) {
return rs.push(rs.pop() | rs.pop());
})),
new Opcode('lor', 0, (function (rs) {
return rs.push2(rs.pop2().or(rs.pop2()), null);
})),
new Opcode('ixor', 0, (function (rs) {
return rs.push(rs.pop() ^ rs.pop());
})),
new Opcode('lxor', 0, (function (rs) {
return rs.push2(rs.pop2().xor(rs.pop2()), null);
})),
new IIncOpcode('iinc'),
new Opcode('i2l', 0, (function (rs) {
return rs.push2(gLong.fromInt(rs.pop()), null);
})),
new Opcode('i2f', 0, function (rs) {
}),
new Opcode('i2d', 0, (function (rs) {
return rs.push(null);
})),
new Opcode('l2i', 0, (function (rs) {
return rs.push(rs.pop2().toInt());
})),
new Opcode('l2f', 0, (function (rs) {
return rs.push(rs.pop2().toNumber());
})),
new Opcode('l2d', 0, (function (rs) {
return rs.push2(rs.pop2().toNumber(), null);
})),
new Opcode('f2i', 0, (function (rs) {
return rs.push(util.float2int(rs.pop()));
})),
new Opcode('f2l', 0, (function (rs) {
return rs.push2(gLong.fromNumber(rs.pop()), null);
})),
new Opcode('f2d', 0, (function (rs) {
return rs.push(null);
})),
new Opcode('d2i', 0, (function (rs) {
return rs.push(util.float2int(rs.pop2()));
})),
new Opcode('d2l', 0, function (rs) {
var d_val = rs.pop2();
if (d_val === Number.POSITIVE_INFINITY) {
rs.push2(gLong.MAX_VALUE, null);
} else if (d_val === Number.NEGATIVE_INFINITY) {
rs.push2(gLong.MIN_VALUE, null);
} else {
rs.push2(gLong.fromNumber(d_val), null);
}
}),
new Opcode('d2f', 0, (function (rs) {
return rs.push(util.wrap_float(rs.pop2()));
})),
new Opcode('i2b', 0, (function (rs) {
return rs.push((rs.pop() << 24) >> 24);
})),
new Opcode('i2c', 0, (function (rs) {
return rs.push(rs.pop() & 0xFFFF);
})),
new Opcode('i2s', 0, (function (rs) {
return rs.push((rs.pop() << 16) >> 16);
})),
new Opcode('lcmp', 0, function (rs) {
var v2 = rs.pop2();
rs.push(rs.pop2().compare(v2));
}),
new Opcode('fcmpl', 0, function (rs) {
var v2 = rs.pop();
var res = util.cmp(rs.pop(), v2);
if (res == null)
rs.push(-1);
else
rs.push(res);
}),
new Opcode('fcmpg', 0, function (rs) {
var v2 = rs.pop();
var res = util.cmp(rs.pop(), v2);
if (res == null)
rs.push(1);
else
rs.push(res);
}),
new Opcode('dcmpl', 0, function (rs) {
var v2 = rs.pop2();
var res = util.cmp(rs.pop2(), v2);
if (res == null)
rs.push(-1);
else
rs.push(res);
}),
new Opcode('dcmpg', 0, function (rs) {
var v2 = rs.pop2();
var res = util.cmp(rs.pop2(), v2);
if (res == null)
rs.push(1);
else
rs.push(res);
}),
new UnaryBranchOpcode('ifeq', (function (v) {
return v === 0;
})),
new UnaryBranchOpcode('ifne', (function (v) {
return v !== 0;
})),
new UnaryBranchOpcode('iflt', (function (v) {
return v < 0;
})),
new UnaryBranchOpcode('ifge', (function (v) {
return v >= 0;
})),
new UnaryBranchOpcode('ifgt', (function (v) {
return v > 0;
})),
new UnaryBranchOpcode('ifle', (function (v) {
return v <= 0;
})),
new BinaryBranchOpcode('if_icmpeq', (function (v1, v2) {
return v1 === v2;
})),
new BinaryBranchOpcode('if_icmpne', (function (v1, v2) {
return v1 !== v2;
})),
new BinaryBranchOpcode('if_icmplt', (function (v1, v2) {
return v1 < v2;
})),
new BinaryBranchOpcode('if_icmpge', (function (v1, v2) {
return v1 >= v2;
})),
new BinaryBranchOpcode('if_icmpgt', (function (v1, v2) {
return v1 > v2;
})),
new BinaryBranchOpcode('if_icmple', (function (v1, v2) {
return v1 <= v2;
})),
new BinaryBranchOpcode('if_acmpeq', (function (v1, v2) {
return v1 === v2;
})),
new BinaryBranchOpcode('if_acmpne', (function (v1, v2) {
return v1 !== v2;
})),
new GotoOpcode('goto', 2),
new JSROpcode('jsr', 2),
new Opcode('ret', 1, function (rs) {
this.goto_pc(rs, rs.cl(this.args[0]));
}),
new TableSwitchOpcode('tableswitch'),
new LookupSwitchOpcode('lookupswitch'),
new ReturnOpcode('ireturn'),
new ReturnOpcode2('lreturn'),
new ReturnOpcode('freturn'),
new ReturnOpcode2('dreturn'),
new ReturnOpcode('areturn'),
new VoidReturnOpcode('return'),
new FieldOpcode('getstatic', function (rs) {
var _this = this;
var desc = this.field_spec.class;
var ref_cls = rs.get_class(desc, true);
var new_execute;
if (this.field_spec.type == 'J' || this.field_spec.type == 'D') {
new_execute = function (rs) {
return rs.push2(_this.cls.static_get(rs, _this.field_spec.name), null);
};
} else {
new_execute = function (rs) {
return rs.push(_this.cls.static_get(rs, _this.field_spec.name));
};
}
if (ref_cls != null) {
var cls_type = ref_cls.field_lookup(rs, this.field_spec.name).cls.get_type();
this.cls = rs.get_class(cls_type, true);
if (this.cls != null) {
new_execute.call(this, rs);
this.execute = new_execute;
} else {
rs.async_op(function (resume_cb, except_cb) {
rs.get_cl().initialize_class(rs, cls_type, (function (class_file) {
resume_cb(undefined, undefined, true, false);
}), except_cb);
});
}
} else {
rs.async_op(function (resume_cb, except_cb) {
rs.get_cl().initialize_class(rs, desc, (function (class_file) {
resume_cb(undefined, undefined, true, false);
}), except_cb);
});
}
}),
new FieldOpcode('putstatic', function (rs) {
var _this = this;
var desc = this.field_spec.class;
var ref_cls = rs.get_class(desc, true);
var new_execute;
if (this.field_spec.type == 'J' || this.field_spec.type == 'D') {
new_execute = function (rs) {
return _this.cls.static_put(rs, _this.field_spec.name, rs.pop2());
};
} else {
new_execute = function (rs) {
return _this.cls.static_put(rs, _this.field_spec.name, rs.pop());
};
}
if (ref_cls != null) {
var cls_type = ref_cls.field_lookup(rs, this.field_spec.name).cls.get_type();
this.cls = rs.get_class(cls_type, true);
if (this.cls != null) {
new_execute.call(this, rs);
this.execute = new_execute;
} else {
rs.async_op(function (resume_cb, except_cb) {
rs.get_cl().initialize_class(rs, cls_type, (function (class_file) {
resume_cb(undefined, undefined, true, false);
}), except_cb);
});
}
return;
}
rs.async_op(function (resume_cb, except_cb) {
rs.get_cl().initialize_class(rs, desc, (function (class_file) {
resume_cb(undefined, undefined, true, false);
}), except_cb);
});
}),
new FieldOpcode('getfield', function (rs) {
var desc = this.field_spec.class;
// Check if the object is null; if we do not do this before get_class, then
// we might try to get a class that we have not initialized!
var obj = rs.check_null(rs.peek());
// cls is guaranteed to be in the inheritance hierarchy of obj, so it must be
// initialized. However, it may not be loaded in the current class's
// ClassLoader...
var cls = rs.get_class(desc, true);
if (cls != null) {
var field = cls.field_lookup(rs, this.field_spec.name);
var name = field.cls.get_type() + this.field_spec.name;
var new_execute;
if (this.field_spec.type == 'J' || this.field_spec.type == 'D') {
new_execute = function (rs) {
return rs.push2(rs.check_null(rs.pop()).get_field(rs, name), null);
};
} else {
new_execute = function (rs) {
return rs.push(rs.check_null(rs.pop()).get_field(rs, name));
};
}
new_execute.call(this, rs);
this.execute = new_execute;
return;
}
// Alright, tell this class's ClassLoader to load the class.
rs.async_op(function (resume_cb, except_cb) {
rs.get_cl().resolve_class(rs, desc, (function () {
resume_cb(undefined, undefined, true, false);
}), except_cb);
});
}),
new FieldOpcode('putfield', function (rs) {
// Check if the object is null; if we do not do this before get_class, then
// we might try to get a class that we have not initialized!
var desc = this.field_spec.class;
var is_cat_2 = (this.field_spec.type == 'J' || this.field_spec.type == 'D');
rs.check_null(rs.peek(is_cat_2 ? 2 : 1));
// cls is guaranteed to be in the inheritance hierarchy of obj, so it must be
// initialized. However, it may not be loaded in the current class's
// ClassLoader...
var cls_obj = rs.get_class(desc, true);
if (cls_obj != null) {
var field = cls_obj.field_lookup(rs, this.field_spec.name);
var name = field.cls.get_type() + this.field_spec.name;
var new_execute;
if (is_cat_2) {
new_execute = function (rs) {
var val = rs.pop2();
rs.check_null(rs.pop()).set_field(rs, name, val);
};
} else {
new_execute = function (rs) {
var val = rs.pop();
rs.check_null(rs.pop()).set_field(rs, name, val);
};
}
new_execute.call(this, rs);
this.execute = new_execute;
} else {
// Alright, tell this class's ClassLoader to load the class.
rs.async_op(function (resume_cb, except_cb) {
rs.get_cl().resolve_class(rs, desc, (function () {
resume_cb(undefined, undefined, true, false);
}), except_cb);
});
}
}),
new DynInvokeOpcode('invokevirtual'),
new InvokeOpcode('invokespecial'),
new InvokeOpcode('invokestatic'),
new DynInvokeOpcode('invokeinterface'),
null,
new ClassOpcode('new', function (rs) {
var _this = this;
var desc = this.class;
this.cls = rs.get_class(desc, true);
if (this.cls != null) {
if (this.cls.is_castable(rs.get_bs_cl().get_resolved_class('Ljava/lang/ClassLoader;', true))) {
rs.push(new JavaClassLoaderObject(rs, this.cls));
this.execute = function (rs) {
return rs.push(new JavaClassLoaderObject(rs, _this.cls));
};
} else if (this.cls.is_castable(rs.get_bs_cl().get_resolved_class('Ljava/lang/Thread;', true))) {
rs.push(new java_object.JavaThreadObject(rs, this.cls));
this.execute = function (rs) {
return rs.push(new java_object.JavaThreadObject(rs, _this.cls));
};
} else {
rs.push(new JavaObject(rs, this.cls));
this.execute = function (rs) {
return rs.push(new JavaObject(rs, _this.cls));
};
}
} else {
rs.async_op(function (resume_cb, except_cb) {
var success_fn = function (class_file) {
var obj;
if (class_file.is_castable(rs.get_bs_cl().get_resolved_class('Ljava/lang/ClassLoader;', true))) {
obj = new JavaClassLoaderObject(rs, class_file);
} else if (class_file.is_castable(rs.get_bs_cl().get_resolved_class('Ljava/lang/Thread;', true))) {
obj = new java_object.JavaThreadObject(rs, class_file);
} else {
obj = new JavaObject(rs, class_file);
}
resume_cb(obj, undefined, true);
};
rs.get_cl().initialize_class(rs, desc, success_fn, except_cb);
});
}
}),
new NewArrayOpcode('newarray'),
new ClassOpcode('anewarray', function (rs) {
var desc = this.class;
var cls = rs.get_cl().get_resolved_class(desc, true);
if (cls != null) {
var new_execute = function (rs) {
return rs.push(rs.heap_newarray(desc, rs.pop()));
};
new_execute.call(this, rs);
this.execute = new_execute;
} else {
rs.async_op(function (resume_cb, except_cb) {
rs.get_cl().resolve_class(rs, desc, (function (class_file) {
resume_cb(undefined, undefined, true, false);
}), except_cb);
});
}
}),
new Opcode('arraylength', 0, (function (rs) {
return rs.push(rs.check_null(rs.pop()).array.length);
})),
new Opcode('athrow', 0, function (rs) {
throw new JavaException(rs.pop());
}),
new ClassOpcode('checkcast', function (rs) {
var desc = this.class;
this.cls = rs.get_cl().get_resolved_class(desc, true);
if (this.cls != null) {
var new_execute = function (rs) {
var o = rs.peek();
if ((o != null) && !o.cls.is_castable(this.cls)) {
var target_class = this.cls.toExternalString();
var candidate_class = o.cls.toExternalString();
var err_cls = rs.get_bs_class('Ljava/lang/ClassCastException;');
rs.java_throw(err_cls, candidate_class + " cannot be cast to " + target_class);
}
};
new_execute.call(this, rs);
this.execute = new_execute;
return;
}
rs.async_op(function (resume_cb, except_cb) {
rs.get_cl().resolve_class(rs, desc, (function () {
return resume_cb(undefined, undefined, true, false);
}), except_cb);
});
}),
new ClassOpcode('instanceof', function (rs) {
var desc = this.class;
this.cls = rs.get_cl().get_resolved_class(desc, true);
if (this.cls != null) {
var new_execute = function (rs) {
var o = rs.pop();
rs.push(o != null ? o.cls.is_castable(this.cls) + 0 : 0);
};
new_execute.call(this, rs);
this.execute = new_execute;
return;
}
rs.async_op(function (resume_cb, except_cb) {
rs.get_cl().resolve_class(rs, desc, (function () {
return resume_cb(undefined, undefined, true, false);
}), except_cb);
});
}),
new Opcode('monitorenter', 0, function (rs) {
if (!exports.monitorenter(rs, rs.peek(), this)) {
throw ReturnException;
}
rs.pop();
}),
new Opcode('monitorexit', 0, (function (rs) {
return exports.monitorexit(rs, rs.pop());
})),
null,
new MultiArrayOpcode('multianewarray'),
new UnaryBranchOpcode('ifnull', (function (v) {
return v == null;
})),
new UnaryBranchOpcode('ifnonnull', (function (v) {
return v != null;
})),
new GotoOpcode('goto_w', 4),
new JSROpcode('jsr_w', 4)
];
});
define('src/attributes',["require", "exports", './util', './opcodes'], function(require, exports, __util__, __opcodes__) {
var util = __util__;
var opcodes = __opcodes__;
var ExceptionHandler = (function () {
function ExceptionHandler() {
this.name = 'ExceptionHandler';
}
ExceptionHandler.prototype.parse = function (bytes_array, constant_pool) {
this.start_pc = bytes_array.get_uint(2);
this.end_pc = bytes_array.get_uint(2);
this.handler_pc = bytes_array.get_uint(2);
var cti = bytes_array.get_uint(2);
this.catch_type = cti === 0 ? "<any>" : constant_pool.get(cti).deref();
};
return ExceptionHandler;
})();
exports.ExceptionHandler = ExceptionHandler;
var Code = (function () {
function Code() {
this.name = 'Code';
}
Code.prototype.parse = function (bytes_array, constant_pool) {
this.constant_pool = constant_pool;
this.max_stack = bytes_array.get_uint(2);
this.max_locals = bytes_array.get_uint(2);
this.code_len = bytes_array.get_uint(4);
if (this.code_len === 0) {
(typeof RELEASE !== "undefined" && RELEASE !== null) || (function () {
throw "Code.parse error: Code length is zero";
})();
}
this._code_array = bytes_array.splice(this.code_len);
this.opcodes = null;
var except_len = bytes_array.get_uint(2);
this.exception_handlers = [];
for (var i = 0; i < except_len; i++) {
var eh = new ExceptionHandler();
this.exception_handlers.push(eh);
eh.parse(bytes_array, constant_pool);
}
// yes, there are even attrs on attrs. BWOM... BWOM...
this.attrs = exports.make_attributes(bytes_array, constant_pool);
this.run_stamp = 0;
};
Code.prototype.parse_code = function () {
this.opcodes = new Array(this.code_len);
while (this._code_array.has_bytes()) {
var op_index = this._code_array.pos();
var c = this._code_array.get_uint(1);
var wide = c === 196;
if (wide) {
// wide opcode needs to be handled specially
c = this._code_array.get_uint(1);
}
if (opcodes.opcodes[c] == null) {
(typeof RELEASE !== "undefined" && RELEASE !== null) || (function () {
throw "unknown opcode code: " + c;
})();
}
var op = Object.create(opcodes.opcodes[c]);
op.take_args(this._code_array, this.constant_pool, wide);
this.opcodes[op_index] = op;
}
this._code_array.rewind();
};
Code.prototype.each_opcode = function (fn) {
for (var i = 0; i < this.code_len; i++) {
if (this.opcodes[i] != null) {
fn(i, this.opcodes[i]);
}
}
};
Code.prototype.get_attribute = function (name) {
for (var i = 0; i < this.attrs.length; i++) {
var attr = this.attrs[i];
if (attr.name === name) {
return attr;
}
}
return null;
};
return Code;
})();
exports.Code = Code;
var LineNumberTable = (function () {
function LineNumberTable() {
this.name = 'LineNumberTable';
}
LineNumberTable.prototype.parse = function (bytes_array, constant_pool) {
this.entries = [];
var lnt_len = bytes_array.get_uint(2);
for (var i = 0; i < lnt_len; i++) {
var spc = bytes_array.get_uint(2);
var ln = bytes_array.get_uint(2);
this.entries.push({
'start_pc': spc,
'line_number': ln
});
}
};
LineNumberTable.prototype.disassemblyOutput = function () {
var rv = " LineNumberTable:\n";
for (var i = 0; i < this.entries.length; i++) {
var entry = this.entries[i];
rv += " line " + entry.line_number + ": " + entry.start_pc + "\n";
}
return rv;
};
return LineNumberTable;
})();
exports.LineNumberTable = LineNumberTable;
var SourceFile = (function () {
function SourceFile() {
this.name = 'SourceFile';
}
SourceFile.prototype.parse = function (bytes_array, constant_pool) {
this.filename = constant_pool.get(bytes_array.get_uint(2)).value;
};
return SourceFile;
})();
exports.SourceFile = SourceFile;
var StackMapTable = (function () {
function StackMapTable() {
this.name = 'StackMapTable';
}
StackMapTable.prototype.parse = function (bytes_array, constant_pool) {
this.num_entries = bytes_array.get_uint(2);
this.entries = [];
for (var i = 0; i < this.num_entries; i++) {
this.entries.push(this.parse_entries(bytes_array, constant_pool));
}
};
StackMapTable.prototype.parse_entries = function (bytes_array, constant_pool) {
var frame_type = bytes_array.get_uint(1);
if ((0 <= frame_type && frame_type < 64)) {
return {
frame_type: frame_type,
frame_name: 'same'
};
} else if ((64 <= frame_type && frame_type < 128)) {
return {
frame_type: frame_type,
frame_name: 'same_locals_1_stack_item',
stack: [this.parse_verification_type_info(bytes_array, constant_pool)]
};
} else if ((128 <= frame_type && frame_type < 247)) {
// reserved for future use
} else if (frame_type === 247) {
return {
frame_type: frame_type,
frame_name: 'same_locals_1_stack_item_frame_extended',
offset_delta: bytes_array.get_uint(2),
stack: [this.parse_verification_type_info(bytes_array, constant_pool)]
};
} else if ((248 <= frame_type && frame_type < 251)) {
return {
frame_type: frame_type,
frame_name: 'chop',
offset_delta: [bytes_array.get_uint(2)]
};
} else if (frame_type === 251) {
return {
frame_type: frame_type,
frame_name: 'same_frame_extended',
offset_delta: [bytes_array.get_uint(2)]
};
} else if ((252 <= frame_type && frame_type < 255)) {
var offset_delta = bytes_array.get_uint(2);
var locals = [];
for (var i = 0; i < frame_type - 251; i++) {
locals.push(this.parse_verification_type_info(bytes_array, constant_pool));
}
return {
frame_type: frame_type,
frame_name: 'append',
offset_delta: offset_delta,
locals: locals
};
} else if (frame_type === 255) {
var offset_delta = bytes_array.get_uint(2);
var num_locals = bytes_array.get_uint(2);
locals = [];
for (var i = 0; i < num_locals; i++) {
locals.push(this.parse_verification_type_info(bytes_array, constant_pool));
}
var num_stack_items = bytes_array.get_uint(2);
var stack = [];
for (var i = 0; i < num_stack_items; i++) {
stack.push(this.parse_verification_type_info(bytes_array, constant_pool));
}
return {
frame_type: frame_type,
frame_name: 'full_frame',
offset_delta: offset_delta,
num_locals: num_locals,
locals: locals,
num_stack_items: num_stack_items,
stack: stack
};
}
};
StackMapTable.prototype.parse_verification_type_info = function (bytes_array, constant_pool) {
var tag = bytes_array.get_uint(1);
if (tag === 7) {
var cls = constant_pool.get(bytes_array.get_uint(2)).deref();
return 'class ' + (/\w/.test(cls[0]) ? util.descriptor2typestr(cls) : "\"" + cls + "\"");
} else if (tag === 8) {
return 'uninitialized ' + bytes_array.get_uint(2);
} else {
var tag_to_type = ['bogus', 'int', 'float', 'double', 'long', 'null', 'this', 'object', 'uninitialized'];
return tag_to_type[tag];
}
};
StackMapTable.prototype.disassemblyOutput = function () {
var rv = " StackMapTable: number_of_entries = " + this.num_entries + "\n";
for (var i = 0; i < this.entries.length; i++) {
var entry = this.entries[i];
rv += " frame_type = " + entry.frame_type + " /* " + entry.frame_name + " */\n";
if (entry['offset_delta'] != null) {
rv += " offset_delta = " + entry['offset_delta'] + "\n";
}
if (entry['locals'] != null) {
rv += " locals = [ " + (entry['locals'].join(', ')) + " ]\n";
}
if (entry['stack'] != null) {
rv += " stack = [ " + (entry['stack'].join(', ')) + " ]\n";
}
}
return rv;
};
return StackMapTable;
})();
exports.StackMapTable = StackMapTable;
var LocalVariableTable = (function () {
function LocalVariableTable() {
this.name = 'LocalVariableTable';
}
LocalVariableTable.prototype.parse = function (bytes_array, constant_pool) {
this.num_entries = bytes_array.get_uint(2);
this.entries = [];
for (var i = 0; i < this.num_entries; i++) {
this.entries.push(this.parse_entries(bytes_array, constant_pool));
}
};
LocalVariableTable.prototype.parse_entries = function (bytes_array, constant_pool) {
return {
start_pc: bytes_array.get_uint(2),
length: bytes_array.get_uint(2),
name: constant_pool.get(bytes_array.get_uint(2)).value,
descriptor: constant_pool.get(bytes_array.get_uint(2)).value,
ref: bytes_array.get_uint(2)
};
};
LocalVariableTable.prototype.disassemblyOutput = function () {
var rv = " LocalVariableTable:\n Start Length Slot Name Signature\n";
for (var i = 0; i < this.num_entries; i++) {
var entry = this.entries[i];
rv += " " + entry.start_pc + " " + entry.length + " " + entry.ref;
rv += "" + entry.name + " " + entry.descriptor + "\n";
}
return rv;
};
return LocalVariableTable;
})();
exports.LocalVariableTable = LocalVariableTable;
var Exceptions = (function () {
function Exceptions() {
this.name = 'Exceptions';
}
Exceptions.prototype.parse = function (bytes_array, constant_pool) {
this.num_exceptions = bytes_array.get_uint(2);
var exc_refs = [];
for (var i = 0; i < this.num_exceptions; i++) {
exc_refs.push(bytes_array.get_uint(2));
}
this.exceptions = exc_refs.map(function (ref) {
return constant_pool.get(ref).deref();
});
};
return Exceptions;
})();
exports.Exceptions = Exceptions;
var InnerClasses = (function () {
function InnerClasses() {
this.name = 'InnerClasses';
}
InnerClasses.prototype.parse = function (bytes_array, constant_pool) {
var num_classes = bytes_array.get_uint(2);
this.classes = [];
for (var i = 0; i < num_classes; i++) {
this.classes.push(this.parse_class(bytes_array, constant_pool));
}
};
InnerClasses.prototype.parse_class = function (bytes_array, constant_pool) {
return {
inner_info_index: bytes_array.get_uint(2),
outer_info_index: bytes_array.get_uint(2),
inner_name_index: bytes_array.get_uint(2),
inner_access_flags: bytes_array.get_uint(2)
};
};
return InnerClasses;
})();
exports.InnerClasses = InnerClasses;
var ConstantValue = (function () {
function ConstantValue() {
this.name = 'ConstantValue';
}
ConstantValue.prototype.parse = function (bytes_array, constant_pool) {
this.ref = bytes_array.get_uint(2);
var valref = constant_pool.get(this.ref);
this.value = (typeof valref.deref === "function") ? valref.deref() : valref.value;
};
return ConstantValue;
})();
exports.ConstantValue = ConstantValue;
var Synthetic = (function () {
function Synthetic() {
this.name = 'Synthetic';
}
Synthetic.prototype.parse = function (bytes_array, constant_pool) {
};
return Synthetic;
})();
exports.Synthetic = Synthetic;
var Deprecated = (function () {
function Deprecated() {
this.name = 'Deprecated';
}
Deprecated.prototype.parse = function (bytes_array, constant_pool) {
};
return Deprecated;
})();
exports.Deprecated = Deprecated;
var Signature = (function () {
function Signature() {
this.name = 'Signature';
}
Signature.prototype.parse = function (bytes_array, constant_pool, attr_len) {
this.raw_bytes = bytes_array.read(attr_len);
var ref = util.read_uint(this.raw_bytes);
this.sig = constant_pool.get(ref).value;
};
return Signature;
})();
exports.Signature = Signature;
var RuntimeVisibleAnnotations = (function () {
function RuntimeVisibleAnnotations() {
this.name = 'RuntimeVisibleAnnotations';
}
RuntimeVisibleAnnotations.prototype.parse = function (bytes_array, constant_pool, attr_len) {
this.raw_bytes = bytes_array.read(attr_len);
};
return RuntimeVisibleAnnotations;
})();
exports.RuntimeVisibleAnnotations = RuntimeVisibleAnnotations;
var AnnotationDefault = (function () {
function AnnotationDefault() {
this.name = 'AnnotationDefault';
}
AnnotationDefault.prototype.parse = function (bytes_array, constant_pool, attr_len) {
this.raw_bytes = bytes_array.read(attr_len);
};
return AnnotationDefault;
})();
exports.AnnotationDefault = AnnotationDefault;
var EnclosingMethod = (function () {
function EnclosingMethod() {
this.name = 'EnclosingMethod';
}
EnclosingMethod.prototype.parse = function (bytes_array, constant_pool) {
this.enc_class = constant_pool.get(bytes_array.get_uint(2)).deref();
var method_ref = bytes_array.get_uint(2);
if (method_ref > 0) {
this.enc_method = constant_pool.get(method_ref).deref();
}
};
return EnclosingMethod;
})();
exports.EnclosingMethod = EnclosingMethod;
function make_attributes(bytes_array, constant_pool) {
var attr_types = {
'Code': Code,
'LineNumberTable': LineNumberTable,
'SourceFile': SourceFile,
'StackMapTable': StackMapTable,
'LocalVariableTable': LocalVariableTable,
'ConstantValue': ConstantValue,
'Exceptions': Exceptions,
'InnerClasses': InnerClasses,
'Synthetic': Synthetic,
'Deprecated': Deprecated,
'Signature': Signature,
'RuntimeVisibleAnnotations': RuntimeVisibleAnnotations,
'AnnotationDefault': AnnotationDefault,
'EnclosingMethod': EnclosingMethod
};
var num_attrs = bytes_array.get_uint(2);
var attrs = [];
for (var i = 0; i < num_attrs; i++) {
var name = constant_pool.get(bytes_array.get_uint(2)).value;
var attr_len = bytes_array.get_uint(4);
if (attr_types[name] != null) {
var attr = new attr_types[name]();
var old_len = bytes_array.size();
attr.parse(bytes_array, constant_pool, attr_len);
var new_len = bytes_array.size();
if (old_len - new_len !== attr_len) {
bytes_array.skip(attr_len - old_len + new_len);
}
attrs.push(attr);
} else {
// we must silently ignore other attrs
bytes_array.skip(attr_len);
}
}
return attrs;
}
exports.make_attributes = make_attributes;
});
define('src/natives',["require", "exports", './gLong', './util', './java_object', './exceptions', './logging', './jvm'], function(require, exports, __gLong__, __util__, __java_object__, __exceptions__, __logging__, __JVM__) {
var gLong = __gLong__;
var util = __util__;
var java_object = __java_object__;
var thread_name = java_object.thread_name, JavaObject = java_object.JavaObject, JavaArray = java_object.JavaArray;
var exceptions = __exceptions__;
var logging = __logging__;
var debug = logging.debug, error = logging.error, trace = logging.trace;
var JVM = __JVM__;
var path = typeof node !== "undefined" ? node.path : require('path');
var fs = typeof node !== "undefined" ? node.fs : require('fs');
// XXX: Avoids a tough circular dependency
// ClassData->methods->natives->...
// Dependency occurs due to instanceof checks.
var ReferenceClassData, PrimitiveClassData, ArrayClassData;
exports.instantiated;
exports.instantiated = false;
function instantiate(rcd, pcd, acd) {
ReferenceClassData = rcd;
PrimitiveClassData = pcd;
ArrayClassData = acd;
}
exports.instantiate = instantiate;
function get_property(rs, jvm_key, _default) {
var jvm, key, val;
if (_default == null) {
_default = null;
}
key = jvm_key.jvm2js_str();
jvm = jvm != null ? jvm : require('./jvm');
val = jvm.system_properties[key];
if (key === 'java.class.path') {
return rs.init_string(val.slice(0, val.length - 1).join(':'));
}
if (val != null) {
return rs.init_string(val, true);
} else {
return _default;
}
}
function o(fn_name, fn) {
return {
fn_name: fn_name,
fn: fn
};
}
exports.trapped_methods = {
java: {
lang: {
ref: {
Reference: [o('<clinit>()V', function (rs) {
})]
},
String: [
o('hashCode()I', function (rs, _this) {
var chars, count, hash, i, offset, _i;
hash = _this.get_field(rs, 'Ljava/lang/String;hash');
if (hash === 0) {
offset = _this.get_field(rs, 'Ljava/lang/String;offset');
chars = _this.get_field(rs, 'Ljava/lang/String;value').array;
count = _this.get_field(rs, 'Ljava/lang/String;count');
for (i = _i = 0; _i < count; i = _i += 1) {
hash = (hash * 31 + chars[offset++]) | 0;
}
_this.set_field(rs, 'Ljava/lang/String;hash', hash);
}
return hash;
})
],
System: [
o('loadLibrary(L!/!/String;)V', function (rs, lib_name) {
var lib = lib_name.jvm2js_str();
if (lib !== 'zip' && lib !== 'net' && lib !== 'nio' && lib !== 'awt' && lib !== 'fontmanager') {
return rs.java_throw((rs.get_bs_class('Ljava/lang/UnsatisfiedLinkError;')), "no " + lib + " in java.library.path");
}
}),
o('adjustPropertiesForBackwardCompatibility(L!/util/Properties;)V', function (rs) {
}),
o('getProperty(L!/!/String;)L!/!/String;', get_property),
o('getProperty(L!/!/String;L!/!/String;)L!/!/String;', get_property)
],
Terminator: [o('setup()V', function (rs) {
})]
},
util: {
concurrent: {
atomic: {
AtomicInteger: [
o('<clinit>()V', function (rs) {
}),
o('compareAndSet(II)Z', function (rs, _this, expect, update) {
_this.set_field(rs, 'Ljava/util/concurrent/atomic/AtomicInteger;value', update);
return true;
})
]
}
}
},
nio: {
Bits: [
o('byteOrder()L!/!/ByteOrder;', function (rs) {
var cls = rs.get_bs_class('Ljava/nio/ByteOrder;');
return cls.static_get(rs, 'LITTLE_ENDIAN');
}),
o('copyToByteArray(JLjava/lang/Object;JJ)V', function (rs, srcAddr, dst, dstPos, length) {
unsafe_memcpy(rs, null, srcAddr, dst, dstPos, length);
})
],
charset: {
Charset$3: [
o('run()L!/lang/Object;', function (rs) {
return null;
})
]
}
}
}
};
function doPrivileged(rs, action) {
var my_sf = rs.curr_frame();
var m = action.cls.method_lookup(rs, 'run()Ljava/lang/Object;');
if (m != null) {
if (!m.access_flags["static"]) {
rs.push(action);
}
m.setup_stack(rs);
my_sf.runner = function () {
var rv = rs.pop();
rs.meta_stack().pop();
rs.push(rv);
};
throw exceptions.ReturnException;
} else {
rs.async_op(function (resume_cb, except_cb) {
action.cls.resolve_method(rs, 'run()Ljava/lang/Object;', (function () {
rs.meta_stack().push({});
resume_cb();
}), except_cb);
});
}
}
function stat_file(fname, cb) {
fs.stat(fname, function (err, stat) {
if (err != null) {
cb(null);
} else {
cb(stat);
}
});
}
function arraycopy_no_check(src, src_pos, dest, dest_pos, length) {
var j = dest_pos;
var end = src_pos + length;
for (var i = src_pos; i < end; i++) {
dest.array[j++] = src.array[i];
}
}
function arraycopy_check(rs, src, src_pos, dest, dest_pos, length) {
var j = dest_pos;
var end = src_pos + length;
var dest_comp_cls = dest.cls.get_component_class();
for (var i = src_pos; i < end; i++) {
if (src.array[i] === null || src.array[i].cls.is_castable(dest_comp_cls)) {
dest.array[j] = src.array[i];
} else {
var exc_cls = rs.get_bs_class('Ljava/lang/ArrayStoreException;');
rs.java_throw(exc_cls, 'Array element in src cannot be cast to dest array type.');
}
j++;
}
}
function unsafe_memcpy(rs, src_base, src_offset_l, dest_base, dest_offset_l, num_bytes_l) {
var num_bytes = num_bytes_l.toNumber();
if (src_base != null) {
var src_offset = src_offset_l.toNumber();
if (dest_base != null) {
return arraycopy_no_check(src_base, src_offset, dest_base, dest_offset_l.toNumber(), num_bytes);
} else {
var dest_addr = rs.block_addr(dest_offset_l);
if (typeof DataView !== "undefined" && DataView !== null) {
for (var i = 0; i < num_bytes; i++) {
rs.mem_blocks[dest_addr].setInt8(i, src_base.array[src_offset + i]);
}
} else {
for (var i = 0; i < num_bytes; i++) {
rs.mem_blocks[dest_addr + i] = src_base.array[src_offset + i];
}
}
}
} else {
var src_addr = rs.block_addr(src_offset_l);
if (dest_base != null) {
var dest_offset = dest_offset_l.toNumber();
if (typeof DataView !== "undefined" && DataView !== null) {
for (var i = 0; i < num_bytes; i++) {
dest_base.array[dest_offset + i] = rs.mem_blocks[src_addr].getInt8(i);
}
} else {
for (var i = 0; i < num_bytes; i++) {
dest_base.array[dest_offset + i] = rs.mem_blocks[src_addr + i];
}
}
} else {
var dest_addr = rs.block_addr(dest_offset_l);
if (typeof DataView !== "undefined" && DataView !== null) {
for (var i = 0; i < num_bytes; i++) {
rs.mem_blocks[dest_addr].setInt8(i, rs.mem_blocks[src_addr].getInt8(i));
}
} else {
for (var i = 0; i < num_bytes; i++) {
rs.mem_blocks[dest_addr + i] = rs.mem_blocks[src_addr + i];
}
}
}
}
}
function unsafe_compare_and_swap(rs, _this, obj, offset, expected, x) {
var actual = obj.get_field_from_offset(rs, offset);
if (actual === expected) {
obj.set_field_from_offset(rs, offset, x);
return true;
} else {
return false;
}
}
function native_define_class(rs, name, bytes, offset, len, loader, resume_cb, except_cb) {
var buff = new Buffer(len);
var b_array = bytes.array;
for (var i = offset; i < offset + len; i++) {
buff.writeUInt8((256 + b_array[i]) % 256, i);
}
loader.define_class(rs, util.int_classname(name.jvm2js_str()), buff, (function (cdata) {
resume_cb(cdata.get_class_object(rs));
}), except_cb);
}
function write_to_file(rs, _this, bytes, offset, len) {
var buf, fd, fd_obj;
fd_obj = _this.get_field(rs, 'Ljava/io/FileOutputStream;fd');
fd = fd_obj.get_field(rs, 'Ljava/io/FileDescriptor;fd');
if (fd === -1) {
rs.java_throw(rs.get_bs_class('Ljava/io/IOException;'), "Bad file descriptor");
}
if (fd !== 1 && fd !== 2) {
buf = new Buffer(bytes.array);
rs.async_op(function (cb) {
return fs.write(fd, buf, offset, len, _this.$pos, function (err, num_bytes) {
_this.$pos += num_bytes;
return cb();
});
});
return;
}
rs.print(util.chars2js_str(bytes, offset, len));
if (typeof node !== "undefined" && node !== null) {
return rs.async_op(function (cb) {
return cb();
});
}
}
function get_cl_from_jclo(rs, jclo) {
if ((jclo != null) && (jclo.$loader != null)) {
return jclo.$loader;
}
return rs.get_bs_cl();
}
function create_stack_trace(rs, throwable) {
var source_file, _ref8;
var stacktrace = [];
var cstack = rs.meta_stack()._cs.slice(1, -1);
for (var i = 0; i < cstack.length; i++) {
var sf = cstack[i];
if (!(!(sf["native"] || sf.locals[0] === throwable))) {
continue;
}
var cls = sf.method.cls;
var ln = -1;
if (throwable.cls.get_type() !== 'Ljava/lang/NoClassDefFoundError;') {
if (sf.method.access_flags["native"]) {
source_file = 'Native Method';
} else {
var src_attr = cls.get_attribute('SourceFile');
source_file = (src_attr != null) ? src_attr.filename : 'unknown';
var code = sf.method.code;
var table;
if (code != null) {
table = code.get_attribute('LineNumberTable');
}
if (table == null) {
break;
}
for (var k in table.entries) {
var row = table.entries[k];
if (row.start_pc <= sf.pc) {
ln = row.line_number;
}
}
}
} else {
source_file = 'unknown';
}
stacktrace.push(new JavaObject(rs, (rs.get_bs_class('Ljava/lang/StackTraceElement;')), {
'Ljava/lang/StackTraceElement;declaringClass': rs.init_string(util.ext_classname(cls.get_type())),
'Ljava/lang/StackTraceElement;methodName': rs.init_string((_ref8 = sf.method.name) != null ? _ref8 : 'unknown'),
'Ljava/lang/StackTraceElement;fileName': rs.init_string(source_file),
'Ljava/lang/StackTraceElement;lineNumber': ln
}));
}
return stacktrace.reverse();
}
function verify_array(rs, obj) {
if (!(obj instanceof java_object.JavaArray)) {
var err_cls = this.get_bs_class('Ljava/lang/IllegalArgumentException;');
this.java_throw(err_cls, 'Object is not an array.');
}
return obj;
}
function array_get(rs, arr, idx) {
var array, err_cls;
array = rs.check_null(arr).array;
if (!((0 <= idx && idx < array.length))) {
err_cls = rs.get_bs_class('Ljava/lang/ArrayIndexOutOfBoundsException;');
rs.java_throw(err_cls, 'Tried to access an illegal index in an array.');
}
return array[idx];
}
exports.native_methods = {
classes: {
awt: {
CanvasGraphicsEnvironment: []
},
doppio: {
JavaScript: [
o('eval(Ljava/lang/String;)Ljava/lang/String;', function (rs, to_eval) {
var rv = eval(to_eval.jvm2js_str());
if (rv != null) {
return rs.init_string("" + rv);
} else {
return null;
}
})
],
Debug: [
o('SetLogLevel(L!/!/!$LogLevel;)V', function (rs, loglevel) {
logging.log_level = loglevel.get_field(rs, 'Lclasses/doppio/Debug$LogLevel;level');
}),
o('GetLogLevel()L!/!/!$LogLevel;', function (rs) {
var ll_cls = rs.get_bs_class('Lclasses/doppio/Debug$LogLevel;');
switch (logging.log_level) {
case 10:
return ll_cls.static_get(rs, 'VTRACE');
case 9:
return ll_cls.static_get(rs, 'TRACE');
case 5:
return ll_cls.static_get(rs, 'DEBUG');
default:
return ll_cls.static_get(rs, 'ERROR');
}
})
]
}
},
java: {
lang: {
ClassLoader: [
o('findLoadedClass0(L!/!/String;)L!/!/Class;', function (rs, _this, name) {
var cls, loader, type;
loader = get_cl_from_jclo(rs, _this);
type = util.int_classname(name.jvm2js_str());
cls = loader.get_resolved_class(type, true);
if (cls != null) {
return cls.get_class_object(rs);
} else {
return null;
}
}),
o('findBootstrapClass(L!/!/String;)L!/!/Class;', function (rs, _this, name) {
var type = util.int_classname(name.jvm2js_str());
rs.async_op(function (resume_cb, except_cb) {
rs.get_bs_cl().resolve_class(rs, type, (function (cls) {
resume_cb(cls.get_class_object(rs));
}), except_cb, true);
});
}),
o('getCaller(I)L!/!/Class;', function (rs, i) {
var cls = rs.meta_stack().get_caller(i).method.cls;
return cls.get_class_object(rs);
}),
o('defineClass1(L!/!/String;[BIIL!/security/ProtectionDomain;L!/!/String;Z)L!/!/Class;', function (rs, _this, name, bytes, offset, len, pd, source, unused) {
var loader = get_cl_from_jclo(rs, _this);
rs.async_op(function (resume_cb, except_cb) {
native_define_class(rs, name, bytes, offset, len, loader, resume_cb, except_cb);
});
}),
o('defineClass1(L!/!/String;[BIIL!/security/ProtectionDomain;L!/!/String;)L!/!/Class;', function (rs, _this, name, bytes, offset, len, pd, source) {
var loader = get_cl_from_jclo(rs, _this);
rs.async_op(function (resume_cb, except_cb) {
native_define_class(rs, name, bytes, offset, len, loader, resume_cb, except_cb);
});
}),
o('resolveClass0(L!/!/Class;)V', function (rs, _this, cls) {
var loader, type;
loader = get_cl_from_jclo(rs, _this);
type = cls.$cls.get_type();
if (loader.get_resolved_class(type, true) != null) {
return;
}
rs.async_op(function (resume_cb, except_cb) {
loader.resolve_class(rs, type, (function () {
resume_cb();
}), except_cb, true);
});
})
],
Compiler: [o('disable()V', function (rs, _this) {
}), o('enable()V', function (rs, _this) {
})],
Float: [
o('floatToRawIntBits(F)I', function (rs, f_val) {
var exp, f_view, i_view, sig, sign;
if (typeof Float32Array !== "undefined" && Float32Array !== null) {
f_view = new Float32Array([f_val]);
i_view = new Int32Array(f_view.buffer);
return i_view[0];
}
if (f_val === 0) {
return 0;
}
if (f_val === Number.POSITIVE_INFINITY) {
return util.FLOAT_POS_INFINITY_AS_INT;
}
if (f_val === Number.NEGATIVE_INFINITY) {
return util.FLOAT_NEG_INFINITY_AS_INT;
}
if (isNaN(f_val)) {
return util.FLOAT_NaN_AS_INT;
}
sign = f_val < 0 ? 1 : 0;
f_val = Math.abs(f_val);
if (f_val <= 1.1754942106924411e-38 && f_val >= 1.4012984643248170e-45) {
exp = 0;
sig = Math.round((f_val / Math.pow(2, -126)) * Math.pow(2, 23));
return (sign << 31) | (exp << 23) | sig;
} else {
exp = Math.floor(Math.log(f_val) / Math.LN2);
sig = Math.round((f_val / Math.pow(2, exp) - 1) * Math.pow(2, 23));
return (sign << 31) | ((exp + 127) << 23) | sig;
}
}),
o('intBitsToFloat(I)F', function (rs, i_val) {
return util.intbits2float(i_val);
})
],
Double: [
o('doubleToRawLongBits(D)J', function (rs, d_val) {
var d_view, exp, high_bits, i_view, sig, sign;
if (typeof Float64Array !== "undefined" && Float64Array !== null) {
d_view = new Float64Array([d_val]);
i_view = new Uint32Array(d_view.buffer);
return gLong.fromBits(i_view[0], i_view[1]);
}
if (d_val === 0) {
return gLong.ZERO;
}
if (d_val === Number.POSITIVE_INFINITY) {
return gLong.fromBits(0, 2146435072);
} else if (d_val === Number.NEGATIVE_INFINITY) {
return gLong.fromBits(0, -1048576);
} else if (isNaN(d_val)) {
return gLong.fromBits(0, 2146959360);
}
sign = d_val < 0 ? 1 << 31 : 0;
d_val = Math.abs(d_val);
if (d_val <= 2.2250738585072010e-308 && d_val >= 5.0000000000000000e-324) {
exp = 0;
sig = gLong.fromNumber((d_val / Math.pow(2, -1022)) * Math.pow(2, 52));
} else {
exp = Math.floor(Math.log(d_val) / Math.LN2);
if (d_val < Math.pow(2, exp)) {
exp = exp - 1;
}
sig = gLong.fromNumber((d_val / Math.pow(2, exp) - 1) * Math.pow(2, 52));
exp = (exp + 1023) << 20;
}
high_bits = sig.getHighBits() | sign | exp;
return gLong.fromBits(sig.getLowBits(), high_bits);
}),
o('longBitsToDouble(J)D', function (rs, l_val) {
return util.longbits2double(l_val.getHighBits(), l_val.getLowBitsUnsigned());
})
],
Object: [
o('getClass()L!/!/Class;', function (rs, _this) {
return _this.cls.get_class_object(rs);
}),
o('hashCode()I', function (rs, _this) {
return _this.ref;
}),
o('clone()L!/!/!;', function (rs, _this) {
return _this.clone(rs);
}),
o('notify()V', function (rs, _this) {
var locker, owner;
debug("TE(notify): on lock *" + _this.ref);
if ((locker = rs.lock_refs[_this.ref]) != null) {
if (locker !== rs.curr_thread) {
owner = thread_name(rs, locker);
rs.java_throw((rs.get_bs_class('Ljava/lang/IllegalMonitorStateException;')), "Thread '" + owner + "' owns this monitor");
}
}
if (rs.waiting_threads[_this.ref] != null) {
rs.waiting_threads[_this.ref].shift();
}
}),
o('notifyAll()V', function (rs, _this) {
var locker, owner;
debug("TE(notifyAll): on lock *" + _this.ref);
if ((locker = rs.lock_refs[_this.ref]) != null) {
if (locker !== rs.curr_thread) {
owner = thread_name(rs, locker);
rs.java_throw((rs.get_bs_class('Ljava/lang/IllegalMonitorStateException;')), "Thread '" + owner + "' owns this monitor");
}
}
if (rs.waiting_threads[_this.ref] != null) {
rs.waiting_threads[_this.ref] = [];
}
}),
o('wait(J)V', function (rs, _this, timeout) {
var locker, owner;
if (timeout !== gLong.ZERO) {
error("TODO(Object::wait): respect the timeout param (" + timeout + ")");
}
if ((locker = rs.lock_refs[_this.ref]) != null) {
if (locker !== rs.curr_thread) {
owner = thread_name(rs, locker);
rs.java_throw((rs.get_bs_class('Ljava/lang/IllegalMonitorStateException;')), "Thread '" + owner + "' owns this monitor");
}
}
rs.lock_refs[_this.ref] = null;
rs.wait(_this);
})
],
Package: [
o('getSystemPackage0(Ljava/lang/String;)Ljava/lang/String;', function (rs, pkg_name_obj) {
var pkg_name = pkg_name_obj.jvm2js_str();
if (rs.get_bs_cl().get_package_names().indexOf(pkg_name) >= 0) {
return pkg_name_obj;
} else {
return null;
}
}),
o('getSystemPackages0()[Ljava/lang/String;', function (rs) {
var cls_name;
return new JavaArray(rs, ((rs.get_bs_class('[Ljava/lang/String;'))), (function () {
var _i, _len, _ref5, _results;
_ref5 = rs.get_bs_cl().get_package_names();
_results = [];
for (_i = 0, _len = _ref5.length; _i < _len; _i++) {
cls_name = _ref5[_i];
_results.push(rs.init_string(cls_name));
}
return _results;
})());
})
],
ProcessEnvironment: [
o('environ()[[B', function (rs) {
var env_arr, k, v, _ref5;
env_arr = [];
_ref5 = process.env;
for (k in _ref5) {
v = _ref5[k];
env_arr.push(new JavaArray(rs, (rs.get_bs_class('[B')), util.bytestr_to_array(k)));
env_arr.push(new JavaArray(rs, (rs.get_bs_class('[B')), util.bytestr_to_array(v)));
}
return new JavaArray(rs, (rs.get_bs_class('[[B')), env_arr);
})
],
reflect: {
Array: [
o('multiNewArray(L!/!/Class;[I)L!/!/Object;', function (rs, jco, lens) {
var _this = this;
var counts = lens.array;
var cls = rs.get_class(jco.$cls.get_type(), true);
if (cls == null) {
rs.async_op(function (resume_cb, except_cb) {
rs.get_cl().initialize_class(rs, jco.$cls.get_type(), (function (cls) {
var type_str = (new Array(counts.length + 1)).join('[') + cls.get_type();
rs.heap_multinewarray(type_str, counts);
resume_cb();
}), except_cb);
});
return;
}
var type_str = (new Array(counts.length + 1)).join('[') + cls.get_type();
return rs.heap_multinewarray(type_str, counts);
}),
o('newArray(L!/!/Class;I)L!/!/Object;', function (rs, _this, len) {
return rs.heap_newarray(_this.$cls.get_type(), len);
}),
o('getLength(Ljava/lang/Object;)I', function (rs, obj) {
var arr = verify_array(rs, obj);
return rs.check_null(arr).array.length;
}),
o('getBoolean(Ljava/lang/Object;I)Z', array_get),
o('getByte(Ljava/lang/Object;I)B', array_get),
o('getChar(Ljava/lang/Object;I)C', array_get),
o('getDouble(Ljava/lang/Object;I)D', array_get),
o('getFloat(Ljava/lang/Object;I)F', array_get),
o('getInt(Ljava/lang/Object;I)I', array_get),
o('getLong(Ljava/lang/Object;I)J', array_get),
o('getShort(Ljava/lang/Object;I)S', array_get),
o('get(Ljava/lang/Object;I)Ljava/lang/Object;', function (rs, arr, idx) {
var val;
val = array_get(rs, arr, idx);
if (val.ref == null) {
return arr.cls.get_component_class().create_wrapper_object(rs, val);
}
return val;
}),
o('set(Ljava/lang/Object;ILjava/lang/Object;)V', function (rs, obj, idx, val) {
var array, ccls, ccname, ecls, illegal_exc, m, my_sf;
var arr = verify_array(rs, obj);
my_sf = rs.curr_frame();
array = rs.check_null(arr).array;
if (!((0 <= idx && idx < array.length))) {
rs.java_throw((rs.get_bs_class('Ljava/lang/ArrayIndexOutOfBoundsException;')), 'Tried to write to an illegal index in an array.');
}
if ((ccls = arr.cls.get_component_class()) instanceof PrimitiveClassData) {
if (val.cls.is_subclass(rs.get_bs_class(ccls.box_class_name()))) {
ccname = ccls.get_type();
m = val.cls.method_lookup(rs, "" + util.internal2external[ccname] + "Value()" + ccname);
rs.push(val);
m.setup_stack(rs);
my_sf.runner = function () {
array[idx] = ccname === 'J' || ccname === 'D' ? rs.pop2() : rs.pop();
return rs.meta_stack().pop();
};
throw exceptions.ReturnException;
}
} else if (val.cls.is_subclass(ccls)) {
array[idx] = val;
return;
}
illegal_exc = 'Ljava/lang/IllegalArgumentException;';
if ((ecls = rs.get_bs_class(illegal_exc, true)) != null) {
return rs.java_throw(ecls, 'argument type mismatch');
} else {
return rs.async_op(function (resume_cb, except_cb) {
return rs.get_cl().initialize_class(rs, illegal_exc, (function (ecls) {
return except_cb((function () {
return rs.java_throw(ecls, 'argument type mismatch');
}));
}), except_cb);
});
}
})
],
Proxy: [
o('defineClass0(L!/!/ClassLoader;L!/!/String;[BII)L!/!/Class;', function (rs, cl, name, bytes, offset, len) {
return rs.async_op(function (success_cb, except_cb) {
return native_define_class(rs, name, bytes, offset, len, get_cl_from_jclo(rs, cl), success_cb, except_cb);
});
})
]
},
SecurityManager: [
o('getClassContext()[Ljava/lang/Class;', function (rs, _this) {
var classes, sf, _i, _ref5;
classes = [];
_ref5 = rs.meta_stack()._cs;
for (_i = _ref5.length - 1; _i >= 0; _i += -1) {
sf = _ref5[_i];
if (!sf["native"]) {
classes.push(sf.method.cls.get_class_object(rs));
}
}
return new JavaArray(rs, rs.get_bs_class('[Ljava/lang/Class;'), classes);
})
],
Shutdown: [
o('halt0(I)V', function (rs, status) {
throw new exceptions.HaltException(status);
})
],
StrictMath: [
o('acos(D)D', function (rs, d_val) {
return Math.acos(d_val);
}),
o('asin(D)D', function (rs, d_val) {
return Math.asin(d_val);
}),
o('atan(D)D', function (rs, d_val) {
return Math.atan(d_val);
}),
o('atan2(DD)D', function (rs, y, x) {
return Math.atan2(y, x);
}),
o('cbrt(D)D', function (rs, d_val) {
var is_neg;
is_neg = d_val < 0;
if (is_neg) {
return -Math.pow(-d_val, 1 / 3);
} else {
return Math.pow(d_val, 1 / 3);
}
}),
o('cos(D)D', function (rs, d_val) {
return Math.cos(d_val);
}),
o('exp(D)D', function (rs, d_val) {
return Math.exp(d_val);
}),
o('log(D)D', function (rs, d_val) {
return Math.log(d_val);
}),
o('log10(D)D', function (rs, d_val) {
return Math.log(d_val) / Math.LN10;
}),
o('pow(DD)D', function (rs, base, exp) {
return Math.pow(base, exp);
}),
o('sin(D)D', function (rs, d_val) {
return Math.sin(d_val);
}),
o('sqrt(D)D', function (rs, d_val) {
return Math.sqrt(d_val);
}),
o('tan(D)D', function (rs, d_val) {
return Math.tan(d_val);
}),
o('floor(D)D', function (rs, d_val) {
return Math.floor(d_val);
}),
o('ceil(D)D', function (rs, d_val) {
return Math.ceil(d_val);
})
],
String: [
o('intern()L!/!/!;', function (rs, _this) {
var js_str = _this.jvm2js_str();
var s = rs.string_pool.get(js_str);
if (s == null) {
rs.string_pool.set(js_str, _this);
return _this;
}
return s;
})
],
System: [
o('arraycopy(L!/!/Object;IL!/!/Object;II)V', function (rs, src, src_pos, dest, dest_pos, length) {
var dest_comp_cls, src_comp_cls;
if ((src == null) || (dest == null)) {
rs.java_throw(rs.get_bs_class('Ljava/lang/NullPointerException;'), 'Cannot copy to/from a null array.');
}
if (!(src.cls instanceof ArrayClassData) || !(dest.cls instanceof ArrayClassData)) {
rs.java_throw(rs.get_bs_class('Ljava/lang/ArrayStoreException;'), 'src and dest arguments must be of array type.');
}
if (src_pos < 0 || (src_pos + length) > src.array.length || dest_pos < 0 || (dest_pos + length) > dest.array.length || length < 0) {
rs.java_throw(rs.get_bs_class('Ljava/lang/ArrayIndexOutOfBoundsException;'), 'Tried to write to an illegal index in an array.');
}
if (src === dest) {
src = {
cls: src.cls,
array: src.array.slice(src_pos, src_pos + length)
};
src_pos = 0;
}
if (src.cls.is_castable(dest.cls)) {
return arraycopy_no_check(src, src_pos, dest, dest_pos, length);
} else {
src_comp_cls = src.cls.get_component_class();
dest_comp_cls = dest.cls.get_component_class();
if ((src_comp_cls instanceof PrimitiveClassData) || (dest_comp_cls instanceof PrimitiveClassData)) {
return rs.java_throw(rs.get_bs_class('Ljava/lang/ArrayStoreException;'), 'If calling arraycopy with a primitive array, both src and dest must be of the same primitive type.');
} else {
return arraycopy_check(rs, src, src_pos, dest, dest_pos, length);
}
}
}),
o('currentTimeMillis()J', function (rs) {
return gLong.fromNumber((new Date()).getTime());
}),
o('identityHashCode(L!/!/Object;)I', function (rs, x) {
var _ref5;
return (_ref5 = x != null ? x.ref : void 0) != null ? _ref5 : 0;
}),
o('initProperties(L!/util/Properties;)L!/util/Properties;', function (rs, props) {
return rs.push(null);
}),
o('nanoTime()J', function (rs) {
return gLong.fromNumber((new Date()).getTime()).multiply(gLong.fromNumber(1000000));
}),
o('setIn0(L!/io/InputStream;)V', function (rs, stream) {
var sys;
sys = rs.get_bs_class('Ljava/lang/System;');
return sys.static_put(rs, 'in', stream);
}),
o('setOut0(L!/io/PrintStream;)V', function (rs, stream) {
var sys;
sys = rs.get_bs_class('Ljava/lang/System;');
return sys.static_put(rs, 'out', stream);
}),
o('setErr0(L!/io/PrintStream;)V', function (rs, stream) {
var sys;
sys = rs.get_bs_class('Ljava/lang/System;');
return sys.static_put(rs, 'err', stream);
})
],
Thread: [
o('currentThread()L!/!/!;', function (rs) {
return rs.curr_thread;
}),
o('setPriority0(I)V', function (rs) {
}),
o('holdsLock(L!/!/Object;)Z', function (rs, obj) {
return rs.curr_thread === rs.lock_refs[obj.ref];
}),
o('isAlive()Z', function (rs, _this) {
var _ref5;
return (_ref5 = _this.$isAlive) != null ? _ref5 : false;
}),
o('isInterrupted(Z)Z', function (rs, _this, clear_flag) {
var tmp, _ref5;
tmp = (_ref5 = _this.$isInterrupted) != null ? _ref5 : false;
if (clear_flag) {
_this.$isInterrupted = false;
}
return tmp;
}),
o('interrupt0()V', function (rs, _this) {
var new_thread_sf;
_this.$isInterrupted = true;
if (_this === rs.curr_thread) {
return;
}
if (rs.parked(_this)) {
rs["yield"](_this);
return;
}
debug("TE(interrupt0): interrupting " + (thread_name(rs, _this)));
new_thread_sf = util.last(_this.$meta_stack._cs);
new_thread_sf.runner = function () {
return rs.java_throw(rs.get_bs_class('Ljava/lang/InterruptedException;'), 'interrupt0 called');
};
_this.$meta_stack.push({});
rs["yield"](_this);
throw exceptions.ReturnException;
}),
o('start0()V', function (rs, _this) {
var new_thread_sf, old_thread_sf, run_method, thread_runner_sf;
_this.$isAlive = true;
_this.$meta_stack = rs.construct_callstack();
rs.thread_pool.push(_this);
old_thread_sf = rs.curr_frame();
debug("TE(start0): starting " + (thread_name(rs, _this)) + " from " + (thread_name(rs, rs.curr_thread)));
rs.curr_thread = _this;
new_thread_sf = rs.curr_frame();
rs.push(_this);
run_method = _this.cls.method_lookup(rs, 'run()V');
thread_runner_sf = run_method.setup_stack(rs);
new_thread_sf.runner = function () {
new_thread_sf.runner = null;
_this.$isAlive = false;
return debug("TE(start0): thread died: " + (thread_name(rs, _this)));
};
old_thread_sf.runner = function () {
debug("TE(start0): thread resumed: " + (thread_name(rs, rs.curr_thread)));
return rs.meta_stack().pop();
};
throw exceptions.ReturnException;
}),
o('sleep(J)V', function (rs, millis) {
rs.curr_thread.wakeup_time = (new Date()).getTime() + millis.toNumber();
return rs.async_op(function (resume_cb) {
return rs.choose_next_thread(null, function (next_thread) {
rs["yield"](next_thread);
return resume_cb();
});
});
}),
o('yield()V', function (rs, _this) {
return rs.async_op(function (resume_cb) {
return rs.choose_next_thread(null, function (next_thread) {
rs["yield"](next_thread);
return resume_cb();
});
});
})
],
Throwable: [
o('fillInStackTrace()L!/!/!;', function (rs, _this) {
var strace;
strace = new JavaArray(rs, rs.get_bs_class('[Ljava/lang/StackTraceElement;'), create_stack_trace(rs, _this));
_this.set_field(rs, 'Ljava/lang/Throwable;stackTrace', strace);
return _this;
}),
o('getStackTraceDepth()I', function (rs, _this) {
return create_stack_trace(rs, _this).length;
}),
o('getStackTraceElement(I)L!/!/StackTraceElement;', function (rs, _this, depth) {
return create_stack_trace(rs, _this)[depth];
})
],
UNIXProcess: [
o('forkAndExec([B[BI[BI[BZLjava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;)I', function (rs, _this, prog, argBlock) {
var args, progname;
progname = util.chars2js_str(prog, 0, prog.array.length);
args = util.chars2js_str(argBlock, 0, argBlock.array.length);
return rs.java_throw(rs.get_bs_class('Ljava/lang/Error;'), "Doppio doesn't support forking processes. Command was: `" + progname + " " + args + "`");
})
]
},
security: {
AccessController: [
o('doPrivileged(L!/!/PrivilegedAction;)L!/lang/Object;', doPrivileged),
o('doPrivileged(L!/!/PrivilegedAction;L!/!/AccessControlContext;)L!/lang/Object;', doPrivileged),
o('doPrivileged(L!/!/PrivilegedExceptionAction;)L!/lang/Object;', doPrivileged),
o('doPrivileged(L!/!/PrivilegedExceptionAction;L!/!/AccessControlContext;)L!/lang/Object;', doPrivileged),
o('getStackAccessControlContext()Ljava/security/AccessControlContext;', function (rs) {
return null;
})
]
},
sql: {
DriverManager: [
o('getCallerClassLoader()Ljava/lang/ClassLoader;', function (rs) {
var rv;
rv = rs.meta_stack().get_caller(1).method.cls.loader.loader_obj;
if (rv !== void 0) {
return rv;
} else {
return null;
}
})
]
},
io: {
Console: [
o('encoding()L!/lang/String;', function () {
return null;
}),
o('istty()Z', function () {
return true;
})
],
FileSystem: [
o('getFileSystem()L!/!/!;', function (rs) {
var cache1, cache2, cache_init, cdata, my_sf;
my_sf = rs.curr_frame();
cdata = rs.get_bs_class('Ljava/io/ExpiringCache;');
cache1 = new JavaObject(rs, cdata);
cache2 = new JavaObject(rs, cdata);
cache_init = cdata.method_lookup(rs, '<init>()V');
rs.push2(cache1, cache2);
cache_init.setup_stack(rs);
my_sf.runner = function () {
cache_init.setup_stack(rs);
return my_sf.runner = function () {
var rv, system_properties;
system_properties = JVM.system_properties;
rv = new JavaObject(rs, rs.get_bs_class('Ljava/io/UnixFileSystem;'), {
'Ljava/io/UnixFileSystem;cache': cache1,
'Ljava/io/UnixFileSystem;javaHomePrefixCache': cache2,
'Ljava/io/UnixFileSystem;slash': system_properties['file.separator'].charCodeAt(0),
'Ljava/io/UnixFileSystem;colon': system_properties['path.separator'].charCodeAt(0),
'Ljava/io/UnixFileSystem;javaHome': rs.init_string(system_properties['java.home'], true)
});
rs.meta_stack().pop();
return rs.push(rv);
};
};
throw exceptions.ReturnException;
})
],
FileOutputStream: [
o('open(L!/lang/String;)V', function (rs, _this, fname) {
return rs.async_op(function (resume_cb) {
return fs.open(fname.jvm2js_str(), 'w', function (err, fd) {
var fd_obj;
fd_obj = _this.get_field(rs, 'Ljava/io/FileOutputStream;fd');
fd_obj.set_field(rs, 'Ljava/io/FileDescriptor;fd', fd);
_this.$pos = 0;
return resume_cb();
});
});
}),
o('openAppend(Ljava/lang/String;)V', function (rs, _this, fname) {
return rs.async_op(function (resume_cb) {
return fs.open(fname.jvm2js_str(), 'a', function (err, fd) {
var fd_obj;
fd_obj = _this.get_field(rs, 'Ljava/io/FileOutputStream;fd');
fd_obj.set_field(rs, 'Ljava/io/FileDescriptor;fd', fd);
return fs.fstat(fd, function (err, stats) {
_this.$pos = stats.size;
return resume_cb();
});
});
});
}),
o('writeBytes([BIIZ)V', write_to_file),
o('writeBytes([BII)V', write_to_file),
o('close0()V', function (rs, _this) {
var fd, fd_obj;
fd_obj = _this.get_field(rs, 'Ljava/io/FileOutputStream;fd');
fd = fd_obj.get_field(rs, 'Ljava/io/FileDescriptor;fd');
return rs.async_op(function (resume_cb, except_cb) {
return fs.close(fd, function (err) {
if (err) {
return except_cb(function () {
return rs.java_throw(rs.get_bs_class('Ljava/io/IOException;'), err.message);
});
} else {
fd_obj.set_field(rs, 'Ljava/io/FileDescriptor;fd', -1);
return resume_cb();
}
});
});
})
],
FileInputStream: [
o('available()I', function (rs, _this) {
var fd, fd_obj;
return fd_obj = _this.get_field(rs, "Ljava/io/FileInputStream;fd"), fd = fd_obj.get_field(rs, "Ljava/io/FileDescriptor;fd"), -1 === fd && rs.java_throw(rs.get_bs_class("Ljava/io/IOException;"), "Bad file descriptor"), 0 === fd ? 0 : rs.async_op(function (cb) {
return fs.fstat(fd, function (err, stats) {
return cb(stats.size - _this.$pos);
});
});
}),
o('read()I', function (rs, _this) {
var fd_obj = _this.get_field(rs, "Ljava/io/FileInputStream;fd");
var fd = fd_obj.get_field(rs, "Ljava/io/FileDescriptor;fd");
if (-1 === fd)
rs.java_throw(rs.get_bs_class("Ljava/io/IOException;"), "Bad file descriptor");
if (0 !== fd)
rs.async_op(function (cb) {
return fs.fstat(fd, function (err, stats) {
var buf;
return buf = new Buffer(stats.size), fs.read(fd, buf, 0, 1, _this.$pos, function (err, bytes_read) {
return _this.$pos++, cb(0 === bytes_read ? -1 : buf.readUInt8(0));
});
});
});
else
rs.async_op(function (cb) {
return rs.async_input(1, function (byte) {
return cb(0 === byte.length ? -1 : byte[0]);
});
});
}),
o('readBytes([BII)I', function (rs, _this, byte_arr, offset, n_bytes) {
var buf, pos;
var fd_obj = _this.get_field(rs, "Ljava/io/FileInputStream;fd");
var fd = fd_obj.get_field(rs, "Ljava/io/FileDescriptor;fd");
if (-1 === fd)
rs.java_throw(rs.get_bs_class("Ljava/io/IOException;"), "Bad file descriptor");
if (0 !== fd) {
pos = _this.$pos;
buf = new Buffer(n_bytes);
rs.async_op(function (cb) {
return fs.read(fd, buf, 0, n_bytes, pos, function (err, bytes_read) {
var i, _i;
if (null != err)
return cb(-1);
for (_this.$pos += bytes_read, i = _i = 0; bytes_read > _i; i = _i += 1)
byte_arr.array[offset + i] = buf.readUInt8(i);
return cb(0 === bytes_read && 0 !== n_bytes ? -1 : bytes_read);
});
});
} else {
rs.async_op(function (cb) {
return rs.async_input(n_bytes, function (bytes) {
var b, idx, _i, _len;
for (idx = _i = 0, _len = bytes.length; _len > _i; idx = ++_i)
b = bytes[idx], byte_arr.array[offset + idx] = b;
return cb(bytes.length);
});
});
}
}),
o('open(Ljava/lang/String;)V', function (rs, _this, filename) {
var filepath;
filepath = filename.jvm2js_str();
return rs.async_op(function (resume_cb, except_cb) {
return fs.open(filepath, 'r', function (e, fd) {
var fd_obj;
if (e != null) {
if (e.code === 'ENOENT') {
return except_cb(function () {
return rs.java_throw(rs.get_bs_class('Ljava/io/FileNotFoundException;'), "" + filepath + " (No such file or directory)");
});
} else {
return except_cb(function () {
throw e;
});
}
} else {
fd_obj = _this.get_field(rs, 'Ljava/io/FileInputStream;fd');
fd_obj.set_field(rs, 'Ljava/io/FileDescriptor;fd', fd);
_this.$pos = 0;
return resume_cb();
}
});
});
}),
o('close0()V', function (rs, _this) {
var fd, fd_obj;
fd_obj = _this.get_field(rs, 'Ljava/io/FileInputStream;fd');
fd = fd_obj.get_field(rs, 'Ljava/io/FileDescriptor;fd');
return rs.async_op(function (resume_cb, except_cb) {
return fs.close(fd, function (err) {
if (err) {
return except_cb(function () {
return rs.java_throw(rs.get_bs_class('Ljava/io/IOException;'), err.message);
});
} else {
fd_obj.set_field(rs, 'Ljava/io/FileDescriptor;fd', -1);
return resume_cb();
}
});
});
}),
o('skip(J)J', function (rs, _this, n_bytes) {
var fd_obj = _this.get_field(rs, "Ljava/io/FileInputStream;fd");
var fd = fd_obj.get_field(rs, "Ljava/io/FileDescriptor;fd");
if (-1 === fd)
rs.java_throw(rs.get_bs_class("Ljava/io/IOException;"), "Bad file descriptor");
if (0 !== fd) {
rs.async_op(function (cb) {
return fs.fstat(fd, function (err, stats) {
var bytes_left, to_skip;
return bytes_left = stats.size - _this.$pos, to_skip = Math.min(n_bytes.toNumber(), bytes_left), _this.$pos += to_skip, cb(gLong.fromNumber(to_skip), null);
});
});
} else {
rs.async_op(function (cb) {
return rs.async_input(n_bytes.toNumber(), function (bytes) {
return cb(gLong.fromNumber(bytes.length), null);
});
});
}
})
],
ObjectInputStream: [
o('latestUserDefinedLoader()Ljava/lang/ClassLoader;', function (rs) {
return null;
})
],
ObjectStreamClass: [
o('initNative()V', function (rs) {
}),
o('hasStaticInitializer(Ljava/lang/Class;)Z', function (rs, cls) {
return cls.$cls.get_method('<clinit>()V') != null;
})
],
RandomAccessFile: [
o('open(Ljava/lang/String;I)V', function (rs, _this, filename, mode) {
var filepath, mode_str;
filepath = filename.jvm2js_str();
mode_str = (function () {
switch (mode) {
case 1:
return 'r';
case 2:
return 'r+';
case 4:
case 8:
return 'rs+';
}
})();
return rs.async_op(function (resume_cb, except_cb) {
return fs.open(filepath, mode_str, function (e, fd) {
var fd_obj;
if (e != null) {
if (e.code === 'ENOENT') {
return except_cb(function () {
return rs.java_throw(rs.get_bs_class('Ljava/io/FileNotFoundException;'), "Could not open file " + filepath);
});
} else {
return except_cb(function () {
throw e;
});
}
} else {
fd_obj = _this.get_field(rs, 'Ljava/io/RandomAccessFile;fd');
fd_obj.set_field(rs, 'Ljava/io/FileDescriptor;fd', fd);
_this.$pos = 0;
return resume_cb();
}
});
});
}),
o('getFilePointer()J', function (rs, _this) {
return gLong.fromNumber(_this.$pos);
}),
o('length()J', function (rs, _this) {
var fd, fd_obj;
fd_obj = _this.get_field(rs, 'Ljava/io/RandomAccessFile;fd');
fd = fd_obj.get_field(rs, 'Ljava/io/FileDescriptor;fd');
return rs.async_op(function (cb) {
return fs.fstat(fd, function (err, stats) {
return cb(gLong.fromNumber(stats.size), null);
});
});
}),
o('seek(J)V', function (rs, _this, pos) {
return _this.$pos = pos.toNumber();
}),
o('readBytes([BII)I', function (rs, _this, byte_arr, offset, len) {
var fd_obj = _this.get_field(rs, "Ljava/io/RandomAccessFile;fd");
var fd = fd_obj.get_field(rs, "Ljava/io/FileDescriptor;fd");
var buf = new Buffer(len);
rs.async_op(function (cb) {
fs.read(fd, buf, 0, len, _this.$pos, function (err, bytes_read) {
var i, _i;
if (null != err)
return cb(-1);
for (i = _i = 0; bytes_read > _i; i = _i += 1)
byte_arr.array[offset + i] = buf.readUInt8(i);
return _this.$pos += bytes_read, cb(0 === bytes_read && 0 !== len ? -1 : bytes_read);
});
});
}),
o('writeBytes([BII)V', function (rs, _this, byte_arr, offset, len) {
var fd_obj = _this.get_field(rs, "Ljava/io/RandomAccessFile;fd");
var fd = fd_obj.get_field(rs, "Ljava/io/FileDescriptor;fd");
var buf = new Buffer(byte_arr.array);
rs.async_op(function (cb) {
fs.write(fd, buf, offset, len, _this.$pos, function (err, num_bytes) {
_this.$pos += num_bytes;
cb();
});
});
}),
o('close0()V', function (rs, _this) {
var fd, fd_obj;
fd_obj = _this.get_field(rs, 'Ljava/io/RandomAccessFile;fd');
fd = fd_obj.get_field(rs, 'Ljava/io/FileDescriptor;fd');
return rs.async_op(function (resume_cb, except_cb) {
return fs.close(fd, function (err) {
if (err) {
return except_cb(function () {
return rs.java_throw(rs.get_bs_class('Ljava/io/IOException;'), err.message);
});
} else {
fd_obj.set_field(rs, 'Ljava/io/FileDescriptor;fd', -1);
return resume_cb();
}
});
});
})
],
UnixFileSystem: [
o('canonicalize0(L!/lang/String;)L!/lang/String;', function (rs, _this, jvm_path_str) {
var js_str;
js_str = jvm_path_str.jvm2js_str();
return rs.init_string(path.resolve(path.normalize(js_str)));
}),
o('checkAccess(Ljava/io/File;I)Z', function (rs, _this, file, access) {
var filepath;
filepath = file.get_field(rs, 'Ljava/io/File;path');
return rs.async_op(function (resume_cb) {
return stat_file(filepath.jvm2js_str(), function (stats) {
var mask;
if (stats == null) {
return resume_cb(false);
} else {
mask = access | (access << 3) | (access << 6);
return resume_cb((stats.mode & mask) > 0);
}
});
});
}),
o('createDirectory(Ljava/io/File;)Z', function (rs, _this, file) {
var filepath;
filepath = (file.get_field(rs, 'Ljava/io/File;path')).jvm2js_str();
return rs.async_op(function (resume_cb) {
return stat_file(filepath, function (stat) {
if (stat != null) {
return resume_cb(false);
} else {
return fs.mkdir(filepath, function (err) {
return resume_cb(err != null ? false : true);
});
}
});
});
}),
o('createFileExclusively(Ljava/lang/String;)Z', function (rs, _this, path) {
var filepath;
filepath = path.jvm2js_str();
return rs.async_op(function (resume_cb, except_cb) {
return stat_file(filepath, function (stat) {
if (stat != null) {
return resume_cb(false);
} else {
return fs.open(filepath, 'w', function (err, fd) {
if (err != null) {
return except_cb(function () {
return rs.java_throw(rs.get_bs_class('Ljava/io/IOException;'), err.message);
});
} else {
return fs.close(fd, function (err) {
if (err != null) {
return except_cb(function () {
return rs.java_throw(rs.get_bs_class('Ljava/io/IOException;'), err.message);
});
} else {
return resume_cb(true);
}
});
}
});
}
});
});
}),
o('createFileExclusively(Ljava/lang/String;Z)Z', function (rs, _this, path) {
var filepath;
filepath = path.jvm2js_str();
return rs.async_op(function (resume_cb, except_cb) {
return stat_file(filepath, function (stat) {
if (stat != null) {
return resume_cb(false);
} else {
return fs.open(filepath, 'w', function (err, fd) {
if (err != null) {
return except_cb(function () {
return rs.java_throw(rs.get_bs_class('Ljava/io/IOException;'), err.message);
});
} else {
return fs.close(fd, function (err) {
if (err != null) {
return except_cb(function () {
return rs.java_throw(rs.get_bs_class('Ljava/io/IOException;'), err.message);
});
} else {
return resume_cb(true);
}
});
}
});
}
});
});
}),
o('delete0(Ljava/io/File;)Z', function (rs, _this, file) {
var filepath;
filepath = (file.get_field(rs, 'Ljava/io/File;path')).jvm2js_str();
return rs.async_op(function (resume_cb, except_cb) {
return stat_file(filepath, function (stats) {
if (stats == null) {
return resume_cb(false);
} else if (stats.isDirectory()) {
return fs.readdir(filepath, function (err, files) {
if (files.length > 0) {
return resume_cb(false);
} else {
return fs.rmdir(filepath, function (err) {
return resume_cb(true);
});
}
});
} else {
return fs.unlink(filepath, function (err) {
return resume_cb(true);
});
}
});
});
}),
o('getBooleanAttributes0(Ljava/io/File;)I', function (rs, _this, file) {
var filepath;
filepath = file.get_field(rs, 'Ljava/io/File;path');
return rs.async_op(function (resume_cb) {
return stat_file(filepath.jvm2js_str(), function (stats) {
if (stats == null) {
return resume_cb(0);
} else if (stats.isFile()) {
return resume_cb(3);
} else if (stats.isDirectory()) {
return resume_cb(5);
} else {
return resume_cb(1);
}
});
});
}),
o('getLastModifiedTime(Ljava/io/File;)J', function (rs, _this, file) {
var filepath;
filepath = file.get_field(rs, 'Ljava/io/File;path').jvm2js_str();
return rs.async_op(function (resume_cb) {
return stat_file(filepath, function (stats) {
if (stats == null) {
return resume_cb(gLong.ZERO, null);
} else {
return resume_cb(gLong.fromNumber((new Date(stats.mtime)).getTime()), null);
}
});
});
}),
o('setLastModifiedTime(Ljava/io/File;J)Z', function (rs, _this, file, time) {
var atime, filepath, mtime;
mtime = time.toNumber();
atime = (new Date()).getTime();
filepath = file.get_field(rs, 'Ljava/io/File;path').jvm2js_str();
return rs.async_op(function (resume_cb) {
return fs.utimes(filepath, atime, mtime, function (err) {
return resume_cb(true);
});
});
}),
o('getLength(Ljava/io/File;)J', function (rs, _this, file) {
var filepath;
filepath = file.get_field(rs, 'Ljava/io/File;path');
return rs.async_op(function (resume_cb) {
return fs.stat(filepath.jvm2js_str(), function (err, stat) {
return resume_cb(gLong.fromNumber(err != null ? 0 : stat.size), null);
});
});
}),
o('list(Ljava/io/File;)[Ljava/lang/String;', function (rs, _this, file) {
var filepath;
filepath = file.get_field(rs, 'Ljava/io/File;path');
return rs.async_op(function (resume_cb) {
return fs.readdir(filepath.jvm2js_str(), function (err, files) {
var f;
if (err != null) {
return resume_cb(null);
} else {
return resume_cb(new JavaArray(rs, rs.get_bs_class('[Ljava/lang/String;'), (function () {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = files.length; _i < _len; _i++) {
f = files[_i];
_results.push(rs.init_string(f));
}
return _results;
})()));
}
});
});
}),
o('rename0(Ljava/io/File;Ljava/io/File;)Z', function (rs, _this, file1, file2) {
var file1path, file2path;
file1path = (file1.get_field(rs, 'Ljava/io/File;path')).jvm2js_str();
file2path = (file2.get_field(rs, 'Ljava/io/File;path')).jvm2js_str();
return rs.async_op(function (resume_cb) {
return fs.rename(file1path, file2path, function (err) {
return resume_cb(err != null ? false : true);
});
});
}),
o('setPermission(Ljava/io/File;IZZ)Z', function (rs, _this, file, access, enable, owneronly) {
var filepath;
filepath = (file.get_field(rs, 'Ljava/io/File;path')).jvm2js_str();
if (owneronly) {
access <<= 6;
} else {
access |= (access << 6) | (access << 3);
}
if (!enable) {
access = ~access;
}
return rs.async_op(function (resume_cb) {
return stat_file(filepath, function (stats) {
var existing_access;
if (stats == null) {
return resume_cb(false);
} else {
existing_access = stats.mode;
access = enable ? existing_access | access : existing_access & access;
return fs.chmod(filepath, access, function (err) {
return resume_cb(err != null ? false : true);
});
}
});
});
}),
o('setReadOnly(Ljava/io/File;)Z', function (rs, _this, file) {
var filepath, mask;
filepath = (file.get_field(rs, 'Ljava/io/File;path')).jvm2js_str();
mask = ~0x92;
return rs.async_op(function (resume_cb) {
return stat_file(filepath, function (stats) {
if (stats == null) {
return resume_cb(false);
} else {
return fs.chmod(filepath, stats.mode & mask, function (err) {
return resume_cb(err != null ? false : true);
});
}
});
});
})
]
},
util: {
concurrent: {
atomic: {
AtomicLong: [
o('VMSupportsCS8()Z', function () {
return true;
})
]
}
},
jar: {
JarFile: [
o('getMetaInfEntryNames()[L!/lang/String;', function (rs) {
return null;
})
]
},
ResourceBundle: [
o('getClassContext()[L!/lang/Class;', function (rs) {
return new JavaArray(rs, rs.get_bs_class('[Ljava/lang/Class;'), [null, null, null]);
})
],
TimeZone: [
o('getSystemTimeZoneID(L!/lang/String;L!/lang/String;)L!/lang/String;', function (rs, java_home, country) {
return rs.init_string('GMT');
}),
o('getSystemGMTOffsetID()L!/lang/String;', function (rs) {
return null;
})
]
}
},
sun: {
font: {
FontManager: [],
FreetypeFontScaler: [o('initIDs(Ljava/lang/Class;)V', function () {
})],
StrikeCache: [
o('getGlyphCacheDescription([J)V', function (rs, infoArray) {
infoArray.array[0] = gLong.fromInt(8);
return infoArray.array[1] = gLong.fromInt(8);
})
]
},
management: {
VMManagementImpl: [
o('getStartupTime()J', function (rs) {
return rs.startup_time;
}),
o('getVersion0()Ljava/lang/String;', function (rs) {
return rs.init_string("1.2", true);
}),
o('initOptionalSupportFields()V', function (rs) {
var field_names, name, vm_management_impl, _i, _len, _results;
field_names = ['compTimeMonitoringSupport', 'threadContentionMonitoringSupport', 'currentThreadCpuTimeSupport', 'otherThreadCpuTimeSupport', 'bootClassPathSupport', 'objectMonitorUsageSupport', 'synchronizerUsageSupport'];
vm_management_impl = rs.get_bs_class('Lsun/management/VMManagementImpl;');
_results = [];
for (_i = 0, _len = field_names.length; _i < _len; _i++) {
name = field_names[_i];
_results.push(vm_management_impl.static_put(rs, name, 0));
}
return _results;
}),
o('isThreadAllocatedMemoryEnabled()Z', function () {
return false;
}),
o('isThreadContentionMonitoringEnabled()Z', function () {
return false;
}),
o('isThreadCpuTimeEnabled()Z', function () {
return false;
}),
o('getAvailableProcessors()I', function () {
return 1;
}),
o('getProcessId()I', function () {
return 1;
})
],
MemoryImpl: [
o('getMemoryManagers0()[Ljava/lang/management/MemoryManagerMXBean;', function (rs) {
return new JavaArray(rs, rs.get_bs_class('[Lsun/management/MemoryManagerImpl;'), []);
}),
o('getMemoryPools0()[Ljava/lang/management/MemoryPoolMXBean;', function (rs) {
return new JavaArray(rs, rs.get_bs_class('[Lsun/management/MemoryPoolImpl;'), []);
})
]
},
misc: {
VM: [
o('initialize()V', function (rs) {
var props, sys_cls, vm_cls;
vm_cls = rs.get_bs_class('Lsun/misc/VM;');
if (!(vm_cls.major_version >= 51)) {
return;
}
sys_cls = rs.get_bs_class('Ljava/lang/System;');
props = sys_cls.static_get(rs, 'props');
vm_cls = rs.get_bs_class('Lsun/misc/VM;');
return vm_cls.static_put('savedProps', props);
})
],
Unsafe: [
o('addressSize()I', function (rs, _this) {
return 4;
}),
o('allocateInstance(Ljava/lang/Class;)Ljava/lang/Object;', function (rs, _this, cls) {
cls = cls.$cls;
if (cls.is_initialized(rs)) {
return new JavaObject(rs, cls);
} else {
return rs.async_op(function (resume_cb, except_cb) {
return cls.loader.initialize_class(rs, cls.get_type(), (function () {
return resume_cb(new JavaObject(rs, cls));
}), except_cb);
});
}
}),
o('allocateMemory(J)J', function (rs, _this, size) {
var i, next_addr, _i;
next_addr = util.last(rs.mem_start_addrs);
if (typeof DataView !== "undefined" && DataView !== null) {
rs.mem_blocks[next_addr] = new DataView(new ArrayBuffer(size));
} else {
rs.mem_blocks[next_addr] = size;
next_addr += 1;
for (i = _i = 0; _i < size; i = _i += 1) {
rs.mem_blocks[next_addr + i] = 0;
}
}
rs.mem_start_addrs.push(next_addr + size);
return gLong.fromNumber(next_addr);
}),
o('copyMemory(Ljava/lang/Object;JLjava/lang/Object;JJ)V', function (rs, _this, src_base, src_offset, dest_base, dest_offset, num_bytes) {
return unsafe_memcpy(rs, src_base, src_offset, dest_base, dest_offset, num_bytes);
}),
o('setMemory(JJB)V', function (rs, _this, address, bytes, value) {
var block_addr, i, _i;
block_addr = rs.block_addr(address);
for (i = _i = 0; _i < bytes; i = _i += 1) {
if (typeof DataView !== "undefined" && DataView !== null) {
rs.mem_blocks[block_addr].setInt8(i, value);
} else {
rs.mem_blocks[block_addr + i] = value;
}
}
}),
o('freeMemory(J)V', function (rs, _this, address) {
var i, num_blocks, _i;
if (typeof DataView !== "undefined" && DataView !== null) {
delete rs.mem_blocks[address.toNumber()];
} else {
address = address.toNumber();
num_blocks = rs.mem_blocks[address - 1];
for (i = _i = 0; _i < num_blocks; i = _i += 1) {
delete rs.mem_blocks[address + i];
}
delete rs.mem_blocks[address - 1];
address = address - 1;
}
return rs.mem_start_addrs.splice(rs.mem_start_addrs.indexOf(address), 1);
}),
o('putLong(JJ)V', function (rs, _this, address, value) {
var block_addr, offset, store_word;
block_addr = rs.block_addr(address);
offset = address - block_addr;
if (typeof DataView !== "undefined" && DataView !== null) {
rs.mem_blocks[block_addr].setInt32(offset, value.getLowBits(), true);
rs.mem_blocks[block_addr].setInt32(offset + 4, value.getHighBits, true);
} else {
store_word = function (rs_, address, word) {
rs_.mem_blocks[address] = word & 0xFF;
rs_.mem_blocks[address + 1] = (word >>> 8) & 0xFF;
rs_.mem_blocks[address + 2] = (word >>> 16) & 0xFF;
return rs_.mem_blocks[address + 3] = (word >>> 24) & 0xFF;
};
store_word(rs, address, value.getLowBits());
store_word(rs, address + 4, value.getHighBits());
}
}),
o('getByte(J)B', function (rs, _this, address) {
var block_addr;
block_addr = rs.block_addr(address);
if (typeof DataView !== "undefined" && DataView !== null) {
return rs.mem_blocks[block_addr].getInt8(address - block_addr);
} else {
return rs.mem_blocks[block_addr];
}
}),
o('arrayBaseOffset(Ljava/lang/Class;)I', function (rs, _this, cls) {
return 0;
}),
o('arrayIndexScale(Ljava/lang/Class;)I', function (rs, _this, cls) {
return 1;
}),
o('compareAndSwapObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z', unsafe_compare_and_swap),
o('compareAndSwapInt(Ljava/lang/Object;JII)Z', unsafe_compare_and_swap),
o('compareAndSwapLong(Ljava/lang/Object;JJJ)Z', unsafe_compare_and_swap),
o('ensureClassInitialized(Ljava/lang/Class;)V', function (rs, _this, cls) {
return rs.async_op(function (resume_cb, except_cb) {
return cls.$cls.loader.initialize_class(rs, cls.$cls.get_type(), (function () {
return resume_cb();
}), except_cb);
});
}),
o('staticFieldOffset(Ljava/lang/reflect/Field;)J', function (rs, _this, field) {
var jco, slot;
jco = field.get_field(rs, 'Ljava/lang/reflect/Field;clazz');
slot = field.get_field(rs, 'Ljava/lang/reflect/Field;slot');
return gLong.fromNumber(slot + jco.ref);
}),
o('objectFieldOffset(Ljava/lang/reflect/Field;)J', function (rs, _this, field) {
var jco, slot;
jco = field.get_field(rs, 'Ljava/lang/reflect/Field;clazz');
slot = field.get_field(rs, 'Ljava/lang/reflect/Field;slot');
return gLong.fromNumber(slot + jco.ref);
}),
o('staticFieldBase(Ljava/lang/reflect/Field;)Ljava/lang/Object;', function (rs, _this, field) {
var cls;
cls = field.get_field(rs, 'Ljava/lang/reflect/Field;clazz');
return new JavaObject(rs, cls.$cls);
}),
o('getBoolean(Ljava/lang/Object;J)Z', function (rs, _this, obj, offset) {
return obj.get_field_from_offset(rs, offset);
}),
o('getBooleanVolatile(Ljava/lang/Object;J)Z', function (rs, _this, obj, offset) {
return obj.get_field_from_offset(rs, offset);
}),
o('getDouble(Ljava/lang/Object;J)D', function (rs, _this, obj, offset) {
return obj.get_field_from_offset(rs, offset);
}),
o('getDoubleVolatile(Ljava/lang/Object;J)D', function (rs, _this, obj, offset) {
return obj.get_field_from_offset(rs, offset);
}),
o('getFloat(Ljava/lang/Object;J)F', function (rs, _this, obj, offset) {
return obj.get_field_from_offset(rs, offset);
}),
o('getFloatVolatile(Ljava/lang/Object;J)F', function (rs, _this, obj, offset) {
return obj.get_field_from_offset(rs, offset);
}),
o('getInt(Ljava/lang/Object;J)I', function (rs, _this, obj, offset) {
return obj.get_field_from_offset(rs, offset);
}),
o('getIntVolatile(Ljava/lang/Object;J)I', function (rs, _this, obj, offset) {
return obj.get_field_from_offset(rs, offset);
}),
o('getLong(Ljava/lang/Object;J)J', function (rs, _this, obj, offset) {
return obj.get_field_from_offset(rs, offset);
}),
o('getLongVolatile(Ljava/lang/Object;J)J', function (rs, _this, obj, offset) {
return obj.get_field_from_offset(rs, offset);
}),
o('getShort(Ljava/lang/Object;J)S', function (rs, _this, obj, offset) {
return obj.get_field_from_offset(rs, offset);
}),
o('getShortVolatile(Ljava/lang/Object;J)S', function (rs, _this, obj, offset) {
return obj.get_field_from_offset(rs, offset);
}),
o('getObject(Ljava/lang/Object;J)Ljava/lang/Object;', function (rs, _this, obj, offset) {
return obj.get_field_from_offset(rs, offset);
}),
o('getObjectVolatile(Ljava/lang/Object;J)Ljava/lang/Object;', function (rs, _this, obj, offset) {
return obj.get_field_from_offset(rs, offset);
}),
o('putDouble(Ljava/lang/Object;JD)V', function (rs, _this, obj, offset, new_value) {
return obj.set_field_from_offset(rs, offset, new_value);
}),
o('putInt(Ljava/lang/Object;JI)V', function (rs, _this, obj, offset, new_value) {
return obj.set_field_from_offset(rs, offset, new_value);
}),
o('putObject(Ljava/lang/Object;JLjava/lang/Object;)V', function (rs, _this, obj, offset, new_obj) {
return obj.set_field_from_offset(rs, offset, new_obj);
}),
o('putObjectVolatile(Ljava/lang/Object;JLjava/lang/Object;)V', function (rs, _this, obj, offset, new_obj) {
return obj.set_field_from_offset(rs, offset, new_obj);
}),
o('putOrderedObject(Ljava/lang/Object;JLjava/lang/Object;)V', function (rs, _this, obj, offset, new_obj) {
return obj.set_field_from_offset(rs, offset, new_obj);
}),
o('defineClass(Ljava/lang/String;[BIILjava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;', function (rs, _this, name, bytes, offset, len, loader, pd) {
return rs.async_op(function (success_cb, except_cb) {
return native_define_class(rs, name, bytes, offset, len, get_cl_from_jclo(rs, loader), success_cb, except_cb);
});
}),
o('pageSize()I', function (rs) {
return 1024;
}),
o('throwException(Ljava/lang/Throwable;)V', function (rs, _this, exception) {
var my_sf;
my_sf = rs.curr_frame();
my_sf.runner = function () {
my_sf.runner = null;
throw new exceptions.JavaException(exception);
};
throw exceptions.ReturnException;
}),
o('park(ZJ)V', function (rs, _this, absolute, time) {
var timeout;
timeout = Infinity;
if (absolute) {
timeout = time;
} else {
if (time > 0) {
timeout = (new Date()).getTime() + time / 1000000;
}
}
return rs.park(rs.curr_thread, timeout);
}),
o('unpark(Ljava/lang/Object;)V', function (rs, _this, thread) {
return rs.unpark(thread);
})
]
},
nio: {
ch: {
FileChannelImpl: [
o('initIDs()J', function (rs) {
return gLong.fromNumber(1024);
}),
o('size0(Ljava/io/FileDescriptor;)J', function (rs, _this, fd_obj) {
var fd = fd_obj.get_field(rs, "Ljava/io/FileDescriptor;fd");
rs.async_op(function (cb, e_cb) {
fs.fstat(fd, function (err, stats) {
if (null != err)
e_cb(function () {
rs.java_throw(rs.get_bs_class("Ljava/io/IOException;"), "Bad file descriptor.");
});
cb(gLong.fromNumber(stats.size));
});
});
}),
o('position0(Ljava/io/FileDescriptor;J)J', function (rs, _this, fd, offset) {
var parent;
parent = _this.get_field(rs, 'Lsun/nio/ch/FileChannelImpl;parent');
return gLong.fromNumber(offset.equals(gLong.NEG_ONE) ? parent.$pos : parent.$pos = offset.toNumber());
})
],
FileDispatcher: [
o('init()V', function (rs) {
}),
o('read0(Ljava/io/FileDescriptor;JI)I', function (rs, fd_obj, address, len) {
var fd = fd_obj.get_field(rs, "Ljava/io/FileDescriptor;fd");
var block_addr = rs.block_addr(address);
var buf = new Buffer(len);
rs.async_op(function (cb) {
fs.read(fd, buf, 0, len, 0, function (err, bytes_read) {
var i, _i, _j;
if ("undefined" != typeof DataView && null !== DataView)
for (i = 0; bytes_read > i; i++)
rs.mem_blocks[block_addr].setInt8(i, buf.readInt8(i));
else
for (i = 0; bytes_read > i; i++)
rs.mem_blocks[block_addr + i] = buf.readInt8(i);
cb(bytes_read);
});
});
}),
o('preClose0(Ljava/io/FileDescriptor;)V', function (rs, fd_obj) {
})
],
NativeThread: [
o("init()V", function (rs) {
}),
o("current()J", function (rs) {
return gLong.fromNumber(-1);
})
]
}
}
}
};
exports.native_methods['java']['lang']['Class'] = [
o('getPrimitiveClass(L!/!/String;)L!/!/!;', function (rs, jvm_str) {
var prim_cls, type_desc;
type_desc = util.typestr2descriptor(jvm_str.jvm2js_str());
prim_cls = rs.get_bs_class(type_desc);
return prim_cls.get_class_object(rs);
}),
o('getClassLoader0()L!/!/ClassLoader;', function (rs, _this) {
var loader;
loader = _this.$cls.loader;
if (loader.loader_obj != null) {
return loader.loader_obj;
}
return null;
}),
o('desiredAssertionStatus0(L!/!/!;)Z', function (rs) {
return false;
}),
o('getName0()L!/!/String;', function (rs, _this) {
return rs.init_string(_this.$cls.toExternalString());
}),
o('forName0(L!/!/String;ZL!/!/ClassLoader;)L!/!/!;', function (rs, jvm_str, initialize, loader) {
var classname = util.int_classname(jvm_str.jvm2js_str());
if (!util.verify_int_classname(classname)) {
rs.java_throw(rs.get_bs_class('Ljava/lang/ClassNotFoundException;'), classname);
}
loader = get_cl_from_jclo(rs, loader);
rs.async_op(function (resume_cb, except_cb) {
if (initialize) {
return loader.initialize_class(rs, classname, (function (cls) {
return resume_cb(cls.get_class_object(rs));
}), except_cb, true);
} else {
return loader.resolve_class(rs, classname, (function (cls) {
return resume_cb(cls.get_class_object(rs));
}), except_cb, true);
}
});
}),
o('getComponentType()L!/!/!;', function (rs, _this) {
if (!(_this.$cls instanceof ArrayClassData)) {
return null;
}
return _this.$cls.get_component_class().get_class_object(rs);
}),
o('getGenericSignature()Ljava/lang/String;', function (rs, _this) {
var sig, _ref5;
sig = (_ref5 = _this.$cls.get_attribute('Signature')) != null ? _ref5.sig : void 0;
if (sig != null) {
return rs.init_string(sig);
} else {
return null;
}
}),
o('getProtectionDomain0()Ljava/security/ProtectionDomain;', function (rs, _this) {
return null;
}),
o('isAssignableFrom(L!/!/!;)Z', function (rs, _this, cls) {
return cls.$cls.is_castable(_this.$cls);
}),
o('isInterface()Z', function (rs, _this) {
if (!(_this.$cls instanceof ReferenceClassData)) {
return false;
}
return _this.$cls.access_flags["interface"];
}),
o('isInstance(L!/!/Object;)Z', function (rs, _this, obj) {
return obj.cls.is_castable(_this.$cls);
}),
o('isPrimitive()Z', function (rs, _this) {
return _this.$cls instanceof PrimitiveClassData;
}),
o('isArray()Z', function (rs, _this) {
return _this.$cls instanceof ArrayClassData;
}),
o('getSuperclass()L!/!/!;', function (rs, _this) {
if (_this.$cls instanceof PrimitiveClassData) {
return null;
}
var cls = _this.$cls;
if (cls.access_flags["interface"] || (cls.get_super_class() == null)) {
return null;
}
return cls.get_super_class().get_class_object(rs);
}),
o('getDeclaredFields0(Z)[Ljava/lang/reflect/Field;', function (rs, _this, public_only) {
var fields = _this.$cls.get_fields();
if (public_only) {
fields = fields.filter(function (f) {
return f.access_flags["public"];
});
}
var base_array = [];
rs.async_op(function (resume_cb, except_cb) {
var i = -1;
function fetch_next_field() {
i++;
if (i < fields.length) {
fields[i].reflector(rs, (function (jco) {
base_array.push(jco);
return fetch_next_field();
}), except_cb);
} else {
var field_arr_cls = rs.get_bs_class('[Ljava/lang/reflect/Field;');
resume_cb(new JavaArray(rs, field_arr_cls, base_array));
}
}
;
fetch_next_field();
});
}),
o('getDeclaredMethods0(Z)[Ljava/lang/reflect/Method;', function (rs, _this, public_only) {
var base_array, m, methods, sig;
methods = _this.$cls.get_methods();
methods = (function () {
var _results;
_results = [];
for (sig in methods) {
m = methods[sig];
if (sig[0] !== '<' && (m.access_flags["public"] || !public_only)) {
_results.push(m);
}
}
return _results;
})();
base_array = [];
rs.async_op(function (resume_cb, except_cb) {
var fetch_next_method, i;
i = -1;
fetch_next_method = function () {
i++;
if (i < methods.length) {
m = methods[i];
return m.reflector(rs, false, (function (jco) {
base_array.push(jco);
return fetch_next_method();
}), except_cb);
} else {
return resume_cb(new JavaArray(rs, rs.get_bs_class('[Ljava/lang/reflect/Method;'), base_array));
}
};
return fetch_next_method();
});
}),
o('getDeclaredConstructors0(Z)[Ljava/lang/reflect/Constructor;', function (rs, _this, public_only) {
var base_array, ctor_array_cdata, m, methods, sig;
methods = _this.$cls.get_methods();
methods = (function () {
var _results;
_results = [];
for (sig in methods) {
m = methods[sig];
if (m.name === '<init>') {
_results.push(m);
}
}
return _results;
})();
if (public_only) {
methods = (function () {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = methods.length; _i < _len; _i++) {
m = methods[_i];
if (m.access_flags["public"]) {
_results.push(m);
}
}
return _results;
})();
}
ctor_array_cdata = rs.get_bs_class('[Ljava/lang/reflect/Constructor;');
base_array = [];
rs.async_op(function (resume_cb, except_cb) {
var fetch_next_method, i;
i = -1;
fetch_next_method = function () {
i++;
if (i < methods.length) {
m = methods[i];
return m.reflector(rs, true, (function (jco) {
base_array.push(jco);
return fetch_next_method();
}), except_cb);
} else {
return resume_cb(new JavaArray(rs, ctor_array_cdata, base_array));
}
};
return fetch_next_method();
});
}),
o('getInterfaces()[L!/!/!;', function (rs, _this) {
var cls, iface, iface_objs, ifaces;
cls = _this.$cls;
ifaces = cls.get_interfaces();
iface_objs = (function () {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = ifaces.length; _i < _len; _i++) {
iface = ifaces[_i];
_results.push(iface.get_class_object(rs));
}
return _results;
})();
return new JavaArray(rs, rs.get_bs_class('[Ljava/lang/Class;'), iface_objs);
}),
o('getModifiers()I', function (rs, _this) {
return _this.$cls.access_byte;
}),
o('getRawAnnotations()[B', function (rs, _this) {
var annotations, cls, m, sig, _ref5;
cls = _this.$cls;
annotations = cls.get_attribute('RuntimeVisibleAnnotations');
if (annotations != null) {
return new JavaArray(rs, rs.get_bs_class('[B'), annotations.raw_bytes);
}
_ref5 = cls.get_methods();
for (sig in _ref5) {
m = _ref5[sig];
annotations = m.get_attribute('RuntimeVisibleAnnotations');
if (annotations != null) {
return new JavaArray(rs, rs.get_bs_class('[B'), annotations.raw_bytes);
}
}
return null;
}),
o('getConstantPool()Lsun/reflect/ConstantPool;', function (rs, _this) {
var cls;
cls = _this.$cls;
return new JavaObject(rs, rs.get_bs_class('Lsun/reflect/ConstantPool;'), {
'Lsun/reflect/ConstantPool;constantPoolOop': cls.constant_pool
});
}),
o('getEnclosingMethod0()[L!/!/Object;', function (rs, _this) {
var cls, em, enc_cls, enc_desc, enc_name;
if (!(_this.$cls instanceof ReferenceClassData)) {
return null;
}
cls = _this.$cls;
em = cls.get_attribute('EnclosingMethod');
if (em == null) {
return null;
}
enc_cls = cls.loader.get_resolved_class(em.enc_class).get_class_object(rs);
if (em.enc_method != null) {
enc_name = rs.init_string(em.enc_method.name);
enc_desc = rs.init_string(em.enc_method.type);
} else {
enc_name = null;
enc_desc = null;
}
return new JavaArray(rs, rs.get_bs_class('[Ljava/lang/Object;'), [enc_cls, enc_name, enc_desc]);
}),
o('getDeclaringClass()L!/!/!;', function (rs, _this) {
var cls, declaring_name, entry, icls, my_class, name, _i, _len, _ref5;
if (!(_this.$cls instanceof ReferenceClassData)) {
return null;
}
cls = _this.$cls;
icls = cls.get_attribute('InnerClasses');
if (icls == null) {
return null;
}
my_class = _this.$cls.get_type();
_ref5 = icls.classes;
for (_i = 0, _len = _ref5.length; _i < _len; _i++) {
entry = _ref5[_i];
if (!(entry.outer_info_index > 0)) {
continue;
}
name = cls.constant_pool.get(entry.inner_info_index).deref();
if (name !== my_class) {
continue;
}
declaring_name = cls.constant_pool.get(entry.outer_info_index).deref();
return cls.loader.get_resolved_class(declaring_name).get_class_object(rs);
}
return null;
}),
o('getDeclaredClasses0()[L!/!/!;', function (rs, _this) {
var c, cls, enclosing_name, flat_names, icls, iclses, my_class, ret, _i, _j, _len, _len1, _ref5;
ret = new JavaArray(rs, rs.get_bs_class('[Ljava/lang/Class;'), []);
if (!(_this.$cls instanceof ReferenceClassData)) {
return ret;
}
cls = _this.$cls;
my_class = _this.$cls.get_type();
iclses = cls.get_attributes('InnerClasses');
if (iclses.length === 0) {
return ret;
}
flat_names = [];
for (_i = 0, _len = iclses.length; _i < _len; _i++) {
icls = iclses[_i];
_ref5 = icls.classes;
for (_j = 0, _len1 = _ref5.length; _j < _len1; _j++) {
c = _ref5[_j];
if (!(c.outer_info_index > 0)) {
continue;
}
enclosing_name = cls.constant_pool.get(c.outer_info_index).deref();
if (enclosing_name !== my_class) {
continue;
}
flat_names.push(cls.constant_pool.get(c.inner_info_index).deref());
}
}
rs.async_op(function (resume_cb, except_cb) {
var fetch_next_jco, i;
i = -1;
fetch_next_jco = function () {
var name;
i++;
if (i < flat_names.length) {
name = flat_names[i];
return cls.loader.resolve_class(rs, name, (function (cls) {
ret.array.push(cls.get_class_object(rs));
return fetch_next_jco();
}), except_cb);
} else {
return resume_cb(ret);
}
};
return fetch_next_jco();
});
})
];
exports.native_methods['java']['lang']['Runtime'] = [
o('availableProcessors()I', function () {
return 1;
}),
o('gc()V', function (rs) {
return rs.async_op(function (cb) {
return cb();
});
}),
o('maxMemory()J', function (rs) {
debug("Warning: maxMemory has no meaningful value in Doppio -- there is no hard memory limit.");
return gLong.MAX_VALUE;
})
];
function setup_caller_stack(rs, method, obj, params) {
var i, p, p_type, primitive_value, _i, _len, _ref5;
if (!method.access_flags["static"]) {
rs.push(obj);
}
i = 0;
_ref5 = method.param_types;
for (_i = 0, _len = _ref5.length; _i < _len; _i++) {
p_type = _ref5[_i];
p = params.array[i++];
if (p_type === 'J' || p_type === 'D') {
if ((p != null ? p.ref : void 0) != null) {
primitive_value = p.get_field(rs, p.cls.get_type() + 'value');
rs.push2(primitive_value, null);
} else {
rs.push2(p, null);
i++;
}
} else if (util.is_primitive_type(p_type)) {
if ((p != null ? p.ref : void 0) != null) {
primitive_value = p.get_field(rs, p.cls.get_type() + 'value');
rs.push(primitive_value);
} else {
rs.push(p);
}
} else {
rs.push(p);
}
}
return rs.curr_frame();
}
exports.native_methods['sun']['reflect'] = {
ConstantPool: [
o('getLongAt0(Ljava/lang/Object;I)J', function (rs, _this, cp, idx) {
return cp.get(idx).value;
}),
o('getUTF8At0(Ljava/lang/Object;I)Ljava/lang/String;', function (rs, _this, cp, idx) {
return rs.init_string(cp.get(idx).value);
})
],
NativeMethodAccessorImpl: [
o('invoke0(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;', function (rs, m, obj, params) {
var caller_sf, cleanup_runner, cls, cls_obj, m_sig, method, name, p_desc, p_types, pt, ret_descriptor, ret_type, slot;
cls = m.get_field(rs, 'Ljava/lang/reflect/Method;clazz');
ret_type = m.get_field(rs, 'Ljava/lang/reflect/Method;returnType');
ret_descriptor = ret_type.$cls.get_type();
if (util.is_primitive_type(ret_descriptor) && ret_descriptor !== 'V') {
cleanup_runner = function () {
var rv;
rv = ret_descriptor === 'J' || ret_descriptor === 'D' ? rs.pop2() : rs.pop();
rs.meta_stack().pop();
return rs.push(ret_type.$cls.create_wrapper_object(rs, rv));
};
} else {
cleanup_runner = function () {
var rv;
rv = rs.pop();
rs.meta_stack().pop();
return rs.push(rv);
};
}
if (cls.$cls.access_byte & 0x200) {
cls_obj = rs.check_null(obj).cls;
name = m.get_field(rs, 'Ljava/lang/reflect/Method;name').jvm2js_str(rs);
p_types = m.get_field(rs, 'Ljava/lang/reflect/Method;parameterTypes');
p_desc = ((function () {
var _i, _len, _ref5, _results;
_ref5 = p_types.array;
_results = [];
for (_i = 0, _len = _ref5.length; _i < _len; _i++) {
pt = _ref5[_i];
_results.push(pt.$cls.get_type());
}
return _results;
})()).join('');
m_sig = "" + name + "(" + p_desc + ")" + ret_descriptor;
method = cls_obj.method_lookup(rs, m_sig);
caller_sf = setup_caller_stack(rs, method, obj, params);
method.setup_stack(rs);
caller_sf.runner = cleanup_runner;
throw exceptions.ReturnException;
} else {
slot = m.get_field(rs, 'Ljava/lang/reflect/Method;slot');
return rs.async_op(function (resume_cb, except_cb) {
return cls.$cls.loader.initialize_class(rs, cls.$cls.get_type(), (function (cls_obj) {
var sig;
method = ((function () {
var _ref5, _results;
_ref5 = cls_obj.get_methods();
_results = [];
for (sig in _ref5) {
method = _ref5[sig];
if (method.idx === slot) {
_results.push(method);
}
}
return _results;
})())[0];
caller_sf = setup_caller_stack(rs, method, obj, params);
return except_cb(function () {
method.setup_stack(rs);
return caller_sf.runner = cleanup_runner;
});
}), except_cb);
});
}
})
],
NativeConstructorAccessorImpl: [
o('newInstance0(Ljava/lang/reflect/Constructor;[Ljava/lang/Object;)Ljava/lang/Object;', function (rs, m, params) {
var cls, slot;
cls = m.get_field(rs, 'Ljava/lang/reflect/Constructor;clazz');
slot = m.get_field(rs, 'Ljava/lang/reflect/Constructor;slot');
return rs.async_op(function (resume_cb, except_cb) {
return cls.$cls.loader.initialize_class(rs, cls.$cls.get_type(), (function (cls_obj) {
var method, my_sf, obj, sig;
method = ((function () {
var _ref5, _results;
_ref5 = cls_obj.get_methods();
_results = [];
for (sig in _ref5) {
method = _ref5[sig];
if (method.idx === slot) {
_results.push(method);
}
}
return _results;
})())[0];
my_sf = rs.curr_frame();
obj = new JavaObject(rs, cls_obj);
rs.push(obj);
if (params != null) {
rs.push_array(params.array);
}
return except_cb(function () {
method.setup_stack(rs);
return my_sf.runner = function () {
rs.meta_stack().pop();
return rs.push(obj);
};
});
}), except_cb);
});
})
],
Reflection: [
o('getCallerClass(I)Ljava/lang/Class;', function (rs, frames_to_skip) {
var caller, cls;
caller = rs.meta_stack().get_caller(frames_to_skip);
if (caller.name.indexOf('Ljava/lang/reflect/Method;::invoke') === 0) {
caller = rs.meta_stack().get_caller(frames_to_skip + 1);
}
cls = caller.method.cls;
return cls.get_class_object(rs);
}),
o('getClassAccessFlags(Ljava/lang/Class;)I', function (rs, class_obj) {
return class_obj.$cls.access_byte;
})
]
};
function flatten_pkg(pkg) {
var pkg_name_arr, rec_flatten, result;
result = {};
pkg_name_arr = [];
rec_flatten = function (pkg) {
var flattened_inner, fn, fn_name, full_name, full_pkg_name, inner_pkg, method, pkg_name, _i, _len;
for (pkg_name in pkg) {
inner_pkg = pkg[pkg_name];
pkg_name_arr.push(pkg_name);
if (inner_pkg instanceof Array) {
full_pkg_name = pkg_name_arr.join('/');
for (_i = 0, _len = inner_pkg.length; _i < _len; _i++) {
method = inner_pkg[_i];
fn_name = method.fn_name, fn = method.fn;
fn_name = fn_name.replace(/!|;/g, (function () {
var depth;
depth = 0;
return function (c) {
if (c === '!') {
return pkg_name_arr[depth++];
} else if (c === ';') {
depth = 0;
return c;
} else {
return c;
}
};
})());
full_name = "L" + full_pkg_name + ";::" + fn_name;
result[full_name] = fn;
}
} else {
flattened_inner = rec_flatten(inner_pkg);
}
pkg_name_arr.pop(pkg_name);
}
};
rec_flatten(pkg);
return result;
}
exports.trapped_methods = flatten_pkg(exports.trapped_methods);
exports.native_methods = flatten_pkg(exports.native_methods);
});
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
define('src/methods',["require", "exports", './util', './opcodes', './attributes', './natives', './logging', './jvm', './exceptions', './java_object'], function(require, exports, __util__, __opcodes__, __attributes__, __natives__, __logging__, __JVM__, __exceptions__, __java_object__) {
var util = __util__;
var opcodes = __opcodes__;
var attributes = __attributes__;
var natives = __natives__;
var logging = __logging__;
var JVM = __JVM__;
var exceptions = __exceptions__;
var java_object = __java_object__;
var ReturnException = exceptions.ReturnException;
var vtrace = logging.vtrace, trace = logging.trace, debug_vars = logging.debug_vars, native_methods = natives.native_methods, trapped_methods = natives.trapped_methods;
var JavaArray = java_object.JavaArray;
var JavaObject = java_object.JavaObject;
var thread_name = java_object.thread_name;
var AbstractMethodField = (function () {
function AbstractMethodField(cls) {
this.cls = cls;
}
AbstractMethodField.prototype.parse = function (bytes_array, constant_pool, idx) {
this.idx = idx;
this.access_byte = bytes_array.get_uint(2);
this.access_flags = util.parse_flags(this.access_byte);
this.name = constant_pool.get(bytes_array.get_uint(2)).value;
this.raw_descriptor = constant_pool.get(bytes_array.get_uint(2)).value;
this.parse_descriptor(this.raw_descriptor);
this.attrs = attributes.make_attributes(bytes_array, constant_pool);
};
AbstractMethodField.prototype.get_attribute = function (name) {
for (var i = 0; i < this.attrs.length; i++) {
var attr = this.attrs[i];
if (attr.name === name) {
return attr;
}
}
return null;
};
AbstractMethodField.prototype.get_attributes = function (name) {
return this.attrs.filter(function (attr) {
return attr.name === name;
});
};
// To satiate TypeScript. Consider it an 'abstract' method.
AbstractMethodField.prototype.parse_descriptor = function (raw_descriptor) {
throw new Error("Unimplemented error.");
};
return AbstractMethodField;
})();
exports.AbstractMethodField = AbstractMethodField;
var Field = (function (_super) {
__extends(Field, _super);
function Field() {
_super.apply(this, arguments);
}
Field.prototype.parse_descriptor = function (raw_descriptor) {
this.type = raw_descriptor;
};
// Must be called asynchronously.
Field.prototype.reflector = function (rs, success_fn, failure_fn) {
var _this = this;
var found = this.get_attribute("Signature");
// note: sig is the generic type parameter (if one exists), not the full
// field type.
var sig = (found != null) ? found.sig : null;
function create_obj(clazz_obj, type_obj) {
var field_cls = rs.get_bs_class('Ljava/lang/reflect/Field;');
return new JavaObject(rs, field_cls, {
// XXX this leaves out 'annotations'
'Ljava/lang/reflect/Field;clazz': clazz_obj,
'Ljava/lang/reflect/Field;name': rs.init_string(_this.name, true),
'Ljava/lang/reflect/Field;type': type_obj,
'Ljava/lang/reflect/Field;modifiers': _this.access_byte,
'Ljava/lang/reflect/Field;slot': _this.idx,
'Ljava/lang/reflect/Field;signature': sig != null ? rs.init_string(sig) : null
});
}
;
var clazz_obj = this.cls.get_class_object(rs);
// type_obj may not be loaded, so we asynchronously load it here.
// In the future, we can speed up reflection by having a synchronous_reflector
// method that we can try first, and which may fail.
this.cls.loader.resolve_class(rs, this.type, (function (type_cls) {
var type_obj = type_cls.get_class_object(rs);
var rv = create_obj(clazz_obj, type_obj);
success_fn(rv);
}), failure_fn);
};
return Field;
})(AbstractMethodField);
exports.Field = Field;
var Method = (function (_super) {
__extends(Method, _super);
function Method() {
_super.apply(this, arguments);
}
Method.prototype.parse_descriptor = function (raw_descriptor) {
this.reset_caches = false;
var match = /\(([^)]*)\)(.*)/.exec(raw_descriptor);
var param_str = match[1];
var return_str = match[2];
var param_carr = param_str.split('');
this.param_types = [];
var field;
while (field = util.carr2descriptor(param_carr)) {
this.param_types.push(field);
}
this.param_bytes = 0;
for (var i = 0; i < this.param_types.length; i++) {
var p = this.param_types[i];
this.param_bytes += (p === 'D' || p === 'J') ? 2 : 1;
}
if (!this.access_flags["static"]) {
this.param_bytes++;
}
this.num_args = this.param_types.length;
if (!this.access_flags["static"]) {
// nonstatic methods get 'this'
this.num_args++;
}
this.return_type = return_str;
};
Method.prototype.full_signature = function () {
return this.cls.get_type() + "::" + this.name + this.raw_descriptor;
};
Method.prototype.parse = function (bytes_array, constant_pool, idx) {
_super.prototype.parse.call(this, bytes_array, constant_pool, idx);
var sig = this.full_signature();
var c;
if ((c = trapped_methods[sig]) != null) {
this.code = c;
this.access_flags["native"] = true;
} else if (this.access_flags["native"]) {
if ((c = native_methods[sig]) != null) {
this.code = c;
} else if (sig.indexOf('::registerNatives()V', 1) < 0 && sig.indexOf('::initIDs()V', 1) < 0) {
if (JVM.show_NYI_natives) {
console.log(sig);
}
this.code = function (rs) {
rs.java_throw(rs.get_bs_class('Ljava/lang/UnsatisfiedLinkError;'), "Native method '" + sig + "' not implemented.\nPlease fix or file a bug at https://github.com/int3/doppio/issues");
};
} else {
this.code = null;
}
} else if (!this.access_flags.abstract) {
this.has_bytecode = true;
this.code = this.get_attribute('Code');
}
};
Method.prototype.reflector = function (rs, is_constructor, success_fn, failure_fn) {
var _ref3, _ref4, _ref5, _ref6, _ref7, _this = this;
if (is_constructor == null) {
is_constructor = false;
}
var typestr = is_constructor ? 'Ljava/lang/reflect/Constructor;' : 'Ljava/lang/reflect/Method;';
var exceptions = (_ref3 = (_ref4 = this.get_attribute("Exceptions")) != null ? _ref4.exceptions : void 0) != null ? _ref3 : [];
var anns = (_ref5 = this.get_attribute("RuntimeVisibleAnnotations")) != null ? _ref5.raw_bytes : void 0;
var adefs = (_ref6 = this.get_attribute("AnnotationDefault")) != null ? _ref6.raw_bytes : void 0;
var sig = (_ref7 = this.get_attribute("Signature")) != null ? _ref7.sig : void 0;
var obj = {};
var clazz_obj = this.cls.get_class_object(rs);
this.cls.loader.resolve_class(rs, this.return_type, (function (rt_cls) {
var rt_obj = rt_cls.get_class_object(rs);
var j = -1;
var etype_objs = [];
var i = -1;
var param_type_objs = [];
var k = 0;
var handlers;
if (_this.code != null && _this.code.exception_handlers != null && _this.code.exception_handlers.length > 0) {
handlers = [{ catch_type: 'Ljava/lang/Throwable;' }];
Array.prototype.push.apply(handlers, _this.code.exception_handlers);
} else {
handlers = [];
}
function fetch_catch_type() {
if (k < handlers.length) {
var eh = handlers[k++];
if (eh.catch_type === '<any>') {
return fetch_catch_type();
}
return _this.cls.loader.resolve_class(rs, eh.catch_type, fetch_catch_type, failure_fn);
} else {
return fetch_ptype();
}
}
;
function fetch_etype() {
j++;
if (j < exceptions.length) {
var e_desc = exceptions[j];
return _this.cls.loader.resolve_class(rs, e_desc, (function (cls) {
etype_objs[j] = cls.get_class_object(rs);
return fetch_etype();
}), failure_fn);
} else {
var jco_arr_cls = rs.get_bs_class('[Ljava/lang/Class;');
var byte_arr_cls = rs.get_bs_class('[B');
var cls = rs.get_bs_class(typestr);
obj[typestr + 'clazz'] = clazz_obj;
obj[typestr + 'name'] = rs.init_string(_this.name, true);
obj[typestr + 'parameterTypes'] = new JavaArray(rs, jco_arr_cls, param_type_objs);
obj[typestr + 'returnType'] = rt_obj;
obj[typestr + 'exceptionTypes'] = new JavaArray(rs, jco_arr_cls, etype_objs);
obj[typestr + 'modifiers'] = _this.access_byte;
obj[typestr + 'slot'] = _this.idx;
obj[typestr + 'signature'] = sig != null ? rs.init_string(sig) : null;
obj[typestr + 'annotations'] = anns != null ? new JavaArray(rs, byte_arr_cls, anns) : null;
obj[typestr + 'annotationDefault'] = adefs != null ? new JavaArray(rs, byte_arr_cls, adefs) : null;
return success_fn(new JavaObject(rs, cls, obj));
}
}
;
function fetch_ptype() {
i++;
if (i < _this.param_types.length) {
return _this.cls.loader.resolve_class(rs, _this.param_types[i], (function (cls) {
param_type_objs[i] = cls.get_class_object(rs);
return fetch_ptype();
}), failure_fn);
} else {
return fetch_etype();
}
}
;
return fetch_catch_type();
}), failure_fn);
};
Method.prototype.take_params = function (caller_stack) {
var start = caller_stack.length - this.param_bytes;
var params = caller_stack.slice(start);
caller_stack.length -= this.param_bytes;
return params;
};
Method.prototype.convert_params = function (rs, params) {
var converted_params = [rs];
var param_idx = 0;
if (!this.access_flags["static"]) {
converted_params.push(params[0]);
param_idx = 1;
}
for (var i = 0; i < this.param_types.length; i++) {
var p = this.param_types[i];
converted_params.push(params[param_idx]);
param_idx += (p === 'J' || p === 'D') ? 2 : 1;
}
return converted_params;
};
Method.prototype.run_manually = function (func, rs, converted_params) {
trace("entering native method " + this.full_signature());
var rv;
try {
rv = func.apply(null, converted_params);
} catch (_error) {
var e = _error;
if (e === ReturnException) {
return;
}
throw e;
}
rs.meta_stack().pop();
var ret_type = this.return_type;
if (ret_type !== 'V') {
if (ret_type === 'Z') {
rs.push(rv + 0);
} else {
rs.push(rv);
}
if (ret_type === 'J' || ret_type === 'D') {
rs.push(null);
}
}
};
Method.prototype.initialize = function () {
this.reset_caches = true;
};
Method.prototype.method_lock = function (rs) {
if (this.access_flags["static"]) {
return this.cls.get_class_object(rs);
} else {
return rs.cl(0);
}
};
Method.prototype.run_bytecode = function (rs) {
trace("entering method " + this.full_signature());
var code = this.code.opcodes;
if (this.reset_caches) {
for (var i = 0; i < code.length; i++) {
var instr = code[i];
if (instr != null) {
instr.reset_cache();
}
}
}
var cf = rs.curr_frame();
if (this.access_flags.synchronized && cf.pc === 0) {
if (!opcodes.monitorenter(rs, this.method_lock(rs))) {
cf.pc = 0;
return;
}
}
var op = code[cf.pc];
while (!rs.should_return) {
var annotation;
if (!((typeof RELEASE !== "undefined" && RELEASE !== null) || logging.log_level < logging.VTRACE)) {
var pc = cf.pc;
if (!op) {
throw this.name + ":" + pc + " => (null)";
}
annotation = op.annotate(pc, this.cls.constant_pool);
}
if (op.execute(rs) === false) {
break;
}
if (!((typeof RELEASE !== "undefined" && RELEASE !== null) || logging.log_level < logging.VTRACE)) {
vtrace(this.cls.get_type() + "::" + this.name + ":" + pc + " => " + op.name + annotation);
var depth = rs.meta_stack().length();
vtrace("D: " + depth + ", S: [" + debug_vars(cf.stack) + "], L: [" + debug_vars(cf.locals) + "], T: " + (!rs.curr_thread.fake ? thread_name(rs, rs.curr_thread) : ""));
}
cf.pc += 1 + op.byte_count;
op = code[cf.pc];
}
rs.should_return = false;
};
Method.prototype.setup_stack = function (runtime_state) {
var sf;
var _this = this;
var ms = runtime_state.meta_stack();
var caller_stack = runtime_state.curr_frame().stack;
var params = this.take_params(caller_stack);
if (this.access_flags["native"]) {
if (this.code != null) {
ms.push(sf = runtime_state.construct_stackframe(this, [], []));
var c_params = this.convert_params(runtime_state, params);
sf.runner = function () {
return _this.run_manually(_this.code, runtime_state, c_params);
};
return sf;
}
return null;
}
if (this.access_flags.abstract) {
var err_cls = runtime_state.get_bs_class('Ljava/lang/Error;');
runtime_state.java_throw(err_cls, "called abstract method: " + this.full_signature());
}
ms.push(sf = runtime_state.construct_stackframe(this, params, []));
if (this.code.run_stamp < runtime_state.run_stamp) {
this.code.run_stamp = runtime_state.run_stamp;
this.code.parse_code();
if (this.access_flags.synchronized) {
for (var i in this.code.opcodes) {
var c = this.code.opcodes[i];
if (c.name.match(/^[ildfa]?return$/)) {
(function (c) {
c.execute = function (rs) {
opcodes.monitorexit(rs, _this.method_lock(rs));
return c.orig_execute(rs);
};
})(c);
}
}
}
}
sf.runner = function () {
return _this.run_bytecode(runtime_state);
};
return sf;
};
return Method;
})(AbstractMethodField);
exports.Method = Method;
});
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
define('src/ClassData',["require", "exports", './util', './ConstantPool', './attributes', './java_object', './logging', './methods', './natives'], function(require, exports, __util__, __ConstantPool__, __attributes__, __java_object__, __logging__, __methods__, __natives__) {
var util = __util__;
var ConstantPool = __ConstantPool__;
var attributes = __attributes__;
var java_object = __java_object__;
var JavaObject = java_object.JavaObject;
var JavaClassObject = java_object.JavaClassObject;
var logging = __logging__;
var methods = __methods__;
var natives = __natives__;
var trace = logging.trace;
var ClassData = (function () {
function ClassData(loader) {
if (!natives.instantiated) {
natives.instantiate(ReferenceClassData, PrimitiveClassData, ArrayClassData);
}
this.loader = loader != null ? loader : null;
this.access_flags = null;
this.initialized = false;
this.resolved = false;
this.jco = null;
this.reset_bit = 0;
}
ClassData.prototype.reset = function () {
this.jco = null;
this.reset_bit = 0;
var sc = this.get_super_class();
if (sc != null && sc.reset_bit === 1) {
sc.reset();
}
var _ref1 = this.get_interfaces;
for (var _i = 0, _len = _ref1.length; _i < _len; _i++) {
var iface = _ref1[_i];
if (iface.reset_bit === 1) {
iface.reset();
}
}
};
ClassData.prototype.toExternalString = function () {
return util.ext_classname(this.this_class);
};
ClassData.prototype.getLoadState = function () {
if (this.initialized) {
return 'initialized';
} else if (this.resolved) {
return 'resolved';
} else {
return 'loaded';
}
};
ClassData.prototype.get_class_loader = function () {
return this.loader;
};
ClassData.prototype.get_type = function () {
return this.this_class;
};
ClassData.prototype.get_super_class_type = function () {
return this.super_class;
};
ClassData.prototype.get_super_class = function () {
return this.super_class_cdata;
};
ClassData.prototype.get_interface_types = function () {
return [];
};
ClassData.prototype.get_interfaces = function () {
return [];
};
ClassData.prototype.get_class_object = function (rs) {
if (this.jco == null) {
this.jco = new JavaClassObject(rs, this);
}
return this.jco;
};
ClassData.prototype.get_method = function (name) {
return null;
};
ClassData.prototype.get_methods = function () {
return {};
};
ClassData.prototype.get_fields = function () {
return [];
};
ClassData.prototype.method_lookup = function (rs, sig) {
var err_cls = rs.get_bs_class('Ljava/lang/NoSuchMethodError;');
rs.java_throw(err_cls, "No such method found in " + util.ext_classname(this.get_type()) + "::" + sig);
return null;
};
ClassData.prototype.field_lookup = function (rs, name) {
var err_cls = rs.get_bs_class('Ljava/lang/NoSuchFieldError;');
rs.java_throw(err_cls, "No such field found in " + util.ext_classname(this.get_type()) + "::" + name);
return null;
};
ClassData.prototype.is_initialized = function () {
if (this.initialized) {
return true;
}
if (!this.is_resolved()) {
return false;
}
if (this.get_method('<clinit>()V') != null) {
return false;
}
var scls = this.get_super_class();
this.initialized = (scls != null && scls.is_initialized());
return this.initialized;
};
ClassData.prototype.is_resolved = function () {
return this.resolved;
};
ClassData.prototype.is_subinterface = function (target) {
return false;
};
ClassData.prototype.is_subclass = function (target) {
if (this === target) {
return true;
}
if (this.get_super_class() == null) {
return false;
}
return this.get_super_class().is_subclass(target);
};
ClassData.prototype.is_castable = function (target) {
throw new Error("Unimplemented.");
};
return ClassData;
})();
exports.ClassData = ClassData;
var PrimitiveClassData = (function (_super) {
__extends(PrimitiveClassData, _super);
function PrimitiveClassData(this_class, loader) {
_super.call(this, loader);
this.this_class = this_class;
this.initialized = true;
this.resolved = true;
}
PrimitiveClassData.prototype.is_castable = function (target) {
return this.this_class === target.this_class;
};
PrimitiveClassData.prototype.box_class_name = function () {
switch (this.this_class) {
case 'B':
return 'Ljava/lang/Byte;';
case 'C':
return 'Ljava/lang/Character;';
case 'D':
return 'Ljava/lang/Double;';
case 'F':
return 'Ljava/lang/Float;';
case 'I':
return 'Ljava/lang/Integer;';
case 'J':
return 'Ljava/lang/Long;';
case 'S':
return 'Ljava/lang/Short;';
case 'Z':
return 'Ljava/lang/Boolean;';
default:
throw new Error("Tried to box a non-primitive class: " + this.this_class);
}
};
PrimitiveClassData.prototype.create_wrapper_object = function (rs, value) {
var box_name = this.box_class_name();
var box_cls = rs.get_bs_class(box_name);
var wrapped = new JavaObject(rs, box_cls);
wrapped.fields[box_name + 'value'] = value;
return wrapped;
};
return PrimitiveClassData;
})(ClassData);
exports.PrimitiveClassData = PrimitiveClassData;
var ArrayClassData = (function (_super) {
__extends(ArrayClassData, _super);
function ArrayClassData(component_type, loader) {
_super.call(this, loader);
this.component_type = component_type;
this.this_class = "[" + this.component_type;
this.super_class = 'Ljava/lang/Object;';
this.access_flags = util.parse_flags(0);
}
ArrayClassData.prototype.reset = function () {
_super.prototype.reset.call(this);
var ccls = this.get_component_class();
if (ccls && ccls.reset_bit) {
ccls.reset();
}
};
ArrayClassData.prototype.get_component_type = function () {
return this.component_type;
};
ArrayClassData.prototype.get_component_class = function () {
return this.component_class_cdata;
};
ArrayClassData.prototype.field_lookup = function (rs, name) {
return this.super_class_cdata.field_lookup(rs, name);
};
ArrayClassData.prototype.method_lookup = function (rs, sig) {
return this.super_class_cdata.method_lookup(rs, sig);
};
ArrayClassData.prototype.set_resolved = function (super_class_cdata, component_class_cdata) {
this.super_class_cdata = super_class_cdata;
this.component_class_cdata = component_class_cdata;
this.resolved = true;
this.initialized = true;
};
ArrayClassData.prototype.is_castable = function (target) {
if (!(target instanceof ArrayClassData)) {
if (target instanceof PrimitiveClassData) {
return false;
}
if (target.access_flags["interface"]) {
var type = target.get_type();
return type === 'Ljava/lang/Cloneable;' || type === 'Ljava/io/Serializable;';
}
return target.get_type() === 'Ljava/lang/Object;';
}
return this.get_component_class().is_castable((target).get_component_class());
};
return ArrayClassData;
})(ClassData);
exports.ArrayClassData = ArrayClassData;
var ReferenceClassData = (function (_super) {
__extends(ReferenceClassData, _super);
function ReferenceClassData(buffer, loader) {
_super.call(this, loader);
var bytes_array = new util.BytesArray(buffer);
if ((bytes_array.get_uint(4)) !== 0xCAFEBABE) {
throw "Magic number invalid";
}
this.minor_version = bytes_array.get_uint(2);
this.major_version = bytes_array.get_uint(2);
if (!(45 <= this.major_version && this.major_version <= 51)) {
throw "Major version invalid";
}
this.constant_pool = new ConstantPool.ConstantPool();
this.constant_pool.parse(bytes_array);
this.access_byte = bytes_array.get_uint(2);
this.access_flags = util.parse_flags(this.access_byte);
this.this_class = this.constant_pool.get(bytes_array.get_uint(2)).deref();
var super_ref = bytes_array.get_uint(2);
if (super_ref !== 0) {
this.super_class = this.constant_pool.get(super_ref).deref();
}
var isize = bytes_array.get_uint(2);
this.interfaces = [];
for (var _i = 0; _i < isize; ++_i) {
this.interfaces.push(this.constant_pool.get(bytes_array.get_uint(2)).deref());
}
var num_fields = bytes_array.get_uint(2);
this.fields = [];
for (var _i = 0; _i < num_fields; ++_i) {
this.fields.push(new methods.Field(this));
}
this.fl_cache = {};
for (var i = 0, _len = this.fields.length; i < _len; ++i) {
var f = this.fields[i];
f.parse(bytes_array, this.constant_pool, i);
this.fl_cache[f.name] = f;
}
var num_methods = bytes_array.get_uint(2);
this.methods = {};
this.ml_cache = {};
for (var i = 0; i < num_methods; i += 1) {
var m = new methods.Method(this);
m.parse(bytes_array, this.constant_pool, i);
var mkey = m.name + m.raw_descriptor;
this.methods[mkey] = m;
}
this.attrs = attributes.make_attributes(bytes_array, this.constant_pool);
if (bytes_array.has_bytes()) {
throw "Leftover bytes in classfile: " + bytes_array;
}
this.static_fields = Object.create(null);
}
ReferenceClassData.prototype.reset = function () {
_super.prototype.reset.call(this);
this.initialized = false;
this.static_fields = Object.create(null);
for (var k in this.methods) {
this.methods[k].initialize();
}
};
ReferenceClassData.prototype.get_interfaces = function () {
return this.interface_cdatas;
};
ReferenceClassData.prototype.get_interface_types = function () {
return this.interfaces;
};
ReferenceClassData.prototype.get_fields = function () {
return this.fields;
};
ReferenceClassData.prototype.get_method = function (sig) {
return this.methods[sig];
};
ReferenceClassData.prototype.get_methods = function () {
return this.methods;
};
ReferenceClassData.prototype.get_attribute = function (name) {
var _ref1 = this.attrs;
for (var _i = 0, _len = _ref1.length; _i < _len; _i++) {
var attr = _ref1[_i];
if (attr.name === name) {
return attr;
}
}
return null;
};
ReferenceClassData.prototype.get_attributes = function (name) {
var _ref1 = this.attrs;
var _results = [];
for (var _i = 0, _len = _ref1.length; _i < _len; _i++) {
var attr = _ref1[_i];
if (attr.name === name) {
_results.push(attr);
}
}
return _results;
};
ReferenceClassData.prototype.get_default_fields = function () {
if (this.default_fields) {
return this.default_fields;
}
this.construct_default_fields();
return this.default_fields;
};
ReferenceClassData.prototype._initialize_static_field = function (rs, name) {
var f = this.fl_cache[name];
if (f != null && f.access_flags["static"]) {
var cva = f.get_attribute('ConstantValue');
if (cva != null) {
var cv = f.type === 'Ljava/lang/String;' ? rs.init_string(cva.value) : cva.value;
}
this.static_fields[name] = cv != null ? cv : util.initial_value(f.raw_descriptor);
} else {
rs.java_throw(this.loader.get_initialized_class('Ljava/lang/NoSuchFieldError;'), name);
}
};
ReferenceClassData.prototype.static_get = function (rs, name) {
if (this.static_fields[name] !== void 0) {
return this.static_fields[name];
}
this._initialize_static_field(rs, name);
return this.static_get(rs, name);
};
ReferenceClassData.prototype.static_put = function (rs, name, val) {
if (this.static_fields[name] !== void 0) {
this.static_fields[name] = val;
} else {
this._initialize_static_field(rs, name);
this.static_put(rs, name, val);
}
};
ReferenceClassData.prototype.set_resolved = function (super_class_cdata, interface_cdatas) {
this.super_class_cdata = super_class_cdata;
trace("Class " + (this.get_type()) + " is now resolved.");
this.interface_cdatas = interface_cdatas;
this.resolved = true;
};
ReferenceClassData.prototype.construct_default_fields = function () {
var cls = this;
this.default_fields = Object.create(null);
while (cls != null) {
var _ref1 = cls.fields;
for (var _i = 0, _len = _ref1.length; _i < _len; _i++) {
var f = _ref1[_i];
if (!(!f.access_flags["static"])) {
continue;
}
var val = util.initial_value(f.raw_descriptor);
this.default_fields[cls.get_type() + f.name] = val;
}
cls = cls.get_super_class();
}
};
ReferenceClassData.prototype.field_lookup = function (rs, name, null_handled) {
var field = this.fl_cache[name];
if (field != null) {
return field;
}
field = this._field_lookup(rs, name);
if ((field != null) || null_handled === true) {
this.fl_cache[name] = field;
return field;
}
var err_cls = rs.get_bs_class('Ljava/lang/NoSuchFieldError;');
rs.java_throw(err_cls, "No such field found in " + util.ext_classname(this.get_type()) + "::" + name);
return null;
};
ReferenceClassData.prototype._field_lookup = function (rs, name) {
for (var i = 0; i < this.fields.length; i++) {
var field = this.fields[i];
if (field.name === name) {
return field;
}
}
// These may not be initialized! But we have them loaded.
var ifaces = this.get_interfaces();
for (var i = 0; i < ifaces.length; i++) {
var field = ifaces[i].field_lookup(rs, name, true);
if (field != null) {
return field;
}
}
var sc = this.get_super_class();
if (sc != null) {
var field = sc.field_lookup(rs, name, true);
if (field != null) {
return field;
}
}
return null;
};
ReferenceClassData.prototype.method_lookup = function (rs, sig) {
if (this.ml_cache[sig] != null) {
return this.ml_cache[sig];
}
var method = this._method_lookup(rs, sig);
if (method == null) {
var err_cls = rs.get_bs_class('Ljava/lang/NoSuchMethodError;');
rs.java_throw(err_cls, "No such method found in " + util.ext_classname(this.get_type()) + "::" + sig);
}
if (method.code != null && method.code.exception_handlers != null) {
var handlers = method.code.exception_handlers;
for (var _i = 0, _len = handlers.length; _i < _len; _i++) {
var eh = handlers[_i];
if (!(eh.catch_type === '<any>' || ((this.loader.get_resolved_class(eh.catch_type, true)) != null))) {
return null;
}
}
}
return method;
};
ReferenceClassData.prototype._method_lookup = function (rs, sig) {
if (sig in this.ml_cache) {
return this.ml_cache[sig];
}
if (sig in this.methods) {
return this.ml_cache[sig] = this.methods[sig];
}
var parent = this.get_super_class();
if (parent != null) {
this.ml_cache[sig] = parent._method_lookup(rs, sig);
if (this.ml_cache[sig] != null) {
return this.ml_cache[sig];
}
}
var _ref1 = this.get_interfaces();
for (var _i = 0, _len = _ref1.length; _i < _len; _i++) {
var ifc = _ref1[_i];
this.ml_cache[sig] = ifc._method_lookup(rs, sig);
if (this.ml_cache[sig] != null) {
return this.ml_cache[sig];
}
}
return this.ml_cache[sig] = null;
};
ReferenceClassData.prototype.resolve_method = function (rs, sig, success_fn, failure_fn) {
var _this = this;
trace("ASYNCHRONOUS: resolve_method " + sig);
var m = this.method_lookup(rs, sig);
var handlers = m.code.exception_handlers;
var i = 0;
var next_handler = function () {
if (i === handlers.length) {
return success_fn(m);
} else {
var eh = handlers[i++];
if (!(eh.catch_type === '<any>' || _this.loader.get_resolved_class(eh.catch_type, true))) {
return _this.loader.resolve_class(rs, eh.catch_type, next_handler, failure_fn);
} else {
return next_handler();
}
}
};
return next_handler();
};
ReferenceClassData.prototype.is_castable = function (target) {
if (!(target instanceof ReferenceClassData)) {
return false;
}
if (this.access_flags["interface"]) {
if (target.access_flags["interface"]) {
return this.is_subinterface(target);
}
if (!target.access_flags["interface"]) {
return target.get_type() === 'Ljava/lang/Object;';
}
} else {
if (target.access_flags["interface"]) {
return this.is_subinterface(target);
}
return this.is_subclass(target);
}
};
ReferenceClassData.prototype.is_subinterface = function (target) {
if (this.this_class === target.this_class) {
return true;
}
var _ref1 = this.get_interfaces();
for (var _i = 0, _len = _ref1.length; _i < _len; _i++) {
var super_iface = _ref1[_i];
if (super_iface.is_subinterface(target)) {
return true;
}
}
if (this.get_super_class() == null) {
return false;
}
return this.get_super_class().is_subinterface(target);
};
return ReferenceClassData;
})(ClassData);
exports.ReferenceClassData = ReferenceClassData;
});
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
define('src/ClassLoader',["require", "exports", './ClassData', './util', './logging', './exceptions', './java_object'], function(require, exports, __ClassData__, __util__, __logging__, __exceptions__, __java_object__) {
var ClassData = __ClassData__;
var ReferenceClassData = ClassData.ReferenceClassData, PrimitiveClassData = ClassData.PrimitiveClassData, ArrayClassData = ClassData.ArrayClassData;
var util = __util__;
var logging = __logging__;
var trace = logging.trace;
var exceptions = __exceptions__;
var JavaException = exceptions.JavaException;
var java_object = __java_object__;
var JavaObject = java_object.JavaObject;
var ClassLoader = (function () {
function ClassLoader(bootstrap) {
this.bootstrap = bootstrap;
this.loaded_classes = Object.create(null);
}
ClassLoader.prototype.serialize = function (visited) {
throw new Error('Abstract method!');
};
ClassLoader.prototype.get_package_names = function () {
var classes = this.get_loaded_class_list(true);
var pkg_names = {};
for (var _i = 0, _len = classes.length; _i < _len; _i++) {
var cls = classes[_i];
pkg_names[cls.substring(0, (cls.lastIndexOf('/')) + 1)] = true;
}
return Object.keys(pkg_names);
};
ClassLoader.prototype.get_loaded_class_list = function (ref_class_only) {
if (ref_class_only == null) {
ref_class_only = false;
}
if (ref_class_only) {
var _ref1 = this.loaded_classes;
var _results = [];
for (var k in _ref1) {
var cdata = _ref1[k];
if ('major_version' in cdata) {
_results.push(k.slice(1, -1));
}
}
return _results;
} else {
return Object.keys(this.loaded_classes);
}
};
ClassLoader.prototype.remove_class = function (type_str) {
var cdata, k, _ref1;
this._rem_class(type_str);
if (util.is_primitive_type(type_str)) {
return;
}
_ref1 = this.loaded_classes;
for (k in _ref1) {
cdata = _ref1[k];
if (type_str === (typeof cdata.get_component_type === "function" ? cdata.get_component_type() : void 0) || type_str === cdata.get_super_class_type()) {
this.remove_class(k);
}
}
};
ClassLoader.prototype._rem_class = function (type_str) {
delete this.loaded_classes[type_str];
};
ClassLoader.prototype._add_class = function (type_str, cdata) {
this.loaded_classes[type_str] = cdata;
};
ClassLoader.prototype._get_class = function (type_str) {
var cdata = this.loaded_classes[type_str];
if (cdata != null && cdata.reset_bit === 1) {
cdata.reset();
}
if (cdata != null) {
return cdata;
} else {
return null;
}
};
ClassLoader.prototype._try_define_array_class = function (type_str) {
var component_type = util.get_component_type(type_str);
var component_cdata = this.get_resolved_class(component_type, true);
if (component_cdata == null) {
return null;
}
return this._define_array_class(type_str, component_cdata);
};
ClassLoader.prototype._define_array_class = function (type_str, component_cdata) {
if (component_cdata.get_class_loader() !== this) {
return component_cdata.get_class_loader()._define_array_class(type_str, component_cdata);
} else {
var cdata = new ArrayClassData(component_cdata.get_type(), this);
this._add_class(type_str, cdata);
cdata.set_resolved(this.bootstrap.get_resolved_class('Ljava/lang/Object;'), component_cdata);
return cdata;
}
};
ClassLoader.prototype._parallel_class_resolve = function (rs, types, success_fn, failure_fn, explicit) {
var _this = this;
if (explicit == null) {
explicit = false;
}
var pending_requests = types.length;
var failure = null;
var resolved = [];
var request_finished = function () {
pending_requests--;
if (pending_requests === 0) {
if (failure == null) {
return success_fn(resolved);
} else {
return failure_fn(failure);
}
}
};
var fetch_data = function (type) {
return _this.resolve_class(rs, type, (function (cdata) {
resolved.push(cdata);
return request_finished();
}), (function (f_fn) {
failure = f_fn;
return request_finished();
}), explicit);
};
for (var _i = 0, _len = types.length; _i < _len; _i++) {
fetch_data(types[_i]);
}
};
ClassLoader.prototype._regular_class_resolve = function (rs, types, success_fn, failure_fn, explicit) {
var _this = this;
if (explicit == null) {
explicit = false;
}
if (!(types.length > 0)) {
return success_fn(null);
}
var resolved = [];
var fetch_class = function (type) {
return _this.resolve_class(rs, type, (function (cdata) {
resolved.push(cdata);
if (types.length > 0) {
return fetch_class(types.shift());
} else {
return success_fn(resolved);
}
}), failure_fn, explicit);
};
return fetch_class(types.shift());
};
ClassLoader.prototype.define_class = function (rs, type_str, data, success_fn, failure_fn, parallel, explicit) {
var _this = this;
if (parallel == null) {
parallel = false;
}
if (explicit == null) {
explicit = false;
}
trace("Defining class " + type_str + "...");
var cdata = new ReferenceClassData(data, this);
var type = cdata.get_type();
if (type !== type_str) {
var msg = util.descriptor2typestr(type_str) + " (wrong name: " + util.descriptor2typestr(type) + ")";
return failure_fn((function () {
var err_cls = _this.get_initialized_class('Ljava/lang/NoClassDefFoundError;');
rs.java_throw(err_cls, msg);
}));
}
this._add_class(type_str, cdata);
var types = cdata.get_interface_types();
types.push(cdata.get_super_class_type());
var to_resolve = [];
var resolved_already = [];
for (var _i = 0, _len = types.length; _i < _len; _i++) {
type = types[_i];
if (type == null) {
continue;
}
var clsdata = this.get_resolved_class(type, true);
if (clsdata != null) {
resolved_already.push(clsdata);
} else {
to_resolve.push(type);
}
}
function process_resolved_classes(cdatas) {
cdatas = resolved_already.concat(cdatas);
var super_cdata = null;
var interface_cdatas = [];
var super_type = cdata.get_super_class_type();
for (var _j = 0, _len1 = cdatas.length; _j < _len1; _j++) {
var a_cdata = cdatas[_j];
type = a_cdata.get_type();
if (type === super_type) {
super_cdata = a_cdata;
} else {
interface_cdatas.push(a_cdata);
}
}
cdata.set_resolved(super_cdata, interface_cdatas);
return success_fn(cdata);
}
if (to_resolve.length > 0) {
if (false) {
return this._parallel_class_resolve(rs, to_resolve, process_resolved_classes, failure_fn, explicit);
} else {
return this._regular_class_resolve(rs, to_resolve, process_resolved_classes, failure_fn, explicit);
}
} else {
return process_resolved_classes([]);
}
};
ClassLoader.prototype.get_loaded_class = function (type_str, null_handled) {
if (null_handled == null) {
null_handled = false;
}
var cdata = this._get_class(type_str);
if (cdata != null) {
return cdata;
}
if (util.is_array_type(type_str)) {
cdata = this._try_define_array_class(type_str);
if (cdata != null) {
return cdata;
}
}
if (util.is_primitive_type(type_str)) {
return this.bootstrap.get_primitive_class(type_str);
}
if (null_handled) {
return null;
}
throw new Error("Error in get_loaded_class: Class " + type_str + " is not loaded.");
};
ClassLoader.prototype.get_resolved_class = function (type_str, null_handled) {
if (null_handled == null) {
null_handled = false;
}
var cdata = this.get_loaded_class(type_str, null_handled);
if (cdata != null && cdata.is_resolved()) {
return cdata;
}
if (null_handled) {
return null;
}
throw new Error("Error in get_resolved_class: Class " + type_str + " is not resolved.");
};
ClassLoader.prototype.get_initialized_class = function (type_str, null_handled) {
if (null_handled == null) {
null_handled = false;
}
var cdata = this.get_resolved_class(type_str, null_handled);
if (cdata != null && cdata.is_initialized()) {
return cdata;
}
if (null_handled) {
return null;
}
throw new Error("Error in get_initialized_class: Class " + type_str + " is not initialized.");
};
ClassLoader.prototype._initialize_class = function (rs, cdata, success_fn, failure_fn) {
var _this = this;
trace("Actually initializing class " + (cdata.get_type()) + "...");
if (!(cdata instanceof ReferenceClassData)) {
if (typeof UNSAFE !== "undefined" && UNSAFE !== null) {
throw new Error("Tried to initialize a non-reference type: " + cdata.get_type());
}
}
var first_clinit = true;
var first_native_frame = rs.construct_nativeframe("$clinit", (function () {
if (rs.curr_frame() !== first_native_frame) {
throw new Error("The top of the meta stack should be this native frame, but it is not: " + (rs.curr_frame().name) + " at " + (rs.meta_stack().length()));
}
rs.meta_stack().pop();
return rs.async_op(function () {
return success_fn(cdata);
});
}), (function (e) {
rs.curr_frame().cdata.reset();
if (e instanceof JavaException) {
if (e.exception.cls.get_type() === 'Ljava/lang/NoClassDefFoundError;') {
rs.meta_stack().pop();
throw e;
}
var nf = rs.curr_frame();
nf.runner = function () {
var rv = rs.pop();
rs.meta_stack().pop();
throw new JavaException(rv);
};
nf.error = function () {
rs.meta_stack().pop();
return failure_fn((function () {
throw e;
}));
};
var cls = _this.bootstrap.get_resolved_class('Ljava/lang/ExceptionInInitializerError;');
var v = new JavaObject(rs, cls);
rs.push_array([v, v, e.exception]);
return cls.method_lookup(rs, '<init>(Ljava/lang/Throwable;)V').setup_stack(rs);
} else {
rs.meta_stack().pop();
throw e;
}
}));
first_native_frame.cdata = cdata;
var class_file = cdata;
while ((class_file != null) && !class_file.is_initialized()) {
trace("initializing class: " + (class_file.get_type()));
class_file.initialized = true;
var clinit = class_file.get_method('<clinit>()V');
if (clinit != null) {
trace("\tFound <clinit>. Pushing stack frame.");
if (first_clinit) {
trace("\tFirst <clinit> in the loop.");
first_clinit = false;
rs.meta_stack().push(first_native_frame);
} else {
var next_nf = rs.construct_nativeframe("$clinit_secondary", (function () {
return rs.meta_stack().pop();
}), (function (e) {
rs.curr_frame().cdata.reset();
rs.meta_stack().pop();
while (!rs.curr_frame()["native"]) {
rs.meta_stack().pop();
}
return rs.async_op((function () {
failure_fn((function () {
throw e;
}), true);
}));
}));
next_nf.cdata = class_file;
rs.meta_stack().push(next_nf);
}
clinit.setup_stack(rs);
}
class_file = class_file.get_super_class();
}
if (!first_clinit) {
rs.run_until_finished((function () {
}), false, rs.stashed_done_cb);
return;
}
success_fn(cdata);
};
ClassLoader.prototype.initialize_class = function (rs, type_str, success_fn, failure_fn, explicit) {
var _this = this;
if (explicit == null) {
explicit = false;
}
trace("Initializing class " + type_str + "...");
var cdata = this.get_initialized_class(type_str, true);
if (cdata != null) {
return success_fn(cdata);
}
if (util.is_array_type(type_str)) {
var component_type = util.get_component_type(type_str);
this.resolve_class(rs, component_type, (function (cdata) {
return success_fn(_this._define_array_class(type_str, cdata));
}), failure_fn, explicit);
return;
}
cdata = this.get_resolved_class(type_str, true);
if (cdata != null) {
return this._initialize_class(rs, cdata, success_fn, failure_fn);
}
return this.resolve_class(rs, type_str, (function (cdata) {
if (cdata.is_initialized(rs)) {
return success_fn(cdata);
} else {
return _this._initialize_class(rs, cdata, success_fn, failure_fn);
}
}), failure_fn, explicit);
};
ClassLoader.prototype.resolve_class = function (rs, type_str, success_fn, failure_fn, explicit) {
var _this = this;
if (explicit == null) {
explicit = false;
}
trace("Resolving class " + type_str + "... [general]");
var rv = this.get_resolved_class(type_str, true);
if (rv != null) {
return success_fn(rv);
}
if (util.is_array_type(type_str)) {
var component_type = util.get_component_type(type_str);
this.resolve_class(rs, component_type, (function (cdata) {
return success_fn(_this._define_array_class(type_str, cdata));
}), failure_fn, explicit);
return;
}
return this._resolve_class(rs, type_str, success_fn, failure_fn, explicit);
};
ClassLoader.prototype._resolve_class = function (rs, type_str, success_fn, failure_fn, explicit) {
throw new Error("Unimplemented.");
};
return ClassLoader;
})();
exports.ClassLoader = ClassLoader;
var BootstrapClassLoader = (function (_super) {
__extends(BootstrapClassLoader, _super);
function BootstrapClassLoader(read_classfile) {
_super.call(this, this);
this.read_classfile = read_classfile;
}
BootstrapClassLoader.prototype.serialize = function (visited) {
if ('bootstrapLoader' in visited) {
return '<*bootstrapLoader>';
}
visited['bootstrapLoader'] = true;
var loaded = {};
var _ref1 = this.loaded_classes;
for (var type in _ref1) {
var cls = _ref1[type];
if (type !== "__proto__") {
loaded["" + type + "(" + (cls.getLoadState()) + ")"] = cls.loader.serialize(visited);
}
}
return {
ref: 'bootstrapLoader',
loaded: loaded
};
};
BootstrapClassLoader.prototype.reset = function () {
var _ref1 = this.loaded_classes;
for (var cname in _ref1) {
var cls = _ref1[cname];
if (cname !== "__proto__") {
cls.reset_bit = 1;
}
}
};
BootstrapClassLoader.prototype.get_primitive_class = function (type_str) {
var cdata = this._get_class(type_str);
if (cdata != null) {
return cdata;
}
cdata = new PrimitiveClassData(type_str, this);
this._add_class(type_str, cdata);
return cdata;
};
BootstrapClassLoader.prototype._resolve_class = function (rs, type_str, success_fn, failure_fn, explicit) {
var _this = this;
if (explicit == null) {
explicit = false;
}
trace("ASYNCHRONOUS: resolve_class " + type_str + " [bootstrap]");
var rv = this.get_resolved_class(type_str, true);
if (rv != null) {
return success_fn(rv);
}
this.read_classfile(type_str, (function (data) {
_this.define_class(rs, type_str, data, success_fn, failure_fn, true, explicit);
}), (function (e) {
try {
e();
} catch (exp) {
trace("Failed to read class " + type_str + ": " + exp + "\n" + exp.stack);
}
return failure_fn(function () {
rs.meta_stack().push(rs.construct_nativeframe('$class_not_found', (function () {
rs.curr_frame().runner = function () {
var rv = rs.pop();
rs.meta_stack().pop();
throw new JavaException(rv);
};
if (!explicit) {
var rv = rs.pop();
var cls = _this.bootstrap.get_initialized_class('Ljava/lang/NoClassDefFoundError;');
var v = new JavaObject(rs, cls);
rs.push_array([v, v, rv]);
return cls.method_lookup(rs, '<init>(Ljava/lang/Throwable;)V').setup_stack(rs);
}
}), (function () {
rs.meta_stack().pop();
return failure_fn((function () {
throw new Error('Failed to throw a ' + (explicit ? 'ClassNotFoundException' : 'NoClassDefFoundError') + '.');
}));
})));
var cls = _this.bootstrap.get_initialized_class('Ljava/lang/ClassNotFoundException;');
var v = new JavaObject(rs, cls);
var msg = rs.init_string(util.ext_classname(type_str));
rs.push_array([v, v, msg]);
return cls.method_lookup(rs, '<init>(Ljava/lang/String;)V').setup_stack(rs);
});
}));
};
return BootstrapClassLoader;
})(ClassLoader);
exports.BootstrapClassLoader = BootstrapClassLoader;
var CustomClassLoader = (function (_super) {
__extends(CustomClassLoader, _super);
function CustomClassLoader(bootstrap, loader_obj) {
_super.call(this, bootstrap);
this.loader_obj = loader_obj;
}
CustomClassLoader.prototype.serialize = function (visited) {
return this.loader_obj.serialize(visited);
};
CustomClassLoader.prototype._resolve_class = function (rs, type_str, success_fn, failure_fn, explicit) {
var _this = this;
if (explicit == null) {
explicit = false;
}
trace("ASYNCHRONOUS: resolve_class " + type_str + " [custom]");
rs.meta_stack().push(rs.construct_nativeframe("$" + (this.loader_obj.cls.get_type()), (function () {
var jclo = rs.pop();
rs.meta_stack().pop();
var cls = jclo.$cls;
if (_this.get_resolved_class(type_str, true) == null) {
_this._add_class(type_str, cls);
}
return rs.async_op(function () {
return success_fn(cls);
});
}), (function (e) {
rs.meta_stack().pop();
return rs.async_op(function () {
return failure_fn(function () {
throw e;
});
});
})));
rs.push2(this.loader_obj, rs.init_string(util.ext_classname(type_str)));
this.loader_obj.cls.method_lookup(rs, 'loadClass(Ljava/lang/String;)Ljava/lang/Class;').setup_stack(rs);
rs.run_until_finished((function () {
}), false, rs.stashed_done_cb);
};
return CustomClassLoader;
})(ClassLoader);
exports.CustomClassLoader = CustomClassLoader;
});
define('src/runtime',["require", "exports", './gLong', './util', './logging', './exceptions', './java_object', './jvm', './ClassLoader'], function(require, exports, __gLong__, __util__, __logging__, __exceptions__, __java_object__, __JVM__, __ClassLoader__) {
var gLong = __gLong__;
var util = __util__;
var logging = __logging__;
var exceptions = __exceptions__;
var java_object = __java_object__;
var JVM = __JVM__;
var ClassLoader = __ClassLoader__;
var vtrace = logging.vtrace;
var trace = logging.trace;
var debug = logging.debug;
var error = logging.error;
var YieldIOException = exceptions.YieldIOException;
var ReturnException = exceptions.ReturnException;
var JavaException = exceptions.JavaException;
var JavaObject = java_object.JavaObject;
var JavaArray = java_object.JavaArray;
var JavaThreadObject = java_object.JavaThreadObject;
var thread_name = java_object.thread_name;
var process = typeof node !== "undefined" ? node.process : global.process;
var CallStack = (function () {
function CallStack(initial_stack) {
this._cs = [StackFrame.native_frame('$bootstrap')];
if (initial_stack != null) {
this._cs[0].stack = initial_stack;
}
}
CallStack.prototype.snap = function () {
var visited = {};
var snapshots = this._cs.map(function (frame) {
return frame.snap(visited);
});
return { serialize: (function () {
return snapshots.map(function (ss) {
return ss.serialize();
});
}) };
};
CallStack.prototype.length = function () {
return this._cs.length;
};
CallStack.prototype.push = function (sf) {
return this._cs.push(sf);
};
CallStack.prototype.pop = function () {
return this._cs.pop();
};
CallStack.prototype.pop_n = function (n) {
this._cs.length -= n;
};
CallStack.prototype.curr_frame = function () {
return util.last(this._cs);
};
CallStack.prototype.get_caller = function (frames_to_skip) {
return this._cs[this._cs.length - 1 - frames_to_skip];
};
return CallStack;
})();
exports.CallStack = CallStack;
var StackFrame = (function () {
function StackFrame(method, locals, stack) {
this.method = method;
this.locals = locals;
this.stack = stack;
this.pc = 0;
this.runner = null;
this.native = false;
this.name = this.method.full_signature();
}
StackFrame.prototype.snap = function (visited) {
var _this = this;
var rv = {
name: this.name,
pc: this.pc,
native: this.native,
loader: null,
stack: null,
locals: null
};
function serializer(obj) {
if (obj != null && typeof obj.serialize === "function") {
return obj.serialize(visited);
}
return obj;
}
function s() {
if (_this.method.cls != null) {
rv.loader = _this.method.cls.loader.serialize(visited);
}
rv.stack = _this.stack.map(serializer);
rv.locals = _this.locals.map(serializer);
return rv;
}
return { serialize: s };
};
StackFrame.native_frame = function (name, handler, error_handler) {
// XXX: Super kludge!
var sf = new StackFrame({
full_signature: (function () {
return name;
})
}, [], []);
sf.runner = handler;
sf.name = name;
if (error_handler != null) {
sf.error = error_handler;
}
sf.native = true;
return sf;
};
return StackFrame;
})();
exports.StackFrame = StackFrame;
var run_count = 0;
var RuntimeState = (function () {
function RuntimeState(print, _async_input, bcl) {
this.print = print;
this._async_input = _async_input;
this.bcl = bcl;
this.input_buffer = [];
this.bcl.reset();
this.startup_time = gLong.fromNumber((new Date()).getTime());
this.run_stamp = ++run_count;
this.mem_start_addrs = [1];
this.mem_blocks = {};
this.high_oref = 1;
this.string_pool = new util.SafeMap();
this.lock_refs = {};
this.lock_counts = {};
this.waiting_threads = {};
this.thread_pool = [];
this.should_return = false;
var ct = new JavaThreadObject(this, null);
this.curr_thread = ct;
this.max_m_count = 100000;
}
RuntimeState.prototype.get_bs_cl = function () {
return this.bcl;
};
RuntimeState.prototype.get_bs_class = function (type, handle_null) {
if (handle_null == null) {
handle_null = false;
}
return this.bcl.get_initialized_class(type, handle_null);
};
RuntimeState.prototype.get_class = function (type, handle_null) {
if (handle_null == null) {
handle_null = false;
}
return this.curr_frame().method.cls.loader.get_initialized_class(type, handle_null);
};
RuntimeState.prototype.get_cl = function () {
return this.curr_frame().method.cls.loader;
};
// XXX: These four methods avoid circular dependencies.
RuntimeState.prototype.construct_cl = function (jclo) {
return new ClassLoader.CustomClassLoader(this.get_bs_cl(), jclo);
};
RuntimeState.prototype.construct_callstack = function () {
return new CallStack();
};
RuntimeState.prototype.construct_stackframe = function (method, locals, stack) {
return new StackFrame(method, locals, stack);
};
RuntimeState.prototype.construct_nativeframe = function (name, handler, error_handler) {
return StackFrame.native_frame(name, handler, error_handler);
};
RuntimeState.prototype.preinitialize_core_classes = function (resume_cb, except_cb) {
var core_classes = [
'Ljava/lang/Class;',
'Ljava/lang/ClassLoader;',
'Ljava/lang/String;',
'Ljava/lang/Error;',
'Ljava/lang/StackTraceElement;',
'Ljava/io/ExpiringCache;',
'Ljava/io/FileDescriptor;',
'Ljava/io/FileNotFoundException;',
'Ljava/io/IOException;',
'Ljava/io/Serializable;',
'Ljava/io/UnixFileSystem;',
'Ljava/lang/ArithmeticException;',
'Ljava/lang/ArrayIndexOutOfBoundsException;',
'Ljava/lang/ArrayStoreException;',
'Ljava/lang/ClassCastException;',
'Ljava/lang/ClassNotFoundException;',
'Ljava/lang/NoClassDefFoundError;',
'Ljava/lang/Cloneable;',
'Ljava/lang/ExceptionInInitializerError;',
'Ljava/lang/IllegalMonitorStateException;',
'Ljava/lang/InterruptedException;',
'Ljava/lang/NegativeArraySizeException;',
'Ljava/lang/NoSuchFieldError;',
'Ljava/lang/NoSuchMethodError;',
'Ljava/lang/NullPointerException;',
'Ljava/lang/reflect/Constructor;',
'Ljava/lang/reflect/Field;',
'Ljava/lang/reflect/Method;',
'Ljava/lang/System;',
'Ljava/lang/Thread;',
'Ljava/lang/ThreadGroup;',
'Ljava/lang/Throwable;',
'Ljava/lang/UnsatisfiedLinkError;',
'Ljava/nio/ByteOrder;',
'Lsun/misc/VM;',
'Lsun/reflect/ConstantPool;',
'Ljava/lang/Byte;',
'Ljava/lang/Character;',
'Ljava/lang/Double;',
'Ljava/lang/Float;',
'Ljava/lang/Integer;',
'Ljava/lang/Long;',
'Ljava/lang/Short;',
'Ljava/lang/Boolean;',
'[Lsun/management/MemoryManagerImpl;',
'[Lsun/management/MemoryPoolImpl;'
];
var i = -1;
var _this = this;
function init_next_core_class() {
trace("init_next_core_class");
i++;
if (i < core_classes.length) {
trace("Initializing " + core_classes[i]);
_this.bcl.initialize_class(_this, core_classes[i], init_next_core_class, except_cb);
} else {
trace("Preinitialization complete.");
resume_cb();
}
}
;
init_next_core_class();
};
RuntimeState.prototype.init_threads = function () {
var _this = this;
var my_sf = this.curr_frame();
var thread_group_cls = this.get_bs_class('Ljava/lang/ThreadGroup;');
var thread_cls = this.get_bs_class('Ljava/lang/Thread;');
var group = new JavaObject(this, thread_group_cls);
this.push(group);
thread_group_cls.method_lookup(this, '<init>()V').setup_stack(this);
my_sf.runner = function () {
var ct = null;
my_sf.runner = function () {
my_sf.runner = null;
ct.$meta_stack = _this.meta_stack();
_this.curr_thread = ct;
_this.curr_thread.$isAlive = true;
_this.thread_pool.push(_this.curr_thread);
thread_cls.static_fields['threadInitNumber'] = 1;
debug("### finished thread init ###");
};
ct = new JavaThreadObject(_this, (_this.get_bs_class('Ljava/lang/Thread;')), {
'Ljava/lang/Thread;name': _this.init_carr('main'),
'Ljava/lang/Thread;priority': 1,
'Ljava/lang/Thread;group': group,
'Ljava/lang/Thread;threadLocals': null,
'Ljava/lang/Thread;blockerLock': new JavaObject(_this, _this.get_bs_class('Ljava/lang/Object;'))
});
};
};
RuntimeState.prototype.meta_stack = function () {
return this.curr_thread.$meta_stack;
};
RuntimeState.prototype.java_throw = function (cls, msg) {
var _this = this;
var v = new JavaObject(this, cls);
this.push_array([v, v, this.init_string(msg)]);
var my_sf = this.curr_frame();
cls.method_lookup(this, '<init>(Ljava/lang/String;)V').setup_stack(this);
my_sf.runner = function () {
if (my_sf.method.has_bytecode) {
my_sf.runner = function () {
return my_sf.method.run_bytecode(_this);
};
} else {
my_sf.runner = null;
}
throw new JavaException(_this.pop());
};
throw ReturnException;
};
RuntimeState.prototype.init_system_class = function () {
var _this = this;
var my_sf = this.curr_frame();
this.get_bs_class('Ljava/lang/System;').get_method('initializeSystemClass()V').setup_stack(this);
my_sf.runner = function () {
my_sf.runner = null;
_this.system_initialized = true;
debug("### finished system class initialization ###");
};
};
RuntimeState.prototype.init_args = function (initial_args) {
var _this = this;
var str_arr_cls = this.get_bs_class('[Ljava/lang/String;');
var args = new JavaArray(this, str_arr_cls, initial_args.map(function (a) {
return _this.init_string(a);
}));
this.curr_thread.$meta_stack = new CallStack([args]);
debug("### finished runtime state initialization ###");
};
RuntimeState.prototype.dump_state = function (snapshot, suffix) {
if (snapshot == null) {
snapshot = this.meta_stack().snap();
}
suffix = suffix != null ? "-" + suffix : '';
var fs;
if (typeof node !== "undefined" && node !== null && node.fs != null) {
fs = node.fs;
} else {
fs = require('fs');
}
var filename = "./core-" + thread_name(this, this.curr_thread) + suffix + ".json";
// 4th parameter to writeFileSync ensures this is not stored in localStorage in the browser
fs.writeFileSync(filename, JSON.stringify(snapshot.serialize()), 'utf8', true);
};
RuntimeState.prototype.choose_next_thread = function (blacklist, cb) {
var _this = this;
var _this = this;
if (blacklist == null) {
blacklist = [];
for (var key in this.waiting_threads) {
blacklist.push.apply(blacklist, this.waiting_threads[key]);
}
}
var wakeup_time = this.curr_thread.wakeup_time;
if (wakeup_time == null) {
wakeup_time = Infinity;
}
var current_time = (new Date()).getTime();
var eligible_threads = this.thread_pool.filter(function (t) {
return t !== _this.curr_thread && t.$isAlive;
});
for (var i = 0; i < eligible_threads.length; i++) {
var t = eligible_threads[i];
if (this.parked(t)) {
if (t.$park_timeout > current_time) {
continue;
}
this.unpark(t);
}
if (blacklist.indexOf(t) >= 0) {
continue;
}
if (t.wakeup_time > current_time) {
if (t.wakeup_time < wakeup_time) {
wakeup_time = t.wakeup_time;
}
continue;
}
debug("TE(choose_next_thread): choosing thread " + thread_name(this, t));
return cb(t);
}
if ((Infinity > wakeup_time && wakeup_time > current_time)) {
debug("TE(choose_next_thread): waiting until " + wakeup_time + " and trying again");
setTimeout((function () {
return _this.choose_next_thread(null, cb);
}), wakeup_time - current_time);
} else {
debug("TE(choose_next_thread): no thread found, sticking with curr_thread");
cb(this.curr_thread);
}
};
RuntimeState.prototype.wait = function (monitor, yieldee) {
debug("TE(wait): waiting " + (thread_name(this, this.curr_thread)) + " on lock " + monitor.ref);
if (this.waiting_threads[monitor.ref] != null) {
this.waiting_threads[monitor.ref].push(this.curr_thread);
} else {
this.waiting_threads[monitor.ref] = [this.curr_thread];
}
if (yieldee != null) {
return this.yield(yieldee);
}
var _this = this;
this.choose_next_thread(this.waiting_threads[monitor.ref], function (nt) {
return _this.yield(nt);
});
};
RuntimeState.prototype.yield = function (yieldee) {
var _this = this;
debug("TE(yield): yielding " + (thread_name(this, this.curr_thread)) + " to " + (thread_name(this, yieldee)));
var old_thread_sf = this.curr_frame();
this.curr_thread = yieldee;
var new_thread_sf = this.curr_frame();
new_thread_sf.runner = (function () {
return _this.meta_stack().pop();
});
old_thread_sf.runner = (function () {
return _this.meta_stack().pop();
});
};
RuntimeState.prototype.park = function (thread, timeout) {
var _this = this;
thread.$park_count++;
thread.$park_timeout = timeout;
debug("TE(park): parking " + (thread_name(this, thread)) + " (count: " + thread.$park_count + ", timeout: " + thread.$park_timeout + ")");
if (this.parked(thread)) {
this.choose_next_thread(null, function (nt) {
return _this.yield(nt);
});
}
};
RuntimeState.prototype.unpark = function (thread) {
debug("TE(unpark): unparking " + (thread_name(this, thread)));
thread.$park_count--;
thread.$park_timeout = Infinity;
if (!this.parked(thread)) {
this.yield(thread);
}
};
RuntimeState.prototype.parked = function (thread) {
return thread.$park_count > 0;
};
RuntimeState.prototype.curr_frame = function () {
return this.meta_stack().curr_frame();
};
RuntimeState.prototype.cl = function (idx) {
return this.curr_frame().locals[idx];
};
RuntimeState.prototype.put_cl = function (idx, val) {
this.curr_frame().locals[idx] = val;
};
RuntimeState.prototype.put_cl2 = function (idx, val) {
this.put_cl(idx, val);
(typeof UNSAFE !== "undefined" && UNSAFE !== null) || this.put_cl(idx + 1, null);
};
RuntimeState.prototype.push = function (arg) {
return this.curr_frame().stack.push(arg);
};
RuntimeState.prototype.push2 = function (arg1, arg2) {
return this.curr_frame().stack.push(arg1, arg2);
};
RuntimeState.prototype.push_array = function (args) {
var cs = this.curr_frame().stack;
Array.prototype.push.apply(cs, args);
};
RuntimeState.prototype.pop = function () {
return this.curr_frame().stack.pop();
};
RuntimeState.prototype.pop2 = function () {
this.pop();
return this.pop();
};
RuntimeState.prototype.peek = function (depth) {
if (depth == null) {
depth = 0;
}
var s = this.curr_frame().stack;
return s[s.length - 1 - depth];
};
RuntimeState.prototype.curr_pc = function () {
return this.curr_frame().pc;
};
RuntimeState.prototype.goto_pc = function (pc) {
return this.curr_frame().pc = pc;
};
RuntimeState.prototype.inc_pc = function (n) {
return this.curr_frame().pc += n;
};
RuntimeState.prototype.check_null = function (obj) {
if (obj == null) {
var err_cls = this.get_bs_class('Ljava/lang/NullPointerException;');
this.java_throw(err_cls, '');
}
return obj;
};
RuntimeState.prototype.heap_newarray = function (type, len) {
if (len < 0) {
var err_cls = this.get_bs_class('Ljava/lang/NegativeArraySizeException;');
this.java_throw(err_cls, "Tried to init [" + type + " array with length " + len);
}
var arr_cls = this.get_class("[" + type);
if (type === 'J') {
return new JavaArray(this, arr_cls, util.arrayset(len, gLong.ZERO));
} else if (type[0] === 'L' || type[0] === '[') {
return new JavaArray(this, arr_cls, util.arrayset(len, null));
} else {
return new JavaArray(this, arr_cls, util.arrayset(len, 0));
}
};
// The innermost component class is already initialized.
RuntimeState.prototype.heap_multinewarray = function (type, counts) {
var _this = this;
var dim = counts.length;
var init_arr = function (curr_dim, type) {
var len = counts[curr_dim];
if (len < 0) {
var err_cls = _this.get_bs_class('Ljava/lang/NegativeArraySizeException;');
_this.java_throw(err_cls, "Tried to init dimension " + curr_dim + " of a " + dim + " dimensional " + type + " array with length " + len);
}
// Gives the JS engine a size hint.
var array = new Array(len);
if (curr_dim + 1 === dim) {
var default_val = util.initial_value(type);
for (var i = 0; i < len; i++) {
array[i] = default_val;
}
} else {
var next_dim = curr_dim + 1;
var comp_type = type.slice(1);
for (var i = 0; i < len; i++) {
array[i] = init_arr(next_dim, comp_type);
}
}
var arr_cls = _this.get_bs_class(type);
return new JavaArray(_this, arr_cls, array);
};
return init_arr(0, type);
};
RuntimeState.prototype.init_string = function (str, intern) {
if (intern == null) {
intern = false;
}
var s = this.string_pool.get(str);
if (intern && s != null) {
return s;
}
var carr = this.init_carr(str);
var str_cls = this.get_bs_class('Ljava/lang/String;');
var jvm_str = new JavaObject(this, str_cls, {
'Ljava/lang/String;value': carr,
'Ljava/lang/String;count': str.length
});
if (intern) {
this.string_pool.set(str, jvm_str);
}
return jvm_str;
};
RuntimeState.prototype.init_carr = function (str) {
var carr = new Array(str.length);
for (var i = 0; i < str.length; i++) {
carr[i] = str.charCodeAt(i);
}
var arr_cls = this.get_bs_class('[C');
return new JavaArray(this, arr_cls, carr);
};
RuntimeState.prototype.block_addr = function (l_address) {
var address = l_address.toNumber();
if (typeof DataView !== "undefined" && DataView !== null) {
var block_addr_ = this.mem_start_addrs[0];
for (var i = 1; i < this.mem_start_addrs.length; i++) {
var addr = this.mem_start_addrs[i];
if (address < addr) {
return block_addr_;
}
block_addr_ = addr;
}
if (typeof UNSAFE !== "undefined" && UNSAFE !== null) {
throw new Error("Invalid memory access at " + address);
}
} else {
if (this.mem_blocks[address] != null) {
return address;
}
}
};
RuntimeState.prototype.handle_toplevel_exception = function (e, no_threads, done_cb) {
var _this = this;
this.unusual_termination = true;
if (e.toplevel_catch_handler != null) {
this.run_until_finished(function () {
return e.toplevel_catch_handler(_this);
}, no_threads, done_cb);
} else {
error("\nInternal JVM Error:", e);
if ((e != null ? e.stack : void 0) != null) {
error(e.stack);
}
done_cb(false);
}
};
RuntimeState.prototype.async_op = function (cb) {
throw new YieldIOException(cb);
};
RuntimeState.prototype.call_bytecode = function (cls, method, args, success_cb, except_cb) {
var _this = this;
var good_cb = function (ret1, ret2) {
return _this.async_op(function (good) {
return good(ret1, ret2);
});
};
var bad_cb = function (e_fn) {
return _this.async_op(function (good, bad) {
return bad(e_fn);
});
};
return this.async_op(function () {
var is_constructor = false;
if (method.name.charAt(0) === '<' && method.name.charAt(1) === 'i') {
var v = new JavaObject(_this, cls);
args.unshift(v, v);
is_constructor = true;
}
var nf = StackFrame.native_frame("$bytecode_call", (function () {
if (method.return_type !== 'V' || is_constructor) {
if (method.return_type === 'J' || method.return_type === 'D') {
_this.pop();
}
var rv = _this.pop();
}
_this.meta_stack().pop();
return success_cb(rv, good_cb, bad_cb);
}), (function (e) {
_this.meta_stack().pop();
return except_cb((function () {
throw e;
}), good_cb, bad_cb);
}));
_this.meta_stack().push(nf);
_this.push_array(args);
method.setup_stack(_this);
return _this.run_until_finished((function () {
}), false, _this.stashed_done_cb);
});
};
RuntimeState.prototype.run_until_finished = function (setup_fn, no_threads, done_cb) {
var _this = this;
var stack;
function nop() {
}
setImmediate((function () {
_this.stashed_done_cb = done_cb;
try {
setup_fn();
var start_time = (new Date()).getTime();
var m_count = _this.max_m_count;
var sf = _this.curr_frame();
while ((sf.runner != null) && m_count > 0) {
sf.runner();
m_count--;
sf = _this.curr_frame();
}
if ((sf.runner != null) && m_count === 0) {
var duration = (new Date()).getTime() - start_time;
if (duration > 2000 || duration < 1000) {
var ms_per_m = duration / _this.max_m_count;
_this.max_m_count = (1000 / ms_per_m) | 0;
}
return _this.run_until_finished(nop, no_threads, done_cb);
}
if (no_threads || _this.thread_pool.length <= 1) {
return done_cb(true);
}
debug("TE(toplevel): finished thread " + (thread_name(_this, _this.curr_thread)));
_this.curr_thread.$isAlive = false;
_this.thread_pool.splice(_this.thread_pool.indexOf(_this.curr_thread), 1);
return _this.choose_next_thread(null, function (next_thread) {
_this.curr_thread = next_thread;
_this.run_until_finished(nop, no_threads, done_cb);
});
} catch (_error) {
var e = _error;
if (e === ReturnException) {
_this.run_until_finished(nop, no_threads, done_cb);
} else if (e instanceof YieldIOException) {
var success_fn = function (ret1, ret2, bytecode, advance_pc) {
if (advance_pc == null) {
advance_pc = true;
}
if (bytecode) {
_this.meta_stack().push(StackFrame.native_frame("async_op"));
}
_this.curr_frame().runner = function () {
_this.meta_stack().pop();
if (bytecode && advance_pc) {
_this.curr_frame().pc += 1 + _this.curr_frame().method.code.opcodes[_this.curr_frame().pc].byte_count;
}
if (ret1 !== void 0) {
if (typeof ret1 === 'boolean') {
ret1 += 0;
}
_this.push(ret1);
}
if (ret2 !== void 0) {
return _this.push(ret2);
}
};
return _this.run_until_finished(nop, no_threads, done_cb);
};
var failure_fn = function (e_cb) {
_this.meta_stack().push(StackFrame.native_frame("async_op"));
_this.curr_frame().runner = function () {
_this.meta_stack().pop();
e_cb();
};
return _this.run_until_finished(nop, no_threads, done_cb);
};
e.condition(success_fn, failure_fn);
} else {
stack = _this.meta_stack();
if ((e.method_catch_handler != null) && stack.length() > 1) {
var frames_to_pop = 0;
while (!e.method_catch_handler(_this, stack.get_caller(frames_to_pop), frames_to_pop === 0)) {
if (stack.length() === ++frames_to_pop) {
if (JVM.dump_state) {
_this.dump_state();
}
stack.pop_n(stack.length() - 1);
_this.handle_toplevel_exception(e, no_threads, done_cb);
return;
}
}
stack.pop_n(frames_to_pop);
_this.run_until_finished(nop, no_threads, done_cb);
} else {
if (JVM.dump_state) {
_this.dump_state();
}
stack.pop_n(Math.max(stack.length() - 1, 0));
_this.handle_toplevel_exception(e, no_threads, done_cb);
}
}
}
}));
};
RuntimeState.prototype.async_input = function (n_bytes, resume) {
if (this.input_buffer.length > 0) {
var data = this.input_buffer.slice(0, n_bytes);
this.input_buffer = this.input_buffer.slice(n_bytes);
resume(data);
return;
}
var _this = this;
this._async_input(function (data) {
if (data.length > n_bytes) {
_this.input_buffer = data.slice(n_bytes);
}
resume(data.slice(0, n_bytes));
});
};
return RuntimeState;
})();
exports.RuntimeState = RuntimeState;
});
define('src/disassembler',["require", "exports", './util'], function(require, exports, __util__) {
var util = __util__;
function pad_left(value, padding) {
var zeroes = new Array(padding).join('0');
return (zeroes + value).slice(-padding);
}
function access_string(access_flags) {
var ordered_flags = ['public', 'protected', 'private', 'static', 'final', 'native'];
if (!access_flags["interface"]) {
ordered_flags.push('abstract');
}
return ordered_flags.filter(function (flag) {
return access_flags[flag];
}).map(function (flag) {
return flag + ' ';
}).join('');
}
// format floats and doubles in the javap way
function format_decimal(val, type_char) {
var valStr = val.toString();
if (type_char === 'f') {
if (val === util.FLOAT_POS_INFINITY || val === Number.POSITIVE_INFINITY) {
valStr = "Infinity";
} else if (val === util.FLOAT_NEG_INFINITY || val === Number.NEGATIVE_INFINITY) {
valStr = "-Infinity";
} else if (val === NaN) {
valStr = "NaN";
}
}
var str;
if (valStr.match(/-?(Infinity|NaN)/)) {
str = valStr;
} else {
var m = valStr.match(/(-?\d+)(\.\d+)?(?:e\+?(-?\d+))?/);
str = m[1] + (m[2] ? m[2] : '.0');
if (type_char === 'f' && m[2] != null && m[2].length > 8) {
str = parseFloat(str).toFixed(7);
}
str = str.replace(/0+$/, '').replace(/\.$/, '.0');
if (m[3] != null) {
str += "E" + m[3];
}
}
return str + type_char;
}
// format the entries for displaying the constant pool. e.g. as '#5.#6' or
// '3.14159f'
function format(entry) {
var val = entry.value;
switch (entry.type) {
case 'Method':
case 'InterfaceMethod':
case 'Field':
return "#" + val.class_ref.value + ".#" + val.sig.value;
case 'NameAndType':
return "#" + val.meth_ref.value + ":#" + val.type_ref.value;
case 'float':
return format_decimal(val, 'f');
case 'double':
return format_decimal(val, 'd');
case 'long':
return val + "l";
default:
return util.escape_whitespace((entry.deref != null ? '#' : '') + val).replace(/"/g, '\\"');
}
}
// pretty-print our field types, e.g. as 'PackageName.ClassName[][]'
function pp_type(field_type) {
if (util.is_array_type(field_type)) {
return pp_type(util.get_component_type(field_type)) + '[]';
}
return util.ext_classname(field_type);
}
function print_excs(excs) {
return " throws " + excs.map(util.ext_classname).join(', ');
}
// For printing columns.
function fixed_width(num, width) {
var num_str = num.toString();
return (new Array(width - num_str.length + 1)).join(' ') + num_str;
}
function disassemble(class_file) {
return show_disassembly(make_dis(class_file));
}
exports.disassemble = disassemble;
function make_dis(class_file) {
// standard class stuff
var src_attr = class_file.get_attribute('SourceFile');
var rva_attr = class_file.get_attribute('RuntimeVisibleAnnotations');
var dis = {
source_file: (src_attr != null) ? src_attr.filename : null,
is_deprecated: class_file.get_attribute('Deprecated') != null,
annotation_bytes: (rva_attr != null) ? rva_attr.raw_bytes : null,
interfaces: class_file.get_interface_types(),
access_string: access_string(class_file.access_flags),
class_type: (class_file.access_flags["interface"] ? 'interface' : 'class'),
class_name: class_file.get_type(),
superclass: class_file.get_super_class_type(),
major_version: class_file.major_version,
minor_version: class_file.minor_version,
constant_pool: [],
inner_classes: [],
fields: [],
methods: []
};
// constant pool entries
var pool = class_file.constant_pool;
pool.each(function (idx, entry) {
return dis.constant_pool.push({
idx: idx,
type: entry.type,
value: format(entry),
extra: util.format_extra_info(entry)
});
});
// inner classes
var inner_classes = class_file.get_attributes('InnerClasses');
for (var i = 0; i < inner_classes.length; i++) {
var icls = inner_classes[i];
var icls_group = [];
for (var j = 0; j < icls.classes.length; j++) {
var cls = icls.classes[j];
var flags = util.parse_flags(cls.inner_access_flags);
var astr = '';
if (flags['public']) {
astr += 'public ';
}
if (flags['abstract']) {
astr += 'abstract ';
}
icls_group.push({
access_string: astr,
type: util.descriptor2typestr(pool.get(cls.inner_info_index).deref()),
raw: cls,
name: cls.inner_name_index > 0 ? pool.get(cls.inner_name_index).value : null,
outer_type: cls.outer_info_index > 0 ? pool.get(cls.outer_info_index).deref() : null
});
}
dis.inner_classes.push(icls_group);
}
// fields
var fields = class_file.get_fields();
for (var i = 0; i < fields.length; i++) {
var f = fields[i];
var sig = f.get_attribute('Signature');
var field = {
type: f.type,
name: f.name,
access_string: access_string(f.access_flags),
signature_bytes: (sig != null) ? sig.raw_bytes : null,
const_type: null,
const_value: null
};
var const_attr = f.get_attribute('ConstantValue');
if (const_attr != null) {
var entry = pool.get(const_attr.ref);
field.const_type = entry.type;
field.const_value = (typeof entry.deref === "function" ? entry.deref() : format(entry));
}
dis.fields.push(field);
}
// methods
var methods = class_file.get_methods();
for (sig in methods) {
var m = methods[sig];
var exc_attr = m.get_attribute('Exceptions');
var method = {
access_string: access_string(m.access_flags),
is_synchronized: m.access_flags.synchronized,
return_type: (m.return_type != null) ? m.return_type : '',
name: m.name,
param_types: m.param_types,
exceptions: (exc_attr != null) ? exc_attr.exceptions : null
};
if (!(m.access_flags["native"] || m.access_flags.abstract)) {
var code = m.code;
code.parse_code();
method['code'] = {
max_stack: code.max_stack,
max_locals: code.max_locals,
num_args: m.num_args,
exception_handlers: code.exception_handlers,
attributes: code.attrs
};
var ops = method['code'].opcodes = [];
code.each_opcode(function (idx, oc) {
return ops.push({
idx: idx,
name: oc.name,
annotation: oc.annotate(idx, pool)
});
});
}
dis.methods.push(method);
}
return dis;
}
function show_disassembly(dis) {
var ifaces = dis.interfaces.map(util.ext_classname).join(',');
var name = util.ext_classname(dis.class_name);
var rv = "Compiled from \"" + (dis.source_file != null ? dis.source_file : 'unknown') + "\"\n" + dis.access_string + dis.class_type + " " + name + " ";
if (dis.class_type === 'interface') {
rv += ifaces.length > 0 ? "extends " + ifaces + "\n" : '\n';
} else {
rv += "extends " + (util.ext_classname(dis.superclass));
rv += ifaces ? " implements " + ifaces + "\n" : '\n';
}
if (dis.source_file) {
rv += " SourceFile: \"" + dis.source_file + "\"\n";
}
if (dis.is_deprecated) {
rv += " Deprecated: length = 0x\n";
}
if (dis.annotation_bytes) {
var alen = dis.annotation_bytes.length.toString(16);
var abytes = dis.annotation_bytes.map(function (b) {
return pad_left(b.toString(16), 2);
}).join(' ');
rv += " RuntimeVisibleAnnotations: length = 0x" + alen + "\n " + abytes + "\n";
}
for (var i = 0, _len = dis.inner_classes.length; i < _len; i++) {
var icls_group = dis.inner_classes[i];
rv += " InnerClass:\n";
for (var j = 0, _len1 = icls_group.length; j < _len1; j++) {
var icls = icls_group[j];
if (icls.name == null) {
// anonymous inner class
rv += " " + icls.access_string + "#" + icls.raw.inner_info_index + "; //class " + icls.type + "\n";
} else {
// it's a named inner class
rv += " " + icls.access_string + "#" + icls.raw.inner_name_index + "= #" + icls.raw.inner_info_index;
if (icls.outer_type == null) {
rv += "; //" + icls.name + "=class " + icls.type + "\n";
} else {
rv += " of #" + icls.raw.outer_info_index + "; //" + icls.name + "=class " + icls.type + " of class " + icls.outer_type + "\n";
}
}
}
}
rv += " minor version: " + dis.minor_version + "\n major version: " + dis.major_version + "\n Constant pool:\n";
for (var i = 0, _len2 = dis.constant_pool.length; i < _len2; i++) {
var entry = dis.constant_pool[i];
rv += "const #" + entry.idx + " = " + entry.type + "\t" + entry.value + ";" + entry.extra + "\n";
}
rv += "\n{\n";
for (var i = 0, _len3 = dis.fields.length; i < _len3; i++) {
var f = dis.fields[i];
rv += "" + f.access_string + (pp_type(f.type)) + " " + f.name + ";\n";
if (f.const_type != null) {
rv += " Constant value: " + f.const_type + " " + f.const_value + "\n";
}
if (f.signature_bytes != null) {
var siglen = f.signature_bytes.length.toString(16);
var sigbytes = ((function () {
var _ref4 = f.signature_bytes;
var _results = [];
for (var _m = 0, _len4 = _ref4.length; _m < _len4; _m++) {
var b = _ref4[_m];
_results.push(pad_left(b.toString(16).toUpperCase(), 2));
}
return _results;
})()).join(' ');
rv += " Signature: length = 0x" + siglen + "\n " + sigbytes + "\n";
}
rv += "\n\n";
}
for (var _m = 0, _len4 = dis.methods.length; _m < _len4; _m++) {
var m = dis.methods[_m];
rv += m.access_string;
if (m.is_synchronized) {
rv += 'synchronized ';
}
var ptypes = m.param_types.map(pp_type).join(', ');
if (m.name === '<clinit>') {
rv += '{}';
} else if (m.name === '<init>') {
rv += "" + name + "(" + ptypes + ")";
} else {
rv += "" + (pp_type(m.return_type)) + " " + m.name + "(" + ptypes + ")";
}
if (m.exceptions != null) {
rv += print_excs(m.exceptions);
}
rv += ";\n";
if (m.code != null) {
var c = m.code;
rv += " Code:\n Stack=" + c.max_stack + ", Locals=" + c.max_locals + ", Args_size=" + c.num_args + "\n";
rv += ((function () {
var _results = [];
for (var _n = 0, _len5 = c.opcodes.length; _n < _len5; _n++) {
var o = c.opcodes[_n];
_results.push(" " + o.idx + ":\t" + o.name + o.annotation + "\n");
}
return _results;
})()).join('');
var ehs = c.exception_handlers;
if (ehs != null && ehs.length > 0) {
rv += " Exception table:\n from to target type\n";
for (var _n = 0, _len5 = ehs.length; _n < _len5; _n++) {
var eh = ehs[_n];
rv += ((function () {
var _ref7 = ['start_pc', 'end_pc', 'handler_pc'];
var _results = [];
for (var _o = 0, _len6 = _ref7.length; _o < _len6; _o++) {
var item = _ref7[_o];
_results.push(fixed_width(eh[item], 6));
}
return _results;
})()).join('');
if (eh.catch_type === '<any>') {
rv += " any\n";
} else {
rv += " Class " + eh.catch_type.slice(1, -1) + "\n";
}
}
rv += "\n";
}
rv += c.attributes.map(function (attr) {
return (typeof attr.disassemblyOutput === "function" ? attr.disassemblyOutput() : void 0) || '';
}).join('');
if (m.exceptions != null) {
rv += " Exceptions:\n" + (print_excs(m.exceptions)) + "\n";
}
}
rv += "\n";
}
rv += "}\n";
return rv;
}
});
define('src/testing',["require", "exports", './jvm', './runtime', './disassembler', './ClassData', './ClassLoader'], function(require, exports, __jvm__, __runtime__, __disassembler__, __ClassData__, __ClassLoader__) {
var jvm = __jvm__;
var runtime = __runtime__;
var RuntimeState = runtime.RuntimeState;
var disassembler = __disassembler__;
var ClassData = __ClassData__;
var ClassLoader = __ClassLoader__;
var BootstrapClassLoader = ClassLoader.BootstrapClassLoader;
var path = typeof node !== "undefined" ? node.path : require('path');
var fs = typeof node !== "undefined" ? node.fs : require('fs');
function find_test_classes(doppio_dir, cb) {
var test_dir = path.resolve(doppio_dir, 'classes/test');
fs.readdir(test_dir, function (err, files) {
cb(files.filter(function (file) {
return path.extname(file) === '.java';
}).map(function (file) {
return "classes/test/" + path.basename(file, '.java');
}));
});
}
exports.find_test_classes = find_test_classes;
function run_tests(test_classes, stdout, hide_diffs, quiet, keep_going, callback) {
var doppio_dir = typeof node !== "undefined" && node !== null ? '/sys/' : path.resolve(__dirname, '..');
var jcl_dir = path.resolve(doppio_dir, 'vendor/classes');
jvm.set_classpath(jcl_dir, doppio_dir);
var xfail_file = path.resolve(doppio_dir, 'classes/test/xfail.txt');
function _runner(test_classes, xfails) {
if (test_classes.length === 0) {
quiet || keep_going || stdout("Pass\n");
return callback(false);
}
var test = test_classes.shift();
quiet || stdout("testing " + test + "...\n");
run_disasm_test(doppio_dir, test, function (disasm_diff) {
if (disasm_diff != null) {
stdout("Failed disasm test " + test + "\n");
hide_diffs || stdout("" + disasm_diff + "\n");
if (!keep_going) {
return callback(true);
}
}
run_stdout_test(doppio_dir, test, function (diff) {
if ((diff != null) != (xfails.indexOf(test) >= 0)) {
if (diff != null) {
stdout("Failed output test: " + test + "\n");
hide_diffs || stdout(diff + "\n");
} else {
stdout("Expected failure passed: " + test + "\n");
}
if (!keep_going) {
return callback(true);
}
}
_runner(test_classes, xfails);
});
});
}
fs.readFile(xfail_file, 'utf-8', function (err, contents) {
var xfails = contents.split('\n').map(function (failname) {
return "classes/test/" + failname;
});
if (test_classes != null && test_classes.length > 0) {
test_classes = test_classes.map(function (tc) {
return tc.replace(/\.class$/, '');
});
_runner(test_classes, xfails);
} else {
exports.find_test_classes(doppio_dir, function (tcs) {
return _runner(tcs, xfails);
});
}
});
}
exports.run_tests = run_tests;
function sanitize(str) {
return str.replace(/\/\/.*/g, '').replace(/^\s*$[\n\r]+/mg, '').replace(/(float|double)\t.*/g, '$1').replace(/[ \t\r]+/g, ' ').replace(/[ ]\n/g, '\n').replace(/\[ \]/g, '[]');
}
function run_disasm_test(doppio_dir, test_class, callback) {
var test_path = path.resolve(doppio_dir, test_class);
fs.readFile(test_path + ".disasm", 'utf8', function (err, contents) {
var javap_disasm = sanitize(contents);
fs.readFile(test_path + ".class", function (err, buffer) {
var doppio_disasm = sanitize(disassembler.disassemble(new ClassData.ReferenceClassData(buffer)));
callback(cleandiff(doppio_disasm, javap_disasm));
});
});
}
function run_stdout_test(doppio_dir, test_class, callback) {
var output_filename = path.resolve(doppio_dir, test_class) + ".runout";
fs.readFile(output_filename, 'utf8', function (err, java_output) {
var doppio_output = '';
var stdout = function (str) {
doppio_output += str;
};
var rs = new RuntimeState(stdout, (function () {
}), new BootstrapClassLoader(jvm.read_classfile));
jvm.run_class(rs, test_class, [], function () {
return callback(cleandiff(doppio_output, java_output));
});
});
}
function cleandiff(our_str, their_str) {
var our_lines = our_str.split(/\n/);
var their_lines = their_str.split(/\n/);
var oidx = 0;
var tidx = 0;
var diff = [];
while (oidx < our_lines.length && tidx < their_lines.length) {
if (our_lines[oidx++] === their_lines[tidx++]) {
continue;
}
diff.push("D:" + our_lines[oidx - 1] + "\nJ:" + their_lines[tidx - 1]);
}
diff.push.apply(diff, our_lines.slice(oidx).map(function (extra) {
return "D:" + extra;
}));
diff.push.apply(diff, their_lines.slice(tidx).map(function (extra) {
return "J:" + extra;
}));
if (diff.length > 0) {
return diff.join('\n');
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment