// 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, const CallArgs &args, EvalType evalType, AbstractFramePtr caller, HandleObject scopeobj) { JS_ASSERT((evalType == INDIRECT_EVAL) == !caller); JS_ASSERT_IF(evalType == INDIRECT_EVAL, scopeobj->isGlobal()); AssertInnerizedScopeChain(cx, *scopeobj); Rooted<GlobalObject*> scopeObjGlobal(cx, &scopeobj->global()); if (!GlobalObject::isRuntimeCodeGenEnabled(cx, scopeObjGlobal)) { 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; } if (!args[0].isString()) { args.rval().set(args[0]); return true; } RootedString str(cx, args[0].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'.) unsigned staticLevel; RootedValue thisv(cx); if (evalType == DIRECT_EVAL) { JS_ASSERT_IF(caller.isStackFrame(), !caller.asStackFrame()->runningInIon()); staticLevel = caller.script()->staticLevel + 1; // Direct calls to eval are supposed to see the caller's |this|. If we // haven't wrapped that yet, do so now, before we make a copy of it for // the eval code to use. if (!ComputeThis(cx, caller)) return false; thisv = caller.thisValue(); } else { JS_ASSERT(args.callee().global() == *scopeobj); staticLevel = 0; // Use the global as 'this', modulo outerization. JSObject *thisobj = JSObject::thisObject(cx, scopeobj); if (!thisobj) return false; thisv = ObjectValue(*thisobj); } Rooted<JSStableString*> stableStr(cx, str->ensureStable(cx)); if (!stableStr) return false; StableCharPtr chars = stableStr->chars(); size_t length = stableStr->length(); JSPrincipals *principals = PrincipalsForCompiledCode(args, cx); JSScript *callerScript = caller ? caller.script() : NULL; EvalJSONResult ejr = TryEvalJSON(cx, callerScript, chars, length, args.rval()); if (ejr != EvalJSON_NotJSON) return ejr == EvalJSON_Success; EvalScriptGuard esg(cx); if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame()) esg.lookupInEvalCache(stableStr, caller.fun(), staticLevel); 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); CompileOptions options(cx); options.setFileAndLine(filename, lineno) .setCompileAndGo(true) .setNoScriptRval(false) .setPrincipals(principals) .setOriginPrincipals(originPrincipals); RootedScript callerScript(cx, caller ? caller.script() : NULL); UnrootedScript compiled = frontend::CompileScript(cx, scopeobj, callerScript, options, chars.get(), length, stableStr, staticLevel); if (!compiled) return false; esg.setNewScript(compiled); } return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType), NullFramePtr() /* evalInFrame */, args.rval().address()); }
// 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, const CallArgs &args, EvalType evalType, AbstractFramePtr caller, HandleObject scopeobj, jsbytecode *pc) { JS_ASSERT((evalType == INDIRECT_EVAL) == !caller); JS_ASSERT((evalType == INDIRECT_EVAL) == !pc); JS_ASSERT_IF(evalType == INDIRECT_EVAL, scopeobj->is<GlobalObject>()); AssertInnerizedScopeChain(cx, *scopeobj); Rooted<GlobalObject*> scopeObjGlobal(cx, &scopeobj->global()); if (!GlobalObject::isRuntimeCodeGenEnabled(cx, scopeObjGlobal)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_EVAL); return false; } // ES5 15.1.2.1 step 1. if (args.length() < 1) { args.rval().setUndefined(); return true; } if (!args[0].isString()) { args.rval().set(args[0]); return true; } RootedString str(cx, args[0].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'.) unsigned staticLevel; RootedValue thisv(cx); if (evalType == DIRECT_EVAL) { JS_ASSERT_IF(caller.isInterpreterFrame(), !caller.asInterpreterFrame()->runningInJit()); staticLevel = caller.script()->staticLevel() + 1; // Direct calls to eval are supposed to see the caller's |this|. If we // haven't wrapped that yet, do so now, before we make a copy of it for // the eval code to use. if (!ComputeThis(cx, caller)) return false; thisv = caller.thisValue(); } else { JS_ASSERT(args.callee().global() == *scopeobj); staticLevel = 0; // Use the global as 'this', modulo outerization. JSObject *thisobj = JSObject::thisObject(cx, scopeobj); if (!thisobj) return false; thisv = ObjectValue(*thisobj); } Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx)); if (!flatStr) return false; RootedScript callerScript(cx, caller ? caller.script() : nullptr); EvalJSONResult ejr = TryEvalJSON(cx, callerScript, flatStr, args.rval()); if (ejr != EvalJSON_NotJSON) return ejr == EvalJSON_Success; EvalScriptGuard esg(cx); if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame()) esg.lookupInEvalCache(flatStr, callerScript, pc); if (!esg.foundScript()) { RootedScript maybeScript(cx); unsigned lineno; const char *filename; JSPrincipals *originPrincipals; uint32_t pcOffset; DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset, &originPrincipals, 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(); CompileOptions options(cx); options.setFileAndLine(filename, 1) .setCompileAndGo(true) .setForEval(true) .setNoScriptRval(false) .setOriginPrincipals(originPrincipals) .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset); AutoStableStringChars flatChars(cx); if (!flatChars.initTwoByte(cx, flatStr)) return false; const char16_t *chars = flatChars.twoByteRange().start().get(); SourceBufferHolder::Ownership ownership = flatChars.maybeGiveOwnershipToCaller() ? SourceBufferHolder::GiveOwnership : SourceBufferHolder::NoOwnership; SourceBufferHolder srcBuf(chars, flatStr->length(), ownership); JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(), scopeobj, callerScript, options, srcBuf, flatStr, staticLevel); if (!compiled) return false; esg.setNewScript(compiled); } return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType), NullFramePtr() /* evalInFrame */, args.rval().address()); }
// 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, const CallArgs& args, EvalType evalType, AbstractFramePtr caller, HandleObject scopeobj, jsbytecode* pc) { 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 (args.length() < 1) { args.rval().setUndefined(); return true; } if (!args[0].isString()) { args.rval().set(args[0]); return true; } RootedString str(cx, args[0].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, args.callee().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, args.rval()); if (ejr != EvalJSON_NotJSON) return ejr == EvalJSON_Success; EvalScriptGuard esg(cx); if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame()) 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<StaticEvalObject*> staticScope(cx, StaticEvalObject::create(cx, enclosing)); if (!staticScope) return false; CompileOptions options(cx); options.setFileAndLine(filename, 1) .setIsRunOnce(true) .setForEval(true) .setNoScriptRval(false) .setMutedErrors(mutedErrors) .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset) .maybeMakeStrictMode(evalType == DIRECT_EVAL && IsStrictEvalPC(pc)); 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, ExecuteType(evalType), NullFramePtr() /* evalInFrame */, args.rval().address()); }