From bfd227112dc7c73c7f04ed68dbe00d4541caebfb Mon Sep 17 00:00:00 2001 From: RBRi Date: Tue, 15 May 2018 19:00:10 +0200 Subject: [PATCH] some fixes/enhancements for the typed array support (#436) * fix broken in operator for typed arrays (zero is a valid index too) * typed arrays are doing the buffer encoding based on little/big endian from the architecture they run on. So far Rhino always uses big endian. With this patch we are able to simulate little endian also by setting a context feature. * regarding the spec the default is always big endian and not architecture dependent * fix toString() for typed arrays --- src/org/mozilla/javascript/Context.java | 7 ++++ .../mozilla/javascript/ContextFactory.java | 3 ++ .../typedarrays/NativeArrayBufferView.java | 15 ++++++++ .../typedarrays/NativeDataView.java | 12 +++---- .../typedarrays/NativeFloat32Array.java | 4 +-- .../typedarrays/NativeFloat64Array.java | 4 +-- .../typedarrays/NativeInt16Array.java | 4 +-- .../typedarrays/NativeInt32Array.java | 4 +-- .../typedarrays/NativeTypedArrayView.java | 35 ++++++++++++++----- .../typedarrays/NativeUint16Array.java | 4 +-- .../typedarrays/NativeUint32Array.java | 4 +-- testsrc/jstests/harmony/typed-array-in.js | 28 +++++++++++++++ .../jstests/harmony/typed-array-to-string.js | 23 ++++++++++++ .../tests/harmony/TypedArrayInTest.java | 13 +++++++ .../tests/harmony/TypedArrayToStringTest.java | 13 +++++++ 15 files changed, 145 insertions(+), 28 deletions(-) create mode 100644 testsrc/jstests/harmony/typed-array-in.js create mode 100644 testsrc/jstests/harmony/typed-array-to-string.js create mode 100644 testsrc/org/mozilla/javascript/tests/harmony/TypedArrayInTest.java create mode 100644 testsrc/org/mozilla/javascript/tests/harmony/TypedArrayToStringTest.java diff --git a/src/org/mozilla/javascript/Context.java b/src/org/mozilla/javascript/Context.java index 620229df5a..28f2be1c37 100644 --- a/src/org/mozilla/javascript/Context.java +++ b/src/org/mozilla/javascript/Context.java @@ -339,6 +339,13 @@ public class Context */ public static final int FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE = 18; + /** + * TypedArray buffer uses little/big endian depending on the platform. + * The default is big endian for Rhino. + * @since 1.7 Release 11 + */ + public static final int FEATURE_LITTLE_ENDIAN = 19; + public static final String languageVersionProperty = "language version"; public static final String errorReporterProperty = "error reporter"; diff --git a/src/org/mozilla/javascript/ContextFactory.java b/src/org/mozilla/javascript/ContextFactory.java index a52681c2b0..57e44e7ac2 100644 --- a/src/org/mozilla/javascript/ContextFactory.java +++ b/src/org/mozilla/javascript/ContextFactory.java @@ -293,6 +293,9 @@ protected boolean hasFeature(Context cx, int featureIndex) case Context.FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE: return false; + + case Context.FEATURE_LITTLE_ENDIAN: + return false; } // It is a bug to call the method with unknown featureIndex throw new IllegalArgumentException(String.valueOf(featureIndex)); diff --git a/src/org/mozilla/javascript/typedarrays/NativeArrayBufferView.java b/src/org/mozilla/javascript/typedarrays/NativeArrayBufferView.java index c8faceed83..decde96a70 100644 --- a/src/org/mozilla/javascript/typedarrays/NativeArrayBufferView.java +++ b/src/org/mozilla/javascript/typedarrays/NativeArrayBufferView.java @@ -6,6 +6,7 @@ package org.mozilla.javascript.typedarrays; +import org.mozilla.javascript.Context; import org.mozilla.javascript.IdScriptableObject; import org.mozilla.javascript.ScriptRuntime; import org.mozilla.javascript.Undefined; @@ -20,6 +21,8 @@ public abstract class NativeArrayBufferView { private static final long serialVersionUID = 6884475582973958419L; + private static Boolean useLittleEndian = null; + /** Many view objects can share the same backing array */ protected final NativeArrayBuffer arrayBuffer; /** The offset, in bytes, from the start of the backing array */ @@ -62,6 +65,18 @@ public int getByteLength() { return byteLength; } + protected static boolean useLittleEndian() { + if (useLittleEndian == null) { + Context ctx = Context.getCurrentContext(); + // for some unit tests this might be null + if (ctx == null) { + return false; + } + useLittleEndian = ctx.hasFeature(Context.FEATURE_LITTLE_ENDIAN); + } + return useLittleEndian.booleanValue(); + } + protected static boolean isArg(Object[] args, int i) { return ((args.length > i) && !Undefined.instance.equals(args[i])); diff --git a/src/org/mozilla/javascript/typedarrays/NativeDataView.java b/src/org/mozilla/javascript/typedarrays/NativeDataView.java index 4e8d16f7f3..5e33affe7f 100644 --- a/src/org/mozilla/javascript/typedarrays/NativeDataView.java +++ b/src/org/mozilla/javascript/typedarrays/NativeDataView.java @@ -99,8 +99,7 @@ private Object js_getInt(int bytes, boolean signed, Object[] args) int pos = ScriptRuntime.toInt32(args[0]); rangeCheck(pos, bytes); - boolean littleEndian = - (isArg(args, 1) && (bytes > 1) && ScriptRuntime.toBoolean(args[1])); + boolean littleEndian = isArg(args, 1) && (bytes > 1) && ScriptRuntime.toBoolean(args[1]); switch (bytes) { case 1: @@ -124,8 +123,7 @@ private Object js_getFloat(int bytes, Object[] args) int pos = ScriptRuntime.toInt32(args[0]); rangeCheck(pos, bytes); - boolean littleEndian = - (isArg(args, 1) && (bytes > 1) && ScriptRuntime.toBoolean(args[1])); + boolean littleEndian = isArg(args, 1) && (bytes > 1) && ScriptRuntime.toBoolean(args[1]); switch (bytes) { case 4: @@ -145,8 +143,7 @@ private void js_setInt(int bytes, boolean signed, Object[] args) int pos = ScriptRuntime.toInt32(args[0]); rangeCheck(pos, bytes); - boolean littleEndian = - (isArg(args, 2) && (bytes > 1) && ScriptRuntime.toBoolean(args[2])); + boolean littleEndian = isArg(args, 2) && (bytes > 1) && ScriptRuntime.toBoolean(args[2]); switch (bytes) { case 1: @@ -183,8 +180,7 @@ private void js_setFloat(int bytes, Object[] args) int pos = ScriptRuntime.toInt32(args[0]); rangeCheck(pos, bytes); - boolean littleEndian = - (isArg(args, 2) && (bytes > 1) && ScriptRuntime.toBoolean(args[2])); + boolean littleEndian = isArg(args, 2) && (bytes > 1) && ScriptRuntime.toBoolean(args[2]); double val = ScriptRuntime.toNumber(args[1]); switch (bytes) { diff --git a/src/org/mozilla/javascript/typedarrays/NativeFloat32Array.java b/src/org/mozilla/javascript/typedarrays/NativeFloat32Array.java index 0f4dfebd21..ea33a13a6d 100644 --- a/src/org/mozilla/javascript/typedarrays/NativeFloat32Array.java +++ b/src/org/mozilla/javascript/typedarrays/NativeFloat32Array.java @@ -78,7 +78,7 @@ protected Object js_get(int index) if (checkIndex(index)) { return Undefined.instance; } - return ByteIo.readFloat32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, false); + return ByteIo.readFloat32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, useLittleEndian()); } @Override @@ -88,7 +88,7 @@ protected Object js_set(int index, Object c) return Undefined.instance; } double val = ScriptRuntime.toNumber(c); - ByteIo.writeFloat32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, false); + ByteIo.writeFloat32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, useLittleEndian()); return null; } diff --git a/src/org/mozilla/javascript/typedarrays/NativeFloat64Array.java b/src/org/mozilla/javascript/typedarrays/NativeFloat64Array.java index 1aaa6e4a2b..cf2861d2c5 100644 --- a/src/org/mozilla/javascript/typedarrays/NativeFloat64Array.java +++ b/src/org/mozilla/javascript/typedarrays/NativeFloat64Array.java @@ -78,7 +78,7 @@ protected Object js_get(int index) if (checkIndex(index)) { return Undefined.instance; } - long base = ByteIo.readUint64Primitive(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, false); + long base = ByteIo.readUint64Primitive(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, useLittleEndian()); return Double.longBitsToDouble(base); } @@ -90,7 +90,7 @@ protected Object js_set(int index, Object c) } double val = ScriptRuntime.toNumber(c); long base = Double.doubleToLongBits(val); - ByteIo.writeUint64(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, base, false); + ByteIo.writeUint64(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, base, useLittleEndian()); return null; } diff --git a/src/org/mozilla/javascript/typedarrays/NativeInt16Array.java b/src/org/mozilla/javascript/typedarrays/NativeInt16Array.java index 7749d282b3..c68393c24a 100644 --- a/src/org/mozilla/javascript/typedarrays/NativeInt16Array.java +++ b/src/org/mozilla/javascript/typedarrays/NativeInt16Array.java @@ -77,7 +77,7 @@ protected Object js_get(int index) if (checkIndex(index)) { return Undefined.instance; } - return ByteIo.readInt16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, false); + return ByteIo.readInt16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, useLittleEndian()); } @Override @@ -87,7 +87,7 @@ protected Object js_set(int index, Object c) return Undefined.instance; } int val = Conversions.toInt16(c); - ByteIo.writeInt16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, false); + ByteIo.writeInt16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, useLittleEndian()); return null; } diff --git a/src/org/mozilla/javascript/typedarrays/NativeInt32Array.java b/src/org/mozilla/javascript/typedarrays/NativeInt32Array.java index c91555a5fa..ff5dc6f51f 100644 --- a/src/org/mozilla/javascript/typedarrays/NativeInt32Array.java +++ b/src/org/mozilla/javascript/typedarrays/NativeInt32Array.java @@ -78,7 +78,7 @@ protected Object js_get(int index) if (checkIndex(index)) { return Undefined.instance; } - return ByteIo.readInt32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, false); + return ByteIo.readInt32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, useLittleEndian()); } @Override @@ -88,7 +88,7 @@ protected Object js_set(int index, Object c) return Undefined.instance; } int val = ScriptRuntime.toInt32(c); - ByteIo.writeInt32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, false); + ByteIo.writeInt32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, useLittleEndian()); return null; } diff --git a/src/org/mozilla/javascript/typedarrays/NativeTypedArrayView.java b/src/org/mozilla/javascript/typedarrays/NativeTypedArrayView.java index 546e22abed..80e7af081f 100644 --- a/src/org/mozilla/javascript/typedarrays/NativeTypedArrayView.java +++ b/src/org/mozilla/javascript/typedarrays/NativeTypedArrayView.java @@ -59,7 +59,7 @@ public Object get(int index, Scriptable start) @Override public boolean has(int index, Scriptable start) { - return ((index > 0) && (index < length)); + return !checkIndex(index); } @Override @@ -245,6 +245,19 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, case Id_constructor: return js_constructor(cx, scope, args); + case Id_toString: + NativeTypedArrayView realThis = realThis(thisObj, f); + final int arrayLength = realThis.getArrayLength(); + final StringBuilder builder = new StringBuilder(); + if (arrayLength > 0) { + builder.append(ScriptRuntime.toString(realThis.js_get(0))); + } + for (int i = 1; i < arrayLength; i++) { + builder.append(','); + builder.append(ScriptRuntime.toString(realThis.js_get(i))); + } + return builder.toString(); + case Id_get: if (args.length > 0) { return realThis(thisObj, f).js_get(ScriptRuntime.toInt32(args[0])); @@ -303,6 +316,7 @@ protected void initPrototypeId(int id) int arity; switch (id) { case Id_constructor: arity = 1; s = "constructor"; break; + case Id_toString: arity = 0; s = "toString"; break; case Id_get: arity = 1; s = "get"; break; case Id_set: arity = 2; s = "set"; break; case Id_subarray: arity = 2; s = "subarray"; break; @@ -326,7 +340,7 @@ protected int findPrototypeId(Symbol k) protected int findPrototypeId(String s) { int id; -// #generated# Last update: 2016-03-04 20:59:23 GMT +// #generated# Last update: 2018-05-13 12:51:10 MESZ L0: { id = 0; String X = null; int c; int s_length = s.length(); if (s_length==3) { @@ -334,7 +348,11 @@ protected int findPrototypeId(String s) if (c=='g') { if (s.charAt(2)=='t' && s.charAt(1)=='e') {id=Id_get; break L0;} } else if (c=='s') { if (s.charAt(2)=='t' && s.charAt(1)=='e') {id=Id_set; break L0;} } } - else if (s_length==8) { X="subarray";id=Id_subarray; } + else if (s_length==8) { + c=s.charAt(0); + if (c=='s') { X="subarray";id=Id_subarray; } + else if (c=='t') { X="toString";id=Id_toString; } + } else if (s_length==11) { X="constructor";id=Id_constructor; } if (X!=null && X!=s && !X.equals(s)) id = 0; break L0; @@ -346,10 +364,11 @@ protected int findPrototypeId(String s) // Table of all functions private static final int Id_constructor = 1, - Id_get = 2, - Id_set = 3, - Id_subarray = 4, - SymbolId_iterator = 5; + Id_toString = 2, + Id_get = 3, + Id_set = 4, + Id_subarray = 5, + SymbolId_iterator = 6; protected static final int MAX_PROTOTYPE_ID = SymbolId_iterator; @@ -401,7 +420,7 @@ protected Object getInstanceIdValue(int id) protected int findInstanceIdInfo(String s) { int id; -// #generated# Last update: 2014-12-08 17:33:28 PST +// #generated# Last update: 2018-05-13 12:51:10 MESZ L0: { id = 0; String X = null; int s_length = s.length(); if (s_length==6) { X="length";id=Id_length; } diff --git a/src/org/mozilla/javascript/typedarrays/NativeUint16Array.java b/src/org/mozilla/javascript/typedarrays/NativeUint16Array.java index 9a4ef66e35..93104d9b42 100644 --- a/src/org/mozilla/javascript/typedarrays/NativeUint16Array.java +++ b/src/org/mozilla/javascript/typedarrays/NativeUint16Array.java @@ -77,7 +77,7 @@ protected Object js_get(int index) if (checkIndex(index)) { return Undefined.instance; } - return ByteIo.readUint16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, false); + return ByteIo.readUint16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, useLittleEndian()); } @Override @@ -87,7 +87,7 @@ protected Object js_set(int index, Object c) return Undefined.instance; } int val = Conversions.toUint16(c); - ByteIo.writeUint16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, false); + ByteIo.writeUint16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, useLittleEndian()); return null; } diff --git a/src/org/mozilla/javascript/typedarrays/NativeUint32Array.java b/src/org/mozilla/javascript/typedarrays/NativeUint32Array.java index b2fd8c6a18..b74738c4b0 100644 --- a/src/org/mozilla/javascript/typedarrays/NativeUint32Array.java +++ b/src/org/mozilla/javascript/typedarrays/NativeUint32Array.java @@ -77,7 +77,7 @@ protected Object js_get(int index) if (checkIndex(index)) { return Undefined.instance; } - return ByteIo.readUint32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, false); + return ByteIo.readUint32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, useLittleEndian()); } @Override @@ -87,7 +87,7 @@ protected Object js_set(int index, Object c) return Undefined.instance; } long val = Conversions.toUint32(c); - ByteIo.writeUint32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, false); + ByteIo.writeUint32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, useLittleEndian()); return null; } diff --git a/testsrc/jstests/harmony/typed-array-in.js b/testsrc/jstests/harmony/typed-array-in.js new file mode 100644 index 0000000000..7b64dfb320 --- /dev/null +++ b/testsrc/jstests/harmony/typed-array-in.js @@ -0,0 +1,28 @@ +load("testsrc/assert.js"); + +var types = [Int8Array, Uint8Array, Int16Array, Uint16Array, + Int32Array, Uint32Array, Uint8ClampedArray, Float32Array, + Float64Array]; + +for (var t = 0; t < types.length; t++) { + var type = types[t]; + + var empty = new type(0); + assertFalse(0 in empty); + assertFalse(1 in empty); + + var one = new type(1); + one[0] = 1; + assertTrue(0 in one); + assertFalse(1 in one); + + var two = new type(2); + assertTrue(0 in two); + assertTrue(1 in two); + assertFalse(2 in two); + + two[1] = 2; + assertFalse(2 in two); +} + +"success"; \ No newline at end of file diff --git a/testsrc/jstests/harmony/typed-array-to-string.js b/testsrc/jstests/harmony/typed-array-to-string.js new file mode 100644 index 0000000000..37638164d0 --- /dev/null +++ b/testsrc/jstests/harmony/typed-array-to-string.js @@ -0,0 +1,23 @@ +load("testsrc/assert.js"); + +var types = [Int8Array, Uint8Array, Int16Array, Uint16Array, + Int32Array, Uint32Array, Uint8ClampedArray, Float32Array, + Float64Array]; + +for (var t = 0; t < types.length; t++) { + var type = types[t]; + + var empty = new type(0); + assertEquals("", empty.toString()); + + var one = new type([7]); + assertEquals("7", one.toString()); + + var two = new type([7,13]); + assertEquals("7,13", two.toString()); + + var many = new type([7,13,21,17,0,1,11]); + assertEquals("7,13,21,17,0,1,11", many.toString()); +} + +"success"; \ No newline at end of file diff --git a/testsrc/org/mozilla/javascript/tests/harmony/TypedArrayInTest.java b/testsrc/org/mozilla/javascript/tests/harmony/TypedArrayInTest.java new file mode 100644 index 0000000000..ae25665472 --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/harmony/TypedArrayInTest.java @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.javascript.tests.harmony; + +import org.mozilla.javascript.drivers.RhinoTest; +import org.mozilla.javascript.drivers.ScriptTestsBase; + +@RhinoTest("testsrc/jstests/harmony/typed-array-in.js") +public class TypedArrayInTest extends ScriptTestsBase +{ +} diff --git a/testsrc/org/mozilla/javascript/tests/harmony/TypedArrayToStringTest.java b/testsrc/org/mozilla/javascript/tests/harmony/TypedArrayToStringTest.java new file mode 100644 index 0000000000..12c555289b --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/harmony/TypedArrayToStringTest.java @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.javascript.tests.harmony; + +import org.mozilla.javascript.drivers.RhinoTest; +import org.mozilla.javascript.drivers.ScriptTestsBase; + +@RhinoTest("testsrc/jstests/harmony/typed-array-to-string.js") +public class TypedArrayToStringTest extends ScriptTestsBase +{ +}