in src/bun.js/bindings/webcore/SerializedScriptValue.cpp [2436:2693]
SerializationReturnCode CloneSerializer::serialize(JSValue in)
{
VM& vm = m_lexicalGlobalObject->vm();
Vector<uint32_t, 16> indexStack;
Vector<uint32_t, 16> lengthStack;
Vector<PropertyNameArray, 16> propertyStack;
Vector<JSObject*, 32> inputObjectStack;
Vector<JSMapIterator*, 4> mapIteratorStack;
Vector<JSSetIterator*, 4> setIteratorStack;
Vector<JSValue, 4> mapIteratorValueStack;
Vector<WalkerState, 16> stateStack;
WalkerState state = StateUnknown;
JSValue inValue = in;
auto scope = DECLARE_THROW_SCOPE(vm);
while (1) {
switch (state) {
arrayStartState:
case ArrayStartState: {
ASSERT(isArray(inValue));
if (inputObjectStack.size() > maximumFilterRecursion)
return SerializationReturnCode::StackOverflowError;
JSArray* inArray = asArray(inValue);
unsigned length = inArray->length();
if (!startArray(inArray))
break;
inputObjectStack.append(inArray);
indexStack.append(0);
lengthStack.append(length);
}
arrayStartVisitMember:
FALLTHROUGH;
case ArrayStartVisitMember: {
JSObject* array = inputObjectStack.last();
uint32_t index = indexStack.last();
if (index == lengthStack.last()) {
indexStack.removeLast();
lengthStack.removeLast();
propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude));
array->getOwnNonIndexPropertyNames(m_lexicalGlobalObject, propertyStack.last(), DontEnumPropertiesMode::Exclude);
if (UNLIKELY(scope.exception()))
return SerializationReturnCode::ExistingExceptionError;
if (propertyStack.last().size()) {
write(NonIndexPropertiesTag);
indexStack.append(0);
goto objectStartVisitMember;
}
propertyStack.removeLast();
endObject();
inputObjectStack.removeLast();
break;
}
inValue = array->getDirectIndex(m_lexicalGlobalObject, index);
if (UNLIKELY(scope.exception()))
return SerializationReturnCode::ExistingExceptionError;
if (!inValue) {
indexStack.last()++;
goto arrayStartVisitMember;
}
write(index);
auto terminalCode = SerializationReturnCode::SuccessfullyCompleted;
if (dumpIfTerminal(inValue, terminalCode)) {
if (terminalCode != SerializationReturnCode::SuccessfullyCompleted)
return terminalCode;
indexStack.last()++;
goto arrayStartVisitMember;
}
stateStack.append(ArrayEndVisitMember);
goto stateUnknown;
}
case ArrayEndVisitMember: {
indexStack.last()++;
goto arrayStartVisitMember;
}
objectStartState:
case ObjectStartState: {
ASSERT(inValue.isObject());
if (inputObjectStack.size() > maximumFilterRecursion)
return SerializationReturnCode::StackOverflowError;
JSObject* inObject = asObject(inValue);
if (!startObject(inObject))
break;
// At this point, all supported objects other than Object
// objects have been handled. If we reach this point and
// the input is not an Object object then we should throw
// a DataCloneError.
if (inObject->classInfo() != JSFinalObject::info())
return SerializationReturnCode::DataCloneError;
inputObjectStack.append(inObject);
indexStack.append(0);
propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude));
inObject->methodTable()->getOwnPropertyNames(inObject, m_lexicalGlobalObject, propertyStack.last(), DontEnumPropertiesMode::Exclude);
if (UNLIKELY(scope.exception()))
return SerializationReturnCode::ExistingExceptionError;
}
objectStartVisitMember:
FALLTHROUGH;
case ObjectStartVisitMember: {
JSObject* object = inputObjectStack.last();
uint32_t index = indexStack.last();
PropertyNameArray& properties = propertyStack.last();
if (index == properties.size()) {
endObject();
inputObjectStack.removeLast();
indexStack.removeLast();
propertyStack.removeLast();
break;
}
inValue = getProperty(object, properties[index]);
if (UNLIKELY(scope.exception()))
return SerializationReturnCode::ExistingExceptionError;
if (!inValue) {
// Property was removed during serialisation
indexStack.last()++;
goto objectStartVisitMember;
}
write(properties[index]);
if (UNLIKELY(scope.exception()))
return SerializationReturnCode::ExistingExceptionError;
auto terminalCode = SerializationReturnCode::SuccessfullyCompleted;
if (!dumpIfTerminal(inValue, terminalCode)) {
stateStack.append(ObjectEndVisitMember);
goto stateUnknown;
}
if (terminalCode != SerializationReturnCode::SuccessfullyCompleted)
return terminalCode;
FALLTHROUGH;
}
case ObjectEndVisitMember: {
if (UNLIKELY(scope.exception()))
return SerializationReturnCode::ExistingExceptionError;
indexStack.last()++;
goto objectStartVisitMember;
}
mapStartState : {
ASSERT(inValue.isObject());
if (inputObjectStack.size() > maximumFilterRecursion)
return SerializationReturnCode::StackOverflowError;
JSMap* inMap = jsCast<JSMap*>(inValue);
if (!startMap(inMap))
break;
JSMapIterator* iterator = JSMapIterator::create(m_lexicalGlobalObject, m_lexicalGlobalObject->mapIteratorStructure(), inMap, IterationKind::Entries);
if (UNLIKELY(scope.exception()))
return SerializationReturnCode::ExistingExceptionError;
m_gcBuffer.appendWithCrashOnOverflow(inMap);
m_gcBuffer.appendWithCrashOnOverflow(iterator);
mapIteratorStack.append(iterator);
inputObjectStack.append(inMap);
goto mapDataStartVisitEntry;
}
mapDataStartVisitEntry:
case MapDataStartVisitEntry: {
JSMapIterator* iterator = mapIteratorStack.last();
JSValue key, value;
if (!iterator->nextKeyValue(m_lexicalGlobalObject, key, value)) {
mapIteratorStack.removeLast();
JSObject* object = inputObjectStack.last();
ASSERT(jsDynamicCast<JSMap*>(object));
propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude));
object->methodTable()->getOwnPropertyNames(object, m_lexicalGlobalObject, propertyStack.last(), DontEnumPropertiesMode::Exclude);
if (UNLIKELY(scope.exception()))
return SerializationReturnCode::ExistingExceptionError;
write(NonMapPropertiesTag);
indexStack.append(0);
goto objectStartVisitMember;
}
inValue = key;
m_gcBuffer.appendWithCrashOnOverflow(value);
mapIteratorValueStack.append(value);
stateStack.append(MapDataEndVisitKey);
goto stateUnknown;
}
case MapDataEndVisitKey: {
inValue = mapIteratorValueStack.last();
mapIteratorValueStack.removeLast();
stateStack.append(MapDataEndVisitValue);
goto stateUnknown;
}
case MapDataEndVisitValue: {
goto mapDataStartVisitEntry;
}
setStartState : {
ASSERT(inValue.isObject());
if (inputObjectStack.size() > maximumFilterRecursion)
return SerializationReturnCode::StackOverflowError;
JSSet* inSet = jsCast<JSSet*>(inValue);
if (!startSet(inSet))
break;
JSSetIterator* iterator = JSSetIterator::create(m_lexicalGlobalObject, m_lexicalGlobalObject->setIteratorStructure(), inSet, IterationKind::Keys);
if (UNLIKELY(scope.exception()))
return SerializationReturnCode::ExistingExceptionError;
m_gcBuffer.appendWithCrashOnOverflow(inSet);
m_gcBuffer.appendWithCrashOnOverflow(iterator);
setIteratorStack.append(iterator);
inputObjectStack.append(inSet);
goto setDataStartVisitEntry;
}
setDataStartVisitEntry:
case SetDataStartVisitEntry: {
JSSetIterator* iterator = setIteratorStack.last();
JSValue key;
if (!iterator->next(m_lexicalGlobalObject, key)) {
setIteratorStack.removeLast();
JSObject* object = inputObjectStack.last();
ASSERT(jsDynamicCast<JSSet*>(object));
propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude));
object->methodTable()->getOwnPropertyNames(object, m_lexicalGlobalObject, propertyStack.last(), DontEnumPropertiesMode::Exclude);
if (UNLIKELY(scope.exception()))
return SerializationReturnCode::ExistingExceptionError;
write(NonSetPropertiesTag);
indexStack.append(0);
goto objectStartVisitMember;
}
inValue = key;
stateStack.append(SetDataEndVisitKey);
goto stateUnknown;
}
case SetDataEndVisitKey: {
goto setDataStartVisitEntry;
}
stateUnknown:
case StateUnknown: {
auto terminalCode = SerializationReturnCode::SuccessfullyCompleted;
if (dumpIfTerminal(inValue, terminalCode)) {
if (terminalCode != SerializationReturnCode::SuccessfullyCompleted)
return terminalCode;
break;
}
if (isArray(inValue))
goto arrayStartState;
if (isMap(inValue))
goto mapStartState;
if (isSet(inValue))
goto setStartState;
goto objectStartState;
}
}
if (stateStack.isEmpty())
break;
state = stateStack.last();
stateStack.removeLast();
}
if (m_failed)
return SerializationReturnCode::UnspecifiedError;
return SerializationReturnCode::SuccessfullyCompleted;
}