summaryrefslogtreecommitdiffstats
path: root/WebCore/bindings/js/SerializedScriptValue.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/bindings/js/SerializedScriptValue.cpp')
-rw-r--r--WebCore/bindings/js/SerializedScriptValue.cpp1424
1 files changed, 0 insertions, 1424 deletions
diff --git a/WebCore/bindings/js/SerializedScriptValue.cpp b/WebCore/bindings/js/SerializedScriptValue.cpp
deleted file mode 100644
index bfa2b75..0000000
--- a/WebCore/bindings/js/SerializedScriptValue.cpp
+++ /dev/null
@@ -1,1424 +0,0 @@
-/*
- * Copyright (C) 2009 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include "config.h"
-#include "SerializedScriptValue.h"
-
-#include "Blob.h"
-#include "File.h"
-#include "FileList.h"
-#include "ImageData.h"
-#include "JSBlob.h"
-#include "JSDOMGlobalObject.h"
-#include "JSFile.h"
-#include "JSFileList.h"
-#include "JSImageData.h"
-#include "SharedBuffer.h"
-#include <limits>
-#include <JavaScriptCore/APICast.h>
-#include <runtime/DateInstance.h>
-#include <runtime/Error.h>
-#include <runtime/ExceptionHelpers.h>
-#include <runtime/JSLock.h>
-#include <runtime/PropertyNameArray.h>
-#include <runtime/RegExp.h>
-#include <runtime/RegExpObject.h>
-#include <wtf/ByteArray.h>
-#include <wtf/HashTraits.h>
-#include <wtf/Vector.h>
-
-using namespace JSC;
-using namespace std;
-
-#if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN)
-#define ASSUME_LITTLE_ENDIAN 0
-#else
-#define ASSUME_LITTLE_ENDIAN 1
-#endif
-
-namespace WebCore {
-
-static const unsigned maximumFilterRecursion = 40000;
-
-enum WalkerState { StateUnknown, ArrayStartState, ArrayStartVisitMember, ArrayEndVisitMember,
- ObjectStartState, ObjectStartVisitMember, ObjectEndVisitMember };
-
-// These can't be reordered, and any new types must be added to the end of the list
-enum SerializationTag {
- ArrayTag = 1,
- ObjectTag = 2,
- UndefinedTag = 3,
- NullTag = 4,
- IntTag = 5,
- ZeroTag = 6,
- OneTag = 7,
- FalseTag = 8,
- TrueTag = 9,
- DoubleTag = 10,
- DateTag = 11,
- FileTag = 12,
- FileListTag = 13,
- ImageDataTag = 14,
- BlobTag = 15,
- StringTag = 16,
- EmptyStringTag = 17,
- RegExpTag = 18,
- ObjectReferenceTag = 19,
- ErrorTag = 255
-};
-
-/* CurrentVersion tracks the serialization version so that persistant stores
- * are able to correctly bail out in the case of encountering newer formats.
- *
- * Initial version was 1.
- * Version 2. added the ObjectReferenceTag and support for serialization of cyclic graphs.
- */
-static const unsigned int CurrentVersion = 2;
-static const unsigned int TerminatorTag = 0xFFFFFFFF;
-static const unsigned int StringPoolTag = 0xFFFFFFFE;
-
-/*
- * Object serialization is performed according to the following grammar, all tags
- * are recorded as a single uint8_t.
- *
- * IndexType (used for the object pool and StringData's constant pool) is the
- * minimum sized unsigned integer type required to represent the maximum index
- * in the constant pool.
- *
- * SerializedValue :- <CurrentVersion:uint32_t> Value
- * Value :- Array | Object | Terminal
- *
- * Array :-
- * ArrayTag <length:uint32_t>(<index:uint32_t><value:Value>)* TerminatorTag
- *
- * Object :-
- * ObjectTag (<name:StringData><value:Value>)* TerminatorTag
- *
- * Terminal :-
- * UndefinedTag
- * | NullTag
- * | IntTag <value:int32_t>
- * | ZeroTag
- * | OneTag
- * | DoubleTag <value:double>
- * | DateTag <value:double>
- * | String
- * | EmptyStringTag
- * | File
- * | FileList
- * | ImageData
- * | Blob
- * | ObjectReferenceTag <opIndex:IndexType>
- *
- * String :-
- * EmptyStringTag
- * StringTag StringData
- *
- * StringData :-
- * StringPoolTag <cpIndex:IndexType>
- * (not (TerminatorTag | StringPoolTag))<length:uint32_t><characters:UChar{length}> // Added to constant pool when seen, string length 0xFFFFFFFF is disallowed
- *
- * File :-
- * FileTag FileData
- *
- * FileData :-
- * <path:StringData> <url:StringData> <type:StringData>
- *
- * FileList :-
- * FileListTag <length:uint32_t>(<file:FileData>){length}
- *
- * ImageData :-
- * ImageDataTag <width:uint32_t><height:uint32_t><length:uint32_t><data:uint8_t{length}>
- *
- * Blob :-
- * BlobTag <url:StringData><type:StringData><size:long long>
- *
- * RegExp :-
- * RegExpTag <pattern:StringData><flags:StringData>
- */
-
-class CloneBase {
-protected:
- CloneBase(ExecState* exec)
- : m_exec(exec)
- , m_failed(false)
- , m_timeoutChecker(exec->globalData().timeoutChecker)
- {
- }
-
- bool shouldTerminate()
- {
- return m_exec->hadException();
- }
-
- unsigned ticksUntilNextCheck()
- {
- return m_timeoutChecker.ticksUntilNextCheck();
- }
-
- bool didTimeOut()
- {
- return m_timeoutChecker.didTimeOut(m_exec);
- }
-
- void throwStackOverflow()
- {
- throwError(m_exec, createStackOverflowError(m_exec));
- }
-
- void throwInterruptedException()
- {
- throwError(m_exec, createInterruptedExecutionException(&m_exec->globalData()));
- }
-
- void fail()
- {
- ASSERT_NOT_REACHED();
- m_failed = true;
- }
-
- ExecState* m_exec;
- bool m_failed;
- TimeoutChecker m_timeoutChecker;
- MarkedArgumentBuffer m_gcBuffer;
-};
-
-class CloneSerializer : CloneBase {
-public:
- static bool serialize(ExecState* exec, JSValue value, Vector<uint8_t>& out)
- {
- CloneSerializer serializer(exec, out);
- return serializer.serialize(value);
- }
-
- static bool serialize(String s, Vector<uint8_t>& out)
- {
- writeLittleEndian(out, CurrentVersion);
- if (s.isEmpty()) {
- writeLittleEndian<uint8_t>(out, EmptyStringTag);
- return true;
- }
- writeLittleEndian<uint8_t>(out, StringTag);
- writeLittleEndian(out, s.length());
- return writeLittleEndian(out, s.impl()->characters(), s.length());
- }
-
-private:
- CloneSerializer(ExecState* exec, Vector<uint8_t>& out)
- : CloneBase(exec)
- , m_buffer(out)
- , m_emptyIdentifier(exec, UString("", 0))
- {
- write(CurrentVersion);
- }
-
- bool serialize(JSValue in);
-
- bool isArray(JSValue value)
- {
- if (!value.isObject())
- return false;
- JSObject* object = asObject(value);
- return isJSArray(&m_exec->globalData(), object) || object->inherits(&JSArray::info);
- }
-
- bool startObjectInternal(JSObject* object)
- {
- // Record object for graph reconstruction
- pair<ObjectPool::iterator, bool> iter = m_objectPool.add(object, m_objectPool.size());
-
- // Handle duplicate references
- if (!iter.second) {
- write(ObjectReferenceTag);
- ASSERT(static_cast<int32_t>(iter.first->second) < m_objectPool.size());
- writeObjectIndex(iter.first->second);
- return false;
- }
-
- m_gcBuffer.append(object);
- return true;
- }
-
- bool startObject(JSObject* object)
- {
- if (!startObjectInternal(object))
- return false;
- write(ObjectTag);
- return true;
- }
-
- bool startArray(JSArray* array)
- {
- if (!startObjectInternal(array))
- return false;
-
- unsigned length = array->length();
- write(ArrayTag);
- write(length);
- return true;
- }
-
- void endObject()
- {
- write(TerminatorTag);
- }
-
- JSValue getSparseIndex(JSArray* array, unsigned propertyName, bool& hasIndex)
- {
- PropertySlot slot(array);
- if (isJSArray(&m_exec->globalData(), array)) {
- if (array->JSArray::getOwnPropertySlot(m_exec, propertyName, slot)) {
- hasIndex = true;
- return slot.getValue(m_exec, propertyName);
- }
- } else if (array->getOwnPropertySlot(m_exec, propertyName, slot)) {
- hasIndex = true;
- return slot.getValue(m_exec, propertyName);
- }
- hasIndex = false;
- return jsNull();
- }
-
- JSValue getProperty(JSObject* object, const Identifier& propertyName)
- {
- PropertySlot slot(object);
- if (object->getOwnPropertySlot(m_exec, propertyName, slot))
- return slot.getValue(m_exec, propertyName);
- return JSValue();
- }
-
- void dumpImmediate(JSValue value)
- {
- if (value.isNull())
- write(NullTag);
- else if (value.isUndefined())
- write(UndefinedTag);
- else if (value.isNumber()) {
- if (value.isInt32()) {
- if (!value.asInt32())
- write(ZeroTag);
- else if (value.asInt32() == 1)
- write(OneTag);
- else {
- write(IntTag);
- write(static_cast<uint32_t>(value.asInt32()));
- }
- } else {
- write(DoubleTag);
- write(value.asDouble());
- }
- } else if (value.isBoolean()) {
- if (value.isTrue())
- write(TrueTag);
- else
- write(FalseTag);
- }
- }
-
- void dumpString(UString str)
- {
- if (str.isEmpty())
- write(EmptyStringTag);
- else {
- write(StringTag);
- write(str);
- }
- }
-
- bool dumpIfTerminal(JSValue value)
- {
- if (!value.isCell()) {
- dumpImmediate(value);
- return true;
- }
-
- if (value.isString()) {
- UString str = asString(value)->value(m_exec);
- dumpString(str);
- return true;
- }
-
- if (value.isNumber()) {
- write(DoubleTag);
- write(value.uncheckedGetNumber());
- return true;
- }
-
- if (value.isObject() && asObject(value)->inherits(&DateInstance::info)) {
- write(DateTag);
- write(asDateInstance(value)->internalNumber());
- return true;
- }
-
- if (isArray(value))
- return false;
-
- if (value.isObject()) {
- JSObject* obj = asObject(value);
- if (obj->inherits(&JSFile::s_info)) {
- write(FileTag);
- write(toFile(obj));
- return true;
- }
- if (obj->inherits(&JSFileList::s_info)) {
- FileList* list = toFileList(obj);
- write(FileListTag);
- unsigned length = list->length();
- write(length);
- for (unsigned i = 0; i < length; i++)
- write(list->item(i));
- return true;
- }
- if (obj->inherits(&JSBlob::s_info)) {
- write(BlobTag);
- Blob* blob = toBlob(obj);
- write(blob->url());
- write(blob->type());
- write(blob->size());
- return true;
- }
- if (obj->inherits(&JSImageData::s_info)) {
- ImageData* data = toImageData(obj);
- write(ImageDataTag);
- write(data->width());
- write(data->height());
- write(data->data()->length());
- write(data->data()->data()->data(), data->data()->length());
- return true;
- }
- if (obj->inherits(&RegExpObject::info)) {
- RegExpObject* regExp = asRegExpObject(obj);
- char flags[3];
- int flagCount = 0;
- if (regExp->regExp()->global())
- flags[flagCount++] = 'g';
- if (regExp->regExp()->ignoreCase())
- flags[flagCount++] = 'i';
- if (regExp->regExp()->multiline())
- flags[flagCount++] = 'm';
- write(RegExpTag);
- write(regExp->regExp()->pattern());
- write(UString(flags, flagCount));
- return true;
- }
-
- CallData unusedData;
- if (getCallData(value, unusedData) == CallTypeNone)
- return false;
- }
- // Any other types are expected to serialize as null.
- write(NullTag);
- return true;
- }
-
- void write(SerializationTag tag)
- {
- writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag));
- }
-
- void write(uint8_t c)
- {
- writeLittleEndian(m_buffer, c);
- }
-
-#if ASSUME_LITTLE_ENDIAN
- template <typename T> static void writeLittleEndian(Vector<uint8_t>& buffer, T value)
- {
- if (sizeof(T) == 1)
- buffer.append(value);
- else
- buffer.append(reinterpret_cast<uint8_t*>(&value), sizeof(value));
- }
-#else
- template <typename T> static void writeLittleEndian(Vector<uint8_t>& buffer, T value)
- {
- for (unsigned i = 0; i < sizeof(T); i++) {
- buffer.append(value & 0xFF);
- value >>= 8;
- }
- }
-#endif
-
- template <typename T> static bool writeLittleEndian(Vector<uint8_t>& buffer, const T* values, uint32_t length)
- {
- if (length > numeric_limits<uint32_t>::max() / sizeof(T))
- return false;
-
-#if ASSUME_LITTLE_ENDIAN
- buffer.append(reinterpret_cast<const uint8_t*>(values), length * sizeof(T));
-#else
- for (unsigned i = 0; i < length; i++) {
- T value = values[i];
- for (unsigned j = 0; j < sizeof(T); j++) {
- buffer.append(static_cast<uint8_t>(value & 0xFF));
- value >>= 8;
- }
- }
-#endif
- return true;
- }
-
- void write(uint32_t i)
- {
- writeLittleEndian(m_buffer, i);
- }
-
- void write(double d)
- {
- union {
- double d;
- int64_t i;
- } u;
- u.d = d;
- writeLittleEndian(m_buffer, u.i);
- }
-
- void write(unsigned long long i)
- {
- writeLittleEndian(m_buffer, i);
- }
-
- void write(uint16_t ch)
- {
- writeLittleEndian(m_buffer, ch);
- }
-
- void writeStringIndex(unsigned i)
- {
- writeConstantPoolIndex(m_constantPool, i);
- }
-
- void writeObjectIndex(unsigned i)
- {
- writeConstantPoolIndex(m_objectPool, i);
- }
-
- template <class T> void writeConstantPoolIndex(const T& constantPool, unsigned i)
- {
- ASSERT(static_cast<int32_t>(i) < constantPool.size());
- if (constantPool.size() <= 0xFF)
- write(static_cast<uint8_t>(i));
- else if (constantPool.size() <= 0xFFFF)
- write(static_cast<uint16_t>(i));
- else
- write(static_cast<uint32_t>(i));
- }
-
- void write(const Identifier& ident)
- {
- UString str = ident.ustring();
- pair<StringConstantPool::iterator, bool> iter = m_constantPool.add(str.impl(), m_constantPool.size());
- if (!iter.second) {
- write(StringPoolTag);
- writeStringIndex(iter.first->second);
- return;
- }
-
- // This condition is unlikely to happen as they would imply an ~8gb
- // string but we should guard against it anyway
- if (str.length() >= StringPoolTag) {
- fail();
- return;
- }
-
- // Guard against overflow
- if (str.length() > (numeric_limits<uint32_t>::max() - sizeof(uint32_t)) / sizeof(UChar)) {
- fail();
- return;
- }
-
- writeLittleEndian<uint32_t>(m_buffer, str.length());
- if (!writeLittleEndian<uint16_t>(m_buffer, reinterpret_cast<const uint16_t*>(str.characters()), str.length()))
- fail();
- }
-
- void write(const UString& str)
- {
- if (str.isNull())
- write(m_emptyIdentifier);
- else
- write(Identifier(m_exec, str));
- }
-
- void write(const String& str)
- {
- if (str.isEmpty())
- write(m_emptyIdentifier);
- else
- write(Identifier(m_exec, str.impl()));
- }
-
- void write(const File* file)
- {
- write(file->path());
- write(file->url());
- write(file->type());
- }
-
- void write(const uint8_t* data, unsigned length)
- {
- m_buffer.append(data, length);
- }
-
- Vector<uint8_t>& m_buffer;
- typedef HashMap<JSObject*, uint32_t> ObjectPool;
- ObjectPool m_objectPool;
- typedef HashMap<RefPtr<StringImpl>, uint32_t, IdentifierRepHash> StringConstantPool;
- StringConstantPool m_constantPool;
- Identifier m_emptyIdentifier;
-};
-
-bool 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) {
- throwStackOverflow();
- return false;
- }
-
- 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()) {
- throwInterruptedException();
- return false;
- }
- 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) {
- throwStackOverflow();
- return false;
- }
- JSObject* inObject = asObject(inValue);
- if (!startObject(inObject))
- break;
- inputObjectStack.append(inObject);
- indexStack.append(0);
- propertyStack.append(PropertyNameArray(m_exec));
- inObject->getOwnPropertyNames(m_exec, propertyStack.last());
- // fallthrough
- }
- objectStartVisitMember:
- case ObjectStartVisitMember: {
- if (!--tickCount) {
- if (didTimeOut()) {
- throwInterruptedException();
- return false;
- }
- 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 false;
-
- if (!inValue) {
- // Property was removed during serialisation
- indexStack.last()++;
- goto objectStartVisitMember;
- }
- write(properties[index]);
-
- if (shouldTerminate())
- return false;
-
- if (!dumpIfTerminal(inValue)) {
- stateStack.append(ObjectEndVisitMember);
- goto stateUnknown;
- }
- // fallthrough
- }
- case ObjectEndVisitMember: {
- if (shouldTerminate())
- return false;
-
- 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()) {
- throwInterruptedException();
- return false;
- }
- tickCount = ticksUntilNextCheck();
- }
- }
- if (m_failed)
- return false;
-
- return true;
-}
-
-class CloneDeserializer : CloneBase {
-public:
- static String deserializeString(const Vector<uint8_t>& buffer)
- {
- const uint8_t* ptr = buffer.begin();
- const uint8_t* end = buffer.end();
- uint32_t version;
- if (!readLittleEndian(ptr, end, version) || version > CurrentVersion)
- return String();
- uint8_t tag;
- if (!readLittleEndian(ptr, end, tag) || tag != StringTag)
- return String();
- uint32_t length;
- if (!readLittleEndian(ptr, end, length) || length >= StringPoolTag)
- return String();
- UString str;
- if (!readString(ptr, end, str, length))
- return String();
- return String(str.impl());
- }
-
- static JSValue deserialize(ExecState* exec, JSGlobalObject* globalObject, const Vector<uint8_t>& buffer)
- {
- if (!buffer.size())
- return jsNull();
- CloneDeserializer deserializer(exec, globalObject, buffer);
- if (!deserializer.isValid()) {
- deserializer.throwValidationError();
- return JSValue();
- }
- return deserializer.deserialize();
- }
-
-private:
- struct CachedString {
- CachedString(const UString& string)
- : m_string(string)
- {
- }
-
- JSValue jsString(ExecState* exec)
- {
- if (!m_jsString)
- m_jsString = JSC::jsString(exec, m_string);
- return m_jsString;
- }
- const UString& ustring() { return m_string; }
-
- private:
- UString m_string;
- JSValue m_jsString;
- };
-
- struct CachedStringRef {
- CachedStringRef()
- : m_base(0)
- , m_index(0)
- {
- }
- CachedStringRef(Vector<CachedString>* base, size_t index)
- : m_base(base)
- , m_index(index)
- {
- }
-
- CachedString* operator->() { ASSERT(m_base); return &m_base->at(m_index); }
-
- private:
- Vector<CachedString>* m_base;
- size_t m_index;
- };
-
- CloneDeserializer(ExecState* exec, JSGlobalObject* globalObject, const Vector<uint8_t>& buffer)
- : CloneBase(exec)
- , m_globalObject(globalObject)
- , m_isDOMGlobalObject(globalObject->inherits(&JSDOMGlobalObject::s_info))
- , m_ptr(buffer.data())
- , m_end(buffer.data() + buffer.size())
- , m_version(0xFFFFFFFF)
- {
- if (!read(m_version))
- m_version = 0xFFFFFFFF;
- }
-
- JSValue deserialize();
-
- void throwValidationError()
- {
- throwError(m_exec, createTypeError(m_exec, "Unable to deserialize data."));
- }
-
- bool isValid() const { return m_version <= CurrentVersion; }
-
- template <typename T> bool readLittleEndian(T& value)
- {
- if (m_failed || !readLittleEndian(m_ptr, m_end, value)) {
- fail();
- return false;
- }
- return true;
- }
-#if ASSUME_LITTLE_ENDIAN
- template <typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value)
- {
- if (ptr > end - sizeof(value))
- return false;
-
- if (sizeof(T) == 1)
- value = *ptr++;
- else {
-#if CPU(ARMV5_OR_LOWER)
- // To protect misaligned memory access.
- memcpy(&value, ptr, sizeof(T));
-#else
- value = *reinterpret_cast<const T*>(ptr);
-#endif
- ptr += sizeof(T);
- }
- return true;
- }
-#else
- template <typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value)
- {
- if (ptr > end - sizeof(value))
- return false;
-
- if (sizeof(T) == 1)
- value = *ptr++;
- else {
- value = 0;
- for (unsigned i = 0; i < sizeof(T); i++)
- value += ((T)*ptr++) << (i * 8);
- }
- return true;
- }
-#endif
-
- bool read(uint32_t& i)
- {
- return readLittleEndian(i);
- }
-
- bool read(int32_t& i)
- {
- return readLittleEndian(*reinterpret_cast<uint32_t*>(&i));
- }
-
- bool read(uint16_t& i)
- {
- return readLittleEndian(i);
- }
-
- bool read(uint8_t& i)
- {
- return readLittleEndian(i);
- }
-
- bool read(double& d)
- {
- union {
- double d;
- uint64_t i64;
- } u;
- if (!readLittleEndian(u.i64))
- return false;
- d = u.d;
- return true;
- }
-
- bool read(unsigned long long& i)
- {
- return readLittleEndian(i);
- }
-
- bool readStringIndex(uint32_t& i)
- {
- return readConstantPoolIndex(m_constantPool, i);
- }
-
- template <class T> bool readConstantPoolIndex(const T& constantPool, uint32_t& i)
- {
- if (constantPool.size() <= 0xFF) {
- uint8_t i8;
- if (!read(i8))
- return false;
- i = i8;
- return true;
- }
- if (constantPool.size() <= 0xFFFF) {
- uint16_t i16;
- if (!read(i16))
- return false;
- i = i16;
- return true;
- }
- return read(i);
- }
-
- static bool readString(const uint8_t*& ptr, const uint8_t* end, UString& str, unsigned length)
- {
- if (length >= numeric_limits<int32_t>::max() / sizeof(UChar))
- return false;
-
- unsigned size = length * sizeof(UChar);
- if ((end - ptr) < static_cast<int>(size))
- return false;
-
-#if ASSUME_LITTLE_ENDIAN
-#if CPU(ARMV5_OR_LOWER)
- // To protect misaligned memory access.
- Vector<UChar> alignedBuffer(length);
- memcpy(alignedBuffer.data(), ptr, length * sizeof(UChar));
- str = UString::adopt(alignedBuffer);
-#else
- str = UString(reinterpret_cast<const UChar*>(ptr), length);
-#endif
- ptr += length * sizeof(UChar);
-#else
- Vector<UChar> buffer;
- buffer.reserveCapacity(length);
- for (unsigned i = 0; i < length; i++) {
- uint16_t ch;
- readLittleEndian(ptr, end, ch);
- buffer.append(ch);
- }
- str = UString::adopt(buffer);
-#endif
- return true;
- }
-
- bool readStringData(CachedStringRef& cachedString)
- {
- bool scratch;
- return readStringData(cachedString, scratch);
- }
-
- bool readStringData(CachedStringRef& cachedString, bool& wasTerminator)
- {
- if (m_failed)
- return false;
- uint32_t length = 0;
- if (!read(length))
- return false;
- if (length == TerminatorTag) {
- wasTerminator = true;
- return false;
- }
- if (length == StringPoolTag) {
- unsigned index = 0;
- if (!readStringIndex(index)) {
- fail();
- return false;
- }
- if (index >= m_constantPool.size()) {
- fail();
- return false;
- }
- cachedString = CachedStringRef(&m_constantPool, index);
- return true;
- }
- UString str;
- if (!readString(m_ptr, m_end, str, length)) {
- fail();
- return false;
- }
- m_constantPool.append(str);
- cachedString = CachedStringRef(&m_constantPool, m_constantPool.size() - 1);
- return true;
- }
-
- SerializationTag readTag()
- {
- if (m_ptr >= m_end)
- return ErrorTag;
- return static_cast<SerializationTag>(*m_ptr++);
- }
-
- void putProperty(JSArray* array, unsigned index, JSValue value)
- {
- if (array->canSetIndex(index))
- array->setIndex(index, value);
- else
- array->put(m_exec, index, value);
- }
-
- void putProperty(JSObject* object, const Identifier& property, JSValue value)
- {
- object->putDirect(property, value);
- }
-
- bool readFile(RefPtr<File>& file)
- {
- CachedStringRef path;
- if (!readStringData(path))
- return 0;
- CachedStringRef url;
- if (!readStringData(url))
- return 0;
- CachedStringRef type;
- if (!readStringData(type))
- return 0;
- if (m_isDOMGlobalObject)
- file = File::create(String(path->ustring().impl()), KURL(KURL(), String(url->ustring().impl())), String(type->ustring().impl()));
- return true;
- }
-
- JSValue readTerminal()
- {
- SerializationTag tag = readTag();
- switch (tag) {
- case UndefinedTag:
- return jsUndefined();
- case NullTag:
- return jsNull();
- case IntTag: {
- int32_t i;
- if (!read(i))
- return JSValue();
- return jsNumber(i);
- }
- case ZeroTag:
- return jsNumber(0);
- case OneTag:
- return jsNumber(1);
- case FalseTag:
- return jsBoolean(false);
- case TrueTag:
- return jsBoolean(true);
- case DoubleTag: {
- double d;
- if (!read(d))
- return JSValue();
- return jsNumber(d);
- }
- case DateTag: {
- double d;
- if (!read(d))
- return JSValue();
- return new (m_exec) DateInstance(m_exec, m_globalObject->dateStructure(), d);
- }
- case FileTag: {
- RefPtr<File> file;
- if (!readFile(file))
- return JSValue();
- if (!m_isDOMGlobalObject)
- return jsNull();
- return toJS(m_exec, static_cast<JSDOMGlobalObject*>(m_globalObject), file.get());
- }
- case FileListTag: {
- unsigned length = 0;
- if (!read(length))
- return JSValue();
- RefPtr<FileList> result = FileList::create();
- for (unsigned i = 0; i < length; i++) {
- RefPtr<File> file;
- if (!readFile(file))
- return JSValue();
- if (m_isDOMGlobalObject)
- result->append(file.get());
- }
- if (!m_isDOMGlobalObject)
- return jsNull();
- return toJS(m_exec, static_cast<JSDOMGlobalObject*>(m_globalObject), result.get());
- }
- case ImageDataTag: {
- uint32_t width;
- if (!read(width))
- return JSValue();
- uint32_t height;
- if (!read(height))
- return JSValue();
- uint32_t length;
- if (!read(length))
- return JSValue();
- if (m_end < ((uint8_t*)0) + length || m_ptr > m_end - length) {
- fail();
- return JSValue();
- }
- if (!m_isDOMGlobalObject) {
- m_ptr += length;
- return jsNull();
- }
- RefPtr<ImageData> result = ImageData::create(width, height);
- memcpy(result->data()->data()->data(), m_ptr, length);
- m_ptr += length;
- return toJS(m_exec, static_cast<JSDOMGlobalObject*>(m_globalObject), result.get());
- }
- case BlobTag: {
- CachedStringRef url;
- if (!readStringData(url))
- return JSValue();
- CachedStringRef type;
- if (!readStringData(type))
- return JSValue();
- unsigned long long size = 0;
- if (!read(size))
- return JSValue();
- if (!m_isDOMGlobalObject)
- return jsNull();
- return toJS(m_exec, static_cast<JSDOMGlobalObject*>(m_globalObject), Blob::create(KURL(KURL(), url->ustring().impl()), String(type->ustring().impl()), size));
- }
- case StringTag: {
- CachedStringRef cachedString;
- if (!readStringData(cachedString))
- return JSValue();
- return cachedString->jsString(m_exec);
- }
- case EmptyStringTag:
- return jsEmptyString(&m_exec->globalData());
- case RegExpTag: {
- CachedStringRef pattern;
- if (!readStringData(pattern))
- return JSValue();
- CachedStringRef flags;
- if (!readStringData(flags))
- return JSValue();
- RefPtr<RegExp> regExp = RegExp::create(&m_exec->globalData(), pattern->ustring(), flags->ustring());
- return new (m_exec) RegExpObject(m_exec->lexicalGlobalObject(), m_globalObject->regExpStructure(), regExp);
- }
- case ObjectReferenceTag: {
- unsigned index = 0;
- if (!readConstantPoolIndex(m_gcBuffer, index)) {
- fail();
- return JSValue();
- }
- return m_gcBuffer.at(index);
- }
- default:
- m_ptr--; // Push the tag back
- return JSValue();
- }
- }
-
- JSGlobalObject* m_globalObject;
- bool m_isDOMGlobalObject;
- const uint8_t* m_ptr;
- const uint8_t* m_end;
- unsigned m_version;
- Vector<CachedString> m_constantPool;
-};
-
-JSValue CloneDeserializer::deserialize()
-{
- Vector<uint32_t, 16> indexStack;
- Vector<Identifier, 16> propertyNameStack;
- Vector<JSObject*, 16> outputObjectStack;
- Vector<JSArray*, 16> outputArrayStack;
- Vector<WalkerState, 16> stateStack;
- WalkerState state = StateUnknown;
- JSValue outValue;
-
- unsigned tickCount = ticksUntilNextCheck();
- while (1) {
- switch (state) {
- arrayStartState:
- case ArrayStartState: {
- uint32_t length;
- if (!read(length)) {
- fail();
- goto error;
- }
- JSArray* outArray = constructEmptyArray(m_exec, m_globalObject);
- outArray->setLength(length);
- m_gcBuffer.append(outArray);
- outputArrayStack.append(outArray);
- // fallthrough
- }
- arrayStartVisitMember:
- case ArrayStartVisitMember: {
- if (!--tickCount) {
- if (didTimeOut()) {
- throwInterruptedException();
- return JSValue();
- }
- tickCount = ticksUntilNextCheck();
- }
-
- uint32_t index;
- if (!read(index)) {
- fail();
- goto error;
- }
- if (index == TerminatorTag) {
- JSArray* outArray = outputArrayStack.last();
- outValue = outArray;
- outputArrayStack.removeLast();
- break;
- }
-
- if (JSValue terminal = readTerminal()) {
- putProperty(outputArrayStack.last(), index, terminal);
- goto arrayStartVisitMember;
- }
- if (m_failed)
- goto error;
- indexStack.append(index);
- stateStack.append(ArrayEndVisitMember);
- goto stateUnknown;
- }
- case ArrayEndVisitMember: {
- JSArray* outArray = outputArrayStack.last();
- putProperty(outArray, indexStack.last(), outValue);
- indexStack.removeLast();
- goto arrayStartVisitMember;
- }
- objectStartState:
- case ObjectStartState: {
- if (outputObjectStack.size() + outputArrayStack.size() > maximumFilterRecursion) {
- throwStackOverflow();
- return JSValue();
- }
- JSObject* outObject = constructEmptyObject(m_exec, m_globalObject);
- m_gcBuffer.append(outObject);
- outputObjectStack.append(outObject);
- // fallthrough
- }
- objectStartVisitMember:
- case ObjectStartVisitMember: {
- if (!--tickCount) {
- if (didTimeOut()) {
- throwInterruptedException();
- return JSValue();
- }
- tickCount = ticksUntilNextCheck();
- }
-
- CachedStringRef cachedString;
- bool wasTerminator = false;
- if (!readStringData(cachedString, wasTerminator)) {
- if (!wasTerminator)
- goto error;
- JSObject* outObject = outputObjectStack.last();
- outValue = outObject;
- outputObjectStack.removeLast();
- break;
- }
-
- if (JSValue terminal = readTerminal()) {
- putProperty(outputObjectStack.last(), Identifier(m_exec, cachedString->ustring()), terminal);
- goto objectStartVisitMember;
- }
- stateStack.append(ObjectEndVisitMember);
- propertyNameStack.append(Identifier(m_exec, cachedString->ustring()));
- goto stateUnknown;
- }
- case ObjectEndVisitMember: {
- putProperty(outputObjectStack.last(), propertyNameStack.last(), outValue);
- propertyNameStack.removeLast();
- goto objectStartVisitMember;
- }
- stateUnknown:
- case StateUnknown:
- if (JSValue terminal = readTerminal()) {
- outValue = terminal;
- break;
- }
- SerializationTag tag = readTag();
- if (tag == ArrayTag)
- goto arrayStartState;
- if (tag == ObjectTag)
- goto objectStartState;
- goto error;
- }
- if (stateStack.isEmpty())
- break;
-
- state = stateStack.last();
- stateStack.removeLast();
-
- if (!--tickCount) {
- if (didTimeOut()) {
- throwInterruptedException();
- return JSValue();
- }
- tickCount = ticksUntilNextCheck();
- }
- }
- ASSERT(outValue);
- ASSERT(!m_failed);
- return outValue;
-error:
- fail();
- throwValidationError();
- return JSValue();
-}
-
-
-
-SerializedScriptValue::~SerializedScriptValue()
-{
-}
-
-SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>& buffer)
-{
- m_data.swap(buffer);
-}
-
-PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(ExecState* exec, JSValue value)
-{
- Vector<uint8_t> buffer;
- if (!CloneSerializer::serialize(exec, value, buffer))
- return 0;
- return adoptRef(new SerializedScriptValue(buffer));
-}
-
-PassRefPtr<SerializedScriptValue> SerializedScriptValue::create()
-{
- Vector<uint8_t> buffer;
- return adoptRef(new SerializedScriptValue(buffer));
-}
-
-PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(String string)
-{
- Vector<uint8_t> buffer;
- if (!CloneSerializer::serialize(string, buffer))
- return 0;
- return adoptRef(new SerializedScriptValue(buffer));
-}
-
-PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(JSContextRef originContext, JSValueRef apiValue, JSValueRef* exception)
-{
- JSLock lock(SilenceAssertionsOnly);
- ExecState* exec = toJS(originContext);
- JSValue value = toJS(exec, apiValue);
- PassRefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::create(exec, value);
- if (exec->hadException()) {
- if (exception)
- *exception = toRef(exec, exec->exception());
- exec->clearException();
- return 0;
- }
- ASSERT(serializedValue);
- return serializedValue;
-}
-
-String SerializedScriptValue::toString()
-{
- return CloneDeserializer::deserializeString(m_data);
-}
-
-JSValue SerializedScriptValue::deserialize(ExecState* exec, JSGlobalObject* globalObject)
-{
- return CloneDeserializer::deserialize(exec, globalObject, m_data);
-}
-
-JSValueRef SerializedScriptValue::deserialize(JSContextRef destinationContext, JSValueRef* exception)
-{
- JSLock lock(SilenceAssertionsOnly);
- ExecState* exec = toJS(destinationContext);
- JSValue value = deserialize(exec, exec->lexicalGlobalObject());
- if (exec->hadException()) {
- if (exception)
- *exception = toRef(exec, exec->exception());
- exec->clearException();
- return 0;
- }
- ASSERT(value);
- return toRef(exec, value);
-}
-
-SerializedScriptValue* SerializedScriptValue::nullValue()
-{
- DEFINE_STATIC_LOCAL(RefPtr<SerializedScriptValue>, emptyValue, (SerializedScriptValue::create()));
- return emptyValue.get();
-}
-
-}