in src/bun.js/bindings/webcore/SerializedScriptValue.cpp [1538:1937]
bool dumpIfTerminal(JSValue value, SerializationReturnCode& code)
{
if (!value.isCell()) {
dumpImmediate(value, code);
return true;
}
ASSERT(value.isCell());
if (value.isString()) {
dumpString(asString(value)->value(m_lexicalGlobalObject));
return true;
}
if (value.isHeapBigInt()) {
write(BigIntTag);
dumpBigIntData(value);
return true;
}
if (value.isSymbol()) {
code = SerializationReturnCode::DataCloneError;
return true;
}
VM& vm = m_lexicalGlobalObject->vm();
if (isArray(value))
return false;
if (value.isObject()) {
auto* obj = asObject(value);
if (auto* dateObject = jsDynamicCast<DateInstance*>(obj)) {
write(DateTag);
write(dateObject->internalNumber());
return true;
}
if (auto* booleanObject = jsDynamicCast<BooleanObject*>(obj)) {
if (!startObjectInternal(booleanObject)) // handle duplicates
return true;
write(booleanObject->internalValue().toBoolean(m_lexicalGlobalObject) ? TrueObjectTag : FalseObjectTag);
return true;
}
if (auto* stringObject = jsDynamicCast<StringObject*>(obj)) {
if (!startObjectInternal(stringObject)) // handle duplicates
return true;
String str = asString(stringObject->internalValue())->value(m_lexicalGlobalObject);
dumpStringObject(str);
return true;
}
if (auto* numberObject = jsDynamicCast<NumberObject*>(obj)) {
if (!startObjectInternal(numberObject)) // handle duplicates
return true;
write(NumberObjectTag);
write(numberObject->internalValue().asNumber());
return true;
}
if (auto* bigIntObject = jsDynamicCast<BigIntObject*>(obj)) {
if (!startObjectInternal(bigIntObject)) // handle duplicates
return true;
JSValue bigIntValue = bigIntObject->internalValue();
ASSERT(bigIntValue.isBigInt());
write(BigIntObjectTag);
dumpBigIntData(bigIntValue);
return true;
}
// if (auto* file = JSFile::toWrapped(vm, obj)) {
// write(FileTag);
// write(*file);
// return true;
// }
// if (auto* list = JSFileList::toWrapped(vm, obj)) {
// write(FileListTag);
// write(list->length());
// for (auto& file : list->files())
// write(file.get());
// return true;
// }
// write bun types
if (auto _cloneable = StructuredCloneableSerialize::fromJS(value)) {
StructuredCloneableSerialize cloneable = WTFMove(_cloneable.value());
write(cloneable.tag);
cloneable.write(this, m_lexicalGlobalObject);
return true;
}
// if (auto* blob = JSBlob::toWrapped(vm, obj)) {
// write(BlobTag);
// m_blobHandles.append(blob->handle().isolatedCopy());
// write(blob->url().string());
// write(blob->type());
// static_assert(sizeof(uint64_t) == sizeof(decltype(blob->size())));
// uint64_t size = blob->size();
// write(size);
// uint64_t memoryCost = blob->memoryCost();
// write(memoryCost);
// return true;
// }
// if (auto* data = JSImageData::toWrapped(vm, obj)) {
// write(ImageDataTag);
// auto addResult = m_imageDataPool.add(*data, m_imageDataPool.size());
// if (!addResult.isNewEntry) {
// write(ImageDataPoolTag);
// writeImageDataIndex(addResult.iterator->value);
// return true;
// }
// write(static_cast<uint32_t>(data->width()));
// write(static_cast<uint32_t>(data->height()));
// CheckedUint32 dataLength = data->data().length();
// if (dataLength.hasOverflowed()) {
// code = SerializationReturnCode::DataCloneError;
// return true;
// }
// write(dataLength);
// write(data->data().data(), dataLength);
// write(data->colorSpace());
// return true;
// }
if (auto* regExp = jsDynamicCast<RegExpObject*>(obj)) {
write(RegExpTag);
write(regExp->regExp()->pattern());
write(String::fromLatin1(JSC::Yarr::flagsString(regExp->regExp()->flags()).data()));
return true;
}
if (auto* errorInstance = jsDynamicCast<ErrorInstance*>(obj)) {
auto& vm = m_lexicalGlobalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto errorTypeValue = errorInstance->get(m_lexicalGlobalObject, vm.propertyNames->name);
RETURN_IF_EXCEPTION(scope, false);
auto errorTypeString = errorTypeValue.toWTFString(m_lexicalGlobalObject);
RETURN_IF_EXCEPTION(scope, false);
String message;
PropertyDescriptor messageDescriptor;
if (errorInstance->getOwnPropertyDescriptor(m_lexicalGlobalObject, vm.propertyNames->message, messageDescriptor) && messageDescriptor.isDataDescriptor()) {
EXCEPTION_ASSERT(!scope.exception());
message = messageDescriptor.value().toWTFString(m_lexicalGlobalObject);
}
RETURN_IF_EXCEPTION(scope, false);
unsigned line = 0;
PropertyDescriptor lineDescriptor;
if (errorInstance->getOwnPropertyDescriptor(m_lexicalGlobalObject, vm.propertyNames->line, lineDescriptor) && lineDescriptor.isDataDescriptor()) {
EXCEPTION_ASSERT(!scope.exception());
line = lineDescriptor.value().toNumber(m_lexicalGlobalObject);
}
RETURN_IF_EXCEPTION(scope, false);
unsigned column = 0;
PropertyDescriptor columnDescriptor;
if (errorInstance->getOwnPropertyDescriptor(m_lexicalGlobalObject, vm.propertyNames->column, columnDescriptor) && columnDescriptor.isDataDescriptor()) {
EXCEPTION_ASSERT(!scope.exception());
column = columnDescriptor.value().toNumber(m_lexicalGlobalObject);
}
RETURN_IF_EXCEPTION(scope, false);
String sourceURL;
PropertyDescriptor sourceURLDescriptor;
if (errorInstance->getOwnPropertyDescriptor(m_lexicalGlobalObject, vm.propertyNames->sourceURL, sourceURLDescriptor) && sourceURLDescriptor.isDataDescriptor()) {
EXCEPTION_ASSERT(!scope.exception());
sourceURL = sourceURLDescriptor.value().toWTFString(m_lexicalGlobalObject);
}
RETURN_IF_EXCEPTION(scope, false);
String stack;
PropertyDescriptor stackDescriptor;
if (errorInstance->getOwnPropertyDescriptor(m_lexicalGlobalObject, vm.propertyNames->stack, stackDescriptor) && stackDescriptor.isDataDescriptor()) {
EXCEPTION_ASSERT(!scope.exception());
stack = stackDescriptor.value().toWTFString(m_lexicalGlobalObject);
}
RETURN_IF_EXCEPTION(scope, false);
write(ErrorInstanceTag);
write(errorNameToSerializableErrorType(errorTypeString));
writeNullableString(message);
write(line);
write(column);
writeNullableString(sourceURL);
writeNullableString(stack);
return true;
}
if (obj->inherits<JSMessagePort>()) {
auto index = m_transferredMessagePorts.find(obj);
if (index != m_transferredMessagePorts.end()) {
write(MessagePortReferenceTag);
write(index->value);
return true;
}
// MessagePort object could not be found in transferred message ports
code = SerializationReturnCode::ValidationError;
return true;
}
if (auto* arrayBuffer = toPossiblySharedArrayBuffer(vm, obj)) {
if (arrayBuffer->isDetached()) {
code = SerializationReturnCode::ValidationError;
return true;
}
auto index = m_transferredArrayBuffers.find(obj);
if (index != m_transferredArrayBuffers.end()) {
write(ArrayBufferTransferTag);
write(index->value);
return true;
}
if (!startObjectInternal(obj)) // handle duplicates
return true;
if (arrayBuffer->isShared() && m_context == SerializationContext::WorkerPostMessage) {
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
if (!JSC::Options::useSharedArrayBuffer()) {
code = SerializationReturnCode::DataCloneError;
return true;
}
uint32_t index = m_sharedBuffers.size();
ArrayBufferContents contents;
if (arrayBuffer->shareWith(contents)) {
write(SharedArrayBufferTag);
m_sharedBuffers.append(WTFMove(contents));
write(index);
return true;
}
}
if (arrayBuffer->isResizableOrGrowableShared()) {
write(ResizableArrayBufferTag);
uint64_t byteLength = arrayBuffer->byteLength();
write(byteLength);
uint64_t maxByteLength = arrayBuffer->maxByteLength().value_or(0);
write(maxByteLength);
write(static_cast<const uint8_t*>(arrayBuffer->data()), byteLength);
return true;
}
write(ArrayBufferTag);
uint64_t byteLength = arrayBuffer->byteLength();
write(byteLength);
write(static_cast<const uint8_t*>(arrayBuffer->data()), byteLength);
return true;
}
if (obj->inherits<JSArrayBufferView>()) {
if (checkForDuplicate(obj))
return true;
bool success = dumpArrayBufferView(obj, code);
recordObject(obj);
return success;
}
#if ENABLE(WEB_CRYPTO)
if (auto* key = JSCryptoKey::toWrapped(vm, obj)) {
write(CryptoKeyTag);
Vector<uint8_t> serializedKey;
// Vector<URLKeepingBlobAlive> dummyBlobHandles;
Vector<RefPtr<MessagePort>> dummyMessagePorts;
Vector<RefPtr<JSC::ArrayBuffer>> dummyArrayBuffers;
#if ENABLE(WEB_CODECS)
Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>> dummyVideoChunks;
Vector<RefPtr<WebCodecsVideoFrame>> dummyVideoFrames;
#endif
#if ENABLE(WEBASSEMBLY)
WasmModuleArray dummyModules;
WasmMemoryHandleArray dummyMemoryHandles;
#endif
ArrayBufferContentsArray dummySharedBuffers;
// CloneSerializer rawKeySerializer(m_lexicalGlobalObject, dummyMessagePorts, dummyArrayBuffers, {},
// #if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
// {},
// #endif
// #if ENABLE(WEB_RTC)
// {},
// #endif
// #if ENABLE(WEB_CODECS)
// dummyVideoChunks,
// dummyVideoFrames,
// #endif
// #if ENABLE(WEBASSEMBLY)
// dummyModules,
// dummyMemoryHandles,
// #endif
// dummyBlobHandles, serializedKey, SerializationContext::Default, dummySharedBuffers, m_forStorage);
CloneSerializer rawKeySerializer(m_lexicalGlobalObject, dummyMessagePorts, dummyArrayBuffers,
#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
{},
#endif
#if ENABLE(WEB_RTC)
{},
#endif
#if ENABLE(WEB_CODECS)
dummyVideoChunks,
dummyVideoFrames,
#endif
#if ENABLE(WEBASSEMBLY)
dummyModules,
dummyMemoryHandles,
#endif
serializedKey, SerializationContext::Default, dummySharedBuffers, m_forStorage);
rawKeySerializer.write(key);
Vector<uint8_t> wrappedKey;
if (!wrapCryptoKey(m_lexicalGlobalObject, serializedKey, wrappedKey))
return false;
write(wrappedKey);
return true;
}
#endif
#if ENABLE(WEB_RTC)
if (auto* rtcCertificate = JSRTCCertificate::toWrapped(vm, obj)) {
write(RTCCertificateTag);
write(rtcCertificate->expires());
write(rtcCertificate->pemCertificate());
write(rtcCertificate->origin().toString());
write(rtcCertificate->pemPrivateKey());
write(static_cast<unsigned>(rtcCertificate->getFingerprints().size()));
for (const auto& fingerprint : rtcCertificate->getFingerprints()) {
write(fingerprint.algorithm);
write(fingerprint.value);
}
return true;
}
#endif
#if ENABLE(WEBASSEMBLY)
if (JSWebAssemblyModule* module = jsDynamicCast<JSWebAssemblyModule*>(obj)) {
if (m_context != SerializationContext::WorkerPostMessage && m_context != SerializationContext::WindowPostMessage)
return false;
uint32_t index = m_wasmModules.size();
m_wasmModules.append(&module->module());
write(WasmModuleTag);
write(agentClusterIDFromGlobalObject(*m_lexicalGlobalObject));
write(index);
return true;
}
if (JSWebAssemblyMemory* memory = jsDynamicCast<JSWebAssemblyMemory*>(obj)) {
if (!JSC::Options::useSharedArrayBuffer() || memory->memory().sharingMode() != JSC::MemorySharingMode::Shared) {
code = SerializationReturnCode::DataCloneError;
return true;
}
if (m_context != SerializationContext::WorkerPostMessage) {
code = SerializationReturnCode::DataCloneError;
return true;
}
uint32_t index = m_wasmMemoryHandles.size();
m_wasmMemoryHandles.append(memory->memory().shared());
write(WasmMemoryTag);
write(agentClusterIDFromGlobalObject(*m_lexicalGlobalObject));
write(index);
return true;
}
#endif
// if (obj->inherits<JSDOMPointReadOnly>()) {
// dumpDOMPoint(obj);
// return true;
// }
// if (obj->inherits<JSDOMRectReadOnly>()) {
// dumpDOMRect(obj);
// return true;
// }
// if (obj->inherits<JSDOMMatrixReadOnly>()) {
// dumpDOMMatrix(obj);
// return true;
// }
// if (obj->inherits<JSDOMQuad>()) {
// dumpDOMQuad(obj);
// return true;
// }
// if (obj->inherits<JSImageBitmap>()) {
// dumpImageBitmap(obj, code);
// return true;
// }
#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
if (obj->inherits<JSOffscreenCanvas>()) {
dumpOffscreenCanvas(obj, code);
return true;
}
#endif
#if ENABLE(WEB_RTC)
if (obj->inherits<JSRTCDataChannel>()) {
dumpRTCDataChannel(obj, code);
return true;
}
#endif
if (obj->inherits<JSDOMException>()) {
dumpDOMException(obj, code);
return true;
}
#if ENABLE(WEB_CODECS)
if (obj->inherits<JSWebCodecsEncodedVideoChunk>()) {
if (m_forStorage == SerializationForStorage::Yes)
return false;
dumpWebCodecsEncodedVideoChunk(obj);
return true;
}
if (obj->inherits<JSWebCodecsVideoFrame>()) {
if (m_forStorage == SerializationForStorage::Yes)
return false;
return dumpWebCodecsVideoFrame(obj);
}
#endif
return false;
}
// Any other types are expected to serialize as null.
write(NullTag);
return true;
}