mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1201620 - Make SavedFrame stacks structured cloneable; r=sfink
This commit is contained in:
parent
0dc5f33ac7
commit
15ad930bb8
@ -146,11 +146,11 @@ class VersionChangeTransaction;
|
||||
|
||||
// If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major
|
||||
// schema version.
|
||||
static_assert(JS_STRUCTURED_CLONE_VERSION == 5,
|
||||
static_assert(JS_STRUCTURED_CLONE_VERSION == 6,
|
||||
"Need to update the major schema version.");
|
||||
|
||||
// Major schema version. Bump for almost everything.
|
||||
const uint32_t kMajorSchemaVersion = 21;
|
||||
const uint32_t kMajorSchemaVersion = 22;
|
||||
|
||||
// Minor schema version. Should almost always be 0 (maybe bump on release
|
||||
// branches if we have to).
|
||||
@ -4066,6 +4066,19 @@ UpgradeSchemaFrom20_0To21_0(mozIStorageConnection* aConnection)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
UpgradeSchemaFrom21_0To22_0(mozIStorageConnection* aConnection)
|
||||
{
|
||||
// The only change between 21 and 22 was a different structured clone format,
|
||||
// but it's backwards-compatible.
|
||||
nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(22, 0));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GetDatabaseFileURL(nsIFile* aDatabaseFile,
|
||||
PersistenceType aPersistenceType,
|
||||
@ -4558,7 +4571,7 @@ CreateStorageConnection(nsIFile* aDBFile,
|
||||
}
|
||||
} else {
|
||||
// This logic needs to change next time we change the schema!
|
||||
static_assert(kSQLiteSchemaVersion == int32_t((21 << 4) + 0),
|
||||
static_assert(kSQLiteSchemaVersion == int32_t((22 << 4) + 0),
|
||||
"Upgrade function needed due to schema version increase.");
|
||||
|
||||
while (schemaVersion != kSQLiteSchemaVersion) {
|
||||
@ -4598,6 +4611,8 @@ CreateStorageConnection(nsIFile* aDBFile,
|
||||
rv = UpgradeSchemaFrom19_0To20_0(aFMDirectory, connection);
|
||||
} else if (schemaVersion == MakeSchemaVersion(20, 0)) {
|
||||
rv = UpgradeSchemaFrom20_0To21_0(connection);
|
||||
} else if (schemaVersion == MakeSchemaVersion(21, 0)) {
|
||||
rv = UpgradeSchemaFrom21_0To22_0(connection);
|
||||
} else {
|
||||
IDB_WARNING("Unable to open IndexedDB database, no upgrade path is "
|
||||
"available!");
|
||||
|
@ -121,7 +121,7 @@ typedef void (*FreeTransferStructuredCloneOp)(uint32_t tag, JS::TransferableOwne
|
||||
// Increment this when anything at all changes in the serialization format.
|
||||
// (Note that this does not need to be bumped for Transferable-only changes,
|
||||
// since they are never saved to persistent storage.)
|
||||
#define JS_STRUCTURED_CLONE_VERSION 5
|
||||
#define JS_STRUCTURED_CLONE_VERSION 6
|
||||
|
||||
struct JSStructuredCloneCallbacks {
|
||||
ReadStructuredCloneOp read;
|
||||
|
@ -78,3 +78,151 @@ BEGIN_TEST(testStructuredClone_string)
|
||||
return true;
|
||||
}
|
||||
END_TEST(testStructuredClone_string)
|
||||
|
||||
struct StructuredCloneTestPrincipals final : public JSPrincipals {
|
||||
uint32_t rank;
|
||||
|
||||
explicit StructuredCloneTestPrincipals(uint32_t rank, int32_t rc = 1) : rank(rank) {
|
||||
this->refcount = rc;
|
||||
}
|
||||
|
||||
bool write(JSContext* cx, JSStructuredCloneWriter* writer) override {
|
||||
return JS_WriteUint32Pair(writer, rank, 0);
|
||||
}
|
||||
|
||||
static bool read(JSContext* cx, JSStructuredCloneReader *reader, JSPrincipals** outPrincipals) {
|
||||
uint32_t rank;
|
||||
uint32_t unused;
|
||||
if (!JS_ReadUint32Pair(reader, &rank, &unused))
|
||||
return false;
|
||||
|
||||
*outPrincipals = new StructuredCloneTestPrincipals(rank);
|
||||
return !!*outPrincipals;
|
||||
}
|
||||
|
||||
static void destroy(JSPrincipals* p) {
|
||||
auto p1 = static_cast<StructuredCloneTestPrincipals*>(p);
|
||||
delete p1;
|
||||
}
|
||||
|
||||
static uint32_t getRank(JSPrincipals* p) {
|
||||
if (!p)
|
||||
return 0;
|
||||
return static_cast<StructuredCloneTestPrincipals*>(p)->rank;
|
||||
}
|
||||
|
||||
static bool subsumes(JSPrincipals* a, JSPrincipals* b) {
|
||||
return getRank(a) > getRank(b);
|
||||
}
|
||||
|
||||
static JSSecurityCallbacks securityCallbacks;
|
||||
|
||||
static StructuredCloneTestPrincipals testPrincipals;
|
||||
};
|
||||
|
||||
JSSecurityCallbacks StructuredCloneTestPrincipals::securityCallbacks = {
|
||||
nullptr, // contentSecurityPolicyAllows
|
||||
subsumes
|
||||
};
|
||||
|
||||
BEGIN_TEST(testStructuredClone_SavedFrame)
|
||||
{
|
||||
JS_SetSecurityCallbacks(rt, &StructuredCloneTestPrincipals::securityCallbacks);
|
||||
JS_InitDestroyPrincipalsCallback(rt, StructuredCloneTestPrincipals::destroy);
|
||||
JS_InitReadPrincipalsCallback(rt, StructuredCloneTestPrincipals::read);
|
||||
|
||||
auto testPrincipals = new StructuredCloneTestPrincipals(42, 0);
|
||||
CHECK(testPrincipals);
|
||||
|
||||
auto DONE = (JSPrincipals*) 0xDEADBEEF;
|
||||
|
||||
struct {
|
||||
const char* name;
|
||||
JSPrincipals* principals;
|
||||
} principalsToTest[] = {
|
||||
{ "IsSystem", &js::ReconstructedSavedFramePrincipals::IsSystem },
|
||||
{ "IsNotSystem", &js::ReconstructedSavedFramePrincipals::IsNotSystem },
|
||||
{ "testPrincipals", testPrincipals },
|
||||
{ "nullptr principals", nullptr },
|
||||
{ "DONE", DONE }
|
||||
};
|
||||
|
||||
const char* FILENAME = "filename.js";
|
||||
|
||||
for (auto* pp = principalsToTest; pp->principals != DONE; pp++) {
|
||||
fprintf(stderr, "Testing with principals '%s'\n", pp->name);
|
||||
|
||||
JS::RootedObject g(cx, JS_NewGlobalObject(cx, getGlobalClass(), pp->principals,
|
||||
JS::FireOnNewGlobalHook));
|
||||
CHECK(g);
|
||||
JSAutoCompartment ac(cx, g);
|
||||
|
||||
CHECK(js::DefineTestingFunctions(cx, g, false, false));
|
||||
|
||||
JS::RootedValue srcVal(cx);
|
||||
CHECK(evaluate("(function one() { \n" // 1
|
||||
" return (function two() { \n" // 2
|
||||
" return (function three() { \n" // 3
|
||||
" return saveStack(); \n" // 4
|
||||
" }()); \n" // 5
|
||||
" }()); \n" // 6
|
||||
"}()); \n", // 7
|
||||
FILENAME,
|
||||
1,
|
||||
&srcVal));
|
||||
|
||||
CHECK(srcVal.isObject());
|
||||
JS::RootedObject srcObj(cx, &srcVal.toObject());
|
||||
|
||||
CHECK(srcObj->is<js::SavedFrame>());
|
||||
js::RootedSavedFrame srcFrame(cx, &srcObj->as<js::SavedFrame>());
|
||||
|
||||
CHECK(srcFrame->getPrincipals() == pp->principals);
|
||||
|
||||
JS::RootedValue destVal(cx);
|
||||
CHECK(JS_StructuredClone(cx, srcVal, &destVal, nullptr, nullptr));
|
||||
|
||||
CHECK(destVal.isObject());
|
||||
JS::RootedObject destObj(cx, &destVal.toObject());
|
||||
|
||||
CHECK(destObj->is<js::SavedFrame>());
|
||||
auto destFrame = &destObj->as<js::SavedFrame>();
|
||||
|
||||
size_t framesCopied = 0;
|
||||
for (auto& f : *destFrame) {
|
||||
framesCopied++;
|
||||
|
||||
CHECK(&f != srcFrame);
|
||||
|
||||
if (pp->principals == testPrincipals) {
|
||||
// We shouldn't get a pointer to the same
|
||||
// StructuredCloneTestPrincipals instance since we should have
|
||||
// serialized and then deserialized it into a new instance.
|
||||
CHECK(f.getPrincipals() != pp->principals);
|
||||
|
||||
// But it should certainly have the same rank.
|
||||
CHECK(StructuredCloneTestPrincipals::getRank(f.getPrincipals()) ==
|
||||
StructuredCloneTestPrincipals::getRank(pp->principals));
|
||||
} else {
|
||||
// For our singleton principals, we should always get the same
|
||||
// pointer back.
|
||||
CHECK(js::ReconstructedSavedFramePrincipals::is(pp->principals) ||
|
||||
pp->principals == nullptr);
|
||||
CHECK(f.getPrincipals() == pp->principals);
|
||||
}
|
||||
|
||||
CHECK(EqualStrings(f.getSource(), srcFrame->getSource()));
|
||||
CHECK(f.getLine() == srcFrame->getLine());
|
||||
CHECK(f.getColumn() == srcFrame->getColumn());
|
||||
CHECK(EqualStrings(f.getFunctionDisplayName(), srcFrame->getFunctionDisplayName()));
|
||||
|
||||
srcFrame = srcFrame->getParent();
|
||||
}
|
||||
|
||||
// Four function frames + one global frame.
|
||||
CHECK(framesCopied == 4);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testStructuredClone_SavedFrame)
|
||||
|
@ -6438,4 +6438,3 @@ JS::ResetTimeZone()
|
||||
icu::TimeZone::recreateDefault();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -7,12 +7,15 @@
|
||||
#ifndef vm_SavedFrame_h
|
||||
#define vm_SavedFrame_h
|
||||
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "js/UbiNode.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class SavedFrame : public NativeObject {
|
||||
friend class SavedStacks;
|
||||
friend struct ::JSStructuredCloneReader;
|
||||
|
||||
public:
|
||||
static const Class class_;
|
||||
@ -120,6 +123,12 @@ class SavedFrame : public NativeObject {
|
||||
!obj.as<SavedFrame>().getReservedSlot(JSSLOT_SOURCE).isNull();
|
||||
}
|
||||
|
||||
static bool isSavedFrameOrWrapperAndNotProto(JSObject& obj) {
|
||||
auto unwrapped = CheckedUnwrap(&obj);
|
||||
MOZ_ASSERT(unwrapped);
|
||||
return isSavedFrameAndNotProto(*unwrapped);
|
||||
}
|
||||
|
||||
struct Lookup;
|
||||
struct HashPolicy;
|
||||
|
||||
@ -142,8 +151,17 @@ class SavedFrame : public NativeObject {
|
||||
};
|
||||
|
||||
private:
|
||||
static SavedFrame* create(JSContext* cx);
|
||||
static bool finishSavedFrameInit(JSContext* cx, HandleObject ctor, HandleObject proto);
|
||||
void initFromLookup(HandleLookup lookup);
|
||||
void initSource(JSAtom* source);
|
||||
void initLine(uint32_t line);
|
||||
void initColumn(uint32_t column);
|
||||
void initFunctionDisplayName(JSAtom* maybeName);
|
||||
void initAsyncCause(JSAtom* maybeCause);
|
||||
void initParent(SavedFrame* maybeParent);
|
||||
void initPrincipalsAlreadyHeld(JSPrincipals* principals);
|
||||
void initPrincipals(JSPrincipals* principals);
|
||||
|
||||
enum {
|
||||
// The reserved slots in the SavedFrame class.
|
||||
|
@ -22,13 +22,7 @@
|
||||
inline void
|
||||
js::AssertObjectIsSavedFrameOrWrapper(JSContext* cx, HandleObject stack)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (stack) {
|
||||
RootedObject savedFrameObj(cx, CheckedUnwrap(stack));
|
||||
MOZ_ASSERT(savedFrameObj);
|
||||
MOZ_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*savedFrameObj));
|
||||
}
|
||||
#endif
|
||||
MOZ_ASSERT_IF(stack, js::SavedFrame::isSavedFrameOrWrapperAndNotProto(*stack));
|
||||
}
|
||||
|
||||
#endif // vm_SavedStacksInl_h
|
||||
|
@ -406,29 +406,91 @@ SavedFrame::getPrincipals()
|
||||
return static_cast<JSPrincipals*>(v.toPrivate());
|
||||
}
|
||||
|
||||
void
|
||||
SavedFrame::initSource(JSAtom* source)
|
||||
{
|
||||
MOZ_ASSERT(source);
|
||||
initReservedSlot(JSSLOT_SOURCE, StringValue(source));
|
||||
}
|
||||
|
||||
void
|
||||
SavedFrame::initLine(uint32_t line)
|
||||
{
|
||||
initReservedSlot(JSSLOT_LINE, PrivateUint32Value(line));
|
||||
}
|
||||
|
||||
void
|
||||
SavedFrame::initColumn(uint32_t column)
|
||||
{
|
||||
initReservedSlot(JSSLOT_COLUMN, PrivateUint32Value(column));
|
||||
}
|
||||
|
||||
void
|
||||
SavedFrame::initPrincipals(JSPrincipals* principals)
|
||||
{
|
||||
if (principals)
|
||||
JS_HoldPrincipals(principals);
|
||||
initPrincipalsAlreadyHeld(principals);
|
||||
}
|
||||
|
||||
void
|
||||
SavedFrame::initPrincipalsAlreadyHeld(JSPrincipals* principals)
|
||||
{
|
||||
MOZ_ASSERT_IF(principals, principals->refcount > 0);
|
||||
initReservedSlot(JSSLOT_PRINCIPALS, PrivateValue(principals));
|
||||
}
|
||||
|
||||
void
|
||||
SavedFrame::initFunctionDisplayName(JSAtom* maybeName)
|
||||
{
|
||||
initReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME, maybeName ? StringValue(maybeName) : NullValue());
|
||||
}
|
||||
|
||||
void
|
||||
SavedFrame::initAsyncCause(JSAtom* maybeCause)
|
||||
{
|
||||
initReservedSlot(JSSLOT_ASYNCCAUSE, maybeCause ? StringValue(maybeCause) : NullValue());
|
||||
}
|
||||
|
||||
void
|
||||
SavedFrame::initParent(SavedFrame* maybeParent)
|
||||
{
|
||||
initReservedSlot(JSSLOT_PARENT, ObjectOrNullValue(maybeParent));
|
||||
}
|
||||
|
||||
void
|
||||
SavedFrame::initFromLookup(SavedFrame::HandleLookup lookup)
|
||||
{
|
||||
MOZ_ASSERT(lookup->source);
|
||||
MOZ_ASSERT(getReservedSlot(JSSLOT_SOURCE).isUndefined());
|
||||
setReservedSlot(JSSLOT_SOURCE, StringValue(lookup->source));
|
||||
initSource(lookup->source);
|
||||
initLine(lookup->line);
|
||||
initColumn(lookup->column);
|
||||
initFunctionDisplayName(lookup->functionDisplayName);
|
||||
initAsyncCause(lookup->asyncCause);
|
||||
initParent(lookup->parent);
|
||||
initPrincipals(lookup->principals);
|
||||
}
|
||||
|
||||
setReservedSlot(JSSLOT_LINE, PrivateUint32Value(lookup->line));
|
||||
setReservedSlot(JSSLOT_COLUMN, PrivateUint32Value(lookup->column));
|
||||
setReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME,
|
||||
lookup->functionDisplayName
|
||||
? StringValue(lookup->functionDisplayName)
|
||||
: NullValue());
|
||||
setReservedSlot(JSSLOT_ASYNCCAUSE,
|
||||
lookup->asyncCause
|
||||
? StringValue(lookup->asyncCause)
|
||||
: NullValue());
|
||||
setReservedSlot(JSSLOT_PARENT, ObjectOrNullValue(lookup->parent));
|
||||
/* static */ SavedFrame*
|
||||
SavedFrame::create(JSContext* cx)
|
||||
{
|
||||
RootedGlobalObject global(cx, cx->global());
|
||||
assertSameCompartment(cx, global);
|
||||
|
||||
MOZ_ASSERT(getReservedSlot(JSSLOT_PRINCIPALS).isUndefined());
|
||||
if (lookup->principals)
|
||||
JS_HoldPrincipals(lookup->principals);
|
||||
setReservedSlot(JSSLOT_PRINCIPALS, PrivateValue(lookup->principals));
|
||||
// Ensure that we don't try to capture the stack again in the
|
||||
// `SavedStacksMetadataCallback` for this new SavedFrame object, and
|
||||
// accidentally cause O(n^2) behavior.
|
||||
SavedStacks::AutoReentrancyGuard guard(cx->compartment()->savedStacks());
|
||||
|
||||
RootedNativeObject proto(cx, GlobalObject::getOrCreateSavedFramePrototype(cx, global));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
assertSameCompartment(cx, proto);
|
||||
|
||||
RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto));
|
||||
if (!frameObj)
|
||||
return nullptr;
|
||||
|
||||
return &frameObj->as<SavedFrame>();
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1222,30 +1284,15 @@ SavedStacks::getOrCreateSavedFrame(JSContext* cx, SavedFrame::HandleLookup looku
|
||||
SavedFrame*
|
||||
SavedStacks::createFrameFromLookup(JSContext* cx, SavedFrame::HandleLookup lookup)
|
||||
{
|
||||
RootedGlobalObject global(cx, cx->global());
|
||||
assertSameCompartment(cx, global);
|
||||
|
||||
// Ensure that we don't try to capture the stack again in the
|
||||
// `SavedStacksMetadataCallback` for this new SavedFrame object, and
|
||||
// accidentally cause O(n^2) behavior.
|
||||
SavedStacks::AutoReentrancyGuard guard(*this);
|
||||
|
||||
RootedNativeObject proto(cx, GlobalObject::getOrCreateSavedFramePrototype(cx, global));
|
||||
if (!proto)
|
||||
RootedSavedFrame frame(cx, SavedFrame::create(cx));
|
||||
if (!frame)
|
||||
return nullptr;
|
||||
assertSameCompartment(cx, proto);
|
||||
frame->initFromLookup(lookup);
|
||||
|
||||
RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto));
|
||||
if (!frameObj)
|
||||
if (!FreezeObject(cx, frame))
|
||||
return nullptr;
|
||||
|
||||
RootedSavedFrame f(cx, &frameObj->as<SavedFrame>());
|
||||
f->initFromLookup(lookup);
|
||||
|
||||
if (!FreezeObject(cx, frameObj))
|
||||
return nullptr;
|
||||
|
||||
return f.get();
|
||||
return frame;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -148,6 +148,7 @@ namespace js {
|
||||
// principals.
|
||||
|
||||
class SavedStacks {
|
||||
friend class SavedFrame;
|
||||
friend JSObject* SavedStacksMetadataCallback(JSContext* cx, JSObject* target);
|
||||
friend bool JS::ubi::ConstructSavedFrameStackSlow(JSContext* cx,
|
||||
JS::ubi::StackFrame& ubiFrame,
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "builtin/MapObject.h"
|
||||
#include "js/Date.h"
|
||||
#include "js/TraceableHashTable.h"
|
||||
#include "vm/SavedFrame.h"
|
||||
#include "vm/SharedArrayObject.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
#include "vm/WrapperObject.h"
|
||||
@ -92,6 +93,12 @@ enum StructuredDataType : uint32_t {
|
||||
SCTAG_END_OF_KEYS,
|
||||
SCTAG_SHARED_TYPED_ARRAY_OBJECT,
|
||||
SCTAG_DATA_VIEW_OBJECT,
|
||||
SCTAG_SAVED_FRAME_OBJECT,
|
||||
|
||||
SCTAG_JSPRINCIPALS,
|
||||
SCTAG_NULL_JSPRINCIPALS,
|
||||
SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM,
|
||||
SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM,
|
||||
|
||||
SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100,
|
||||
SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int8,
|
||||
@ -243,6 +250,7 @@ struct JSStructuredCloneReader {
|
||||
bool readSharedTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp);
|
||||
bool readArrayBuffer(uint32_t nbytes, MutableHandleValue vp);
|
||||
bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp);
|
||||
JSObject* readSavedFrame(uint32_t principalsTag);
|
||||
bool startRead(MutableHandleValue vp);
|
||||
|
||||
SCInput& in;
|
||||
@ -305,6 +313,7 @@ struct JSStructuredCloneWriter {
|
||||
bool traverseObject(HandleObject obj);
|
||||
bool traverseMap(HandleObject obj);
|
||||
bool traverseSet(HandleObject obj);
|
||||
bool traverseSavedFrame(HandleObject obj);
|
||||
|
||||
bool parseTransferable();
|
||||
bool reportErrorTransferable(uint32_t errorId);
|
||||
@ -327,6 +336,7 @@ struct JSStructuredCloneWriter {
|
||||
// For JSObject: Property IDs as value
|
||||
// For Map: Key followed by value
|
||||
// For Set: Key
|
||||
// For SavedFrame: parent SavedFrame
|
||||
AutoValueVector entries;
|
||||
|
||||
// The "memory" list described in the HTML5 internal structured cloning algorithm.
|
||||
@ -1056,6 +1066,84 @@ JSStructuredCloneWriter::traverseSet(HandleObject obj)
|
||||
//
|
||||
// Notice how the end-of-children marker for key1 is sandwiched between the
|
||||
// value1 beginning and end.
|
||||
bool
|
||||
JSStructuredCloneWriter::traverseSavedFrame(HandleObject obj)
|
||||
{
|
||||
RootedObject unwrapped(context(), js::CheckedUnwrap(obj));
|
||||
MOZ_ASSERT(unwrapped && unwrapped->is<SavedFrame>());
|
||||
|
||||
RootedSavedFrame savedFrame(context(), &unwrapped->as<SavedFrame>());
|
||||
|
||||
RootedObject parent(context(), savedFrame->getParent());
|
||||
if (!context()->compartment()->wrap(context(), &parent))
|
||||
return false;
|
||||
|
||||
if (!objs.append(ObjectValue(*obj)) ||
|
||||
!entries.append(parent ? ObjectValue(*parent) : NullValue()) ||
|
||||
!counts.append(1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
checkStack();
|
||||
|
||||
// Write the SavedFrame tag and the SavedFrame's principals.
|
||||
|
||||
if (savedFrame->getPrincipals() == &ReconstructedSavedFramePrincipals::IsSystem) {
|
||||
if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT,
|
||||
SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM))
|
||||
{
|
||||
return false;
|
||||
};
|
||||
} else if (savedFrame->getPrincipals() == &ReconstructedSavedFramePrincipals::IsNotSystem) {
|
||||
if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT,
|
||||
SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (auto principals = savedFrame->getPrincipals()) {
|
||||
if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT, SCTAG_JSPRINCIPALS) ||
|
||||
!principals->write(context(), this))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT, SCTAG_NULL_JSPRINCIPALS))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the SavedFrame's reserved slots, except for the parent, which is
|
||||
// queued on objs for further traversal.
|
||||
|
||||
RootedValue val(context());
|
||||
|
||||
val = StringValue(savedFrame->getSource());
|
||||
if (!startWrite(val))
|
||||
return false;
|
||||
|
||||
val = NumberValue(savedFrame->getLine());
|
||||
if (!startWrite(val))
|
||||
return false;
|
||||
|
||||
val = NumberValue(savedFrame->getColumn());
|
||||
if (!startWrite(val))
|
||||
return false;
|
||||
|
||||
auto name = savedFrame->getFunctionDisplayName();
|
||||
val = name ? StringValue(name) : NullValue();
|
||||
if (!startWrite(val))
|
||||
return false;
|
||||
|
||||
auto cause = savedFrame->getAsyncCause();
|
||||
val = cause ? StringValue(cause) : NullValue();
|
||||
if (!startWrite(val))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
JSStructuredCloneWriter::startWrite(HandleValue v)
|
||||
{
|
||||
@ -1130,6 +1218,8 @@ JSStructuredCloneWriter::startWrite(HandleValue v)
|
||||
return traverseMap(obj);
|
||||
} else if (cls == ESClass_Set) {
|
||||
return traverseSet(obj);
|
||||
} else if (SavedFrame::isSavedFrameOrWrapperAndNotProto(*obj)) {
|
||||
return traverseSavedFrame(obj);
|
||||
}
|
||||
|
||||
if (callbacks && callbacks->write)
|
||||
@ -1287,7 +1377,7 @@ JSStructuredCloneWriter::write(HandleValue v)
|
||||
|
||||
if (!startWrite(key) || !startWrite(val))
|
||||
return false;
|
||||
} else if (cls == ESClass_Set) {
|
||||
} else if (cls == ESClass_Set || SavedFrame::isSavedFrameOrWrapperAndNotProto(*obj)) {
|
||||
if (!startWrite(key))
|
||||
return false;
|
||||
} else {
|
||||
@ -1796,6 +1886,14 @@ JSStructuredCloneReader::startRead(MutableHandleValue vp)
|
||||
break;
|
||||
}
|
||||
|
||||
case SCTAG_SAVED_FRAME_OBJECT: {
|
||||
auto obj = readSavedFrame(data);
|
||||
if (!obj || !objs.append(ObjectValue(*obj)))
|
||||
return false;
|
||||
vp.setObject(*obj);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
if (tag <= SCTAG_FLOAT_MAX) {
|
||||
double d = ReinterpretPairAsDouble(tag, data);
|
||||
@ -1913,6 +2011,83 @@ JSStructuredCloneReader::readTransferMap()
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
JSStructuredCloneReader::readSavedFrame(uint32_t principalsTag)
|
||||
{
|
||||
RootedSavedFrame savedFrame(context(), SavedFrame::create(context()));
|
||||
if (!savedFrame)
|
||||
return nullptr;
|
||||
|
||||
JSPrincipals* principals;
|
||||
if (principalsTag == SCTAG_JSPRINCIPALS) {
|
||||
if (!context()->runtime()->readPrincipals) {
|
||||
JS_ReportErrorNumber(context(), GetErrorMessage, nullptr,
|
||||
JSMSG_SC_UNSUPPORTED_TYPE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!context()->runtime()->readPrincipals(context(), this, &principals))
|
||||
return nullptr;
|
||||
} else if (principalsTag == SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM) {
|
||||
principals = &ReconstructedSavedFramePrincipals::IsSystem;
|
||||
principals->refcount++;
|
||||
} else if (principalsTag == SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM) {
|
||||
principals = &ReconstructedSavedFramePrincipals::IsNotSystem;
|
||||
principals->refcount++;
|
||||
} else if (principalsTag == SCTAG_NULL_JSPRINCIPALS) {
|
||||
principals = nullptr;
|
||||
} else {
|
||||
JS_ReportErrorNumber(context(), GetErrorMessage, nullptr,
|
||||
JSMSG_SC_BAD_SERIALIZED_DATA, "bad SavedFrame principals");
|
||||
return nullptr;
|
||||
}
|
||||
savedFrame->initPrincipalsAlreadyHeld(principals);
|
||||
|
||||
RootedValue source(context());
|
||||
if (!startRead(&source) || !source.isString())
|
||||
return nullptr;
|
||||
auto atomSource = AtomizeString(context(), source.toString());
|
||||
if (!atomSource)
|
||||
return nullptr;
|
||||
savedFrame->initSource(atomSource);
|
||||
|
||||
RootedValue lineVal(context());
|
||||
uint32_t line;
|
||||
if (!startRead(&lineVal) || !lineVal.isNumber() || !ToUint32(context(), lineVal, &line))
|
||||
return nullptr;
|
||||
savedFrame->initLine(line);
|
||||
|
||||
RootedValue columnVal(context());
|
||||
uint32_t column;
|
||||
if (!startRead(&columnVal) || !columnVal.isNumber() || !ToUint32(context(), columnVal, &column))
|
||||
return nullptr;
|
||||
savedFrame->initColumn(column);
|
||||
|
||||
RootedValue name(context());
|
||||
if (!startRead(&name) || !(name.isString() || name.isNull()))
|
||||
return nullptr;
|
||||
JSAtom* atomName = nullptr;
|
||||
if (name.isString()) {
|
||||
atomName = AtomizeString(context(), name.toString());
|
||||
if (!atomName)
|
||||
return nullptr;
|
||||
}
|
||||
savedFrame->initFunctionDisplayName(atomName);
|
||||
|
||||
RootedValue cause(context());
|
||||
if (!startRead(&cause) || !(cause.isString() || cause.isNull()))
|
||||
return nullptr;
|
||||
JSAtom* atomCause = nullptr;
|
||||
if (cause.isString()) {
|
||||
atomCause = AtomizeString(context(), cause.toString());
|
||||
if (!atomCause)
|
||||
return nullptr;
|
||||
}
|
||||
savedFrame->initAsyncCause(atomCause);
|
||||
|
||||
return savedFrame;
|
||||
}
|
||||
|
||||
// Perform the whole recursive reading procedure.
|
||||
bool
|
||||
JSStructuredCloneReader::read(MutableHandleValue vp)
|
||||
@ -1961,7 +2136,9 @@ JSStructuredCloneReader::read(MutableHandleValue vp)
|
||||
if (!startRead(&key))
|
||||
return false;
|
||||
|
||||
if (key.isNull() && !(obj->is<MapObject>() || obj->is<SetObject>())) {
|
||||
if (key.isNull() &&
|
||||
!(obj->is<MapObject>() || obj->is<SetObject>() || obj->is<SavedFrame>()))
|
||||
{
|
||||
// Backwards compatibility: Null formerly indicated the end of
|
||||
// object properties.
|
||||
objs.popBack();
|
||||
@ -1976,6 +2153,21 @@ JSStructuredCloneReader::read(MutableHandleValue vp)
|
||||
continue;
|
||||
}
|
||||
|
||||
// SavedFrame object: there is one following value, the parent
|
||||
// SavedFrame, which is either null or another SavedFrame object.
|
||||
if (obj->is<SavedFrame>()) {
|
||||
SavedFrame* parentFrame;
|
||||
if (key.isNull())
|
||||
parentFrame = nullptr;
|
||||
else if (key.isObject() && key.toObject().is<SavedFrame>())
|
||||
parentFrame = &key.toObject().as<SavedFrame>();
|
||||
else
|
||||
return false;
|
||||
|
||||
obj->as<SavedFrame>().initParent(parentFrame);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Everything else uses a series of key,value,key,value,... Value
|
||||
// objects.
|
||||
RootedValue val(context());
|
||||
|
Loading…
Reference in New Issue
Block a user