--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -37,16 +37,35 @@ function IsDetachedBuffer(buffer) {
function GetAttachedArrayBuffer(tarray) {
var buffer = ViewedArrayBufferIfReified(tarray);
if (IsDetachedBuffer(buffer))
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
return buffer;
}
+// A function which ensures that the argument is either a typed array or a
+// cross-compartment wrapper for a typed array and that the typed array involved
+// has an attached array buffer. If one of those conditions doesn't hold (wrong
+// kind of argument, or detached array buffer), an exception is thrown. The
+// return value is `true` if the argument is a typed array, `false` if it's a
+// cross-compartment wrapper for a typed array.
+function IsTypedArrayEnsuringArrayBuffer(arg) {
+ if (IsObject(arg) && IsTypedArray(arg)) {
+ GetAttachedArrayBuffer(arg);
+ return true;
+ }
+
+ // This is a bit hacky but gets the job done: the first `arg` is used to
+ // test for a wrapped typed array, the second as an argument to
+ // GetAttachedArrayBuffer.
+ callFunction(CallTypedArrayMethodIfWrapped, arg, arg, "GetAttachedArrayBuffer");
+ return false;
+}
+
// ES6 draft 20150304 %TypedArray%.prototype.copyWithin
function TypedArrayCopyWithin(target, start, end = undefined) {
// This function is not generic.
if (!IsObject(this) || !IsTypedArray(this)) {
return callFunction(CallTypedArrayMethodIfWrapped, this, target, start, end,
"TypedArrayCopyWithin");
}
@@ -117,53 +136,51 @@ function TypedArrayCopyWithin(target, st
// ES6 draft rev30 (2014/12/24) 22.2.3.6 %TypedArray%.prototype.entries()
function TypedArrayEntries() {
// Step 1.
var O = this;
// We need to be a bit careful here, because in the Xray case we want to
// create the iterator in our current compartment.
//
- // Before doing that, though, we want to check that we have a typed
- // array and it does not have a detached array buffer. We do the latter by
- // just calling GetAttachedArrayBuffer() and letting it throw if there isn't
- // one. In the case when we're not sure we have a typed array (e.g. we
- // might have a cross-compartment wrapper for one), we can go ahead and call
- // GetAttachedArrayBuffer via CallTypedArrayMethodIfWrapped; that will throw
- // if we're not actually a wrapped typed array, or if we have a detached
- // array buffer.
+ // Before doing that, though, we want to check that we have a typed array
+ // and it does not have a detached array buffer. We do the latter by just
+ // calling GetAttachedArrayBuffer() and letting it throw if there isn't one.
+ // In the case when we're not sure we have a typed array (e.g. we might have
+ // a cross-compartment wrapper for one), we can go ahead and call
+ // GetAttachedArrayBuffer via IsTypedArrayEnsuringArrayBuffer; that will
+ // throw if we're not actually a wrapped typed array, or if we have a
+ // detached array buffer.
- // Step 2-3.
- if (!IsObject(O) || !IsTypedArray(O)) {
- // And also step 4-6.
- callFunction(CallTypedArrayMethodIfWrapped, O, O, "GetAttachedArrayBuffer");
- } else {
- // Step 4-6.
- GetAttachedArrayBuffer(O);
- }
+ // Step 2-6.
+ IsTypedArrayEnsuringArrayBuffer(O);
// Step 7.
return CreateArrayIterator(O, ITEM_KIND_KEY_AND_VALUE);
}
// ES6 draft rev30 (2014/12/24) 22.2.3.7 %TypedArray%.prototype.every(callbackfn[, thisArg]).
function TypedArrayEvery(callbackfn, thisArg = undefined) {
- // This function is not generic.
- if (!IsObject(this) || !IsTypedArray(this)) {
- return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
- "TypedArrayEvery");
- }
-
- GetAttachedArrayBuffer(this);
-
// Steps 1-2.
var O = this;
+ // This function is not generic.
+ // We want to make sure that we have an attached buffer, per spec prose.
+ var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
+
+ // If we got here, `this` is either a typed array or a cross-compartment
+ // wrapper for one.
+
// Steps 3-5.
- var len = TypedArrayLength(O);
+ var len;
+ if (isTypedArray) {
+ len = TypedArrayLength(O);
+ } else {
+ len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
+ }
// Step 6.
if (arguments.length === 0)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.every");
if (!IsCallable(callbackfn))
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
// Step 7.
@@ -230,25 +247,29 @@ function TypedArrayFill(value, start = 0
// ES6 draft 32 (2015-02-02) 22.2.3.9 %TypedArray%.prototype.filter(callbackfn[, thisArg])
function TypedArrayFilter(callbackfn, thisArg = undefined) {
// Step 1.
var O = this;
// Steps 2-3.
// This function is not generic.
- if (!IsObject(O) || !IsTypedArray(O)) {
- return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
- "TypedArrayFilter");
- }
+ // We want to make sure that we have an attached buffer, per spec prose.
+ var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
- GetAttachedArrayBuffer(O);
+ // If we got here, `this` is either a typed array or a cross-compartment
+ // wrapper for one.
// Step 4.
- var len = TypedArrayLength(O);
+ var len;
+ if (isTypedArray) {
+ len = TypedArrayLength(O);
+ } else {
+ len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
+ }
// Step 5.
if (arguments.length === 0)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.filter");
if (!IsCallable(callbackfn))
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
// Step 6.
@@ -291,29 +312,33 @@ function TypedArrayFilter(callbackfn, th
}
// Step 18.
return A;
}
// ES6 draft rev28 (2014/10/14) 22.2.3.10 %TypedArray%.prototype.find(predicate[, thisArg]).
function TypedArrayFind(predicate, thisArg = undefined) {
- // This function is not generic.
- if (!IsObject(this) || !IsTypedArray(this)) {
- return callFunction(CallTypedArrayMethodIfWrapped, this, predicate, thisArg,
- "TypedArrayFind");
- }
-
- GetAttachedArrayBuffer(this);
-
// Steps 1-2.
var O = this;
+ // This function is not generic.
+ // We want to make sure that we have an attached buffer, per spec prose.
+ var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
+
+ // If we got here, `this` is either a typed array or a cross-compartment
+ // wrapper for one.
+
// Steps 3-5.
- var len = TypedArrayLength(O);
+ var len;
+ if (isTypedArray) {
+ len = TypedArrayLength(O);
+ } else {
+ len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
+ }
// Step 6.
if (arguments.length === 0)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.find");
if (!IsCallable(predicate))
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate));
// Step 7.
@@ -330,29 +355,33 @@ function TypedArrayFind(predicate, thisA
}
// Step 10.
return undefined;
}
// ES6 draft rev28 (2014/10/14) 22.2.3.11 %TypedArray%.prototype.findIndex(predicate[, thisArg]).
function TypedArrayFindIndex(predicate, thisArg = undefined) {
- // This function is not generic.
- if (!IsObject(this) || !IsTypedArray(this)) {
- return callFunction(CallTypedArrayMethodIfWrapped, this, predicate, thisArg,
- "TypedArrayFindIndex");
- }
-
- GetAttachedArrayBuffer(this);
-
// Steps 1-2.
var O = this;
+ // This function is not generic.
+ // We want to make sure that we have an attached buffer, per spec prose.
+ var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
+
+ // If we got here, `this` is either a typed array or a cross-compartment
+ // wrapper for one.
+
// Steps 3-5.
- var len = TypedArrayLength(O);
+ var len;
+ if (isTypedArray) {
+ len = TypedArrayLength(O);
+ } else {
+ len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
+ }
// Step 6.
if (arguments.length === 0)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.findIndex");
if (!IsCallable(predicate))
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate));
// Step 7.
@@ -367,29 +396,33 @@ function TypedArrayFindIndex(predicate,
}
// Step 10.
return -1;
}
// ES6 draft rev31 (2015-01-15) 22.1.3.10 %TypedArray%.prototype.forEach(callbackfn[,thisArg])
function TypedArrayForEach(callbackfn, thisArg = undefined) {
- // This function is not generic.
- if (!IsObject(this) || !IsTypedArray(this)) {
- return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
- "TypedArrayForEach");
- }
-
- GetAttachedArrayBuffer(this);
-
// Step 1-2.
var O = this;
+ // This function is not generic.
+ // We want to make sure that we have an attached buffer, per spec prose.
+ var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
+
+ // If we got here, `this` is either a typed array or a cross-compartment
+ // wrapper for one.
+
// Step 3-4.
- var len = TypedArrayLength(O);
+ var len;
+ if (isTypedArray) {
+ len = TypedArrayLength(O);
+ } else {
+ len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
+ }
// Step 5.
if (arguments.length === 0)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'TypedArray.prototype.forEach');
if (!IsCallable(callbackfn))
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
// Step 6.
@@ -511,20 +544,17 @@ function TypedArrayJoin(separator) {
// ES6 draft (2016/1/11) 22.2.3.15 %TypedArray%.prototype.keys()
function TypedArrayKeys() {
// Step 1.
var O = this;
// See the big comment in TypedArrayEntries for what we're doing here.
// Step 2.
- if (!IsObject(O) || !IsTypedArray(O))
- callFunction(CallTypedArrayMethodIfWrapped, O, O, "GetAttachedArrayBuffer");
- else
- GetAttachedArrayBuffer(O);
+ IsTypedArrayEnsuringArrayBuffer(O);
// Step 3.
return CreateArrayIterator(O, ITEM_KIND_KEY);
}
// ES6 draft rev29 (2014/12/06) 22.2.3.16 %TypedArray%.prototype.lastIndexOf(searchElement [,fromIndex]).
function TypedArrayLastIndexOf(searchElement, fromIndex = undefined) {
// This function is not generic.
@@ -564,25 +594,29 @@ function TypedArrayLastIndexOf(searchEle
// ES6 draft rev32 (2015-02-02) 22.2.3.18 %TypedArray%.prototype.map(callbackfn [, thisArg]).
function TypedArrayMap(callbackfn, thisArg = undefined) {
// Step 1.
var O = this;
// Steps 2-3.
// This function is not generic.
- if (!IsObject(O) || !IsTypedArray(O)) {
- return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
- "TypedArrayMap");
- }
+ // We want to make sure that we have an attached buffer, per spec prose.
+ var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
- GetAttachedArrayBuffer(O);
+ // If we got here, `this` is either a typed array or a cross-compartment
+ // wrapper for one.
// Step 4.
- var len = TypedArrayLength(O);
+ var len;
+ if (isTypedArray) {
+ len = TypedArrayLength(O);
+ } else {
+ len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
+ }
// Step 5.
if (arguments.length === 0)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, '%TypedArray%.prototype.map');
if (!IsCallable(callbackfn))
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
// Step 6.
@@ -606,27 +640,33 @@ function TypedArrayMap(callbackfn, thisA
}
// Step 14.
return A;
}
// ES6 draft rev30 (2014/12/24) 22.2.3.19 %TypedArray%.prototype.reduce(callbackfn[, initialValue]).
function TypedArrayReduce(callbackfn/*, initialValue*/) {
- // This function is not generic.
- if (!IsObject(this) || !IsTypedArray(this))
- return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, "TypedArrayReduce");
-
- GetAttachedArrayBuffer(this);
-
// Steps 1-2.
var O = this;
+ // This function is not generic.
+ // We want to make sure that we have an attached buffer, per spec prose.
+ var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
+
+ // If we got here, `this` is either a typed array or a cross-compartment
+ // wrapper for one.
+
// Steps 3-5.
- var len = TypedArrayLength(O);
+ var len;
+ if (isTypedArray) {
+ len = TypedArrayLength(O);
+ } else {
+ len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
+ }
// Step 6.
if (arguments.length === 0)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.reduce");
if (!IsCallable(callbackfn))
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
// Step 7.
@@ -647,27 +687,33 @@ function TypedArrayReduce(callbackfn/*,
}
// Step 12.
return accumulator;
}
// ES6 draft rev30 (2014/12/24) 22.2.3.20 %TypedArray%.prototype.reduceRight(callbackfn[, initialValue]).
function TypedArrayReduceRight(callbackfn/*, initialValue*/) {
- // This function is not generic.
- if (!IsObject(this) || !IsTypedArray(this))
- return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, "TypedArrayReduceRight");
-
- GetAttachedArrayBuffer(this);
-
// Steps 1-2.
var O = this;
+ // This function is not generic.
+ // We want to make sure that we have an attached buffer, per spec prose.
+ var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
+
+ // If we got here, `this` is either a typed array or a cross-compartment
+ // wrapper for one.
+
// Steps 3-5.
- var len = TypedArrayLength(O);
+ var len;
+ if (isTypedArray) {
+ len = TypedArrayLength(O);
+ } else {
+ len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
+ }
// Step 6.
if (arguments.length === 0)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.reduceRight");
if (!IsCallable(callbackfn))
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
// Step 7.
@@ -908,29 +954,33 @@ function TypedArraySlice(start, end) {
}
// Step 19.
return A;
}
// ES6 draft rev30 (2014/12/24) 22.2.3.25 %TypedArray%.prototype.some(callbackfn[, thisArg]).
function TypedArraySome(callbackfn, thisArg = undefined) {
- // This function is not generic.
- if (!IsObject(this) || !IsTypedArray(this)) {
- return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
- "TypedArraySome");
- }
-
- GetAttachedArrayBuffer(this);
-
// Steps 1-2.
var O = this;
+ // This function is not generic.
+ // We want to make sure that we have an attached buffer, per spec prose.
+ var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
+
+ // If we got here, `this` is either a typed array or a cross-compartment
+ // wrapper for one.
+
// Steps 3-5.
- var len = TypedArrayLength(O);
+ var len;
+ if (isTypedArray) {
+ len = TypedArrayLength(O);
+ } else {
+ len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
+ }
// Step 6.
if (arguments.length === 0)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.some");
if (!IsCallable(callbackfn))
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
// Step 7.
@@ -981,26 +1031,22 @@ function TypedArrayCompare(x, y) {
if (Number_isNaN(x) || Number_isNaN(y))
return Number_isNaN(x) ? 1 : -1;
}
// ES6 draft 20151210 22.2.3.26 %TypedArray%.prototype.sort ( comparefn ).
function TypedArraySort(comparefn) {
// This function is not generic.
- if (!IsObject(this) || !IsTypedArray(this)) {
- return callFunction(CallTypedArrayMethodIfWrapped, this, comparefn,
- "TypedArraySort");
- }
// Step 1.
var obj = this;
// Step 2.
- var buffer = GetAttachedArrayBuffer(obj);
+ var isTypedArray = IsTypedArrayEnsuringArrayBuffer(obj);
// Step 3.
var len = TypedArrayLength(obj);
if (comparefn === undefined) {
comparefn = TypedArrayCompare;
// CountingSort doesn't invoke the comparefn
if (IsUint8TypedArray(obj)) {
@@ -1013,30 +1059,40 @@ function TypedArraySort(comparefn) {
return RadixSort(obj, len, 2 /* nbytes */, true /* signed */, false /* floating */, comparefn);
} else if (IsUint32TypedArray(obj)) {
return RadixSort(obj, len, 4 /* nbytes */, false /* signed */, false /* floating */, comparefn);
} else if (IsInt32TypedArray(obj)) {
return RadixSort(obj, len, 4 /* nbytes */, true /* signed */, false /* floating */, comparefn);
} else if (IsFloat32TypedArray(obj)) {
return RadixSort(obj, len, 4 /* nbytes */, true /* signed */, true /* floating */, comparefn);
}
- // To satisfy step 2 from TypedArray SortCompare described in 22.2.3.26
- // the user supplied comparefn is wrapped.
- var wrappedCompareFn = comparefn;
- comparefn = function(x, y) {
- // Step a.
- var v = wrappedCompareFn(x, y);
- // Step b.
- if (IsDetachedBuffer(buffer))
- ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
- // Step c. is redundant, see:
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1121937#c36
- // Step d.
- return v;
+ }
+
+ // To satisfy step 2 from TypedArray SortCompare described in 22.2.3.26
+ // the user supplied comparefn is wrapped.
+ var wrappedCompareFn = comparefn;
+ comparefn = function(x, y) {
+ // Step a.
+ var v = wrappedCompareFn(x, y);
+ // Step b.
+ var bufferDetached;
+ if (isTypedArray) {
+ bufferDetached = IsDetachedBuffer(buffer);
+ } else {
+ // This is totally cheating and only works because we know `this`
+ // and `buffer` are same-compartment".
+ bufferDetached = callFunction(CallTypedArrayMethodIfWrapped, this,
+ buffer, "IsDetachedBuffer");
}
+ if (bufferDetached)
+ ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
+ // Step c. is redundant, see:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1121937#c36
+ // Step d.
+ return v;
}
return QuickSort(obj, len, comparefn);
}
// ES6 draft 20150304 %TypedArray%.prototype.subarray
function TypedArraySubarray(begin, end) {
// Step 1.
@@ -1084,25 +1140,17 @@ function TypedArraySubarray(begin, end)
}
// ES6 draft rev30 (2014/12/24) 22.2.3.30 %TypedArray%.prototype.values()
function TypedArrayValues() {
// Step 1.
var O = this;
// See the big comment in TypedArrayEntries for what we're doing here.
-
- // Steps 2-6.
- if (!IsObject(O) || !IsTypedArray(O)) {
- // And also steps 4-6.
- callFunction(CallTypedArrayMethodIfWrapped, O, O, "GetAttachedArrayBuffer");
- } else {
- // Steps 4-6.
- GetAttachedArrayBuffer(O);
- }
+ IsTypedArrayEnsuringArrayBuffer(O);
// Step 7.
return CreateArrayIterator(O, ITEM_KIND_VALUE);
}
_SetCanonicalName(TypedArrayValues, "values");
// Proposed for ES7:
// https://github.com/tc39/Array.prototype.includes/blob/7c023c19a0/spec.md