зеркало из https://github.com/mozilla/gecko-dev.git
Bug 920322 - XDR/Clone singletons. r=bhackett,jandem
This commit is contained in:
Родитель
eac3d50496
Коммит
469b6da4af
|
@ -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_))
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
_(Double) \
|
||||
_(Float32) \
|
||||
_(Value) \
|
||||
_(CloneLiteral) \
|
||||
_(Parameter) \
|
||||
_(Callee) \
|
||||
_(TableSwitch) \
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace jit {
|
|||
|
||||
#define MIR_OPCODE_LIST(_) \
|
||||
_(Constant) \
|
||||
_(CloneLiteral) \
|
||||
_(Parameter) \
|
||||
_(Callee) \
|
||||
_(TableSwitch) \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 &)
|
||||
|
|
282
js/src/jsobj.cpp
282
js/src/jsobj.cpp
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче