forked from mirrors/0ad
Split off StructuredClone from ScriptInterface
Follows 34b1920e7b.
This separates StructuredClone & DeepCopy logic into its own header,
reducing the size of the monolithic ScriptInterface header.
Differential Revision: https://code.wildfiregames.com/D3922
This was SVN commit r25419.
This commit is contained in:
@@ -34,7 +34,7 @@ constexpr int DEFAULT_HEAP_GROWTH_BYTES_GCTRIGGER = 2 * 1024 * 1024;
|
||||
* should only be used on a single thread.
|
||||
*
|
||||
* (One means to share data between threads and contexts is to create
|
||||
* a ScriptInterface::StructuredClone.)
|
||||
* a Script::StructuredClone.)
|
||||
*/
|
||||
|
||||
class ScriptContext
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
#include "js/GCHashTable.h"
|
||||
#include "js/JSON.h"
|
||||
#include "js/SourceText.h"
|
||||
#include "js/StructuredClone.h"
|
||||
#include "js/Proxy.h"
|
||||
#include "js/Warnings.h"
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "ScriptExtraHeaders.h"
|
||||
#include "ScriptInterface.h"
|
||||
#include "ScriptStats.h"
|
||||
#include "StructuredClone.h"
|
||||
|
||||
#include "lib/debug.h"
|
||||
#include "lib/utf8.h"
|
||||
@@ -187,8 +188,8 @@ JS::Value deepcopy(const ScriptRequest& rq, JS::HandleValue val)
|
||||
return JS::UndefinedValue();
|
||||
}
|
||||
|
||||
JS::RootedValue ret(rq.cx);
|
||||
if (!JS_StructuredClone(rq.cx, val, &ret, NULL, NULL))
|
||||
JS::RootedValue ret(rq.cx, Script::DeepCopy(rq, val));
|
||||
if (ret.isNullOrUndefined())
|
||||
{
|
||||
ScriptException::Raise(rq, "deepcopy StructureClone copy failed.");
|
||||
return JS::UndefinedValue();
|
||||
@@ -343,7 +344,7 @@ ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugN
|
||||
|
||||
ScriptRequest rq(this);
|
||||
m_CmptPrivate.pScriptInterface = this;
|
||||
JS_SetCompartmentPrivate(js::GetObjectCompartment(rq.glob), (void*)&m_CmptPrivate);
|
||||
JS::SetRealmPrivate(JS::GetObjectRealmOrNull(rq.glob), (void*)&m_CmptPrivate);
|
||||
}
|
||||
|
||||
ScriptInterface::~ScriptInterface()
|
||||
@@ -362,7 +363,7 @@ void ScriptInterface::SetCallbackData(void* pCBData)
|
||||
|
||||
ScriptInterface::CmptPrivate* ScriptInterface::GetScriptInterfaceAndCBData(JSContext* cx)
|
||||
{
|
||||
CmptPrivate* pCmptPrivate = (CmptPrivate*)JS_GetCompartmentPrivate(js::GetContextCompartment(cx));
|
||||
CmptPrivate* pCmptPrivate = (CmptPrivate*)JS::GetRealmPrivate(JS::GetCurrentRealmOrNull(cx));
|
||||
return pCmptPrivate;
|
||||
}
|
||||
|
||||
@@ -963,36 +964,3 @@ std::string ScriptInterface::ToString(JS::MutableHandleValue obj, bool pretty) c
|
||||
ScriptFunction::Call(rq, obj, "toSource", source);
|
||||
return utf8_from_wstring(source);
|
||||
}
|
||||
|
||||
JS::Value ScriptInterface::CloneValueFromOtherCompartment(const ScriptInterface& otherCompartment, JS::HandleValue val) const
|
||||
{
|
||||
PROFILE("CloneValueFromOtherCompartment");
|
||||
ScriptRequest rq(this);
|
||||
JS::RootedValue out(rq.cx);
|
||||
ScriptInterface::StructuredClone structuredClone = otherCompartment.WriteStructuredClone(val);
|
||||
ReadStructuredClone(structuredClone, &out);
|
||||
return out.get();
|
||||
}
|
||||
|
||||
ScriptInterface::StructuredClone ScriptInterface::WriteStructuredClone(JS::HandleValue v) const
|
||||
{
|
||||
ScriptRequest rq(this);
|
||||
ScriptInterface::StructuredClone ret(new JSStructuredCloneData(JS::StructuredCloneScope::SameProcess));
|
||||
JS::CloneDataPolicy policy;
|
||||
if (!JS_WriteStructuredClone(rq.cx, v, ret.get(), JS::StructuredCloneScope::SameProcess, policy, nullptr, nullptr, JS::UndefinedHandleValue))
|
||||
{
|
||||
debug_warn(L"Writing a structured clone with JS_WriteStructuredClone failed!");
|
||||
ScriptException::CatchPending(rq);
|
||||
return ScriptInterface::StructuredClone();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ScriptInterface::ReadStructuredClone(const ScriptInterface::StructuredClone& ptr, JS::MutableHandleValue ret) const
|
||||
{
|
||||
ScriptRequest rq(this);
|
||||
JS::CloneDataPolicy policy;
|
||||
if (!JS_ReadStructuredClone(rq.cx, *ptr, JS_STRUCTURED_CLONE_VERSION, ptr->scope(), ret, policy, nullptr, nullptr))
|
||||
ScriptException::CatchPending(rq);
|
||||
}
|
||||
|
||||
@@ -48,8 +48,6 @@ ERROR_TYPE(Scripting_DefineType, CreationFailed);
|
||||
// but as large as necessary for all wrapped functions)
|
||||
#define SCRIPT_INTERFACE_MAX_ARGS 8
|
||||
|
||||
class JSStructuredCloneData;
|
||||
|
||||
class ScriptInterface;
|
||||
struct ScriptInterface_impl;
|
||||
|
||||
@@ -314,26 +312,6 @@ public:
|
||||
*/
|
||||
bool MathRandom(double& nbr);
|
||||
|
||||
/**
|
||||
* Structured clones are a way to serialize 'simple' JS::Values into a buffer
|
||||
* that can safely be passed between compartments and between threads.
|
||||
* A StructuredClone can be stored and read multiple times if desired.
|
||||
* We wrap them in shared_ptr so memory management is automatic and
|
||||
* thread-safe.
|
||||
*/
|
||||
using StructuredClone = shared_ptr<JSStructuredCloneData>;
|
||||
|
||||
StructuredClone WriteStructuredClone(JS::HandleValue v) const;
|
||||
void ReadStructuredClone(const StructuredClone& ptr, JS::MutableHandleValue ret) const;
|
||||
|
||||
/**
|
||||
* Construct a new value (usable in this ScriptInterface's compartment) by cloning
|
||||
* a value from a different compartment.
|
||||
* Complex values (functions, XML, etc) won't be cloned correctly, but basic
|
||||
* types and cyclic references should be fine.
|
||||
*/
|
||||
JS::Value CloneValueFromOtherCompartment(const ScriptInterface& otherCompartment, JS::HandleValue val) const;
|
||||
|
||||
/**
|
||||
* Retrieve the private data field of a JSObject that is an instance of the given JSClass.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/* Copyright (C) 2021 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "ScriptExceptions.h"
|
||||
#include "ScriptInterface.h"
|
||||
#include "ScriptRequest.h"
|
||||
#include "StructuredClone.h"
|
||||
|
||||
// Ignore warnings in SM headers.
|
||||
#if GCC_VERSION || CLANG_VERSION
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
# pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
|
||||
#elif MSC_VERSION
|
||||
# pragma warning(push, 1)
|
||||
#endif
|
||||
|
||||
#include "js/StructuredClone.h"
|
||||
|
||||
#if GCC_VERSION || CLANG_VERSION
|
||||
# pragma GCC diagnostic pop
|
||||
#elif MSC_VERSION
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
Script::StructuredClone Script::WriteStructuredClone(const ScriptRequest& rq, JS::HandleValue v)
|
||||
{
|
||||
Script::StructuredClone ret(new JSStructuredCloneData(JS::StructuredCloneScope::SameProcess));
|
||||
JS::CloneDataPolicy policy;
|
||||
if (!JS_WriteStructuredClone(rq.cx, v, ret.get(), JS::StructuredCloneScope::SameProcess, policy, nullptr, nullptr, JS::UndefinedHandleValue))
|
||||
{
|
||||
debug_warn(L"Writing a structured clone with JS_WriteStructuredClone failed!");
|
||||
ScriptException::CatchPending(rq);
|
||||
return StructuredClone();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Script::ReadStructuredClone(const ScriptRequest& rq, const Script::StructuredClone& ptr, JS::MutableHandleValue ret)
|
||||
{
|
||||
JS::CloneDataPolicy policy;
|
||||
if (!JS_ReadStructuredClone(rq.cx, *ptr, JS_STRUCTURED_CLONE_VERSION, ptr->scope(), ret, policy, nullptr, nullptr))
|
||||
ScriptException::CatchPending(rq);
|
||||
}
|
||||
|
||||
JS::Value Script::CloneValueFromOtherCompartment(const ScriptInterface& to, const ScriptInterface& from, JS::HandleValue val)
|
||||
{
|
||||
PROFILE("CloneValueFromOtherCompartment");
|
||||
Script::StructuredClone structuredClone;
|
||||
{
|
||||
ScriptRequest fromRq(from);
|
||||
structuredClone = WriteStructuredClone(fromRq, val);
|
||||
}
|
||||
ScriptRequest toRq(to);
|
||||
JS::RootedValue out(toRq.cx);
|
||||
ReadStructuredClone(toRq, structuredClone, &out);
|
||||
return out.get();
|
||||
}
|
||||
|
||||
JS::Value Script::DeepCopy(const ScriptRequest& rq, JS::HandleValue val)
|
||||
{
|
||||
JS::RootedValue out(rq.cx);
|
||||
ReadStructuredClone(rq, WriteStructuredClone(rq, val), &out);
|
||||
return out.get();
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/* Copyright (C) 2021 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_SCRIPTINTERFACE_STRUCTUREDCLONE
|
||||
#define INCLUDED_SCRIPTINTERFACE_STRUCTUREDCLONE
|
||||
|
||||
#include "ScriptForward.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class JSStructuredCloneData;
|
||||
|
||||
namespace Script
|
||||
{
|
||||
/**
|
||||
* Structured clones are a way to serialize 'simple' JS::Values into a buffer
|
||||
* that can safely be passed between compartments and between threads.
|
||||
* A StructuredClone can be stored and read multiple times if desired.
|
||||
* We wrap them in shared_ptr so memory management is automatic and
|
||||
* thread-safe.
|
||||
*/
|
||||
using StructuredClone = std::shared_ptr<JSStructuredCloneData>;
|
||||
|
||||
StructuredClone WriteStructuredClone(const ScriptRequest& rq, JS::HandleValue v);
|
||||
void ReadStructuredClone(const ScriptRequest& rq, const StructuredClone& ptr, JS::MutableHandleValue ret);
|
||||
|
||||
/**
|
||||
* Construct a new value by cloning a value (possibly from a different Compartment).
|
||||
* Complex values (functions, XML, etc) won't be cloned correctly, but basic
|
||||
* types and cyclic references should be fine.
|
||||
* Takes ScriptInterfaces to enter the correct realm.
|
||||
* Caller beware - manipulating several compartments in the same function is tricky.
|
||||
* @param to - ScriptInterface of the target. Should match the rooting context of the result.
|
||||
* @param from - ScriptInterface of @a val.
|
||||
*/
|
||||
JS::Value CloneValueFromOtherCompartment(const ScriptInterface& to, const ScriptInterface& from, JS::HandleValue val);
|
||||
|
||||
/**
|
||||
* Clone a JS value, ensuring that changes to the result
|
||||
* won't affect the original value.
|
||||
* Works by cloning, so the same restrictions as CloneValueFromOtherCompartment apply.
|
||||
*/
|
||||
JS::Value DeepCopy(const ScriptRequest& rq, JS::HandleValue val);
|
||||
|
||||
} // namespace Script
|
||||
|
||||
#endif // INCLUDED_SCRIPTINTERFACE_STRUCTUREDCLONE
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "scriptinterface/FunctionWrapper.h"
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "scriptinterface/StructuredClone.h"
|
||||
|
||||
#include "ps/CLogger.h"
|
||||
|
||||
@@ -73,7 +74,7 @@ public:
|
||||
{
|
||||
ScriptRequest rq2(script2);
|
||||
|
||||
JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherCompartment(script1, obj1));
|
||||
JS::RootedValue obj2(rq2.cx, Script::CloneValueFromOtherCompartment(script2, script1, obj1));
|
||||
|
||||
std::string source;
|
||||
TS_ASSERT(ScriptFunction::Call(rq2, obj2, "toSource", source));
|
||||
@@ -95,7 +96,7 @@ public:
|
||||
{
|
||||
ScriptRequest rq2(script2);
|
||||
|
||||
JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherCompartment(script1, obj1));
|
||||
JS::RootedValue obj2(rq2.cx, Script::CloneValueFromOtherCompartment(script2, script1, obj1));
|
||||
|
||||
std::string source;
|
||||
TS_ASSERT(ScriptFunction::Call(rq2, obj2, "toSource", source));
|
||||
@@ -115,7 +116,7 @@ public:
|
||||
|
||||
{
|
||||
ScriptRequest rq2(script2);
|
||||
JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherCompartment(script1, obj1));
|
||||
JS::RootedValue obj2(rq2.cx, Script::CloneValueFromOtherCompartment(script2, script1, obj1));
|
||||
|
||||
// Use JSAPI function to check if the values of the properties "a", "b" are equals a.x[0]
|
||||
JS::RootedValue prop_a(rq2.cx);
|
||||
@@ -244,7 +245,7 @@ public:
|
||||
TS_ASSERT_STR_EQUALS(script.ToString(&val), "({x:1, z:[2, \"3\\u263A\\uD800\"], y:true})");
|
||||
}
|
||||
|
||||
// This function tests a common way to mod functions, by crating a wrapper that
|
||||
// This function tests a common way to mod functions, by creating a wrapper that
|
||||
// extends the functionality and is then assigned to the name of the function.
|
||||
void test_function_override()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user