summaryrefslogtreecommitdiff
path: root/js/src/vm/StructuredClone.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/StructuredClone.cpp')
-rw-r--r--js/src/vm/StructuredClone.cpp78
1 files changed, 76 insertions, 2 deletions
diff --git a/js/src/vm/StructuredClone.cpp b/js/src/vm/StructuredClone.cpp
index 26e57976fc..e99cfe8f71 100644
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -31,6 +31,7 @@
#include "mozilla/CheckedInt.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/FloatingPoint.h"
+#include "mozilla/RangedPtr.h"
#include <algorithm>
@@ -39,6 +40,8 @@
#include "jsdate.h"
#include "jswrapper.h"
+#include "vm/BigIntType.h"
+
#include "builtin/MapObject.h"
#include "js/Date.h"
#include "js/GCHashTable.h"
@@ -57,6 +60,7 @@ using mozilla::IsNaN;
using mozilla::LittleEndian;
using mozilla::NativeEndian;
using mozilla::NumbersAreIdentical;
+using mozilla::RangedPtr;
using JS::CanonicalizeNaN;
// When you make updates here, make sure you consider whether you need to bump the
@@ -104,6 +108,9 @@ enum StructuredDataType : uint32_t {
SCTAG_SHARED_ARRAY_BUFFER_OBJECT,
+ SCTAG_BIGINT,
+ SCTAG_BIGINT_OBJECT,
+
SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100,
SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int8,
SCTAG_TYPED_ARRAY_V1_UINT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint8,
@@ -114,7 +121,8 @@ enum StructuredDataType : uint32_t {
SCTAG_TYPED_ARRAY_V1_FLOAT32 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Float32,
SCTAG_TYPED_ARRAY_V1_FLOAT64 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Float64,
SCTAG_TYPED_ARRAY_V1_UINT8_CLAMPED = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint8Clamped,
- SCTAG_TYPED_ARRAY_V1_MAX = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::MaxTypedArrayViewType - 1,
+ // BigInt64 and BigUint64 are not supported in the v1 format.
+ SCTAG_TYPED_ARRAY_V1_MAX = SCTAG_TYPED_ARRAY_V1_UINT8_CLAMPED,
/*
* Define a separate range of numbers for Transferable-only tags, since
@@ -349,6 +357,8 @@ struct JSStructuredCloneReader {
JSString* readStringImpl(uint32_t nchars);
JSString* readString(uint32_t data);
+ BigInt* readBigInt(uint32_t data);
+
bool checkDouble(double d);
bool readTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp,
bool v1Read = false);
@@ -439,6 +449,8 @@ struct JSStructuredCloneWriter {
bool traverseSet(HandleObject obj);
bool traverseSavedFrame(HandleObject obj);
+ bool writeBigInt(uint32_t tag, BigInt* bi);
+
bool reportDataCloneError(uint32_t errorId);
bool parseTransferable();
@@ -1055,6 +1067,23 @@ JSStructuredCloneWriter::writeString(uint32_t tag, JSString* str)
: out.writeChars(linear->twoByteChars(nogc), length);
}
+bool
+JSStructuredCloneWriter::writeBigInt(uint32_t tag, BigInt* bi)
+{
+ bool signBit = bi->isNegative();
+ size_t length = bi->digitLength();
+ // The length must fit in 31 bits to leave room for a sign bit.
+ if (length > size_t(INT32_MAX)) {
+ return false;
+ }
+ uint32_t lengthAndSign = length | (static_cast<uint32_t>(signBit) << 31);
+
+ if (!out.writePair(tag, lengthAndSign)) {
+ return false;
+ }
+ return out.writeArray(bi->digits().data(), length);
+}
+
inline void
JSStructuredCloneWriter::checkStack()
{
@@ -1388,6 +1417,8 @@ JSStructuredCloneWriter::startWrite(HandleValue v)
return out.writePair(SCTAG_NULL, 0);
} else if (v.isUndefined()) {
return out.writePair(SCTAG_UNDEFINED, 0);
+ } else if (v.isBigInt()) {
+ return writeBigInt(SCTAG_BIGINT, v.toBigInt());
} else if (v.isObject()) {
RootedObject obj(context(), &v.toObject());
@@ -1441,6 +1472,12 @@ JSStructuredCloneWriter::startWrite(HandleValue v)
return writeString(SCTAG_STRING_OBJECT, unboxed.toString());
} else if (cls == ESClass::Map) {
return traverseMap(obj);
+ } else if (cls == ESClass::BigInt) {
+ RootedValue unboxed(context());
+ if (!Unbox(context(), obj, &unboxed)) {
+ return false;
+ }
+ return writeBigInt(SCTAG_BIGINT_OBJECT, unboxed.toBigInt());
} else if (cls == ESClass::Set) {
return traverseSet(obj);
} else if (SavedFrame::isSavedFrameOrWrapperAndNotProto(*obj)) {
@@ -1745,6 +1782,22 @@ JSStructuredCloneReader::readString(uint32_t data)
return latin1 ? readStringImpl<Latin1Char>(nchars) : readStringImpl<char16_t>(nchars);
}
+BigInt* JSStructuredCloneReader::readBigInt(uint32_t data) {
+ size_t length = data & JS_BITMASK(31);
+ bool isNegative = data & (1 << 31);
+ if (length == 0) {
+ return BigInt::zero(context());
+ }
+ BigInt* result = BigInt::createUninitialized(context(), length, isNegative);
+ if (!result) {
+ return nullptr;
+ }
+ if (!in.readArray(result->digits().data(), length)) {
+ return nullptr;
+ }
+ return result;
+}
+
static uint32_t
TagToV1ArrayType(uint32_t tag)
{
@@ -1756,7 +1809,7 @@ bool
JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp,
bool v1Read)
{
- if (arrayType > Scalar::Uint8Clamped) {
+ if (arrayType > (v1Read ? Scalar::Uint8Clamped : Scalar::BigUint64)) {
JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
"unhandled typed array element type");
return false;
@@ -1820,6 +1873,12 @@ JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, Mut
case Scalar::Uint8Clamped:
obj = JS_NewUint8ClampedArrayWithBuffer(context(), buffer, byteOffset, nelems);
break;
+ case Scalar::BigInt64:
+ obj = JS_NewBigInt64ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+ break;
+ case Scalar::BigUint64:
+ obj = JS_NewBigUint64ArrayWithBuffer(context(), buffer, byteOffset, nelems);
+ break;
default:
MOZ_CRASH("Can't happen: arrayType range checked above");
}
@@ -1955,6 +2014,8 @@ JSStructuredCloneReader::readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems,
case Scalar::Float32:
return in.readArray((uint32_t*) buffer.dataPointer(), nelems);
case Scalar::Float64:
+ case Scalar::BigInt64:
+ case Scalar::BigUint64:
return in.readArray((uint64_t*) buffer.dataPointer(), nelems);
default:
MOZ_CRASH("Can't happen: arrayType range checked by caller");
@@ -2021,6 +2082,19 @@ JSStructuredCloneReader::startRead(MutableHandleValue vp)
break;
}
+ case SCTAG_BIGINT:
+ case SCTAG_BIGINT_OBJECT: {
+ RootedBigInt bi(context(), readBigInt(data));
+ if (!bi) {
+ return false;
+ }
+ vp.setBigInt(bi);
+ if (tag == SCTAG_BIGINT_OBJECT && !PrimitiveToObject(context(), vp)) {
+ return false;
+ }
+ break;
+ }
+
case SCTAG_DATE_OBJECT: {
double d;
if (!in.readDouble(&d) || !checkDouble(d))