Created
September 5, 2013 18:18
-
-
Save jvilk/6453990 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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: { | |
'&': '&', | |
'<': '<', | |
'>': '>', | |
'"': '"', | |
"'": ''', | |
'/': '/' | |
} | |
}; | |
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