JSObject * JSAbstractFramePtr::callObject(JSContext *cx) { AbstractFramePtr frame = Valueify(*this); JS_ASSERT_IF(frame.isStackFrame(), cx->stack.space().containsSlow(frame.asStackFrame())); if (!frame.isFunctionFrame()) return NULL; JSObject *o = GetDebugScopeForFrame(cx, frame); /* * Given that fp is a function frame and GetDebugScopeForFrame always fills * in missing scopes, we can expect to find fp's CallObject on 'o'. Note: * - GetDebugScopeForFrame wraps every ScopeObject (missing or not) with * a DebugScopeObject proxy. * - If fp is an eval-in-function, then fp has no callobj of its own and * JS_GetFrameCallObject will return the innermost function's callobj. */ while (o) { ScopeObject &scope = o->asDebugScope().scope(); if (scope.isCall()) return o; o = o->enclosingScope(); } return NULL; }
// Initialize the decl env Object, call object, and any arguments obj of the current frame. bool jit::EnsureHasScopeObjects(JSContext* cx, AbstractFramePtr fp) { if (fp.isFunctionFrame() && fp.fun()->isHeavyweight() && !fp.hasCallObj()) { return fp.initFunctionScopeObjects(cx); } return true; }
// Initialize the decl env Object, call object, and any arguments obj of the current frame. bool jit::EnsureHasScopeObjects(JSContext* cx, AbstractFramePtr fp) { // Ion does not compile eval scripts. MOZ_ASSERT(!fp.isEvalFrame()); if (fp.isFunctionFrame() && fp.callee()->needsCallObject() && !fp.hasCallObj()) { return fp.initFunctionScopeObjects(cx); } return true; }
bool js::DirectEval(JSContext *cx, const CallArgs &args) { // Direct eval can assume it was called from an interpreted or baseline frame. ScriptFrameIter iter(cx); AbstractFramePtr caller = iter.abstractFramePtr(); JS_ASSERT(caller.scopeChain()->global().valueIsEval(args.calleev())); JS_ASSERT(JSOp(*iter.pc()) == JSOP_EVAL || JSOp(*iter.pc()) == JSOP_SPREADEVAL); JS_ASSERT_IF(caller.isFunctionFrame(), caller.compartment() == caller.callee()->compartment()); RootedObject scopeChain(cx, caller.scopeChain()); return EvalKernel(cx, args, DIRECT_EVAL, caller, scopeChain, iter.pc()); }
bool js::DirectEval(JSContext *cx, const CallArgs &args) { // Direct eval can assume it was called from an interpreted or baseline frame. ScriptFrameIter iter(cx); AbstractFramePtr caller = iter.abstractFramePtr(); JS_ASSERT(IsBuiltinEvalForScope(caller.scopeChain(), args.calleev())); JS_ASSERT(JSOp(*iter.pc()) == JSOP_EVAL); JS_ASSERT_IF(caller.isFunctionFrame(), caller.compartment() == caller.callee()->compartment()); if (!WarnOnTooManyArgs(cx, args)) return false; RootedObject scopeChain(cx, caller.scopeChain()); return EvalKernel(cx, args, DIRECT_EVAL, caller, scopeChain, iter.pc()); }
// Initialize the decl env Object, call object, and any arguments obj of the // current frame. bool jit::EnsureHasEnvironmentObjects(JSContext* cx, AbstractFramePtr fp) { // Ion does not compile eval scripts. MOZ_ASSERT(!fp.isEvalFrame()); if (fp.isFunctionFrame()) { // Ion does not handle extra var environments due to parameter // expressions yet. MOZ_ASSERT(!fp.callee()->needsExtraBodyVarEnvironment()); if (!fp.hasInitialEnvironment() && fp.callee()->needsFunctionEnvironmentObjects()) { if (!fp.initFunctionEnvironmentObjects(cx)) return false; } } return true; }
void StackFrame::initExecuteFrame(JSContext *cx, JSScript *script, AbstractFramePtr evalInFramePrev, const Value &thisv, JSObject &scopeChain, ExecuteType type) { /* * See encoding of ExecuteType. When GLOBAL isn't set, we are executing a * script in the context of another frame and the frame type is determined * by the context. */ flags_ = type | HAS_SCOPECHAIN; JSObject *callee = nullptr; if (!(flags_ & (GLOBAL))) { if (evalInFramePrev) { JS_ASSERT(evalInFramePrev.isFunctionFrame() || evalInFramePrev.isGlobalFrame()); if (evalInFramePrev.isFunctionFrame()) { callee = evalInFramePrev.callee(); flags_ |= FUNCTION; } else { flags_ |= GLOBAL; } } else { ScriptFrameIter iter(cx); JS_ASSERT(iter.isFunctionFrame() || iter.isGlobalFrame()); if (iter.isFunctionFrame()) { callee = iter.callee(); flags_ |= FUNCTION; } else { flags_ |= GLOBAL; } } } Value *dstvp = (Value *)this - 2; dstvp[1] = thisv; if (isFunctionFrame()) { dstvp[0] = ObjectValue(*callee); exec.fun = &callee->as<JSFunction>(); u.evalScript = script; } else { JS_ASSERT(isGlobalFrame()); dstvp[0] = NullValue(); exec.script = script; #ifdef DEBUG u.evalScript = (JSScript *)0xbad; #endif } scopeChain_ = &scopeChain; prev_ = nullptr; prevpc_ = nullptr; prevsp_ = nullptr; JS_ASSERT_IF(evalInFramePrev, isDebuggerFrame()); evalInFramePrev_ = evalInFramePrev; #ifdef DEBUG Debug_SetValueRangeToCrashOnTouch(&rval_, 1); hookData_ = (void *)0xbad; #endif }
// Common code implementing direct and indirect eval. // // Evaluate call.argv[2], if it is a string, in the context of the given calling // 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, HandleValue v, EvalType evalType, AbstractFramePtr caller, HandleObject scopeobj, jsbytecode* pc, MutableHandleValue vp) { MOZ_ASSERT((evalType == INDIRECT_EVAL) == !caller); MOZ_ASSERT((evalType == INDIRECT_EVAL) == !pc); MOZ_ASSERT_IF(evalType == INDIRECT_EVAL, IsGlobalLexicalScope(scopeobj)); AssertInnerizedScopeChain(cx, *scopeobj); Rooted<GlobalObject*> scopeObjGlobal(cx, &scopeobj->global()); if (!GlobalObject::isRuntimeCodeGenEnabled(cx, scopeObjGlobal)) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_EVAL); return false; } // ES5 15.1.2.1 step 1. if (!v.isString()) { vp.set(v); return true; } RootedString str(cx, v.toString()); // ES5 15.1.2.1 steps 2-8. // Per ES5, indirect eval runs in the global scope. (eval is specified this // way so that the compiler can make assumptions about what bindings may or // may not exist in the current frame if it doesn't see 'eval'.) MOZ_ASSERT_IF(evalType != DIRECT_EVAL, cx->global() == &scopeobj->as<ClonedBlockObject>().global()); RootedLinearString linearStr(cx, str->ensureLinear(cx)); if (!linearStr) return false; RootedScript callerScript(cx, caller ? caller.script() : nullptr); EvalJSONResult ejr = TryEvalJSON(cx, linearStr, vp); if (ejr != EvalJSON_NotJSON) return ejr == EvalJSON_Success; EvalScriptGuard esg(cx); if (evalType == DIRECT_EVAL && caller.isFunctionFrame()) esg.lookupInEvalCache(linearStr, callerScript, pc); if (!esg.foundScript()) { RootedScript maybeScript(cx); unsigned lineno; const char* filename; bool mutedErrors; uint32_t pcOffset; DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset, &mutedErrors, evalType == DIRECT_EVAL ? CALLED_FROM_JSOP_EVAL : NOT_CALLED_FROM_JSOP_EVAL); const char* introducerFilename = filename; if (maybeScript && maybeScript->scriptSource()->introducerFilename()) introducerFilename = maybeScript->scriptSource()->introducerFilename(); RootedObject enclosing(cx); if (evalType == DIRECT_EVAL) enclosing = callerScript->innermostStaticScope(pc); else enclosing = &cx->global()->lexicalScope().staticBlock(); Rooted<StaticEvalScope*> staticScope(cx, StaticEvalScope::create(cx, enclosing)); if (!staticScope) return false; CompileOptions options(cx); options.setIsRunOnce(true) .setForEval(true) .setNoScriptRval(false) .setMutedErrors(mutedErrors) .maybeMakeStrictMode(evalType == DIRECT_EVAL && IsStrictEvalPC(pc)); if (introducerFilename) { options.setFileAndLine(filename, 1); options.setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset); } else { options.setFileAndLine("eval", 1); options.setIntroductionType("eval"); } AutoStableStringChars linearChars(cx); if (!linearChars.initTwoByte(cx, linearStr)) return false; const char16_t* chars = linearChars.twoByteRange().start().get(); SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller() ? SourceBufferHolder::GiveOwnership : SourceBufferHolder::NoOwnership; SourceBufferHolder srcBuf(chars, linearStr->length(), ownership); JSScript* compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(), scopeobj, staticScope, callerScript, options, srcBuf, linearStr); if (!compiled) return false; if (compiled->strict()) staticScope->setStrict(); esg.setNewScript(compiled); } // Look up the newTarget from the frame iterator. Value newTargetVal = NullValue(); return ExecuteKernel(cx, esg.script(), *scopeobj, newTargetVal, NullFramePtr() /* evalInFrame */, vp.address()); }
UnrootedScript frontend::CompileScript(JSContext *cx, HandleObject scopeChain, AbstractFramePtr callerFrame, const CompileOptions &options, StableCharPtr chars, size_t length, JSString *source_ /* = NULL */, unsigned staticLevel /* = 0 */) { RootedString source(cx, source_); class ProbesManager { const char* filename; unsigned lineno; public: ProbesManager(const char *f, unsigned l) : filename(f), lineno(l) { Probes::compileScriptBegin(filename, lineno); } ~ProbesManager() { Probes::compileScriptEnd(filename, lineno); } }; ProbesManager probesManager(options.filename, options.lineno); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(callerFrame, options.compileAndGo); JS_ASSERT_IF(staticLevel != 0, callerFrame); if (!CheckLength(cx, length)) return UnrootedScript(NULL); JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE); ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return UnrootedScript(NULL); ScriptSourceHolder ssh(ss); SourceCompressionToken sct(cx); switch (options.sourcePolicy) { case CompileOptions::SAVE_SOURCE: if (!ss->setSourceCopy(cx, chars, length, false, &sct)) return UnrootedScript(NULL); break; case CompileOptions::LAZY_SOURCE: ss->setSourceRetrievable(); break; case CompileOptions::NO_SOURCE: break; } Parser parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return UnrootedScript(NULL); parser.sct = &sct; GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx)); ParseContext pc(&parser, &globalsc, staticLevel, /* bodyid = */ 0); if (!pc.init()) return UnrootedScript(NULL); bool savedCallerFun = options.compileAndGo && callerFrame && callerFrame.isFunctionFrame(); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, options, staticLevel, ss, 0, length)); if (!script) return UnrootedScript(NULL); // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). InternalHandle<Bindings*> bindings(script, &script->bindings); if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, NULL)) return UnrootedScript(NULL); // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL; JS_ASSERT_IF(globalScope, globalScope->isNative()); JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter bce(/* parent = */ NULL, &parser, &globalsc, script, callerFrame, !!globalScope, options.lineno, options.selfHostingMode); if (!bce.init()) return UnrootedScript(NULL); /* If this is a direct call to eval, inherit the caller's strictness. */ if (callerFrame && callerFrame.script()->strict) globalsc.strict = true; if (options.compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = AtomizeString<CanGC>(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return UnrootedScript(NULL); } if (callerFrame && callerFrame.isFunctionFrame()) { /* * An eval script in a caller frame needs to have its enclosing * function captured in case it refers to an upvar, and someone * wishes to decompile it while it's running. */ JSFunction *fun = callerFrame.fun(); ObjectBox *funbox = parser.newFunctionBox(fun, &pc, fun->strict()); if (!funbox) return UnrootedScript(NULL); bce.objectList.add(funbox); } } TokenStream &tokenStream = parser.tokenStream; bool canHaveDirectives = true; for (;;) { TokenKind tt = tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return UnrootedScript(NULL); } ParseNode *pn = parser.statement(); if (!pn) return UnrootedScript(NULL); if (canHaveDirectives) { if (!parser.maybeParseDirective(pn, &canHaveDirectives)) return UnrootedScript(NULL); } if (!FoldConstants(cx, &pn, &parser)) return UnrootedScript(NULL); if (!NameFunctions(cx, pn)) return UnrootedScript(NULL); if (!EmitTree(cx, &bce, pn)) return UnrootedScript(NULL); parser.freeTree(pn); } if (!SetSourceMap(cx, tokenStream, ss, script)) return UnrootedScript(NULL); // It's an error to use |arguments| in a function that has a rest parameter. if (callerFrame && callerFrame.isFunctionFrame() && callerFrame.fun()->hasRest()) { HandlePropertyName arguments = cx->names().arguments; for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) { if (r.front().key() == arguments) { parser.reportError(NULL, JSMSG_ARGUMENTS_AND_REST); return UnrootedScript(NULL); } } } /* * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_STOP) < 0) return UnrootedScript(NULL); if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return UnrootedScript(NULL); bce.tellDebuggerAboutCompiledScript(cx); if (!sct.complete()) return UnrootedScript(NULL); return script; }