author | Brian Hackett <bhackett1024@gmail.com> |
Thu, 12 Apr 2012 09:23:51 -0700 | |
changeset 91613 | 4cfb132c7c16f6b76b9397091a7f54fc5e62d82a |
parent 91612 | 84351dbcd62d12769fb601e11cc098849faee073 |
child 91614 | 95fd0e0254396ec64d0f568e40ad58525fa28fb8 |
push id | 22465 |
push user | mak77@bonardo.net |
push date | Sat, 14 Apr 2012 11:58:29 +0000 |
treeherder | mozilla-central@6880c195054f [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | billm |
bugs | 714647 |
milestone | 14.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -221,16 +221,17 @@ EXPORTS_NAMESPACES += ds gc EXPORTS_ds = \ BitArray.h \ $(NULL) EXPORTS_gc = \ Statistics.h \ Barrier.h \ + Root.h \ $(NULL) ###################################################### # BEGIN include exported headers from the JS engine # # Ultimately, after cleansing INSTALLED_HEADERS, # these will be the ONLY headers exported by # the js engine
--- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -49,25 +49,25 @@ #include "vm/RegExpStatics-inl.h" using namespace js; using namespace js::types; class RegExpMatchBuilder { JSContext * const cx; - JSObject * const array; + RootedVarObject array; bool setProperty(JSAtom *name, Value v) { return !!js_DefineProperty(cx, array, ATOM_TO_JSID(name), &v, JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE); } public: - RegExpMatchBuilder(JSContext *cx, JSObject *array) : cx(cx), array(array) {} + RegExpMatchBuilder(JSContext *cx, JSObject *array) : cx(cx), array(cx, array) {} bool append(uint32_t index, Value v) { JS_ASSERT(!array->getOps()->getElement); return !!js_DefineElement(cx, array, index, &v, JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE); } bool setIndex(int index) { @@ -76,29 +76,31 @@ class RegExpMatchBuilder bool setInput(JSString *str) { JS_ASSERT(str); return setProperty(cx->runtime->atomState.inputAtom, StringValue(str)); } }; static bool -CreateRegExpMatchResult(JSContext *cx, JSString *input, const jschar *chars, size_t length, +CreateRegExpMatchResult(JSContext *cx, JSString *input_, const jschar *chars, size_t length, MatchPairs *matchPairs, Value *rval) { + RootedVarString input(cx, input_); + /* * Create the (slow) result array for a match. * * Array contents: * 0: matched string * 1..pairCount-1: paren matches * input: input string * index: start index for the match */ - JSObject *array = NewSlowEmptyArray(cx); + RootedVarObject array(cx, NewSlowEmptyArray(cx)); if (!array) return false; if (!input) { input = js_NewStringCopyN(cx, chars, length); if (!input) return false; } @@ -220,17 +222,18 @@ EscapeNakedForwardSlashes(JSContext *cx, * _ => pattern := ToString(pattern) if defined(pattern) else '' * flags := ToString(flags) if defined(flags) else '' */ static bool CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args) { if (args.length() == 0) { RegExpStatics *res = cx->regExpStatics(); - RegExpObject *reobj = builder.build(cx->runtime->emptyString, res->getFlags()); + RegExpObject *reobj = builder.build(RootedVarAtom(cx, cx->runtime->emptyString), + res->getFlags()); if (!reobj) return false; args.rval() = ObjectValue(*reobj); return true; } Value sourceValue = args[0]; @@ -267,17 +270,17 @@ CompileRegExpObject(JSContext *cx, RegEx /* * 'toSource' is a permanent read-only property, so this is equivalent * to executing RegExpObject::getSource on the unwrapped object. */ Value v; if (!sourceObj.getProperty(cx, cx->runtime->atomState.sourceAtom, &v)) return false; - RegExpObject *reobj = builder.build(&v.toString()->asAtom(), flags); + RegExpObject *reobj = builder.build(RootedVarAtom(cx, &v.toString()->asAtom()), flags); if (!reobj) return false; args.rval() = ObjectValue(*reobj); return true; } JSAtom *source; @@ -299,17 +302,17 @@ CompileRegExpObject(JSContext *cx, RegEx JSString *flagStr = ToString(cx, args[1]); if (!flagStr) return false; args[1].setString(flagStr); if (!ParseRegExpFlags(cx, flagStr, &flags)) return false; } - JSAtom *escapedSourceStr = EscapeNakedForwardSlashes(cx, source); + RootedVarAtom escapedSourceStr(cx, EscapeNakedForwardSlashes(cx, source)); if (!escapedSourceStr) return false; if (!js::detail::RegExpCode::checkSyntax(cx, NULL, escapedSourceStr)) return false; RegExpStatics *res = cx->regExpStatics(); RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags())); @@ -477,32 +480,32 @@ static JSPropertySpec regexp_static_prop {0,0,0,0,0} }; JSObject * js_InitRegExpClass(JSContext *cx, JSObject *obj) { JS_ASSERT(obj->isNative()); - GlobalObject *global = &obj->asGlobal(); + RootedVar<GlobalObject*> global(cx, &obj->asGlobal()); - JSObject *proto = global->createBlankPrototype(cx, &RegExpClass); + RootedVarObject proto(cx, global->createBlankPrototype(cx, &RegExpClass)); if (!proto) return NULL; proto->setPrivate(NULL); - RegExpObject *reproto = &proto->asRegExp(); - RegExpObjectBuilder builder(cx, reproto); - if (!builder.build(cx->runtime->emptyString, RegExpFlag(0))) + RegExpObjectBuilder builder(cx, &proto->asRegExp()); + if (!builder.build(RootedVarAtom(cx, cx->runtime->emptyString), RegExpFlag(0))) return NULL; if (!DefinePropertiesAndBrand(cx, proto, NULL, regexp_methods)) return NULL; - JSFunction *ctor = global->createConstructor(cx, regexp_construct, CLASS_ATOM(cx, RegExp), 2); + RootedVarFunction ctor(cx); + ctor = global->createConstructor(cx, regexp_construct, CLASS_ATOM(cx, RegExp), 2); if (!ctor) return NULL; if (!LinkConstructorAndPrototype(cx, ctor, proto)) return NULL; /* Add static properties to the RegExp constructor. */ if (!JS_DefineProperties(cx, ctor, regexp_static_props)) @@ -570,24 +573,24 @@ ExecuteRegExp(JSContext *cx, Native nati CallArgs args = CallArgsFromVp(argc, vp); /* Step 1. */ bool ok; JSObject *obj = NonGenericMethodGuard(cx, args, native, &RegExpClass, &ok); if (!obj) return ok; - RegExpObject &reobj = obj->asRegExp(); + RootedVar<RegExpObject*> reobj(cx, &obj->asRegExp()); RegExpGuard re; - if (StartsWithGreedyStar(reobj.getSource())) { - if (!GetSharedForGreedyStar(cx, reobj.getSource(), reobj.getFlags(), &re)) + if (StartsWithGreedyStar(reobj->getSource())) { + if (!GetSharedForGreedyStar(cx, reobj->getSource(), reobj->getFlags(), &re)) return false; } else { - if (!reobj.getShared(cx, &re)) + if (!reobj->getShared(cx, &re)) return false; } RegExpStatics *res = cx->regExpStatics(); /* Step 2. */ JSString *input = ToString(cx, (args.length() > 0) ? args[0] : UndefinedValue()); if (!input) @@ -596,48 +599,48 @@ ExecuteRegExp(JSContext *cx, Native nati /* Step 3. */ JSLinearString *linearInput = input->ensureLinear(cx); if (!linearInput) return false; const jschar *chars = linearInput->chars(); size_t length = input->length(); /* Step 4. */ - const Value &lastIndex = reobj.getLastIndex(); + const Value &lastIndex = reobj->getLastIndex(); /* Step 5. */ double i; if (!ToInteger(cx, lastIndex, &i)) return false; /* Steps 6-7 (with sticky extension). */ if (!re->global() && !re->sticky()) i = 0; /* Step 9a. */ if (i < 0 || i > length) { - reobj.zeroLastIndex(); + reobj->zeroLastIndex(); args.rval() = NullValue(); return true; } /* Steps 8-21. */ RegExpExecType execType = (native == regexp_test) ? RegExpTest : RegExpExec; size_t lastIndexInt(i); if (!ExecuteRegExp(cx, res, *re, linearInput, chars, length, &lastIndexInt, execType, &args.rval())) { return false; } /* Step 11 (with sticky extension). */ if (re->global() || (!args.rval().isNull() && re->sticky())) { if (args.rval().isNull()) - reobj.zeroLastIndex(); + reobj->zeroLastIndex(); else - reobj.setLastIndex(lastIndexInt); + reobj->setLastIndex(lastIndexInt); } return true; } /* ES5 15.10.6.2. */ JSBool js::regexp_exec(JSContext *cx, unsigned argc, Value *vp)
--- a/js/src/configure.in +++ b/js/src/configure.in @@ -4150,17 +4150,17 @@ MOZ_ARG_ENABLE_BOOL(gcgenerational, JSGC_GENERATIONAL= ) if test -n "$JSGC_GENERATIONAL"; then AC_DEFINE(JSGC_GENERATIONAL) fi dnl ======================================================== dnl = Perform moving GC stack rooting analysis dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(gcrootanalysis, +MOZ_ARG_ENABLE_BOOL(root-analysis, [ --enable-root-analysis Enable moving GC stack root analysis], JSGC_ROOT_ANALYSIS=1, JSGC_ROOT_ANALYSIS= ) if test -n "$JSGC_ROOT_ANALYSIS"; then AC_DEFINE(JSGC_ROOT_ANALYSIS) fi dnl ========================================================
--- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -48,19 +48,21 @@ #include "vm/GlobalObject.h" #include "jsinferinlines.h" using namespace js; using namespace js::frontend; bool -DefineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *script) +DefineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript* script) { - JSObject *globalObj = globalScope.globalObj; + Root<JSScript*> root(cx, &script); + + HandleObject globalObj = globalScope.globalObj; /* Define and update global properties. */ for (size_t i = 0; i < globalScope.defs.length(); i++) { GlobalScope::GlobalDef &def = globalScope.defs[i]; /* Names that could be resolved ahead of time can be skipped. */ if (!def.atom) continue; @@ -160,17 +162,16 @@ frontend::CompileScript(JSContext *cx, J uint32_t tcflags, const jschar *chars, size_t length, const char *filename, unsigned lineno, JSVersion version, JSString *source /* = NULL */, unsigned staticLevel /* = 0 */) { TokenKind tt; ParseNode *pn; - JSScript *script; bool inDirectivePrologue; JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_COMPILE_FOR_EVAL | TCF_NEED_SCRIPT_GLOBAL))); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. @@ -194,18 +195,17 @@ frontend::CompileScript(JSContext *cx, J // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalObj = scopeChain && scopeChain == &scopeChain->global() ? &scopeChain->global() : NULL; JS_ASSERT_IF(globalObj, globalObj->isNative()); JS_ASSERT_IF(globalObj, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalObj->getClass())); - /* Null script early in case of error, to reduce our code footprint. */ - script = NULL; + RootedVar<JSScript*> script(cx); GlobalScope globalScope(cx, globalObj, &bce); bce.flags |= tcflags; bce.setScopeChain(scopeChain); bce.globalScope = &globalScope; if (!SetStaticLevel(&bce, staticLevel)) goto out;
--- a/js/src/frontend/BytecodeEmitter-inl.h +++ b/js/src/frontend/BytecodeEmitter-inl.h @@ -44,18 +44,19 @@ #include "frontend/ParseNode.h" #include "frontend/TokenStream.h" namespace js { inline TreeContext::TreeContext(Parser *prs) : flags(0), bodyid(0), blockidGen(0), parenDepth(0), yieldCount(0), - topStmt(NULL), topScopeStmt(NULL), blockChain(NULL), blockNode(NULL), - decls(prs->context), parser(prs), yieldNode(NULL), argumentsNode(NULL), scopeChain_(NULL), + topStmt(NULL), topScopeStmt(NULL), blockChain(prs->context), blockNode(NULL), + decls(prs->context), parser(prs), yieldNode(NULL), argumentsNode(NULL), + fun_(prs->context), scopeChain_(prs->context), lexdeps(prs->context), parent(prs->tc), staticLevel(0), funbox(NULL), functionList(NULL), innermostWith(NULL), bindings(prs->context), bindingsRoot(prs->context, &bindings), funcStmts(NULL) { prs->tc = this; } /*
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -421,17 +421,17 @@ frontend::GenerateBlockId(TreeContext *t void frontend::PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_t top) { stmt->type = type; stmt->flags = 0; stmt->blockid = tc->blockid(); SET_STATEMENT_TOP(stmt, top); stmt->label = NULL; - JS_ASSERT(!stmt->blockObj); + stmt->blockObj = NULL; stmt->down = tc->topStmt; tc->topStmt = stmt; if (STMT_LINKS_SCOPE(stmt)) { stmt->downScope = tc->topScopeStmt; tc->topScopeStmt = stmt; } else { stmt->downScope = NULL; } @@ -1358,17 +1358,16 @@ BindGlobal(JSContext *cx, BytecodeEmitte * to update the special cases in EmitFor (for-in) and EmitAssignment (= and * op=, e.g. +=). */ static JSBool BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) { Definition *dn; JSOp op; - JSAtom *atom; Definition::Kind dn_kind; JS_ASSERT(pn->isKind(PNK_NAME)); /* Idempotency tests come first, since we may be called more than once. */ if (pn->pn_dflags & PND_BOUND) return JS_TRUE; @@ -1392,17 +1391,17 @@ BindNameToSlot(JSContext *cx, BytecodeEm dn = (Definition *) pn; } op = pn->getOp(); if (op == JSOP_NOP) return JS_TRUE; JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM); - atom = pn->pn_atom; + RootedVarAtom atom(cx, pn->pn_atom); UpvarCookie cookie = dn->pn_cookie; dn_kind = dn->kind(); /* * Turn attempts to mutate const-declared bindings into get ops (for * pre-increment and pre-decrement ops, our caller will have to emit * JSOP_POS, JSOP_ONE, and JSOP_ADD as well). * @@ -2367,17 +2366,17 @@ EmitSwitch(JSContext *cx, BytecodeEmitte ptrdiff_t top, off, defaultOffset; ParseNode *pn2, *pn3, *pn4; uint32_t caseCount, tableLength; ParseNode **table; int32_t i, low, high; int noteIndex; size_t switchSize, tableSize; jsbytecode *pc, *savepc; - StmtInfo stmtInfo; + StmtInfo stmtInfo(cx); /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */ switchOp = JSOP_TABLESWITCH; ok = JS_TRUE; hasDefault = constPropagated = JS_FALSE; defaultOffset = -1; pn2 = pn->pn_right; @@ -3980,17 +3979,17 @@ ParseNode::getConstantValue(JSContext *c types::FixArrayType(cx, obj); vp->setObject(*obj); return true; } case PNK_RC: { JS_ASSERT(isOp(JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST)); gc::AllocKind kind = GuessObjectGCKind(pn_count); - JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass, kind); + RootedVarObject obj(cx, NewBuiltinClassInstance(cx, &ObjectClass, kind)); if (!obj) return false; for (ParseNode *pn = pn_head; pn; pn = pn->pn_next) { Value value; if (!pn->pn_right->getConstantValue(cx, strictChecks, &value)) return false; @@ -4139,17 +4138,17 @@ EmitCatch(JSContext *cx, BytecodeEmitter /* * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr12127. See * the comment on EmitSwitch. */ MOZ_NEVER_INLINE static bool EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) { - StmtInfo stmtInfo; + StmtInfo stmtInfo(cx); ptrdiff_t catchJump = -1; /* * Push stmtInfo to track jumps-over-catches and gosubs-to-finally * for later fixup. * * When a finally block is active (STMT_FINALLY in our tree context), * non-local jumps (including jumps-over-catches) result in a GOSUB @@ -4371,17 +4370,17 @@ EmitTry(JSContext *cx, BytecodeEmitter * return false; return true; } static bool EmitIf(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) { - StmtInfo stmtInfo; + StmtInfo stmtInfo(cx); /* Initialize so we can detect else-if chains and avoid recursion. */ stmtInfo.type = STMT_IF; ptrdiff_t beq = -1; ptrdiff_t jmp = -1; ptrdiff_t noteIndex = -1; if_again: @@ -4528,17 +4527,17 @@ EmitLet(JSContext *cx, BytecodeEmitter * for (uint32_t i = alreadyPushed; i < blockObjCount; ++i) { /* Tell the decompiler not to print the decl in the let head. */ if (NewSrcNote(cx, bce, SRC_CONTINUE) < 0) return false; if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) return false; } - StmtInfo stmtInfo; + StmtInfo stmtInfo(cx); PushBlockScope(bce, &stmtInfo, blockObj, bce->offset()); if (!letNotes.update(cx, bce, bce->offset())) return false; ptrdiff_t declNote = NewSrcNote(cx, bce, SRC_DECL); if (declNote < 0) return false; @@ -4654,17 +4653,17 @@ EmitXMLProcessingInstruction(JSContext * * the comment on EmitSwitch. */ MOZ_NEVER_INLINE static bool EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) { JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE)); JS_ASSERT(pn->getOp() == JSOP_LEAVEBLOCK); - StmtInfo stmtInfo; + StmtInfo stmtInfo(cx); ObjectBox *objbox = pn->pn_objbox; StaticBlockObject &blockObj = objbox->object->asStaticBlock(); PushBlockScope(bce, &stmtInfo, blockObj, bce->offset()); /* * For compound statements (i.e. { stmt-list }), the decompiler does not * emit curlies by default. However, if this stmt-list contains a let * declaration, this is semantically invalid so we need to add a srcnote to @@ -4703,34 +4702,34 @@ EmitLexicalScope(JSContext *cx, Bytecode EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObj.slotCount()); return PopStatementBCE(cx, bce); } static bool EmitWith(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) { - StmtInfo stmtInfo; + StmtInfo stmtInfo(cx); if (!EmitTree(cx, bce, pn->pn_left)) return false; PushStatement(bce, &stmtInfo, STMT_WITH, bce->offset()); if (Emit1(cx, bce, JSOP_ENTERWITH) < 0) return false; if (!EmitTree(cx, bce, pn->pn_right)) return false; if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0) return false; return PopStatementBCE(cx, bce); } static bool EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) { - StmtInfo stmtInfo; + StmtInfo stmtInfo(cx); PushStatement(bce, &stmtInfo, STMT_FOR_IN_LOOP, top); ParseNode *forHead = pn->pn_left; ParseNode *forBody = pn->pn_right; ParseNode *pn1 = forHead->pn_kid1; bool letDecl = pn1 && pn1->isKind(PNK_LEXICALSCOPE); JS_ASSERT_IF(letDecl, pn1->isLet()); @@ -4786,17 +4785,17 @@ EmitForIn(JSContext *cx, BytecodeEmitter * object depending on the loop variant (for-in, for-each-in, or * destructuring for-in). */ JS_ASSERT(pn->isOp(JSOP_ITER)); if (Emit2(cx, bce, JSOP_ITER, (uint8_t) pn->pn_iflags) < 0) return false; /* Enter the block before the loop body, after evaluating the obj. */ - StmtInfo letStmt; + StmtInfo letStmt(cx); if (letDecl) { PushBlockScope(bce, &letStmt, *blockObj, bce->offset()); letStmt.flags |= SIF_FOR_BLOCK; if (!EmitEnterBlock(cx, bce, pn1, JSOP_ENTERLET1)) return false; } /* Annotate so the decompiler can find the loop-closing jump. */ @@ -4896,17 +4895,17 @@ EmitForIn(JSContext *cx, BytecodeEmitter } return true; } static bool EmitNormalFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) { - StmtInfo stmtInfo; + StmtInfo stmtInfo(cx); PushStatement(bce, &stmtInfo, STMT_FOR_LOOP, top); ParseNode *forHead = pn->pn_left; ParseNode *forBody = pn->pn_right; /* C-style for (init; cond; update) ... loop. */ JSOp op = JSOP_POP; ParseNode *pn3 = forHead->pn_kid1; @@ -5053,17 +5052,17 @@ EmitFor(JSContext *cx, BytecodeEmitter * static JS_NEVER_INLINE bool EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) { #if JS_HAS_XML_SUPPORT if (pn->isArity(PN_NULLARY)) return Emit1(cx, bce, JSOP_GETFUNNS) >= 0; #endif - JSFunction *fun = pn->pn_funbox->function(); + RootedVarFunction fun(cx, pn->pn_funbox->function()); JS_ASSERT(fun->isInterpreted()); if (fun->script()) { /* * This second pass is needed to emit JSOP_NOP with a source note * for the already-emitted function definition prolog opcode. See * comments in the PNK_STATEMENTLIST case. */ JS_ASSERT(pn->isOp(JSOP_NOP)); @@ -5176,17 +5175,17 @@ EmitDo(JSContext *cx, BytecodeEmitter *b /* Compile the loop body. */ ptrdiff_t top = EmitLoopHead(cx, bce, pn->pn_left); if (top < 0) return false; if (!EmitLoopEntry(cx, bce, NULL)) return false; - StmtInfo stmtInfo; + StmtInfo stmtInfo(cx); PushStatement(bce, &stmtInfo, STMT_DO_LOOP, top); if (!EmitTree(cx, bce, pn->pn_left)) return false; /* Set loop and enclosing label update offsets, for continue. */ ptrdiff_t off = bce->offset(); StmtInfo *stmt = &stmtInfo; do { @@ -5229,17 +5228,17 @@ EmitWhile(JSContext *cx, BytecodeEmitter * i test at the top test at the bottom * = =============== ================== * 0 ifeq-pass goto; ifne-fail * 1 ifeq-fail; goto; ifne-pass goto; ifne-pass; ifne-fail * 2 2*(ifeq-fail; goto); ifeq-pass goto; 2*ifne-pass; ifne-fail * . . . * N N*(ifeq-fail; goto); ifeq-pass goto; N*ifne-pass; ifne-fail */ - StmtInfo stmtInfo; + StmtInfo stmtInfo(cx); PushStatement(bce, &stmtInfo, STMT_WHILE_LOOP, top); ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_WHILE); if (noteIndex < 0) return false; ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0); if (jmp < 0) @@ -5367,17 +5366,17 @@ EmitStatementList(JSContext *cx, Bytecod ptrdiff_t noteIndex = -1; ptrdiff_t tmp = bce->offset(); if (pn->pn_xflags & PNX_NEEDBRACES) { noteIndex = NewSrcNote2(cx, bce, SRC_BRACE, 0); if (noteIndex < 0 || Emit1(cx, bce, JSOP_NOP) < 0) return false; } - StmtInfo stmtInfo; + StmtInfo stmtInfo(cx); PushStatement(bce, &stmtInfo, STMT_BLOCK, top); ParseNode *pnchild = pn->pn_head; if (pn->pn_xflags & PNX_FUNCDEFS) { /* * This block contains top-level function definitions. To ensure * that we emit the bytecode defining them before the rest of code * in the block we use a separate pass over functions. During the @@ -5848,17 +5847,17 @@ EmitLabel(JSContext *cx, BytecodeEmitter if (noteIndex < 0) return false; ptrdiff_t top = EmitJump(cx, bce, JSOP_LABEL, 0); if (top < 0) return false; /* Emit code for the labeled statement. */ - StmtInfo stmtInfo; + StmtInfo stmtInfo(cx); PushStatement(bce, &stmtInfo, STMT_LABEL, bce->offset()); stmtInfo.label = atom; if (!EmitTree(cx, bce, pn2)) return false; if (!PopStatementBCE(cx, bce)) return false; /* Patch the JSOP_LABEL offset. */ @@ -5872,17 +5871,17 @@ EmitLabel(JSContext *cx, BytecodeEmitter return true; } static bool EmitSyntheticStatements(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) { JS_ASSERT(pn->isArity(PN_LIST)); - StmtInfo stmtInfo; + StmtInfo stmtInfo(cx); PushStatement(bce, &stmtInfo, STMT_SEQ, top); for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { if (!EmitTree(cx, bce, pn2)) return false; } return PopStatementBCE(cx, bce); } @@ -5948,17 +5947,17 @@ EmitObject(JSContext *cx, BytecodeEmitte ptrdiff_t offset = bce->next() - bce->base(); if (!EmitNewInit(cx, bce, JSProto_Object, pn)) return false; /* * Try to construct the shape of the object as we go, so we can emit a * JSOP_NEWOBJECT with the final shape instead. */ - JSObject *obj = NULL; + RootedVarObject obj(cx); if (bce->compileAndGo()) { gc::AllocKind kind = GuessObjectGCKind(pn->pn_count); obj = NewBuiltinClassInstance(cx, &ObjectClass, kind); if (!obj) return false; } for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
--- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -49,16 +49,18 @@ #include "jsopcode.h" #include "jsscript.h" #include "jsprvtd.h" #include "jspubtd.h" #include "frontend/Parser.h" #include "frontend/ParseMaps.h" +#include "vm/ScopeObject.h" + namespace js { typedef HashSet<JSAtom *> FuncStmtSet; /* * NB: If you add enumerators for scope statements, add them between STMT_WITH * and STMT_CATCH, or you will break the STMT_TYPE_IS_SCOPE macro. If you add * non-looping statement enumerators, add them before STMT_DO_LOOP or you will @@ -131,22 +133,22 @@ STMT_TYPE_IN_RANGE(uint16_t type, StmtTy struct StmtInfo { uint16_t type; /* statement type */ uint16_t flags; /* flags, see below */ uint32_t blockid; /* for simplified dominance computation */ ptrdiff_t update; /* loop update offset (top if none) */ ptrdiff_t breaks; /* offset of last break in loop */ ptrdiff_t continues; /* offset of last continue in loop */ - union { - JSAtom *label; /* name of LABEL */ - StaticBlockObject *blockObj;/* block scope object */ - }; + RootedVarAtom label; /* name of LABEL */ + RootedVar<StaticBlockObject *> blockObj; /* block scope object */ StmtInfo *down; /* info for enclosing statement */ StmtInfo *downScope; /* next enclosing lexical scope */ + + StmtInfo(JSContext *cx) : label(cx), blockObj(cx) {} }; #define SIF_SCOPE 0x0001 /* statement has its own lexical scope */ #define SIF_BODY_BLOCK 0x0002 /* STMT_BLOCK type is a function body */ #define SIF_FOR_BLOCK 0x0004 /* for (let ...) induced block scope */ /* * To reuse space in StmtInfo, rename breaks and continues for use during @@ -331,36 +333,35 @@ struct TreeContext { /* t uint32_t bodyid; /* block number of program/function body */ uint32_t blockidGen; /* preincremented block number generator */ uint32_t parenDepth; /* nesting depth of parens that might turn out to be generator expressions */ uint32_t yieldCount; /* number of |yield| tokens encountered at non-zero depth in current paren tree */ StmtInfo *topStmt; /* top of statement info stack */ StmtInfo *topScopeStmt; /* top lexical scope statement */ - StaticBlockObject *blockChain; /* compile block scope chain (NB: one + RootedVar<StaticBlockObject *> blockChain; + /* compile time block scope chain (NB: one deeper than the topScopeStmt/downScope chain when in head of let block/expr) */ ParseNode *blockNode; /* parse node for a block with let declarations (block with its own lexical scope) */ AtomDecls decls; /* function, const, and var declarations */ Parser *parser; /* ptr to common parsing and lexing data */ ParseNode *yieldNode; /* parse node for a yield expression that might be an error if we turn out to be inside a generator expression */ ParseNode *argumentsNode; /* parse node for an arguments variable that might be an error if we turn out to be inside a generator expression */ private: - union { - JSFunction *fun_; /* function to store argument and variable + RootedVarFunction fun_; /* function to store argument and variable names when flags & TCF_IN_FUNCTION */ - JSObject *scopeChain_; /* scope chain object for the script */ - }; + RootedVarObject scopeChain_; /* scope chain object for the script */ public: JSFunction *fun() const { JS_ASSERT(inFunction()); return fun_; } void setFunction(JSFunction *fun) { JS_ASSERT(inFunction()); @@ -539,43 +540,51 @@ class GCConstList { GCConstList(JSContext *cx) : list(cx) {} bool append(Value v) { return list.append(v); } size_t length() const { return list.length(); } void finish(JSConstArray *array); }; struct GlobalScope { GlobalScope(JSContext *cx, JSObject *globalObj, BytecodeEmitter *bce) - : globalObj(globalObj), bce(bce), defs(cx), names(cx) + : globalObj(cx, globalObj), bce(bce), defs(cx), names(cx), + defsRoot(cx, &defs), namesRoot(cx, &names) { } struct GlobalDef { JSAtom *atom; // If non-NULL, specifies the property name to add. FunctionBox *funbox; // If non-NULL, function value for the property. // This value is only set/used if atom is non-NULL. uint32_t knownSlot; // If atom is NULL, this is the known shape slot. GlobalDef() { } GlobalDef(uint32_t knownSlot) : atom(NULL), knownSlot(knownSlot) { } GlobalDef(JSAtom *atom, FunctionBox *box) : atom(atom), funbox(box) { } }; - JSObject *globalObj; + RootedVarObject globalObj; BytecodeEmitter *bce; /* * This is the table of global names encountered during parsing. Each * global name appears in the list only once, and the |names| table * maps back into |defs| for fast lookup. * * A definition may either specify an existing global property, or a new * one that must be added after compilation succeeds. */ Vector<GlobalDef, 16> defs; AtomIndexMap names; + + /* + * Protect the inline elements within defs/names from being clobbered by + * root analysis. The atoms in this structure must be separately rooted. + */ + JS::SkipRoot defsRoot; + JS::SkipRoot namesRoot; }; struct BytecodeEmitter : public TreeContext { struct { jsbytecode *base; /* base of JS bytecode vector */ jsbytecode *limit; /* one byte beyond end of bytecode */ jsbytecode *next; /* pointer to next free bytecode */
--- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -227,18 +227,18 @@ FoldBinaryNumeric(JSContext *cx, JSOp op static JSBool FoldXMLConstants(JSContext *cx, ParseNode *pn, TreeContext *tc) { JS_ASSERT(pn->isArity(PN_LIST)); ParseNodeKind kind = pn->getKind(); ParseNode **pnp = &pn->pn_head; ParseNode *pn1 = *pnp; - JSString *accum = NULL; - JSString *str = NULL; + RootedVarString accum(cx); + RootedVarString str(cx); if ((pn->pn_xflags & PNX_CANTFOLD) == 0) { if (kind == PNK_XMLETAGO) accum = cx->runtime->atomState.etagoAtom; else if (kind == PNK_XMLSTAGO || kind == PNK_XMLPTAGC) accum = cx->runtime->atomState.stagoAtom; } /* @@ -742,25 +742,23 @@ js::FoldConstants(JSContext *cx, ParseNo pn->setOp(JSOP_STRING); pn->setArity(PN_NULLARY); break; } /* Handle a binary string concatenation. */ JS_ASSERT(pn->isArity(PN_BINARY)); if (pn1->isKind(PNK_STRING) || pn2->isKind(PNK_STRING)) { - JSString *left, *right, *str; - if (!FoldType(cx, !pn1->isKind(PNK_STRING) ? pn1 : pn2, PNK_STRING)) return false; if (!pn1->isKind(PNK_STRING) || !pn2->isKind(PNK_STRING)) return true; - left = pn1->pn_atom; - right = pn2->pn_atom; - str = js_ConcatStrings(cx, left, right); + RootedVarString left(cx, pn1->pn_atom); + RootedVarString right(cx, pn2->pn_atom); + RootedVarString str(cx, js_ConcatStrings(cx, left, right)); if (!str) return false; pn->pn_atom = js_AtomizeString(cx, str); if (!pn->pn_atom) return false; pn->setKind(PNK_STRING); pn->setOp(JSOP_STRING); pn->setArity(PN_NULLARY);
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -169,16 +169,18 @@ Parser::setPrincipals(JSPrincipals *prin originPrincipals = originPrin; if (originPrincipals) JS_HoldPrincipals(originPrincipals); } ObjectBox * Parser::newObjectBox(JSObject *obj) { + JS_ASSERT(obj && !IsPoisonedPtr(obj)); + /* * We use JSContext.tempLifoAlloc to allocate parsed objects and place them * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc * arenas containing the entries must be alive until we are done with * scanning, parsing and code generation for the whole script or top-level * function. */ ObjectBox *objbox = context->tempLifoAlloc().new_<ObjectBox>(); @@ -192,17 +194,17 @@ Parser::newObjectBox(JSObject *obj) objbox->object = obj; objbox->isFunctionBox = false; return objbox; } FunctionBox * Parser::newFunctionBox(JSObject *obj, ParseNode *fn, TreeContext *tc) { - JS_ASSERT(obj); + JS_ASSERT(obj && !IsPoisonedPtr(obj)); JS_ASSERT(obj->isFunction()); /* * We use JSContext.tempLifoAlloc to allocate parsed objects and place them * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc * arenas containing the entries must be alive until we are done with * scanning, parsing and code generation for the whole script or top-level * function. @@ -578,30 +580,30 @@ CheckStrictParameters(JSContext *cx, Tre } static bool BindLocalVariable(JSContext *cx, TreeContext *tc, ParseNode *pn, BindingKind kind) { JS_ASSERT(kind == VARIABLE || kind == CONSTANT); unsigned index = tc->bindings.numVars(); - if (!tc->bindings.add(cx, pn->pn_atom, kind)) + if (!tc->bindings.add(cx, RootedVarAtom(cx, pn->pn_atom), kind)) return false; pn->pn_cookie.set(tc->staticLevel, index); pn->pn_dflags |= PND_BOUND; return true; } ParseNode * Parser::functionBody(FunctionBodyType type) { JS_ASSERT(tc->inFunction()); - StmtInfo stmtInfo; + StmtInfo stmtInfo(context); PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); stmtInfo.flags = SIF_BODY_BLOCK; unsigned oldflags = tc->flags; tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID); ParseNode *pn; if (type == StatementListBody) { @@ -641,17 +643,17 @@ Parser::functionBody(FunctionBodyType ty /* * Check CheckStrictParameters before arguments logic below adds * 'arguments' to bindings. */ if (!CheckStrictParameters(context, tc)) return NULL; - PropertyName * const arguments = context->runtime->atomState.argumentsAtom; + RootedVar<PropertyName*> const arguments(context, context->runtime->atomState.argumentsAtom); /* * Non-top-level functions use JSOP_DEFFUN which is a dynamic scope * operation which means it aliases any bindings with the same name. * Due to the implicit declaration mechanism (below), 'arguments' will not * have decls and, even if it did, they will not be noted as closed in the * emitter. Thus, in the corner case of function-statement-overridding- * arguments, flag the whole scope as dynamic. @@ -1349,17 +1351,17 @@ Parser::functionArguments(TreeContext &f } list->append(item); break; } #endif /* JS_HAS_DESTRUCTURING */ case TOK_NAME: { - PropertyName *name = tokenStream.currentToken().name(); + RootedVar<PropertyName*> name(context, tokenStream.currentToken().name()); #ifdef JS_HAS_DESTRUCTURING /* * ECMA-262 requires us to support duplicate parameter names, * but if the parameter list includes destructuring, we * consider the code to have "opted in" to higher standards and * forbid duplicates. We may see a destructuring parameter * later, so always note duplicates now. @@ -1409,17 +1411,17 @@ Parser::functionArguments(TreeContext &f return false; } } return true; } ParseNode * -Parser::functionDef(PropertyName *funName, FunctionType type, FunctionSyntaxKind kind) +Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSyntaxKind kind) { JS_ASSERT_IF(kind == Statement, funName); /* Make a TOK_FUNCTION node. */ ParseNode *pn = FunctionNode::create(PNK_FUNCTION, tc); if (!pn) return NULL; pn->pn_body = NULL; @@ -1532,17 +1534,17 @@ Parser::functionDef(PropertyName *funNam TreeContext funtc(tc->parser); if (!funtc.init(context)) return NULL; FunctionBox *funbox = EnterFunction(pn, &funtc, funName, kind); if (!funbox) return NULL; - JSFunction *fun = funbox->function(); + RootedVarFunction fun(context, funbox->function()); /* Now parse formal argument list and compute fun->nargs. */ ParseNode *prelude = NULL; if (!functionArguments(funtc, funbox, &prelude)) return NULL; fun->setArgCount(funtc.bindings.numArgs()); @@ -1722,17 +1724,17 @@ Parser::functionDef(PropertyName *funNam tokenStream.setStrictMode(false); return pn; } ParseNode * Parser::functionStmt() { - PropertyName *name = NULL; + RootedVarPropertyName name(context); if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME) { name = tokenStream.currentToken().name(); } else { /* Unnamed function expressions are forbidden in statement context. */ reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_UNNAMED_FUNCTION_STMT); return NULL; } @@ -1743,17 +1745,17 @@ Parser::functionStmt() } return functionDef(name, Normal, Statement); } ParseNode * Parser::functionExpr() { - PropertyName *name = NULL; + RootedVarPropertyName name(context); if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME) name = tokenStream.currentToken().name(); else tokenStream.ungetToken(); return functionDef(name, Normal, Expression); } /* @@ -1963,18 +1965,18 @@ ReportRedeclaration(JSContext *cx, TreeC */ static JSBool BindLet(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc) { ParseNode *pn = data->pn; if (!CheckStrictBinding(cx, tc, atom->asPropertyName(), pn)) return false; - StaticBlockObject &blockObj = *data->let.blockObj; - unsigned blockCount = blockObj.slotCount(); + RootedVar<StaticBlockObject *> blockObj(cx, data->let.blockObj); + unsigned blockCount = blockObj->slotCount(); if (blockCount == JS_BIT(16)) { ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR, data->let.overflow); return false; } /* * For bindings that are hoisted to the beginning of the block/function, @@ -2001,25 +2003,25 @@ BindLet(JSContext *cx, BindData *data, J pn->pn_dflags |= PND_LET | PND_BOUND; /* * Define the let binding's property before storing pn in the the binding's * slot indexed by blockCount off the class-reserved slot base. */ bool redeclared; jsid id = ATOM_TO_JSID(atom); - const Shape *shape = blockObj.addVar(cx, id, blockCount, &redeclared); + const Shape *shape = blockObj->addVar(cx, id, blockCount, &redeclared); if (!shape) { if (redeclared) ReportRedeclaration(cx, tc, pn, false, atom); return false; } /* Store pn in the static block object. */ - blockObj.setDefinitionParseNode(blockCount, reinterpret_cast<Definition *>(pn)); + blockObj->setDefinitionParseNode(blockCount, reinterpret_cast<Definition *>(pn)); return true; } template <class Op> static inline bool ForEachLetDef(TreeContext *tc, StaticBlockObject &blockObj, Op op) { for (Shape::Range r = blockObj.lastProperty()->all(); !r.empty(); r.popFront()) { @@ -2080,21 +2082,22 @@ OuterLet(TreeContext *tc, StmtInfo *stmt * gets its own script and frame. The eval-from-function-code case is harder, * since functions do not atomize gvars and then reserve their atom indexes as * stack frame slots. */ static bool DefineGlobal(ParseNode *pn, BytecodeEmitter *bce, PropertyName *name) { GlobalScope *globalScope = bce->globalScope; - JSObject *globalObj = globalScope->globalObj; + HandleObject globalObj = globalScope->globalObj; if (!bce->compileAndGo() || !globalObj || bce->compilingForEval()) return true; + JS_ASSERT(!IsPoisonedPtr(name)); AtomIndexAddPtr p = globalScope->names.lookupForAdd(name); if (!p) { JSContext *cx = bce->parser->context; JSObject *holder; JSProperty *prop; if (!globalObj->lookupProperty(cx, name, &holder, &prop)) return false; @@ -2122,16 +2125,18 @@ DefineGlobal(ParseNode *pn, BytecodeEmit def = GlobalScope::GlobalDef(shape->slot()); } else { def = GlobalScope::GlobalDef(name, funbox); } if (!globalScope->defs.append(def)) return false; + JS_ASSERT(!IsPoisonedPtr(name)); + jsatomid index = globalScope->names.count(); if (!globalScope->names.add(p, name, index)) return false; JS_ASSERT(index == globalScope->defs.length() - 1); } else { /* * Functions can be redeclared, and the last one takes effect. Check @@ -2586,17 +2591,18 @@ CheckDestructuring(JSContext *cx, BindDa bool ok; if (left->isKind(PNK_ARRAYCOMP)) { ReportCompileErrorNumber(cx, TS(tc->parser), left, JSREPORT_ERROR, JSMSG_ARRAY_COMP_LEFTSIDE); return false; } - StaticBlockObject *blockObj = data && data->binder == BindLet ? data->let.blockObj : NULL; + RootedVar<StaticBlockObject *> blockObj(cx); + blockObj = data && data->binder == BindLet ? data->let.blockObj : NULL; uint32_t blockCountBefore = blockObj ? blockObj->slotCount() : 0; if (left->isKind(PNK_RB)) { for (ParseNode *pn = left->pn_head; pn; pn = pn->pn_next) { /* Nullary comma is an elision; binary comma is an expression.*/ if (!pn->isArrayHole()) { if (pn->isKind(PNK_RB) || pn->isKind(PNK_RC)) { ok = CheckDestructuring(cx, data, pn, tc, false); @@ -2897,17 +2903,17 @@ Parser::letBlock(LetContext letContext) MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET); ParseNode *vars = variables(PNK_LET, blockObj, DontHoistVars); if (!vars) return NULL; MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET); - StmtInfo stmtInfo; + StmtInfo stmtInfo(context); ParseNode *block = PushLetScope(context, tc, *blockObj, &stmtInfo); if (!block) return NULL; pnlet->pn_left = vars; pnlet->pn_right = block; ParseNode *ret; @@ -3032,17 +3038,17 @@ Parser::switchStatement() MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH); MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH); /* * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode * because that function states tc->topStmt->blockid. */ - StmtInfo stmtInfo; + StmtInfo stmtInfo(context); PushStatement(tc, &stmtInfo, STMT_SWITCH, -1); /* pn2 is a list of case nodes. The default case has pn_left == NULL */ ParseNode *pn2 = ListNode::create(PNK_STATEMENTLIST, tc); if (!pn2) return NULL; pn2->makeEmpty(); if (!GenerateBlockIdForStmtNode(pn2, tc)) @@ -3154,17 +3160,17 @@ Parser::forStatement() { JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); /* A FOR node is binary, left is loop control and right is the body. */ ParseNode *pn = BinaryNode::create(PNK_FOR, tc); if (!pn) return NULL; - StmtInfo forStmt; + StmtInfo forStmt(context); PushStatement(tc, &forStmt, STMT_FOR_LOOP, -1); pn->setOp(JSOP_ITER); pn->pn_iflags = 0; if (tokenStream.matchToken(TOK_NAME)) { if (tokenStream.currentToken().name() == context->runtime->atomState.eachAtom) pn->pn_iflags = JSITER_FOREACH; else @@ -3246,19 +3252,19 @@ Parser::forStatement() ParseNode *forParent = NULL; /* * We can be sure that it's a for/in loop if there's still an 'in' * keyword here, even if JavaScript recognizes 'in' as an operator, * as we've excluded 'in' from being parsed in RelExpr by setting * the TCF_IN_FOR_INIT flag in our TreeContext. */ - ParseNode *forHead; /* initialized by both branches. */ - StmtInfo letStmt; /* used if blockObj != NULL. */ - ParseNode *pn2, *pn3; /* forHead->pn_kid1 and pn_kid2. */ + ParseNode *forHead; /* initialized by both branches. */ + StmtInfo letStmt(context); /* used if blockObj != NULL. */ + ParseNode *pn2, *pn3; /* forHead->pn_kid1 and pn_kid2. */ bool forOf; if (pn1 && matchInOrOf(&forOf)) { /* * Parse the rest of the for/in or for/of head. * * Here pn1 is everything to the left of 'in' or 'of'. At the end of * this block, pn1 is a decl or NULL, pn2 is the assignment target that * receives the enumeration value each iteration, and pn3 is the rhs of @@ -3547,17 +3553,17 @@ Parser::tryStatement() * finally nodes are TOK_LC statement lists. */ ParseNode *pn = TernaryNode::create(PNK_TRY, tc); if (!pn) return NULL; pn->setOp(JSOP_NOP); MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY); - StmtInfo stmtInfo; + StmtInfo stmtInfo(context); if (!PushBlocklikeStatement(&stmtInfo, STMT_TRY, tc)) return NULL; pn->pn_kid1 = statements(); if (!pn->pn_kid1) return NULL; MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY); PopStatement(tc); @@ -3714,17 +3720,17 @@ Parser::withStatement() if (!pn2) return NULL; MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH); pn->pn_left = pn2; ParseNode *oldWith = tc->innermostWith; tc->innermostWith = pn; - StmtInfo stmtInfo; + StmtInfo stmtInfo(context); PushStatement(tc, &stmtInfo, STMT_WITH, -1); pn2 = statement(); if (!pn2) return NULL; PopStatement(tc); pn->pn_pos.end = pn2->pn_pos.end; pn->pn_right = pn2; @@ -3885,17 +3891,17 @@ Parser::expressionStatement() return NULL; } } ForgetUse(pn2); (void) tokenStream.getToken(); /* Push a label struct and parse the statement. */ - StmtInfo stmtInfo; + StmtInfo stmtInfo(context); PushStatement(tc, &stmtInfo, STMT_LABEL, -1); stmtInfo.label = label; ParseNode *pn = statement(); if (!pn) return NULL; /* Normalize empty statement to empty block for the decompiler. */ if (pn->isKind(PNK_SEMI) && !pn->pn_kid) { @@ -3966,17 +3972,17 @@ Parser::statement() /* An IF node has three kids: condition, then, and optional else. */ pn = TernaryNode::create(PNK_IF, tc); if (!pn) return NULL; ParseNode *pn1 = condition(); if (!pn1) return NULL; - StmtInfo stmtInfo; + StmtInfo stmtInfo(context); PushStatement(tc, &stmtInfo, STMT_IF, -1); ParseNode *pn2 = statement(); if (!pn2) return NULL; if (pn2->isKind(PNK_SEMI) && !pn2->pn_kid && !reportErrorNumber(NULL, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_EMPTY_CONSEQUENT)) @@ -4005,17 +4011,17 @@ Parser::statement() case TOK_SWITCH: return switchStatement(); case TOK_WHILE: { pn = BinaryNode::create(PNK_WHILE, tc); if (!pn) return NULL; - StmtInfo stmtInfo; + StmtInfo stmtInfo(context); PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1); ParseNode *pn2 = condition(); if (!pn2) return NULL; pn->pn_left = pn2; ParseNode *pn3 = statement(); if (!pn3) return NULL; @@ -4025,17 +4031,17 @@ Parser::statement() return pn; } case TOK_DO: { pn = BinaryNode::create(PNK_DOWHILE, tc); if (!pn) return NULL; - StmtInfo stmtInfo; + StmtInfo stmtInfo(context); PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1); ParseNode *pn2 = statement(); if (!pn2) return NULL; pn->pn_left = pn2; MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO); ParseNode *pn3 = condition(); if (!pn3) @@ -4202,17 +4208,17 @@ Parser::statement() break; case TOK_LC: { unsigned oldflags; oldflags = tc->flags; tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT; - StmtInfo stmtInfo; + StmtInfo stmtInfo(context); if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, tc)) return NULL; pn = statements(); if (!pn) return NULL; MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND); PopStatement(tc); @@ -5303,17 +5309,17 @@ CompExprTransplanter::transplant(ParseNo * (possibly nested) for-loop, initialized by |kind, op, kid|. */ ParseNode * Parser::comprehensionTail(ParseNode *kid, unsigned blockid, bool isGenexp, ParseNodeKind kind, JSOp op) { unsigned adjust; ParseNode *pn, *pn2, *pn3, **pnp; - StmtInfo stmtInfo; + StmtInfo stmtInfo(context); BindData data; TokenKind tt; JS_ASSERT(tokenStream.currentToken().type == TOK_FOR); if (kind == PNK_SEMI) { /* * Generator expression desugars to an immediately applied lambda that @@ -5752,17 +5758,17 @@ Parser::memberExpr(JSBool allowCallSynta #if JS_HAS_XML_SUPPORT else if (!tc->inStrictMode()) { TokenPtr begin = lhs->pn_pos.begin; if (tt == TOK_LP) { /* Filters are effectively 'with', so deoptimize names. */ tc->flags |= TCF_FUN_HEAVYWEIGHT; tc->noteBindingsAccessedDynamically(); - StmtInfo stmtInfo; + StmtInfo stmtInfo(context); ParseNode *oldWith = tc->innermostWith; tc->innermostWith = lhs; PushStatement(tc, &stmtInfo, STMT_WITH, -1); ParseNode *filter = bracketedExpr(); if (!filter) return NULL; filter->setInParens(true); @@ -6929,17 +6935,18 @@ Parser::primaryExpr(TokenKind tt, bool a return NULL; pn3->pn_atom = atom; break; } pn->pn_xflags |= PNX_NONCONST; /* NB: Getter function in { get x(){} } is unnamed. */ - pn2 = functionDef(NULL, op == JSOP_GETTER ? Getter : Setter, Expression); + pn2 = functionDef(RootedVarPropertyName(context, NULL), + op == JSOP_GETTER ? Getter : Setter, Expression); if (!pn2) return NULL; TokenPos pos = {begin, pn2->pn_pos.end}; pn2 = new_<BinaryNode>(PNK_COLON, op, pos, pn3, pn2); goto skip; } case TOK_STRING: { atom = tokenStream.currentToken().atom(); @@ -7142,17 +7149,17 @@ Parser::primaryExpr(TokenKind tt, bool a if (!pn) return NULL; const jschar *chars = tokenStream.getTokenbuf().begin(); size_t length = tokenStream.getTokenbuf().length(); RegExpFlag flags = tokenStream.currentToken().regExpFlags(); RegExpStatics *res = context->regExpStatics(); - RegExpObject *reobj; + RootedVar<RegExpObject*> reobj(context); if (context->hasfp()) reobj = RegExpObject::create(context, res, chars, length, flags, &tokenStream); else reobj = RegExpObject::createNoStatics(context, chars, length, flags, &tokenStream); if (!reobj) return NULL;
--- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -238,17 +238,17 @@ struct Parser : private AutoGCRooter ParseNode *parenExpr(JSBool *genexp = NULL); /* * Additional JS parsers. */ enum FunctionType { Getter, Setter, Normal }; bool functionArguments(TreeContext &funtc, FunctionBox *funbox, ParseNode **list); - ParseNode *functionDef(PropertyName *name, FunctionType type, FunctionSyntaxKind kind); + ParseNode *functionDef(HandlePropertyName name, FunctionType type, FunctionSyntaxKind kind); ParseNode *unaryOpExpr(ParseNodeKind kind, JSOp op); ParseNode *condition(); ParseNode *comprehensionTail(ParseNode *kid, unsigned blockid, bool isGenexp, ParseNodeKind kind = PNK_SEMI, JSOp op = JSOP_NOP); ParseNode *generatorExpr(ParseNode *kid); JSBool argumentList(ParseNode *listNode);
--- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -149,17 +149,18 @@ js::IsIdentifier(JSLinearString *str) #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4351) #endif /* Initialize members that aren't initialized in |init|. */ TokenStream::TokenStream(JSContext *cx, JSPrincipals *prin, JSPrincipals *originPrin) - : tokens(), cursor(), lookahead(), flags(), listenerTSData(), tokenbuf(cx), + : tokens(), tokensRoot(cx, &tokens), + cursor(), lookahead(), flags(), userbufRoot(cx, &userbuf), listenerTSData(), tokenbuf(cx), cx(cx), originPrincipals(JSScript::normalizeOriginPrincipals(prin, originPrin)) { if (originPrincipals) JS_HoldPrincipals(originPrincipals); } #ifdef _MSC_VER #pragma warning(pop)
--- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -331,30 +331,34 @@ struct Token { /* * FIXME: Init type early enough such that all mutators can assert * type-safety. See bug 697000. */ void setName(JSOp op, PropertyName *name) { JS_ASSERT(op == JSOP_NAME); + JS_ASSERT(!IsPoisonedPtr(name)); u.s.op = op; u.s.n.name = name; } void setAtom(JSOp op, JSAtom *atom) { JS_ASSERT(op == JSOP_STRING || op == JSOP_XMLCOMMENT || JSOP_XMLCDATA); + JS_ASSERT(!IsPoisonedPtr(atom)); u.s.op = op; u.s.n.atom = atom; } void setProcessingInstruction(PropertyName *target, JSAtom *data) { JS_ASSERT(target); JS_ASSERT(data); JS_ASSERT(!target->empty()); + JS_ASSERT(!IsPoisonedPtr(target)); + JS_ASSERT(!IsPoisonedPtr(data)); u.xmlpi.target = target; u.xmlpi.data = data; } void setRegExpFlags(js::RegExpFlag flags) { JS_ASSERT((flags & AllFlags) == flags); u.reflags = flags; } @@ -803,23 +807,25 @@ class TokenStream while (--n >= 0) getChar(); } void updateLineInfoForEOL(); void updateFlagsForEOL(); Token tokens[ntokens];/* circular token buffer */ - unsigned cursor; /* index of last parsed token */ - unsigned lookahead; /* count of lookahead tokens */ - unsigned lineno; /* current line number */ - unsigned flags; /* flags -- see above */ + JS::SkipRoot tokensRoot; /* prevent overwriting of token buffer */ + unsigned cursor; /* index of last parsed token */ + unsigned lookahead; /* count of lookahead tokens */ + unsigned lineno; /* current line number */ + unsigned flags; /* flags -- see above */ const jschar *linebase; /* start of current line; points into userbuf */ const jschar *prevLinebase; /* start of previous line; NULL if on the first line */ TokenBuf userbuf; /* user input buffer */ + JS::SkipRoot userbufRoot; /* prevent overwriting of char pointers within userbuf */ const char *filename; /* input filename or null */ jschar *sourceMap; /* source map's filename or null */ void *listenerTSData;/* listener data for this TokenStream */ CharBuffer tokenbuf; /* current token string buffer */ int8_t oneCharTokens[128]; /* table of one-char tokens */ bool maybeEOL[256]; /* probabilistic EOL lookup table */ bool maybeStrSpecial[256];/* speeds up string scanning */ JSVersion version; /* (i.e. to identify keywords) */
--- a/js/src/gc/Barrier-inl.h +++ b/js/src/gc/Barrier-inl.h @@ -119,16 +119,17 @@ HeapValue::init(const Value &v) JS_ASSERT(!IsPoisonedValue(v)); value = v; post(); } inline void HeapValue::init(JSCompartment *comp, const Value &v) { + JS_ASSERT(!IsPoisonedValue(v)); value = v; post(comp); } inline HeapValue & HeapValue::operator=(const Value &v) { pre();
--- a/js/src/gc/Root.h +++ b/js/src/gc/Root.h @@ -35,20 +35,21 @@ * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsgc_root_h__ #define jsgc_root_h__ -#include "jsapi.h" -#include "jsprvtd.h" +#include "jspubtd.h" -namespace js { +#ifdef __cplusplus + +namespace JS { /* * Moving GC Stack Rooting * * A moving GC may change the physical location of GC allocated things, even * when they are rooted, updating all pointers to the thing to refer to its new * location. The GC must therefore know about all live pointers to a thing, * not just one of them, in order to behave correctly. @@ -87,43 +88,65 @@ namespace js { * should generally replace those arguments with handles and avoid any * explicit rooting. This has two benefits. First, when several such * functions call each other then redundant rooting of multiple copies of the * GC thing can be avoided. Second, if the caller does not pass a rooted * value a compile error will be generated, which is quicker and easier to * fix than when relying on a separate rooting analysis. */ -template <> struct RootMethods<const jsid> +template <typename T> class Root; +template <typename T> class RootedVar; + +template <typename T> +struct RootMethods { }; + +/* + * Reference to a stack location rooted for GC. See "Moving GC Stack Rooting" + * comment in jscntxt.h. + */ +template <typename T> +class Handle { - static jsid initial() { return JSID_VOID; } - static ThingRootKind kind() { return THING_ROOT_ID; } - static bool poisoned(jsid id) { return IsPoisonedId(id); } -}; + public: + /* Copy handles of different types, with implicit coercion. */ + template <typename S> Handle(Handle<S> handle) { + testAssign<S>(); + ptr = reinterpret_cast<const T *>(handle.address()); + } + + /* Get a handle from a rooted stack location, with implicit coercion. */ + template <typename S> inline Handle(const Root<S> &root); + template <typename S> inline Handle(const RootedVar<S> &root); + + const T *address() { return ptr; } -template <> struct RootMethods<jsid> -{ - static jsid initial() { return JSID_VOID; } - static ThingRootKind kind() { return THING_ROOT_ID; } - static bool poisoned(jsid id) { return IsPoisonedId(id); } + operator T () { return value(); } + T operator ->() { return value(); } + + private: + const T *ptr; + T value() { return *ptr; } + + template <typename S> + void testAssign() { +#ifdef DEBUG + T a = RootMethods<T>::initial(); + S b = RootMethods<S>::initial(); + a = b; + (void)a; +#endif + } }; -template <> struct RootMethods<const Value> -{ - static Value initial() { return UndefinedValue(); } - static ThingRootKind kind() { return THING_ROOT_VALUE; } - static bool poisoned(const Value &v) { return IsPoisonedValue(v); } -}; - -template <> struct RootMethods<Value> -{ - static Value initial() { return UndefinedValue(); } - static ThingRootKind kind() { return THING_ROOT_VALUE; } - static bool poisoned(const Value &v) { return IsPoisonedValue(v); } -}; +typedef Handle<JSObject*> HandleObject; +typedef Handle<JSFunction*> HandleFunction; +typedef Handle<JSString*> HandleString; +typedef Handle<jsid> HandleId; +typedef Handle<Value> HandleValue; template <typename T> struct RootMethods<T *> { static T *initial() { return NULL; } static ThingRootKind kind() { return T::rootKind(); } static bool poisoned(T *v) { return IsPoisonedPtr(v); } }; @@ -137,20 +160,22 @@ struct RootMethods<T *> * initialized, as otherwise the GC may try to use the the uninitialized value. * It is generally preferable to use either RootedVar for local variables, or * Handle for arguments. */ template <typename T> class Root { public: - Root(JSContext *cx, const T *ptr + Root(JSContext *cx_, const T *ptr JS_GUARD_OBJECT_NOTIFIER_PARAM) { #ifdef JSGC_ROOT_ANALYSIS + ContextFriendFields *cx = ContextFriendFields::get(cx_); + ThingRootKind kind = RootMethods<T>::kind(); this->stack = reinterpret_cast<Root<T>**>(&cx->thingGCRooters[kind]); this->prev = *stack; *stack = this; #endif JS_ASSERT(!RootMethods<T>::poisoned(*ptr)); @@ -186,63 +211,68 @@ class Root template<typename T> template <typename S> inline Handle<T>::Handle(const Root<S> &root) { testAssign<S>(); ptr = reinterpret_cast<const T *>(root.address()); } -typedef Root<JSObject*> RootObject; -typedef Root<JSFunction*> RootFunction; -typedef Root<Shape*> RootShape; -typedef Root<BaseShape*> RootBaseShape; -typedef Root<types::TypeObject*> RootTypeObject; -typedef Root<JSString*> RootString; -typedef Root<JSAtom*> RootAtom; -typedef Root<jsid> RootId; -typedef Root<Value> RootValue; +typedef Root<JSObject*> RootObject; +typedef Root<JSFunction*> RootFunction; +typedef Root<JSString*> RootString; +typedef Root<jsid> RootId; +typedef Root<Value> RootValue; -/* Mark a stack location as a root for a rooting analysis. */ -class CheckRoot +/* + * Mark a stack location as a root for the rooting analysis, without actually + * rooting it in release builds. This should only be used for stack locations + * of GC things that cannot be relocated by a garbage collection, and that + * are definitely reachable via another path. + */ +class SkipRoot { #if defined(DEBUG) && defined(JSGC_ROOT_ANALYSIS) - CheckRoot **stack, *prev; - const uint8_t *ptr; + SkipRoot **stack, *prev; + const uint8_t *start; + const uint8_t *end; public: template <typename T> - CheckRoot(JSContext *cx, const T *ptr + SkipRoot(JSContext *cx_, const T *ptr JS_GUARD_OBJECT_NOTIFIER_PARAM) { - this->stack = &cx->checkGCRooters; + ContextFriendFields *cx = ContextFriendFields::get(cx_); + + this->stack = &cx->skipGCRooters; this->prev = *stack; *stack = this; - this->ptr = static_cast<const uint8_t*>(ptr); + this->start = (const uint8_t *) ptr; + this->end = this->start + sizeof(T); JS_GUARD_OBJECT_NOTIFIER_INIT; } - ~CheckRoot() + ~SkipRoot() { JS_ASSERT(*stack == this); *stack = prev; } - CheckRoot *previous() { return prev; } + SkipRoot *previous() { return prev; } bool contains(const uint8_t *v, size_t len) { - return ptr >= v && ptr < v + len; + return v >= start && v + len <= end; } #else /* DEBUG && JSGC_ROOT_ANALYSIS */ public: template <typename T> - CheckRoot(JSContext *cx, const T *ptr + SkipRoot(JSContext *cx, const T *ptr JS_GUARD_OBJECT_NOTIFIER_PARAM) { JS_GUARD_OBJECT_NOTIFIER_INIT; } #endif /* DEBUG && JSGC_ROOT_ANALYSIS */ JS_DECL_USE_GUARD_OBJECT_NOTIFIER @@ -256,45 +286,54 @@ class RootedVar RootedVar(JSContext *cx) : ptr(RootMethods<T>::initial()), root(cx, &ptr) {} RootedVar(JSContext *cx, T initial) : ptr(initial), root(cx, &ptr) {} - operator T () { return ptr; } - T operator ->() { return ptr; } + RootedVar() MOZ_DELETE; + RootedVar(const RootedVar &) MOZ_DELETE; + + operator T () const { return ptr; } + T operator ->() const { return ptr; } T * address() { return &ptr; } const T * address() const { return &ptr; } + T & reference() { return ptr; } T raw() { return ptr; } T & operator =(T value) { JS_ASSERT(!RootMethods<T>::poisoned(value)); ptr = value; return ptr; } + T & operator =(const RootedVar &value) + { + ptr = value; + return ptr; + } + private: T ptr; Root<T> root; }; template <typename T> template <typename S> inline Handle<T>::Handle(const RootedVar<S> &root) { ptr = reinterpret_cast<const T *>(root.address()); } -typedef RootedVar<JSObject*> RootedVarObject; -typedef RootedVar<JSFunction*> RootedVarFunction; -typedef RootedVar<Shape*> RootedVarShape; -typedef RootedVar<BaseShape*> RootedVarBaseShape; -typedef RootedVar<types::TypeObject*> RootedVarTypeObject; -typedef RootedVar<JSString*> RootedVarString; -typedef RootedVar<JSAtom*> RootedVarAtom; -typedef RootedVar<jsid> RootedVarId; -typedef RootedVar<Value> RootedVarValue; +typedef RootedVar<JSObject*> RootedVarObject; +typedef RootedVar<JSFunction*> RootedVarFunction; +typedef RootedVar<JSString*> RootedVarString; +typedef RootedVar<jsid> RootedVarId; +typedef RootedVar<Value> RootedVarValue; -} /* namespace js */ +} /* namespace JS */ + +#endif /* __cplusplus */ + #endif /* jsgc_root_h___ */
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -3078,17 +3078,17 @@ JS_ResolveStub(JSContext *cx, JSObject * return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp) { JS_ASSERT(type != JSTYPE_OBJECT && type != JSTYPE_FUNCTION); JS_ASSERT(obj); - return DefaultValue(cx, obj, type, vp); + return DefaultValue(cx, RootedVarObject(cx, obj), type, vp); } JS_PUBLIC_API(JSObject *) JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, JSClass *clasp, JSNative constructor, unsigned nargs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs) { @@ -3618,17 +3618,17 @@ JS_PUBLIC_API(JSBool) JS_AlreadyHasOwnUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, JSBool *foundp) { JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); return atom && JS_AlreadyHasOwnPropertyById(cx, obj, ATOM_TO_JSID(atom), foundp); } static JSBool -DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, const Value &value, +DefinePropertyById(JSContext *cx, HandleObject obj, jsid id, const Value &value, PropertyOp getter, StrictPropertyOp setter, unsigned attrs, unsigned flags, int tinyid) { /* * JSPROP_READONLY has no meaning when accessors are involved. Ideally we'd * throw if this happens, but we've accepted it for long enough that it's * not worth trying to make callers change their ways. Just flip it off on * its way through the API layer so that we can enforce this internally. @@ -3678,44 +3678,46 @@ DefinePropertyById(JSContext *cx, JSObje if (flags != 0 && obj->isNative()) { return !!DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, flags, tinyid); } return obj->defineGeneric(cx, id, value, getter, setter, attrs); } JS_PUBLIC_API(JSBool) -JS_DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, jsval value, +JS_DefinePropertyById(JSContext *cx, JSObject *obj_, jsid id, jsval value, JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) { + RootedVarObject obj(cx, obj_); return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, 0, 0); } JS_PUBLIC_API(JSBool) -JS_DefineElement(JSContext *cx, JSObject *obj, uint32_t index, jsval value, +JS_DefineElement(JSContext *cx, JSObject *obj_, uint32_t index, jsval value, JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) { + RootedVarObject obj(cx, obj_); AssertNoGC(cx); CHECK_REQUEST(cx); jsid id; if (!IndexToId(cx, index, &id)) return false; return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, 0, 0); } static JSBool -DefineProperty(JSContext *cx, JSObject *obj, const char *name, const Value &value, +DefineProperty(JSContext *cx, JSObject *obj_, const char *name, const Value &value_, PropertyOp getter, StrictPropertyOp setter, unsigned attrs, unsigned flags, int tinyid) { jsid id; JSAtom *atom; - RootObject objRoot(cx, &obj); - RootValue valueRoot(cx, &value); + RootedVarObject obj(cx, obj_); + RootedVarValue value(cx, value_); if (attrs & JSPROP_INDEX) { id = INT_TO_JSID(intptr_t(name)); atom = NULL; attrs &= ~JSPROP_INDEX; } else { atom = js_Atomize(cx, name, strlen(name)); if (!atom) @@ -3736,20 +3738,21 @@ JS_DefineProperty(JSContext *cx, JSObjec JS_PUBLIC_API(JSBool) JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, int8_t tinyid, jsval value, PropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) { return DefineProperty(cx, obj, name, value, getter, setter, attrs, Shape::HAS_SHORTID, tinyid); } static JSBool -DefineUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, +DefineUCProperty(JSContext *cx, JSObject *obj_, const jschar *name, size_t namelen, const Value &value, PropertyOp getter, StrictPropertyOp setter, unsigned attrs, unsigned flags, int tinyid) { + RootObject obj(cx, &obj_); JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); return atom && DefinePropertyById(cx, obj, ATOM_TO_JSID(atom), value, getter, setter, attrs, flags, tinyid); } JS_PUBLIC_API(JSBool) JS_DefineUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval value, JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) @@ -3767,17 +3770,17 @@ JS_DefineUCPropertyWithTinyId(JSContext } JS_PUBLIC_API(JSBool) JS_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id, jsval descriptor, JSBool *bp) { AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id, descriptor); - return js_DefineOwnProperty(cx, obj, id, descriptor, bp); + return js_DefineOwnProperty(cx, RootedVarObject(cx, obj), RootedVarId(cx, id), descriptor, bp); } JS_PUBLIC_API(JSObject *) JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *jsclasp, JSObject *proto, unsigned attrs) { AssertNoGC(cx); CHECK_REQUEST(cx); @@ -3796,18 +3799,20 @@ JS_DefineObject(JSContext *cx, JSObject if (!DefineProperty(cx, obj, name, ObjectValue(*nobj), NULL, NULL, attrs, 0, 0)) return NULL; return nobj; } JS_PUBLIC_API(JSBool) -JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds) -{ +JS_DefineConstDoubles(JSContext *cx, JSObject *obj_, JSConstDoubleSpec *cds) +{ + RootedVarObject obj(cx, obj_); + JSBool ok; unsigned attrs; AssertNoGC(cx); CHECK_REQUEST(cx); for (ok = JS_TRUE; cds->name; cds++) { Value value = DoubleValue(cds->dval); attrs = cds->flags; @@ -3944,17 +3949,17 @@ JS_GetUCPropertyAttrsGetterAndSetter(JSC attrsp, foundp, getterp, setterp); } JS_PUBLIC_API(JSBool) JS_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { AssertNoGC(cx); CHECK_REQUEST(cx); - return GetOwnPropertyDescriptor(cx, obj, id, vp); + return GetOwnPropertyDescriptor(cx, RootedVarObject(cx, obj), RootedVarId(cx, id), vp); } static JSBool SetPropertyAttributesById(JSContext *cx, JSObject *obj, jsid id, unsigned attrs, JSBool *foundp) { JSObject *obj2; JSProperty *prop; @@ -4004,17 +4009,17 @@ JS_ForwardGetPropertyTo(JSContext *cx, J assertSameCompartment(cx, onBehalfOf); JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); return obj->getGeneric(cx, onBehalfOf, id, vp); } JS_PUBLIC_API(JSBool) JS_GetPropertyByIdDefault(JSContext *cx, JSObject *obj, jsid id, jsval def, jsval *vp) { - return GetPropertyDefault(cx, obj, id, def, vp); + return GetPropertyDefault(cx, RootedVarObject(cx, obj), RootedVarId(cx, id), def, vp); } JS_PUBLIC_API(JSBool) JS_GetElement(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp) { return JS_ForwardGetElementTo(cx, obj, index, obj, vp); } @@ -4064,17 +4069,17 @@ JS_GetUCProperty(JSContext *cx, JSObject } JS_PUBLIC_API(JSBool) JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, jsval *vp) { AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); - if (!js_GetMethod(cx, obj, id, 0, vp)) + if (!js_GetMethod(cx, RootedVarObject(cx, obj), id, 0, vp)) return JS_FALSE; if (objp) *objp = obj; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, jsval *vp) @@ -4519,21 +4524,24 @@ JS_NewFunctionById(JSContext *cx, JSNati CHECK_REQUEST(cx); assertSameCompartment(cx, parent); RootObject parentRoot(cx, &parent); return js_NewFunction(cx, NULL, native, nargs, flags, parentRoot, JSID_TO_ATOM(id)); } JS_PUBLIC_API(JSObject *) -JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) -{ - AssertNoGC(cx); - CHECK_REQUEST(cx); - assertSameCompartment(cx, parent); // XXX no funobj for now +JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent_) +{ + AssertNoGC(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, parent_); // XXX no funobj for now + + RootedVarObject parent(cx, parent_); + if (!parent) { if (cx->hasfp()) parent = &cx->fp()->scopeChain(); if (!parent) parent = cx->globalObject; JS_ASSERT(parent); } @@ -4542,17 +4550,17 @@ JS_CloneFunctionObject(JSContext *cx, JS * We cannot clone this object, so fail (we used to return funobj, bad * idea, but we changed incompatibly to teach any abusers a lesson!). */ Value v = ObjectValue(*funobj); js_ReportIsNotFunction(cx, &v, 0); return NULL; } - JSFunction *fun = funobj->toFunction(); + RootedVarFunction fun(cx, funobj->toFunction()); if (fun->isInterpreted() && fun->script()->compileAndGo) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_FUNOBJ_SCOPE); return NULL; } return CloneFunctionObject(cx, fun, parent, fun->getAllocKind()); @@ -5060,29 +5068,27 @@ CompileUCFunctionForPrincipalsCommon(JSC RootObject objRoot(cx, &obj); JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); AssertNoGC(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, principals); AutoLastFrameCheck lfc(cx); - JSAtom *funAtom; - if (!name) { - funAtom = NULL; - } else { + RootedVarAtom funAtom(cx); + if (name) { funAtom = js_Atomize(cx, name, strlen(name)); if (!funAtom) return NULL; } Bindings bindings(cx); for (unsigned i = 0; i < nargs; i++) { uint16_t dummy; - JSAtom *argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i])); + RootedVarAtom argAtom(cx, js_Atomize(cx, argnames[i], strlen(argnames[i]))); if (!argAtom || !bindings.addArgument(cx, argAtom, &dummy)) return NULL; } JSFunction *fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, objRoot, funAtom); if (!fun) return NULL; @@ -5253,24 +5259,26 @@ JS_PUBLIC_API(JSBool) JS_ExecuteScriptVersion(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval, JSVersion version) { AutoVersionAPI ava(cx, version); return JS_ExecuteScript(cx, obj, script, rval); } bool -EvaluateUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj, +EvaluateUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj_, JSPrincipals *principals, JSPrincipals *originPrincipals, const jschar *chars, unsigned length, const char *filename, unsigned lineno, jsval *rval, JSVersion compileVersion) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + RootedVarObject obj(cx, obj_); + uint32_t flags = TCF_COMPILE_N_GO | TCF_NEED_SCRIPT_GLOBAL; if (!rval) flags |= TCF_NO_SCRIPT_RVAL; CHECK_REQUEST(cx); AutoLastFrameCheck lfc(cx); JSScript *script = frontend::CompileScript(cx, obj, NULL, principals, originPrincipals, flags, chars, length, filename, lineno, @@ -5372,25 +5380,27 @@ JS_CallFunction(JSContext *cx, JSObject CHECK_REQUEST(cx); assertSameCompartment(cx, obj, fun, JSValueArray(argv, argc)); AutoLastFrameCheck lfc(cx); return Invoke(cx, ObjectOrNullValue(obj), ObjectValue(*fun), argc, argv, rval); } JS_PUBLIC_API(JSBool) -JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, unsigned argc, jsval *argv, +JS_CallFunctionName(JSContext *cx, JSObject *obj_, const char *name, unsigned argc, jsval *argv, jsval *rval) { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); AssertNoGC(cx); CHECK_REQUEST(cx); - assertSameCompartment(cx, obj, JSValueArray(argv, argc)); + assertSameCompartment(cx, obj_, JSValueArray(argv, argc)); AutoLastFrameCheck lfc(cx); + RootedVarObject obj(cx, obj_); + Value v; JSAtom *atom = js_Atomize(cx, name, strlen(name)); return atom && js_GetMethod(cx, obj, ATOM_TO_JSID(atom), 0, &v) && Invoke(cx, ObjectOrNullValue(obj), v, argc, argv, rval); } JS_PUBLIC_API(JSBool) @@ -5754,17 +5764,17 @@ JS_NewDependentString(JSContext *cx, JSS return js_NewDependentString(cx, str, start, length); } JS_PUBLIC_API(JSString *) JS_ConcatStrings(JSContext *cx, JSString *left, JSString *right) { AssertNoGC(cx); CHECK_REQUEST(cx); - return js_ConcatStrings(cx, left, right); + return js_ConcatStrings(cx, RootedVarString(cx, left), RootedVarString(cx, right)); } JS_PUBLIC_API(const jschar *) JS_UndependString(JSContext *cx, JSString *str) { AssertNoGC(cx); CHECK_REQUEST(cx); return str->getCharsZ(cx);
--- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -51,16 +51,17 @@ #include <stddef.h> #include <stdio.h> #include "js-config.h" #include "jspubtd.h" #include "jsutil.h" #include "jsval.h" #include "js/Utility.h" +#include "gc/Root.h" #ifdef __cplusplus #include "jsalloc.h" #include "js/Vector.h" #endif /************************************************************************/ @@ -765,16 +766,30 @@ PrivateUint32Value(uint32_t ui) } JS_ALWAYS_INLINE bool SameType(const Value &lhs, const Value &rhs) { return JSVAL_SAME_TYPE_IMPL(lhs.data, rhs.data); } +template <> struct RootMethods<const Value> +{ + static Value initial() { return UndefinedValue(); } + static ThingRootKind kind() { return THING_ROOT_VALUE; } + static bool poisoned(const Value &v) { return IsPoisonedValue(v); } +}; + +template <> struct RootMethods<Value> +{ + static Value initial() { return UndefinedValue(); } + static ThingRootKind kind() { return THING_ROOT_VALUE; } + static bool poisoned(const Value &v) { return IsPoisonedValue(v); } +}; + /************************************************************************/ #ifndef __GNUC__ /* * The default assignment operator for |struct C| has the signature: * * C& C::operator=(const C&) @@ -1048,17 +1063,17 @@ class AutoEnumStateRooter : private Auto }; template<class T> class AutoVectorRooter : protected AutoGCRooter { public: explicit AutoVectorRooter(JSContext *cx, ptrdiff_t tag JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, tag), vector(cx) + : AutoGCRooter(cx, tag), vector(cx), vectorRoot(cx, &vector) { JS_GUARD_OBJECT_NOTIFIER_INIT; } size_t length() const { return vector.length(); } bool append(const T &v) { return vector.append(v); } @@ -1111,16 +1126,20 @@ class AutoVectorRooter : protected AutoG void makeRangeGCSafe(size_t oldLength) { T *t = vector.begin() + oldLength; for (size_t i = oldLength; i < vector.length(); ++i, ++t) memset(t, 0, sizeof(T)); } typedef js::Vector<T, 8> VectorImpl; VectorImpl vector; + + /* Prevent overwriting of inline elements in vector. */ + SkipRoot vectorRoot; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoValueVector : public AutoVectorRooter<Value> { public: explicit AutoValueVector(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) @@ -2453,26 +2472,44 @@ extern JS_PUBLIC_API(JSBool) JS_IsInRequest(JSRuntime *rt); extern JS_PUBLIC_API(JSBool) JS_IsInSuspendedRequest(JSRuntime *rt); #ifdef __cplusplus JS_END_EXTERN_C +namespace JS { + inline bool IsPoisonedId(jsid iden) { if (JSID_IS_STRING(iden)) return JS::IsPoisonedPtr(JSID_TO_STRING(iden)); if (JSID_IS_OBJECT(iden)) return JS::IsPoisonedPtr(JSID_TO_OBJECT(iden)); return false; } +template <> struct RootMethods<const jsid> +{ + static jsid initial() { return JSID_VOID; } + static ThingRootKind kind() { return THING_ROOT_ID; } + static bool poisoned(jsid id) { return IsPoisonedId(id); } +}; + +template <> struct RootMethods<jsid> +{ + static jsid initial() { return JSID_VOID; } + static ThingRootKind kind() { return THING_ROOT_ID; } + static bool poisoned(jsid id) { return IsPoisonedId(id); } +}; + +} /* namespace JS */ + class JSAutoRequest { public: JSAutoRequest(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) : mContext(cx), mSaveDepth(0) { JS_GUARD_OBJECT_NOTIFIER_INIT; JS_BeginRequest(mContext); } ~JSAutoRequest() {
--- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -478,17 +478,17 @@ GetElements(JSContext *cx, JSObject *aob } } /* * Set the value of the property at the given index to v assuming v is rooted. */ static JSBool -SetArrayElement(JSContext *cx, JSObject *obj, double index, const Value &v) +SetArrayElement(JSContext *cx, HandleObject obj, double index, const Value &v) { JS_ASSERT(index >= 0); if (obj->isDenseArray()) { /* Predicted/prefetched code should favor the remains-dense case. */ JSObject::EnsureDenseResult result = JSObject::ED_SPARSE; do { if (index > uint32_t(-1)) @@ -501,17 +501,17 @@ SetArrayElement(JSContext *cx, JSObject obj->setDenseArrayLength(idx + 1); obj->setDenseArrayElementWithType(cx, idx, v); return true; } while (false); if (result == JSObject::ED_FAILED) return false; JS_ASSERT(result == JSObject::ED_SPARSE); - if (!obj->makeDenseArraySlow(cx)) + if (!JSObject::makeDenseArraySlow(cx, obj)) return JS_FALSE; } AutoIdRooter idr(cx); if (!IndexToId(cx, obj, index, NULL, idr.addr(), JS_TRUE)) return JS_FALSE; JS_ASSERT(!JSID_IS_VOID(idr.id())); @@ -529,17 +529,17 @@ SetArrayElement(JSContext *cx, JSObject * * - Return 0 if the deletion fails because the property is not * configurable (that is, [[Delete]] would return false). Note that if * |strict| is true we will throw, not return zero. * * - Return -1 if an exception occurs (that is, [[Delete]] would throw). */ static int -DeleteArrayElement(JSContext *cx, JSObject *obj, double index, bool strict) +DeleteArrayElement(JSContext *cx, HandleObject obj, double index, bool strict) { JS_ASSERT(index >= 0); JS_ASSERT(floor(index) == index); if (obj->isDenseArray()) { if (index <= UINT32_MAX) { uint32_t idx = uint32_t(index); if (idx < obj->getDenseArrayInitializedLength()) { @@ -564,17 +564,17 @@ DeleteArrayElement(JSContext *cx, JSObje return v.isTrue() ? 1 : 0; } /* * When hole is true, delete the property at the given index. Otherwise set * its value to v assuming v is rooted. */ static JSBool -SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, double index, +SetOrDeleteArrayElement(JSContext *cx, HandleObject obj, double index, JSBool hole, const Value &v) { if (hole) { JS_ASSERT(v.isUndefined()); return DeleteArrayElement(cx, obj, index, true) >= 0; } return SetArrayElement(cx, obj, index, v); } @@ -603,18 +603,20 @@ array_length_getter(JSContext *cx, JSObj vp->setNumber(obj->getArrayLength()); return JS_TRUE; } } while ((obj = obj->getProto()) != NULL); return JS_TRUE; } static JSBool -array_length_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) +array_length_setter(JSContext *cx, JSObject *obj_, jsid id, JSBool strict, Value *vp) { + RootedVarObject obj(cx, obj_); + if (!obj->isArray()) { return obj->defineProperty(cx, cx->runtime->atomState.lengthAtom, *vp, NULL, NULL, JSPROP_ENUMERATE); } uint32_t newlen; if (!ToUint32(cx, *vp, &newlen)) return false; @@ -800,32 +802,44 @@ array_getProperty(JSContext *cx, JSObjec } if (name == cx->runtime->atomState.protoAtom) { vp->setObjectOrNull(obj->getProto()); return true; } if (!obj->isDenseArray()) - return js_GetProperty(cx, obj, receiver, ATOM_TO_JSID(name), vp); + { + return js_GetProperty(cx, + RootedVarObject(cx, obj), + RootedVarObject(cx, receiver), + ATOM_TO_JSID(name), + vp); + } JSObject *proto = obj->getProto(); if (!proto) { vp->setUndefined(); return true; } return proto->getProperty(cx, receiver, name, vp); } static JSBool array_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp) { if (!obj->isDenseArray()) - return js_GetElement(cx, obj, receiver, index, vp); + { + return js_GetElement(cx, + RootedVarObject(cx, obj), + RootedVarObject(cx, receiver), + index, + vp); + } if (index < obj->getDenseArrayInitializedLength()) { *vp = obj->getDenseArrayElement(index); if (!vp->isMagic(JS_ARRAY_HOLE)) { /* Type information for dense array elements must be correct. */ JS_ASSERT_IF(!obj->hasSingletonType(), js::types::TypeHasProperty(cx, obj->type(), JSID_VOID, *vp)); @@ -845,17 +859,21 @@ array_getElement(JSContext *cx, JSObject static JSBool array_getSpecial(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp) { if (obj->isDenseArray() && !obj->getProto()) { vp->setUndefined(); return true; } - return js_GetProperty(cx, obj, receiver, SPECIALID_TO_JSID(sid), vp); + return js_GetProperty(cx, + RootedVarObject(cx, obj), + RootedVarObject(cx, receiver), + SPECIALID_TO_JSID(sid), + vp); } static JSBool array_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp) { Value idval = IdToValue(id); uint32_t index; @@ -891,27 +909,29 @@ slowarray_addProperty(JSContext *cx, JSO static JSType array_typeOf(JSContext *cx, JSObject *obj) { return JSTYPE_OBJECT; } static JSBool -array_setGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict) +array_setGeneric(JSContext *cx, JSObject *obj_, jsid id_, Value *vp, JSBool strict) { - uint32_t i; + RootedVarObject obj(cx, obj_); + RootedVarId id(cx, id_); if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) return array_length_setter(cx, obj, id, strict, vp); if (!obj->isDenseArray()) return js_SetPropertyHelper(cx, obj, id, 0, vp, strict); do { + uint32_t i; if (!js_IdIsIndex(id, &i)) break; if (js_PrototypeHasIndexedProperties(cx, obj)) break; JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, i, 1); if (result != JSObject::ED_OK) { if (result == JSObject::ED_FAILED) @@ -921,30 +941,32 @@ array_setGeneric(JSContext *cx, JSObject } if (i >= obj->getArrayLength()) obj->setDenseArrayLength(i + 1); obj->setDenseArrayElementWithType(cx, i, *vp); return true; } while (false); - if (!obj->makeDenseArraySlow(cx)) + if (!JSObject::makeDenseArraySlow(cx, obj)) return false; return js_SetPropertyHelper(cx, obj, id, 0, vp, strict); } static JSBool array_setProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict) { return array_setGeneric(cx, obj, ATOM_TO_JSID(name), vp, strict); } static JSBool -array_setElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict) +array_setElement(JSContext *cx, JSObject *obj_, uint32_t index, Value *vp, JSBool strict) { + RootedVarObject obj(cx, obj_); + jsid id; if (!IndexToId(cx, index, &id)) return false; if (!obj->isDenseArray()) return js_SetPropertyHelper(cx, obj, id, 0, vp, strict); do { @@ -966,17 +988,17 @@ array_setElement(JSContext *cx, JSObject } if (index >= obj->getArrayLength()) obj->setDenseArrayLength(index + 1); obj->setDenseArrayElementWithType(cx, index, *vp); return true; } while (false); - if (!obj->makeDenseArraySlow(cx)) + if (!JSObject::makeDenseArraySlow(cx, obj)) return false; return js_SetPropertyHelper(cx, obj, id, 0, vp, strict); } static JSBool array_setSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict) { return array_setGeneric(cx, obj, SPECIALID_TO_JSID(sid), vp, strict); @@ -1000,19 +1022,21 @@ js_PrototypeHasIndexedProperties(JSConte return JS_TRUE; if (obj->isIndexed()) return JS_TRUE; } return JS_FALSE; } static JSBool -array_defineGeneric(JSContext *cx, JSObject *obj, jsid id, const Value *value, +array_defineGeneric(JSContext *cx, JSObject *obj_, jsid id, const Value *value, JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs) { + RootedVarObject obj(cx, obj_); + if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) return JS_TRUE; if (!obj->isDenseArray()) return js_DefineProperty(cx, obj, id, value, getter, setter, attrs); do { uint32_t i = 0; // init to shut GCC up @@ -1029,35 +1053,37 @@ array_defineGeneric(JSContext *cx, JSObj } if (i >= obj->getArrayLength()) obj->setDenseArrayLength(i + 1); obj->setDenseArrayElementWithType(cx, i, *value); return true; } while (false); - if (!obj->makeDenseArraySlow(cx)) + if (!JSObject::makeDenseArraySlow(cx, obj)) return false; return js_DefineProperty(cx, obj, id, value, getter, setter, attrs); } static JSBool array_defineProperty(JSContext *cx, JSObject *obj, PropertyName *name, const Value *value, JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs) { return array_defineGeneric(cx, obj, ATOM_TO_JSID(name), value, getter, setter, attrs); } namespace js { /* non-static for direct definition of array elements within the engine */ JSBool -array_defineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *value, +array_defineElement(JSContext *cx, JSObject *obj_, uint32_t index, const Value *value, PropertyOp getter, StrictPropertyOp setter, unsigned attrs) { + RootedVarObject obj(cx, obj_); + if (!obj->isDenseArray()) return js_DefineElement(cx, obj, index, value, getter, setter, attrs); jsid id; if (!IndexToId(cx, index, &id)) return false; do { @@ -1077,17 +1103,17 @@ array_defineElement(JSContext *cx, JSObj } if (index >= obj->getArrayLength()) obj->setDenseArrayLength(index + 1); obj->setDenseArrayElementWithType(cx, index, *value); return true; } while (false); - if (!obj->makeDenseArraySlow(cx)) + if (!JSObject::makeDenseArraySlow(cx, obj)) return false; return js_DefineElement(cx, obj, index, value, getter, setter, attrs); } } // namespace js static JSBool array_defineSpecial(JSContext *cx, JSObject *obj, SpecialId sid, const Value *value, @@ -1208,25 +1234,27 @@ array_trace(JSTracer *trc, JSObject *obj { JS_ASSERT(obj->isDenseArray()); uint32_t initLength = obj->getDenseArrayInitializedLength(); MarkArraySlots(trc, initLength, obj->getDenseArrayElements(), "element"); } static JSBool -array_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props) +array_fix(JSContext *cx, JSObject *obj_, bool *success, AutoIdVector *props) { + RootedVarObject obj(cx, obj_); + JS_ASSERT(obj->isDenseArray()); /* * We must slowify dense arrays; otherwise, we'd need to detect assignments to holes, * since that is effectively adding a new property to the array. */ - if (!obj->makeDenseArraySlow(cx) || + if (!JSObject::makeDenseArraySlow(cx, obj) || !GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, props)) return false; *success = true; return true; } Class js::ArrayClass = { @@ -1349,100 +1377,100 @@ AddLengthProperty(JSContext *cx, JSObjec return obj->addProperty(cx, lengthId, array_length_getter, array_length_setter, SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0); } /* * Convert an array object from fast-and-dense to slow-and-flexible. */ -JSBool -JSObject::makeDenseArraySlow(JSContext *cx) +/* static */ bool +JSObject::makeDenseArraySlow(JSContext *cx, HandleObject obj) { - JS_ASSERT(isDenseArray()); - - MarkTypeObjectFlags(cx, this, + JS_ASSERT(obj->isDenseArray()); + + MarkTypeObjectFlags(cx, obj, OBJECT_FLAG_NON_PACKED_ARRAY | OBJECT_FLAG_NON_DENSE_ARRAY); - uint32_t arrayCapacity = getDenseArrayCapacity(); - uint32_t arrayInitialized = getDenseArrayInitializedLength(); + uint32_t arrayCapacity = obj->getDenseArrayCapacity(); + uint32_t arrayInitialized = obj->getDenseArrayInitializedLength(); /* * Get an allocated array of the existing elements, evicting from the fixed * slots if necessary. */ - if (!hasDynamicElements()) { - if (!growElements(cx, arrayCapacity)) + if (!obj->hasDynamicElements()) { + if (!obj->growElements(cx, arrayCapacity)) return false; - JS_ASSERT(hasDynamicElements()); + JS_ASSERT(obj->hasDynamicElements()); } /* * Save old map now, before calling InitScopeForObject. We'll have to undo * on error. This is gross, but a better way is not obvious. Note: the * exact contents of the array are not preserved on error. */ - js::Shape *oldShape = lastProperty(); + js::Shape *oldShape = obj->lastProperty(); /* Create a native scope. */ - gc::AllocKind kind = getAllocKind(); - Shape *shape = EmptyShape::getInitialShape(cx, &SlowArrayClass, getProto(), + gc::AllocKind kind = obj->getAllocKind(); + Shape *shape = EmptyShape::getInitialShape(cx, &SlowArrayClass, obj->getProto(), oldShape->getObjectParent(), kind); if (!shape) return false; - this->shape_ = shape; + obj->shape_ = shape; /* Take ownership of the dense elements, reset to an empty dense array. */ - HeapSlot *elems = elements; - elements = emptyObjectElements; + HeapSlot *elems = obj->elements; + obj->elements = emptyObjectElements; /* Root all values in the array during conversion. */ AutoValueArray autoArray(cx, (Value *) elems, arrayInitialized); /* * Begin with the length property to share more of the property tree. * The getter/setter here will directly access the object's private value. */ - if (!AddLengthProperty(cx, this)) { - this->shape_ = oldShape; - if (elements != emptyObjectElements) - cx->free_(getElementsHeader()); - elements = elems; + if (!AddLengthProperty(cx, obj)) { + obj->shape_ = oldShape; + if (obj->elements != emptyObjectElements) + cx->free_(obj->getElementsHeader()); + obj->elements = elems; return false; } /* * Create new properties pointing to existing elements. Pack the array to * remove holes, so that shapes use successive slots (as for other objects). */ uint32_t next = 0; for (uint32_t i = 0; i < arrayInitialized; i++) { /* Dense array indexes can always fit in a jsid. */ jsid id; JS_ALWAYS_TRUE(ValueToId(cx, Int32Value(i), &id)); if (elems[i].isMagic(JS_ARRAY_HOLE)) continue; - if (!addDataProperty(cx, id, next, JSPROP_ENUMERATE)) { - this->shape_ = oldShape; - cx->free_(getElementsHeader()); - elements = elems; + if (!obj->addDataProperty(cx, id, next, JSPROP_ENUMERATE)) { + obj->shape_ = oldShape; + cx->free_(obj->getElementsHeader()); + obj->elements = elems; return false; } - initSlot(next, elems[i]); + obj->initSlot(next, elems[i]); next++; } ObjectElements *oldheader = ObjectElements::fromElements(elems); - getElementsHeader()->length = oldheader->length; + obj->getElementsHeader()->length = oldheader->length; cx->free_(oldheader); return true; } #if JS_HAS_TOSOURCE class ArraySharpDetector { @@ -1703,17 +1731,17 @@ array_toString_sub(JSContext *cx, JSObje /* ES5 15.4.4.2. NB: The algorithm here differs from the one in ES3. */ static JSBool array_toString(JSContext *cx, unsigned argc, Value *vp) { JS_CHECK_RECURSION(cx, return false); CallArgs args = CallArgsFromVp(argc, vp); - JSObject *obj = ToObject(cx, &args.thisv()); + RootedVarObject obj(cx, ToObject(cx, &args.thisv())); if (!obj) return false; Value join = args.calleev(); if (!obj->getProperty(cx, cx->runtime->atomState.joinAtom, &join)) return false; if (!js_IsCallable(join)) { @@ -1777,17 +1805,17 @@ InitArrayTypes(JSContext *cx, TypeObject enum ShouldUpdateTypes { UpdateTypes = true, DontUpdateTypes = false }; static bool -InitArrayElements(JSContext *cx, JSObject *obj, uint32_t start, uint32_t count, const Value *vector, ShouldUpdateTypes updateTypes) +InitArrayElements(JSContext *cx, HandleObject obj, uint32_t start, uint32_t count, const Value *vector, ShouldUpdateTypes updateTypes) { JS_ASSERT(count <= MAX_ARRAY_INDEX); if (count == 0) return true; if (updateTypes && !InitArrayTypes(cx, obj->getType(cx), vector, count)) return false; @@ -1826,17 +1854,17 @@ InitArrayElements(JSContext *cx, JSObjec return false; } } if (vector == end) return true; /* Finish out any remaining elements past the max array index. */ - if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx)) + if (obj->isDenseArray() && !JSObject::makeDenseArraySlow(cx, obj)) return false; JS_ASSERT(start == MAX_ARRAY_INDEX + 1); AutoValueRooter tvr(cx); AutoIdRooter idr(cx); Value idval = DoubleValue(MAX_ARRAY_INDEX + 1); do { *tvr.addr() = *vector++; @@ -1905,17 +1933,17 @@ array_join(JSContext *cx, unsigned argc, return false; return array_toString_sub(cx, obj, JS_FALSE, str, args); } static JSBool array_reverse(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - JSObject *obj = ToObject(cx, &args.thisv()); + RootedVarObject obj(cx, ToObject(cx, &args.thisv())); if (!obj) return false; uint32_t len; if (!js_GetLengthProperty(cx, obj, &len)) return false; do { @@ -2209,17 +2237,17 @@ js::array_sort(JSContext *cx, unsigned a JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SORT_ARG); return false; } fval = args[0]; /* non-default compare function */ } else { fval.setNull(); } - JSObject *obj = ToObject(cx, &args.thisv()); + RootedVarObject obj(cx, ToObject(cx, &args.thisv())); if (!obj) return false; uint32_t len; if (!js_GetLengthProperty(cx, obj, &len)) return false; if (len == 0) { args.rval().setObject(*obj); @@ -2375,43 +2403,43 @@ js::array_sort(JSContext *cx, unsigned a args.rval().setObject(*obj); return true; } /* * Perl-inspired push, pop, shift, unshift, and splice methods. */ static bool -array_push_slowly(JSContext *cx, JSObject *obj, CallArgs &args) +array_push_slowly(JSContext *cx, HandleObject obj, CallArgs &args) { uint32_t length; if (!js_GetLengthProperty(cx, obj, &length)) return false; if (!InitArrayElements(cx, obj, length, args.length(), args.array(), UpdateTypes)) return false; /* Per ECMA-262, return the new array length. */ double newlength = length + double(args.length()); args.rval().setNumber(newlength); return js_SetLengthProperty(cx, obj, newlength); } static bool -array_push1_dense(JSContext* cx, JSObject* obj, CallArgs &args) +array_push1_dense(JSContext* cx, HandleObject obj, CallArgs &args) { JS_ASSERT(args.length() == 1); uint32_t length = obj->getArrayLength(); JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, 1); if (result != JSObject::ED_OK) { if (result == JSObject::ED_FAILED) return false; JS_ASSERT(result == JSObject::ED_SPARSE); - if (!obj->makeDenseArraySlow(cx)) + if (!JSObject::makeDenseArraySlow(cx, obj)) return false; return array_push_slowly(cx, obj, args); } obj->setDenseArrayLength(length + 1); obj->setDenseArrayElementWithType(cx, length, args[0]); args.rval().setNumber(obj->getArrayLength()); return true; @@ -2447,29 +2475,29 @@ js_NewbornArrayPush(JSContext *cx, JSObj { return NewbornArrayPushImpl(cx, obj, vp); } JSBool js::array_push(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - JSObject *obj = ToObject(cx, &args.thisv()); + RootedVarObject obj(cx, ToObject(cx, &args.thisv())); if (!obj) return false; /* Insist on one argument and obj of the expected class. */ if (args.length() != 1 || !obj->isDenseArray()) return array_push_slowly(cx, obj, args); return array_push1_dense(cx, obj, args); } static JSBool -array_pop_slowly(JSContext *cx, JSObject* obj, CallArgs &args) +array_pop_slowly(JSContext *cx, HandleObject obj, CallArgs &args) { uint32_t index; if (!js_GetLengthProperty(cx, obj, &index)) return false; if (index == 0) { args.rval().setUndefined(); return js_SetLengthProperty(cx, obj, index); @@ -2485,17 +2513,17 @@ array_pop_slowly(JSContext *cx, JSObject if (!hole && DeleteArrayElement(cx, obj, index, true) < 0) return false; args.rval() = elt; return js_SetLengthProperty(cx, obj, index); } static JSBool -array_pop_dense(JSContext *cx, JSObject* obj, CallArgs &args) +array_pop_dense(JSContext *cx, HandleObject obj, CallArgs &args) { uint32_t index = obj->getArrayLength(); if (index == 0) { args.rval().setUndefined(); return JS_TRUE; } index--; @@ -2515,17 +2543,17 @@ array_pop_dense(JSContext *cx, JSObject* args.rval() = elt; return JS_TRUE; } JSBool js::array_pop(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - JSObject *obj = ToObject(cx, &args.thisv()); + RootedVarObject obj(cx, ToObject(cx, &args.thisv())); if (!obj) return false; if (obj->isDenseArray()) return array_pop_dense(cx, obj, args); return array_pop_slowly(cx, obj, args); } #ifdef JS_METHODJIT @@ -2544,17 +2572,17 @@ mjit::stubs::ArrayShift(VMFrame &f) obj->moveDenseArrayElementsUnbarriered(0, 1, initlen); } #endif /* JS_METHODJIT */ JSBool js::array_shift(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - JSObject *obj = ToObject(cx, &args.thisv()); + RootedVarObject obj(cx, ToObject(cx, &args.thisv())); if (!obj) return JS_FALSE; uint32_t length; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; if (length == 0) { @@ -2596,17 +2624,17 @@ js::array_shift(JSContext *cx, unsigned } return js_SetLengthProperty(cx, obj, length); } static JSBool array_unshift(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - JSObject *obj = ToObject(cx, &args.thisv()); + RootedVarObject obj(cx, ToObject(cx, &args.thisv())); if (!obj) return false; uint32_t length; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; double newlen = length; @@ -2718,17 +2746,17 @@ CanOptimizeForDenseStorage(JSObject *arr /* ES5 15.4.4.12. */ static JSBool array_splice(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); /* Step 1. */ - JSObject *obj = ToObject(cx, &args.thisv()); + RootedVarObject obj(cx, ToObject(cx, &args.thisv())); if (!obj) return false; /* Steps 3-4. */ uint32_t len; if (!js_GetLengthProperty(cx, obj, &len)) return false; @@ -2849,17 +2877,17 @@ array_splice(JSContext *cx, unsigned arg if (obj->isDenseArray()) { JSObject::EnsureDenseResult res = obj->ensureDenseArrayElements(cx, obj->getArrayLength(), itemCount - actualDeleteCount); if (res == JSObject::ED_FAILED) return false; if (res == JSObject::ED_SPARSE) { - if (!obj->makeDenseArraySlow(cx)) + if (!JSObject::makeDenseArraySlow(cx, obj)) return false; } else { JS_ASSERT(res == JSObject::ED_OK); } } if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) { obj->moveDenseArrayElements(actualStart + itemCount, @@ -2941,21 +2969,21 @@ mjit::stubs::ArrayConcatTwoArrays(VMFram */ JSBool js::array_concat(JSContext *cx, unsigned argc, Value *vp) { /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */ Value *p = JS_ARGV(cx, vp) - 1; /* Create a new Array object and root it using *vp. */ - JSObject *aobj = ToObject(cx, &vp[1]); + RootedVarObject aobj(cx, ToObject(cx, &vp[1])); if (!aobj) return false; - JSObject *nobj; + RootedVarObject nobj(cx); uint32_t length; if (aobj->isDenseArray()) { length = aobj->getArrayLength(); const Value *vector = aobj->getDenseArrayElements(); uint32_t initlen = aobj->getDenseArrayInitializedLength(); nobj = NewDenseCopiedArray(cx, initlen, vector); if (!nobj) return JS_FALSE; @@ -3009,17 +3037,16 @@ js::array_concat(JSContext *cx, unsigned } return js_SetLengthProperty(cx, nobj, length); } static JSBool array_slice(JSContext *cx, unsigned argc, Value *vp) { - JSObject *nobj; uint32_t length, begin, end, slot; JSBool hole; CallArgs args = CallArgsFromVp(argc, vp); JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; @@ -3054,16 +3081,18 @@ array_slice(JSContext *cx, unsigned argc } end = (uint32_t)d; } } if (begin > end) begin = end; + RootedVarObject nobj(cx); + if (obj->isDenseArray() && end <= obj->getDenseArrayInitializedLength() && !js_PrototypeHasIndexedProperties(cx, obj)) { nobj = NewDenseCopiedArray(cx, end - begin, obj->getDenseArrayElements() + begin); if (!nobj) return JS_FALSE; TryReuseArrayType(obj, nobj); args.rval().setObject(*nobj); return JS_TRUE; @@ -3333,17 +3362,17 @@ array_map(JSContext *cx, unsigned argc, JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK); if (!callable) return false; /* Step 5. */ Value thisv = args.length() >= 2 ? args[1] : UndefinedValue(); /* Step 6. */ - JSObject *arr = NewDenseAllocatedArray(cx, len); + RootedVarObject arr(cx, NewDenseAllocatedArray(cx, len)); if (!arr) return false; TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array); if (!newtype) return false; arr->setType(newtype); /* Step 7. */ @@ -3409,17 +3438,17 @@ array_filter(JSContext *cx, unsigned arg JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK); if (!callable) return false; /* Step 5. */ Value thisv = args.length() >= 2 ? args[1] : UndefinedValue(); /* Step 6. */ - JSObject *arr = NewDenseAllocatedArray(cx, 0); + RootedVarObject arr(cx, NewDenseAllocatedArray(cx, 0)); if (!arr) return false; TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array); if (!newtype) return false; arr->setType(newtype); /* Step 7. */ @@ -3648,17 +3677,17 @@ static JSFunctionSpec array_static_metho JS_FS_END }; /* ES5 15.4.2 */ JSBool js_Array(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Array); + RootedVarTypeObject type(cx, GetTypeCallerInitObject(cx, JSProto_Array)); if (!type) return JS_FALSE; if (args.length() != 1 || !args[0].isNumber()) { if (!InitArrayTypes(cx, type, args.array(), args.length())) return false; JSObject *obj = (args.length() == 0) ? NewDenseEmptyArray(cx) @@ -3789,17 +3818,17 @@ NewArray(JSContext *cx, uint32_t length, /* Fixup the elements pointer and length, which may be incorrect. */ obj->setFixedElements(); obj->setArrayLength(cx, length); if (allocateCapacity && !EnsureNewArrayElements(cx, obj, length)) return NULL; return obj; } - Root<GlobalObject*> parentRoot(cx, &parent); + JS::Root<GlobalObject*> parentRoot(cx, &parent); if (!proto && !FindProto(cx, &ArrayClass, parentRoot, &proto)) return NULL; RootObject protoRoot(cx, &proto); RootedVarTypeObject type(cx); type = proto->getNewType(cx); @@ -3882,17 +3911,17 @@ NewDenseCopiedArray(JSContext *cx, uint3 obj->initDenseArrayElements(0, vp, length); return obj; } JSObject * NewSlowEmptyArray(JSContext *cx) { - JSObject *obj = NewBuiltinClassInstance(cx, &SlowArrayClass); + RootedVarObject obj(cx, NewBuiltinClassInstance(cx, &SlowArrayClass)); if (!obj || !AddLengthProperty(cx, obj)) return NULL; obj->setArrayLength(cx, 0); return obj; } }
--- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -68,38 +68,16 @@ js_IdIsIndex(jsid id, uint32_t *indexp) } if (JS_UNLIKELY(!JSID_IS_STRING(id))) return JS_FALSE; return js::StringIsArrayIndex(JSID_TO_ATOM(id), indexp); } -/* - * Dense arrays are not native -- aobj->isNative() for a dense array aobj - * results in false, meaning aobj->map does not point to a js::Shape. - * - * But Array methods are called via aobj.sort(), e.g., and the interpreter and - * the trace recorder must consult the property cache in order to perform well. - * The cache works only for native objects. - * - * Therefore the interpreter (js_Interpret in JSOP_GETPROP and JSOP_CALLPROP) - * and js_GetPropertyHelper use this inline function to skip up one link in the - * prototype chain when obj is a dense array, in order to find a native object - * (to wit, Array.prototype) in which to probe for cached methods. - * - * Note that setting aobj.__proto__ for a dense array aobj turns aobj into a - * slow array, avoiding the neede to skip. - * - * Callers of js_GetProtoIfDenseArray must take care to use the original object - * (obj) for the |this| value of a getter, setter, or method call (bug 476447). - */ -inline JSObject * -js_GetProtoIfDenseArray(JSObject *obj); - extern JSObject * js_InitArrayClass(JSContext *cx, JSObject *obj); extern bool js_InitContextBusyArrayTable(JSContext *cx); namespace js {
--- a/js/src/jsbool.cpp +++ b/js/src/jsbool.cpp @@ -154,24 +154,25 @@ Boolean(JSContext *cx, unsigned argc, Va return true; } JSObject * js_InitBooleanClass(JSContext *cx, JSObject *obj) { JS_ASSERT(obj->isNative()); - GlobalObject *global = &obj->asGlobal(); + RootedVar<GlobalObject*> global(cx, &obj->asGlobal()); - JSObject *booleanProto = global->createBlankPrototype(cx, &BooleanClass); + RootedVarObject booleanProto (cx, global->createBlankPrototype(cx, &BooleanClass)); if (!booleanProto) return NULL; booleanProto->setFixedSlot(BooleanObject::PRIMITIVE_VALUE_SLOT, BooleanValue(false)); - JSFunction *ctor = global->createConstructor(cx, Boolean, CLASS_ATOM(cx, Boolean), 1); + RootedVarFunction ctor(cx); + ctor = global->createConstructor(cx, Boolean, CLASS_ATOM(cx, Boolean), 1); if (!ctor) return NULL; if (!LinkConstructorAndPrototype(cx, ctor, booleanProto)) return NULL; if (!DefinePropertiesAndBrand(cx, booleanProto, NULL, boolean_methods)) return NULL;
--- a/js/src/jsclone.cpp +++ b/js/src/jsclone.cpp @@ -601,26 +601,26 @@ JSStructuredCloneWriter::startWrite(cons bool JSStructuredCloneWriter::write(const Value &v) { if (!startWrite(v)) return false; while (!counts.empty()) { - JSObject *obj = &objs.back().toObject(); + RootedVarObject obj(context(), &objs.back().toObject()); // The objects in |obj| can live in other compartments. AutoEnterCompartmentAndPushPrincipal ac; if (!ac.enter(context(), obj)) return false; if (counts.back()) { counts.back()--; - jsid id = ids.back(); + RootedVarId id(context(), ids.back()); ids.popBack(); checkStack(); if (JSID_IS_STRING(id) || JSID_IS_INT(id)) { /* * If obj still has an own property named id, write it out. * The cost of re-checking could be avoided by using * NativeIterators. */
--- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -960,17 +960,17 @@ JSContext::JSContext(JSRuntime *rt) #ifdef DEBUG , stackIterAssertionEnabled(true) #endif { PodZero(&link); #ifdef JSGC_ROOT_ANALYSIS PodArrayZero(thingGCRooters); #ifdef DEBUG - checkGCRooters = NULL; + skipGCRooters = NULL; #endif #endif } JSContext::~JSContext() { /* Free the stuff hanging off of cx. */ if (parseMapPool_)
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -740,19 +740,16 @@ struct JSArgumentFormatMap { size_t length; JSArgumentFormatter formatter; JSArgumentFormatMap *next; }; #endif namespace js { -template <typename T> class Root; -class CheckRoot; - struct AutoResolving; static inline bool OptionsHasXML(uint32_t options) { return !!(options & JSOPTION_XML); } @@ -1019,39 +1016,16 @@ struct JSContext : js::ContextFriendFiel inline js::LifoAlloc &typeLifoAlloc(); #ifdef JS_THREADSAFE unsigned outstandingRequests;/* number of JS_BeginRequest calls without the corresponding JS_EndRequest. */ #endif - -#ifdef JSGC_ROOT_ANALYSIS - - /* - * Stack allocated GC roots for stack GC heap pointers, which may be - * overwritten if moved during a GC. - */ - js::Root<js::gc::Cell*> *thingGCRooters[js::THING_ROOT_COUNT]; - -#ifdef DEBUG - /* - * Stack allocated list of stack locations which hold non-relocatable - * GC heap pointers (where the target is rooted somewhere else) or integer - * values which may be confused for GC heap pointers. These are used to - * suppress false positives which occur when a rooting analysis treats the - * location as holding a relocatable pointer, but have no other effect on - * GC behavior. - */ - js::CheckRoot *checkGCRooters; -#endif - -#endif /* JSGC_ROOT_ANALYSIS */ - /* Stored here to avoid passing it around as a parameter. */ unsigned resolveFlags; /* Random number generator state, used by jsmath.cpp. */ int64_t rngSeed; /* Location to stash the iteration value between JSOP_MOREITER and JSOP_ITERNEXT. */ js::Value iterValue;
--- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -321,17 +321,17 @@ CallJSNative(JSContext *cx, Native nativ extern JSBool CallOrConstructBoundFunction(JSContext *, unsigned, js::Value *); STATIC_PRECONDITION(ubound(args.argv_) >= argc) JS_ALWAYS_INLINE bool CallJSNativeConstructor(JSContext *cx, Native native, const CallArgs &args) { #ifdef DEBUG - JSObject &callee = args.callee(); + RootedVarObject callee(cx, &args.callee()); #endif JS_ASSERT(args.thisv().isMagic()); if (!CallJSNative(cx, native, args)) return false; /* * Native constructors must return non-primitive values on success. @@ -346,18 +346,18 @@ CallJSNativeConstructor(JSContext *cx, N * CallOrConstructBoundFunction is an exception as well because we * might have used bind on a proxy function. * * (new Object(Object)) returns the callee. */ JS_ASSERT_IF(native != FunctionProxyClass.construct && native != CallableObjectClass.construct && native != js::CallOrConstructBoundFunction && - (!callee.isFunction() || callee.toFunction()->native() != js_Object), - !args.rval().isPrimitive() && callee != args.rval().toObject()); + (!callee->isFunction() || callee->toFunction()->native() != js_Object), + !args.rval().isPrimitive() && callee != &args.rval().toObject()); return true; } JS_ALWAYS_INLINE bool CallJSPropertyOp(JSContext *cx, PropertyOp op, JSObject *receiver, jsid id, Value *vp) { assertSameCompartment(cx, receiver, id, *vp);
--- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -498,17 +498,18 @@ msFromTime(double t) */ static JSBool date_convert(JSContext *cx, JSObject *obj, JSType hint, Value *vp) { JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID); JS_ASSERT(obj->isDate()); - return DefaultValue(cx, obj, (hint == JSTYPE_VOID) ? JSTYPE_STRING : hint, vp); + return DefaultValue(cx, RootedVarObject(cx, obj), + (hint == JSTYPE_VOID) ? JSTYPE_STRING : hint, vp); } /* * Other Support routines and definitions */ Class js::DateClass = { js_Date_str, @@ -2660,24 +2661,25 @@ js_Date(JSContext *cx, unsigned argc, Va JSObject * js_InitDateClass(JSContext *cx, JSObject *obj) { JS_ASSERT(obj->isNative()); /* Set the static LocalTZA. */ LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond); - GlobalObject *global = &obj->asGlobal(); - - JSObject *dateProto = global->createBlankPrototype(cx, &DateClass); + RootedVar<GlobalObject*> global(cx, &obj->asGlobal()); + + RootedVarObject dateProto(cx, global->createBlankPrototype(cx, &DateClass)); if (!dateProto) return NULL; SetDateToNaN(cx, dateProto); - JSFunction *ctor = global->createConstructor(cx, js_Date, CLASS_ATOM(cx, Date), MAXARGS); + RootedVarFunction ctor(cx); + ctor = global->createConstructor(cx, js_Date, CLASS_ATOM(cx, Date), MAXARGS); if (!ctor) return NULL; if (!LinkConstructorAndPrototype(cx, ctor, dateProto)) return NULL; if (!DefinePropertiesAndBrand(cx, ctor, NULL, date_static_methods)) return NULL;
--- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -792,23 +792,25 @@ JS_PropertyIterator(JSObject *obj, JSSco JS_ASSERT(shape->isEmptyShape()); shape = NULL; } return *iteratorp = reinterpret_cast<JSScopeProperty *>(const_cast<Shape *>(shape)); } JS_PUBLIC_API(JSBool) -JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, +JS_GetPropertyDesc(JSContext *cx, JSObject *obj_, JSScopeProperty *sprop, JSPropertyDesc *pd) { - assertSameCompartment(cx, obj); + assertSameCompartment(cx, obj_); Shape *shape = (Shape *) sprop; pd->id = IdToJsval(shape->propid()); + RootedVarObject obj(cx, obj_); + JSBool wasThrowing = cx->isExceptionPending(); Value lastException = UndefinedValue(); if (wasThrowing) lastException = cx->getPendingException(); cx->clearPendingException(); if (!js_GetProperty(cx, obj, shape->propid(), &pd->value)) { if (!cx->isExceptionPending()) {
--- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -276,18 +276,18 @@ struct SuppressErrorsGuard JS_SetErrorReporter(cx, prevReporter); } }; static void SetExnPrivate(JSContext *cx, JSObject *exnObject, JSExnPrivate *priv); static bool -InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message, - JSString *filename, unsigned lineno, JSErrorReport *report, int exnType) +InitExnPrivate(JSContext *cx, HandleObject exnObject, HandleString message, + HandleString filename, unsigned lineno, JSErrorReport *report, int exnType) { JS_ASSERT(exnObject->isError()); JS_ASSERT(!exnObject->getPrivate()); JSCheckAccessOp checkAccess = cx->runtime->securityCallbacks->checkObjectAccess; Vector<JSStackTraceStackElem> frames(cx); { @@ -418,27 +418,29 @@ exn_finalize(FreeOp *fop, JSObject *obj) JS_DropPrincipals(fop->runtime(), prin); fop->free_(report); } fop->free_(priv); } } static JSBool -exn_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, +exn_resolve(JSContext *cx, JSObject *obj_, jsid id, unsigned flags, JSObject **objp) { JSExnPrivate *priv; JSString *str; JSAtom *atom; JSString *stack; const char *prop; jsval v; unsigned attrs; + RootedVarObject obj(cx, obj_); + *objp = NULL; priv = GetExnPrivate(obj); if (priv && JSID_IS_ATOM(id)) { str = JSID_TO_STRING(id); atom = cx->runtime->atomState.messageAtom; if (str == atom) { prop = js_message_str; @@ -637,38 +639,38 @@ Exception(JSContext *cx, unsigned argc, return false; if (!protov.isObject()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, "Error"); return false; } JSObject *errProto = &protov.toObject(); - JSObject *obj = NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL); + RootedVarObject obj(cx, NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL)); if (!obj) return false; /* Set the 'message' property. */ - JSString *message; + RootedVarString message(cx); if (args.hasDefined(0)) { message = ToString(cx, args[0]); if (!message) return false; args[0].setString(message); } else { message = NULL; } /* Find the scripted caller. */ FrameRegsIter iter(cx); while (!iter.done() && !iter.fp()->isScriptFrame()) ++iter; /* Set the 'fileName' property. */ - JSString *filename; + RootedVarString filename(cx); if (args.length() > 1) { filename = ToString(cx, args[1]); if (!filename) return false; args[1].setString(filename); } else { if (!iter.done()) { filename = FilenameToString(cx, iter.fp()->script()->filename); @@ -713,17 +715,17 @@ exn_toString(JSContext *cx, unsigned arg JSObject &obj = args.thisv().toObject(); /* Step 3. */ Value nameVal; if (!obj.getProperty(cx, cx->runtime->atomState.nameAtom, &nameVal)) return false; /* Step 4. */ - JSString *name; + RootedVarString name(cx); if (nameVal.isUndefined()) { name = CLASS_ATOM(cx, Error); } else { name = ToString(cx, nameVal); if (!name) return false; } @@ -866,44 +868,45 @@ JS_STATIC_ASSERT(JSProto_Error + JSEXN_I JS_STATIC_ASSERT(JSProto_Error + JSEXN_EVALERR == JSProto_EvalError); JS_STATIC_ASSERT(JSProto_Error + JSEXN_RANGEERR == JSProto_RangeError); JS_STATIC_ASSERT(JSProto_Error + JSEXN_REFERENCEERR == JSProto_ReferenceError); JS_STATIC_ASSERT(JSProto_Error + JSEXN_SYNTAXERR == JSProto_SyntaxError); JS_STATIC_ASSERT(JSProto_Error + JSEXN_TYPEERR == JSProto_TypeError); JS_STATIC_ASSERT(JSProto_Error + JSEXN_URIERR == JSProto_URIError); static JSObject * -InitErrorClass(JSContext *cx, GlobalObject *global, int type, JSObject &proto) +InitErrorClass(JSContext *cx, Handle<GlobalObject*> global, int type, HandleObject proto) { JSProtoKey key = GetExceptionProtoKey(type); - JSAtom *name = cx->runtime->atomState.classAtoms[key]; - JSObject *errorProto = global->createBlankPrototypeInheriting(cx, &ErrorClass, proto); + RootedVarAtom name(cx, cx->runtime->atomState.classAtoms[key]); + RootedVarObject errorProto(cx, global->createBlankPrototypeInheriting(cx, &ErrorClass, *proto)); if (!errorProto) return NULL; - Value empty = StringValue(cx->runtime->emptyString); - jsid nameId = ATOM_TO_JSID(cx->runtime->atomState.nameAtom); - jsid messageId = ATOM_TO_JSID(cx->runtime->atomState.messageAtom); - jsid fileNameId = ATOM_TO_JSID(cx->runtime->atomState.fileNameAtom); - jsid lineNumberId = ATOM_TO_JSID(cx->runtime->atomState.lineNumberAtom); + RootedVarValue empty(cx, StringValue(cx->runtime->emptyString)); + RootedVarId nameId(cx, ATOM_TO_JSID(cx->runtime->atomState.nameAtom)); + RootedVarId messageId(cx, ATOM_TO_JSID(cx->runtime->atomState.messageAtom)); + RootedVarId fileNameId(cx, ATOM_TO_JSID(cx->runtime->atomState.fileNameAtom)); + RootedVarId lineNumberId(cx, ATOM_TO_JSID(cx->runtime->atomState.lineNumberAtom)); if (!DefineNativeProperty(cx, errorProto, nameId, StringValue(name), JS_PropertyStub, JS_StrictPropertyStub, 0, 0, 0) || !DefineNativeProperty(cx, errorProto, messageId, empty, JS_PropertyStub, JS_StrictPropertyStub, 0, 0, 0) || !DefineNativeProperty(cx, errorProto, fileNameId, empty, JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0) || !DefineNativeProperty(cx, errorProto, lineNumberId, Int32Value(0), JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0)) { return NULL; } /* Create the corresponding constructor. */ - JSFunction *ctor = global->createConstructor(cx, Exception, name, 1, - JSFunction::ExtendedFinalizeKind); + RootedVarFunction ctor(cx); + ctor = global->createConstructor(cx, Exception, name, 1, + JSFunction::ExtendedFinalizeKind); if (!ctor) return NULL; ctor->setExtendedSlot(0, Int32Value(int32_t(type))); if (!LinkConstructorAndPrototype(cx, ctor, errorProto)) return NULL; if (!DefineConstructorAndPrototype(cx, global, key, ctor, errorProto)) @@ -915,34 +918,35 @@ InitErrorClass(JSContext *cx, GlobalObje } JSObject * js_InitExceptionClasses(JSContext *cx, JSObject *obj) { JS_ASSERT(obj->isGlobal()); JS_ASSERT(obj->isNative()); - GlobalObject *global = &obj->asGlobal(); + RootedVar<GlobalObject*> global(cx, &obj->asGlobal()); - JSObject *objectProto = global->getOrCreateObjectPrototype(cx); + RootedVarObject objectProto(cx, global->getOrCreateObjectPrototype(cx)); if (!objectProto) return NULL; /* Initialize the base Error class first. */ - JSObject *errorProto = InitErrorClass(cx, global, JSEXN_ERR, *objectProto); + RootedVarObject errorProto(cx); + errorProto = InitErrorClass(cx, global, JSEXN_ERR, objectProto); if (!errorProto) return NULL; /* |Error.prototype| alone has method properties. */ if (!DefinePropertiesAndBrand(cx, errorProto, NULL, exception_methods)) return NULL; /* Define all remaining *Error constructors. */ for (int i = JSEXN_ERR + 1; i < JSEXN_LIMIT; i++) { - if (!InitErrorClass(cx, global, i, *errorProto)) + if (!InitErrorClass(cx, global, i, errorProto)) return NULL; } return errorProto; } const JSErrorFormatString* js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale, @@ -985,26 +989,43 @@ GetErrorTypeNameFromNumber(JSContext* cx static struct exnname { char *name; char *exception; } errortoexnname[] = { #define MSG_DEF(name, number, count, exception, format) \ {#name, #exception}, #include "js.msg" #undef MSG_DEF }; #endif /* DEBUG */ +struct AutoSetGeneratingError +{ + JSContext *cx; + + AutoSetGeneratingError(JSContext *cx) + : cx(cx) + { + JS_ASSERT(!cx->generatingError); + cx->generatingError = true; + } + + ~AutoSetGeneratingError() + { + JS_ASSERT(cx->generatingError); + cx->generatingError = false; + } +}; + JSBool js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp, JSErrorCallback callback, void *userRef) { JSErrNum errorNumber; const JSErrorFormatString *errorString; JSExnType exn; jsval tv[4]; - JSObject *errProto, *errObject; - JSString *messageStr, *filenameStr; + JSObject *errProto; /* * Tell our caller to report immediately if this report is just a warning. */ JS_ASSERT(reportp); if (JSREPORT_IS_WARNING(reportp->flags)) return false; @@ -1044,25 +1065,28 @@ js_ErrorToException(JSContext *cx, const * Try to get an appropriate prototype by looking up the corresponding * exception constructor name in the scope chain of the current context's * top stack frame, or in the global object if no frame is active. */ if (!js_GetClassPrototype(cx, NULL, GetExceptionProtoKey(exn), &errProto)) return false; tv[0] = OBJECT_TO_JSVAL(errProto); - if (!(errObject = NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL))) + RootedVarObject errObject(cx, NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL)); + if (!errObject) return false; tv[1] = OBJECT_TO_JSVAL(errObject); - if (!(messageStr = JS_NewStringCopyZ(cx, message))) + RootedVarString messageStr(cx, JS_NewStringCopyZ(cx, message)); + if (!messageStr) return false; tv[2] = STRING_TO_JSVAL(messageStr); - if (!(filenameStr = JS_NewStringCopyZ(cx, reportp->filename))) + RootedVarString filenameStr(cx, JS_NewStringCopyZ(cx, reportp->filename)); + if (!filenameStr) return false; tv[3] = STRING_TO_JSVAL(filenameStr); if (!InitExnPrivate(cx, errObject, messageStr, filenameStr, reportp->lineno, reportp, exn)) { return false; } @@ -1093,26 +1117,26 @@ IsDuckTypedErrorObject(JSContext *cx, JS *filename_strp = filename_str; return true; } JSBool js_ReportUncaughtException(JSContext *cx) { - jsval exn; JSObject *exnObject; jsval roots[6]; JSErrorReport *reportp, report; JSString *str; if (!JS_IsExceptionPending(cx)) return true; - if (!JS_GetPendingException(cx, &exn)) + RootedVarValue exn(cx); + if (!JS_GetPendingException(cx, exn.address())) return false; PodArrayZero(roots); AutoArrayRooter tvr(cx, ArrayLength(roots), roots); /* * Because ToString below could error and an exception object could become * unrooted, we must root exnObject. Later, if exnObject is non-null, we
--- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -184,27 +184,16 @@ JS_DefineFunctionsWithHelp(JSContext *cx #endif JS_END_EXTERN_C #ifdef __cplusplus namespace js { -struct ContextFriendFields { - JSRuntime *const runtime; - - ContextFriendFields(JSRuntime *rt) - : runtime(rt) { } - - static const ContextFriendFields *get(const JSContext *cx) { - return reinterpret_cast<const ContextFriendFields *>(cx); - } -}; - struct RuntimeFriendFields { /* * If non-zero, we were been asked to call the operation callback as soon * as possible. */ volatile int32_t interrupt; /* Limit pointer for checking native stack consumption. */
--- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -239,17 +239,17 @@ fun_enumerate(JSContext *cx, JSObject *o if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED)) return false; } return true; } static JSObject * -ResolveInterpretedFunctionPrototype(JSContext *cx, JSObject *obj) +ResolveInterpretedFunctionPrototype(JSContext *cx, HandleObject obj) { #ifdef DEBUG JSFunction *fun = obj->toFunction(); JS_ASSERT(fun->isInterpreted()); JS_ASSERT(!fun->isFunctionPrototype()); #endif /* @@ -262,17 +262,17 @@ ResolveInterpretedFunctionPrototype(JSCo /* * Make the prototype object an instance of Object with the same parent * as the function object itself. */ JSObject *objProto = obj->global().getOrCreateObjectPrototype(cx); if (!objProto) return NULL; - JSObject *proto = NewObjectWithGivenProto(cx, &ObjectClass, objProto, NULL); + RootedVarObject proto(cx, NewObjectWithGivenProto(cx, &ObjectClass, objProto, NULL)); if (!proto || !proto->setSingletonType(cx)) return NULL; /* * Per ES5 15.3.5.2 a user-defined function's .prototype property is * initially non-configurable, non-enumerable, and writable. Per ES5 13.2 * the prototype's .constructor property is configurable, non-enumerable, * and writable. @@ -1073,17 +1073,17 @@ Function(JSContext *cx, unsigned argc, V /* * Check that it's a name. This also implicitly guards against * TOK_ERROR, which was already reported. */ if (tt != TOK_NAME) return OnBadFormal(cx, tt); /* Check for a duplicate parameter name. */ - PropertyName *name = ts.currentToken().name(); + RootedVar<PropertyName*> name(cx, ts.currentToken().name()); if (bindings.hasBinding(cx, name)) { JSAutoByteString bytes; if (!js_AtomToPrintableString(cx, name, &bytes)) return false; if (!ReportCompileErrorNumber(cx, &ts, NULL, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_DUPLICATE_FORMAL, bytes.ptr())) { @@ -1145,48 +1145,51 @@ Function(JSContext *cx, unsigned argc, V bool IsBuiltinFunctionConstructor(JSFunction *fun) { return fun->maybeNative() == Function; } const Shape * -LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj) +LookupInterpretedFunctionPrototype(JSContext *cx, RootedVarObject funobj) { #ifdef DEBUG JSFunction *fun = funobj->toFunction(); JS_ASSERT(fun->isInterpreted()); JS_ASSERT(!fun->isFunctionPrototype()); JS_ASSERT(!funobj->isBoundFunction()); #endif jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); - const Shape *shape = funobj->nativeLookup(cx, id); + RootedVar<const Shape*> shape(cx, funobj->nativeLookup(cx, id)); if (!shape) { if (!ResolveInterpretedFunctionPrototype(cx, funobj)) return NULL; + id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); shape = funobj->nativeLookup(cx, id); } JS_ASSERT(!shape->configurable()); JS_ASSERT(shape->isDataDescriptor()); JS_ASSERT(shape->hasSlot()); return shape; } } /* namespace js */ JSFunction * js_NewFunction(JSContext *cx, JSObject *funobj, Native native, unsigned nargs, - unsigned flags, HandleObject parent, JSAtom *atom, js::gc::AllocKind kind) + unsigned flags, HandleObject parent, JSAtom *atom_, js::gc::AllocKind kind) { JS_ASSERT(kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind); JS_ASSERT(sizeof(JSFunction) <= gc::Arena::thingSize(JSFunction::FinalizeKind)); JS_ASSERT(sizeof(FunctionExtended) <= gc::Arena::thingSize(JSFunction::ExtendedFinalizeKind)); + RootedVarAtom atom(cx, atom_); + JSFunction *fun; if (funobj) { JS_ASSERT(funobj->isFunction()); JS_ASSERT(funobj->getParent() == parent); } else { funobj = NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent), kind); if (!funobj) @@ -1213,18 +1216,18 @@ js_NewFunction(JSContext *cx, JSObject * if (native && !fun->setSingletonType(cx)) return NULL; return fun; } JSFunction * JS_FASTCALL -js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, - JSObject *proto, gc::AllocKind kind) +js_CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent, + HandleObject proto, gc::AllocKind kind) { JS_ASSERT(parent); JS_ASSERT(proto); JSObject *cloneobj = NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent), kind); if (!cloneobj) return NULL; JSFunction *clone = static_cast<JSFunction *>(cloneobj);
--- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -240,17 +240,18 @@ extern JSString * fun_toStringHelper(JSContext *cx, JSObject *obj, unsigned indent); extern JSFunction * js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, unsigned nargs, unsigned flags, js::HandleObject parent, JSAtom *atom, js::gc::AllocKind kind = JSFunction::FinalizeKind); extern JSFunction * JS_FASTCALL -js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, JSObject *proto, +js_CloneFunctionObject(JSContext *cx, js::HandleFunction fun, + js::HandleObject parent, js::HandleObject proto, js::gc::AllocKind kind = JSFunction::FinalizeKind); extern JSFunction * js_DefineFunction(JSContext *cx, js::HandleObject obj, jsid id, JSNative native, unsigned nargs, unsigned flags, js::gc::AllocKind kind = JSFunction::FinalizeKind); /*
--- a/js/src/jsfuninlines.h +++ b/js/src/jsfuninlines.h @@ -140,23 +140,23 @@ IsNativeFunction(const js::Value &v, JSN * When we have an object of a builtin class, we don't quite know what its * valueOf/toString methods are, since these methods may have been overwritten * or shadowed. However, we can still do better than the general case by * hard-coding the necessary properties for us to find the native we expect. * * TODO: a per-thread shape-based cache would be faster and simpler. */ static JS_ALWAYS_INLINE bool -ClassMethodIsNative(JSContext *cx, JSObject *obj, Class *clasp, jsid methodid, JSNative native) +ClassMethodIsNative(JSContext *cx, HandleObject obj, Class *clasp, HandleId methodid, JSNative native) { JS_ASSERT(obj->getClass() == clasp); Value v; if (!HasDataProperty(cx, obj, methodid, &v)) { - JSObject *proto = obj->getProto(); + RootedVarObject proto(cx, obj->getProto()); if (!proto || proto->getClass() != clasp || !HasDataProperty(cx, proto, methodid, &v)) return false; } return js::IsNativeFunction(v, native); } extern JS_ALWAYS_INLINE bool @@ -200,55 +200,40 @@ GetFunctionNameBytes(JSContext *cx, JSFu extern JSFunctionSpec function_methods[]; extern JSBool Function(JSContext *cx, unsigned argc, Value *vp); extern bool IsBuiltinFunctionConstructor(JSFunction *fun); -/* - * Preconditions: funobj->isInterpreted() && !funobj->isFunctionPrototype() && - * !funobj->isBoundFunction(). This is sufficient to establish that funobj has - * a non-configurable non-method .prototype data property, thought it might not - * have been resolved yet, and its value could be anything. - * - * Return the shape of the .prototype property of funobj, resolving it if - * needed. On error, return NULL. - * - * This is not safe to call on trace because it defines properties, which can - * trigger lookups that could reenter. - */ -const Shape * -LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj); - static inline JSObject * SkipScopeParent(JSObject *parent) { if (!parent) return NULL; while (parent->isScope()) parent = &parent->asScope().enclosingScope(); return parent; } inline JSFunction * -CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, +CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent, gc::AllocKind kind = JSFunction::FinalizeKind) { JS_ASSERT(parent); - JSObject *proto = parent->global().getOrCreateFunctionPrototype(cx); + RootedVarObject proto(cx, parent->global().getOrCreateFunctionPrototype(cx)); if (!proto) return NULL; return js_CloneFunctionObject(cx, fun, parent, proto, kind); } inline JSFunction * -CloneFunctionObjectIfNotSingleton(JSContext *cx, JSFunction *fun, JSObject *parent) +CloneFunctionObjectIfNotSingleton(JSContext *cx, HandleFunction fun, HandleObject parent) { /* * For attempts to clone functions at a function definition opcode, * don't perform the clone if the function has singleton type. This * was called pessimistically, and we need to preserve the type's * property that if it is singleton there is only a single object * with its type in existence. */ @@ -258,31 +243,33 @@ CloneFunctionObjectIfNotSingleton(JSCont fun->setEnvironment(parent); return fun; } return CloneFunctionObject(cx, fun, parent); } inline JSFunction * -CloneFunctionObject(JSContext *cx, JSFunction *fun) +CloneFunctionObject(JSContext *cx, HandleFunction fun) { /* * Variant which makes an exact clone of fun, preserving parent and proto. * Calling the above version CloneFunctionObject(cx, fun, fun->getParent()) * is not equivalent: API clients, including XPConnect, can reparent * objects so that fun->global() != fun->getProto()->global(). * See ReparentWrapperIfFound. */ JS_ASSERT(fun->getParent() && fun->getProto()); if (fun->hasSingletonType()) return fun; - return js_CloneFunctionObject(cx, fun, fun->environment(), fun->getProto(), + return js_CloneFunctionObject(cx, fun, + RootedVarObject(cx, fun->environment()), + RootedVarObject(cx, fun->getProto()), JSFunction::ExtendedFinalizeKind); } } /* namespace js */ inline void JSFunction::setScript(JSScript *script_) {
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -3944,35 +3944,37 @@ SetDeterministicGC(JSContext *cx, bool e static void CheckStackRoot(JSTracer *trc, uintptr_t *w) { /* Mark memory as defined for valgrind, as in MarkWordConservatively. */ #ifdef JS_VALGRIND VALGRIND_MAKE_MEM_DEFINED(&w, sizeof(w)); #endif - ConservativeGCTest test = MarkIfGCThingWord(trc, *w, DONT_MARK_THING); + ConservativeGCTest test = MarkIfGCThingWord(trc, *w); if (test == CGCT_VALID) { JSContext *iter = NULL; bool matched = false; JSRuntime *rt = trc->runtime; - for (unsigned i = 0; i < THING_ROOT_COUNT; i++) { - Root<Cell*> *rooter = rt->thingGCRooters[i]; - while (rooter) { - if (rooter->address() == (Cell **) w) - matched = true; - rooter = rooter->previous(); + for (ContextIter cx(rt); !cx.done(); cx.next()) { + for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) { + Root<void*> *rooter = cx->thingGCRooters[i]; + while (rooter) { + if (rooter->address() == static_cast<void*>(w)) + matched = true; + rooter = rooter->previous(); + } } - } - CheckRoot *check = rt->checkGCRooters; - while (check) { - if (check->contains(static_cast<uint8_t*>(w), sizeof(w))) - matched = true; - check = check->previous(); + SkipRoot *skip = cx->skipGCRooters; + while (skip) { + if (skip->contains(reinterpret_cast<uint8_t*>(w), sizeof(w))) + matched = true; + skip = skip->previous(); + } } if (!matched) { /* * Only poison the last byte in the word. It is easy to get * accidental collisions when a value that does not occupy a full * word is used to overwrite a now-dead GC thing pointer. In this * case we want to avoid damaging the smaller value. */ @@ -4000,27 +4002,27 @@ CheckStackRoots(JSContext *cx) ThreadData *td = JS_THREAD_DATA(cx); ConservativeGCThreadData *ctd = &td->conservativeGC; ctd->recordStackTop(); JS_ASSERT(ctd->hasStackToScan()); uintptr_t *stackMin, *stackEnd; #if JS_STACK_GROWTH_DIRECTION > 0 - stackMin = td->nativeStackBase; - stackEnd = ctd->nativeStackTop; + stackMin = rt->nativeStackBase; + stackEnd = cgcd->nativeStackTop; #else - stackMin = ctd->nativeStackTop + 1; - stackEnd = td->nativeStackBase; + stackMin = cgcd->nativeStackTop + 1; + stackEnd = reinterpret_cast<uintptr_t *>(rt->nativeStackBase); #endif JS_ASSERT(stackMin <= stackEnd); CheckStackRootsRange(&checker, stackMin, stackEnd); - CheckStackRootsRange(&checker, ctd->registerSnapshot.words, - ArrayEnd(ctd->registerSnapshot.words)); + CheckStackRootsRange(&checker, cgcd->registerSnapshot.words, + ArrayEnd(cgcd->registerSnapshot.words)); } #endif /* DEBUG && JSGC_ROOT_ANALYSIS && !JS_THREADSAFE */ #ifdef JS_GC_ZEAL /* * Write barrier verification
--- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -1692,30 +1692,31 @@ public: if (source->isOwnProperty(configurable)) { updated = true; cx->compartment->types.addPendingRecompile(cx, info); } } }; static void -CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun); +CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, JSFunction *fun); bool TypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable) { /* * Everywhere compiled code depends on definite properties associated with * a type object's newScript, we need to make sure there are constraints * in place which will mark those properties as configured should the * definite properties be invalidated. */ if (object->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) { if (object->newScript) { - CheckNewScriptProperties(cx, object, object->newScript->fun); + CheckNewScriptProperties(cx, RootedVarTypeObject(cx, object), + object->newScript->fun); } else { JS_ASSERT(object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED); object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; } } if (isOwnProperty(configurable)) return true; @@ -4377,17 +4378,17 @@ AnalyzeNewScriptProperties(JSContext *cx /* Only handle 'this' values popped in unconditional code. */ Bytecode *poppedCode = analysis->maybeCode(uses->offset); if (!poppedCode || !poppedCode->unconditional) return false; pc = script->code + uses->offset; op = JSOp(*pc); - JSObject *obj = *pbaseobj; + RootedVarObject obj(cx, *pbaseobj); if (op == JSOP_SETPROP && uses->u.which == 1) { /* * Don't use GetAtomId here, we need to watch for SETPROP on * integer properties and bail out. We can't mark the aggregate * JSID_VOID type property as being in a definite slot. */ jsid id = ATOM_TO_JSID(script->getAtom(GET_UINT32_INDEX(pc))); @@ -4527,31 +4528,32 @@ AnalyzeNewScriptProperties(JSContext *cx } /* * Either make the newScript information for type when it is constructed * by the specified script, or regenerate the constraints for an existing * newScript on the type after they were cleared by a GC. */ static void -CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun) +CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, JSFunction *fun) { if (type->unknownProperties() || fun->script()->isInnerFunction) return; /* Strawman object to add properties to and watch for duplicates. */ - JSObject *baseobj = NewBuiltinClassInstance(cx, &ObjectClass, gc::FINALIZE_OBJECT16); + RootedVarObject baseobj(cx); + baseobj = NewBuiltinClassInstance(cx, &ObjectClass, gc::FINALIZE_OBJECT16); if (!baseobj) { if (type->newScript) type->clearNewScript(cx); return; } Vector<TypeNewScript::Initializer> initializerList(cx); - AnalyzeNewScriptProperties(cx, type, fun, &baseobj, &initializerList); + AnalyzeNewScriptProperties(cx, type, fun, baseobj.address(), &initializerList); if (!baseobj || baseobj->slotSpan() == 0 || !!(type->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED)) { if (type->newScript) type->clearNewScript(cx); return; } /* * If the type already has a new script, we are just regenerating the type @@ -5360,20 +5362,22 @@ JSScript::makeAnalysis(JSContext *cx) AutoEnterAnalysis enter(cx); types->analysis = cx->typeLifoAlloc().new_<ScriptAnalysis>(this); if (!types->analysis) return false; + RootedVar<JSScript*> self(cx, this); + types->analysis->analyzeBytecode(cx); - if (types->analysis->OOM()) { - types->analysis = NULL; + if (self->types->analysis->OOM()) { + self->types->analysis = NULL; return false; } return true; } bool JSScript::typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton) @@ -5649,22 +5653,22 @@ JSObject::getNewType(JSContext *cx, JSFu RootedVarObject self(cx, this); if (!setDelegate(cx)) return NULL; bool markUnknown = self->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN); - TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, - JSProto_Object, self, markUnknown); + RootedVarTypeObject type(cx); + type = cx->compartment->types.newTypeObject(cx, NULL, JSProto_Object, self, markUnknown); if (!type) return NULL; - if (!table.relookupOrAdd(p, self, type)) + if (!table.relookupOrAdd(p, self, type.raw())) return NULL; if (!cx->typeInferenceEnabled()) return type; AutoEnterTypeInference enter(cx); /*
--- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -1422,21 +1422,21 @@ JSScript::ensureHasTypes(JSContext *cx) { return types || makeTypes(cx); } inline bool JSScript::ensureRanAnalysis(JSContext *cx, JSObject *scope) { JSScript *self = this; + JS::SkipRoot root(cx, &self); if (!self->ensureHasTypes(cx)) return false; if (!self->types->hasScope()) { - js::CheckRoot root(cx, &self); js::RootObject objRoot(cx, &scope); if (!js::types::TypeScript::SetScope(cx, self, scope)) return false; } if (!self->hasAnalysis() && !self->makeAnalysis(cx)) return false; JS_ASSERT(self->analysis()->ranBytecode()); return true;
--- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -206,46 +206,46 @@ js::GetScopeChain(JSContext *cx, StackFr } /* * Special-case cloning the innermost block; this doesn't have enough in * common with subsequent steps to include in the loop. * * create() leaves the clone's enclosingScope unset. We set it below. */ - ClonedBlockObject *innermostNewChild = ClonedBlockObject::create(cx, *sharedBlock, fp); + RootedVar<ClonedBlockObject *> innermostNewChild(cx); + innermostNewChild = ClonedBlockObject::create(cx, *sharedBlock, fp); if (!innermostNewChild) return NULL; /* * Clone our way towards outer scopes until we reach the innermost * enclosing function, or the innermost block we've already cloned. */ - ClonedBlockObject *newChild = innermostNewChild; + RootedVar<ClonedBlockObject *> newChild(cx, innermostNewChild); for (;;) { JS_ASSERT(newChild->getProto() == sharedBlock); sharedBlock = sharedBlock->enclosingBlock(); /* Sometimes limitBlock will be NULL, so check that first. */ if (sharedBlock == limitBlock || !sharedBlock) break; /* As in the call above, we don't know the real parent yet. */ - ClonedBlockObject *clone = ClonedBlockObject::create(cx, *sharedBlock, fp); + RootedVar<ClonedBlockObject *> clone(cx, ClonedBlockObject::create(cx, *sharedBlock, fp)); if (!clone) return NULL; - if (!newChild->setEnclosingScope(cx, *clone)) + if (!newChild->setEnclosingScope(cx, clone)) return NULL; newChild = clone; } - if (!newChild->setEnclosingScope(cx, fp->scopeChain())) + if (!newChild->setEnclosingScope(cx, RootedVarObject(cx, &fp->scopeChain()))) return NULL; - /* * If we found a limit block belonging to this frame, then we should have * found it in blockChain. */ JS_ASSERT_IF(limitBlock && limitBlock->isClonedBlock() && limitClone->getPrivate() == js_FloatingFrameIfGenerator(cx, fp), sharedBlock); @@ -363,37 +363,37 @@ Class js_NoSuchMethodClass = { * * this.__noSuchMethod__(id, args) * * where id is the name of the method that this invocation attempted to * call by name, and args is an Array containing this invocation's actual * parameters. */ bool -js::OnUnknownMethod(JSContext *cx, JSObject *obj, Value idval, Value *vp) +js::OnUnknownMethod(JSContext *cx, HandleObject obj, Value idval, Value *vp) { jsid id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); AutoValueRooter tvr(cx); if (!js_GetMethod(cx, obj, id, 0, tvr.addr())) return false; TypeScript::MonitorUnknown(cx, cx->fp()->script(), cx->regs().pc); if (tvr.value().isPrimitive()) { *vp = tvr.value(); } else { #if JS_HAS_XML_SUPPORT /* Extract the function name from function::name qname. */ if (idval.isObject()) { - obj = &idval.toObject(); + JSObject *obj = &idval.toObject(); if (js_GetLocalNameFromFunctionQName(obj, &id, cx)) idval = IdToValue(id); } #endif - obj = NewObjectWithClassProto(cx, &js_NoSuchMethodClass, NULL, NULL); + JSObject *obj = NewObjectWithClassProto(cx, &js_NoSuchMethodClass, NULL, NULL); if (!obj) return false; obj->setSlot(JSSLOT_FOUND_FUNCTION, tvr.value()); obj->setSlot(JSSLOT_SAVED_ID, idval); vp->setObject(*obj); } return true; @@ -642,17 +642,17 @@ js::InvokeGetterOrSetter(JSContext *cx, } bool js::ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value &thisv, ExecuteType type, StackFrame *evalInFrame, Value *result) { JS_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG); - Root<JSScript*> scriptRoot(cx, &script); + JS::Root<JSScript*> scriptRoot(cx, &script); if (script->isEmpty()) { if (result) result->setUndefined(); return true; } ExecuteFrameGuard efg; @@ -922,31 +922,31 @@ js::ValueToId(JSContext *cx, const Value static bool EnterWith(JSContext *cx, int stackIndex) { StackFrame *fp = cx->fp(); Value *sp = cx->regs().sp; JS_ASSERT(stackIndex < 0); JS_ASSERT(fp->base() <= sp + stackIndex); - JSObject *obj; + RootedVarObject obj(cx); if (sp[-1].isObject()) { obj = &sp[-1].toObject(); } else { obj = js_ValueToNonNullObject(cx, sp[-1]); if (!obj) return JS_FALSE; sp[-1].setObject(*obj); } - JSObject *parent = GetScopeChain(cx, fp); + RootedVarObject parent(cx, GetScopeChain(cx, fp)); if (!parent) return JS_FALSE; - JSObject *withobj = WithObject::create(cx, fp, *obj, *parent, + JSObject *withobj = WithObject::create(cx, fp, obj, parent, sp + stackIndex - fp->base()); if (!withobj) return JS_FALSE; fp->setScopeChainNoCallObj(*withobj); return JS_TRUE; } @@ -1220,29 +1220,30 @@ inline InterpreterFrames::InterpreterFra cx->runtime->interpreterFrames = this; } inline InterpreterFrames::~InterpreterFrames() { context->runtime->interpreterFrames = older; } -#if defined(DEBUG) && !defined(JS_THREADSAFE) +#if defined(DEBUG) && !defined(JS_THREADSAFE) && !defined(JSGC_ROOT_ANALYSIS) void js::AssertValidPropertyCacheHit(JSContext *cx, - JSObject *start, JSObject *found, + JSObject *start_, JSObject *found, PropertyCacheEntry *entry) { jsbytecode *pc; cx->stack.currentScript(&pc); uint64_t sample = cx->runtime->gcNumber; PropertyCacheEntry savedEntry = *entry; - PropertyName *name = GetNameFromBytecode(cx, pc, JSOp(*pc), js_CodeSpec[*pc]); + RootedVarPropertyName name(cx, GetNameFromBytecode(cx, pc, JSOp(*pc), js_CodeSpec[*pc])); + RootedVarObject start(cx, start_); JSObject *obj, *pobj; JSProperty *prop; JSBool ok; if (JOF_OPMODE(*pc) == JOF_NAME) ok = FindProperty(cx, name, start, &obj, &pobj, &prop); else @@ -1292,17 +1293,17 @@ IteratorMore(JSContext *cx, JSObject *it { if (iterobj->isIterator()) { NativeIterator *ni = iterobj->getNativeIterator(); if (ni->isKeyIter()) { *cond = (ni->props_cursor < ni->props_end); return true; } } - if (!js_IteratorMore(cx, iterobj, rval)) + if (!js_IteratorMore(cx, RootedVarObject(cx, iterobj), rval)) return false; *cond = rval->isTrue(); return true; } static inline bool IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval) { @@ -1542,21 +1543,35 @@ js::Interpret(JSContext *cx, StackFrame /* * Help Debugger find frames running scripts that it has put in * single-step mode. */ InterpreterFrames interpreterFrame(cx, ®s, interruptEnabler); /* Copy in hot values that change infrequently. */ JSRuntime *const rt = cx->runtime; - JSScript *script; + RootedVar<JSScript*> script(cx); SET_SCRIPT(regs.fp()->script()); Value *argv = regs.fp()->maybeFormalArgs(); CHECK_INTERRUPT_HANDLER(); + /* + * Pool of rooters for use in this interpreter frame. References to these + * are used for local variables within interpreter cases. This avoids + * creating new rooters each time an interpreter case is entered, and also + * correctness pitfalls due to incorrect compilation of destructor calls + * around computed gotos. + */ + RootedVarValue rootValue0(cx), rootValue1(cx); + RootedVarString rootString0(cx), rootString1(cx); + RootedVarObject rootObject0(cx), rootObject1(cx); + RootedVarFunction rootFunction0(cx); + RootedVarTypeObject rootType0(cx); + RootedVarPropertyName rootName0(cx); + if (rt->profilingScripts) ENABLE_INTERRUPTS(); if (!entryFrame) entryFrame = regs.fp(); /* * Initialize the index segment register used by LOAD_ATOM and @@ -2479,21 +2494,25 @@ BEGIN_CASE(JSOP_POS) if (!ToNumber(cx, ®s.sp[-1])) goto error; if (!regs.sp[-1].isInt32()) TypeScript::MonitorOverflow(cx, script, regs.pc); END_CASE(JSOP_POS) BEGIN_CASE(JSOP_DELNAME) { - PropertyName *name; + RootedVarPropertyName &name = rootName0; LOAD_NAME(0, name); + + RootedVarObject &scopeObj = rootObject0; + scopeObj = cx->stack.currentScriptedScopeChain(); + JSObject *obj, *obj2; JSProperty *prop; - if (!FindProperty(cx, name, cx->stack.currentScriptedScopeChain(), &obj, &obj2, &prop)) + if (!FindProperty(cx, name, scopeObj, &obj, &obj2, &prop)) goto error; /* Strict mode code should never contain JSOP_DELNAME opcodes. */ JS_ASSERT(!script->strictModeCode); /* ECMA says to return true if name is undefined or inherited. */ PUSH_BOOLEAN(true); if (prop) { @@ -2722,20 +2741,20 @@ BEGIN_CASE(JSOP_NEW) BEGIN_CASE(JSOP_CALL) BEGIN_CASE(JSOP_FUNCALL) { CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp); JS_ASSERT(args.base() >= regs.fp()->base()); bool construct = (*regs.pc == JSOP_NEW); - JSFunction *fun; + RootedVarFunction &fun = rootFunction0; /* Don't bother trying to fast-path calls to scripted non-constructors. */ - if (!IsFunctionObject(args.calleev(), &fun) || !fun->isInterpretedConstructor()) { + if (!IsFunctionObject(args.calleev(), fun.address()) || !fun->isInterpretedConstructor()) { if (construct) { if (!InvokeConstructorKernel(cx, args)) goto error; } else { if (!InvokeKernel(cx, args)) goto error; } Value *newsp = args.spAfterCall(); @@ -2819,38 +2838,42 @@ BEGIN_CASE(JSOP_SETCALL) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS); goto error; } END_CASE(JSOP_SETCALL) BEGIN_CASE(JSOP_IMPLICITTHIS) { - PropertyName *name; + RootedVarPropertyName &name = rootName0; LOAD_NAME(0, name); + RootedVarObject &scopeObj = rootObject0; + scopeObj = cx->stack.currentScriptedScopeChain(); + JSObject *obj, *obj2; JSProperty *prop; - if (!FindPropertyHelper(cx, name, false, cx->stack.currentScriptedScopeChain(), &obj, &obj2, &prop)) + if (!FindPropertyHelper(cx, name, false, scopeObj, &obj, &obj2, &prop)) goto error; Value v; if (!ComputeImplicitThis(cx, obj, &v)) goto error; PUSH_COPY(v); } END_CASE(JSOP_IMPLICITTHIS) BEGIN_CASE(JSOP_GETGNAME) BEGIN_CASE(JSOP_CALLGNAME) BEGIN_CASE(JSOP_NAME) BEGIN_CASE(JSOP_CALLNAME) { - Value rval; - if (!NameOperation(cx, regs.pc, &rval)) + RootedVarValue &rval = rootValue0; + + if (!NameOperation(cx, regs.pc, rval.address())) goto error; PUSH_COPY(rval); TypeScript::Monitor(cx, script, regs.pc, rval); } END_CASE(JSOP_NAME) BEGIN_CASE(JSOP_UINT16) @@ -3113,35 +3136,37 @@ BEGIN_CASE(JSOP_DEFVAR) /* ES5 10.5 step 8 (with subsequent errata). */ unsigned attrs = JSPROP_ENUMERATE; if (!regs.fp()->isEvalFrame()) attrs |= JSPROP_PERMANENT; if (op == JSOP_DEFCONST) attrs |= JSPROP_READONLY; /* Step 8b. */ - JSObject &obj = regs.fp()->varObj(); + RootedVarObject &obj = rootObject0; + obj = ®s.fp()->varObj(); if (!DefVarOrConstOperation(cx, obj, dn, attrs)) goto error; } END_CASE(JSOP_DEFVAR) BEGIN_CASE(JSOP_DEFFUN) { /* * A top-level function defined in Global or Eval code (see ECMA-262 * Ed. 3), or else a SpiderMonkey extension: a named function statement in * a compound statement (not at the top statement level of global code, or * at the top level of a function body). */ - JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc)); + RootedVarFunction &fun = rootFunction0; + fun = script->getFunction(GET_UINT32_INDEX(regs.pc)); JSObject *obj = fun; - JSObject *obj2; + RootedVarObject &obj2 = rootObject0; if (fun->isNullClosure()) { /* * Even a null closure needs a parent for principals finding. * FIXME: bug 476950, although debugger users may also demand some kind * of scope link for debugger-assisted eval-in-frame. */ obj2 = ®s.fp()->scopeChain(); } else { @@ -3236,22 +3261,23 @@ BEGIN_CASE(JSOP_DEFFUN) goto error; } while (false); } END_CASE(JSOP_DEFFUN) BEGIN_CASE(JSOP_LAMBDA) { /* Load the specified function object literal. */ - JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc)); + RootedVarFunction &fun = rootFunction0; + fun = script->getFunction(GET_UINT32_INDEX(regs.pc)); JSObject *obj = fun; /* do-while(0) so we can break instead of using a goto. */ do { - JSObject *parent; + RootedVarObject &parent = rootObject0; if (fun->isNullClosure()) { parent = ®s.fp()->scopeChain(); } else { parent = GetScopeChain(cx, regs.fp()); if (!parent) goto error; } @@ -3403,19 +3429,21 @@ BEGIN_CASE(JSOP_NEWARRAY) PUSH_OBJECT(*obj); CHECK_INTERRUPT_HANDLER(); } END_CASE(JSOP_NEWARRAY) BEGIN_CASE(JSOP_NEWOBJECT) { - JSObject *baseobj = script->getObject(GET_UINT32_INDEX(regs.pc)); - - JSObject *obj = CopyInitializerObject(cx, baseobj); + RootedVarObject &baseobj = rootObject0; + baseobj = script->getObject(GET_UINT32_INDEX(regs.pc)); + + RootedVarObject &obj = rootObject1; + obj = CopyInitializerObject(cx, baseobj); if (!obj || !SetInitializerObjectType(cx, script, regs.pc, obj)) goto error; PUSH_OBJECT(*obj); CHECK_INTERRUPT_HANDLER(); } END_CASE(JSOP_NEWOBJECT) @@ -3429,17 +3457,18 @@ END_CASE(JSOP_ENDINIT) BEGIN_CASE(JSOP_INITPROP) { /* Load the property's initial value into rval. */ JS_ASSERT(regs.sp - regs.fp()->base() >= 2); Value rval = regs.sp[-1]; /* Load the object being initialized into lval/obj. */ - JSObject *obj = ®s.sp[-2].toObject(); + RootedVarObject &obj = rootObject0; + obj = ®s.sp[-2].toObject(); JS_ASSERT(obj->isObject()); JSAtom *atom; LOAD_ATOM(0, atom); jsid id = ATOM_TO_JSID(atom); if (JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom) ? !js_SetPropertyHelper(cx, obj, id, 0, &rval, script->strictModeCode)
--- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -317,17 +317,17 @@ UnwindScope(JSContext *cx, uint32_t stac /* * Unwind for an uncatchable exception. This means not running finalizers, etc; * just preserving the basic engine stack invariants. */ extern void UnwindForUncatchableException(JSContext *cx, const FrameRegs ®s); extern bool -OnUnknownMethod(JSContext *cx, JSObject *obj, Value idval, Value *vp); +OnUnknownMethod(JSContext *cx, HandleObject obj, Value idval, Value *vp); extern bool IsActiveWithOrBlock(JSContext *cx, JSObject &obj, uint32_t stackDepth); class TryNoteIter { const FrameRegs ®s; JSScript *script;
--- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -166,29 +166,29 @@ NativeGet(JSContext *cx, JSObject *obj, *vp = pobj->nativeGetSlot(shape->slot()); } else { if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp)) return false; } return true; } -#if defined(DEBUG) && !defined(JS_THREADSAFE) +#if defined(DEBUG) && !defined(JS_THREADSAFE) && !defined(JSGC_ROOT_ANALYSIS) extern void AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found, PropertyCacheEntry *entry); #else inline void AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found, PropertyCacheEntry *entry) {} #endif inline bool -GetPropertyGenericMaybeCallXML(JSContext *cx, JSOp op, JSObject *obj, jsid id, Value *vp) +GetPropertyGenericMaybeCallXML(JSContext *cx, JSOp op, HandleObject obj, jsid id, Value *vp) { /* * Various XML properties behave differently when accessed in a * call vs. normal context, and getGeneric will not work right. */ #if JS_HAS_XML_SUPPORT if (op == JSOP_CALLPROP && obj->isXML()) return js_GetXMLMethod(cx, obj, id, vp); @@ -250,32 +250,34 @@ GetPropertyOperation(JSContext *cx, jsby JS_PROPERTY_CACHE(cx).test(cx, pc, obj, obj2, entry, name); if (!name) { AssertValidPropertyCacheHit(cx, obj, obj2, entry); if (!NativeGet(cx, obj, obj2, entry->prop, JSGET_CACHE_RESULT, vp)) return false; return true; } + RootObject objRoot(cx, &obj); + jsid id = ATOM_TO_JSID(name); if (obj->getOps()->getProperty) { - if (!GetPropertyGenericMaybeCallXML(cx, op, obj, id, vp)) + if (!GetPropertyGenericMaybeCallXML(cx, op, objRoot, id, vp)) return false; } else { - if (!GetPropertyHelper(cx, obj, id, JSGET_CACHE_RESULT, vp)) + if (!GetPropertyHelper(cx, objRoot, id, JSGET_CACHE_RESULT, vp)) return false; } #if JS_HAS_NO_SUCH_METHOD if (op == JSOP_CALLPROP && JS_UNLIKELY(vp->isPrimitive()) && lval.isObject()) { - if (!OnUnknownMethod(cx, obj, IdToValue(id), vp)) + if (!OnUnknownMethod(cx, objRoot, IdToValue(id), vp)) return false; } #endif return true; } inline bool @@ -303,19 +305,19 @@ SetPropertyOperation(JSContext *cx, jsby const Shape *shape = entry->prop; JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable()); JS_ASSERT_IF(shape->hasSlot(), entry->isOwnPropertyHit()); if (entry->isOwnPropertyHit() || ((obj2 = obj->getProto()) && obj2->lastProperty() == entry->pshape)) { #ifdef DEBUG if (entry->isOwnPropertyHit()) { - JS_ASSERT(obj->nativeContains(cx, *shape)); + JS_ASSERT(obj->nativeLookupNoAllocation(cx, shape->propid()) == shape); } else { - JS_ASSERT(obj2->nativeContains(cx, *shape)); + JS_ASSERT(obj2->nativeLookupNoAllocation(cx, shape->propid()) == shape); JS_ASSERT(entry->isPrototypePropertyHit()); JS_ASSERT(entry->kshape != entry->pshape); JS_ASSERT(!shape->hasSlot()); } #endif if (shape->hasDefaultSetter() && shape->hasSlot()) { /* Fast path for, e.g., plain Object instance properties. */ @@ -328,29 +330,31 @@ SetPropertyOperation(JSContext *cx, jsby } return true; } GET_NAME_FROM_BYTECODE(cx->stack.currentScript(), pc, 0, name); } bool strict = cx->stack.currentScript()->strictModeCode; - Value rref = rval; + RootedVarValue rref(cx, rval); JSOp op = JSOp(*pc); + RootObject objRoot(cx, &obj); + jsid id = ATOM_TO_JSID(name); if (JS_LIKELY(!obj->getOps()->setProperty)) { unsigned defineHow = (op == JSOP_SETNAME) ? DNP_CACHE_RESULT | DNP_UNQUALIFIED : DNP_CACHE_RESULT; - if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rref, strict)) + if (!js_SetPropertyHelper(cx, objRoot, id, defineHow, rref.address(), strict)) return false; } else { - if (!obj->setGeneric(cx, id, &rref, strict)) + if (!obj->setGeneric(cx, id, rref.address(), strict)) return false; } return true; } inline bool NameOperation(JSContext *cx, jsbytecode *pc, Value *vp) @@ -377,18 +381,21 @@ NameOperation(JSContext *cx, jsbytecode AssertValidPropertyCacheHit(cx, obj, obj2, entry); if (!NativeGet(cx, obj, obj2, entry->prop, 0, vp)) return false; return true; } jsid id = ATOM_TO_JSID(name); + RootPropertyName nameRoot(cx, &name); + RootObject objRoot(cx, &obj); + JSProperty *prop; - if (!FindPropertyHelper(cx, name, true, obj, &obj, &obj2, &prop)) + if (!FindPropertyHelper(cx, nameRoot, true, objRoot, &obj, &obj2, &prop)) return false; if (!prop) { /* Kludge to allow (typeof foo == "undefined") tests. */ JSOp op2 = JSOp(pc[JSOP_NAME_LENGTH]); if (op2 == JSOP_TYPEOF) { vp->setUndefined(); return true; } @@ -410,40 +417,40 @@ NameOperation(JSContext *cx, jsbytecode if (!NativeGet(cx, normalized, obj2, shape, 0, vp)) return false; } return true; } inline bool -DefVarOrConstOperation(JSContext *cx, JSObject &varobj, PropertyName *dn, unsigned attrs) +DefVarOrConstOperation(JSContext *cx, HandleObject varobj, PropertyName *dn, unsigned attrs) { - JS_ASSERT(varobj.isVarObj()); - JS_ASSERT(!varobj.getOps()->defineProperty); + JS_ASSERT(varobj->isVarObj()); + JS_ASSERT(!varobj->getOps()->defineProperty); JSProperty *prop; JSObject *obj2; - if (!varobj.lookupProperty(cx, dn, &obj2, &prop)) + if (!varobj->lookupProperty(cx, dn, &obj2, &prop)) return false; /* Steps 8c, 8d. */ - if (!prop || (obj2 != &varobj && varobj.isGlobal())) { - if (!DefineNativeProperty(cx, &varobj, dn, UndefinedValue(), + if (!prop || (obj2 != varobj && varobj->isGlobal())) { + if (!DefineNativeProperty(cx, varobj, dn, UndefinedValue(), JS_PropertyStub, JS_StrictPropertyStub, attrs, 0, 0)) { return false; } } else { /* * Extension: ordinarily we'd be done here -- but for |const|. If we * see a redeclaration that's |const|, we consider it a conflict. */ unsigned oldAttrs; - if (!varobj.getPropertyAttributes(cx, dn, &oldAttrs)) + if (!varobj->getPropertyAttributes(cx, dn, &oldAttrs)) return false; if (attrs & JSPROP_READONLY) { JSAutoByteString bytes; if (js_AtomToPrintableString(cx, dn, &bytes)) { JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, JSMSG_REDECLARED_VAR, (oldAttrs & JSPROP_READONLY) @@ -473,17 +480,17 @@ FunctionNeedsPrologue(JSContext *cx, JSF } inline bool ScriptPrologue(JSContext *cx, StackFrame *fp, bool newType) { JS_ASSERT_IF(fp->isNonEvalFunctionFrame() && fp->fun()->isHeavyweight(), fp->hasCallObj()); if (fp->isConstructing()) { - JSObject *obj = js_CreateThisForFunction(cx, &fp->callee(), newType); + JSObject *obj = js_CreateThisForFunction(cx, RootedVarObject(cx, &fp->callee()), newType); if (!obj) return false; fp->functionThis().setObject(*obj); } Probes::enterJSFun(cx, fp->maybeFun(), fp->script()); return true; @@ -527,65 +534,67 @@ InterpreterFrames::enableInterruptsIfRun { if (script == regs->fp()->script()) enabler.enableInterrupts(); } static JS_ALWAYS_INLINE bool AddOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res) { - Value lval = lhs; - Value rval = rhs; - - if (lval.isInt32() && rval.isInt32()) { - int32_t l = lval.toInt32(), r = rval.toInt32(); + if (lhs.isInt32() && rhs.isInt32()) { + int32_t l = lhs.toInt32(), r = rhs.toInt32(); int32_t sum = l + r; if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000))) { res->setDouble(double(l) + double(r)); types::TypeScript::MonitorOverflow(cx); } else { res->setInt32(sum); } } else #if JS_HAS_XML_SUPPORT - if (IsXML(lval) && IsXML(rval)) { - if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), res)) + if (IsXML(lhs) && IsXML(rhs)) { + if (!js_ConcatenateXML(cx, &lhs.toObject(), &rhs.toObject(), res)) return false; types::TypeScript::MonitorUnknown(cx); } else #endif { + RootedVarValue lval_(cx, lhs); + RootedVarValue rval_(cx, rhs); + Value &lval = lval_.reference(); + Value &rval = rval_.reference(); + /* * If either operand is an object, any non-integer result must be * reported to inference. */ bool lIsObject = lval.isObject(), rIsObject = rval.isObject(); if (!ToPrimitive(cx, &lval)) return false; if (!ToPrimitive(cx, &rval)) return false; bool lIsString, rIsString; if ((lIsString = lval.isString()) | (rIsString = rval.isString())) { - js::AutoStringRooter lstr(cx), rstr(cx); + RootedVarString lstr(cx), rstr(cx); if (lIsString) { - lstr.setString(lval.toString()); + lstr = lval.toString(); } else { - lstr.setString(ToString(cx, lval)); - if (!lstr.string()) + lstr = ToString(cx, lval); + if (!lstr) return false; } if (rIsString) { - rstr.setString(rval.toString()); + rstr = rval.toString(); } else { - rstr.setString(ToString(cx, rval)); - if (!rstr.string()) + rstr = ToString(cx, rval); + if (!rstr) return false; } - JSString *str = js_ConcatStrings(cx, lstr.string(), rstr.string()); + JSString *str = js_ConcatStrings(cx, lstr, rstr); if (!str) return false; if (lIsObject || rIsObject) types::TypeScript::MonitorString(cx); res->setString(str); } else { double l, r; if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r)) @@ -688,17 +697,17 @@ ToIdOperation(JSContext *cx, const Value return false; if (!res->isInt32()) types::TypeScript::MonitorUnknown(cx); return true; } static JS_ALWAYS_INLINE bool -GetObjectElementOperation(JSContext *cx, JSOp op, JSObject *obj, const Value &rref, Value *res) +GetObjectElementOperation(JSContext *cx, JSOp op, HandleObject obj, const Value &rref, Value *res) { #if JS_HAS_XML_SUPPORT if (op == JSOP_CALLELEM && JS_UNLIKELY(obj->isXML())) { jsid id; if (!FetchElementId(cx, obj, rref, id, res)) return false; return js_GetXMLMethod(cx, obj, id, res); } @@ -768,17 +777,17 @@ GetElementOperation(JSContext *cx, JSOp return true; } } if (lref.isMagic(JS_OPTIMIZED_ARGUMENTS)) return NormalArgumentsObject::optimizedGetElem(cx, cx->fp(), rref, res); bool isObject = lref.isObject(); - JSObject *obj = ValueToObject(cx, lref); + RootedVarObject obj(cx, ValueToObject(cx, lref)); if (!obj) return false; if (!GetObjectElementOperation(cx, op, obj, rref, res)) return false; #if JS_HAS_NO_SUCH_METHOD if (op == JSOP_CALLELEM && JS_UNLIKELY(res->isPrimitive()) && isObject) { if (!OnUnknownMethod(cx, obj, rref, res)) @@ -812,18 +821,18 @@ SetObjectElementOperation(JSContext *cx, types::TypeScript::GetPcScript(cx, &script, &pc); if (script->hasAnalysis()) script->analysis()->getCode(pc).arrayWriteHole = true; } } } while (0); - Value tmp = value; - return obj->setGeneric(cx, id, &tmp, strict); + RootedVarValue tmp(cx, value); + return obj->setGeneric(cx, id, tmp.address(), strict); } #define RELATIONAL_OP(OP) \ JS_BEGIN_MACRO \ Value lval = lhs; \ Value rval = rhs; \ /* Optimize for two int-tagged operands (typical loop control). */ \ if (lval.isInt32() && rval.isInt32()) { \
--- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -438,17 +438,17 @@ JS_FRIEND_API(bool) js::GetPropertyNames(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector *props) { return Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), props); } size_t sCustomIteratorCount = 0; static inline bool -GetCustomIterator(JSContext *cx, JSObject *obj, unsigned flags, Value *vp) +GetCustomIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp) { JS_CHECK_RECURSION(cx, return false); /* * for-of iteration does not fall back on __iterator__ or property * enumeration. This is more conservative than the current proposed spec. */ if (flags == JSITER_FOR_OF) { @@ -579,28 +579,28 @@ RegisterEnumerator(JSContext *cx, JSObje cx->enumerators = iterobj; JS_ASSERT(!(ni->flags & JSITER_ACTIVE)); ni->flags |= JSITER_ACTIVE; } } static inline bool -VectorToKeyIterator(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector &keys, +VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &keys, uint32_t slength, uint32_t key, Value *vp) { JS_ASSERT(!(flags & JSITER_FOREACH)); if (obj) { if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx)) return false; types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED); } - JSObject *iterobj = NewIteratorObject(cx, flags); + RootedVarObject iterobj(cx, NewIteratorObject(cx, flags)); if (!iterobj) return false; NativeIterator *ni = NativeIterator::allocateIterator(cx, slength, keys); if (!ni) return false; ni->init(obj, flags, slength, key); @@ -626,23 +626,23 @@ VectorToKeyIterator(JSContext *cx, JSObj RegisterEnumerator(cx, iterobj, ni); return true; } namespace js { bool -VectorToKeyIterator(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector &props, Value *vp) +VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &props, Value *vp) { return VectorToKeyIterator(cx, obj, flags, props, 0, 0, vp); } bool -VectorToValueIterator(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector &keys, +VectorToValueIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &keys, Value *vp) { JS_ASSERT(flags & JSITER_FOREACH); if (obj) { if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx)) return false; types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED); @@ -660,34 +660,34 @@ VectorToValueIterator(JSContext *cx, JSO iterobj->setNativeIterator(ni); vp->setObject(*iterobj); RegisterEnumerator(cx, iterobj, ni); return true; } bool -EnumeratedIdVectorToIterator(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector &props, Value *vp) +EnumeratedIdVectorToIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &props, Value *vp) { if (!(flags & JSITER_FOREACH)) return VectorToKeyIterator(cx, obj, flags, props, vp); return VectorToValueIterator(cx, obj, flags, props, vp); } static inline void UpdateNativeIterator(NativeIterator *ni, JSObject *obj) { // Update the object for which the native iterator is associated, so // SuppressDeletedPropertyHelper will recognize the iterator as a match. ni->obj = obj; } bool -GetIterator(JSContext *cx, JSObject *obj, unsigned flags, Value *vp) +GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp) { Vector<const Shape *, 8> shapes(cx); uint32_t key = 0; bool keysOnly = (flags == JSITER_ENUMERATE); if (obj) { /* Enumerate Iterator.prototype directly. */ @@ -844,17 +844,17 @@ js_ThrowStopIteration(JSContext *cx) } static JSBool iterator_next(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); bool ok; - JSObject *obj = NonGenericMethodGuard(cx, args, iterator_next, &IteratorClass, &ok); + RootedVarObject obj(cx, NonGenericMethodGuard(cx, args, iterator_next, &IteratorClass, &ok)); if (!obj) return ok; if (!js_IteratorMore(cx, obj, &args.rval())) return false; if (!args.rval().toBoolean()) { js_ThrowStopIteration(cx); @@ -888,30 +888,30 @@ js::ValueToIterator(JSContext *cx, unsig /* * Make sure the more/next state machine doesn't get stuck. A value might be * left in iterValue when a trace is left due to an operation time-out after * JSOP_MOREITER but before the value is picked up by FOR*. */ cx->iterValue.setMagic(JS_NO_ITER_VALUE); - JSObject *obj; + RootedVarObject obj(cx); if (vp->isObject()) { /* Common case. */ obj = &vp->toObject(); } else { /* * Enumerating over null and undefined gives an empty enumerator. * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of * the first production in 12.6.4 and step 4 of the second production, * but it's "web JS" compatible. ES5 fixed for-in to match this de-facto * standard. */ if ((flags & JSITER_ENUMERATE)) { - if (!js_ValueToObjectOrNull(cx, *vp, &obj)) + if (!js_ValueToObjectOrNull(cx, *vp, obj.address())) return false; /* fall through */ } else { obj = js_ValueToNonNullObject(cx, *vp); if (!obj) return false; } } @@ -1149,17 +1149,17 @@ ElementIteratorObject::setIndex(uint32_t { setReservedSlot(IndexSlot, Int32Value(int32_t(index))); } bool ElementIteratorObject::iteratorNext(JSContext *cx, Value *vp) { uint32_t i, length; - JSObject *obj = getTargetObject(); + RootedVarObject obj(cx, getTargetObject()); if (!js_GetLengthProperty(cx, obj, &length)) goto error; i = getIndex(); if (i >= length) { setIndex(CLOSED_INDEX); vp->setMagic(JS_NO_ITER_VALUE); return true; @@ -1181,17 +1181,17 @@ ElementIteratorObject::iteratorNext(JSCo inline js::ElementIteratorObject * JSObject::asElementIterator() { JS_ASSERT(isElementIterator()); return static_cast<js::ElementIteratorObject *>(this); } JSBool -js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval) +js_IteratorMore(JSContext *cx, HandleObject iterobj, Value *rval) { /* Fast path for native iterators */ NativeIterator *ni = NULL; if (iterobj->isIterator()) { /* Key iterators are handled by fast-paths. */ ni = iterobj->getNativeIterator(); bool more = ni->props_cursor < ni->props_end; if (ni->isKeyIter() || !more) {
--- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -158,42 +158,42 @@ class ElementIteratorObject : public JSO * If there are any more elements to visit, store the value of the next * element in *vp, increment the index, and return true. If not, call * vp->setMagic(JS_NO_ITER_VALUE) and return true. Return false on error. */ bool iteratorNext(JSContext *cx, Value *vp); }; bool -VectorToIdArray(JSContext *cx, js::AutoIdVector &props, JSIdArray **idap); +VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap); bool -GetIterator(JSContext *cx, JSObject *obj, unsigned flags, js::Value *vp); +GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp); bool -VectorToKeyIterator(JSContext *cx, JSObject *obj, unsigned flags, js::AutoIdVector &props, js::Value *vp); +VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &props, Value *vp); bool -VectorToValueIterator(JSContext *cx, JSObject *obj, unsigned flags, js::AutoIdVector &props, js::Value *vp); +VectorToValueIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &props, Value *vp); /* * Creates either a key or value iterator, depending on flags. For a value * iterator, performs value-lookup to convert the given list of jsids. */ bool -EnumeratedIdVectorToIterator(JSContext *cx, JSObject *obj, unsigned flags, js::AutoIdVector &props, js::Value *vp); +EnumeratedIdVectorToIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &props, Value *vp); /* * Convert the value stored in *vp to its iteration object. The flags should * contain JSITER_ENUMERATE if js::ValueToIterator is called when enumerating * for-in semantics are required, and when the caller can guarantee that the * iterator will never be exposed to scripts. */ extern JSBool -ValueToIterator(JSContext *cx, unsigned flags, js::Value *vp); +ValueToIterator(JSContext *cx, unsigned flags, Value *vp); extern bool CloseIterator(JSContext *cx, JSObject *iterObj); extern bool UnwindIteratorForException(JSContext *cx, JSObject *obj); extern void @@ -211,34 +211,34 @@ extern bool js_SuppressDeletedElements(JSContext *cx, JSObject *obj, uint32_t begin, uint32_t end); /* * IteratorMore() indicates whether another value is available. It might * internally call iterobj.next() and then cache the value until its * picked up by IteratorNext(). The value is cached in the current context. */ extern JSBool -js_IteratorMore(JSContext *cx, JSObject *iterobj, js::Value *rval); +js_IteratorMore(JSContext *cx, js::HandleObject iterobj, js::Value *rval); extern JSBool js_IteratorNext(JSContext *cx, JSObject *iterobj, js::Value *rval); extern JSBool js_ThrowStopIteration(JSContext *cx); namespace js { /* * Get the next value from an iterator object. * * On success, store the next value in *vp and return true; if there are no * more values, store the magic value JS_NO_ITER_VALUE in *vp and return true. */ inline bool -Next(JSContext *cx, JSObject *iter, Value *vp) +Next(JSContext *cx, HandleObject iter, Value *vp) { if (!js_IteratorMore(cx, iter, vp)) return false; if (vp->toBoolean()) return js_IteratorNext(cx, iter, vp); vp->setMagic(JS_NO_ITER_VALUE); return true; } @@ -257,17 +257,17 @@ Next(JSContext *cx, JSObject *iter, Valu */ template <class Op> bool ForOf(JSContext *cx, const Value &iterable, Op op) { Value iterv(iterable); if (!ValueToIterator(cx, JSITER_FOR_OF, &iterv)) return false; - JSObject *iter = &iterv.toObject(); + RootedVarObject iter(cx, &iterv.toObject()); bool ok = true; while (ok) { Value v; ok = Next(cx, iter, &v); if (ok) { if (v.isMagic(JS_NO_ITER_VALUE)) break;
--- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -689,19 +689,21 @@ js_IsMathFunction(Native native) for (size_t i=0; math_static_methods[i].name != NULL; i++) { if (native == math_static_methods[i].call) return true; } return false; } JSObject * -js_InitMathClass(JSContext *cx, JSObject *obj) +js_InitMathClass(JSContext *cx, JSObject *obj_) { - JSObject *Math = NewObjectWithClassProto(cx, &MathClass, NULL, obj); + RootedVarObject obj(cx, obj_); + + RootedVarObject Math(cx, NewObjectWithClassProto(cx, &MathClass, NULL, obj)); if (!Math || !Math->setSingletonType(cx)) return NULL; if (!JS_DefineProperty(cx, obj, js_Math_str, OBJECT_TO_JSVAL(Math), JS_PropertyStub, JS_StrictPropertyStub, 0)) { return NULL; }
--- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -1012,24 +1012,25 @@ FinishRuntimeNumberState(JSRuntime *rt) JSObject * js_InitNumberClass(JSContext *cx, JSObject *obj) { JS_ASSERT(obj->isNative()); /* XXX must do at least once per new thread, so do it per JSContext... */ FIX_FPU(); - GlobalObject *global = &obj->asGlobal(); + RootedVar<GlobalObject*> global(cx, &obj->asGlobal()); - JSObject *numberProto = global->createBlankPrototype(cx, &NumberClass); + RootedVarObject numberProto(cx, global->createBlankPrototype(cx, &NumberClass)); if (!numberProto) return NULL; numberProto->asNumber().setPrimitiveValue(0); - JSFunction *ctor = global->createConstructor(cx, Number, CLASS_ATOM(cx, Number), 1); + RootedVarFunction ctor(cx); + ctor = global->createConstructor(cx, Number, CLASS_ATOM(cx, Number), 1); if (!ctor) return NULL; if (!LinkConstructorAndPrototype(cx, ctor, numberProto)) return NULL; /* Add numeric constants (MAX_VALUE, NaN, &c.) to the Number constructor. */ if (!JS_DefineConstDoubles(cx, ctor, number_constants))
--- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -841,23 +841,23 @@ EvalCacheLookup(JSContext *cx, JSLinearS * with calls to JS_GetScriptObject for scripts in the eval cache (currently, * script->object aliases script->evalHashLink()). */ class EvalScriptGuard { JSContext *cx_; JSLinearString *str_; JSScript **bucket_; - JSScript *script_; + RootedVar<JSScript*> script_; public: EvalScriptGuard(JSContext *cx, JSLinearString *str) : cx_(cx), str_(str), - script_(NULL) { + script_(cx) { bucket_ = EvalCacheHash(cx, str); } ~EvalScriptGuard() { if (script_) { CallDestroyScriptHook(cx_->runtime->defaultFreeOp(), script_); script_->isActiveEval = false; script_->isCachedEval = true; @@ -904,22 +904,22 @@ enum EvalType { DIRECT_EVAL = EXECUTE_DI * frame, with the provided scope chain, with the semantics of either a direct * or indirect eval (see ES5 10.4.2). If this is an indirect eval, scopeobj * must be a global object. * * On success, store the completion value in call.rval and return true. */ static bool EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *caller, - JSObject &scopeobj) + HandleObject scopeobj) { JS_ASSERT((evalType == INDIRECT_EVAL) == (caller == NULL)); - AssertInnerizedScopeChain(cx, scopeobj); - - if (!scopeobj.global().isRuntimeCodeGenEnabled(cx)) { + AssertInnerizedScopeChain(cx, *scopeobj); + + if (!scopeobj->global().isRuntimeCodeGenEnabled(cx)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_EVAL); return false; } /* ES5 15.1.2.1 step 1. */ if (args.length() < 1) { args.rval().setUndefined(); return true; @@ -951,21 +951,21 @@ EvalKernel(JSContext *cx, const CallArgs return false; thisv = caller->thisValue(); #ifdef DEBUG jsbytecode *callerPC = caller->pcQuadratic(cx); JS_ASSERT(callerPC && JSOp(*callerPC) == JSOP_EVAL); #endif } else { - JS_ASSERT(args.callee().global() == scopeobj); + JS_ASSERT(args.callee().global() == *scopeobj); staticLevel = 0; /* Use the global as 'this', modulo outerization. */ - JSObject *thisobj = scopeobj.thisObject(cx); + JSObject *thisobj = scopeobj->thisObject(cx); if (!thisobj) return false; thisv = ObjectValue(*thisobj); } JSLinearString *linearStr = str->ensureLinear(cx); if (!linearStr) return false; @@ -1015,38 +1015,38 @@ EvalKernel(JSContext *cx, const CallArgs } } EvalScriptGuard esg(cx, linearStr); JSPrincipals *principals = PrincipalsForCompiledCode(args, cx); if (evalType == DIRECT_EVAL && caller->isNonEvalFunctionFrame()) - esg.lookupInEvalCache(caller, staticLevel, principals, scopeobj); + esg.lookupInEvalCache(caller, staticLevel, principals, *scopeobj); if (!esg.foundScript()) { unsigned lineno; const char *filename; JSPrincipals *originPrincipals; CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals, evalType == DIRECT_EVAL ? CALLED_FROM_JSOP_EVAL : NOT_CALLED_FROM_JSOP_EVAL); uint32_t tcflags = TCF_COMPILE_N_GO | TCF_COMPILE_FOR_EVAL; - JSScript *compiled = frontend::CompileScript(cx, &scopeobj, caller, + JSScript *compiled = frontend::CompileScript(cx, scopeobj, caller, principals, originPrincipals, tcflags, chars, length, filename, lineno, cx->findVersion(), linearStr, staticLevel); if (!compiled) return false; esg.setNewScript(compiled); } - return ExecuteKernel(cx, esg.script(), scopeobj, thisv, ExecuteType(evalType), + return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType), NULL /* evalInFrame */, &args.rval()); } /* * We once supported a second argument to eval to use as the scope chain * when evaluating the code string. Warn when such uses are seen so that * authors will know that support for eval(s, o) has been removed. */ @@ -1081,38 +1081,39 @@ namespace js { * * NB: This method handles only indirect eval. */ JSBool eval(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); return WarnOnTooManyArgs(cx, args) && - EvalKernel(cx, args, INDIRECT_EVAL, NULL, args.callee().global()); + EvalKernel(cx, args, INDIRECT_EVAL, NULL, + RootedVarObject(cx, &args.callee().global())); } bool DirectEval(JSContext *cx, const CallArgs &args) { /* Direct eval can assume it was called from an interpreted frame. */ StackFrame *caller = cx->fp(); JS_ASSERT(caller->isScriptFrame()); JS_ASSERT(IsBuiltinEvalForScope(&caller->scopeChain(), args.calleev())); JS_ASSERT(JSOp(*cx->regs().pc) == JSOP_EVAL); AutoFunctionCallProbe callProbe(cx, args.callee().toFunction(), caller->script()); - JSObject *scopeChain = GetScopeChain(cx, caller); + RootedVarObject scopeChain(cx, GetScopeChain(cx, caller)); if (!scopeChain) return false; if (!WarnOnTooManyArgs(cx, args)) return false; - return EvalKernel(cx, args, DIRECT_EVAL, caller, *scopeChain); + return EvalKernel(cx, args, DIRECT_EVAL, caller, scopeChain); } bool IsBuiltinEvalForScope(JSObject *scopeChain, const Value &v) { return scopeChain->global().getOriginalEval() == v; } @@ -1189,28 +1190,28 @@ obj_watch(JSContext *cx, unsigned argc, JSObject *callable = js_ValueToCallableObject(cx, &vp[3], 0); if (!callable) return false; jsid propid; if (!ValueToId(cx, vp[2], &propid)) return false; - JSObject *obj = ToObject(cx, &vp[1]); + RootedVarObject obj(cx, ToObject(cx, &vp[1])); if (!obj) return false; Value tmp; unsigned attrs; if (!CheckAccess(cx, obj, propid, JSACC_WATCH, &tmp, &attrs)) return false; vp->setUndefined(); - if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx)) + if (obj->isDenseArray() && !JSObject::makeDenseArraySlow(cx, obj)) return false; return JS_SetWatchPoint(cx, obj, propid, obj_watch_handler, callable); } static JSBool obj_unwatch(JSContext *cx, unsigned argc, Value *vp) { JSObject *obj = ToObject(cx, &vp[1]); @@ -1247,17 +1248,17 @@ obj_hasOwnProperty(JSContext *cx, unsign JSBool js_HasOwnPropertyHelper(JSContext *cx, LookupGenericOp lookup, unsigned argc, Value *vp) { jsid id; if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id)) return JS_FALSE; - JSObject *obj = ToObject(cx, &vp[1]); + RootedVarObject obj(cx, ToObject(cx, &vp[1])); if (!obj) return false; JSObject *obj2; JSProperty *prop; if (obj->isProxy()) { bool has; if (!Proxy::hasOwn(cx, obj, id, &has)) return false; @@ -1266,17 +1267,17 @@ js_HasOwnPropertyHelper(JSContext *cx, L } if (!js_HasOwnProperty(cx, lookup, obj, id, &obj2, &prop)) return JS_FALSE; vp->setBoolean(!!prop); return JS_TRUE; } JSBool -js_HasOwnProperty(JSContext *cx, LookupGenericOp lookup, JSObject *obj, jsid id, +js_HasOwnProperty(JSContext *cx, LookupGenericOp lookup, HandleObject obj, jsid id, JSObject **objp, JSProperty **propp) { JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING); if (!(lookup ? lookup : js_LookupProperty)(cx, obj, id, objp, propp)) return false; if (!*propp) return true; @@ -1405,18 +1406,23 @@ DefineAccessor(JSContext *cx, unsigned a return false; /* enumerable: true */ PropertyName *acc = (Type == Getter) ? state.getAtom : state.setAtom; if (!descObj->defineProperty(cx, acc, args[1])) return false; JSBool dummy; - if (!js_DefineOwnProperty(cx, &args.thisv().toObject(), id, ObjectValue(*descObj), &dummy)) + if (!js_DefineOwnProperty(cx, + RootedVarObject(cx, &args.thisv().toObject()), + RootedVarId(cx, id), + ObjectValue(*descObj), &dummy)) + { return false; + } args.rval().setUndefined(); return true; } JS_FRIEND_API(JSBool) js::obj_defineGetter(JSContext *cx, unsigned argc, Value *vp) { return DefineAccessor<Getter>(cx, argc, vp); @@ -1578,17 +1584,17 @@ PropDesc::makeObject(JSContext *cx) return false; } pd.setObject(*obj); return true; } bool -GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, PropertyDescriptor *desc) +GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, PropertyDescriptor *desc) { if (obj->isProxy()) return Proxy::getOwnPropertyDescriptor(cx, obj, id, false, desc); JSObject *pobj; JSProperty *prop; if (!js_HasOwnProperty(cx, obj->getOps()->lookupGeneric, obj, id, &pobj, &prop)) return false; @@ -1616,17 +1622,17 @@ GetOwnPropertyDescriptor(JSContext *cx, if (doGet && !obj->getGeneric(cx, id, &desc->value)) return false; desc->obj = obj; return true; } bool -GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, Value *vp) +GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, Value *vp) { AutoPropertyDescriptorRooter desc(cx); return GetOwnPropertyDescriptor(cx, obj, id, &desc) && NewPropertyDescriptorObject(cx, &desc, vp); } } @@ -1652,23 +1658,23 @@ GetFirstArgumentAsObject(JSContext *cx, *objp = &v.toObject(); return true; } static JSBool obj_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp) { - JSObject *obj; - if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyDescriptor", &obj)) + RootedVarObject obj(cx); + if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyDescriptor", obj.address())) return JS_FALSE; - AutoIdRooter nameidr(cx); - if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), nameidr.addr())) + RootedVarId id(cx); + if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), id.address())) return JS_FALSE; - return GetOwnPropertyDescriptor(cx, obj, nameidr.id(), vp); + return GetOwnPropertyDescriptor(cx, obj, id, vp); } static JSBool obj_keys(JSContext *cx, unsigned argc, Value *vp) { JSObject *obj; if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.keys", &obj)) return false; @@ -1879,24 +1885,24 @@ Reject(JSContext *cx, JSObject *obj, uns if (throwError) return Throw(cx, obj, errorNumber); *rval = false; return JS_TRUE; } static JSBool -DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc, +DefinePropertyOnObject(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc, bool throwError, bool *rval) { /* 8.12.9 step 1. */ JSProperty *current; - JSObject *obj2; + RootedVarObject obj2(cx); JS_ASSERT(!obj->getOps()->lookupGeneric); - if (!js_HasOwnProperty(cx, NULL, obj, id, &obj2, ¤t)) + if (!js_HasOwnProperty(cx, NULL, obj, id, obj2.address(), ¤t)) return JS_FALSE; JS_ASSERT(!obj->getOps()->defineProperty); /* 8.12.9 steps 2-4. */ if (!current) { if (!obj->isExtensible()) return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval); @@ -2179,27 +2185,27 @@ DefinePropertyOnObject(JSContext *cx, JS if (!CallJSPropertyOp(cx, obj2->getClass()->delProperty, obj2, id, &dummy)) return false; } return js_DefineProperty(cx, obj, id, &v, getter, setter, attrs); } static JSBool -DefinePropertyOnArray(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc, +DefinePropertyOnArray(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc, bool throwError, bool *rval) { /* * We probably should optimize dense array property definitions where * the descriptor describes a traditional array property (enumerable, * configurable, writable, numeric index or length without altering its * attributes). Such definitions are probably unlikely, so we don't bother * for now. */ - if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx)) + if (obj->isDenseArray() && !JSObject::makeDenseArraySlow(cx, obj)) return JS_FALSE; uint32_t oldLen = obj->getArrayLength(); if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { /* * Our optimization of storage of the length property of arrays makes * it very difficult to properly implement defining the property. For @@ -2233,17 +2239,17 @@ DefinePropertyOnArray(JSContext *cx, JSO } return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval); } namespace js { bool -DefineProperty(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc, bool throwError, +DefineProperty(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc, bool throwError, bool *rval) { if (obj->isArray()) return DefinePropertyOnArray(cx, obj, id, desc, throwError, rval); if (obj->getOps()->lookupGeneric) { if (obj->isProxy()) return Proxy::defineProperty(cx, obj, id, desc.pd); @@ -2251,17 +2257,17 @@ DefineProperty(JSContext *cx, JSObject * } return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval); } } /* namespace js */ JSBool -js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id, const Value &descriptor, JSBool *bp) +js_DefineOwnProperty(JSContext *cx, HandleObject obj, HandleId id, const Value &descriptor, JSBool *bp) { AutoPropDescArrayRooter descs(cx); PropDesc *desc = descs.append(); if (!desc || !desc->initialize(cx, descriptor)) return false; bool rval; if (!DefineProperty(cx, obj, id, *desc, true, &rval)) @@ -2269,22 +2275,22 @@ js_DefineOwnProperty(JSContext *cx, JSOb *bp = !!rval; return true; } /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */ static JSBool obj_defineProperty(JSContext *cx, unsigned argc, Value *vp) { - JSObject *obj; - if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperty", &obj)) + RootedVarObject obj(cx); + if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperty", obj.address())) return false; - jsid id; - if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), &id)) + RootedVarId id(cx); + if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), id.address())) return JS_FALSE; const Value descval = argc >= 3 ? vp[4] : UndefinedValue(); JSBool junk; if (!js_DefineOwnProperty(cx, obj, id, descval, &junk)) return false; @@ -2309,45 +2315,45 @@ ReadPropertyDescriptors(JSContext *cx, J return false; } return true; } } /* namespace js */ static bool -DefineProperties(JSContext *cx, JSObject *obj, JSObject *props) +DefineProperties(JSContext *cx, HandleObject obj, JSObject *props) { AutoIdVector ids(cx); AutoPropDescArrayRooter descs(cx); if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs)) return false; bool dummy; for (size_t i = 0, len = ids.length(); i < len; i++) { - if (!DefineProperty(cx, obj, ids[i], descs[i], true, &dummy)) + if (!DefineProperty(cx, obj, RootedVarId(cx, ids[i]), descs[i], true, &dummy)) return false; } return true; } extern JSBool -js_PopulateObject(JSContext *cx, JSObject *newborn, JSObject *props) +js_PopulateObject(JSContext *cx, HandleObject newborn, JSObject *props) { return DefineProperties(cx, newborn, props); } /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */ static JSBool obj_defineProperties(JSContext *cx, unsigned argc, Value *vp) { /* Steps 1 and 7. */ - JSObject *obj; - if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperties", &obj)) + RootedVarObject obj(cx); + if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperties", obj.address())) return false; vp->setObject(*obj); /* Step 2. */ if (argc < 2) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED, "Object.defineProperties", "0", "s"); return false; @@ -2387,17 +2393,18 @@ obj_create(JSContext *cx, unsigned argc, JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XML_PROTO_FORBIDDEN); return false; } /* * Use the callee's global as the parent of the new object to avoid dynamic * scoping (i.e., using the caller's global). */ - JSObject *obj = NewObjectWithGivenProto(cx, &ObjectClass, proto, &args.callee().global()); + RootedVarObject obj(cx); + obj = NewObjectWithGivenProto(cx, &ObjectClass, proto, &args.callee().global()); if (!obj) return false; /* Don't track types or array-ness for objects created here. */ MarkTypeObjectUnknownProperties(cx, obj->type()); /* 15.2.3.5 step 4. */ if (args.hasDefined(1)) { @@ -2502,17 +2509,17 @@ JSObject::sealOrFreeze(JSContext *cx, Im } else { if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, &props)) return false; } /* preventExtensions must slowify dense arrays, so we can assign to holes without checks. */ JS_ASSERT(!self->isDenseArray()); - if (isNative() && !inDictionaryMode()) { + if (self->isNative() && !self->inDictionaryMode()) { /* * Seal/freeze non-dictionary objects by constructing a new shape * hierarchy mirroring the original one, which can be shared if many * objects with the same structure are sealed/frozen. If we use the * generic path below then any non-empty object will be converted to * dictionary mode. */ Shape *last = EmptyShape::getInitialShape(cx, self->getClass(), @@ -2539,17 +2546,17 @@ JSObject::sealOrFreeze(JSContext *cx, Im MarkTypePropertyConfigured(cx, self, child.propid); last = JS_PROPERTY_TREE(cx).getChild(cx, last, self->numFixedSlots(), child); if (!last) return NULL; } JS_ASSERT(self->lastProperty()->slotSpan() == last->slotSpan()); - JS_ALWAYS_TRUE(setLastProperty(cx, last)); + JS_ALWAYS_TRUE(self->setLastProperty(cx, last)); } else { for (size_t i = 0; i < props.length(); i++) { jsid id = props[i]; unsigned attrs; if (!self->getGenericAttributes(cx, id, &attrs)) return false; @@ -2853,17 +2860,17 @@ js::NewObjectWithClassProto(JSContext *c if (entry != -1 && !obj->hasDynamicSlots()) cache.fillGlobal(entry, clasp, &parent->asGlobal(), kind, obj); return obj; } JSObject * -js::NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc::AllocKind kind) +js::NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind kind) { JS_ASSERT(type->proto->hasNewType(type)); JS_ASSERT(parent); if (CanBeFinalizedInBackground(kind, &ObjectClass)) kind = GetBackgroundAllocKind(kind); NewObjectCache &cache = cx->compartment->newObjectCache; @@ -2880,20 +2887,20 @@ js::NewObjectWithType(JSContext *cx, typ if (entry != -1 && !obj->hasDynamicSlots()) cache.fillType(entry, &ObjectClass, type, kind, obj); return obj; } JSObject * -js::NewReshapedObject(JSContext *cx, TypeObject *type, JSObject *parent, +js::NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind kind, const Shape *shape) { - JSObject *res = NewObjectWithType(cx, type, parent, kind); + RootedVarObject res(cx, NewObjectWithType(cx, type, parent, kind)); if (!res) return NULL; if (shape->isEmptyShape()) return res; /* Get all the ids in the object, in order. */ js::AutoIdVector ids(cx); @@ -2928,17 +2935,17 @@ js_CreateThis(JSContext *cx, Class *newc JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL; JSObject *parent = callee->getParent(); gc::AllocKind kind = NewObjectGCKind(cx, newclasp); return NewObjectWithClassProto(cx, newclasp, proto, parent, kind); } static inline JSObject * -CreateThisForFunctionWithType(JSContext *cx, types::TypeObject *type, JSObject *parent) +CreateThisForFunctionWithType(JSContext *cx, HandleTypeObject type, JSObject *parent) { if (type->newScript) { /* * Make an object with the type's associated finalize kind and shape, * which reflects any properties that will definitely be added to the * object before it is read from. */ gc::AllocKind kind = type->newScript->allocKind; @@ -2948,38 +2955,38 @@ CreateThisForFunctionWithType(JSContext return res; } gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass); return NewObjectWithType(cx, type, parent, kind); } JSObject * -js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto) +js_CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject *proto) { JSObject *res; if (proto) { - types::TypeObject *type = proto->getNewType(cx, callee->toFunction()); + RootedVarTypeObject type(cx, proto->getNewType(cx, callee->toFunction())); if (!type) return NULL; res = CreateThisForFunctionWithType(cx, type, callee->getParent()); } else { gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass); res = NewObjectWithClassProto(cx, &ObjectClass, proto, callee->getParent(), kind); } if (res && cx->typeInferenceEnabled()) TypeScript::SetThis(cx, callee->toFunction()->script(), types::Type::ObjectType(res)); return res; } JSObject * -js_CreateThisForFunction(JSContext *cx, JSObject *callee, bool newType) +js_CreateThisForFunction(JSContext *cx, HandleObject callee, bool newType) { Value protov; if (!callee->getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &protov)) return NULL; JSObject *proto; if (protov.isObject()) proto = &protov.toObject(); else @@ -3186,25 +3193,29 @@ CopySlots(JSContext *cx, JSObject *from, if (!cx->compartment->wrap(cx, &v)) return false; to->setSlot(n, v); } return true; } JS_FRIEND_API(JSObject *) -JS_CloneObject(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent) -{ +JS_CloneObject(JSContext *cx, JSObject *obj_, JSObject *proto_, JSObject *parent_) +{ + RootedVarObject obj(cx, obj_); + RootedVarObject proto(cx, proto_); + RootedVarObject parent(cx, parent_); + /* * We can only clone native objects and proxies. Dense arrays are slowified if * we try to clone them. */ if (!obj->isNative()) { if (obj->isDenseArray()) { - if (!obj->makeDenseArraySlow(cx)) + if (!JSObject::makeDenseArraySlow(cx, obj)) return NULL; } else if (!obj->isProxy()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CLONE_OBJECT); return NULL; } } JSObject *clone = NewObjectWithGivenProto(cx, obj->getClass(), proto, parent, obj->getAllocKind()); @@ -3627,17 +3638,17 @@ DefineConstructorAndPrototype(JSContext RootedVarObject proto(cx); proto = NewObjectWithClassProto(cx, clasp, protoProto, obj); if (!proto) return NULL; if (!proto->setSingletonType(cx)) return NULL; - if (clasp == &ArrayClass && !proto->makeDenseArraySlow(cx)) + if (clasp == &ArrayClass && !JSObject::makeDenseArraySlow(cx, proto)) return NULL; /* After this point, control must exit via label bad or out. */ RootedVarObject ctor(cx); bool named = false; bool cached = false; if (!constructor) { /* @@ -3898,17 +3909,18 @@ JSObject::growSlots(JSContext *cx, uint3 * by calling 'new' on a particular script, bump the GC kind for that * type to give these objects a larger number of fixed slots when future * objects are constructed. */ if (!hasLazyType() && !oldCount && type()->newScript) { gc::AllocKind kind = type()->newScript->allocKind; unsigned newScriptSlots = gc::GetGCKindSlots(kind); if (newScriptSlots == numFixedSlots() && gc::TryIncrementAllocKind(&kind)) { - JSObject *obj = NewReshapedObject(cx, type(), getParent(), kind, + JSObject *obj = NewReshapedObject(cx, RootedVarTypeObject(cx, type()), + getParent(), kind, type()->newScript->shape); if (!obj) return false; type()->newScript->allocKind = kind; type()->newScript->shape = obj->lastProperty(); type()->markStateChange(cx); } @@ -4416,26 +4428,28 @@ js_AddNativeProperty(JSContext *cx, JSOb */ if (!js_PurgeScopeChain(cx, obj, id)) return NULL; return obj->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid); } JSBool -js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value, +js_DefineProperty(JSContext *cx, JSObject *obj_, jsid id, const Value *value, PropertyOp getter, StrictPropertyOp setter, unsigned attrs) { + RootObject obj(cx, &obj_); return !!DefineNativeProperty(cx, obj, id, *value, getter, setter, attrs, 0, 0); } JSBool -js_DefineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *value, +js_DefineElement(JSContext *cx, JSObject *obj_, uint32_t index, const Value *value, PropertyOp getter, StrictPropertyOp setter, unsigned attrs) { + RootObject obj(cx, &obj_); jsid id; if (!IndexToId(cx, index, &id)) return false; return !!DefineNativeProperty(cx, obj, id, *value, getter, setter, attrs, 0, 0); } /* * Backward compatibility requires allowing addProperty hooks to mutate the @@ -4457,25 +4471,24 @@ CallAddPropertyHook(JSContext *cx, Class } } return true; } namespace js { const Shape * -DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value_, +DefineNativeProperty(JSContext *cx, HandleObject obj, jsid id, const Value &value_, PropertyOp getter, StrictPropertyOp setter, unsigned attrs, unsigned flags, int shortid, unsigned defineHow /* = 0 */) { JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_DONT_PURGE | DNP_SKIP_TYPE)) == 0); JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS)); - RootObject objRoot(cx, &obj); RootId idRoot(cx, &id); /* Make a local copy of value so addProperty can mutate its inout parameter. */ RootedVarValue value(cx); value = value_; /* Convert string indices to integers if appropriate. */ id = js_CheckForStringIndex(id); @@ -4649,39 +4662,39 @@ CallResolveOp(JSContext *cx, JSObject *s *propp = (JSProperty *) shape; } } return true; } static JS_ALWAYS_INLINE bool -LookupPropertyWithFlagsInline(JSContext *cx, JSObject *obj, jsid id, unsigned flags, +LookupPropertyWithFlagsInline(JSContext *cx, JSObject *obj_, jsid id_, unsigned flags, JSObject **objp, JSProperty **propp) { + RootedVarObject obj(cx, obj_); + RootedVarId id(cx, id_); + /* We should not get string indices which aren't already integers here. */ - JS_ASSERT(id == js_CheckForStringIndex(id)); - - RootObject objRoot(cx, &obj); - RootId idRoot(cx, &id); + JS_ASSERT(id.raw() == js_CheckForStringIndex(id)); /* Search scopes starting with obj and following the prototype link. */ - JSObject *start = obj; + RootedVarObject start(cx, obj); while (true) { const Shape *shape = obj->nativeLookup(cx, id); if (shape) { *objp = obj; *propp = (JSProperty *) shape; return true; } /* Try obj's class resolve hook if id was not found in obj's scope. */ if (obj->getClass()->resolve != JS_ResolveStub) { bool recursed; - if (!CallResolveOp(cx, start, objRoot, idRoot, flags, objp, propp, &recursed)) + if (!CallResolveOp(cx, start, obj, id, flags, objp, propp, &recursed)) return false; if (recursed) break; if (*propp) { /* * For stats we do not recalculate protoIndex even if it was * resolved on some other object. */ @@ -4747,27 +4760,29 @@ js::LookupPropertyWithFlags(JSContext *c { /* Convert string indices to integers if appropriate. */ id = js_CheckForStringIndex(id); return LookupPropertyWithFlagsInline(cx, obj, id, flags, objp, propp); } bool -js::FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, JSObject *scopeChain, +js::FindPropertyHelper(JSContext *cx, + HandlePropertyName name, bool cacheResult, HandleObject scopeChain, JSObject **objp, JSObject **pobjp, JSProperty **propp) { - jsid id = ATOM_TO_JSID(name); - JSObject *obj, *parent, *pobj; + RootedVarId id(cx, ATOM_TO_JSID(name)); + + JSObject *pobj; int scopeIndex; JSProperty *prop; /* Scan entries on the scope chain that we can cache across. */ - obj = scopeChain; - parent = obj->enclosingScope(); + RootedVarObject obj(cx, scopeChain); + RootedVarObject parent(cx, obj->enclosingScope()); for (scopeIndex = 0; parent ? IsCacheableNonGlobalScope(obj) : !obj->getOps()->lookupProperty; ++scopeIndex) { if (!LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, &pobj, &prop)) return false; @@ -4839,17 +4854,17 @@ js::FindPropertyHelper(JSContext *cx, Pr return true; } /* * On return, if |*pobjp| is a native object, then |*propp| is a |Shape *|. * Otherwise, its type and meaning depends on the host object's implementation. */ bool -js::FindProperty(JSContext *cx, PropertyName *name, JSObject *scopeChain, +js::FindProperty(JSContext *cx, HandlePropertyName name, HandleObject scopeChain, JSObject **objp, JSObject **pobjp, JSProperty **propp) { return !!FindPropertyHelper(cx, name, false, scopeChain, objp, pobjp, propp); } JSObject * js::FindIdentifierBase(JSContext *cx, JSObject *scopeChain, PropertyName *name) { @@ -4935,16 +4950,19 @@ js_NativeGetInline(JSContext *cx, JSObje jsbytecode *pc; JSScript *script = cx->stack.currentScript(&pc); if (script && script->hasAnalysis()) { analyze::Bytecode *code = script->analysis()->maybeCode(pc); if (code) code->accessGetter = true; } + Root<const Shape*> rootShape(cx, &shape); + RootObject pobjRoot(cx, &pobj); + if (!shape->get(cx, receiver, obj, pobj, vp)) return false; /* Update slotful shapes according to the value produced by the getter. */ if (shape->hasSlot() && pobj->nativeContains(cx, *shape)) pobj->nativeSetSlot(shape->slot(), *vp); return true; @@ -4978,16 +4996,19 @@ js_NativeSet(JSContext *cx, JSObject *ob * Such properties effectively function as data descriptors which are * not writable, so attempting to set such a property should do nothing * or throw if we're in strict mode. */ if (!shape->hasGetterValue() && shape->hasDefaultSetter()) return js_ReportGetterOnlyAssignment(cx); } + RootObject objRoot(cx, &obj); + Root<const Shape *> shapeRoot(cx, &shape); + int32_t sample = cx->runtime->propertyRemovals; if (!shape->set(cx, obj, strict, vp)) return false; /* * Update any slot for the shape with the value produced by the setter, * unless the setter deleted the shape. */ @@ -4996,29 +5017,29 @@ js_NativeSet(JSContext *cx, JSObject *ob obj->nativeContains(cx, *shape))) { obj->setSlot(shape->slot(), *vp); } return true; } static JS_ALWAYS_INLINE JSBool -js_GetPropertyHelperInline(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, +js_GetPropertyHelperInline(JSContext *cx, HandleObject obj, HandleObject receiver, jsid id_, uint32_t getHow, Value *vp) { - JSObject *aobj, *obj2; JSProperty *prop; - const Shape *shape; + + RootedVarId id(cx, id_); /* Convert string indices to integers if appropriate. */ id = js_CheckForStringIndex(id); - aobj = js_GetProtoIfDenseArray(obj); /* This call site is hot -- use the always-inlined variant of LookupPropertyWithFlags(). */ - if (!LookupPropertyWithFlagsInline(cx, aobj, id, cx->resolveFlags, &obj2, &prop)) + RootedVarObject obj2(cx); + if (!LookupPropertyWithFlagsInline(cx, obj, id, cx->resolveFlags, obj2.address(), &prop)) return false; if (!prop) { vp->setUndefined(); if (!CallJSPropertyOp(cx, obj->getClass()->getProperty, obj, id, vp)) return JS_FALSE; @@ -5079,70 +5100,70 @@ js_GetPropertyHelperInline(JSContext *cx } if (!obj2->isNative()) { return obj2->isProxy() ? Proxy::get(cx, obj2, receiver, id, vp) : obj2->getGeneric(cx, id, vp); } - shape = (Shape *) prop; + Shape *shape = (Shape *) prop; if (getHow & JSGET_CACHE_RESULT) - JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, obj2, shape); + JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, obj2, shape); /* This call site is hot -- use the always-inlined variant of js_NativeGet(). */ if (!js_NativeGetInline(cx, receiver, obj, obj2, shape, getHow, vp)) return JS_FALSE; return JS_TRUE; } bool -js::GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uint32_t getHow, Value *vp) +js::GetPropertyHelper(JSContext *cx, HandleObject obj, jsid id, uint32_t getHow, Value *vp) { return !!js_GetPropertyHelperInline(cx, obj, obj, id, getHow, vp); } JSBool -js_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp) +js_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, jsid id, Value *vp) { /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */ return js_GetPropertyHelperInline(cx, obj, receiver, id, 0, vp); } JSBool -js_GetElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp) +js_GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index, Value *vp) { jsid id; if (!IndexToId(cx, index, &id)) return false; /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */ return js_GetPropertyHelperInline(cx, obj, receiver, id, 0, vp); } JSBool -js::GetPropertyDefault(JSContext *cx, JSObject *obj, jsid id, const Value &def, Value *vp) +js::GetPropertyDefault(JSContext *cx, HandleObject obj, HandleId id, const Value &def, Value *vp) { JSProperty *prop; - JSObject *obj2; - if (!LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop)) + RootedVarObject obj2(cx); + if (!LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_QUALIFIED, obj2.address(), &prop)) return false; if (!prop) { *vp = def; return true; } return js_GetProperty(cx, obj2, id, vp); } JSBool -js_GetMethod(JSContext *cx, JSObject *obj, jsid id, unsigned getHow, Value *vp) +js_GetMethod(JSContext *cx, HandleObject obj, jsid id, unsigned getHow, Value *vp) { JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); GenericIdOp op = obj->getOps()->getGeneric; if (!op) { #if JS_HAS_XML_SUPPORT JS_ASSERT(!obj->isXML()); #endif @@ -5200,35 +5221,35 @@ JSObject::reportNotExtensible(JSContext JSDVG_IGNORE_STACK, ObjectValue(*this), NULL, NULL, NULL); } bool JSObject::callMethod(JSContext *cx, jsid id, unsigned argc, Value *argv, Value *vp) { Value fval; - return js_GetMethod(cx, this, id, 0, &fval) && + return js_GetMethod(cx, RootedVarObject(cx, this), id, 0, &fval) && Invoke(cx, ObjectValue(*this), fval, argc, argv, vp); } JSBool -js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, unsigned defineHow, +js_SetPropertyHelper(JSContext *cx, HandleObject obj, jsid id, unsigned defineHow, Value *vp, JSBool strict) { JSObject *pobj; JSProperty *prop; - const Shape *shape; unsigned attrs, flags; int shortid; Class *clasp; PropertyOp getter; StrictPropertyOp setter; bool added; JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_UNQUALIFIED)) == 0); + RootId idRoot(cx, &id); /* Convert string indices to integers if appropriate. */ id = js_CheckForStringIndex(id); if (JS_UNLIKELY(obj->watched())) { /* Fire watchpoints, if any. */ WatchpointMap *wpmap = cx->compartment->watchpointMap; if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp)) @@ -5265,17 +5286,18 @@ js_SetPropertyHelper(JSContext *cx, JSOb JS_ASSERT(!obj->isBlock()); if (obj->isGlobal() && (defineHow & DNP_UNQUALIFIED) && !js::CheckUndeclaredVarAssignment(cx, JSID_TO_STRING(id))) { return JS_FALSE; } } - shape = (Shape *) prop; + + RootedVarShape shape(cx, (Shape *) prop); /* * Now either shape is null, meaning id was not found in obj or one of its * prototypes; or shape is non-null, meaning id was found directly in pobj. */ attrs = JSPROP_ENUMERATE; flags = 0; shortid = 0; @@ -5393,17 +5415,17 @@ js_SetPropertyHelper(JSContext *cx, JSOb if ((defineHow & DNP_CACHE_RESULT) && !added) JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, obj, shape); return js_NativeSet(cx, obj, shape, added, strict, vp); } JSBool -js_SetElementHelper(JSContext *cx, JSObject *obj, uint32_t index, unsigned defineHow, +js_SetElementHelper(JSContext *cx, HandleObject obj, uint32_t index, unsigned defineHow, Value *vp, JSBool strict) { jsid id; if (!IndexToId(cx, index, &id)) return false; return js_SetPropertyHelper(cx, obj, id, defineHow, vp, strict); } @@ -5531,17 +5553,17 @@ JSBool js_DeleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict) { return js_DeleteGeneric(cx, obj, SPECIALID_TO_JSID(sid), rval, strict); } namespace js { bool -HasDataProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) +HasDataProperty(JSContext *cx, HandleObject obj, jsid id, Value *vp) { JS_ASSERT(id == js_CheckForStringIndex(id)); if (const Shape *shape = obj->nativeLookup(cx, id)) { if (shape->hasDefaultGetter() && shape->hasSlot()) { *vp = obj->nativeGetSlot(shape->slot()); return true; } } @@ -5553,40 +5575,40 @@ HasDataProperty(JSContext *cx, JSObject * Gets |obj[id]|. If that value's not callable, returns true and stores a * non-primitive value in *vp. If it's callable, calls it with no arguments * and |obj| as |this|, returning the result in *vp. * * This is a mini-abstraction for ES5 8.12.8 [[DefaultValue]], either steps 1-2 * or steps 3-4. */ static bool -MaybeCallMethod(JSContext *cx, JSObject *obj, jsid id, Value *vp) +MaybeCallMethod(JSContext *cx, HandleObject obj, jsid id, Value *vp) { if (!js_GetMethod(cx, obj, id, 0, vp)) return false; if (!js_IsCallable(*vp)) { *vp = ObjectValue(*obj); return true; } return Invoke(cx, ObjectValue(*obj), *vp, 0, NULL, vp); } JSBool -DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp) +DefaultValue(JSContext *cx, HandleObject obj, JSType hint, Value *vp) { JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID); JS_ASSERT(!obj->isXML()); Class *clasp = obj->getClass(); if (hint == JSTYPE_STRING) { /* Optimize (new String(...)).toString(). */ if (clasp == &StringClass && ClassMethodIsNative(cx, obj, &StringClass, - ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), + RootedVarId(cx, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom)), js_str_toString)) { *vp = StringValue(obj->asString().unbox()); return true; } if (!MaybeCallMethod(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), vp)) return false; if (vp->isPrimitive()) @@ -5595,21 +5617,21 @@ DefaultValue(JSContext *cx, JSObject *ob if (!MaybeCallMethod(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), vp)) return false; if (vp->isPrimitive()) return true; } else { /* Optimize (new String(...)).valueOf(). */ if ((clasp == &StringClass && ClassMethodIsNative(cx, obj, &StringClass, - ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), + RootedVarId(cx, ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom)), js_str_toString)) || (clasp == &NumberClass && ClassMethodIsNative(cx, obj, &NumberClass, - ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), + RootedVarId(cx, ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom)), js_num_valueOf))) { *vp = obj->isString() ? StringValue(obj->asString().unbox()) : NumberValue(obj->asNumber().unbox()); return true; } if (!MaybeCallMethod(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), vp)) @@ -5809,17 +5831,17 @@ js_GetClassPrototype(JSContext *cx, JSOb return FindClassPrototype(cx, scopeobj, protoKey, protop, clasp); } JSObject * PrimitiveToObject(JSContext *cx, const Value &v) { if (v.isString()) - return StringObject::create(cx, v.toString()); + return StringObject::create(cx, RootedVarString(cx, v.toString())); if (v.isNumber()) return NumberObject::create(cx, v.toNumber()); JS_ASSERT(v.isBoolean()); return BooleanObject::create(cx, v.toBoolean()); } JSBool
--- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -268,57 +268,57 @@ extern JSBool js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const js::Value *value, JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs); extern JSBool js_DefineElement(JSContext *cx, JSObject *obj, uint32_t index, const js::Value *value, JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs); extern JSBool -js_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, js::Value *vp); +js_GetProperty(JSContext *cx, js::HandleObject obj, js::HandleObject receiver, jsid id, js::Value *vp); extern JSBool -js_GetElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t, js::Value *vp); +js_GetElement(JSContext *cx, js::HandleObject obj, js::HandleObject receiver, uint32_t, js::Value *vp); inline JSBool -js_GetProperty(JSContext *cx, JSObject *obj, jsid id, js::Value *vp) +js_GetProperty(JSContext *cx, js::HandleObject obj, jsid id, js::Value *vp) { return js_GetProperty(cx, obj, obj, id, vp); } inline JSBool -js_GetElement(JSContext *cx, JSObject *obj, uint32_t index, js::Value *vp) +js_GetElement(JSContext *cx, js::HandleObject obj, uint32_t index, js::Value *vp) { return js_GetElement(cx, obj, obj, index, vp); } namespace js { extern JSBool -GetPropertyDefault(JSContext *cx, JSObject *obj, jsid id, const Value &def, Value *vp); +GetPropertyDefault(JSContext *cx, js::HandleObject obj, js::HandleId id, const Value &def, Value *vp); } /* namespace js */ extern JSBool -js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, unsigned defineHow, +js_SetPropertyHelper(JSContext *cx, js::HandleObject obj, jsid id, unsigned defineHow, js::Value *vp, JSBool strict); namespace js { inline bool -SetPropertyHelper(JSContext *cx, JSObject *obj, PropertyName *name, unsigned defineHow, +SetPropertyHelper(JSContext *cx, HandleObject obj, PropertyName *name, unsigned defineHow, Value *vp, JSBool strict) { return !!js_SetPropertyHelper(cx, obj, ATOM_TO_JSID(name), defineHow, vp, strict); } } /* namespace js */ extern JSBool -js_SetElementHelper(JSContext *cx, JSObject *obj, uint32_t index, unsigned defineHow, +js_SetElementHelper(JSContext *cx, js::HandleObject obj, uint32_t index, unsigned defineHow, js::Value *vp, JSBool strict); extern JSBool js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp); extern JSBool js_GetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp); @@ -342,17 +342,17 @@ js_DeleteGeneric(JSContext *cx, JSObject extern JSType js_TypeOf(JSContext *cx, JSObject *obj); namespace js { /* ES5 8.12.8. */ extern JSBool -DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp); +DefaultValue(JSContext *cx, HandleObject obj, JSType hint, Value *vp); extern Class ArrayClass; extern Class ArrayBufferClass; extern Class BlockClass; extern Class BooleanClass; extern Class CallableObjectClass; extern Class DateClass; extern Class ErrorClass; @@ -734,17 +734,17 @@ struct JSObject : public js::ObjectImpl inline EnsureDenseResult ensureDenseArrayElements(JSContext *cx, unsigned index, unsigned extra); /* * Check if after growing the dense array will be too sparse. * newElementsHint is an estimated number of elements to be added. */ bool willBeSparseDenseArray(unsigned requiredCapacity, unsigned newElementsHint); - JSBool makeDenseArraySlow(JSContext *cx); + static bool makeDenseArraySlow(JSContext *cx, js::HandleObject obj); /* * If this array object has a data property with index i, set *vp to its * value and return true. If not, do vp->setMagic(JS_ARRAY_HOLE) and return * true. On OOM, report it and return false. */ bool arrayGetOwnDataElement(JSContext *cx, size_t i, js::Value *vp); @@ -1175,17 +1175,17 @@ js_LeaveSharpObject(JSContext *cx, JSIdA extern void js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map); extern JSBool js_HasOwnPropertyHelper(JSContext *cx, js::LookupGenericOp lookup, unsigned argc, js::Value *vp); extern JSBool -js_HasOwnProperty(JSContext *cx, js::LookupGenericOp lookup, JSObject *obj, jsid id, +js_HasOwnProperty(JSContext *cx, js::LookupGenericOp lookup, js::HandleObject obj, jsid id, JSObject **objp, JSProperty **propp); extern JSBool js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, js::Value *vp); #if JS_HAS_OBJ_PROTO_PROP extern JSPropertySpec object_props[]; #else @@ -1294,17 +1294,17 @@ extern const char js_propertyIsEnumerabl #ifdef OLD_GETTER_SETTER_METHODS extern const char js_defineGetter_str[]; extern const char js_defineSetter_str[]; extern const char js_lookupGetter_str[]; extern const char js_lookupSetter_str[]; #endif extern JSBool -js_PopulateObject(JSContext *cx, JSObject *newborn, JSObject *props); +js_PopulateObject(JSContext *cx, js::HandleObject newborn, JSObject *props); /* * Fast access to immutable standard objects (constructors and prototypes). */ extern JSBool js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp); @@ -1314,21 +1314,21 @@ js_GetClassObject(JSContext *cx, JSObjec */ extern JSBool js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey key, js::Value *vp, js::Class *clasp = NULL); // Specialized call for constructing |this| with a known function callee, // and a known prototype. extern JSObject * -js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto); +js_CreateThisForFunctionWithProto(JSContext *cx, js::HandleObject callee, JSObject *proto); // Specialized call for constructing |this| with a known function callee. extern JSObject * -js_CreateThisForFunction(JSContext *cx, JSObject *callee, bool newType); +js_CreateThisForFunction(JSContext *cx, js::HandleObject callee, bool newType); // Generic call for constructing |this|. extern JSObject * js_CreateThis(JSContext *cx, js::Class *clasp, JSObject *callee); extern jsid js_CheckForStringIndex(jsid id); @@ -1337,17 +1337,17 @@ js_CheckForStringIndex(jsid id); * and setter, slot, attributes, and other members. */ extern js::Shape * js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, JSPropertyOp getter, JSStrictPropertyOp setter, uint32_t slot, unsigned attrs, unsigned flags, int shortid); extern JSBool -js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id, +js_DefineOwnProperty(JSContext *cx, js::HandleObject obj, js::HandleId id, const js::Value &descriptor, JSBool *bp); namespace js { /* * Flags for the defineHow parameter of js_DefineNativeProperty. */ const unsigned DNP_CACHE_RESULT = 1; /* an interpreter call from JSOP_INITPROP */ @@ -1356,22 +1356,22 @@ const unsigned DNP_UNQUALIFIED = 4; / the defineHow argument of js_SetPropertyHelper. */ const unsigned DNP_SKIP_TYPE = 8; /* Don't update type information */ /* * Return successfully added or changed shape or NULL on error. */ extern const Shape * -DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value, +DefineNativeProperty(JSContext *cx, HandleObject obj, jsid id, const Value &value, PropertyOp getter, StrictPropertyOp setter, unsigned attrs, unsigned flags, int shortid, unsigned defineHow = 0); inline const Shape * -DefineNativeProperty(JSContext *cx, JSObject *obj, PropertyName *name, const Value &value, +DefineNativeProperty(JSContext *cx, HandleObject obj, PropertyName *name, const Value &value, PropertyOp getter, StrictPropertyOp setter, unsigned attrs, unsigned flags, int shortid, unsigned defineHow = 0) { return DefineNativeProperty(cx, obj, ATOM_TO_JSID(name), value, getter, setter, attrs, flags, shortid, defineHow); } /* @@ -1392,17 +1392,18 @@ LookupPropertyWithFlags(JSContext *cx, J * Call the [[DefineOwnProperty]] internal method of obj. * * If obj is an array, this follows ES5 15.4.5.1. * If obj is any other native object, this follows ES5 8.12.9. * If obj is a proxy, this calls the proxy handler's defineProperty method. * Otherwise, this reports an error and returns false. */ extern bool -DefineProperty(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc, bool throwError, +DefineProperty(JSContext *cx, js::HandleObject obj, + js::HandleId id, const PropDesc &desc, bool throwError, bool *rval); /* * Read property descriptors from props, as for Object.defineProperties. See * ES5 15.2.3.7 steps 3-5. */ extern bool ReadPropertyDescriptors(JSContext *cx, JSObject *props, bool checkAccessors, @@ -1413,25 +1414,26 @@ ReadPropertyDescriptors(JSContext *cx, J * bytecode. */ static const unsigned RESOLVE_INFER = 0xffff; /* * If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success. */ extern bool -FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, JSObject *scopeChain, +FindPropertyHelper(JSContext *cx, HandlePropertyName name, + bool cacheResult, HandleObject scopeChain, JSObject **objp, JSObject **pobjp, JSProperty **propp); /* * Search for name either on the current scope chain or on the scope chain's * global object, per the global parameter. */ extern bool -FindProperty(JSContext *cx, PropertyName *name, JSObject *scopeChain, +FindProperty(JSContext *cx, HandlePropertyName name, HandleObject scopeChain, JSObject **objp, JSObject **pobjp, JSProperty **propp); extern JSObject * FindIdentifierBase(JSContext *cx, JSObject *scopeChain, PropertyName *name); } extern JSObject * @@ -1452,60 +1454,60 @@ js_NativeGet(JSContext *cx, JSObject *ob extern JSBool js_NativeSet(JSContext *cx, JSObject *obj, const js::Shape *shape, bool added, bool strict, js::Value *vp); namespace js { bool -GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uint32_t getHow, Value *vp); +GetPropertyHelper(JSContext *cx, HandleObject obj, jsid id, uint32_t getHow, Value *vp); inline bool -GetPropertyHelper(JSContext *cx, JSObject *obj, PropertyName *name, uint32_t getHow, Value *vp) +GetPropertyHelper(JSContext *cx, HandleObject obj, PropertyName *name, uint32_t getHow, Value *vp) { return GetPropertyHelper(cx, obj, ATOM_TO_JSID(name), getHow, vp); } bool -GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, PropertyDescriptor *desc); +GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, PropertyDescriptor *desc); bool -GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, Value *vp); +GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, Value *vp); bool NewPropertyDescriptorObject(JSContext *cx, const PropertyDescriptor *desc, Value *vp); } /* namespace js */ extern JSBool -js_GetMethod(JSContext *cx, JSObject *obj, jsid id, unsigned getHow, js::Value *vp); +js_GetMethod(JSContext *cx, js::HandleObject obj, jsid id, unsigned getHow, js::Value *vp); namespace js { inline bool -GetMethod(JSContext *cx, JSObject *obj, PropertyName *name, unsigned getHow, Value *vp) +GetMethod(JSContext *cx, HandleObject obj, PropertyName *name, unsigned getHow, Value *vp) { return js_GetMethod(cx, obj, ATOM_TO_JSID(name), getHow, vp); } } /* namespace js */ namespace js { /* * If obj has an already-resolved data property for id, return true and * store the property value in *vp. This helper assumes the caller has already * called js_CheckForStringIndex. */ extern bool -HasDataProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp); +HasDataProperty(JSContext *cx, HandleObject obj, jsid id, Value *vp); inline bool -HasDataProperty(JSContext *cx, JSObject *obj, JSAtom *atom, Value *vp) +HasDataProperty(JSContext *cx, HandleObject obj, JSAtom *atom, Value *vp) { return HasDataProperty(cx, obj, js_CheckForStringIndex(ATOM_TO_JSID(atom)), vp); } extern JSBool CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, js::Value *vp, unsigned *attrsp);
--- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -91,17 +91,21 @@ JSObject::enumerate(JSContext *cx, JSIte JSNewEnumerateOp op = getOps()->enumerate; return (op ? op : JS_EnumerateState)(cx, this, iterop, statep, idp); } inline bool JSObject::defaultValue(JSContext *cx, JSType hint, js::Value *vp) { JSConvertOp op = getClass()->convert; - bool ok = (op == JS_ConvertStub ? js::DefaultValue : op)(cx, this, hint, vp); + bool ok; + if (op == JS_ConvertStub) + ok = js::DefaultValue(cx, js::RootedVarObject(cx, this), hint, vp); + else + ok = op(cx, this, hint, vp); JS_ASSERT_IF(ok, vp->isPrimitive()); return ok; } inline JSType JSObject::typeOf(JSContext *cx) { js::TypeOfOp op = getOps()->typeOf; @@ -115,31 +119,34 @@ JSObject::thisObject(JSContext *cx) return op ? op(cx, this) : this; } inline JSBool JSObject::setGeneric(JSContext *cx, jsid id, js::Value *vp, JSBool strict) { if (getOps()->setGeneric) return nonNativeSetProperty(cx, id, vp, strict); - return js_SetPropertyHelper(cx, this, id, 0, vp, strict); + return js_SetPropertyHelper(cx, + js::RootedVarObject(cx, this), + js::RootedVarId(cx, id), + 0, vp, strict); } inline JSBool JSObject::setProperty(JSContext *cx, js::PropertyName *name, js::Value *vp, JSBool strict) { return setGeneric(cx, ATOM_TO_JSID(name), vp, strict); } inline JSBool JSObject::setElement(JSContext *cx, uint32_t index, js::Value *vp, JSBool strict) { if (getOps()->setElement) return nonNativeSetElement(cx, index, vp, strict); - return js_SetElementHelper(cx, this, index, 0, vp, strict); + return js_SetElementHelper(cx, js::RootedVarObject(cx, this), index, 0, vp, strict); } inline JSBool JSObject::setSpecial(JSContext *cx, js::SpecialId sid, js::Value *vp, JSBool strict) { return setGeneric(cx, SPECIALID_TO_JSID(sid), vp, strict); } @@ -179,17 +186,17 @@ JSObject::changePropertyAttributes(JSCon inline JSBool JSObject::getGeneric(JSContext *cx, JSObject *receiver, jsid id, js::Value *vp) { js::GenericIdOp op = getOps()->getGeneric; if (op) { if (!op(cx, this, receiver, id, vp)) return false; } else { - if (!js_GetProperty(cx, this, receiver, id, vp)) + if (!js_GetProperty(cx, js::RootedVarObject(cx, this), js::RootedVarObject(cx, receiver), id, vp)) return false; } return true; } inline JSBool JSObject::getProperty(JSContext *cx, JSObject *receiver, js::PropertyName *name, js::Value *vp) { @@ -1530,21 +1537,21 @@ bool FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject **protop, Class *clasp); /* * Create a plain object with the specified type. This bypasses getNewType to * avoid losing creation site information for objects made by scripted 'new'. */ JSObject * -NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc::AllocKind kind); +NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind kind); /* Make an object with pregenerated shape from a NEWOBJECT bytecode. */ static inline JSObject * -CopyInitializerObject(JSContext *cx, JSObject *baseobj) +CopyInitializerObject(JSContext *cx, HandleObject baseobj) { JS_ASSERT(baseobj->getClass() == &ObjectClass); JS_ASSERT(!baseobj->inDictionaryMode()); gc::AllocKind kind = gc::GetGCObjectFixedSlotsKind(baseobj->numFixedSlots()); #ifdef JS_THREADSAFE kind = gc::GetBackgroundAllocKind(kind); #endif @@ -1556,17 +1563,17 @@ CopyInitializerObject(JSContext *cx, JSO if (!obj->setLastProperty(cx, baseobj->lastProperty())) return NULL; return obj; } JSObject * -NewReshapedObject(JSContext *cx, js::types::TypeObject *type, JSObject *parent, +NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind kind, const Shape *shape); /* * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of * the object, zero if the final size is unknown. This should only be used for * objects that do not require any fixed slots. */ static inline gc::AllocKind @@ -1728,22 +1735,16 @@ DefineConstructorAndPrototype(JSContext extern JSObject * js_InitClass(JSContext *cx, js::HandleObject obj, JSObject *parent_proto, js::Class *clasp, JSNative constructor, unsigned nargs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs, JSObject **ctorp = NULL, js::gc::AllocKind ctorKind = JSFunction::FinalizeKind); -inline JSObject * -js_GetProtoIfDenseArray(JSObject *obj) -{ - return obj->isDenseArray() ? obj->getProto() : obj; -} - /* * js_PurgeScopeChain does nothing if obj is not itself a prototype or parent * scope, else it reshapes the scope and prototype chains it links. It calls * js_PurgeScopeChainHelper, which asserts that obj is flagged as a delegate * (i.e., obj has ever been on a prototype or parent chain). */ extern bool js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id);
--- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -309,17 +309,17 @@ static bool PreprocessValue(JSContext *cx, JSObject *holder, KeyType key, Value *vp, StringifyContext *scx) { JSString *keyStr = NULL; /* Step 2. */ if (vp->isObject()) { Value toJSON; jsid id = ATOM_TO_JSID(cx->runtime->atomState.toJSONAtom); - if (!js_GetMethod(cx, &vp->toObject(), id, 0, &toJSON)) + if (!js_GetMethod(cx, RootedVarObject(cx, &vp->toObject()), id, 0, &toJSON)) return false; if (js_IsCallable(toJSON)) { keyStr = KeyStringifier<KeyType>::toString(cx, key); if (!keyStr) return false; InvokeArgsGuard args; @@ -742,17 +742,17 @@ js_Stringify(JSContext *cx, Value *vp, J if (!gap.append(str->chars(), len)) return false; } else { /* Step 8. */ JS_ASSERT(gap.empty()); } /* Step 9. */ - JSObject *wrapper = NewBuiltinClassInstance(cx, &ObjectClass); + RootedVarObject wrapper(cx, NewBuiltinClassInstance(cx, &ObjectClass)); if (!wrapper) return false; /* Step 10. */ jsid emptyId = ATOM_TO_JSID(cx->runtime->atomState.emptyAtom); if (!DefineNativeProperty(cx, wrapper, emptyId, *vp, JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0)) { @@ -780,17 +780,17 @@ Walk(JSContext *cx, JSObject *holder, js /* Step 1. */ Value val; if (!holder->getGeneric(cx, name, &val)) return false; /* Step 2. */ if (val.isObject()) { - JSObject *obj = &val.toObject(); + RootedVarObject obj(cx, &val.toObject()); /* 'val' must have been produced by the JSON parser, so not a proxy. */ JS_ASSERT(!obj->isProxy()); if (obj->isArray()) { /* Step 2a(ii). */ uint32_t length = obj->getArrayLength(); /* Step 2a(i), 2a(iii-iv). */
--- a/js/src/jsonparser.cpp +++ b/js/src/jsonparser.cpp @@ -534,17 +534,18 @@ JSONParser::parse(Value *vp) switch (state) { case FinishObjectMember: { Value v = valueStack.popCopy(); /* * NB: Relies on js_DefineNativeProperty performing * js_CheckForStringIndex. */ jsid propid = ATOM_TO_JSID(&valueStack.popCopy().toString()->asAtom()); - if (!DefineNativeProperty(cx, &valueStack.back().toObject(), propid, v, + RootedVarObject obj(cx, &valueStack.back().toObject()); + if (!DefineNativeProperty(cx, obj, propid, v, JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0)) { return false; } token = advanceAfterProperty(); if (token == ObjectClose) break;
--- a/js/src/jspropertycache.cpp +++ b/js/src/jspropertycache.cpp @@ -50,25 +50,16 @@ using namespace js; PropertyCacheEntry * PropertyCache::fill(JSContext *cx, JSObject *obj, unsigned scopeIndex, JSObject *pobj, const Shape *shape) { JS_ASSERT(this == &JS_PROPERTY_CACHE(cx)); JS_ASSERT(!cx->runtime->gcRunning); /* - * Check for fill from js_SetPropertyHelper where the setter removed shape - * from pobj (via unwatch or delete, e.g.). - */ - if (!pobj->nativeContains(cx, *shape)) { - PCMETER(oddfills++); - return JS_NO_PROP_CACHE_FILL; - } - - /* * Check for overdeep scope and prototype chain. Because resolve, getter, * and setter hooks can change the prototype chain using JS_SetPrototype * after LookupPropertyWithFlags has returned, we calculate the protoIndex * here and not in LookupPropertyWithFlags. * * The scopeIndex can't be wrong. We require JS_SetParent calls to happen * before any running script might consult a parent-linked scope chain. If * this requirement is not satisfied, the fill in progress will never hit,
--- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -265,18 +265,20 @@ ProxyHandler::keys(JSContext *cx, JSObje JS_ASSERT(i <= props.length()); props.resize(i); return true; } bool -ProxyHandler::iterate(JSContext *cx, JSObject *proxy, unsigned flags, Value *vp) +ProxyHandler::iterate(JSContext *cx, JSObject *proxy_, unsigned flags, Value *vp) { + RootedVarObject proxy(cx, proxy_); + JS_ASSERT(OperationInProgress(cx, proxy)); AutoIdVector props(cx); if ((flags & JSITER_OWNONLY) ? !keys(cx, proxy, props) : !enumerate(cx, proxy, props)) { return false; } return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp); @@ -313,17 +315,17 @@ ProxyHandler::regexp_toShared(JSContext { JS_NOT_REACHED("This should have been a wrapped regexp"); return false; } bool ProxyHandler::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *vp) { - return DefaultValue(cx, proxy, hint, vp); + return DefaultValue(cx, RootedVarObject(cx, proxy), hint, vp); } bool ProxyHandler::iteratorNext(JSContext *cx, JSObject *proxy, Value *vp) { vp->setMagic(JS_NO_ITER_VALUE); return true; } @@ -1751,17 +1753,18 @@ FixProxy(JSContext *cx, JSObject *proxy, JSObject *parent = proxy->getParent(); Class *clasp = IsFunctionProxy(proxy) ? &CallableObjectClass : &ObjectClass; /* * Make a blank object from the recipe fix provided to us. This must have * number of fixed slots as the proxy so that we can swap their contents. */ gc::AllocKind kind = proxy->getAllocKind(); - JSObject *newborn = NewObjectWithGivenProto(cx, clasp, proto, parent, kind); + RootedVarObject newborn(cx); + newborn = NewObjectWithGivenProto(cx, clasp, proto, parent, kind); if (!newborn) return false; if (clasp == &CallableObjectClass) { newborn->setSlot(JSSLOT_CALLABLE_CALL, GetCall(proxy)); newborn->setSlot(JSSLOT_CALLABLE_CONSTRUCT, GetConstruct(proxy)); }
--- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -124,16 +124,17 @@ struct ArgumentsData; struct Class; class RegExpGuard; class RegExpObject; class RegExpObjectBuilder; class RegExpShared; class RegExpStatics; class MatchPairs; +class PropertyName; namespace detail { class RegExpCode; } enum RegExpFlag { IgnoreCaseFlag = 0x01, GlobalFlag = 0x02, MultilineFlag = 0x04, @@ -244,82 +245,33 @@ namespace types { class TypeSet; struct TypeCallsite; struct TypeObject; struct TypeCompartment; } /* namespace types */ -enum ThingRootKind -{ - THING_ROOT_OBJECT, - THING_ROOT_SHAPE, - THING_ROOT_BASE_SHAPE, - THING_ROOT_TYPE_OBJECT, - THING_ROOT_STRING, - THING_ROOT_SCRIPT, - THING_ROOT_ID, - THING_ROOT_VALUE, - THING_ROOT_LIMIT -}; - -template <typename T> class Root; -template <typename T> class RootedVar; - -template <typename T> -struct RootMethods { }; - -/* - * Reference to a stack location rooted for GC. See "Moving GC Stack Rooting" - * comment in jscntxt.h. - */ -template <typename T> -class Handle -{ - public: - /* Copy handles of different types, with implicit coercion. */ - template <typename S> Handle(Handle<S> handle) { - testAssign<S>(); - ptr = reinterpret_cast<const T *>(handle.address()); - } +typedef JS::Handle<Shape*> HandleShape; +typedef JS::Handle<BaseShape*> HandleBaseShape; +typedef JS::Handle<types::TypeObject*> HandleTypeObject; +typedef JS::Handle<JSAtom*> HandleAtom; +typedef JS::Handle<PropertyName*> HandlePropertyName; - /* Get a handle from a rooted stack location, with implicit coercion. */ - template <typename S> inline Handle(const Root<S> &root); - template <typename S> inline Handle(const RootedVar<S> &root); - - const T *address() { return ptr; } - - operator T () { return value(); } - T operator ->() { return value(); } - - private: - const T *ptr; - T value() { return *ptr; } +typedef JS::Root<Shape*> RootShape; +typedef JS::Root<BaseShape*> RootBaseShape; +typedef JS::Root<types::TypeObject*> RootTypeObject; +typedef JS::Root<JSAtom*> RootAtom; +typedef JS::Root<PropertyName*> RootPropertyName; - template <typename S> - void testAssign() { -#ifdef DEBUG - T a = RootMethods<T>::initial(); - S b = RootMethods<S>::initial(); - a = b; - (void)a; -#endif - } -}; - -typedef Handle<JSObject*> HandleObject; -typedef Handle<JSFunction*> HandleFunction; -typedef Handle<Shape*> HandleShape; -typedef Handle<BaseShape*> HandleBaseShape; -typedef Handle<types::TypeObject*> HandleTypeObject; -typedef Handle<JSString*> HandleString; -typedef Handle<JSAtom*> HandleAtom; -typedef Handle<jsid> HandleId; -typedef Handle<Value> HandleValue; +typedef JS::RootedVar<Shape*> RootedVarShape; +typedef JS::RootedVar<BaseShape*> RootedVarBaseShape; +typedef JS::RootedVar<types::TypeObject*> RootedVarTypeObject; +typedef JS::RootedVar<JSAtom*> RootedVarAtom; +typedef JS::RootedVar<PropertyName*> RootedVarPropertyName; enum XDRMode { XDR_ENCODE, XDR_DECODE }; template <XDRMode mode> class XDRState;
--- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -239,9 +239,72 @@ typedef struct JSString typedef struct PRCallOnceType JSCallOnceType; #else typedef JSBool JSCallOnceType; #endif typedef JSBool (*JSInitCallback)(void); JS_END_EXTERN_C +#ifdef __cplusplus + +namespace JS { + +template <typename T> +class Root; + +class SkipRoot; + +enum ThingRootKind +{ + THING_ROOT_OBJECT, + THING_ROOT_SHAPE, + THING_ROOT_BASE_SHAPE, + THING_ROOT_TYPE_OBJECT, + THING_ROOT_STRING, + THING_ROOT_SCRIPT, + THING_ROOT_ID, + THING_ROOT_VALUE, + THING_ROOT_LIMIT +}; + +struct ContextFriendFields { + JSRuntime *const runtime; + + ContextFriendFields(JSRuntime *rt) + : runtime(rt) { } + + static const ContextFriendFields *get(const JSContext *cx) { + return reinterpret_cast<const ContextFriendFields *>(cx); + } + + static ContextFriendFields *get(JSContext *cx) { + return reinterpret_cast<ContextFriendFields *>(cx); + } + +#ifdef JSGC_ROOT_ANALYSIS + + /* + * Stack allocated GC roots for stack GC heap pointers, which may be + * overwritten if moved during a GC. + */ + Root<void*> *thingGCRooters[THING_ROOT_LIMIT]; + +#ifdef DEBUG + /* + * Stack allocated list of stack locations which hold non-relocatable + * GC heap pointers (where the target is rooted somewhere else) or integer + * values which may be confused for GC heap pointers. These are used to + * suppress false positives which occur when a rooting analysis treats the + * location as holding a relocatable pointer, but have no other effect on + * GC behavior. + */ + SkipRoot *skipGCRooters; +#endif + +#endif /* JSGC_ROOT_ANALYSIS */ +}; + +} /* namespace JS */ + +#endif /* __cplusplus */ + #endif /* jspubtd_h___ */
--- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -174,17 +174,19 @@ class NodeBuilder Value callbacks[AST_LIMIT]; /* user-specified callbacks */ Value userv; /* user-specified builder object or null */ public: NodeBuilder(JSContext *c, bool l, char const *s) : cx(c), saveLoc(l), src(s) { } - bool init(JSObject *userobj = NULL) { + bool init(JSObject *userobj_ = NULL) { + RootedVarObject userobj(cx, userobj_); + if (src) { if (!atomValue(src, &srcval)) return false; } else { srcval.setNull(); } if (!userobj) { @@ -197,17 +199,20 @@ class NodeBuilder userv.setObject(*userobj); for (unsigned i = 0; i < AST_LIMIT; i++) { Value funv; const char *name = callbackNames[i]; JSAtom *atom = js_Atomize(cx, name, strlen(name)); - if (!atom || !GetPropertyDefault(cx, userobj, ATOM_TO_JSID(atom), NullValue(), &funv)) + if (!atom) + return false; + RootedVarId id(cx, ATOM_TO_JSID(atom)); + if (!GetPropertyDefault(cx, userobj, id, NullValue(), &funv)) return false; if (funv.isNullOrUndefined()) { callbacks[i].setNull(); continue; } if (!funv.isObject() || !funv.toObject().isFunction()) { @@ -3118,34 +3123,32 @@ reflect_parse(JSContext *cx, uint32_t ar if (!arg.isNullOrUndefined()) { if (!arg.isObject()) { js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, arg, NULL, "not an object", NULL); return JS_FALSE; } - JSObject *config = &arg.toObject(); + RootedVarObject config(cx, &arg.toObject()); Value prop; /* config.loc */ - if (!GetPropertyDefault(cx, config, ATOM_TO_JSID(cx->runtime->atomState.locAtom), - BooleanValue(true), &prop)) { + RootedVarId locId(cx, ATOM_TO_JSID(cx->runtime->atomState.locAtom)); + if (!GetPropertyDefault(cx, config, locId, BooleanValue(true), &prop)) return JS_FALSE; - } loc = js_ValueToBoolean(prop); if (loc) { /* config.source */ - if (!GetPropertyDefault(cx, config, ATOM_TO_JSID(cx->runtime->atomState.sourceAtom), - NullValue(), &prop)) { + RootedVarId sourceId(cx, ATOM_TO_JSID(cx->runtime->atomState.sourceAtom)); + if (!GetPropertyDefault(cx, config, sourceId, NullValue(), &prop)) return JS_FALSE; - } if (!prop.isNullOrUndefined()) { JSString *str = ToString(cx, prop); if (!str) return JS_FALSE; size_t length = str->length(); const jschar *chars = str->getChars(cx); @@ -3154,28 +3157,27 @@ reflect_parse(JSContext *cx, uint32_t ar filename = DeflateString(cx, chars, length); if (!filename) return JS_FALSE; filenamep.reset(filename); } /* config.line */ - if (!GetPropertyDefault(cx, config, ATOM_TO_JSID(cx->runtime->atomState.lineAtom), - Int32Value(1), &prop) || + RootedVarId lineId(cx, ATOM_TO_JSID(cx->runtime->atomState.lineAtom)); + if (!GetPropertyDefault(cx, config, lineId, Int32Value(1), &prop) || !ToUint32(cx, prop, &lineno)) { return JS_FALSE; } } /* config.builder */ - if (!GetPropertyDefault(cx, config, ATOM_TO_JSID(cx->runtime->atomState.builderAtom), - NullValue(), &prop)) { + RootedVarId builderId(cx, ATOM_TO_JSID(cx->runtime->atomState.builderAtom)); + if (!GetPropertyDefault(cx, config, builderId, NullValue(), &prop)) return JS_FALSE; - } if (!prop.isNullOrUndefined()) { if (!prop.isObject()) { js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, prop, NULL, "not an object", NULL); return JS_FALSE; } builder = &prop.toObject();
--- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -307,17 +307,17 @@ PropertyTable::grow(JSContext *cx) Shape * Shape::getChildBinding(JSContext *cx, const StackShape &child) { JS_ASSERT(!inDictionary()); Shape *shape = JS_PROPERTY_TREE(cx).getChild(cx, this, numFixedSlots(), child); if (shape) { - JS_ASSERT(shape->parent == this); + //JS_ASSERT(shape->parent == this); // XXX 'this' is not rooted here /* * Update the number of fixed slots which bindings of this shape will * have. Bindings are constructed as new properties come in, so the * call object allocation class is not known ahead of time. Compute * the fixed slot count here, which will feed into call objects created * off of the bindings. */ @@ -515,22 +515,24 @@ JSObject::addProperty(JSContext *cx, jsi if (!isExtensible()) { reportNotExtensible(cx); return NULL; } NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter); + RootedVarObject self(cx, this); + Shape **spp = NULL; if (inDictionaryMode()) spp = lastProperty()->table().search(id, true); - return addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, - spp, allowDictionary); + return self->addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, + spp, allowDictionary); } Shape * JSObject::addPropertyInternal(JSContext *cx, jsid id, PropertyOp getter, StrictPropertyOp setter, uint32_t slot, unsigned attrs, unsigned flags, int shortid, Shape **spp, bool allowDictionary) @@ -546,17 +548,17 @@ JSObject::addPropertyInternal(JSContext (slot == SHAPE_INVALID_SLOT) || lastProperty()->hasMissingSlot() || (slot == lastProperty()->maybeSlot() + 1); JS_ASSERT_IF(!allowDictionary, stableSlot); if (allowDictionary && (!stableSlot || lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT)) { if (!toDictionaryMode(cx)) return NULL; - table = &lastProperty()->table(); + table = &self->lastProperty()->table(); spp = table->search(id, true); } } else { table = &lastProperty()->table(); if (table->needsToGrow()) { if (!table->grow(cx)) return NULL; spp = table->search(id, true); @@ -990,17 +992,17 @@ JSObject::replaceWithNewEquivalentShape( JSObject *self = this; if (!inDictionaryMode()) { RootObject selfRoot(cx, &self); RootShape newRoot(cx, &newShape); if (!toDictionaryMode(cx)) return NULL; - oldShape = lastProperty(); + oldShape = self->lastProperty(); } if (!newShape) { RootObject selfRoot(cx, &self); RootShape oldRoot(cx, &oldShape); newShape = js_NewGCShape(cx); if (!newShape) return NULL; @@ -1017,17 +1019,17 @@ JSObject::replaceWithNewEquivalentShape( * enumeration order (see bug 601399). */ StackShape nshape(oldShape); newShape->initDictionaryShape(nshape, self->numFixedSlots(), oldShape->listp); JS_ASSERT(newShape->parent == oldShape); oldShape->removeFromDictionary(self); - if (newShape == lastProperty()) + if (newShape == self->lastProperty()) oldShape->handoffTableTo(newShape); if (spp) SHAPE_STORE_PRESERVING_COLLISION(spp, newShape); return newShape; } bool @@ -1264,17 +1266,17 @@ Bindings::setParent(JSContext *cx, JSObj { /* * This may be invoked on GC heap allocated bindings, in which case this * is pointing to an internal value of a JSScript that can't itself be * relocated. The script itself will be rooted, and will not be moved, so * mark the stack value as non-relocatable for the stack root analysis. */ Bindings *self = this; - CheckRoot root(cx, &self); + SkipRoot root(cx, &self); RootObject rootObj(cx, &obj); if (!ensureShape(cx)) return false; /* This is only used for Block objects, which have a NULL proto. */ Shape *newShape = Shape::setObjectParent(cx, obj, NULL, self->lastBinding); @@ -1315,31 +1317,34 @@ EmptyShape::getInitialShape(JSContext *c size_t nfixed = GetGCKindSlots(kind, clasp); InitialShapeEntry::Lookup lookup(clasp, proto, parent, nfixed, objectFlags); InitialShapeSet::AddPtr p = table.lookupForAdd(lookup); if (p) return p->shape; + RootObject protoRoot(cx, &lookup.proto); + RootObject parentRoot(cx, &lookup.parent); + RootedVar<UnownedBaseShape*> nbase(cx); StackBaseShape base(clasp, parent, objectFlags); nbase = BaseShape::getUnowned(cx, base); if (!nbase) return NULL; Shape *shape = JS_PROPERTY_TREE(cx).newShape(cx); if (!shape) return NULL; new (shape) EmptyShape(nbase, nfixed); InitialShapeEntry entry; entry.shape = shape; - entry.proto = proto; + entry.proto = lookup.proto; if (!table.relookupOrAdd(p, lookup, entry)) return NULL; return shape; } void
--- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -514,16 +514,20 @@ struct Shape : public js::gc::Cell has a double-indirect back pointer, either to the next shape's parent if not last, else to obj->shape_ */ }; static inline Shape *search(JSContext *cx, Shape *start, jsid id, Shape ***pspp, bool adding = false); +#ifdef DEBUG + static inline Shape *searchNoAllocation(JSContext *cx, Shape *start, jsid id); +#endif + inline void removeFromDictionary(JSObject *obj); inline void insertIntoDictionary(HeapPtrShape *dictp); inline void initDictionaryShape(const StackShape &child, uint32_t nfixed, HeapPtrShape *dictp); Shape *getChildBinding(JSContext *cx, const StackShape &child); @@ -1074,16 +1078,34 @@ Shape::search(JSContext *cx, Shape *star for (Shape *shape = start; shape; shape = shape->parent) { if (shape->propidRef() == id) return shape; } return NULL; } +#ifdef DEBUG +/* static */ inline Shape * +Shape::searchNoAllocation(JSContext *cx, Shape *start, jsid id) +{ + if (start->hasTable()) { + Shape **spp = start->table().search(id, false); + return SHAPE_FETCH(spp); + } + + for (Shape *shape = start; shape; shape = shape->parent) { + if (shape->propidRef() == id) + return shape; + } + + return NULL; +} +#endif /* DEBUG */ + } // namespace js #ifdef _MSC_VER #pragma warning(pop) #pragma warning(pop) #endif namespace JS {
--- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -98,17 +98,17 @@ Bindings::lookup(JSContext *cx, JSAtom * if (shape->getter() == CallObject::getArgOp) return ARGUMENT; return shape->writable() ? VARIABLE : CONSTANT; } bool -Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind) +Bindings::add(JSContext *cx, HandleAtom name, BindingKind kind) { if (!ensureShape(cx)) return false; if (nargs + nvars == BINDING_COUNT_LIMIT) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, (kind == ARGUMENT) ? JSMSG_TOO_MANY_FUN_ARGS @@ -487,20 +487,20 @@ js::XDRScript(XDRState<mode> *xdr, JSScr if (!bindings.addDestructuring(cx, &dummy)) return false; } else { JS_ASSERT(!names[i]); } continue; } - JSAtom *name; + RootedVarAtom name(cx); if (mode == XDR_ENCODE) name = names[i]; - if (!XDRAtom(xdr, &name)) + if (!XDRAtom(xdr, name.address())) return false; if (mode == XDR_DECODE) { BindingKind kind = (i < nargs) ? ARGUMENT : (bitmap[i >> JS_BITS_PER_UINT32_LOG2] & JS_BIT(i & (JS_BITS_PER_UINT32 - 1)) ? CONSTANT : VARIABLE);
--- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -189,33 +189,33 @@ class Bindings * given name prevails. (We preserve both bindings for the decompiler, * which must deal with such cases.) Pass null for name when indicating a * destructuring argument. Return true on success. * * The parser builds shape paths for functions, usable by Call objects at * runtime, by calling an "add" method. All ARGUMENT bindings must be added * before before any VARIABLE or CONSTANT bindings. */ - bool add(JSContext *cx, JSAtom *name, BindingKind kind); + bool add(JSContext *cx, HandleAtom name, BindingKind kind); /* Convenience specializations. */ - bool addVariable(JSContext *cx, JSAtom *name) { + bool addVariable(JSContext *cx, HandleAtom name) { return add(cx, name, VARIABLE); } - bool addConstant(JSContext *cx, JSAtom *name) { + bool addConstant(JSContext *cx, HandleAtom name) { return add(cx, name, CONSTANT); } - bool addArgument(JSContext *cx, JSAtom *name, uint16_t *slotp) { + bool addArgument(JSContext *cx, HandleAtom name, uint16_t *slotp) { JS_ASSERT(name != NULL); /* not destructuring */ *slotp = nargs; return add(cx, name, ARGUMENT); } bool addDestructuring(JSContext *cx, uint16_t *slotp) { *slotp = nargs; - return add(cx, NULL, ARGUMENT); + return add(cx, RootedVarAtom(cx), ARGUMENT); } void noteDup() { hasDup_ = true; } bool hasDup() const { return hasDup_; } /* * Look up an argument or variable name, returning its kind when found or * NONE when no such name exists. When indexp is not null and the name
--- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -464,21 +464,21 @@ static JS_ALWAYS_INLINE JSString * ThisToStringForStringProto(JSContext *cx, CallReceiver call) { JS_CHECK_RECURSION(cx, return NULL); if (call.thisv().isString()) return call.thisv().toString(); if (call.thisv().isObject()) { - JSObject *obj = &call.thisv().toObject(); + RootedVarObject obj(cx, &call.thisv().toObject()); if (obj->isString() && ClassMethodIsNative(cx, obj, &StringClass, - ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), + RootedVarId(cx, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom)), js_str_toString)) { JSString *str = obj->asString().unbox(); call.thisv().setString(str); return str; } } else if (call.thisv().isNullOrUndefined()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO, @@ -1493,17 +1493,17 @@ class StringRegExpGuard /* If the pattern is not already a regular expression, make it so. */ bool normalizeRegExp(JSContext *cx, bool flat, unsigned optarg, CallArgs args) { if (re_.initialized()) return true; /* Build RegExp from pattern string. */ - JSString *opt; + RootedVarString opt(cx); if (optarg < args.length()) { opt = ToString(cx, args[optarg]); if (!opt) return false; } else { opt = NULL; } @@ -1546,42 +1546,46 @@ enum MatchControlFlags { REPLACE_ARGS = TEST_GLOBAL_BIT | TEST_SINGLE_BIT | CALLBACK_ON_SINGLE_BIT }; /* Factor out looping and matching logic. */ static bool DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, RegExpShared &re, DoMatchCallback callback, void *data, MatchControlFlags flags, Value *rval) { - JSLinearString *linearStr = str->ensureLinear(cx); + RootedVar<JSLinearString*> linearStr(cx, str->ensureLinear(cx)); if (!linearStr) return false; - const jschar *chars = linearStr->chars(); - size_t length = linearStr->length(); - if (re.global()) { RegExpExecType type = (flags & TEST_GLOBAL_BIT) ? RegExpTest : RegExpExec; for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) { if (!JS_CHECK_OPERATION_LIMIT(cx)) return false; - if (!ExecuteRegExp(cx, res, re, linearStr, chars, length, &i, type, rval)) + + const jschar *chars = linearStr->chars(); + size_t charsLen = linearStr->length(); + + if (!ExecuteRegExp(cx, res, re, linearStr, chars, charsLen, &i, type, rval)) return false; if (!Matched(type, *rval)) break; if (!callback(cx, res, count, data)) return false; if (!res->matched()) ++i; } } else { + const jschar *chars = linearStr->chars(); + size_t charsLen = linearStr->length(); + RegExpExecType type = (flags & TEST_SINGLE_BIT) ? RegExpTest : RegExpExec; bool callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT); size_t i = 0; - if (!ExecuteRegExp(cx, res, re, linearStr, chars, length, &i, type, rval)) + if (!ExecuteRegExp(cx, res, re, linearStr, chars, charsLen, &i, type, rval)) return false; if (callbackOnSingle && Matched(type, *rval) && !callback(cx, res, 0, data)) return false; } return true; } static bool @@ -1647,18 +1651,18 @@ js::str_match(JSContext *cx, unsigned ar /* Return if there was an error in tryFlatMatch. */ if (cx->isExceptionPending()) return false; if (!g.normalizeRegExp(cx, false, 1, args)) return false; - JSObject *array = NULL; - MatchArgType arg = &array; + RootedVarObject array(cx); + MatchArgType arg = array.address(); RegExpStatics *res = cx->regExpStatics(); Value rval; if (!DoMatch(cx, res, str, g.regExp(), MatchCallback, arg, MATCH_ARGS, &rval)) return false; if (g.regExp().global()) args.rval() = ObjectOrNullValue(array); else @@ -1707,24 +1711,24 @@ js::str_search(JSContext *cx, unsigned a else args.rval() = Int32Value(-1); return true; } struct ReplaceData { ReplaceData(JSContext *cx) - : sb(cx) + : str(cx), lambda(cx), elembase(cx), repstr(cx), sb(cx) {} - JSString *str; /* 'this' parameter object as a string */ + RootedVarString str; /* 'this' parameter object as a string */ StringRegExpGuard g; /* regexp parameter object and private data */ - JSObject *lambda; /* replacement function object or null */ - JSObject *elembase; /* object for function(a){return b[a]} replace */ - JSLinearString *repstr; /* replacement string */ + RootedVarObject lambda; /* replacement function object or null */ + RootedVarObject elembase; /* object for function(a){return b[a]} replace */ + RootedVar<JSLinearString*> repstr; /* replacement string */ const jschar *dollar; /* null or pointer to first $ in repstr */ const jschar *dollarEnd; /* limit pointer for js_strchr_limit */ int leftIndex; /* left context index in str->chars */ JSSubString dollarStr; /* for "$$" InterpretDollar result */ bool calledBack; /* record whether callback has been called */ InvokeArgsGuard args; /* arguments for lambda call */ StringBuffer sb; /* buffer built during DoMatch */ }; @@ -1791,17 +1795,18 @@ InterpretDollar(JSContext *cx, RegExpSta return true; } return false; } static bool FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t *sizep) { - if (JSObject *base = rdata.elembase) { + RootedVarObject base(cx, rdata.elembase); + if (base) { /* * The base object is used when replace was passed a lambda which looks like * 'function(a) { return b[a]; }' for the base object b. b will not change * in the course of the replace unless we end up making a scripted call due * to accessing a scripted getter or a value with a scripted toString. */ JS_ASSERT(rdata.lambda); JS_ASSERT(!base->getOps()->lookupProperty); @@ -1939,36 +1944,38 @@ DoReplace(JSContext *cx, RegExpStatics * } static bool ReplaceRegExpCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p) { ReplaceData &rdata = *static_cast<ReplaceData *>(p); rdata.calledBack = true; - JSLinearString &str = rdata.str->asLinear(); /* flattened for regexp */ size_t leftoff = rdata.leftIndex; - const jschar *left = str.chars() + leftoff; size_t leftlen = res->matchStart() - leftoff; rdata.leftIndex = res->matchLimit(); size_t replen = 0; /* silence 'unused' warning */ if (!FindReplaceLength(cx, res, rdata, &replen)) return false; size_t growth = leftlen + replen; if (!rdata.sb.reserve(rdata.sb.length() + growth)) return false; + + JSLinearString &str = rdata.str->asLinear(); /* flattened for regexp */ + const jschar *left = str.chars() + leftoff; + rdata.sb.infallibleAppend(left, leftlen); /* skipped-over portion of the search value */ DoReplace(cx, res, rdata); return true; } static bool -BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr, +BuildFlatReplacement(JSContext *cx, HandleString textstr, HandleString repstr, const FlatMatch &fm, CallArgs *args) { RopeBuilder builder(cx); size_t match = fm.match(); size_t matchEnd = match + fm.patternLength(); if (textstr->isRope()) { /* @@ -1990,48 +1997,52 @@ BuildFlatReplacement(JSContext *cx, JSSt */ if (match >= pos) { /* * If this part of the rope overlaps with the left side of * the pattern, then it must be the only one to overlap with * the first character in the pattern, so we include the * replacement string here. */ - JSString *leftSide = js_NewDependentString(cx, str, 0, match - pos); + RootedVarString leftSide(cx); + leftSide = js_NewDependentString(cx, str, 0, match - pos); if (!leftSide || !builder.append(leftSide) || !builder.append(repstr)) { return false; } } /* * If str runs off the end of the matched string, append the * last part of str. */ if (strEnd > matchEnd) { - JSString *rightSide = js_NewDependentString(cx, str, matchEnd - pos, - strEnd - matchEnd); + RootedVarString rightSide(cx); + rightSide = js_NewDependentString(cx, str, matchEnd - pos, + strEnd - matchEnd); if (!rightSide || !builder.append(rightSide)) return false; } } else { - if (!builder.append(str)) + if (!builder.append(RootedVarString(cx, str))) return false; } pos += str->length(); if (!r.popFront()) return false; } } else { - JSString *leftSide = js_NewDependentString(cx, textstr, 0, match); + RootedVarString leftSide(cx); + leftSide = js_NewDependentString(cx, textstr, 0, match); if (!leftSide) return false; - JSString *rightSide = js_NewDependentString(cx, textstr, match + fm.patternLength(), - textstr->length() - match - fm.patternLength()); + RootedVarString rightSide(cx); + rightSide = js_NewDependentString(cx, textstr, match + fm.patternLength(), + textstr->length() - match - fm.patternLength()); if (!rightSide || !builder.append(leftSide) || !builder.append(repstr) || !builder.append(rightSide)) { return false; } } @@ -2097,25 +2108,25 @@ BuildDollarReplacement(JSContext *cx, JS break; default: /* The dollar we saw was not special (no matter what its mother told it). */ ENSURE(newReplaceChars.append(*it)); continue; } ++it; /* We always eat an extra char in the above switch. */ } - JSString *leftSide = js_NewDependentString(cx, textstr, 0, matchStart); + RootedVarString leftSide(cx, js_NewDependentString(cx, textstr, 0, matchStart)); ENSURE(leftSide); - JSString *newReplace = newReplaceChars.finishString(); + RootedVarString newReplace(cx, newReplaceChars.finishString()); ENSURE(newReplace); JS_ASSERT(textstr->length() >= matchLimit); - JSString *rightSide = js_NewDependentString(cx, textstr, matchLimit, - textstr->length() - matchLimit); + RootedVarString rightSide(cx, js_NewDependentString(cx, textstr, matchLimit, + textstr->length() - matchLimit)); ENSURE(rightSide); RopeBuilder builder(cx); ENSURE(builder.append(leftSide) && builder.append(newReplace) && builder.append(rightSide)); #undef ENSURE @@ -2179,27 +2190,27 @@ str_replace_flat_lambda(JSContext *cx, C Value *sp = args.array(); sp[0].setString(matchStr); sp[1].setInt32(fm.match()); sp[2].setString(rdata.str); if (!Invoke(cx, rdata.args)) return false; - JSString *repstr = ToString(cx, args.rval()); + RootedVarString repstr(cx, ToString(cx, args.rval())); if (!repstr) return false; - JSString *leftSide = js_NewDependentString(cx, rdata.str, 0, fm.match()); + RootedVarString leftSide(cx, js_NewDependentString(cx, rdata.str, 0, fm.match())); if (!leftSide) return false; size_t matchLimit = fm.match() + fm.patternLength(); - JSString *rightSide = js_NewDependentString(cx, rdata.str, matchLimit, - rdata.str->length() - matchLimit); + RootedVarString rightSide(cx, js_NewDependentString(cx, rdata.str, matchLimit, + rdata.str->length() - matchLimit)); if (!rightSide) return false; RopeBuilder builder(cx); if (!(builder.append(leftSide) && builder.append(repstr) && builder.append(rightSide))) { return false; @@ -2378,17 +2389,17 @@ class SplitMatchResult { void setResult(size_t length, size_t endIndex) { length_ = length; endIndex_ = endIndex; } }; template<class Matcher> static JSObject * -SplitHelper(JSContext *cx, JSLinearString *str, uint32_t limit, Matcher splitMatch, TypeObject *type) +SplitHelper(JSContext *cx, Handle<JSLinearString*> str, uint32_t limit, Matcher splitMatch, TypeObject *type) { size_t strLength = str->length(); SplitMatchResult result; /* Step 11. */ if (strLength == 0) { if (!splitMatch(cx, str, 0, &result)) return NULL; @@ -2401,18 +2412,18 @@ SplitHelper(JSContext *cx, JSLinearStrin * var a = "".split(""); * assertEq(a.length, 0); * var b = "".split(/.?/); * assertEq(b.length, 0); */ if (!result.isFailure()) return NewDenseEmptyArray(cx); - Value v = StringValue(str); - return NewDenseCopiedArray(cx, 1, &v); + RootedVarValue v(cx, StringValue(str)); + return NewDenseCopiedArray(cx, 1, v.address()); } /* Step 12. */ size_t lastEndIndex = 0; size_t index = 0; /* Step 13. */ AutoValueVector splits(cx); @@ -2540,52 +2551,51 @@ class SplitRegExpMatcher result->setResult(sep.length, index); return true; } }; class SplitStringMatcher { - const jschar *sepChars; - size_t sepLength; + RootedVar<JSLinearString*> sep; public: - SplitStringMatcher(JSLinearString *sep) { - sepChars = sep->chars(); - sepLength = sep->length(); - } + SplitStringMatcher(JSContext *cx, JSLinearString *sep) + : sep(cx, sep) + {} static const bool returnsCaptures = false; bool operator()(JSContext *cx, JSLinearString *str, size_t index, SplitMatchResult *res) { JS_ASSERT(index == 0 || index < str->length()); const jschar *chars = str->chars(); - int match = StringMatch(chars + index, str->length() - index, sepChars, sepLength); + int match = StringMatch(chars + index, str->length() - index, + sep->chars(), sep->length()); if (match == -1) res->setFailure(); else - res->setResult(sepLength, index + match + sepLength); + res->setResult(sep->length(), index + match + sep->length()); return true; } }; /* ES5 15.5.4.14 */ JSBool js::str_split(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); /* Steps 1-2. */ JSString *str = ThisToStringForStringProto(cx, args); if (!str) return false; - TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Array); + RootedVarTypeObject type(cx, GetTypeCallerInitObject(cx, JSProto_Array)); if (!type) return false; AddTypeProperty(cx, type, NULL, Type::StringType()); /* Step 5: Use the second argument as the split limit, if given. */ uint32_t limit; if (args.hasDefined(1)) { double d; @@ -2626,24 +2636,24 @@ js::str_split(JSContext *cx, unsigned ar Value v = StringValue(str); JSObject *aobj = NewDenseCopiedArray(cx, 1, &v); if (!aobj) return false; aobj->setType(type); args.rval() = ObjectValue(*aobj); return true; } - JSLinearString *strlin = str->ensureLinear(cx); + RootedVar<JSLinearString*> strlin(cx, str->ensureLinear(cx)); if (!strlin) return false; /* Steps 11-15. */ JSObject *aobj; if (!re.initialized()) - aobj = SplitHelper(cx, strlin, limit, SplitStringMatcher(sepstr), type); + aobj = SplitHelper(cx, strlin, limit, SplitStringMatcher(cx, sepstr), type); else aobj = SplitHelper(cx, strlin, limit, SplitRegExpMatcher(*re, cx->regExpStatics()), type); if (!aobj) return false; /* Step 16. */ aobj->setType(type); args.rval() = ObjectValue(*aobj); @@ -2701,22 +2711,22 @@ out: /* * Python-esque sequence operations. */ static JSBool str_concat(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - JSString *str = ThisToStringForStringProto(cx, args); + RootedVarString str(cx, ThisToStringForStringProto(cx, args)); if (!str) return false; for (unsigned i = 0; i < args.length(); i++) { - JSString *argStr = ToString(cx, args[i]); + RootedVarString argStr(cx, ToString(cx, args[i])); if (!argStr) return false; str = js_ConcatStrings(cx, str, argStr); if (!str) return false; } @@ -3008,17 +3018,17 @@ static JSFunctionSpec string_methods[] = JS_FS_END }; JSBool js_String(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - JSString *str; + RootedVarString str(cx); if (args.length() > 0) { str = ToString(cx, args[0]); if (!str) return false; } else { str = cx->runtime->emptyString; } @@ -3086,24 +3096,25 @@ StringObject::assignInitialShape(JSConte LENGTH_SLOT, JSPROP_PERMANENT | JSPROP_READONLY); } JSObject * js_InitStringClass(JSContext *cx, JSObject *obj) { JS_ASSERT(obj->isNative()); - GlobalObject *global = &obj->asGlobal(); - - JSObject *proto = global->createBlankPrototype(cx, &StringClass); - if (!proto || !proto->asString().init(cx, cx->runtime->emptyString)) + RootedVar<GlobalObject*> global(cx, &obj->asGlobal()); + + RootedVarObject proto(cx, global->createBlankPrototype(cx, &StringClass)); + if (!proto || !proto->asString().init(cx, RootedVarString(cx, cx->runtime->emptyString))) return NULL; /* Now create the String function. */ - JSFunction *ctor = global->createConstructor(cx, js_String, CLASS_ATOM(cx, String), 1); + RootedVarFunction ctor(cx); + ctor = global->createConstructor(cx, js_String, CLASS_ATOM(cx, String), 1); if (!ctor) return NULL; if (!LinkConstructorAndPrototype(cx, ctor, proto)) return NULL; if (!DefinePropertiesAndBrand(cx, proto, NULL, string_methods) || !DefinePropertiesAndBrand(cx, ctor, NULL, string_static_methods)) @@ -3308,17 +3319,17 @@ js_ValueToSource(JSContext *cx, const Va return js_NewStringCopyN(cx, js_negzero_ucNstr, 2); } return ToString(cx, v); } Value rval = NullValue(); Value fval; jsid id = ATOM_TO_JSID(cx->runtime->atomState.toSourceAtom); - if (!js_GetMethod(cx, &v.toObject(), id, 0, &fval)) + if (!js_GetMethod(cx, RootedVarObject(cx, &v.toObject()), id, 0, &fval)) return NULL; if (js_IsCallable(fval)) { if (!Invoke(cx, v, fval, 0, NULL, &rval)) return NULL; } return ToString(cx, rval); }
--- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -69,17 +69,17 @@ class MutatingRopeSegmentRange; /* * Utility for building a rope (lazy concatenation) of strings. */ class RopeBuilder; } /* namespace js */ extern JSString * JS_FASTCALL -js_ConcatStrings(JSContext *cx, JSString *s1, JSString *s2); +js_ConcatStrings(JSContext *cx, js::HandleString s1, js::HandleString s2); extern JSString * JS_FASTCALL js_toLowerCase(JSContext *cx, JSString *str); extern JSString * JS_FASTCALL js_toUpperCase(JSContext *cx, JSString *str); struct JSSubString {
--- a/js/src/jsstrinlines.h +++ b/js/src/jsstrinlines.h @@ -48,27 +48,27 @@ #include "jscntxtinlines.h" #include "jsgcinlines.h" #include "vm/String-inl.h" namespace js { class RopeBuilder { JSContext *cx; - JSString *res; + RootedVarString res; RopeBuilder(const RopeBuilder &other) MOZ_DELETE; void operator=(const RopeBuilder &other) MOZ_DELETE; public: RopeBuilder(JSContext *cx) - : cx(cx), res(cx->runtime->emptyString) + : cx(cx), res(cx, cx->runtime->emptyString) {} - inline bool append(JSString *str) { + inline bool append(HandleString str) { res = js_ConcatStrings(cx, res, str); return !!res; } inline JSString *result() { return res; } };
--- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -469,45 +469,45 @@ JSBool ArrayBuffer::obj_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp) { obj = getArrayBuffer(obj); if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) { vp->setInt32(obj->arrayBufferByteLength()); return true; } - JSObject *delegate = DelegateObject(cx, obj); + RootedVarObject delegate(cx, DelegateObject(cx, obj)); if (!delegate) return false; - return js_GetProperty(cx, delegate, receiver, id, vp); + return js_GetProperty(cx, delegate, RootedVarObject(cx, receiver), id, vp); } JSBool ArrayBuffer::obj_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, Value *vp) { obj = getArrayBuffer(obj); if (name == cx->runtime->atomState.byteLengthAtom) { vp->setInt32(obj->arrayBufferByteLength()); return true; } - JSObject *delegate = DelegateObject(cx, obj); + RootedVarObject delegate(cx, DelegateObject(cx, obj)); if (!delegate) return false; - return js_GetProperty(cx, delegate, receiver, ATOM_TO_JSID(name), vp); + return js_GetProperty(cx, delegate, RootedVarObject(cx, receiver), ATOM_TO_JSID(name), vp); } JSBool ArrayBuffer::obj_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp) { - JSObject *delegate = DelegateObject(cx, getArrayBuffer(obj)); + RootedVarObject delegate(cx, DelegateObject(cx, getArrayBuffer(obj))); if (!delegate) return false; - return js_GetElement(cx, delegate, receiver, index, vp); + return js_GetElement(cx, delegate, RootedVarObject(cx, receiver), index, vp); } JSBool ArrayBuffer::obj_getElementIfPresent(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp, bool *present) { JSObject *delegate = DelegateObject(cx, getArrayBuffer(obj)); if (!delegate) @@ -522,16 +522,20 @@ ArrayBuffer::obj_getSpecial(JSContext *c } JSBool ArrayBuffer::obj_setGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict) { if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) return true; + RootedVarObject delegate(cx, DelegateObject(cx, obj)); + if (!delegate) + return false; + if (JSID_IS_ATOM(id, cx->runtime->atomState.protoAtom)) { // setting __proto__ = null // effectively removes the prototype chain. // any attempt to set __proto__ on native // objects after setting them to null makes // __proto__ just a plain property. // the following code simulates this behaviour on arrays. // @@ -541,19 +545,16 @@ ArrayBuffer::obj_setGeneric(JSContext *c // of treating it as special or plain. // if the delegate's prototype has now changed // then we change our prototype too. // // otherwise __proto__ was a plain property // and we don't modify our prototype chain // since obj_getProperty will fetch it as a plain // property from the delegate. - JSObject *delegate = DelegateObject(cx, obj); - if (!delegate) - return false; JSObject *oldDelegateProto = delegate->getProto(); if (!js_SetPropertyHelper(cx, delegate, id, 0, vp, strict)) return false; if (delegate->getProto() != oldDelegateProto) { // actual __proto__ was set and not a plain property called @@ -567,33 +568,29 @@ ArrayBuffer::obj_setGeneric(JSContext *c // restore delegate prototype chain SetProto(cx, delegate, oldDelegateProto, true); return false; } } return true; } - JSObject *delegate = DelegateObject(cx, obj); - if (!delegate) - return false; - return js_SetPropertyHelper(cx, delegate, id, 0, vp, strict); } JSBool ArrayBuffer::obj_setProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict) { return obj_setGeneric(cx, obj, ATOM_TO_JSID(name), vp, strict); } JSBool ArrayBuffer::obj_setElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict) { - JSObject *delegate = DelegateObject(cx, obj); + RootedVarObject delegate(cx, DelegateObject(cx, obj)); if (!delegate) return false; return js_SetElementHelper(cx, delegate, index, 0, vp, strict); } JSBool ArrayBuffer::obj_setSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict)
--- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -260,17 +260,17 @@ Wrapper::keys(JSContext *cx, JSObject *w GET(GetPropertyNames(cx, wrappedObject(wrapper), JSITER_OWNONLY, &props)); } bool Wrapper::iterate(JSContext *cx, JSObject *wrapper, unsigned flags, Value *vp) { vp->setUndefined(); // default result if we refuse to perform this action const jsid id = JSID_VOID; - GET(GetIterator(cx, wrappedObject(wrapper), flags, vp)); + GET(GetIterator(cx, RootedVarObject(cx, wrappedObject(wrapper)), flags, vp)); } bool Wrapper::call(JSContext *cx, JSObject *wrapper, unsigned argc, Value *vp) { vp->setUndefined(); // default result if we refuse to perform this action const jsid id = JSID_VOID; CHECKED(ProxyHandler::call(cx, wrapper, argc, vp), CALL); @@ -361,17 +361,17 @@ Wrapper::defaultValue(JSContext *cx, JSO if (hint == JSTYPE_VOID) return ToPrimitive(cx, vp); return ToPrimitive(cx, hint, vp); } bool Wrapper::iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp) { - if (!js_IteratorMore(cx, wrappedObject(wrapper), vp)) + if (!js_IteratorMore(cx, RootedVarObject(cx, wrappedObject(wrapper)), vp)) return false; if (vp->toBoolean()) { *vp = cx->iterValue; cx->iterValue.setUndefined(); } else { vp->setMagic(JS_NO_ITER_VALUE); } @@ -687,18 +687,18 @@ static bool Reify(JSContext *cx, JSCompartment *origin, Value *vp) { JSObject *iterObj = &vp->toObject(); NativeIterator *ni = iterObj->getNativeIterator(); AutoCloseIterator close(cx, iterObj); /* Wrap the iteratee. */ - JSObject *obj = ni->obj; - if (!origin->wrap(cx, &obj)) + RootedVarObject obj(cx, ni->obj); + if (!origin->wrap(cx, obj.address())) return false; /* * Wrap the elements in the iterator's snapshot. * N.B. the order of closing/creating iterators is important due to the * implicit cx->enumerators state. */ size_t length = ni->numKeys();
--- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -151,17 +151,17 @@ const char js_amp_entity_str[] = "&am const char js_gt_entity_str[] = ">"; const char js_lt_entity_str[] = "<"; const char js_quot_entity_str[] = """; const char js_leftcurly_entity_str[] = "{"; #define IS_STAR(str) ((str)->length() == 1 && *(str)->chars() == '*') static JSBool -GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp); +GetXMLFunction(JSContext *cx, HandleObject obj, HandleId id, jsval *vp); static JSBool IsDeclared(const JSObject *obj) { jsval v; JS_ASSERT(obj->getClass() == &NamespaceClass); v = obj->getNamespaceDeclared(); @@ -400,31 +400,31 @@ static JSPropertySpec qname_props[] = { {js_localName_str, 0, QNAME_ATTRS, QNameLocalName_getter, 0}, {0,0,0,0,0} }; static JSString * ConvertQNameToString(JSContext *cx, JSObject *obj) { JS_ASSERT(obj->isQName()); - JSString *uri = obj->getNameURI(); - JSString *str; + RootedVarString uri(cx, obj->getNameURI()); + RootedVarString str(cx); if (!uri) { /* No uri means wildcard qualifier. */ str = cx->runtime->atomState.starQualifierAtom; } else if (uri->empty()) { /* Empty string for uri means localName is in no namespace. */ str = cx->runtime->emptyString; } else { - JSString *qualstr = cx->runtime->atomState.qualifierAtom; + RootedVarString qualstr(cx, cx->runtime->atomState.qualifierAtom); str = js_ConcatStrings(cx, uri, qualstr); if (!str) return NULL; } - str = js_ConcatStrings(cx, str, obj->getQNameLocalName()); + str = js_ConcatStrings(cx, str, RootedVarString(cx, obj->getQNameLocalName())); if (!str) return NULL; if (obj->getClass() == &AttributeNameClass) { JS::Anchor<JSString *> anchor(str); size_t length = str->length(); jschar *chars = (jschar *) cx->malloc_((length + 2) * sizeof(jschar)); if (!chars) @@ -3760,23 +3760,25 @@ GetNamedProperty(JSContext *cx, JSXML *x } } return JS_TRUE; } /* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */ static JSBool -GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +GetProperty(JSContext *cx, JSObject *obj_, jsid id_, jsval *vp) { JSXML *xml, *list, *kid; uint32_t index; JSObject *kidobj, *listobj; JSObject *nameqn; - jsid funid; + + RootedVarObject obj(cx, obj_); + RootedVarId id(cx, id_); if (!obj->isXML()) return true; xml = (JSXML *) obj->getPrivate(); if (!xml) return true; if (js_IdIsIndex(id, &index)) { @@ -3806,17 +3808,18 @@ GetProperty(JSContext *cx, JSObject *obj } } return true; } /* * ECMA-357 9.2.1.1/9.1.1.1 qname case. */ - nameqn = ToXMLName(cx, IdToJsval(id), &funid); + RootedVarId funid(cx); + nameqn = ToXMLName(cx, IdToJsval(id), funid.address()); if (!nameqn) return false; if (!JSID_IS_VOID(funid)) return GetXMLFunction(cx, obj, funid, vp); jsval roots[2] = { OBJECT_TO_JSVAL(nameqn), JSVAL_NULL }; AutoArrayRooter tvr(cx, ArrayLength(roots), roots); @@ -3875,28 +3878,31 @@ KidToString(JSContext *cx, JSXML *xml, u } /* Forward declared -- its implementation uses other statics that call it. */ static JSBool ResolveValue(JSContext *cx, JSXML *list, JSXML **result); /* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */ static JSBool -PutProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) +PutProperty(JSContext *cx, JSObject *obj_, jsid id_, JSBool strict, jsval *vp) { JSBool ok, primitiveAssign; enum { OBJ_ROOT, ID_ROOT, VAL_ROOT }; JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match; - JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj; + JSObject *vobj, *nameobj, *attrobj, *kidobj, *copyobj; JSObject *targetprop, *nameqn, *attrqn; uint32_t index, i, j, k, n, q, matchIndex; jsval attrval, nsval; jsid funid; JSObject *ns; + RootedVarObject obj(cx, obj_); + RootedVarId id(cx, id_); + if (!obj->isXML()) return JS_TRUE; xml = (JSXML *) obj->getPrivate(); if (!xml) return JS_TRUE; xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) @@ -4089,17 +4095,17 @@ PutProperty(JSContext *cx, JSObject *obj nameobj->getQNameLocalName()); if (!nameobj) goto bad; } id = OBJECT_TO_JSID(nameobj); if (parent) { /* 2(e)(i). */ - parentobj = js_GetXMLObject(cx, parent); + RootedVarObject parentobj(cx, js_GetXMLObject(cx, parent)); if (!parentobj) goto bad; ok = PutProperty(cx, parentobj, id, strict, vp); if (!ok) goto out; /* 2(e)(ii). */ ok = GetProperty(cx, parentobj, id, vp); @@ -4297,26 +4303,26 @@ PutProperty(JSContext *cx, JSObject *obj goto out; /* 7(b-c). */ if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { n = vxml->xml_kids.length; if (n == 0) { *vp = STRING_TO_JSVAL(cx->runtime->emptyString); } else { - JSString *left = KidToString(cx, vxml, 0); + RootedVarString left(cx, KidToString(cx, vxml, 0)); if (!left) goto bad; - JSString *space = cx->runtime->atomState.spaceAtom; + RootedVarString space(cx, cx->runtime->atomState.spaceAtom); for (i = 1; i < n; i++) { left = js_ConcatStrings(cx, left, space); if (!left) goto bad; - JSString *right = KidToString(cx, vxml, i); + RootedVarString right(cx, KidToString(cx, vxml, i)); if (!right) goto bad; left = js_ConcatStrings(cx, left, right); if (!left) goto bad; } roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left); @@ -4522,17 +4528,16 @@ bad: } /* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */ static JSBool ResolveValue(JSContext *cx, JSXML *list, JSXML **result) { JSXML *target, *base; JSObject *targetprop; - jsid id; jsval tv; if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) { if (!js_GetXMLObject(cx, list)) return JS_FALSE; *result = list; return JS_TRUE; } @@ -4553,17 +4558,17 @@ ResolveValue(JSContext *cx, JSXML *list, return JS_FALSE; if (!base) { *result = NULL; return JS_TRUE; } if (!js_GetXMLObject(cx, base)) return JS_FALSE; - id = OBJECT_TO_JSID(targetprop); + RootedVarId id(cx, OBJECT_TO_JSID(targetprop)); if (!GetProperty(cx, base->object, id, &tv)) return JS_FALSE; target = (JSXML *) JSVAL_TO_OBJECT(tv)->getPrivate(); if (JSXML_LENGTH(target) == 0) { if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) { *result = NULL; return JS_TRUE; @@ -5207,29 +5212,29 @@ again: return simple; } } /* * 11.2.2.1 Step 3(d) onward. */ JSBool -js_GetXMLMethod(JSContext *cx, JSObject *obj, jsid id, Value *vp) +js_GetXMLMethod(JSContext *cx, HandleObject obj, jsid id, Value *vp) { JS_ASSERT(obj->isXML()); if (JSID_IS_OBJECT(id)) js_GetLocalNameFromFunctionQName(JSID_TO_OBJECT(id), &id, cx); /* * As our callers have a bad habit of passing a pointer to an unrooted * local value as vp, we use a proper root here. */ AutoValueRooter tvr(cx); - JSBool ok = GetXMLFunction(cx, obj, id, tvr.addr()); + JSBool ok = GetXMLFunction(cx, obj, RootedVarId(cx, id), tvr.addr()); *vp = tvr.value(); return ok; } JSBool js_TestXMLEquality(JSContext *cx, const Value &v1, const Value &v2, JSBool *bp) { JSXML *xml, *vxml; @@ -6332,17 +6337,18 @@ xml_normalize_helper(JSContext *cx, JSOb if (kid->xml_class == JSXML_CLASS_ELEMENT) { kidobj = js_GetXMLObject(cx, kid); if (!kidobj || !xml_normalize_helper(cx, kidobj, kid)) return JS_FALSE; } else if (kid->xml_class == JSXML_CLASS_TEXT) { while (i + 1 < n && (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) && kid2->xml_class == JSXML_CLASS_TEXT) { - str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value); + str = js_ConcatStrings(cx, RootedVarString(cx, kid->xml_value), + RootedVarString(cx, kid2->xml_value)); if (!str) return JS_FALSE; NormalizingDelete(cx, xml, i + 1); n = xml->xml_kids.length; kid->xml_value = str; } if (kid->xml_value->empty()) { NormalizingDelete(cx, xml, i); @@ -6954,34 +6960,32 @@ xml_text(JSContext *cx, unsigned argc, j XML_METHOD_PROLOG; return xml_text_helper(cx, obj, xml, vp); } /* XML and XMLList */ static JSString * xml_toString_helper(JSContext *cx, JSXML *xml) { - JSString *str, *kidstr; - if (xml->xml_class == JSXML_CLASS_ATTRIBUTE || xml->xml_class == JSXML_CLASS_TEXT) { return xml->xml_value; } if (!HasSimpleContent(xml)) return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object), 0); - str = cx->runtime->emptyString; + RootedVarString str(cx, cx->runtime->emptyString); if (!js_EnterLocalRootScope(cx)) return NULL; JSXMLArrayCursor<JSXML> cursor(&xml->xml_kids); while (JSXML *kid = cursor.getNext()) { if (kid->xml_class != JSXML_CLASS_COMMENT && kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) { - kidstr = xml_toString_helper(cx, kid); + RootedVarString kidstr(cx, xml_toString_helper(cx, kid)); if (!kidstr) { str = NULL; break; } str = js_ConcatStrings(cx, str, kidstr); if (!str) break; } @@ -7790,25 +7794,25 @@ js_FindXMLProperty(JSContext *cx, const if (str && js_ValueToPrintable(cx, StringValue(str), &printable)) { JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, JSMSG_UNDEFINED_XML_NAME, printable.ptr()); } return JS_FALSE; } static JSBool -GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +GetXMLFunction(JSContext *cx, HandleObject obj, HandleId id, jsval *vp) { JS_ASSERT(obj->isXML()); /* * See comments before xml_lookupGeneric about the need for the proto * chain lookup. */ - JSObject *target = obj; + RootedVarObject target(cx, obj); for (;;) { if (!js_GetProperty(cx, target, id, vp)) return false; if (!JSVAL_IS_PRIMITIVE(*vp) && JSVAL_TO_OBJECT(*vp)->isFunction()) return true; target = target->getProto(); if (target == NULL || !target->isNative()) break;
--- a/js/src/jsxml.h +++ b/js/src/jsxml.h @@ -282,17 +282,17 @@ js_GetAnyName(JSContext *cx, jsid *idp); /* * Note: nameval must be either QName, AttributeName, or AnyName. */ extern JSBool js_FindXMLProperty(JSContext *cx, const js::Value &nameval, JSObject **objp, jsid *idp); extern JSBool -js_GetXMLMethod(JSContext *cx, JSObject *obj, jsid id, js::Value *vp); +js_GetXMLMethod(JSContext *cx, js::HandleObject obj, jsid id, js::Value *vp); extern JSBool js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp); extern JSBool js_DeleteXMLListElements(JSContext *cx, JSObject *listobj); extern JSBool
--- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -6530,17 +6530,17 @@ mjit::Compiler::emitEval(uint32_t argc) pushSyncedEntry(0); } bool mjit::Compiler::jsop_newinit() { bool isArray; unsigned count = 0; - JSObject *baseobj = NULL; + RootedVarObject baseobj(cx); switch (*PC) { case JSOP_NEWINIT: isArray = (GET_UINT8(PC) == JSProto_Array); break; case JSOP_NEWARRAY: isArray = true; count = GET_UINT24(PC); break; @@ -6566,17 +6566,17 @@ mjit::Compiler::jsop_newinit() stub = JS_FUNC_TO_DATA_PTR(void *, stubs::NewInitObject); stubArg = (void *) baseobj; } /* * Don't bake in types for non-compileAndGo scripts, or at initializers * producing objects with singleton types. */ - types::TypeObject *type = NULL; + RootedVarTypeObject type(cx); if (globalObj && !types::UseNewTypeForInitializer(cx, script, PC)) { type = types::TypeScript::InitObject(cx, script, PC, isArray ? JSProto_Array : JSProto_Object); if (!type) return false; } size_t maxArraySlots = @@ -7003,17 +7003,17 @@ mjit::Compiler::leaveBlock() // NULL // call js_CreateThisFromFunctionWithProto(...) // bool mjit::Compiler::constructThis() { JS_ASSERT(isConstructing); - JSFunction *fun = script->function(); + RootedVarFunction fun(cx, script->function()); do { if (!cx->typeInferenceEnabled() || !fun->hasSingletonType() || fun->getType(cx)->unknownProperties()) { break; }
--- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -636,17 +636,17 @@ js_InternalThrow(VMFrame &f) return NULL; } void JS_FASTCALL stubs::CreateThis(VMFrame &f, JSObject *proto) { JSContext *cx = f.cx; StackFrame *fp = f.fp(); - JSObject *callee = &fp->callee(); + RootedVarObject callee(cx, &fp->callee()); JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto); if (!obj) THROW(); fp->formalArgs()[-1].setObject(*obj); } void JS_FASTCALL stubs::ScriptDebugPrologue(VMFrame &f) @@ -898,17 +898,17 @@ js_InternalInterpret(void *returnData, v break; case REJOIN_DEFLOCALFUN: fp->slots()[GET_SLOTNO(pc)].setObject(* (JSObject *) returnReg); f.regs.pc = nextpc; break; case REJOIN_THIS_PROTOTYPE: { - JSObject *callee = &fp->callee(); + RootedVarObject callee(cx, &fp->callee()); JSObject *proto = f.regs.sp[0].isObject() ? &f.regs.sp[0].toObject() : NULL; JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto); if (!obj) return js_InternalThrow(f); fp->formalArgs()[-1].setObject(*obj); if (Probes::callTrackingActive(cx)) Probes::enterJSFun(f.cx, f.fp()->maybeFun(), f.fp()->script());
--- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -690,38 +690,37 @@ IsCacheableProtoChain(JSObject *obj, JSO return true; } template <typename IC> struct GetPropHelper { // These fields are set in the constructor and describe a property lookup. JSContext *cx; JSObject *obj; - PropertyName *name; + RootedVarPropertyName name; IC ⁣ VMFrame &f; // These fields are set by |bind| and |lookup|. After a call to either // function, these are set exactly as they are in JSOP_GETPROP or JSOP_NAME. - JSObject *aobj; JSObject *holder; JSProperty *prop; // This field is set by |bind| and |lookup| only if they returned // Lookup_Cacheable, otherwise it is NULL. const Shape *shape; GetPropHelper(JSContext *cx, JSObject *obj, PropertyName *name, IC &ic, VMFrame &f) - : cx(cx), obj(obj), name(name), ic(ic), f(f), holder(NULL), prop(NULL), shape(NULL) + : cx(cx), obj(obj), name(cx, name), ic(ic), f(f), holder(NULL), prop(NULL), shape(NULL) { } public: LookupStatus bind() { RecompilationMonitor monitor(cx); - JSObject *scopeChain = cx->stack.currentScriptedScopeChain(); + RootedVarObject scopeChain(cx, cx->stack.currentScriptedScopeChain()); if (js_CodeSpec[*f.pc()].format & JOF_GNAME) scopeChain = &scopeChain->global(); if (!FindProperty(cx, name, scopeChain, &obj, &holder, &prop)) return ic.error(cx); if (monitor.recompiled()) return Lookup_Uncacheable; if (!prop) return ic.disable(cx, "lookup failed"); @@ -729,17 +728,19 @@ struct GetPropHelper { return ic.disable(cx, "non-native"); if (!IsCacheableProtoChain(obj, holder)) return ic.disable(cx, "non-native holder"); shape = (const Shape *)prop; return Lookup_Cacheable; } LookupStatus lookup() { - JSObject *aobj = js_GetProtoIfDenseArray(obj); + JSObject *aobj = obj; + if (obj->isDenseArray()) + aobj = obj->getProto(); if (!aobj->isNative()) return ic.disable(f, "non-native"); RecompilationMonitor monitor(cx); if (!aobj->lookupProperty(cx, name, &holder, &prop)) return ic.error(cx); if (monitor.recompiled()) return Lookup_Uncacheable; @@ -2375,18 +2376,17 @@ GetElementIC::attachTypedArray(VMFrame & #endif /* JS_METHODJIT_TYPED_ARRAY */ LookupStatus GetElementIC::update(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp) { /* * Only treat this as a GETPROP for non-numeric string identifiers. The * GETPROP IC assumes the id has already gone through filtering for string - * indexes in the emitter, i.e. js_GetProtoIfDenseArray is only valid to - * use when looking up non-integer identifiers. + * indexes in the emitter. */ uint32_t dummy; if (v.isString() && JSID_IS_ATOM(id) && !JSID_TO_ATOM(id)->isIndex(&dummy)) return attachGetProp(f, obj, v, JSID_TO_ATOM(id)->asPropertyName(), vp); #if defined JS_METHODJIT_TYPED_ARRAY /* * Typed array ICs can make stub calls, and need to know which registers @@ -2416,17 +2416,17 @@ ic::GetElement(VMFrame &f, ic::GetElemen stubs::GetElem(f); return; } Value idval = f.regs.sp[-1]; RecompilationMonitor monitor(cx); - JSObject *obj = ValueToObject(cx, f.regs.sp[-2]); + RootedVarObject obj(cx, ValueToObject(cx, f.regs.sp[-2])); if (!obj) THROW(); #if JS_HAS_XML_SUPPORT // Some XML properties behave differently when accessed in a call vs. normal // context, so we fall back to stubs::GetElem. if (obj->isXML()) { ic->disable(f, "XML object");
--- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -213,21 +213,24 @@ stubs::ToId(VMFrame &f) if (!FetchElementId(f.cx, obj, idval, id, &idval)) THROW(); if (!idval.isInt32()) TypeScript::MonitorUnknown(f.cx, f.script(), f.pc()); } void JS_FASTCALL -stubs::ImplicitThis(VMFrame &f, PropertyName *name) +stubs::ImplicitThis(VMFrame &f, PropertyName *name_) { + RootedVarObject scopeObj(f.cx, f.cx->stack.currentScriptedScopeChain()); + RootedVarPropertyName name(f.cx, name_); + JSObject *obj, *obj2; JSProperty *prop; - if (!FindPropertyHelper(f.cx, name, false, f.cx->stack.currentScriptedScopeChain(), &obj, &obj2, &prop)) + if (!FindPropertyHelper(f.cx, name, false, scopeObj, &obj, &obj2, &prop)) THROW(); if (!ComputeImplicitThis(f.cx, obj, &f.regs.sp[0])) THROW(); } void JS_FASTCALL stubs::BitOr(VMFrame &f) @@ -313,31 +316,32 @@ stubs::Ursh(VMFrame &f) u >>= (j & 31); if (!f.regs.sp[-2].setNumber(uint32_t(u))) TypeScript::MonitorOverflow(f.cx, f.script(), f.pc()); } template<JSBool strict> void JS_FASTCALL -stubs::DefFun(VMFrame &f, JSFunction *fun) +stubs::DefFun(VMFrame &f, JSFunction *fun_) { - JSObject *obj2; + RootedVarFunction fun(f.cx, fun_); JSContext *cx = f.cx; StackFrame *fp = f.fp(); /* * A top-level function defined in Global or Eval code (see ECMA-262 * Ed. 3), or else a SpiderMonkey extension: a named function statement in * a compound statement (not at the top statement level of global code, or * at the top level of a function body). */ JSObject *obj = fun; + RootedVarObject obj2(f.cx); if (fun->isNullClosure()) { /* * Even a null closure needs a parent for principals finding. * FIXME: bug 476950, although debugger users may also demand some kind * of scope link for debugger-assisted eval-in-frame. */ obj2 = &fp->scopeChain(); } else { @@ -617,17 +621,17 @@ stubs::Add(VMFrame &f) JSContext *cx = f.cx; FrameRegs ®s = f.regs; Value rval = regs.sp[-1]; Value lval = regs.sp[-2]; /* The string + string case is easily the hottest; try it first. */ bool lIsString = lval.isString(); bool rIsString = rval.isString(); - JSString *lstr, *rstr; + RootedVarString lstr(cx), rstr(cx); if (lIsString && rIsString) { lstr = lval.toString(); rstr = rval.toString(); goto string_concat; } else #if JS_HAS_XML_SUPPORT if (lval.isObject() && lval.toObject().isXML() && @@ -921,17 +925,17 @@ void JS_FASTCALL stubs::NewInitObject(VMFrame &f, JSObject *baseobj) { JSContext *cx = f.cx; TypeObject *type = (TypeObject *) f.scratch; JSObject *obj; if (baseobj) { - obj = CopyInitializerObject(cx, baseobj); + obj = CopyInitializerObject(cx, RootedVarObject(cx, baseobj)); } else { gc::AllocKind kind = GuessObjectGCKind(0); obj = NewBuiltinClassInstance(cx, &ObjectClass, kind); } if (!obj) THROW(); @@ -996,19 +1000,21 @@ stubs::RegExp(VMFrame &f, JSObject *rege JS_ASSERT(proto); JSObject *obj = CloneRegExpObject(f.cx, regex, proto); if (!obj) THROW(); f.regs.sp[0].setObject(*obj); } JSObject * JS_FASTCALL -stubs::Lambda(VMFrame &f, JSFunction *fun) +stubs::Lambda(VMFrame &f, JSFunction *fun_) { - JSObject *parent; + RootedVarFunction fun(f.cx, fun_); + + RootedVarObject parent(f.cx); if (fun->isNullClosure()) { parent = &f.fp()->scopeChain(); } else { parent = GetScopeChain(f.cx, f.fp()); if (!parent) THROWV(NULL); } @@ -1069,17 +1075,17 @@ InitPropOrMethod(VMFrame &f, PropertyNam FrameRegs ®s = f.regs; /* Load the property's initial value into rval. */ JS_ASSERT(regs.sp - f.fp()->base() >= 2); Value rval; rval = regs.sp[-1]; /* Load the object being initialized into lval/obj. */ - JSObject *obj = ®s.sp[-2].toObject(); + RootedVarObject obj(cx, ®s.sp[-2].toObject()); JS_ASSERT(obj->isNative()); /* Get the immediate property name into id. */ jsid id = ATOM_TO_JSID(name); if (JS_UNLIKELY(name == cx->runtime->atomState.protoAtom) ? !js_SetPropertyHelper(cx, obj, id, 0, &rval, false) : !DefineNativeProperty(cx, obj, id, rval, NULL, NULL, @@ -1110,17 +1116,17 @@ stubs::IterNext(VMFrame &f, int32_t offs JSBool JS_FASTCALL stubs::IterMore(VMFrame &f) { JS_ASSERT(f.regs.sp - 1 >= f.fp()->base()); JS_ASSERT(f.regs.sp[-1].isObject()); Value v; JSObject *iterobj = &f.regs.sp[-1].toObject(); - if (!js_IteratorMore(f.cx, iterobj, &v)) + if (!js_IteratorMore(f.cx, RootedVarObject(f.cx, iterobj), &v)) THROWV(JS_FALSE); return v.toBoolean(); } void JS_FASTCALL stubs::EndIter(VMFrame &f) { @@ -1413,21 +1419,24 @@ stubs::Pos(VMFrame &f) { if (!ToNumber(f.cx, &f.regs.sp[-1])) THROW(); if (!f.regs.sp[-1].isInt32()) TypeScript::MonitorOverflow(f.cx, f.script(), f.pc()); } void JS_FASTCALL -stubs::DelName(VMFrame &f, PropertyName *name) +stubs::DelName(VMFrame &f, PropertyName *name_) { + RootedVarObject scopeObj(f.cx, f.cx->stack.currentScriptedScopeChain()); + RootedVarPropertyName name(f.cx, name_); + JSObject *obj, *obj2; JSProperty *prop; - if (!FindProperty(f.cx, name, f.cx->stack.currentScriptedScopeChain(), &obj, &obj2, &prop)) + if (!FindProperty(f.cx, name, scopeObj, &obj, &obj2, &prop)) THROW(); /* Strict mode code should never contain JSOP_DELNAME opcodes. */ JS_ASSERT(!f.script()->strictModeCode); /* ECMA says to return true if name is undefined or inherited. */ f.regs.sp++; f.regs.sp[-1] = BooleanValue(true); @@ -1480,17 +1489,17 @@ stubs::DefVarOrConst(VMFrame &f, Propert unsigned attrs = JSPROP_ENUMERATE; if (!f.fp()->isEvalFrame()) attrs |= JSPROP_PERMANENT; if (JSOp(*f.regs.pc) == JSOP_DEFCONST) attrs |= JSPROP_READONLY; JSObject &obj = f.fp()->varObj(); - if (!DefVarOrConstOperation(f.cx, obj, dn, attrs)) + if (!DefVarOrConstOperation(f.cx, RootedVarObject(f.cx, &obj), dn, attrs)) THROW(); } void JS_FASTCALL stubs::SetConst(VMFrame &f, PropertyName *name) { JSContext *cx = f.cx;
--- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -2737,24 +2737,26 @@ ShapeOf(JSContext *cx, unsigned argc, js } /* * If referent has an own property named id, copy that property to obj[id]. * Since obj is native, this isn't totally transparent; properties of a * non-native referent may be simplified to data properties. */ static JSBool -CopyProperty(JSContext *cx, JSObject *obj, JSObject *referent, jsid id, +CopyProperty(JSContext *cx, JSObject *obj_, JSObject *referent, jsid id, unsigned lookupFlags, JSObject **objp) { JSProperty *prop; PropertyDescriptor desc; unsigned propFlags = 0; JSObject *obj2; + RootedVarObject obj(cx, obj_); + *objp = NULL; if (referent->isNative()) { if (!LookupPropertyWithFlags(cx, referent, id, lookupFlags, &obj2, &prop)) return false; if (obj2 != referent) return true; const Shape *shape = (Shape *) prop; @@ -4296,19 +4298,21 @@ global_enumerate(JSContext *cx, JSObject #ifdef LAZY_STANDARD_CLASSES return JS_EnumerateStandardClasses(cx, obj); #else return JS_TRUE; #endif } static JSBool -global_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, +global_resolve(JSContext *cx, JSObject *obj_, jsid id, unsigned flags, JSObject **objp) { + RootedVarObject obj(cx, obj_); + #ifdef LAZY_STANDARD_CLASSES JSBool resolved; if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) return JS_FALSE; if (resolved) { *objp = obj; return JS_TRUE;
--- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -81,31 +81,31 @@ js_PutArgsObject(StackFrame *fp) fp->forEachCanonicalActualArg(PutArg(comp, argsobj)); argsobj.setStackFrame(NULL); } else { JS_ASSERT(!argsobj.maybeStackFrame()); } } ArgumentsObject * -ArgumentsObject::create(JSContext *cx, uint32_t argc, JSObject &callee) +ArgumentsObject::create(JSContext *cx, uint32_t argc, HandleObject callee) { JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX); - JSObject *proto = callee.global().getOrCreateObjectPrototype(cx); + RootedVarObject proto(cx, callee->global().getOrCreateObjectPrototype(cx)); if (!proto) return NULL; RootedVarTypeObject type(cx); type = proto->getNewType(cx); if (!type) return NULL; - bool strict = callee.toFunction()->inStrictMode(); + bool strict = callee->toFunction()->inStrictMode(); Class *clasp = strict ? &StrictArgumentsObjectClass : &NormalArgumentsObjectClass; RootedVarShape emptyArgumentsShape(cx); emptyArgumentsShape = EmptyShape::getInitialShape(cx, clasp, proto, proto->getParent(), FINALIZE_KIND, BaseShape::INDEXED); if (!emptyArgumentsShape) @@ -115,17 +115,17 @@ ArgumentsObject::create(JSContext *cx, u unsigned numBytes = offsetof(ArgumentsData, slots) + numDeletedWords * sizeof(size_t) + argc * sizeof(Value); ArgumentsData *data = (ArgumentsData *)cx->malloc_(numBytes); if (!data) return NULL; - data->callee.init(ObjectValue(callee)); + data->callee.init(ObjectValue(*callee)); for (HeapValue *vp = data->slots; vp != data->slots + argc; vp++) vp->init(UndefinedValue()); data->deletedBits = (size_t *)(data->slots + argc); ClearAllBitArrayElements(data->deletedBits, numDeletedWords); /* We have everything needed to fill in the object, so make the object. */ JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyArgumentsShape, type, NULL); if (!obj) @@ -144,17 +144,18 @@ ArgumentsObject::create(JSContext *cx, u return &argsobj; } ArgumentsObject * ArgumentsObject::create(JSContext *cx, StackFrame *fp) { JS_ASSERT(fp->script()->needsArgsObj()); - ArgumentsObject *argsobj = ArgumentsObject::create(cx, fp->numActualArgs(), fp->callee()); + ArgumentsObject *argsobj = ArgumentsObject::create(cx, fp->numActualArgs(), + RootedVarObject(cx, &fp->callee())); if (!argsobj) return NULL; /* * Strict mode functions have arguments objects that copy the initial * actual parameter values. Non-strict mode arguments use the frame pointer * to retrieve up-to-date parameter values. */ @@ -165,17 +166,17 @@ ArgumentsObject::create(JSContext *cx, S fp->initArgsObj(*argsobj); return argsobj; } ArgumentsObject * ArgumentsObject::createUnexpected(JSContext *cx, StackFrame *fp) { - ArgumentsObject *argsobj = create(cx, fp->numActualArgs(), fp->callee()); + ArgumentsObject *argsobj = create(cx, fp->numActualArgs(), RootedVarObject(cx, &fp->callee())); if (!argsobj) return NULL; fp->forEachCanonicalActualArg(PutArg(cx->compartment, *argsobj)); return argsobj; } static JSBool @@ -270,41 +271,41 @@ ArgSetter(JSContext *cx, JSObject *obj, } static JSBool args_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSObject **objp) { *objp = NULL; - NormalArgumentsObject &argsobj = obj->asNormalArguments(); + RootedVar<NormalArgumentsObject*> argsobj(cx, &obj->asNormalArguments()); unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE; if (JSID_IS_INT(id)) { uint32_t arg = uint32_t(JSID_TO_INT(id)); - if (arg >= argsobj.initialLength() || argsobj.isElementDeleted(arg)) + if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg)) return true; attrs |= JSPROP_ENUMERATE; } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { - if (argsobj.hasOverriddenLength()) + if (argsobj->hasOverriddenLength()) return true; } else { if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) return true; - if (argsobj.callee().isMagic(JS_OVERWRITTEN_CALLEE)) + if (argsobj->callee().isMagic(JS_OVERWRITTEN_CALLEE)) return true; } Value undef = UndefinedValue(); - if (!js_DefineProperty(cx, &argsobj, id, &undef, ArgGetter, ArgSetter, attrs)) + if (!js_DefineProperty(cx, argsobj, id, &undef, ArgGetter, ArgSetter, attrs)) return JS_FALSE; - *objp = &argsobj; + *objp = argsobj; return true; } bool NormalArgumentsObject::optimizedGetElem(JSContext *cx, StackFrame *fp, const Value &elem, Value *vp) { JS_ASSERT(!fp->hasArgsObj()); @@ -399,37 +400,38 @@ StrictArgGetter(JSContext *cx, JSObject } static JSBool StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) { if (!obj->isStrictArguments()) return true; - StrictArgumentsObject &argsobj = obj->asStrictArguments(); + RootedVar<StrictArgumentsObject*> argsobj(cx, &obj->asStrictArguments()); + RootId idRoot(cx, &id); if (JSID_IS_INT(id)) { unsigned arg = unsigned(JSID_TO_INT(id)); - if (arg < argsobj.initialLength()) { - argsobj.setElement(arg, *vp); + if (arg < argsobj->initialLength()) { + argsobj->setElement(arg, *vp); return true; } } else { JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)); } /* * For simplicity we use delete/set to replace the property with one * backed by the default Object getter and setter. Note that we rely on * args_delProperty to clear the corresponding reserved slot so the GC can * collect its value. */ AutoValueRooter tvr(cx); - return js_DeleteGeneric(cx, &argsobj, id, tvr.addr(), strict) && - js_SetPropertyHelper(cx, &argsobj, id, 0, vp, strict); + return js_DeleteGeneric(cx, argsobj, id, tvr.addr(), strict) && + js_SetPropertyHelper(cx, argsobj, id, 0, vp, strict); } static JSBool strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSObject **objp) { *objp = NULL; StrictArgumentsObject &argsobj = obj->asStrictArguments();
--- a/js/src/vm/ArgumentsObject.h +++ b/js/src/vm/ArgumentsObject.h @@ -133,17 +133,17 @@ class ArgumentsObject : public JSObject static const uint32_t STACK_FRAME_SLOT = 2; /* Lower-order bit stolen from the length slot. */ static const uint32_t LENGTH_OVERRIDDEN_BIT = 0x1; static const uint32_t PACKED_BITS_COUNT = 1; void initInitialLength(uint32_t length); void initData(ArgumentsData *data); - static ArgumentsObject *create(JSContext *cx, uint32_t argc, JSObject &callee); + static ArgumentsObject *create(JSContext *cx, uint32_t argc, HandleObject callee); public: static const uint32_t RESERVED_SLOTS = 3; static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4; /* Create an arguments object for a frame that is expecting them. */ static ArgumentsObject *create(JSContext *cx, StackFrame *fp);
--- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -823,17 +823,17 @@ Debugger::newCompletionValue(JSContext * result->setNull(); return true; default: JS_NOT_REACHED("bad status passed to Debugger::newCompletionValue"); } /* Common tail for JSTRAP_RETURN and JSTRAP_THROW. */ - JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass); + RootedVarObject obj(cx, NewBuiltinClassInstance(cx, &ObjectClass)); if (!obj || !wrapDebuggeeValue(cx, &value) || !DefineNativeProperty(cx, obj, key, value, JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0)) { return false; } @@ -899,17 +899,17 @@ Debugger::parseResumptionValue(AutoCompa if (!cx->compartment->wrap(cx, vp)) { vp->setUndefined(); return JSTRAP_ERROR; } return shape->propid() == returnId ? JSTRAP_RETURN : JSTRAP_THROW; } bool -CallMethodIfPresent(JSContext *cx, JSObject *obj, const char *name, int argc, Value *argv, +CallMethodIfPresent(JSContext *cx, HandleObject obj, const char *name, int argc, Value *argv, Value *rval) { rval->setUndefined(); JSAtom *atom = js_Atomize(cx, name, strlen(name)); Value fval; return atom && js_GetMethod(cx, obj, ATOM_TO_JSID(atom), 0, &fval) && (!js_IsCallable(fval) || @@ -1137,17 +1137,17 @@ Debugger::onTrap(JSContext *cx, Value *v AutoCompartment ac(cx, dbg->object); if (!ac.enter()) return JSTRAP_ERROR; Value argv[1]; if (!dbg->getScriptFrame(cx, fp, &argv[0])) return dbg->handleUncaughtException(ac, vp, false); Value rv; - bool ok = CallMethodIfPresent(cx, bp->handler, "hit", 1, argv, &rv); + bool ok = CallMethodIfPresent(cx, RootedVarObject(cx, bp->handler), "hit", 1, argv, &rv); JSTrapStatus st = dbg->parseResumptionValue(ac, ok, rv, vp, true); if (st != JSTRAP_CONTINUE) return st; /* Calling JS code invalidates site. Reload it. */ site = script->getBreakpointSite(pc); } } @@ -3167,17 +3167,17 @@ DebuggerFrame_getArguments(JSContext *cx THIS_FRAME(cx, argc, vp, "get arguments", args, thisobj, fp); Value argumentsv = thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS); if (!argumentsv.isUndefined()) { JS_ASSERT(argumentsv.isObjectOrNull()); args.rval() = argumentsv; return true; } - JSObject *argsobj; + RootedVarObject argsobj(cx); if (fp->hasArgs()) { /* Create an arguments object. */ RootedVar<GlobalObject*> global(cx); global = &args.callee().global(); JSObject *proto = global->getOrCreateArrayPrototype(cx); if (!proto) return false; argsobj = NewObjectWithGivenProto(cx, &DebuggerArguments_class, proto, global); @@ -3432,17 +3432,17 @@ DebuggerFrameEval(JSContext *cx, unsigne } } } AutoCompartment ac(cx, &fp->scopeChain()); if (!ac.enter()) return false; - Env *env = JS_GetFrameScopeChain(cx, Jsvalify(fp)); + RootedVar<Env*> env(cx, JS_GetFrameScopeChain(cx, Jsvalify(fp))); if (!env) return false; /* If evalWithBindings, create the inner environment. */ if (mode == WithBindings) { /* TODO - This should probably be a Call object, like ES5 strict eval. */ env = NewObjectWithGivenProto(cx, &ObjectClass, NULL, env); if (!env) @@ -3559,25 +3559,25 @@ DebuggerObject_checkThis(JSContext *cx, "Debugger.Object", fnname, "prototype object"); return NULL; } return thisobj; } #define THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, fnname, args, obj) \ CallArgs args = CallArgsFromVp(argc, vp); \ - JSObject *obj = DebuggerObject_checkThis(cx, args, fnname); \ + RootedVarObject obj(cx, DebuggerObject_checkThis(cx, args, fnname)); \ if (!obj) \ return false; \ obj = (JSObject *) obj->getPrivate(); \ JS_ASSERT(obj) #define THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, fnname, args, dbg, obj) \ CallArgs args = CallArgsFromVp(argc, vp); \ - JSObject *obj = DebuggerObject_checkThis(cx, args, fnname); \ + RootedVarObject obj(cx, DebuggerObject_checkThis(cx, args, fnname)); \ if (!obj) \ return false; \ Debugger *dbg = Debugger::fromChildJSObject(obj); \ obj = (JSObject *) obj->getPrivate(); \ JS_ASSERT(obj) static JSBool DebuggerObject_construct(JSContext *cx, unsigned argc, Value *vp) @@ -3715,25 +3715,25 @@ DebuggerObject_getEnvironment(JSContext return dbg->wrapEnvironment(cx, env, &args.rval()); } static JSBool DebuggerObject_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp) { THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "getOwnPropertyDescriptor", args, dbg, obj); - jsid id; - if (!ValueToId(cx, argc >= 1 ? args[0] : UndefinedValue(), &id)) + RootedVarId id(cx); + if (!ValueToId(cx, argc >= 1 ? args[0] : UndefinedValue(), id.address())) return false; /* Bug: This can cause the debuggee to run! */ AutoPropertyDescriptorRooter desc(cx); { AutoCompartment ac(cx, obj); - if (!ac.enter() || !cx->compartment->wrapId(cx, &id)) + if (!ac.enter() || !cx->compartment->wrapId(cx, id.address())) return false; ErrorCopier ec(ac, dbg->toJSObject()); if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) return false; } if (desc.obj) { @@ -3849,33 +3849,33 @@ WrapIdAndPropDesc(JSContext *cx, JSObjec } static JSBool DebuggerObject_defineProperty(JSContext *cx, unsigned argc, Value *vp) { THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "defineProperty", args, dbg, obj); REQUIRE_ARGC("Debugger.Object.defineProperty", 2); - jsid id; - if (!ValueToId(cx, args[0], &id)) + RootedVarId id(cx); + if (!ValueToId(cx, args[0], id.address())) return JS_FALSE; const Value &descval = args[1]; AutoPropDescArrayRooter descs(cx); PropDesc *desc = descs.append(); if (!desc || !desc->initialize(cx, descval, false)) return false; desc->pd.setUndefined(); if (!UnwrapPropDesc(cx, dbg, obj, desc)) return false; { AutoCompartment ac(cx, obj); - if (!ac.enter() || !WrapIdAndPropDesc(cx, obj, &id, desc)) + if (!ac.enter() || !WrapIdAndPropDesc(cx, obj, id.address(), desc)) return false; ErrorCopier ec(ac, dbg->toJSObject()); bool dummy; if (!DefineProperty(cx, obj, id, *desc, true, &dummy)) return false; } @@ -3910,17 +3910,17 @@ DebuggerObject_defineProperties(JSContex for (size_t i = 0; i < n; i++) { if (!WrapIdAndPropDesc(cx, obj, &ids[i], &descs[i])) return false; } ErrorCopier ec(ac, dbg->toJSObject()); for (size_t i = 0; i < n; i++) { bool dummy; - if (!DefineProperty(cx, obj, ids[i], descs[i], true, &dummy)) + if (!DefineProperty(cx, obj, RootedVarId(cx, ids[i]), descs[i], true, &dummy)) return false; } } args.rval().setUndefined(); return true; }
--- a/js/src/vm/ObjectImpl.cpp +++ b/js/src/vm/ObjectImpl.cpp @@ -137,16 +137,25 @@ MOZ_NEVER_INLINE const Shape * js::ObjectImpl::nativeLookup(JSContext *cx, jsid id) { MOZ_ASSERT(isNative()); Shape **spp; return Shape::search(cx, lastProperty(), id, &spp); } +#ifdef DEBUG +const Shape * +js::ObjectImpl::nativeLookupNoAllocation(JSContext *cx, jsid id) +{ + MOZ_ASSERT(isNative()); + return Shape::searchNoAllocation(cx, lastProperty(), id); +} +#endif + void js::ObjectImpl::markChildren(JSTracer *trc) { MarkTypeObject(trc, &type_, "type"); MarkShape(trc, &shape_, "shape"); Class *clasp = shape_->getObjectClass();
--- a/js/src/vm/ObjectImpl.h +++ b/js/src/vm/ObjectImpl.h @@ -742,16 +742,20 @@ class ObjectImpl : public gc::Cell inline uint32_t slotSpan() const; /* Compute dynamicSlotsCount() for this object. */ inline uint32_t numDynamicSlots() const; const Shape * nativeLookup(JSContext *cx, jsid id); +#ifdef DEBUG + const Shape * nativeLookupNoAllocation(JSContext *cx, jsid id); +#endif + inline Class *getClass() const; inline JSClass *getJSClass() const; inline bool hasClass(const Class *c) const; inline const ObjectOps *getOps() const; /* * An object is a delegate if it is on another object's prototype or scope * chain, and therefore the delegate might be asked implicitly to get or
--- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -55,17 +55,17 @@ using js::detail::RegExpCode; JS_STATIC_ASSERT(IgnoreCaseFlag == JSREG_FOLD); JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB); JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE); JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY); /* RegExpObjectBuilder */ RegExpObjectBuilder::RegExpObjectBuilder(JSContext *cx, RegExpObject *reobj) - : cx(cx), reobj_(reobj) + : cx(cx), reobj_(cx, reobj) {} bool RegExpObjectBuilder::getOrCreate() { if (reobj_) return true; @@ -88,61 +88,61 @@ RegExpObjectBuilder::getOrCreateClone(Re return false; clone->initPrivate(NULL); reobj_ = &clone->asRegExp(); return true; } RegExpObject * -RegExpObjectBuilder::build(JSAtom *source, RegExpShared &shared) +RegExpObjectBuilder::build(HandleAtom source, RegExpShared &shared) { if (!getOrCreate()) return NULL; if (!reobj_->init(cx, source, shared.getFlags())) return NULL; reobj_->setShared(cx, shared); return reobj_; } RegExpObject * -RegExpObjectBuilder::build(JSAtom *source, RegExpFlag flags) +RegExpObjectBuilder::build(HandleAtom source, RegExpFlag flags) { if (!getOrCreate()) return NULL; - return reobj_->init(cx, source, flags) ? reobj_ : NULL; + return reobj_->init(cx, source, flags) ? reobj_.raw() : NULL; } RegExpObject * -RegExpObjectBuilder::clone(RegExpObject *other, RegExpObject *proto) +RegExpObjectBuilder::clone(Handle<RegExpObject *> other, Handle<RegExpObject *> proto) { if (!getOrCreateClone(proto)) return NULL; /* * Check that the RegExpShared for the original is okay to use in * the clone -- if the |RegExpStatics| provides more flags we'll * need a different |RegExpShared|. */ RegExpStatics *res = cx->regExpStatics(); RegExpFlag origFlags = other->getFlags(); RegExpFlag staticsFlags = res->getFlags(); if ((origFlags & staticsFlags) != staticsFlags) { RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags); - return build(other->getSource(), newFlags); + return build(RootedVar<JSLinearString*>(cx, other->getSource()), newFlags); } RegExpGuard g; if (!other->getShared(cx, &g)) return NULL; - return build(other->getSource(), *g); + return build(RootedVarAtom(cx, other->getSource()), *g); } /* MatchPairs */ MatchPairs * MatchPairs::create(LifoAlloc &alloc, size_t pairCount, size_t backingPairCount) { void *mem = alloc.alloc(calculateSize(backingPairCount)); @@ -368,25 +368,25 @@ RegExpObject::create(JSContext *cx, RegE RegExpFlag staticsFlags = res->getFlags(); return createNoStatics(cx, chars, length, RegExpFlag(flags | staticsFlags), tokenStream); } RegExpObject * RegExpObject::createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags, TokenStream *tokenStream) { - JSAtom *source = js_AtomizeChars(cx, chars, length); + RootedVarAtom source(cx, js_AtomizeChars(cx, chars, length)); if (!source) return NULL; return createNoStatics(cx, source, flags, tokenStream); } RegExpObject * -RegExpObject::createNoStatics(JSContext *cx, JSAtom *source, RegExpFlag flags, +RegExpObject::createNoStatics(JSContext *cx, HandleAtom source, RegExpFlag flags, TokenStream *tokenStream) { if (!RegExpCode::checkSyntax(cx, tokenStream, source)) return NULL; RegExpObjectBuilder builder(cx); return builder.build(source, flags); } @@ -410,78 +410,86 @@ RegExpObject::assignInitialShape(JSConte JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0); JS_STATIC_ASSERT(SOURCE_SLOT == LAST_INDEX_SLOT + 1); JS_STATIC_ASSERT(GLOBAL_FLAG_SLOT == SOURCE_SLOT + 1); JS_STATIC_ASSERT(IGNORE_CASE_FLAG_SLOT == GLOBAL_FLAG_SLOT + 1); JS_STATIC_ASSERT(MULTILINE_FLAG_SLOT == IGNORE_CASE_FLAG_SLOT + 1); JS_STATIC_ASSERT(STICKY_FLAG_SLOT == MULTILINE_FLAG_SLOT + 1); + RootedVarObject self(cx, this); + /* The lastIndex property alone is writable but non-configurable. */ if (!addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lastIndexAtom), LAST_INDEX_SLOT, JSPROP_PERMANENT)) { return NULL; } /* Remaining instance properties are non-writable and non-configurable. */ - if (!addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.sourceAtom), - SOURCE_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) || - !addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.globalAtom), - GLOBAL_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) || - !addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.ignoreCaseAtom), - IGNORE_CASE_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) || - !addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.multilineAtom), - MULTILINE_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY)) + if (!self->addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.sourceAtom), + SOURCE_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) || + !self->addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.globalAtom), + GLOBAL_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) || + !self->addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.ignoreCaseAtom), + IGNORE_CASE_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) || + !self->addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.multilineAtom), + MULTILINE_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY)) { return NULL; } - return addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.stickyAtom), - STICKY_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY); + return self->addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.stickyAtom), + STICKY_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY); } inline bool -RegExpObject::init(JSContext *cx, JSAtom *source, RegExpFlag flags) +RegExpObject::init(JSContext *cx, HandleAtom source, RegExpFlag flags) { + RootedVar<RegExpObject *> self(cx, this); + if (nativeEmpty()) { if (isDelegate()) { if (!assignInitialShape(cx)) return false; } else { Shape *shape = assignInitialShape(cx); if (!shape) return false; - EmptyShape::insertInitialShape(cx, shape, getProto()); + EmptyShape::insertInitialShape(cx, shape, self->getProto()); } - JS_ASSERT(!nativeEmpty()); + JS_ASSERT(!self->nativeEmpty()); } DebugOnly<JSAtomState *> atomState = &cx->runtime->atomState; - JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->lastIndexAtom))->slot() == LAST_INDEX_SLOT); - JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->sourceAtom))->slot() == SOURCE_SLOT); - JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->globalAtom))->slot() == GLOBAL_FLAG_SLOT); - JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->ignoreCaseAtom))->slot() == - IGNORE_CASE_FLAG_SLOT); - JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->multilineAtom))->slot() == - MULTILINE_FLAG_SLOT); - JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->stickyAtom))->slot() == STICKY_FLAG_SLOT); + JS_ASSERT(self->nativeLookupNoAllocation(cx, ATOM_TO_JSID(atomState->lastIndexAtom))->slot() == + LAST_INDEX_SLOT); + JS_ASSERT(self->nativeLookupNoAllocation(cx, ATOM_TO_JSID(atomState->sourceAtom))->slot() == + SOURCE_SLOT); + JS_ASSERT(self->nativeLookupNoAllocation(cx, ATOM_TO_JSID(atomState->globalAtom))->slot() == + GLOBAL_FLAG_SLOT); + JS_ASSERT(self->nativeLookupNoAllocation(cx, ATOM_TO_JSID(atomState->ignoreCaseAtom))->slot() == + IGNORE_CASE_FLAG_SLOT); + JS_ASSERT(self->nativeLookupNoAllocation(cx, ATOM_TO_JSID(atomState->multilineAtom))->slot() == + MULTILINE_FLAG_SLOT); + JS_ASSERT(self->nativeLookupNoAllocation(cx, ATOM_TO_JSID(atomState->stickyAtom))->slot() == + STICKY_FLAG_SLOT); /* * If this is a re-initialization with an existing RegExpShared, 'flags' * may not match getShared()->flags, so forget the RegExpShared. */ - JSObject::setPrivate(NULL); + self->JSObject::setPrivate(NULL); - zeroLastIndex(); - setSource(source); - setGlobal(flags & GlobalFlag); - setIgnoreCase(flags & IgnoreCaseFlag); - setMultiline(flags & MultilineFlag); - setSticky(flags & StickyFlag); + self->zeroLastIndex(); + self->setSource(source); + self->setGlobal(flags & GlobalFlag); + self->setIgnoreCase(flags & IgnoreCaseFlag); + self->setMultiline(flags & MultilineFlag); + self->setSticky(flags & StickyFlag); return true; } RegExpRunStatus RegExpObject::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex, MatchPairs **output) { RegExpGuard g; @@ -702,17 +710,18 @@ RegExpCompartment::get(JSContext *cx, JS JSObject * js::CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto) { JS_ASSERT(obj->isRegExp()); JS_ASSERT(proto->isRegExp()); RegExpObjectBuilder builder(cx); - return builder.clone(&obj->asRegExp(), &proto->asRegExp()); + return builder.clone(RootedVar<RegExpObject*>(cx, &obj->asRegExp()), + RootedVar<RegExpObject*>(cx, &proto->asRegExp())); } bool js::ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut) { size_t n = flagStr->length(); const jschar *s = flagStr->getChars(cx); if (!s) @@ -746,26 +755,26 @@ js::ParseRegExpFlags(JSContext *cx, JSSt } return true; } template<XDRMode mode> bool js::XDRScriptRegExpObject(XDRState<mode> *xdr, HeapPtrObject *objp) { - JSAtom *source = 0; + RootedVarAtom source(xdr->cx()); uint32_t flagsword = 0; if (mode == XDR_ENCODE) { JS_ASSERT(objp); RegExpObject &reobj = (*objp)->asRegExp(); source = reobj.getSource(); flagsword = reobj.getFlags(); } - if (!XDRAtom(xdr, &source) || !xdr->codeUint32(&flagsword)) + if (!XDRAtom(xdr, source.address()) || !xdr->codeUint32(&flagsword)) return false; if (mode == XDR_DECODE) { RegExpFlag flags = RegExpFlag(flagsword); RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx(), source, flags, NULL); if (!reobj) return false; if (!reobj->clearParent(xdr->cx()))
--- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -39,16 +39,17 @@ * ***** END LICENSE BLOCK ***** */ #ifndef RegExpObject_h__ #define RegExpObject_h__ #include "mozilla/Attributes.h" #include <stddef.h> +#include "jscntxt.h" #include "jsobj.h" #include "js/TemplateLib.h" #include "yarr/Yarr.h" #if ENABLE_YARR_JIT #include "yarr/YarrJIT.h" #include "yarr/YarrSyntaxChecker.h" @@ -82,32 +83,32 @@ enum RegExpRunStatus { RegExpRunStatus_Error, RegExpRunStatus_Success, RegExpRunStatus_Success_NotFound }; class RegExpObjectBuilder { - JSContext *cx; - RegExpObject *reobj_; + JSContext *cx; + RootedVar<RegExpObject*> reobj_; bool getOrCreate(); bool getOrCreateClone(RegExpObject *proto); public: RegExpObjectBuilder(JSContext *cx, RegExpObject *reobj = NULL); RegExpObject *reobj() { return reobj_; } - RegExpObject *build(JSAtom *source, RegExpFlag flags); - RegExpObject *build(JSAtom *source, RegExpShared &shared); + RegExpObject *build(HandleAtom source, RegExpFlag flags); + RegExpObject *build(HandleAtom source, RegExpShared &shared); /* Perform a VM-internal clone. */ - RegExpObject *clone(RegExpObject *other, RegExpObject *proto); + RegExpObject *clone(Handle<RegExpObject*> other, Handle<RegExpObject*> proto); }; JSObject * CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto); namespace detail { class RegExpCode @@ -361,17 +362,17 @@ class RegExpObject : public JSObject create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length, RegExpFlag flags, TokenStream *ts); static RegExpObject * createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags, TokenStream *ts); static RegExpObject * - createNoStatics(JSContext *cx, JSAtom *atom, RegExpFlag flags, TokenStream *ts); + createNoStatics(JSContext *cx, HandleAtom atom, RegExpFlag flags, TokenStream *ts); /* * Run the regular expression over the input text. * * Results are placed in |output| as integer pairs. For eaxmple, * |output[0]| and |output[1]| represent the text indices that make * up the "0" (whole match) pair. Capturing parens will result in * more output. @@ -428,17 +429,17 @@ class RegExpObject : public JSObject /* * Compute the initial shape to associate with fresh RegExp objects, * encoding their initial properties. Return the shape after * changing this regular expression object's last property to it. */ Shape *assignInitialShape(JSContext *cx); - inline bool init(JSContext *cx, JSAtom *source, RegExpFlag flags); + inline bool init(JSContext *cx, HandleAtom source, RegExpFlag flags); /* * Precondition: the syntax for |source| has already been validated. * Side effect: sets the private field. */ bool createShared(JSContext *cx, RegExpGuard *g); RegExpShared *maybeShared() const;
--- a/js/src/vm/ScopeObject-inl.h +++ b/js/src/vm/ScopeObject-inl.h @@ -61,21 +61,22 @@ ScopeCoordinateAtom(JSScript *script, js inline JSObject & ScopeObject::enclosingScope() const { return getReservedSlot(SCOPE_CHAIN_SLOT).toObject(); } inline bool -ScopeObject::setEnclosingScope(JSContext *cx, JSObject &obj) +ScopeObject::setEnclosingScope(JSContext *cx, HandleObject obj) { - if (!obj.setDelegate(cx)) + RootedVarObject self(cx, this); + if (!obj->setDelegate(cx)) return false; - setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(obj)); + self->setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj)); return true; } inline StackFrame * ScopeObject::maybeStackFrame() const { JS_ASSERT(!isStaticBlock()); return reinterpret_cast<StackFrame *>(JSObject::getPrivate());
--- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -144,45 +144,44 @@ js_PutCallObject(StackFrame *fp) /* * Construct a call object for the given bindings. If this is a call object * for a function invocation, callee should be the function being called. * Otherwise it must be a call object for eval of strict mode code, and callee * must be null. */ CallObject * -CallObject::create(JSContext *cx, JSScript *script, JSObject &enclosing, JSObject *callee) +CallObject::create(JSContext *cx, JSScript *script, HandleObject enclosing, HandleObject callee) { RootedVarShape shape(cx); shape = script->bindings.callObjectShape(cx); if (shape == NULL) return NULL; gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots() + 1); RootedVarTypeObject type(cx); - type = cx->compartment->getEmptyType(cx); if (!type) return NULL; HeapSlot *slots; if (!PreallocateObjectDynamicSlots(cx, shape, &slots)) return NULL; - JSObject *obj = JSObject::create(cx, kind, shape, type, slots); + RootedVarObject obj(cx, JSObject::create(cx, kind, shape, type, slots)); if (!obj) return NULL; /* * Update the parent for bindings associated with non-compileAndGo scripts, * whose call objects do not have a consistent global variable and need * to be updated dynamically. */ - JSObject &global = enclosing.global(); + JSObject &global = enclosing->global(); if (&global != obj->getParent()) { JS_ASSERT(obj->getParent() == NULL); if (!obj->setParent(cx, &global)) return NULL; } #ifdef DEBUG JS_ASSERT(!obj->inDictionaryMode()); @@ -200,29 +199,31 @@ CallObject::create(JSContext *cx, JSScri JS_ASSERT_IF(callee, callee->isFunction()); obj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee)); /* * If |bindings| is for a function that has extensible parents, that means * its Call should have its own shape; see BaseShape::extensibleParents. */ - if (obj->lastProperty()->extensibleParents() && !obj->generateOwnShape(cx)) - return NULL; + if (obj->lastProperty()->extensibleParents()) { + if (!obj->generateOwnShape(cx)) + return NULL; + } return &obj->asCall(); } CallObject * CallObject::createForFunction(JSContext *cx, StackFrame *fp) { JS_ASSERT(fp->isNonEvalFunctionFrame()); JS_ASSERT(!fp->hasCallObj()); - JSObject *scopeChain = &fp->scopeChain(); + RootedVarObject scopeChain(cx, &fp->scopeChain()); JS_ASSERT_IF(scopeChain->isWith() || scopeChain->isBlock() || scopeChain->isCall(), scopeChain->getPrivate() != fp); /* * For a named function expression Call's parent points to an environment * object holding function's name. */ if (JSAtom *lambdaName = CallObjectLambdaName(fp->fun())) { @@ -232,29 +233,31 @@ CallObject::createForFunction(JSContext if (!DefineNativeProperty(cx, scopeChain, ATOM_TO_JSID(lambdaName), ObjectValue(fp->callee()), NULL, NULL, JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) { return NULL; } } - CallObject *callobj = create(cx, fp->script(), *scopeChain, &fp->callee()); + CallObject *callobj = create(cx, fp->script(), scopeChain, RootedVarObject(cx, &fp->callee())); if (!callobj) return NULL; callobj->setStackFrame(fp); fp->setScopeChainWithOwnCallObj(*callobj); return callobj; } CallObject * CallObject::createForStrictEval(JSContext *cx, StackFrame *fp) { - CallObject *callobj = create(cx, fp->script(), fp->scopeChain(), NULL); + CallObject *callobj = create(cx, fp->script(), + RootedVarObject(cx, &fp->scopeChain()), + RootedVarObject(cx)); if (!callobj) return NULL; callobj->setStackFrame(fp); fp->setScopeChainWithOwnCallObj(*callobj); return callobj; } @@ -423,48 +426,48 @@ DeclEnvObject::create(JSContext *cx, Sta