This commit is contained in:
Marcus Cavanaugh 2014-09-30 10:00:20 -07:00
Родитель b9b1520e88
Коммит 9b5373a0ec
4 изменённых файлов: 316 добавлений и 49 удалений

Просмотреть файл

@ -465,22 +465,6 @@ Native["java/lang/Math.floor.(D)D"] = function(ctx, stack) {
stack.push2(Math.floor(stack.pop2()));
}
var internedStrings = new Map();
Native["java/lang/String.intern.()Ljava/lang/String;"] = function(ctx, stack) {
var javaString = stack.pop();
var string = util.fromJavaString(javaString);
var internedString = internedStrings.get(string);
if (internedString) {
stack.push(internedString);
} else {
internedStrings.set(string, javaString);
stack.push(javaString);
}
}
Native["java/lang/Thread.currentThread.()Ljava/lang/Thread;"] = function(ctx, stack) {
stack.push(ctx.thread);
}

Просмотреть файл

@ -227,7 +227,8 @@ Override.simple = function(key, fn, opts) {
} else if (e.javaClassName) {
ctx.raiseExceptionAndYield(e.javaClassName, e.message);
} else {
ctx.raiseExceptionAndYield("java/lang/RuntimeError", e);
console.error(e, e.stack);
ctx.raiseExceptionAndYield("java/lang/RuntimeException", e);
}
}
};

338
string.js
Просмотреть файл

