// Copyright 2016 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/value-serializer.h" #include #include #include "include/v8.h" #include "src/api.h" #include "src/base/build_config.h" #include "test/unittests/test-utils.h" #include "testing/gtest/include/gtest/gtest.h" namespace v8 { namespace { class ValueSerializerTest : public TestWithIsolate { protected: ValueSerializerTest() : serialization_context_(Context::New(isolate())), deserialization_context_(Context::New(isolate())) {} const Local& serialization_context() { return serialization_context_; } const Local& deserialization_context() { return deserialization_context_; } template void RoundTripTest(const InputFunctor& input_functor, const OutputFunctor& output_functor) { EncodeTest(input_functor, [this, &output_functor](const std::vector& data) { DecodeTest(data, output_functor); }); } // Variant for the common case where a script is used to build the original // value. template void RoundTripTest(const char* source, const OutputFunctor& output_functor) { RoundTripTest([this, source]() { return EvaluateScriptForInput(source); }, output_functor); } Maybe> DoEncode(Local value) { // This approximates what the API implementation would do. // TODO(jbroman): Use the public API once it exists. i::Isolate* internal_isolate = reinterpret_cast(isolate()); i::HandleScope handle_scope(internal_isolate); i::ValueSerializer serializer(internal_isolate); serializer.WriteHeader(); if (serializer.WriteObject(Utils::OpenHandle(*value)).FromMaybe(false)) { return Just(serializer.ReleaseBuffer()); } if (internal_isolate->has_pending_exception()) { internal_isolate->OptionalRescheduleException(true); } return Nothing>(); } template void EncodeTest(const InputFunctor& input_functor, const EncodedDataFunctor& encoded_data_functor) { Context::Scope scope(serialization_context()); TryCatch try_catch(isolate()); Local input_value = input_functor(); std::vector buffer; ASSERT_TRUE(DoEncode(input_value).To(&buffer)); ASSERT_FALSE(try_catch.HasCaught()); encoded_data_functor(buffer); } template void InvalidEncodeTest(const char* source, const MessageFunctor& functor) { Context::Scope scope(serialization_context()); TryCatch try_catch(isolate()); Local input_value = EvaluateScriptForInput(source); ASSERT_TRUE(DoEncode(input_value).IsNothing()); functor(try_catch.Message()); } void InvalidEncodeTest(const char* source) { InvalidEncodeTest(source, [](Local) {}); } template void DecodeTest(const std::vector& data, const OutputFunctor& output_functor) { Context::Scope scope(deserialization_context()); TryCatch try_catch(isolate()); // TODO(jbroman): Use the public API once it exists. i::Isolate* internal_isolate = reinterpret_cast(isolate()); i::HandleScope handle_scope(internal_isolate); i::ValueDeserializer deserializer( internal_isolate, i::Vector(&data[0], static_cast(data.size()))); ASSERT_TRUE(deserializer.ReadHeader().FromMaybe(false)); Local result; ASSERT_TRUE(ToLocal(deserializer.ReadObject(), &result)); ASSERT_FALSE(result.IsEmpty()); ASSERT_FALSE(try_catch.HasCaught()); ASSERT_TRUE(deserialization_context() ->Global() ->CreateDataProperty(deserialization_context_, StringFromUtf8("result"), result) .FromMaybe(false)); output_functor(result); ASSERT_FALSE(try_catch.HasCaught()); } template void DecodeTestForVersion0(const std::vector& data, const OutputFunctor& output_functor) { Context::Scope scope(deserialization_context()); TryCatch try_catch(isolate()); // TODO(jbroman): Use the public API once it exists. i::Isolate* internal_isolate = reinterpret_cast(isolate()); i::HandleScope handle_scope(internal_isolate); i::ValueDeserializer deserializer( internal_isolate, i::Vector(&data[0], static_cast(data.size()))); // TODO(jbroman): Enable legacy support. ASSERT_TRUE(deserializer.ReadHeader().FromMaybe(false)); // TODO(jbroman): Check version 0. Local result; ASSERT_TRUE(ToLocal( deserializer.ReadObjectUsingEntireBufferForLegacyFormat(), &result)); ASSERT_FALSE(result.IsEmpty()); ASSERT_FALSE(try_catch.HasCaught()); ASSERT_TRUE(deserialization_context() ->Global() ->CreateDataProperty(deserialization_context_, StringFromUtf8("result"), result) .FromMaybe(false)); output_functor(result); ASSERT_FALSE(try_catch.HasCaught()); } void InvalidDecodeTest(const std::vector& data) { Context::Scope scope(deserialization_context()); TryCatch try_catch(isolate()); i::Isolate* internal_isolate = reinterpret_cast(isolate()); i::HandleScope handle_scope(internal_isolate); i::ValueDeserializer deserializer( internal_isolate, i::Vector(&data[0], static_cast(data.size()))); Maybe header_result = deserializer.ReadHeader(); if (header_result.IsNothing()) return; ASSERT_TRUE(header_result.ToChecked()); ASSERT_TRUE(deserializer.ReadObject().is_null()); } Local EvaluateScriptForInput(const char* utf8_source) { Local source = StringFromUtf8(utf8_source); Local