SerializationReturnCode CloneSerializer::serialize(JSValue in) { Vector<uint32_t, 16> indexStack; Vector<uint32_t, 16> lengthStack; Vector<PropertyNameArray, 16> propertyStack; Vector<JSObject*, 16> inputObjectStack; Vector<JSArray*, 16> inputArrayStack; Vector<WalkerState, 16> stateStack; WalkerState state = StateUnknown; JSValue inValue = in; unsigned tickCount = ticksUntilNextCheck(); while (1) { switch (state) { arrayStartState: case ArrayStartState: { ASSERT(isArray(inValue)); if (inputObjectStack.size() + inputArrayStack.size() > maximumFilterRecursion) return StackOverflowError; JSArray* inArray = asArray(inValue); unsigned length = inArray->length(); if (!startArray(inArray)) break; inputArrayStack.append(inArray); indexStack.append(0); lengthStack.append(length); // fallthrough } arrayStartVisitMember: case ArrayStartVisitMember: { if (!--tickCount) { if (didTimeOut()) return InterruptedExecutionError; tickCount = ticksUntilNextCheck(); } JSArray* array = inputArrayStack.last(); uint32_t index = indexStack.last(); if (index == lengthStack.last()) { endObject(); inputArrayStack.removeLast(); indexStack.removeLast(); lengthStack.removeLast(); break; } if (array->canGetIndex(index)) inValue = array->getIndex(index); else { bool hasIndex = false; inValue = getSparseIndex(array, index, hasIndex); if (!hasIndex) { indexStack.last()++; goto arrayStartVisitMember; } } write(index); if (dumpIfTerminal(inValue)) { indexStack.last()++; goto arrayStartVisitMember; } stateStack.append(ArrayEndVisitMember); goto stateUnknown; } case ArrayEndVisitMember: { indexStack.last()++; goto arrayStartVisitMember; } objectStartState: case ObjectStartState: { ASSERT(inValue.isObject()); if (inputObjectStack.size() + inputArrayStack.size() > maximumFilterRecursion) return StackOverflowError; JSObject* inObject = asObject(inValue); if (!startObject(inObject)) break; inputObjectStack.append(inObject); indexStack.append(0); propertyStack.append(PropertyNameArray(m_exec)); inObject->methodTable()->getOwnPropertyNames(inObject, m_exec, propertyStack.last(), ExcludeDontEnumProperties); // fallthrough } objectStartVisitMember: case ObjectStartVisitMember: { if (!--tickCount) { if (didTimeOut()) return InterruptedExecutionError; tickCount = ticksUntilNextCheck(); } 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 (shouldTerminate()) return ExistingExceptionError; if (!inValue) { // Property was removed during serialisation indexStack.last()++; goto objectStartVisitMember; } write(properties[index]); if (shouldTerminate()) return ExistingExceptionError; if (!dumpIfTerminal(inValue)) { stateStack.append(ObjectEndVisitMember); goto stateUnknown; } // fallthrough } case ObjectEndVisitMember: { if (shouldTerminate()) return ExistingExceptionError; indexStack.last()++; goto objectStartVisitMember; } stateUnknown: case StateUnknown: if (dumpIfTerminal(inValue)) break; if (isArray(inValue)) goto arrayStartState; goto objectStartState; } if (stateStack.isEmpty()) break; state = stateStack.last(); stateStack.removeLast(); if (!--tickCount) { if (didTimeOut()) return InterruptedExecutionError; tickCount = ticksUntilNextCheck(); } } if (m_failed) return UnspecifiedError; return SuccessfullyCompleted; }
EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec) { JSValue thisValue = exec->hostThisValue(); bool isRealArray = isJSArray(&exec->globalData(), thisValue); if (!isRealArray && !thisValue.inherits(&JSArray::info)) return throwVMTypeError(exec); JSArray* thisObj = asArray(thisValue); HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements; if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) { if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth) return throwVMError(exec, createStackOverflowError(exec)); } bool alreadyVisited = !arrayVisitedElements.add(thisObj).second; if (alreadyVisited) return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoiding infinite recursion. //FYWEBKITMOD begin: setting up StringJoiner (while integrating r148721 due to fix of bug 114779). But not using 'ConstructFromLiteral' optimization which we dont have in our webkit. unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); if (exec->hadException()) return JSValue::encode(jsUndefined()); //WebCore::String separator(",", WebCore::String::ConstructFromLiteral); //JSStringJoiner stringJoiner(separator, length); JSStringJoiner stringJoiner(",", length); //FYWEBKITMOD end unsigned totalSize = length ? length - 1 : 0; Vector<RefPtr<UString::Rep>, 256> strBuffer(length); for (unsigned k = 0; k < length; k++) { JSValue element; if (isRealArray && thisObj->canGetIndex(k)) element = thisObj->getIndex(k); else { element = thisObj->get(exec, k); //FYWEBKITMOD begin: added while integrating r148721 due to fix of bug 114779 if (exec->hadException()) return JSValue::encode(jsUndefined()); //FYWEBKITMOD end } //FYWEBKITMOD begin: removed (USE_FIX 1) old implementation which joined the strings manually and added usage of JSStringJoiner (while integrating r148721 due to fix of bug 114779) #define USE_FIX 1 //keep this defined!!! #ifdef USE_FIX if (element.isUndefinedOrNull()) stringJoiner.append(WebCore::String()); //FYWEBKITMOD: else stringJoiner.append(element.toString(exec).rep()); //FYWEBKITMOD: using toString() instead of new toWTFString(). Only difference is an optimization used in the toWTFString() case. if (exec->hadException()) return JSValue::encode(jsUndefined()); } arrayVisitedElements.remove(thisObj); return JSValue::encode(stringJoiner.build(exec)); #else if (element.isUndefinedOrNull()) continue; UString str = element.toString(exec); strBuffer[k] = str.rep(); totalSize += str.size(); if (!strBuffer.data()) { throwOutOfMemoryError(exec); } if (exec->hadException()) break; }