Bug 920322 - XDR/Clone singletons. r=bhackett,jandem

This commit is contained in:
Nicolas B. Pierron 2014-01-23 06:43:28 -08:00
parent 57cbe92f60
commit 89b309f197
27 changed files with 617 additions and 42 deletions

View File

@ -22,6 +22,7 @@
#include "jit/AsmJSModule.h"
#include "jit/AsmJSSignalHandlers.h"
#include "jit/CodeGenerator.h"
#include "jit/CompileWrappers.h"
#include "jit/MIR.h"
#include "jit/MIRGraph.h"
#ifdef JS_ION_PERF
@ -2043,7 +2044,9 @@ class FunctionCompiler
graph_ = lifo_.new_<MIRGraph>(alloc_);
info_ = lifo_.new_<CompileInfo>(locals_.count(), SequentialExecution);
const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_AsmJS);
mirGen_ = lifo_.new_<MIRGenerator>(CompileCompartment::get(cx()->compartment()), alloc_,
const JitCompileOptions options;
mirGen_ = lifo_.new_<MIRGenerator>(CompileCompartment::get(cx()->compartment()),
options, alloc_,
graph_, info_, optimizationInfo);
if (!newBlock(/* pred = */ nullptr, &curBlock_, fn_))

View File

@ -1190,9 +1190,33 @@ BaselineCompiler::emit_JSOP_STRING()
return true;
}
typedef JSObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleObject, NewObjectKind);
static const VMFunction DeepCloneObjectLiteralInfo =
FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral);
bool
BaselineCompiler::emit_JSOP_OBJECT()
{
if (JS::CompartmentOptionsRef(cx).cloneSingletons(cx)) {
RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc)));
if (!obj)
return false;
prepareVMCall();
pushArg(ImmWord(js::MaybeSingletonObject));
pushArg(ImmGCPtr(obj));
if (!callVM(DeepCloneObjectLiteralInfo))
return false;
// Box and push return value.
masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
frame.push(R0);
return true;
}
JS::CompartmentOptionsRef(cx).setSingletonsAsValues();
frame.push(ObjectValue(*script->getObject(pc)));
return true;
}

View File

@ -1078,6 +1078,18 @@ CodeGenerator::visitTableSwitchV(LTableSwitchV *ins)
return emitTableSwitchDispatch(mir, index, ToRegisterOrInvalid(ins->tempPointer()));
}
typedef JSObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleObject, NewObjectKind);
static const VMFunction DeepCloneObjectLiteralInfo =
FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral);
bool
CodeGenerator::visitCloneLiteral(LCloneLiteral *lir)
{
pushArg(ImmWord(js::MaybeSingletonObject));
pushArg(ToRegister(lir->output()));
return callVM(DeepCloneObjectLiteralInfo, lir);
}
bool
CodeGenerator::visitParameter(LParameter *lir)
{

View File

@ -60,6 +60,7 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitGoto(LGoto *lir);
bool visitTableSwitch(LTableSwitch *ins);
bool visitTableSwitchV(LTableSwitchV *ins);
bool visitCloneLiteral(LCloneLiteral *lir);
bool visitParameter(LParameter *lir);
bool visitCallee(LCallee *lir);
bool visitStart(LStart *lir);

View File

@ -231,6 +231,19 @@ CompileCompartment::hasObjectMetadataCallback()
return compartment()->hasObjectMetadataCallback();
}
// Note: This function is thread-safe because setSingletonAsValue sets a boolean
// variable to false, and this boolean variable has no way to be resetted to
// true. So even if there is a concurrent write, this concurrent write will
// always have the same value. If there is a concurrent read, then we will
// clone a singleton instead of using the value which is baked in the JSScript,
// and this would be an unfortunate allocation, but this will not change the
// semantics of the JavaScript code which is executed.
void
CompileCompartment::setSingletonsAsValues()
{
return JS::CompartmentOptionsRef(compartment()).setSingletonsAsValues();
}
#ifdef JS_THREADSAFE
AutoLockForCompilation::AutoLockForCompilation(CompileCompartment *compartment
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
@ -239,3 +252,14 @@ AutoLockForCompilation::AutoLockForCompilation(CompileCompartment *compartment
init(compartment->compartment()->runtimeFromAnyThread());
}
#endif
JitCompileOptions::JitCompileOptions()
: cloneSingletons_(false)
{
}
JitCompileOptions::JitCompileOptions(JSContext *cx)
{
JS::CompartmentOptions &options = cx->compartment()->options();
cloneSingletons_ = options.cloneSingletons(cx);
}

View File

@ -113,8 +113,26 @@ class CompileCompartment
const JitCompartment *jitCompartment();
bool hasObjectMetadataCallback();
// Mirror CompartmentOptions.
void setSingletonsAsValues();
};
class JitCompileOptions
{
public:
JitCompileOptions();
JitCompileOptions(JSContext *cx);
bool cloneSingletons() const {
return cloneSingletons_;
}
private:
bool cloneSingletons_;
};
} // namespace jit
} // namespace js

