mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-03 10:33:33 +00:00
Merge mozilla-central into mozilla-inbound on a CLOSED TREE
This commit is contained in:
commit
4c2155d99e
@ -375,12 +375,12 @@ frontend::CompileFunctionBody(JSContext *cx, JSFunction *fun,
|
||||
* NB: do not use AutoLocalNameArray because it will release space
|
||||
* allocated from cx->tempLifoAlloc by DefineArg.
|
||||
*/
|
||||
Vector<JSAtom *> names(cx);
|
||||
BindingNames names(cx);
|
||||
if (!funbce.bindings.getLocalNameArray(cx, &names)) {
|
||||
fn = NULL;
|
||||
} else {
|
||||
for (unsigned i = 0; i < nargs; i++) {
|
||||
if (!DefineArg(fn, names[i], i, &funbce)) {
|
||||
if (!DefineArg(fn, names[i].maybeAtom, i, &funbce)) {
|
||||
fn = NULL;
|
||||
break;
|
||||
}
|
||||
|
@ -544,7 +544,7 @@ class GCConstList {
|
||||
Vector<Value> list;
|
||||
public:
|
||||
GCConstList(JSContext *cx) : list(cx) {}
|
||||
bool append(Value v) { return list.append(v); }
|
||||
bool append(Value v) { JS_ASSERT_IF(v.isString(), v.toString()->isAtom()); return list.append(v); }
|
||||
size_t length() const { return list.length(); }
|
||||
void finish(ConstArray *array);
|
||||
};
|
||||
|
24
js/src/jit-test/tests/basic/testScriptCloning.js
Normal file
24
js/src/jit-test/tests/basic/testScriptCloning.js
Normal file
@ -0,0 +1,24 @@
|
||||
var g = newGlobal('new-compartment');
|
||||
|
||||
g.f = new Function('return function(x) { return x }');
|
||||
assertEq(g.eval("clone(f)()(9)"), 9);
|
||||
|
||||
g.f = new Function('return function(x) { let(y = x+1) { return y } }');
|
||||
assertEq(g.eval("clone(f)()(9)"), 10);
|
||||
|
||||
g.f = new Function('return function(x) { let(y = x, z = 1) { return y+z } }');
|
||||
assertEq(g.eval("clone(f)()(9)"), 10);
|
||||
|
||||
g.f = new Function('return function(x) { return x.search(/ponies/) }');
|
||||
assertEq(g.eval("clone(f)()('123ponies')"), 3);
|
||||
|
||||
g.f = new Function('return function(x,y) { return x.search(/a/) + y.search(/b/) }');
|
||||
assertEq(g.eval("clone(f)()('12a','foo')"), 1);
|
||||
|
||||
g.f = new Function('return [function(x) x+2, function(y) let(z=y+1) z]');
|
||||
assertEq(g.eval("let ([f,g] = clone(f)()) f(g(4))"), 7);
|
||||
|
||||
g.f = new Function('return function(x) { switch(x) { case "a": return "b"; case null: return "c" } }');
|
||||
assertEq(g.eval("clone(f)()('a')"), "b");
|
||||
assertEq(g.eval("clone(f)()(null)"), "c");
|
||||
assertEq(g.eval("clone(f)()(3)"), undefined);
|
@ -667,14 +667,18 @@ bool
|
||||
js::XDRAtom(XDRState<mode> *xdr, JSAtom **atomp)
|
||||
{
|
||||
if (mode == XDR_ENCODE) {
|
||||
JSString *str = *atomp;
|
||||
return xdr->codeString(&str);
|
||||
uint32_t nchars = (*atomp)->length();
|
||||
if (!xdr->codeUint32(&nchars))
|
||||
return false;
|
||||
|
||||
jschar *chars = const_cast<jschar *>((*atomp)->getChars(xdr->cx()));
|
||||
if (!chars)
|
||||
return false;
|
||||
|
||||
return xdr->codeChars(chars, nchars);
|
||||
}
|
||||
|
||||
/*
|
||||
* Inline XDRState::codeString when decoding to avoid JSString allocation
|
||||
* for already existing atoms. See bug 321985.
|
||||
*/
|
||||
/* Avoid JSString allocation for already existing atoms. See bug 321985. */
|
||||
uint32_t nchars;
|
||||
if (!xdr->codeUint32(&nchars))
|
||||
return false;
|
||||
|
@ -436,7 +436,7 @@ JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun)
|
||||
extern JS_PUBLIC_API(uintptr_t *)
|
||||
JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp)
|
||||
{
|
||||
Vector<JSAtom *> localNames(cx);
|
||||
BindingNames localNames(cx);
|
||||
if (!fun->script()->bindings.getLocalNameArray(cx, &localNames))
|
||||
return NULL;
|
||||
|
||||
@ -449,15 +449,16 @@ JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JS_ASSERT(sizeof(*names) == sizeof(*localNames.begin()));
|
||||
js_memcpy(names, localNames.begin(), localNames.length() * sizeof(*names));
|
||||
for (size_t i = 0; i < localNames.length(); i++)
|
||||
names[i] = reinterpret_cast<uintptr_t>(localNames[i].maybeAtom);
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(JSAtom *)
|
||||
JS_LocalNameToAtom(uintptr_t w)
|
||||
{
|
||||
return JS_LOCAL_NAME_TO_ATOM(w);
|
||||
return reinterpret_cast<JSAtom *>(w);
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(JSString *)
|
||||
|
@ -374,6 +374,7 @@ template<XDRMode mode>
|
||||
bool
|
||||
js::XDRInterpretedFunction(XDRState<mode> *xdr, JSObject **objp, JSScript *parentScript)
|
||||
{
|
||||
/* NB: Keep this in sync with CloneInterpretedFunction. */
|
||||
JSFunction *fun;
|
||||
JSAtom *atom;
|
||||
uint32_t firstword; /* flag telling whether fun->atom is non-null,
|
||||
@ -442,6 +443,35 @@ js::XDRInterpretedFunction(XDRState<XDR_ENCODE> *xdr, JSObject **objp, JSScript
|
||||
template bool
|
||||
js::XDRInterpretedFunction(XDRState<XDR_DECODE> *xdr, JSObject **objp, JSScript *parentScript);
|
||||
|
||||
JSObject *
|
||||
js::CloneInterpretedFunction(JSContext *cx, JSFunction *srcFun)
|
||||
{
|
||||
/* NB: Keep this in sync with XDRInterpretedFunction. */
|
||||
|
||||
RootedVarObject parent(cx, NULL);
|
||||
JSFunction *clone = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, parent, NULL);
|
||||
if (!clone)
|
||||
return NULL;
|
||||
if (!clone->clearParent(cx))
|
||||
return NULL;
|
||||
if (!clone->clearType(cx))
|
||||
return NULL;
|
||||
|
||||
JSScript *clonedScript = CloneScript(cx, srcFun->script());
|
||||
if (!clonedScript)
|
||||
return NULL;
|
||||
|
||||
clone->nargs = srcFun->nargs;
|
||||
clone->flags = srcFun->flags;
|
||||
clone->atom.init(srcFun->atom);
|
||||
clone->initScript(clonedScript);
|
||||
if (!clonedScript->typeSetFunction(cx, clone))
|
||||
return NULL;
|
||||
|
||||
js_CallNewScriptHook(cx, clone->script(), clone);
|
||||
return clone;
|
||||
}
|
||||
|
||||
/*
|
||||
* [[HasInstance]] internal method for Function objects: fetch the .prototype
|
||||
* property of its 'this' parameter, and walks the prototype chain of v (only
|
||||
|
@ -121,9 +121,6 @@ struct JSFunction : public JSObject
|
||||
/* uint16_t representation bounds number of call object dynamic slots. */
|
||||
enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };
|
||||
|
||||
#define JS_LOCAL_NAME_TO_ATOM(nameWord) ((JSAtom *) ((nameWord) & ~uintptr_t(1)))
|
||||
#define JS_LOCAL_NAME_IS_CONST(nameWord) ((((nameWord) & uintptr_t(1))) != 0)
|
||||
|
||||
/*
|
||||
* For an interpreted function, accessors for the initial scope object of
|
||||
* activations (stack frames) of the function.
|
||||
@ -314,6 +311,9 @@ template<XDRMode mode>
|
||||
bool
|
||||
XDRInterpretedFunction(XDRState<mode> *xdr, JSObject **objp, JSScript *parentScript);
|
||||
|
||||
extern JSObject *
|
||||
CloneInterpretedFunction(JSContext *cx, JSFunction *fun);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
extern JSBool
|
||||
|
@ -1091,7 +1091,7 @@ struct JSPrinter
|
||||
jsbytecode *dvgfence; /* DecompileExpression fencepost */
|
||||
jsbytecode **pcstack; /* DecompileExpression modeled stack */
|
||||
JSFunction *fun; /* interpreted function */
|
||||
Vector<JSAtom *> *localNames; /* argument and variable names */
|
||||
BindingNames *localNames; /* argument and variable names */
|
||||
Vector<DecompiledOpcode> *decompiledOpcodes; /* optional state for decompiled ops */
|
||||
|
||||
DecompiledOpcode &decompiled(jsbytecode *pc) {
|
||||
@ -1122,7 +1122,7 @@ js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun,
|
||||
jp->localNames = NULL;
|
||||
jp->decompiledOpcodes = NULL;
|
||||
if (fun && fun->isInterpreted() && fun->script()->bindings.count() > 0) {
|
||||
jp->localNames = cx->new_<Vector<JSAtom *> >(cx);
|
||||
jp->localNames = cx->new_<BindingNames>(cx);
|
||||
if (!jp->localNames || !fun->script()->bindings.getLocalNameArray(cx, jp->localNames)) {
|
||||
js_DestroyPrinter(jp);
|
||||
return NULL;
|
||||
@ -1771,7 +1771,7 @@ GetArgOrVarAtom(JSPrinter *jp, unsigned slot)
|
||||
{
|
||||
LOCAL_ASSERT_RV(jp->fun, NULL);
|
||||
LOCAL_ASSERT_RV(slot < jp->fun->script()->bindings.count(), NULL);
|
||||
JSAtom *name = (*jp->localNames)[slot];
|
||||
JSAtom *name = (*jp->localNames)[slot].maybeAtom;
|
||||
#if !JS_HAS_DESTRUCTURING
|
||||
LOCAL_ASSERT_RV(name, NULL);
|
||||
#endif
|
||||
@ -4667,8 +4667,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, int nb)
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
if (sn && SN_TYPE(sn) == SRC_GENEXP) {
|
||||
Vector<JSAtom *> *innerLocalNames;
|
||||
Vector<JSAtom *> *outerLocalNames;
|
||||
BindingNames *innerLocalNames;
|
||||
BindingNames *outerLocalNames;
|
||||
JSScript *inner, *outer;
|
||||
Vector<DecompiledOpcode> *decompiledOpcodes;
|
||||
SprintStack ss2(cx);
|
||||
@ -4684,7 +4684,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, int nb)
|
||||
*/
|
||||
LifoAllocScope las(&cx->tempLifoAlloc());
|
||||
if (fun->script()->bindings.count() > 0) {
|
||||
innerLocalNames = cx->new_<Vector<JSAtom *> >(cx);
|
||||
innerLocalNames = cx->new_<BindingNames>(cx);
|
||||
if (!innerLocalNames ||
|
||||
!fun->script()->bindings.getLocalNameArray(cx, innerLocalNames))
|
||||
{
|
||||
|
@ -76,6 +76,8 @@
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
#include "vm/RegExpObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
using namespace js::frontend;
|
||||
@ -214,12 +216,13 @@ Bindings::callObjectShape(JSContext *cx) const
|
||||
}
|
||||
|
||||
bool
|
||||
Bindings::getLocalNameArray(JSContext *cx, Vector<JSAtom *> *namesp)
|
||||
Bindings::getLocalNameArray(JSContext *cx, BindingNames *namesp)
|
||||
{
|
||||
JS_ASSERT(lastBinding);
|
||||
JS_ASSERT(count() > 0);
|
||||
if (count() == 0)
|
||||
return true;
|
||||
|
||||
Vector<JSAtom *> &names = *namesp;
|
||||
BindingNames &names = *namesp;
|
||||
JS_ASSERT(names.empty());
|
||||
|
||||
unsigned n = count();
|
||||
@ -229,7 +232,7 @@ Bindings::getLocalNameArray(JSContext *cx, Vector<JSAtom *> *namesp)
|
||||
#ifdef DEBUG
|
||||
JSAtom * const POISON = reinterpret_cast<JSAtom *>(0xdeadbeef);
|
||||
for (unsigned i = 0; i < n; i++)
|
||||
names[i] = POISON;
|
||||
names[i].maybeAtom = POISON;
|
||||
#endif
|
||||
|
||||
for (Shape::Range r = lastBinding->all(); !r.empty(); r.popFront()) {
|
||||
@ -238,23 +241,25 @@ Bindings::getLocalNameArray(JSContext *cx, Vector<JSAtom *> *namesp)
|
||||
|
||||
if (shape.getter() == CallObject::getArgOp) {
|
||||
JS_ASSERT(index < nargs);
|
||||
names[index].kind = ARGUMENT;
|
||||
} else {
|
||||
JS_ASSERT(index < nvars);
|
||||
index += nargs;
|
||||
names[index].kind = shape.writable() ? VARIABLE : CONSTANT;
|
||||
}
|
||||
|
||||
if (JSID_IS_ATOM(shape.propid())) {
|
||||
names[index] = JSID_TO_ATOM(shape.propid());
|
||||
names[index].maybeAtom = JSID_TO_ATOM(shape.propid());
|
||||
} else {
|
||||
JS_ASSERT(JSID_IS_INT(shape.propid()));
|
||||
JS_ASSERT(shape.getter() == CallObject::getArgOp);
|
||||
names[index] = NULL;
|
||||
names[index].maybeAtom = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
for (unsigned i = 0; i < n; i++)
|
||||
JS_ASSERT(names[i] != POISON);
|
||||
JS_ASSERT(names[i].maybeAtom != POISON);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
@ -308,7 +313,7 @@ XDRScriptConst(XDRState<mode> *xdr, HeapValue *vp)
|
||||
enum ConstTag {
|
||||
SCRIPT_INT = 0,
|
||||
SCRIPT_DOUBLE = 1,
|
||||
SCRIPT_STRING = 2,
|
||||
SCRIPT_ATOM = 2,
|
||||
SCRIPT_TRUE = 3,
|
||||
SCRIPT_FALSE = 4,
|
||||
SCRIPT_NULL = 5,
|
||||
@ -322,7 +327,7 @@ XDRScriptConst(XDRState<mode> *xdr, HeapValue *vp)
|
||||
} else if (vp->isDouble()) {
|
||||
tag = SCRIPT_DOUBLE;
|
||||
} else if (vp->isString()) {
|
||||
tag = SCRIPT_STRING;
|
||||
tag = SCRIPT_ATOM;
|
||||
} else if (vp->isTrue()) {
|
||||
tag = SCRIPT_TRUE;
|
||||
} else if (vp->isFalse()) {
|
||||
@ -359,14 +364,14 @@ XDRScriptConst(XDRState<mode> *xdr, HeapValue *vp)
|
||||
vp->init(DoubleValue(d));
|
||||
break;
|
||||
}
|
||||
case SCRIPT_STRING: {
|
||||
JSString *str;
|
||||
case SCRIPT_ATOM: {
|
||||
JSAtom *atom;
|
||||
if (mode == XDR_ENCODE)
|
||||
str = vp->toString();
|
||||
if (!xdr->codeString(&str))
|
||||
atom = &vp->toString()->asAtom();
|
||||
if (!XDRAtom(xdr, &atom))
|
||||
return false;
|
||||
if (mode == XDR_DECODE)
|
||||
vp->init(StringValue(str));
|
||||
vp->init(StringValue(atom));
|
||||
break;
|
||||
}
|
||||
case SCRIPT_TRUE:
|
||||
@ -393,6 +398,8 @@ template<XDRMode mode>
|
||||
bool
|
||||
js::XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript)
|
||||
{
|
||||
/* NB: Keep this in sync with CloneScript. */
|
||||
|
||||
enum ScriptBits {
|
||||
NoScriptRval,
|
||||
SavedCallerFun,
|
||||
@ -465,13 +472,13 @@ js::XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript)
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector<JSAtom *> names(cx);
|
||||
BindingNames names(cx);
|
||||
if (mode == XDR_ENCODE) {
|
||||
if (!script->bindings.getLocalNameArray(cx, &names))
|
||||
return false;
|
||||
PodZero(bitmap, bitmapLength);
|
||||
for (unsigned i = 0; i < nameCount; i++) {
|
||||
if (i < nargs && names[i])
|
||||
if (i < nargs && names[i].maybeAtom)
|
||||
bitmap[i >> JS_BITS_PER_UINT32_LOG2] |= JS_BIT(i & (JS_BITS_PER_UINT32 - 1));
|
||||
}
|
||||
}
|
||||
@ -489,14 +496,14 @@ js::XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript)
|
||||
if (!bindings.addDestructuring(cx, &dummy))
|
||||
return false;
|
||||
} else {
|
||||
JS_ASSERT(!names[i]);
|
||||
JS_ASSERT(!names[i].maybeAtom);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
RootedVarAtom name(cx);
|
||||
if (mode == XDR_ENCODE)
|
||||
name = names[i];
|
||||
name = names[i].maybeAtom;
|
||||
if (!XDRAtom(xdr, name.address()))
|
||||
return false;
|
||||
if (mode == XDR_DECODE) {
|
||||
@ -1120,11 +1127,10 @@ JS_STATIC_ASSERT(sizeof(ConstArray) +
|
||||
< JSScript::INVALID_OFFSET);
|
||||
JS_STATIC_ASSERT(JSScript::INVALID_OFFSET <= 255);
|
||||
|
||||
JSScript *
|
||||
JSScript::NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
|
||||
uint32_t nobjects, uint32_t nregexps,
|
||||
uint32_t ntrynotes, uint32_t nconsts, uint32_t nglobals,
|
||||
uint16_t nClosedArgs, uint16_t nClosedVars, uint32_t nTypeSets, JSVersion version)
|
||||
static inline size_t
|
||||
ScriptDataSize(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
|
||||
uint32_t nobjects, uint32_t nregexps, uint32_t ntrynotes, uint32_t nconsts,
|
||||
uint32_t nglobals, uint16_t nClosedArgs, uint16_t nClosedVars)
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
@ -1146,16 +1152,33 @@ JSScript::NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t
|
||||
|
||||
size += length * sizeof(jsbytecode);
|
||||
size += nsrcnotes * sizeof(jssrcnote);
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* We assume that calloc aligns on sizeof(Value) if the size we ask to
|
||||
* allocate divides sizeof(Value).
|
||||
*/
|
||||
JS_STATIC_ASSERT(sizeof(Value) == sizeof(double));
|
||||
static inline uint8_t *
|
||||
AllocScriptData(JSContext *cx, size_t size)
|
||||
{
|
||||
uint8_t *data = static_cast<uint8_t *>(cx->calloc_(JS_ROUNDUP(size, sizeof(Value))));
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
JS_ASSERT(size_t(data) % sizeof(Value) == 0);
|
||||
return data;
|
||||
}
|
||||
|
||||
JSScript *
|
||||
JSScript::NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
|
||||
uint32_t nobjects, uint32_t nregexps,
|
||||
uint32_t ntrynotes, uint32_t nconsts, uint32_t nglobals,
|
||||
uint16_t nClosedArgs, uint16_t nClosedVars, uint32_t nTypeSets, JSVersion version)
|
||||
{
|
||||
size_t size = ScriptDataSize(cx, length, nsrcnotes, natoms, nobjects, nregexps,
|
||||
ntrynotes, nconsts, nglobals, nClosedArgs, nClosedVars);
|
||||
|
||||
uint8_t *data = AllocScriptData(cx, size);
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
JSScript *script = js_NewGCScript(cx);
|
||||
if (!script) {
|
||||
Foreground::free_(data);
|
||||
@ -1449,6 +1472,10 @@ JSScript::NewScriptFromEmitter(JSContext *cx, BytecodeEmitter *bce)
|
||||
Debugger::onNewScript(cx, script, compileAndGoGlobal);
|
||||
}
|
||||
|
||||
/*
|
||||
* initScriptCounts updates scriptCountsMap if necessary. The other script
|
||||
* maps in JSCompartment are populated lazily.
|
||||
*/
|
||||
if (cx->hasRunOption(JSOPTION_PCCOUNT))
|
||||
(void) script->initScriptCounts(cx);
|
||||
|
||||
@ -1738,28 +1765,186 @@ CurrentScriptFileLineOriginSlow(JSContext *cx, const char **file, unsigned *line
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
JSScript *
|
||||
js::CloneScript(JSContext *cx, JSScript *script)
|
||||
template <class T>
|
||||
static inline T *
|
||||
Rebase(JSScript *dst, JSScript *src, T *srcp)
|
||||
{
|
||||
JS_ASSERT(cx->compartment != script->compartment());
|
||||
size_t off = reinterpret_cast<uint8_t *>(srcp) - src->data;
|
||||
return reinterpret_cast<T *>(dst->data + off);
|
||||
}
|
||||
|
||||
/* Serialize script. */
|
||||
XDREncoder encoder(cx);
|
||||
JSScript *
|
||||
js::CloneScript(JSContext *cx, JSScript *src)
|
||||
{
|
||||
/* NB: Keep this in sync with XDRScript. */
|
||||
|
||||
if (!XDRScript(&encoder, &script, NULL))
|
||||
uint32_t nconsts = JSScript::isValidOffset(src->constsOffset) ? src->consts()->length : 0;
|
||||
uint32_t nobjects = JSScript::isValidOffset(src->objectsOffset) ? src->objects()->length : 0;
|
||||
uint32_t nregexps = JSScript::isValidOffset(src->regexpsOffset) ? src->regexps()->length : 0;
|
||||
uint32_t ntrynotes = JSScript::isValidOffset(src->trynotesOffset) ? src->trynotes()->length : 0;
|
||||
uint32_t nClosedArgs = src->numClosedArgs();
|
||||
uint32_t nClosedVars = src->numClosedVars();
|
||||
JS_ASSERT(!JSScript::isValidOffset(src->globalsOffset));
|
||||
uint32_t nglobals = 0;
|
||||
|
||||
/* Script data */
|
||||
|
||||
size_t size = ScriptDataSize(cx, src->length, src->numNotes(), src->natoms,
|
||||
nobjects, nregexps, ntrynotes, nconsts, nglobals,
|
||||
nClosedArgs, nClosedVars);
|
||||
|
||||
uint8_t *data = AllocScriptData(cx, size);
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
uint32_t nbytes;
|
||||
const void *p = encoder.getData(&nbytes);
|
||||
/* Bindings */
|
||||
|
||||
/* De-serialize script. */
|
||||
XDRDecoder decoder(cx, p, nbytes, cx->compartment->principals, script->originPrincipals);
|
||||
Bindings bindings(cx);
|
||||
BindingNames names(cx);
|
||||
if (!src->bindings.getLocalNameArray(cx, &names))
|
||||
return false;
|
||||
|
||||
JSScript *newScript;
|
||||
if (!XDRScript(&decoder, &newScript, NULL))
|
||||
for (unsigned i = 0; i < names.length(); ++i) {
|
||||
if (JSAtom *atom = names[i].maybeAtom) {
|
||||
if (!bindings.add(cx, RootedVarAtom(cx, atom), names[i].kind))
|
||||
return false;
|
||||
} else {
|
||||
uint16_t _;
|
||||
if (!bindings.addDestructuring(cx, &_))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bindings.ensureShape(cx))
|
||||
return false;
|
||||
bindings.makeImmutable();
|
||||
|
||||
/* Objects */
|
||||
|
||||
AutoObjectVector objects(cx);
|
||||
if (nobjects != 0) {
|
||||
HeapPtrObject *vector = src->objects()->vector;
|
||||
for (unsigned i = 0; i < nobjects; i++) {
|
||||
JSObject *clone = vector[i]->isStaticBlock()
|
||||
? CloneStaticBlockObject(cx, vector[i]->asStaticBlock(), objects, src)
|
||||
: CloneInterpretedFunction(cx, vector[i]->toFunction());
|
||||
if (!clone || !objects.append(clone))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* RegExps */
|
||||
|
||||
AutoObjectVector regexps(cx);
|
||||
for (unsigned i = 0; i < nregexps; i++) {
|
||||
HeapPtrObject *vector = src->regexps()->vector;
|
||||
for (unsigned i = 0; i < nregexps; i++) {
|
||||
JSObject *clone = CloneScriptRegExpObject(cx, vector[i]->asRegExp());
|
||||
if (!clone || !regexps.append(clone))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now that all fallible allocation is complete, create the GC thing. */
|
||||
|
||||
JSScript *dst = js_NewGCScript(cx);
|
||||
if (!dst) {
|
||||
Foreground::free_(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return newScript;
|
||||
PodZero(dst);
|
||||
|
||||
new (&dst->bindings) Bindings(cx);
|
||||
dst->bindings.transfer(cx, &bindings);
|
||||
|
||||
/* This assignment must occur before all the Rebase calls. */
|
||||
dst->data = data;
|
||||
memcpy(data, src->data, size);
|
||||
|
||||
dst->code = Rebase<jsbytecode>(dst, src, src->code);
|
||||
|
||||
/* Script filenames are runtime-wide. */
|
||||
dst->filename = src->filename;
|
||||
|
||||
/* Atoms are runtime-wide. */
|
||||
if (src->natoms != 0)
|
||||
dst->atoms = Rebase<HeapPtrAtom>(dst, src, src->atoms);
|
||||
|
||||
dst->principals = cx->compartment->principals;
|
||||
if (dst->principals)
|
||||
JS_HoldPrincipals(dst->principals);
|
||||
|
||||
/* Establish invariant: principals implies originPrincipals. */
|
||||
dst->originPrincipals = src->originPrincipals;
|
||||
if (!dst->originPrincipals)
|
||||
dst->originPrincipals = dst->principals;
|
||||
if (dst->originPrincipals)
|
||||
JS_HoldPrincipals(dst->originPrincipals);
|
||||
|
||||
dst->length = src->length;
|
||||
dst->lineno = src->lineno;
|
||||
dst->mainOffset = src->mainOffset;
|
||||
dst->natoms = src->natoms;
|
||||
dst->setVersion(src->getVersion());
|
||||
dst->nfixed = src->nfixed;
|
||||
dst->nTypeSets = src->nTypeSets;
|
||||
dst->nslots = src->nslots;
|
||||
dst->staticLevel = src->staticLevel;
|
||||
if (src->argumentsHasLocalBinding()) {
|
||||
dst->setArgumentsHasLocalBinding(src->argumentsLocalSlot());
|
||||
if (src->analyzedArgsUsage())
|
||||
dst->setNeedsArgsObj(src->needsArgsObj());
|
||||
}
|
||||
dst->constsOffset = src->constsOffset;
|
||||
dst->objectsOffset = src->objectsOffset;
|
||||
dst->regexpsOffset = src->regexpsOffset;
|
||||
dst->trynotesOffset = src->trynotesOffset;
|
||||
dst->globalsOffset = src->globalsOffset;
|
||||
dst->closedArgsOffset = src->closedArgsOffset;
|
||||
dst->closedVarsOffset = src->closedVarsOffset;
|
||||
dst->noScriptRval = src->noScriptRval;
|
||||
dst->savedCallerFun = src->savedCallerFun;
|
||||
dst->strictModeCode = src->strictModeCode;
|
||||
dst->compileAndGo = src->compileAndGo;
|
||||
dst->bindingsAccessedDynamically = src->bindingsAccessedDynamically;
|
||||
dst->hasSingletons = src->hasSingletons;
|
||||
dst->isGenerator = src->isGenerator;
|
||||
|
||||
/*
|
||||
* initScriptCounts updates scriptCountsMap if necessary. The other script
|
||||
* maps in JSCompartment are populated lazily.
|
||||
*/
|
||||
if (cx->hasRunOption(JSOPTION_PCCOUNT))
|
||||
(void) dst->initScriptCounts(cx);
|
||||
|
||||
if (nconsts != 0) {
|
||||
HeapValue *vector = Rebase<HeapValue>(dst, src, src->consts()->vector);
|
||||
dst->consts()->vector = vector;
|
||||
for (unsigned i = 0; i < nconsts; ++i)
|
||||
JS_ASSERT_IF(vector[i].isMarkable(), vector[i].toString()->isAtom());
|
||||
}
|
||||
if (nobjects != 0) {
|
||||
HeapPtrObject *vector = Rebase<HeapPtr<JSObject> >(dst, src, src->objects()->vector);
|
||||
dst->objects()->vector = vector;
|
||||
for (unsigned i = 0; i < nobjects; ++i)
|
||||
vector[i].init(objects[i]);
|
||||
}
|
||||
if (nregexps != 0) {
|
||||
HeapPtrObject *vector = Rebase<HeapPtr<JSObject> >(dst, src, src->regexps()->vector);
|
||||
dst->regexps()->vector = vector;
|
||||
for (unsigned i = 0; i < nregexps; ++i)
|
||||
vector[i].init(regexps[i]);
|
||||
}
|
||||
if (ntrynotes != 0)
|
||||
dst->trynotes()->vector = Rebase<JSTryNote>(dst, src, src->trynotes()->vector);
|
||||
if (nClosedArgs != 0)
|
||||
dst->closedArgs()->vector = Rebase<uint32_t>(dst, src, src->closedArgs()->vector);
|
||||
if (nClosedVars != 0)
|
||||
dst->closedVars()->vector = Rebase<uint32_t>(dst, src, src->closedVars()->vector);
|
||||
JS_ASSERT(nglobals == 0);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
DebugScript *
|
||||
|
@ -110,6 +110,13 @@ struct Shape;
|
||||
|
||||
enum BindingKind { NONE, ARGUMENT, VARIABLE, CONSTANT };
|
||||
|
||||
struct BindingName {
|
||||
JSAtom *maybeAtom;
|
||||
BindingKind kind;
|
||||
};
|
||||
|
||||
typedef Vector<BindingName, 32> BindingNames;
|
||||
|
||||
/*
|
||||
* Formal parameters and local variables are stored in a shape tree
|
||||
* path encapsulated within this class. This class represents bindings for
|
||||
@ -238,7 +245,7 @@ class Bindings
|
||||
* The name at an element will be null when the element is for an argument
|
||||
* corresponding to a destructuring pattern.
|
||||
*/
|
||||
bool getLocalNameArray(JSContext *cx, Vector<JSAtom *> *namesp);
|
||||
bool getLocalNameArray(JSContext *cx, BindingNames *namesp);
|
||||
|
||||
/*
|
||||
* Protect stored bindings from mutation. Subsequent attempts to add
|
||||
@ -585,6 +592,8 @@ struct JSScript : public js::gc::Cell
|
||||
JSVersion version);
|
||||
static JSScript *NewScriptFromEmitter(JSContext *cx, js::BytecodeEmitter *bce);
|
||||
|
||||
void setVersion(JSVersion v) { version = v; }
|
||||
|
||||
/* See TCF_ARGUMENTS_HAS_LOCAL_BINDING comment. */
|
||||
bool argumentsHasLocalBinding() const { return argsHasLocalBinding_; }
|
||||
jsbytecode *argumentsBytecode() const { JS_ASSERT(code[0] == JSOP_ARGUMENTS); return code; }
|
||||
|
@ -3676,12 +3676,12 @@ DebuggerObject_getParameterNames(JSContext *cx, unsigned argc, Value *vp)
|
||||
JS_ASSERT(fun->nargs == fun->script()->bindings.numArgs());
|
||||
|
||||
if (fun->nargs > 0) {
|
||||
Vector<JSAtom *> names(cx);
|
||||
BindingNames names(cx);
|
||||
if (!fun->script()->bindings.getLocalNameArray(cx, &names))
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < fun->nargs; i++) {
|
||||
JSAtom *name = names[i];
|
||||
JSAtom *name = names[i].maybeAtom;
|
||||
result->setDenseArrayElement(i, name ? StringValue(name) : UndefinedValue());
|
||||
}
|
||||
}
|
||||
|
@ -762,6 +762,8 @@ template<XDRMode mode>
|
||||
bool
|
||||
js::XDRScriptRegExpObject(XDRState<mode> *xdr, HeapPtrObject *objp)
|
||||
{
|
||||
/* NB: Keep this in sync with CloneScriptRegExpObject. */
|
||||
|
||||
RootedVarAtom source(xdr->cx());
|
||||
uint32_t flagsword = 0;
|
||||
|
||||
@ -793,3 +795,19 @@ js::XDRScriptRegExpObject(XDRState<XDR_ENCODE> *xdr, HeapPtrObject *objp);
|
||||
|
||||
template bool
|
||||
js::XDRScriptRegExpObject(XDRState<XDR_DECODE> *xdr, HeapPtrObject *objp);
|
||||
|
||||
JSObject *
|
||||
js::CloneScriptRegExpObject(JSContext *cx, RegExpObject &reobj)
|
||||
{
|
||||
/* NB: Keep this in sync with XDRScriptRegExpObject. */
|
||||
|
||||
RootedVarAtom source(cx, reobj.getSource());
|
||||
RegExpObject *clone = RegExpObject::createNoStatics(cx, source, reobj.getFlags(), NULL);
|
||||
if (!clone)
|
||||
return NULL;
|
||||
if (!clone->clearParent(cx))
|
||||
return false;
|
||||
if (!clone->clearType(cx))
|
||||
return false;
|
||||
return clone;
|
||||
}
|
||||
|
@ -474,6 +474,9 @@ template<XDRMode mode>
|
||||
bool
|
||||
XDRScriptRegExpObject(XDRState<mode> *xdr, HeapPtrObject *objp);
|
||||
|
||||
extern JSObject *
|
||||
CloneScriptRegExpObject(JSContext *cx, RegExpObject &re);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif
|
||||
|
@ -877,18 +877,23 @@ Class js::BlockClass = {
|
||||
|
||||
#define NO_PARENT_INDEX UINT32_MAX
|
||||
|
||||
/*
|
||||
* If there's a parent id, then get the parent out of our script's object
|
||||
* array. We know that we clone block objects in outer-to-inner order, which
|
||||
* means that getting the parent now will work.
|
||||
*/
|
||||
static uint32_t
|
||||
FindObjectIndex(ObjectArray *array, JSObject *obj)
|
||||
FindObjectIndex(JSScript *script, StaticBlockObject *maybeBlock)
|
||||
{
|
||||
size_t i;
|
||||
if (!maybeBlock || !JSScript::isValidOffset(script->objectsOffset))
|
||||
return NO_PARENT_INDEX;
|
||||
|
||||
if (array) {
|
||||
i = array->length;
|
||||
do {
|
||||
|
||||
if (array->vector[--i] == obj)
|
||||
return i;
|
||||
} while (i != 0);
|
||||
ObjectArray *objects = script->objects();
|
||||
HeapPtrObject *vector = objects->vector;
|
||||
unsigned length = objects->length;
|
||||
for (unsigned i = 0; i < length; ++i) {
|
||||
if (vector[i] == maybeBlock)
|
||||
return i;
|
||||
}
|
||||
|
||||
return NO_PARENT_INDEX;
|
||||
@ -898,6 +903,8 @@ template<XDRMode mode>
|
||||
bool
|
||||
js::XDRStaticBlockObject(XDRState<mode> *xdr, JSScript *script, StaticBlockObject **objp)
|
||||
{
|
||||
/* NB: Keep this in sync with CloneStaticBlockObject. */
|
||||
|
||||
JSContext *cx = xdr->cx();
|
||||
|
||||
StaticBlockObject *obj = NULL;
|
||||
@ -906,9 +913,7 @@ js::XDRStaticBlockObject(XDRState<mode> *xdr, JSScript *script, StaticBlockObjec
|
||||
uint32_t depthAndCount = 0;
|
||||
if (mode == XDR_ENCODE) {
|
||||
obj = *objp;
|
||||
parentId = script->hasObjects()
|
||||
? FindObjectIndex(script->objects(), obj->enclosingBlock())
|
||||
: NO_PARENT_INDEX;
|
||||
parentId = FindObjectIndex(script, obj->enclosingBlock());
|
||||
uint32_t depth = obj->stackDepth();
|
||||
JS_ASSERT(depth <= UINT16_MAX);
|
||||
count = obj->slotCount();
|
||||
@ -926,11 +931,6 @@ js::XDRStaticBlockObject(XDRState<mode> *xdr, JSScript *script, StaticBlockObjec
|
||||
return false;
|
||||
*objp = obj;
|
||||
|
||||
/*
|
||||
* If there's a parent id, then get the parent out of our script's
|
||||
* object array. We know that we XDR block object in outer-to-inner
|
||||
* order, which means that getting the parent now will work.
|
||||
*/
|
||||
obj->setEnclosingBlock(parentId == NO_PARENT_INDEX
|
||||
? NULL
|
||||
: &script->getObject(parentId)->asStaticBlock());
|
||||
@ -975,7 +975,8 @@ js::XDRStaticBlockObject(XDRState<mode> *xdr, JSScript *script, StaticBlockObjec
|
||||
}
|
||||
} else {
|
||||
AutoShapeVector shapes(cx);
|
||||
shapes.growBy(count);
|
||||
if (!shapes.growBy(count))
|
||||
return false;
|
||||
|
||||
for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
|
||||
const Shape *shape = &r.front();
|
||||
@ -1015,3 +1016,43 @@ js::XDRStaticBlockObject(XDRState<XDR_ENCODE> *xdr, JSScript *script, StaticBloc
|
||||
|
||||
template bool
|
||||
js::XDRStaticBlockObject(XDRState<XDR_DECODE> *xdr, JSScript *script, StaticBlockObject **objp);
|
||||
|
||||
JSObject *
|
||||
js::CloneStaticBlockObject(JSContext *cx, StaticBlockObject &srcBlock,
|
||||
const AutoObjectVector &objects, JSScript *src)
|
||||
{
|
||||
/* NB: Keep this in sync with XDRStaticBlockObject. */
|
||||
|
||||
StaticBlockObject *clone = StaticBlockObject::create(cx);
|
||||
if (!clone)
|
||||
return false;
|
||||
|
||||
uint32_t parentId = FindObjectIndex(src, srcBlock.enclosingBlock());
|
||||
clone->setEnclosingBlock(parentId == NO_PARENT_INDEX
|
||||
? NULL
|
||||
: &objects[parentId]->asStaticBlock());
|
||||
|
||||
clone->setStackDepth(srcBlock.stackDepth());
|
||||
|
||||
/* Shape::Range is reverse order, so build a list in forward order. */
|
||||
AutoShapeVector shapes(cx);
|
||||
if (!shapes.growBy(srcBlock.slotCount()))
|
||||
return false;
|
||||
for (Shape::Range r = srcBlock.lastProperty()->all(); !r.empty(); r.popFront())
|
||||
shapes[r.front().shortid()] = &r.front();
|
||||
|
||||
for (const Shape **p = shapes.begin(); p != shapes.end(); ++p) {
|
||||
jsid id = (*p)->propid();
|
||||
unsigned i = (*p)->shortid();
|
||||
|
||||
bool redeclared;
|
||||
if (!clone->addVar(cx, id, i, &redeclared)) {
|
||||
JS_ASSERT(!redeclared);
|
||||
return false;
|
||||
}
|
||||
|
||||
clone->setAliased(i, srcBlock.isAliased(i));
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
@ -299,6 +299,10 @@ template<XDRMode mode>
|
||||
bool
|
||||
XDRStaticBlockObject(XDRState<mode> *xdr, JSScript *script, StaticBlockObject **objp);
|
||||
|
||||
extern JSObject *
|
||||
CloneStaticBlockObject(JSContext *cx, StaticBlockObject &srcBlock,
|
||||
const AutoObjectVector &objects, JSScript *src);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* ScopeObject_h___ */
|
||||
|
@ -129,44 +129,6 @@ XDRState<mode>::codeChars(jschar *chars, size_t nchars)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert between a JS (Unicode) string and the XDR representation.
|
||||
*/
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
XDRState<mode>::codeString(JSString **strp)
|
||||
{
|
||||
uint32_t nchars;
|
||||
jschar *chars;
|
||||
|
||||
if (mode == XDR_ENCODE)
|
||||
nchars = (*strp)->length();
|
||||
if (!codeUint32(&nchars))
|
||||
return false;
|
||||
|
||||
if (mode == XDR_DECODE)
|
||||
chars = (jschar *) cx()->malloc_((nchars + 1) * sizeof(jschar));
|
||||
else
|
||||
chars = const_cast<jschar *>((*strp)->getChars(cx()));
|
||||
if (!chars)
|
||||
return false;
|
||||
|
||||
if (!codeChars(chars, nchars))
|
||||
goto bad;
|
||||
if (mode == XDR_DECODE) {
|
||||
chars[nchars] = 0;
|
||||
*strp = JS_NewUCString(cx(), chars, nchars);
|
||||
if (!*strp)
|
||||
goto bad;
|
||||
}
|
||||
return true;
|
||||
|
||||
bad:
|
||||
if (mode == XDR_DECODE)
|
||||
Foreground::free_(chars);
|
||||
return false;
|
||||
}
|
||||
|
||||
template<XDRMode mode>
|
||||
static bool
|
||||
VersionCheck(XDRState<mode> *xdr)
|
||||
|
@ -57,7 +57,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 - 114);
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 115);
|
||||
|
||||
class XDRBuffer {
|
||||
public:
|
||||
@ -287,7 +287,6 @@ class XDRState {
|
||||
}
|
||||
|
||||
bool codeChars(jschar *chars, size_t nchars);
|
||||
bool codeString(JSString **strp);
|
||||
|
||||
bool codeFunction(JSObject **objp);
|
||||
bool codeScript(JSScript **scriptp);
|
||||
|
Loading…
Reference in New Issue
Block a user