@ -3,19 +3,19 @@
'use strict';
/**
* These methods reimplement java.lang.String using native JS strings,
* stored as the `str` property on the java.lang.String instance.
* string.js: Native implementations of String and StringBuffer.
*
* All methods on java.lang.String are implemented here in the same
* order as java.lang.String, with the exception of a couple of the
* String.valueOf() methods, whose absence is documented below.
* Methods are defined in the same order as the Java source.
* Any missing methods have been noted in comments.
*/
//################################################################
// java.lang.String (manipulated via the 'str' property)
function isString(obj) {
return obj && obj.str !== undefined;
}
//****************************************************************
// Constructors
@ -81,10 +81,8 @@ Override.simple(
Override.simple(
"java/lang/String.<init>.(Ljava/lang/StringBuffer;)V",
function(buffer) {
var value = buffer.class.getField("I.value.[C").get(buffer);
var count = buffer.class.getField("I.count.I").get(buffer);
this.str = util.fromJavaChars(value, 0, count);
function(jBuffer) {
this.str = util.fromJavaChars(jBuffer.buf, 0, jBuffer.count);
});
Override.simple(
@ -112,10 +110,7 @@ Override.simple("java/lang/String.getChars.(II[CI)V", function(srcBegin, srcEnd,
dstBegin + (srcEnd - srcBegin) > dst.length || dstBegin < 0) {
throw new JavaException("java/lang/IndexOutOfBoundsException");
}
var len = srcEnd - srcBegin;
for (var i = 0; i < len; i++) {
dst[dstBegin + i] = this.str.charCodeAt(srcBegin + i);
}
dst.set(util.stringToCharArray(this.str.substring(srcBegin, srcEnd)), dstBegin);
});
// Java returns encodings like "UTF_16"; TextEncoder and friends only
@ -277,7 +272,7 @@ Override.simple("java/lang/String.toString.()Ljava/lang/String;", function() {
});
Override.simple("java/lang/String.toCharArray.()[C", function() {
return util.javaStringToArrayBuffer(this);
return util.stringToCharArray(this.str);
});
//****************************************************************
@ -320,20 +315,309 @@ Override.simple("java/lang/String.valueOf.(J)Ljava/lang/String;", function(n, _)
// String.valueOf(float) and String.valueOf(double) have been left in
// Java for now, as they follow complex formatting rules.
// Additionally, the test suite covers things like positive zero vs.
// negative zero, which we don't currently distinguish.
// Java for now, as they require support for complex formatting rules.
// Additionally, their tests check for coverage of nuanced things like
// positive zero vs. negative zero, which we don't currently support.
// Note: String.intern is implemented in `native.js`.
var internedStrings = new Map();
Native["java/lang/String.intern.()Ljava/lang/String;"] = function(ctx, stack) {
var javaString = stack.pop();
var string = util.fromJavaString(javaString);
var internedString = internedStrings.get(string);
if (internedString) {
stack.push(internedString);
} else {
internedStrings.set(string, javaString);
stack.push(javaString);
}
}
//****************************************************************
// StringBuffer
// Overriding StringBuffer.toString() avoids calling "new
// String(this)" via JVM bytecode.
Override.simple("java/lang/StringBuffer.toString.()Ljava/lang/String;", function() {
var value = this.class.getField("I.value.[C").get(this);
var count = this.class.getField("I.count.I").get(this);
return util.fromJavaChars(value, 0, count);
//################################################################
// java.lang.StringBuffer (manipulated via the 'buf' property)
Override.simple("java/lang/StringBuffer.<init>.()V", function() {
this.buf = new Uint16Array(16); // Initial buffer size: 16, per the Java implementation.
this.count = 0;
});
Override.simple("java/lang/StringBuffer.<init>.(I)V", function(length) {
if (length < 0) {
throw new JavaException("java/lang/NegativeArraySizeException");
}
this.buf = new Uint16Array(length);
this.count = 0;
});
Override.simple("java/lang/StringBuffer.<init>.(Ljava/lang/String;)V", function(jStr) {
var stringBuf = util.stringToCharArray(jStr.str);
this.buf = new Uint16Array(stringBuf.length + 16);
this.buf.set(stringBuf, 0);
this.count = stringBuf.length;
});
Override.simple("java/lang/StringBuffer.length.()I", function() {
return this.count;
});
Override.simple("java/lang/StringBuffer.capacity.()I", function() {
return this.buf.length;
});
Override.simple("java/lang/StringBuffer.copy.()V", function() {
// We don't support copying (there's no need unless we also support shared buffers).
});
/**
* Expand capacity to max(minCapacity, (capacity + 1) * 2).
*
* @this StringBuffer
* @param {number} minCapacity
*/
function expandCapacity(minCapacity) {
var newCapacity = (this.buf.length + 1) * 2;
if (newCapacity < 0) {
newCapacity = 65535; // In Java, this is much larger (MAX_INTEGER).
} else if (minCapacity > newCapacity) {
newCapacity = minCapacity;
}
var oldBuf = this.buf;
this.buf = new Uint16Array(newCapacity);
this.buf.set(oldBuf, 0);
}
Override.simple("java/lang/StringBuffer.ensureCapacity.(I)V", function(minCapacity) {
if (this.buf.length < minCapacity) {
expandCapacity.call(this, minCapacity);
}
});
Override.simple("java/lang/StringBuffer.expandCapacity.(I)V", function(minCapacity) {
expandCapacity.call(this, minCapacity);
});
Override.simple("java/lang/StringBuffer.setLength.(I)V", function(newLength) {
if (newLength < 0) {
throw new JavaException("java/lang/StringIndexOutOfBoundsException");
}
if (newLength > this.buf.length) {
expandCapacity.call(this, newLength);
}
this.count = newLength;
});
Override.simple("java/lang/StringBuffer.charAt.(I)C", function(index) {
if (index < 0 || index >= this.count) {
throw new JavaException("java/lang/StringIndexOutOfBoundsException");
}
return this.buf[index];
});
Override.simple("java/lang/StringBuffer.getChars.(II[CI)V", function(srcBegin, srcEnd, dst, dstBegin) {
if (srcBegin < 0 || srcEnd < 0 || srcEnd > this.count || srcBegin > srcEnd ||
dstBegin + (srcEnd - srcBegin) > dst.length || dstBegin < 0) {
throw new JavaException("java/lang/StringIndexOutOfBoundsException");
}
dst.set(this.buf.subarray(srcBegin, srcEnd), dstBegin);
});
Override.simple("java/lang/StringBuffer.setCharAt.(IC)V", function(index, ch) {
if (index < 0 || index >= this.count) {
throw new JavaException("java/lang/StringIndexOutOfBoundsException");
}
this.buf[index] = ch;
});
/**
* Append `data`, which should be either a JS String or a Uint16Array.
* Data must not be null.
*
* @this StringBuffer
* @param {Uint16Array|string} data
* @return this
*/
function stringBufferAppend(data) {
if (data == null) {
throw new JavaException("java/lang/NullPointerException");
}
if (!(data instanceof Uint16Array)) {
data = util.stringToCharArray(data);
}
if (this.buf.length < this.count + data.length) {
expandCapacity.call(this, this.count + data.length);
}
this.buf.set(data, this.count);
this.count += data.length;
return this;
}
// StringBuffer.append(java.lang.Object) left in Java to avoid Object.toString().
Override.simple("java/lang/StringBuffer.append.(Ljava/lang/String;)Ljava/lang/StringBuffer;", function(jStr) {
return stringBufferAppend.call(this, jStr ? jStr.str : "null");
});
Override.simple("java/lang/StringBuffer.append.([C)Ljava/lang/StringBuffer;", function(chars) {
return stringBufferAppend.call(this, chars);
});
Override.simple("java/lang/StringBuffer.append.([CII)Ljava/lang/StringBuffer;", function(chars, offset, length) {
if (offset < 0) {
throw new JavaException("java/lang/IndexOutOfBoundsException");
}
return stringBufferAppend.call(this, chars.subarray(offset, offset + length));
});
Override.simple("java/lang/StringBuffer.append.(Z)Ljava/lang/StringBuffer;", function(bool) {
return stringBufferAppend.call(this, bool ? "true" : "false");
});
Override.simple("java/lang/StringBuffer.append.(C)Ljava/lang/StringBuffer;", function(ch) {
if (this.buf.length < this.count + 1) {
expandCapacity.call(this, this.count + 1);
}
this.buf[this.count++] = ch;
return this;
});
Override.simple("java/lang/StringBuffer.append.(I)Ljava/lang/StringBuffer;", function(n) {
return stringBufferAppend.call(this, n + "");
});
Override.simple("java/lang/StringBuffer.append.(J)Ljava/lang/StringBuffer;", function(n, _) {
return stringBufferAppend.call(this, n + "");
});
// StringBuffer.append(float) left in Java (see String.valueOf(float) above).
// StringBuffer.append(double) left in Java (see String.valueOf(double) above).
/**
* Delete characters between [start, end).
*
* @this StringBuffer
* @param {number} start
* @param {number} end
* @return this
*/
function stringBufferDelete(start, end) {
if (start < 0) {
throw new JavaException("java/lang/StringIndexOutOfBoundsException");
}
if (end > this.count) {
end = this.count;
}
if (start > end) {
throw new JavaException("java/lang/StringIndexOutOfBoundsException");
}
var len = end - start;
if (len > 0) {
// When Gecko 34 is released, we can use TypedArray.copyWithin() instead.
this.buf.set(this.buf.subarray(start + len, this.count), start);
this.count -= len;
}
return this;
}
Override.simple("java/lang/StringBuffer.delete.(II)Ljava/lang/StringBuffer;",
stringBufferDelete);
Override.simple("java/lang/StringBuffer.deleteCharAt.(I)Ljava/lang/StringBuffer;", function(index) {
if (index < 0 || index >= this.count) {
throw new JavaException("java/lang/StringIndexOutOfBoundsException");
}
return stringBufferDelete.call(this, index, index + 1);
});
/**
* Insert `data` at the given `offset`.
*
* @this StringBuffer
* @param {number} offset
* @param {Uint16Array|string} data
* @return this
*/
function stringBufferInsert(offset, data) {
if (data == null) {
throw new JavaException("java/lang/NullPointerException");
}
if (offset < 0 || offset > this.count) {
throw new JavaException("java/lang/StringIndexOutOfBoundsException");
}
if (!(data instanceof Uint16Array)) {
data = util.stringToCharArray(data);
}
if (this.buf.length < this.count + data.length) {
expandCapacity.call(this, this.count + data.length);
}
// When Gecko 34 is released, we can use TypedArray.copyWithin() instead.
this.buf.set(this.buf.subarray(offset, this.count), offset + data.length);
this.buf.set(data, offset);
this.count += data.length;
return this;
}
// StringBuffer.insert(Object) left in Java (for String.valueOf()).
Override.simple("java/lang/StringBuffer.insert.(ILjava/lang/String;)Ljava/lang/StringBuffer;", function(offset, jStr) {
return stringBufferInsert.call(this, offset, jStr ? jStr.str : "null");
});
Override.simple("java/lang/StringBuffer.insert.(I[C)Ljava/lang/StringBuffer;", function(offset, chars) {
return stringBufferInsert.call(this, offset, chars);
});
Override.simple("java/lang/StringBuffer.insert.(IZ)Ljava/lang/StringBuffer;", function(offset, bool) {
return stringBufferInsert.call(this, offset, bool ? "true" : "false");
});
Override.simple("java/lang/StringBuffer.insert.(IC)Ljava/lang/StringBuffer;", function(offset, ch) {
return stringBufferInsert.call(this, offset, String.fromCharCode(ch));
});
Override.simple("java/lang/StringBuffer.insert.(II)Ljava/lang/StringBuffer;", function(offset, n) {
return stringBufferInsert.call(this, offset, n + "");
});
Override.simple("java/lang/StringBuffer.insert.(IJ)Ljava/lang/StringBuffer;", function(offset, n, _) {
return stringBufferInsert.call(this, offset, n + "");
});
// StringBuffer.insert(float) left in Java.
// StringBuffer.insert(double) left in Java.
Override.simple("java/lang/StringBuffer.reverse.()Ljava/lang/StringBuffer;", function() {
var buf = this.buf;
for (var i = 0, j = this.count - 1; i < j; i++, j--) {
var tmp = buf[i];
buf[i] = buf[j];
buf[j] = tmp;
}
return this;
});
Override.simple("java/lang/StringBuffer.toString.()Ljava/lang/String;", function() {
return util.fromJavaChars(this.buf, 0, this.count);
});
Override.simple("java/lang/StringBuffer.setShared.()V", function() {
// Our StringBuffers are never shared. Everyone gets their very own!
});
Override.simple("java/lang/StringBuffer.getValue.()[C", function() {
// In theory, this method should only be called by String (which
// we've overridden to not do), so it should never be called. In any
// case, mutating this buf would have the same effect here as it
// would in Java.
return this.buf;
});

Просмотреть файл

@ -67,10 +67,8 @@ var util = (function () {
* NOTE: Do not modify the ArrayBuffer; it may be shared between
* multiple string instances.
*/
function javaStringToArrayBuffer(jStr) {
if (!jStr)
return null;
return new Uint16Array(jStringEncoder.encode(jStr.str).buffer);
function stringToCharArray(str) {
return new Uint16Array(jStringEncoder.encode(str).buffer);
}
var id = (function() {
@ -128,7 +126,7 @@ var util = (function () {
double2long: double2long,
fromJavaChars: fromJavaChars,
fromJavaString: fromJavaString,
javaStringToArrayBuffer: javaStringToArrayBuffer,
stringToCharArray: stringToCharArray,
id: id,
tag: tag,
compareTypedArrays: compareTypedArrays,