View File

@ -1706,10 +1706,11 @@ IonCompile(JSContext *cx, JSScript *script,
return AbortReason_Alloc;
const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(optimizationLevel);
const JitCompileOptions options(cx);
IonBuilder *builder = alloc->new_<IonBuilder>((JSContext *) nullptr,
CompileCompartment::get(cx->compartment()),
temp, graph, constraints,
options, temp, graph, constraints,
inspector, info, optimizationInfo,
baselineFrameInspector);
if (!builder)

View File

@ -2187,7 +2187,9 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
types::CompilerConstraintList *constraints = types::NewCompilerConstraintList(temp);
BaselineInspector inspector(script);
IonBuilder builder(cx, CompileCompartment::get(cx->compartment()), &temp, &graph, constraints,
const JitCompileOptions options(cx);
IonBuilder builder(cx, CompileCompartment::get(cx->compartment()), options, &temp, &graph, constraints,
&inspector, &info, optimizationInfo, /* baselineFrame = */ nullptr);
if (!builder.build()) {

View File

@ -103,13 +103,14 @@ jit::NewBaselineFrameInspector(TempAllocator *temp, BaselineFrame *frame)
return inspector;
}
IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp,
IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp,
const JitCompileOptions &options, TempAllocator *temp,
MIRGraph *graph, types::CompilerConstraintList *constraints,
BaselineInspector *inspector, CompileInfo *info,
const OptimizationInfo *optimizationInfo,
BaselineFrameInspector *baselineFrame, size_t inliningDepth,
uint32_t loopDepth)
: MIRGenerator(comp, temp, graph, info, optimizationInfo),
: MIRGenerator(comp, options, temp, graph, info, optimizationInfo),
backgroundCodegen_(nullptr),
analysisContext(analysisContext),
baselineFrame_(baselineFrame),
@ -3899,7 +3900,7 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target)
unlock();
// Build the graph.
IonBuilder inlineBuilder(analysisContext, compartment, &alloc(), &graph(), constraints(),
IonBuilder inlineBuilder(analysisContext, compartment, options, &alloc(), &graph(), constraints(),
&inspector, info, &optimizationInfo(), nullptr, inliningDepth_ + 1,
loopDepth_);
if (!inlineBuilder.buildInline(this, outerResumePoint, callInfo)) {
@ -9105,6 +9106,14 @@ IonBuilder::jsop_regexp(RegExpObject *reobj)
bool
IonBuilder::jsop_object(JSObject *obj)
{
if (options.cloneSingletons()) {
MCloneLiteral *clone = MCloneLiteral::New(alloc(), constant(ObjectValue(*obj)));
current->add(clone);
current->push(clone);
return resumeAfter(clone);
}
compartment->setSingletonsAsValues();
pushConstant(ObjectValue(*obj));
return true;
}

View File

@ -212,7 +212,8 @@ class IonBuilder : public MIRGenerator
static int CmpSuccessors(const void *a, const void *b);
public:
IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp,
IonBuilder(JSContext *analysisContext, CompileCompartment *comp,
const JitCompileOptions &options, TempAllocator *temp,
MIRGraph *graph, types::CompilerConstraintList *constraints,
BaselineInspector *inspector, CompileInfo *info,
const OptimizationInfo *optimizationInfo, BaselineFrameInspector *baselineFrame,

View File

@ -232,6 +232,27 @@ class LValue : public LInstructionHelper<BOX_PIECES, 0, 0>
}
};
// Clone an object literal such as we are not modifying the object contained in
// the sources.
class LCloneLiteral : public LCallInstructionHelper<1, 1, 0>
{
public:
LIR_HEADER(CloneLiteral)
LCloneLiteral(const LAllocation &obj)
{
setOperand(0, obj);
}
const LAllocation *getObjectLiteral() {
return getOperand(0);
}
MCloneLiteral *mir() const {
return mir_->toCloneLiteral();
}
};
// Formal argument for a function, returning a box. Formal arguments are
// initially read from the stack.
class LParameter : public LInstructionHelper<BOX_PIECES, 0, 0>

View File

@ -17,6 +17,7 @@
_(Double) \
_(Float32) \
_(Value) \
_(CloneLiteral) \
_(Parameter) \
_(Callee) \
_(TableSwitch) \

View File

@ -27,6 +27,16 @@ using namespace jit;
using mozilla::DebugOnly;
using JS::GenericNaN;
bool
LIRGenerator::visitCloneLiteral(MCloneLiteral *ins)
{
JS_ASSERT(ins->type() == MIRType_Object);
JS_ASSERT(ins->input()->type() == MIRType_Object);
LCloneLiteral *lir = new(alloc()) LCloneLiteral(useRegisterAtStart(ins->input()));
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitParameter(MParameter *param)
{

View File

@ -61,6 +61,7 @@ class LIRGenerator : public LIRGeneratorSpecific
// Visitor hooks are explicit, to give CPU-specific versions a chance to
// intercept without a bunch of explicit gunk in the .cpp.
bool visitCloneLiteral(MCloneLiteral *ins);
bool visitParameter(MParameter *param);
bool visitCallee(MCallee *callee);
bool visitGoto(MGoto *ins);

View File

@ -539,6 +539,12 @@ MConstant::canProduceFloat32() const
return true;
}
MCloneLiteral *
MCloneLiteral::New(TempAllocator &alloc, MDefinition *obj)
{
return new(alloc) MCloneLiteral(obj);
}
void
MControlInstruction::printOpcode(FILE *fp) const
{

View File

@ -999,6 +999,27 @@ class MConstant : public MNullaryInstruction
bool canProduceFloat32() const;
};
// Deep clone a constant JSObject.
class MCloneLiteral
: public MUnaryInstruction,
public ObjectPolicy<0>
{
protected:
MCloneLiteral(MDefinition *obj)
: MUnaryInstruction(obj)
{
setResultType(MIRType_Object);
}
public:
INSTRUCTION_HEADER(CloneLiteral)
static MCloneLiteral *New(TempAllocator &alloc, MDefinition *obj);
TypePolicy *typePolicy() {
return this;
}
};
class MParameter : public MNullaryInstruction
{
int32_t index_;

View File

@ -34,7 +34,8 @@ class OptimizationInfo;
class MIRGenerator
{
public:
MIRGenerator(CompileCompartment *compartment, TempAllocator *alloc, MIRGraph *graph,
MIRGenerator(CompileCompartment *compartment, const JitCompileOptions &options,
TempAllocator *alloc, MIRGraph *graph,
CompileInfo *info, const OptimizationInfo *optimizationInfo);
TempAllocator &alloc() {
@ -163,6 +164,9 @@ class MIRGenerator
public:
AsmJSPerfSpewer &perfSpewer() { return asmJSPerfSpewer_; }
#endif
public:
const JitCompileOptions options;
};
} // namespace jit

View File

@ -16,7 +16,7 @@
using namespace js;
using namespace js::jit;
MIRGenerator::MIRGenerator(CompileCompartment *compartment,
MIRGenerator::MIRGenerator(CompileCompartment *compartment, const JitCompileOptions &options,
TempAllocator *alloc, MIRGraph *graph, CompileInfo *info,
const OptimizationInfo *optimizationInfo)
: compartment(compartment),
@ -31,7 +31,8 @@ MIRGenerator::MIRGenerator(CompileCompartment *compartment,
asmJSHeapAccesses_(*alloc),
asmJSGlobalAccesses_(*alloc),
minAsmJSHeapLength_(AsmJSAllocationGranularity),
modifiesFrameArguments_(false)
modifiesFrameArguments_(false),
options(options)
{ }
bool

View File

@ -12,6 +12,7 @@ namespace jit {
#define MIR_OPCODE_LIST(_) \
_(Constant) \
_(CloneLiteral) \
_(Parameter) \
_(Callee) \
_(TableSwitch) \

View File

@ -112,6 +112,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
// obviously safe for now. We can loosen as we need.
SAFE_OP(Constant)
UNSAFE_OP(CloneLiteral)
SAFE_OP(Parameter)
SAFE_OP(Callee)
SAFE_OP(TableSwitch)

View File

@ -2500,6 +2500,12 @@ JS::CompartmentOptions::asmJS(JSContext *cx) const
return asmJSOverride_.get(cx->options().asmJS());
}
bool
JS::CompartmentOptions::cloneSingletons(JSContext *cx) const
{
return cloneSingletonsOverride_.get(cx->options().cloneSingletons());
}
JS::CompartmentOptions &
JS::CompartmentOptions::setZone(ZoneSpecifier spec)
{

View File

@ -1462,7 +1462,8 @@ class JS_PUBLIC_API(ContextOptions) {
baseline_(false),
typeInference_(false),
ion_(false),
asmJS_(false)
asmJS_(false),
cloneSingletons_(false)
{
}
@ -1586,6 +1587,16 @@ class JS_PUBLIC_API(ContextOptions) {
return *this;
}
bool cloneSingletons() const { return cloneSingletons_; }
ContextOptions &setCloneSingletons(bool flag) {
cloneSingletons_ = flag;
return *this;
}
ContextOptions &toggleCloneSingletons() {
cloneSingletons_ = !cloneSingletons_;
return *this;
}
private:
bool extraWarnings_ : 1;
bool werror_ : 1;
@ -1599,6 +1610,7 @@ class JS_PUBLIC_API(ContextOptions) {
bool typeInference_ : 1;
bool ion_ : 1;
bool asmJS_ : 1;
bool cloneSingletons_ : 1;
};
JS_PUBLIC_API(ContextOptions &)
@ -2611,6 +2623,7 @@ class JS_PUBLIC_API(CompartmentOptions)
: version_(JSVERSION_UNKNOWN)
, invisibleToDebugger_(false)
, mergeable_(false)
, singletonsAsTemplates_(true)
{
zone_.spec = JS::FreshZone;
}
@ -2654,6 +2667,9 @@ class JS_PUBLIC_API(CompartmentOptions)
bool asmJS(JSContext *cx) const;
Override &asmJSOverride() { return asmJSOverride_; }
bool cloneSingletons(JSContext *cx) const;
Override &cloneSingletonsOverride() { return cloneSingletonsOverride_; }
void *zonePointer() const {
JS_ASSERT(uintptr_t(zone_.pointer) > uintptr_t(JS::SystemZone));
return zone_.pointer;
@ -2662,6 +2678,13 @@ class JS_PUBLIC_API(CompartmentOptions)
CompartmentOptions &setZone(ZoneSpecifier spec);
CompartmentOptions &setSameZoneAs(JSObject *obj);
void setSingletonsAsValues() {
singletonsAsTemplates_ = false;
}
bool getSingletonsAsTemplates() const {
return singletonsAsTemplates_;
};
private:
JSVersion version_;
bool invisibleToDebugger_;
@ -2670,10 +2693,16 @@ class JS_PUBLIC_API(CompartmentOptions)
Override typeInferenceOverride_;
Override ionOverride_;
Override asmJSOverride_;
Override cloneSingletonsOverride_;
union {
ZoneSpecifier spec;
void *pointer; // js::Zone* is not exposed in the API.
} zone_;
// To XDR singletons, we need to ensure that all singletons are all used as
// templates, by making JSOP_OBJECT return a clone of the JSScript
// singleton, instead of returning the value which is baked in the JSScript.
bool singletonsAsTemplates_;
};
JS_PUBLIC_API(CompartmentOptions &)

View File

@ -1803,6 +1803,7 @@ js::CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto,
JSMSG_CANT_CLONE_OBJECT);
return nullptr;
}
RootedObject clone(cx, NewObjectWithGivenProto(cx, obj->getClass(), proto, parent));
if (!clone)
return nullptr;
@ -1824,6 +1825,287 @@ js::CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto,
return clone;
}
JSObject *
js::DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind)
{
/* NB: Keep this in sync with XDRObjectLiteral. */
JS_ASSERT(JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
JS_ASSERT(obj->is<JSObject>() || obj->is<ArrayObject>());
// Result of the clone function.
RootedObject clone(cx);
// Temporary element/slot which would be stored in the cloned object.
RootedValue v(cx);
RootedObject deepObj(cx);
if (obj->getClass() == &ArrayObject::class_) {
clone = NewDenseUnallocatedArray(cx, obj->as<ArrayObject>().length(), nullptr, newKind);
} else {
// Object literals are tenured by default as holded by the JSScript.
JS_ASSERT(obj->isTenured());
AllocKind kind = obj->tenuredGetAllocKind();
Rooted<TypeObject*> typeObj(cx, obj->getType(cx));
if (!typeObj)
return nullptr;
RootedObject parent(cx, obj->getParent());
clone = NewObjectWithGivenProto(cx, &JSObject::class_, typeObj->proto().toObject(),
parent, kind, newKind);
}
// Allocate the same number of slots.
if (!clone || !clone->ensureElements(cx, obj->getDenseCapacity()))
return nullptr;
// Copy the number of initialized elements.
uint32_t initialized = obj->getDenseInitializedLength();
if (initialized)
clone->setDenseInitializedLength(initialized);
// Recursive copy of dense element.
for (uint32_t i = 0; i < initialized; ++i) {
v = obj->getDenseElement(i);
if (v.isObject()) {
deepObj = &v.toObject();
deepObj = js::DeepCloneObjectLiteral(cx, deepObj, newKind);
if (!deepObj) {
JS_ReportOutOfMemory(cx);
return nullptr;
}
v.setObject(*deepObj);
}
clone->initDenseElement(i, v);
}
JS_ASSERT(obj->compartment() == clone->compartment());
JS_ASSERT(!obj->hasPrivate());
RootedShape shape(cx, obj->lastProperty());
size_t span = shape->slotSpan();
clone->setLastProperty(cx, clone, shape);
for (size_t i = 0; i < span; i++) {
v = obj->getSlot(i);
if (v.isObject()) {
deepObj = &v.toObject();
deepObj = js::DeepCloneObjectLiteral(cx, deepObj, newKind);
if (!deepObj)
return nullptr;
v.setObject(*deepObj);
}
clone->setSlot(i, v);
}
if (obj->getClass() == &ArrayObject::class_)
FixArrayType(cx, clone);
else
FixObjectType(cx, clone);
#ifdef DEBUG
Rooted<TypeObject*> typeObj(cx, obj->getType(cx));
Rooted<TypeObject*> cloneTypeObj(cx, clone->getType(cx));
if (!typeObj || !cloneTypeObj)
return nullptr;
JS_ASSERT(typeObj == cloneTypeObj);
#endif
return clone;
}
template<XDRMode mode>
bool
js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleObject obj)
{
/* NB: Keep this in sync with DeepCloneObjectLiteral. */
JSContext *cx = xdr->cx();
JS_ASSERT_IF(mode == XDR_ENCODE, JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
// Distinguish between objects and array classes.
uint32_t isArray = 0;
{
if (mode == XDR_ENCODE) {
JS_ASSERT(obj->is<JSObject>() || obj->is<ArrayObject>());
isArray = obj->getClass() == &ArrayObject::class_ ? 1 : 0;
}
if (!xdr->codeUint32(&isArray))
return false;
}
if (isArray) {
uint32_t length;
if (mode == XDR_ENCODE)
length = obj->as<ArrayObject>().length();
if (!xdr->codeUint32(&length))
return false;
if (mode == XDR_DECODE)
obj.set(NewDenseUnallocatedArray(cx, length, NULL, js::MaybeSingletonObject));
} else {
// Code the alloc kind of the object.
AllocKind kind;
{
if (mode == XDR_ENCODE) {
JS_ASSERT(obj->getClass() == &JSObject::class_);
JS_ASSERT(obj->isTenured());
kind = obj->tenuredGetAllocKind();
}
if (!xdr->codeEnum32(&kind))
return false;
if (mode == XDR_DECODE)
obj.set(NewBuiltinClassInstance(cx, &JSObject::class_, kind, js::MaybeSingletonObject));
}
}
{
uint32_t capacity;
if (mode == XDR_ENCODE)
capacity = obj->getDenseCapacity();
if (!xdr->codeUint32(&capacity))
return false;
if (mode == XDR_DECODE) {
if (!obj->ensureElements(cx, capacity)) {
JS_ReportOutOfMemory(cx);
return false;
}
}
}
uint32_t initialized;
{
if (mode == XDR_ENCODE)
initialized = obj->getDenseInitializedLength();
if (!xdr->codeUint32(&initialized))
return false;
if (mode == XDR_DECODE) {
if (initialized)
obj->setDenseInitializedLength(initialized);
}
}
RootedValue tmpValue(cx);
// Recursively copy dense elements.
{
for (unsigned i = 0; i < initialized; i++) {
if (mode == XDR_ENCODE)
tmpValue = obj->getDenseElement(i);
if (!xdr->codeConstValue(&tmpValue))
return false;
if (mode == XDR_DECODE)
obj->initDenseElement(i, tmpValue);
}
}
JS_ASSERT(!obj->hasPrivate());
RootedShape shape(cx, obj->lastProperty());
// Code the number of slots in the vector.
unsigned nslot = 0;
// Code ids of the object in order. As opposed to DeepCloneObjectLiteral we
// cannot just re-use the shape of the original bytecode value and we have
// to write down the shape as well as the corresponding values. Ideally we
// would have a mechanism to serialize the shape too.
js::AutoIdVector ids(cx);
{
if (mode == XDR_ENCODE && !shape->isEmptyShape()) {
nslot = shape->slotSpan();
if (!ids.reserve(nslot))
return false;
for (unsigned i = 0; i < nslot; i++)
ids.infallibleAppend(JSID_VOID);
for (Shape::Range<NoGC> it(shape); !it.empty(); it.popFront()) {
// If we have reached the native property of the array class, we
// exit as the remaining would only be reserved slots.
if (!it.front().hasSlot()) {
JS_ASSERT(isArray);
break;
}
JS_ASSERT(it.front().hasDefaultGetter());
ids[it.front().slot()] = it.front().propid();
}
}
if (!xdr->codeUint32(&nslot))
return false;
RootedAtom atom(cx);
RootedId id(cx);
uint32_t idType = 0;
for (unsigned i = 0; i < nslot; i++) {
if (mode == XDR_ENCODE) {
id = ids[i];
if (JSID_IS_INT(id))
idType = JSID_TYPE_INT;
else if (JSID_IS_ATOM(id))
idType = JSID_TYPE_STRING;
else
MOZ_ASSUME_UNREACHABLE("Object property is not yet supported by XDR.");
tmpValue = obj->getSlot(i);
}
if (!xdr->codeUint32(&idType))
return false;
if (idType == JSID_TYPE_STRING) {
if (mode == XDR_ENCODE)
atom = JSID_TO_ATOM(id);
if (!XDRAtom(xdr, &atom))
return false;
if (mode == XDR_DECODE)
id = AtomToId(atom);
} else {
JS_ASSERT(idType == JSID_TYPE_INT);
uint32_t indexVal;
if (mode == XDR_ENCODE)
indexVal = uint32_t(JSID_TO_INT(id));
if (!xdr->codeUint32(&indexVal))
return false;
if (mode == XDR_DECODE)
id = INT_TO_JSID(int32_t(indexVal));
}
if (!xdr->codeConstValue(&tmpValue))
return false;
if (mode == XDR_DECODE) {
if (!DefineNativeProperty(cx, obj, id, tmpValue, NULL, NULL,
JSPROP_ENUMERATE, 0, 0)) {
return false;
}
}
}
JS_ASSERT_IF(mode == XDR_DECODE, !obj->inDictionaryMode());
}
if (mode == XDR_DECODE) {
if (isArray)
FixArrayType(cx, obj);
else
FixObjectType(cx, obj);
}
return true;
}
template bool
js::XDRObjectLiteral(XDRState<XDR_ENCODE> *xdr, MutableHandleObject obj);
template bool
js::XDRObjectLiteral(XDRState<XDR_DECODE> *xdr, MutableHandleObject obj);
JSObject *
js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj)
{

View File

@ -22,6 +22,7 @@
#include "js/GCAPI.h"
#include "vm/ObjectImpl.h"
#include "vm/Shape.h"
#include "vm/Xdr.h"
namespace JS {
struct ObjectsExtraSizes;
@ -1403,6 +1404,9 @@ CreateThis(JSContext *cx, const js::Class *clasp, js::HandleObject callee);
extern JSObject *
CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto, HandleObject parent);
extern JSObject *
DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind = GenericObject);
/*
* Flags for the defineHow parameter of js_DefineNativeProperty.
*/
@ -1578,6 +1582,10 @@ ToObjectFromStack(JSContext *cx, HandleValue vp)
return ToObjectSlow(cx, vp, true);
}
template<XDRMode mode>
bool
XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleObject obj);
extern JSObject *
CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj);

View File

@ -22,6 +22,7 @@
#include "jscntxt.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jstypes.h"
#include "jsutil.h"
@ -305,7 +306,9 @@ js::XDRScriptConst(XDRState<mode> *xdr, MutableHandleValue vp)
SCRIPT_TRUE = 3,
SCRIPT_FALSE = 4,
SCRIPT_NULL = 5,
SCRIPT_VOID = 6
SCRIPT_OBJECT = 6,
SCRIPT_VOID = 7,
SCRIPT_HOLE = 8
};
uint32_t tag;
@ -322,6 +325,10 @@ js::XDRScriptConst(XDRState<mode> *xdr, MutableHandleValue vp)
tag = SCRIPT_FALSE;
} else if (vp.isNull()) {
tag = SCRIPT_NULL;
} else if (vp.isObject()) {
tag = SCRIPT_OBJECT;
} else if (vp.isMagic(JS_ELEMENTS_HOLE)) {
tag = SCRIPT_HOLE;
} else {
JS_ASSERT(vp.isUndefined());
tag = SCRIPT_VOID;
@ -374,10 +381,26 @@ js::XDRScriptConst(XDRState<mode> *xdr, MutableHandleValue vp)
if (mode == XDR_DECODE)
vp.set(NullValue());
break;
case SCRIPT_OBJECT: {
RootedObject obj(cx);
if (mode == XDR_ENCODE)
obj = &vp.toObject();
if (!XDRObjectLiteral(xdr, &obj))
return false;
if (mode == XDR_DECODE)
vp.setObject(*obj);
break;
}
case SCRIPT_VOID:
if (mode == XDR_DECODE)
vp.set(UndefinedValue());
break;
case SCRIPT_HOLE:
if (mode == XDR_DECODE)
vp.setMagic(JS_ELEMENTS_HOLE);
break;
}
return true;
}
@ -405,6 +428,12 @@ FindBlockIndex(JSScript *script, StaticBlockObject &block)
static bool
SaveSharedScriptData(ExclusiveContext *, Handle<JSScript *>, SharedScriptData *, uint32_t);
enum XDRClassKind {
CK_BlockObject = 0,
CK_JSFunction = 1,
CK_JSObject = 2
};
template<XDRMode mode>
bool
js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
@ -728,15 +757,53 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
*/
for (i = 0; i != nobjects; ++i) {
HeapPtr<JSObject> *objp = &script->objects()->vector[i];
uint32_t isBlock;
XDRClassKind classk;
if (mode == XDR_ENCODE) {
JSObject *obj = *objp;
JS_ASSERT(obj->is<JSFunction>() || obj->is<StaticBlockObject>());
isBlock = obj->is<BlockObject>() ? 1 : 0;
if (obj->is<BlockObject>())
classk = CK_BlockObject;
else if (obj->is<JSFunction>())
classk = CK_JSFunction;
else if (obj->is<JSObject>() || obj->is<ArrayObject>())
classk = CK_JSObject;
else
MOZ_ASSUME_UNREACHABLE("Cannot encode this class of object.");
}
if (!xdr->codeUint32(&isBlock))
if (!xdr->codeEnum32(&classk))
return false;
if (isBlock == 0) {
switch (classk) {
case CK_BlockObject: {
/* Code the nested block's enclosing scope. */
uint32_t blockEnclosingScopeIndex = 0;
if (mode == XDR_ENCODE) {
if (StaticBlockObject *block = (*objp)->as<StaticBlockObject>().enclosingBlock())
blockEnclosingScopeIndex = FindBlockIndex(script, *block);
else
blockEnclosingScopeIndex = UINT32_MAX;
}
if (!xdr->codeUint32(&blockEnclosingScopeIndex))
return false;
Rooted<JSObject*> blockEnclosingScope(cx);
if (mode == XDR_DECODE) {
if (blockEnclosingScopeIndex != UINT32_MAX) {
JS_ASSERT(blockEnclosingScopeIndex < i);
blockEnclosingScope = script->objects()->vector[blockEnclosingScopeIndex];
} else {
blockEnclosingScope = fun;
}
}
Rooted<StaticBlockObject*> tmp(cx, static_cast<StaticBlockObject *>(objp->get()));
if (!XDRStaticBlockObject(xdr, blockEnclosingScope, tmp.address()))
return false;
*objp = tmp;
break;
}
case CK_JSFunction: {
/* Code the nested function's enclosing scope. */
uint32_t funEnclosingScopeIndex = 0;
if (mode == XDR_ENCODE) {
@ -769,32 +836,22 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp))
return false;
*objp = tmp;
} else {
/* Code the nested block's enclosing scope. */
JS_ASSERT(isBlock == 1);
uint32_t blockEnclosingScopeIndex = 0;
if (mode == XDR_ENCODE) {
if (StaticBlockObject *block = (*objp)->as<StaticBlockObject>().enclosingBlock())
blockEnclosingScopeIndex = FindBlockIndex(script, *block);
else
blockEnclosingScopeIndex = UINT32_MAX;
}
if (!xdr->codeUint32(&blockEnclosingScopeIndex))
return false;
Rooted<JSObject*> blockEnclosingScope(cx);
if (mode == XDR_DECODE) {
if (blockEnclosingScopeIndex != UINT32_MAX) {
JS_ASSERT(blockEnclosingScopeIndex < i);
blockEnclosingScope = script->objects()->vector[blockEnclosingScopeIndex];
} else {
blockEnclosingScope = fun;
}
}
break;
}
Rooted<StaticBlockObject*> tmp(cx, static_cast<StaticBlockObject *>(objp->get()));
if (!XDRStaticBlockObject(xdr, blockEnclosingScope, tmp.address()))
case CK_JSObject: {
/* Code object literal. */
RootedObject tmp(cx, *objp);
if (!XDRObjectLiteral(xdr, &tmp))
return false;
*objp = tmp;
break;
}
default: {
MOZ_ASSUME_UNREACHABLE("Unknown class kind.");
return false;
}
}
}

