SerializedScriptValue.cpp   [plain text]


/*
 * Copyright (C) 2010 Google 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:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * 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.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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 "SharedBuffer.h"

#include <v8.h>
#include <wtf/Assertions.h>
#include <wtf/RefCounted.h>
#include <wtf/Vector.h>

// FIXME:
// - catch V8 exceptions
// - be ready to get empty handles
// - consider crashing in debug mode on deserialization errors

namespace WebCore {

namespace {

typedef UChar BufferValueType;

// Serialization format is a sequence of (tag, optional data)
// pairs. Tag always takes exactly one byte.
enum SerializationTag {
    InvalidTag = '!',
    PaddingTag = '\0',
    UndefinedTag = '_',
    NullTag = '0',
    TrueTag = 'T',
    FalseTag = 'F',
    StringTag = 'S',
    Int32Tag = 'I',
    NumberTag = 'N',
    ObjectTag = '{',
    ArrayTag = '[',
};

// Helpers to do verbose handle casts.

template <typename T, typename U>
static v8::Handle<T> handleCast(v8::Handle<U> handle) { return v8::Handle<T>::Cast(handle); }

template <typename T, typename U>
static v8::Local<T> handleCast(v8::Local<U> handle) { return v8::Local<T>::Cast(handle); }

static bool shouldCheckForCycles(int depth)
{
    ASSERT(depth >= 0);
    // Since we are not required to spot the cycle as soon as it
    // happens we can check for cycles only when the current depth
    // is a power of two.
    return !(depth & (depth - 1));
}

static const int maxDepth = 20000;

// VarInt encoding constants.
static const int varIntShift = 7;
static const int varIntMask = (1 << varIntShift) - 1;

// ZigZag encoding helps VarInt encoding stay small for negative
// numbers with small absolute values.
class ZigZag {
public:
    static uint32_t encode(uint32_t value)
    {
        if (value & (1U << 31))
            value = ((~value) << 1) + 1;
        else
            value <<= 1;
        return value;
    }

    static uint32_t decode(uint32_t value)
    {
        if (value & 1)
            value = ~(value >> 1);
        else
            value >>= 1;
        return value;
    }

private:
    ZigZag();
};

// Writer is responsible for serializing primitive types and storing
// information used to reconstruct composite types.
class Writer : Noncopyable {
public:
    Writer() : m_position(0)
    {
    }

    // Write functions for primitive types.

    void writeUndefined() { append(UndefinedTag); }

    void writeNull() { append(NullTag); }

    void writeTrue() { append(TrueTag); }

    void writeFalse() { append(FalseTag); }

    void writeString(const char* data, int length)
    {
        append(StringTag);
        doWriteUint32(static_cast<uint32_t>(length));
        append(data, length);
    }

    void writeInt32(int32_t value)
    {
        append(Int32Tag);
        doWriteUint32(ZigZag::encode(static_cast<uint32_t>(value)));
    }

    void writeNumber(double number)
    {
        append(NumberTag);
        append(reinterpret_cast<char*>(&number), sizeof(number));
    }

    // Records that a composite object can be constructed by using
    // |length| previously stored values.
    void endComposite(SerializationTag tag, int32_t length)
    {
        ASSERT(tag == ObjectTag || tag == ArrayTag);
        append(tag);
        doWriteUint32(static_cast<uint32_t>(length));
    }

    Vector<BufferValueType>& data()
    {
        fillHole();
        return m_buffer;
    }

private:
    void doWriteUint32(uint32_t value)
    {
        while (true) {
            char b = (value & varIntMask);
            value >>= varIntShift;
            if (!value) {
                append(b);
                break;
            }
            append(b | (1 << varIntShift));
        }
    }

    void append(SerializationTag tag)
    {
        append(static_cast<char>(tag));
    }

    void append(char b)
    {
        ensureSpace(1);
        *charAt(m_position++) = b;
    }

    void append(const char* data, int length)
    {
        ensureSpace(length);
        memcpy(charAt(m_position), data, length);
        m_position += length;
    }

    void ensureSpace(int extra)
    {
        COMPILE_ASSERT(sizeof(BufferValueType) == 2, BufferValueTypeIsTwoBytes);
        m_buffer.grow((m_position + extra + 1) / 2); // "+ 1" to round up.
    }

    void fillHole()
    {
        COMPILE_ASSERT(sizeof(BufferValueType) == 2, BufferValueTypeIsTwoBytes);
        // If the writer is at odd position in the buffer, then one of
        // the bytes in the last UChar is not initialized.
        if (m_position % 2)
            *charAt(m_position) = static_cast<char>(PaddingTag);
    }

    char* charAt(int position) { return reinterpret_cast<char*>(m_buffer.data()) + position; }

    Vector<BufferValueType> m_buffer;
    unsigned m_position;
};

class Serializer {
public:
    explicit Serializer(Writer& writer)
        : m_writer(writer)
        , m_state(0)
        , m_depth(0)
    {
    }

    bool serialize(v8::Handle<v8::Value> value)
    {
        v8::HandleScope scope;
        StackCleaner cleaner(&m_state);
        if (!doSerialize(value))
            return false;
        while (top()) {
            int length;
            while (!top()->isDone(&length)) {
                // Note that doSerialize() can change current top().
                if (!doSerialize(top()->advance()))
                    return false;
            }
            m_writer.endComposite(top()->tag(), length);
            pop();
        }
        return true;
    }

private:
    class StateBase : public Noncopyable {
    public:
        virtual ~StateBase() { }

        // Link to the next state to form a stack.
        StateBase* nextState() { return m_next; }
        void setNextState(StateBase* next) { m_next = next; }

        // Composite object we're processing in this state.
        v8::Handle<v8::Value> composite() { return m_composite; }

        // Serialization tag for the current composite.
        virtual SerializationTag tag() const = 0;

        // Returns whether iteration over subobjects of the current
        // composite object is done. If yes, |*length| is set to the
        // number of subobjects.
        virtual bool isDone(int* length) = 0;

        // Advances to the next subobject.
        // Requires: !this->isDone().
        virtual v8::Local<v8::Value> advance() = 0;

    protected:
        StateBase(v8::Handle<v8::Value> composite)
            : m_next(0)
            , m_composite(composite)
        {
        }

    private:
        StateBase* m_next;
        v8::Handle<v8::Value> m_composite;
    };

    template <typename T, SerializationTag compositeTag>
    class State : public StateBase {
    public:
        v8::Handle<T> composite() { return handleCast<T>(StateBase::composite()); }

        virtual SerializationTag tag() const { return compositeTag; }

    protected:
        explicit State(v8::Handle<T> composite) : StateBase(composite)
        {
        }
    };

    // Helper to clean up the state stack in case of errors.
    class StackCleaner : Noncopyable {
    public:
        explicit StackCleaner(StateBase** stack) : m_stack(stack)
        {
        }

        ~StackCleaner()
        {
            StateBase* state = *m_stack;
            while (state) {
                StateBase* tmp = state->nextState();
                delete state;
                state = tmp;
            }
            *m_stack = 0;
        }

    private:
        StateBase** m_stack;
    };

    class ArrayState : public State<v8::Array, ArrayTag> {
    public:
        ArrayState(v8::Handle<v8::Array> array)
            : State<v8::Array, ArrayTag>(array)
            , m_index(0)
        {
        }

        virtual bool isDone(int* length)
        {
            *length = composite()->Length();
            return static_cast<int>(m_index) >= *length;
        }

        virtual v8::Local<v8::Value> advance()
        {
            ASSERT(m_index < composite()->Length());
            v8::HandleScope scope;
            return scope.Close(composite()->Get(v8::Integer::New(m_index++)));
        }

    private:
        unsigned m_index;
    };

    class ObjectState : public State<v8::Object, ObjectTag> {
    public:
        ObjectState(v8::Handle<v8::Object> object)
            : State<v8::Object, ObjectTag>(object)
            , m_propertyNames(object->GetPropertyNames())
            , m_index(-1)
            , m_length(0)
        {
            nextProperty();
        }

        virtual bool isDone(int* length)
        {
            *length = m_length;
            return m_index >= 2 * m_propertyNames->Length();
        }

        virtual v8::Local<v8::Value> advance()
        {
            ASSERT(m_index < 2 * m_propertyNames->Length());
            if (!(m_index % 2)) {
                ++m_index;
                return m_propertyName;
            }
            v8::Local<v8::Value> result = composite()->Get(m_propertyName);
            nextProperty();
            return result;
        }

    private:
        void nextProperty()
        {
            v8::HandleScope scope;
            ++m_index;
            ASSERT(!(m_index % 2));
            for (; m_index < 2 * m_propertyNames->Length(); m_index += 2) {
                v8::Local<v8::Value> propertyName = m_propertyNames->Get(v8::Integer::New(m_index / 2));
                if ((propertyName->IsString() && composite()->HasRealNamedProperty(handleCast<v8::String>(propertyName)))
                    || (propertyName->IsInt32() && composite()->HasRealIndexedProperty(propertyName->Uint32Value()))) {
                    m_propertyName = scope.Close(propertyName);
                    m_length += 2;
                    return;
                }
            }
        }

        v8::Local<v8::Array> m_propertyNames;
        v8::Local<v8::Value> m_propertyName;
        unsigned m_index;
        unsigned m_length;
    };

    bool doSerialize(v8::Handle<v8::Value> value)
    {
        if (value->IsUndefined())
            m_writer.writeUndefined();
        else if (value->IsNull())
            m_writer.writeNull();
        else if (value->IsTrue())
            m_writer.writeTrue();
        else if (value->IsFalse())
            m_writer.writeFalse();
        else if (value->IsInt32())
            m_writer.writeInt32(value->Int32Value());
        else if (value->IsNumber())
            m_writer.writeNumber(handleCast<v8::Number>(value)->Value());
        else if (value->IsString()) {
            v8::String::Utf8Value stringValue(value);
            m_writer.writeString(*stringValue, stringValue.length());
        } else if (value->IsArray()) {
            if (!checkComposite(value))
                return false;
            push(new ArrayState(handleCast<v8::Array>(value)));
        } else if (value->IsObject()) {
            if (!checkComposite(value))
                return false;
            push(new ObjectState(handleCast<v8::Object>(value)));
            // FIXME:
            // - check not a wrapper
            // - support File, ImageData, etc.
        }
        return true;
    }

    void push(StateBase* state)
    {
        state->setNextState(m_state);
        m_state = state;
        ++m_depth;
    }

    StateBase* top() { return m_state; }

    void pop()
    {
        if (!m_state)
            return;
        StateBase* top = m_state;
        m_state = top->nextState();
        delete top;
        --m_depth;
    }

    bool checkComposite(v8::Handle<v8::Value> composite)
    {
        if (m_depth > maxDepth)
            return false;
        if (!shouldCheckForCycles(m_depth))
            return true;
        for (StateBase* state = top(); state; state = state->nextState()) {
            if (state->composite() == composite)
                return false;
        }
        return true;
    }

    Writer& m_writer;
    StateBase* m_state;
    int m_depth;
};

// Reader is responsible for deserializing primitive types and
// restoring information about saved objects of composite types.
class Reader {
public:
    Reader(const char* buffer, int length)
        : m_buffer(buffer)
        , m_length(length)
        , m_position(0)
    {
        ASSERT(length >= 0);
    }

    bool isEof() const { return m_position >= m_length; }

    bool read(SerializationTag* tag, v8::Handle<v8::Value>* value, int* length)
    {
        uint32_t rawLength;
        if (!readTag(tag))
            return false;
        switch (*tag) {
        case InvalidTag:
            return false;
        case PaddingTag:
            break;
        case UndefinedTag:
            *value = v8::Undefined();
            break;
        case NullTag:
            *value = v8::Null();
            break;
        case TrueTag:
            *value = v8::True();
            break;
        case FalseTag:
            *value = v8::False();
            break;
        case StringTag:
            if (!readString(value))
                return false;
            break;
        case Int32Tag:
            if (!readInt32(value))
                return false;
            break;
        case NumberTag:
            if (!readNumber(value))
                return false;
            break;
        case ObjectTag:
        case ArrayTag:
            if (!doReadUint32(&rawLength))
                return false;
            *length = rawLength;
            break;
        }
        return true;
    }

private:
    bool readTag(SerializationTag* tag)
    {
        if (m_position >= m_length)
            return false;
        *tag = static_cast<SerializationTag>(m_buffer[m_position++]);
        return true;
    }

    bool readString(v8::Handle<v8::Value>* value)
    {
        uint32_t length;
        if (!doReadUint32(&length))
            return false;
        if (m_position + length > m_length)
            return false;
        *value = v8::String::New(m_buffer + m_position, length);
        m_position += length;
        return true;
    }

    bool readInt32(v8::Handle<v8::Value>* value)
    {
        uint32_t rawValue;
        if (!doReadUint32(&rawValue))
            return false;
        *value = v8::Integer::New(static_cast<int32_t>(ZigZag::decode(rawValue)));
        return true;
    }

    bool readNumber(v8::Handle<v8::Value>* value)
    {
        if (m_position + sizeof(double) > m_length)
            return false;
        double number;
        char* numberAsByteArray = reinterpret_cast<char*>(&number);
        for (unsigned i = 0; i < sizeof(double); ++i)
            numberAsByteArray[i] = m_buffer[m_position++];
        *value = v8::Number::New(number);
        return true;
    }

    bool doReadUint32(uint32_t* value)
    {
        *value = 0;
        char currentByte;
        int shift = 0;
        do {
            if (m_position >= m_length)
                return false;
            currentByte = m_buffer[m_position++];
            *value |= ((currentByte & varIntMask) << shift);
            shift += varIntShift;
        } while (currentByte & (1 << varIntShift));
        return true;
    }

    const char* m_buffer;
    const unsigned m_length;
    unsigned m_position;
};

class Deserializer {
public:
    explicit Deserializer(Reader& reader) : m_reader(reader)
    {
    }

    v8::Local<v8::Value> deserialize()
    {
        v8::HandleScope scope;
        while (!m_reader.isEof()) {
            if (!doDeserialize())
                return v8::Local<v8::Value>();
        }
        if (stackDepth() != 1)
            return v8::Local<v8::Value>();
        return scope.Close(element(0));
    }

private:
    bool doDeserialize()
    {
        SerializationTag tag;
        v8::Local<v8::Value> value;
        int length = 0;
        if (!m_reader.read(&tag, &value, &length))
            return false;
        if (!value.IsEmpty()) {
            push(value);
        } else if (tag == ObjectTag) {
            if (length > stackDepth())
                return false;
            v8::Local<v8::Object> object = v8::Object::New();
            for (int i = stackDepth() - length; i < stackDepth(); i += 2) {
                v8::Local<v8::Value> propertyName = element(i);
                v8::Local<v8::Value> propertyValue = element(i + 1);
                object->Set(propertyName, propertyValue);
            }
            pop(length);
            push(object);
        } else if (tag == ArrayTag) {
            if (length > stackDepth())
                return false;
            v8::Local<v8::Array> array = v8::Array::New(length);
            const int depth = stackDepth() - length;
            {
                v8::HandleScope scope;
                for (int i = 0; i < length; ++i)
                    array->Set(v8::Integer::New(i), element(depth + i));
            }
            pop(length);
            push(array);
        } else if (tag != PaddingTag)
            return false;
        return true;
    }

    void push(v8::Local<v8::Value> value) { m_stack.append(value); }

    void pop(unsigned length)
    {
        ASSERT(length <= m_stack.size());
        m_stack.shrink(m_stack.size() - length);
    }

    int stackDepth() const { return m_stack.size(); }

    v8::Local<v8::Value> element(unsigned index)
    {
        ASSERT(index < m_stack.size());
        return m_stack[index];
    }

    Reader& m_reader;
    Vector<v8::Local<v8::Value> > m_stack;
};

} // namespace

SerializedScriptValue::SerializedScriptValue(v8::Handle<v8::Value> value)
{
    Writer writer;
    Serializer serializer(writer);
    if (!serializer.serialize(value)) {
        // FIXME: throw exception
        return;
    }
    m_data = StringImpl::adopt(writer.data());
}

SerializedScriptValue::SerializedScriptValue(String data, StringDataMode mode)
{
    if (mode == WireData)
        m_data = data;
    else {
        ASSERT(mode == StringValue);
        RefPtr<SharedBuffer> buffer = utf8Buffer(data);
        Writer writer;
        writer.writeString(buffer->data(), buffer->size());
        m_data = StringImpl::adopt(writer.data());
    }
}

v8::Local<v8::Value> SerializedScriptValue::deserialize()
{
    if (!m_data.impl())
        return v8::Local<v8::Value>();
    COMPILE_ASSERT(sizeof(BufferValueType) == 2, BufferValueTypeIsTwoBytes);
    Reader reader(reinterpret_cast<const char*>(m_data.impl()->characters()), 2 * m_data.length());
    Deserializer deserializer(reader);
    return deserializer.deserialize();
}

} // namespace WebCore