View File

@ -2772,7 +2772,19 @@ CASE(JSOP_STRING)
END_CASE(JSOP_STRING)
CASE(JSOP_OBJECT)
PUSH_OBJECT(*script->getObject(REGS.pc));
{
RootedObject &ref = rootObject0;
ref = script->getObject(REGS.pc);
if (JS::CompartmentOptionsRef(cx).cloneSingletons(cx)) {
JSObject *obj = js::DeepCloneObjectLiteral(cx, ref, js::MaybeSingletonObject);
if (!obj)
goto error;
PUSH_OBJECT(*obj);
} else {
JS::CompartmentOptionsRef(cx).setSingletonsAsValues();
PUSH_OBJECT(*ref);
}
}
END_CASE(JSOP_OBJECT)
CASE(JSOP_REGEXP)

View File

@ -8,6 +8,7 @@
#define vm_Xdr_h
#include "mozilla/Endian.h"
#include "mozilla/TypeTraits.h"
#include "jsatom.h"
@ -22,7 +23,7 @@ namespace js {
* and saved versions. If deserialization fails, the data should be
* invalidated if possible.
*/
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 164);
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 165);
class XDRBuffer {
public:
@ -161,6 +162,24 @@ class XDRState {
return true;
}
/*
* Use SFINAE to refuse any specialization which is not an enum. Uses of
* this function do not have to specialize the type of the enumerated field
* as C++ will extract the parameterized from the argument list.
*/
template <typename T>
bool codeEnum32(T *val, typename mozilla::EnableIf<mozilla::IsEnum<T>::value, T>::Type * = NULL)
{
uint32_t tmp;
if (mode == XDR_ENCODE)
tmp = *val;
if (!codeUint32(&tmp))
return false;
if (mode == XDR_DECODE)
*val = T(tmp);
return true;
}
bool codeDouble(double *dp) {
union DoublePun {
double d;