Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * vim: set ts=8 sts=4 et sw=4 tw=99: |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "builtin/Eval.h" |
michael@0 | 8 | |
michael@0 | 9 | #include "mozilla/HashFunctions.h" |
michael@0 | 10 | |
michael@0 | 11 | #include "jscntxt.h" |
michael@0 | 12 | #include "jsonparser.h" |
michael@0 | 13 | |
michael@0 | 14 | #include "frontend/BytecodeCompiler.h" |
michael@0 | 15 | #include "vm/GlobalObject.h" |
michael@0 | 16 | |
michael@0 | 17 | #include "vm/Interpreter-inl.h" |
michael@0 | 18 | |
michael@0 | 19 | using namespace js; |
michael@0 | 20 | |
michael@0 | 21 | using mozilla::AddToHash; |
michael@0 | 22 | using mozilla::HashString; |
michael@0 | 23 | |
michael@0 | 24 | // We should be able to assert this for *any* fp->scopeChain(). |
michael@0 | 25 | static void |
michael@0 | 26 | AssertInnerizedScopeChain(JSContext *cx, JSObject &scopeobj) |
michael@0 | 27 | { |
michael@0 | 28 | #ifdef DEBUG |
michael@0 | 29 | RootedObject obj(cx); |
michael@0 | 30 | for (obj = &scopeobj; obj; obj = obj->enclosingScope()) { |
michael@0 | 31 | if (JSObjectOp op = obj->getClass()->ext.innerObject) { |
michael@0 | 32 | JS_ASSERT(op(cx, obj) == obj); |
michael@0 | 33 | } |
michael@0 | 34 | } |
michael@0 | 35 | #endif |
michael@0 | 36 | } |
michael@0 | 37 | |
michael@0 | 38 | static bool |
michael@0 | 39 | IsEvalCacheCandidate(JSScript *script) |
michael@0 | 40 | { |
michael@0 | 41 | // Make sure there are no inner objects which might use the wrong parent |
michael@0 | 42 | // and/or call scope by reusing the previous eval's script. Skip the |
michael@0 | 43 | // script's first object, which entrains the eval's scope. |
michael@0 | 44 | return script->savedCallerFun() && |
michael@0 | 45 | !script->hasSingletons() && |
michael@0 | 46 | script->objects()->length == 1 && |
michael@0 | 47 | !script->hasRegexps(); |
michael@0 | 48 | } |
michael@0 | 49 | |
michael@0 | 50 | /* static */ HashNumber |
michael@0 | 51 | EvalCacheHashPolicy::hash(const EvalCacheLookup &l) |
michael@0 | 52 | { |
michael@0 | 53 | return AddToHash(HashString(l.str->chars(), l.str->length()), |
michael@0 | 54 | l.callerScript.get(), |
michael@0 | 55 | l.version, |
michael@0 | 56 | l.pc); |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | /* static */ bool |
michael@0 | 60 | EvalCacheHashPolicy::match(const EvalCacheEntry &cacheEntry, const EvalCacheLookup &l) |
michael@0 | 61 | { |
michael@0 | 62 | JSScript *script = cacheEntry.script; |
michael@0 | 63 | |
michael@0 | 64 | JS_ASSERT(IsEvalCacheCandidate(script)); |
michael@0 | 65 | |
michael@0 | 66 | // Get the source string passed for safekeeping in the atom map |
michael@0 | 67 | // by the prior eval to frontend::CompileScript. |
michael@0 | 68 | JSAtom *keyStr = script->atoms[0]; |
michael@0 | 69 | |
michael@0 | 70 | return EqualStrings(keyStr, l.str) && |
michael@0 | 71 | cacheEntry.callerScript == l.callerScript && |
michael@0 | 72 | script->getVersion() == l.version && |
michael@0 | 73 | cacheEntry.pc == l.pc; |
michael@0 | 74 | } |
michael@0 | 75 | |
michael@0 | 76 | // There are two things we want to do with each script executed in EvalKernel: |
michael@0 | 77 | // 1. notify OldDebugAPI about script creation/destruction |
michael@0 | 78 | // 2. add the script to the eval cache when EvalKernel is finished |
michael@0 | 79 | // |
michael@0 | 80 | // NB: Although the eval cache keeps a script alive wrt to the JS engine, from |
michael@0 | 81 | // an OldDebugAPI user's perspective, we want each eval() to create and |
michael@0 | 82 | // destroy a script. This hides implementation details and means we don't have |
michael@0 | 83 | // to deal with calls to JS_GetScriptObject for scripts in the eval cache. |
michael@0 | 84 | class EvalScriptGuard |
michael@0 | 85 | { |
michael@0 | 86 | JSContext *cx_; |
michael@0 | 87 | Rooted<JSScript*> script_; |
michael@0 | 88 | |
michael@0 | 89 | /* These fields are only valid if lookup_.str is non-nullptr. */ |
michael@0 | 90 | EvalCacheLookup lookup_; |
michael@0 | 91 | EvalCache::AddPtr p_; |
michael@0 | 92 | |
michael@0 | 93 | Rooted<JSLinearString*> lookupStr_; |
michael@0 | 94 | |
michael@0 | 95 | public: |
michael@0 | 96 | EvalScriptGuard(JSContext *cx) |
michael@0 | 97 | : cx_(cx), script_(cx), lookup_(cx), lookupStr_(cx) {} |
michael@0 | 98 | |
michael@0 | 99 | ~EvalScriptGuard() { |
michael@0 | 100 | if (script_) { |
michael@0 | 101 | CallDestroyScriptHook(cx_->runtime()->defaultFreeOp(), script_); |
michael@0 | 102 | script_->cacheForEval(); |
michael@0 | 103 | EvalCacheEntry cacheEntry = {script_, lookup_.callerScript, lookup_.pc}; |
michael@0 | 104 | lookup_.str = lookupStr_; |
michael@0 | 105 | if (lookup_.str && IsEvalCacheCandidate(script_)) |
michael@0 | 106 | cx_->runtime()->evalCache.relookupOrAdd(p_, lookup_, cacheEntry); |
michael@0 | 107 | } |
michael@0 | 108 | } |
michael@0 | 109 | |
michael@0 | 110 | void lookupInEvalCache(JSLinearString *str, JSScript *callerScript, jsbytecode *pc) |
michael@0 | 111 | { |
michael@0 | 112 | lookupStr_ = str; |
michael@0 | 113 | lookup_.str = str; |
michael@0 | 114 | lookup_.callerScript = callerScript; |
michael@0 | 115 | lookup_.version = cx_->findVersion(); |
michael@0 | 116 | lookup_.pc = pc; |
michael@0 | 117 | p_ = cx_->runtime()->evalCache.lookupForAdd(lookup_); |
michael@0 | 118 | if (p_) { |
michael@0 | 119 | script_ = p_->script; |
michael@0 | 120 | cx_->runtime()->evalCache.remove(p_); |
michael@0 | 121 | CallNewScriptHook(cx_, script_, NullPtr()); |
michael@0 | 122 | script_->uncacheForEval(); |
michael@0 | 123 | } |
michael@0 | 124 | } |
michael@0 | 125 | |
michael@0 | 126 | void setNewScript(JSScript *script) { |
michael@0 | 127 | // JSScript::initFromEmitter has already called js_CallNewScriptHook. |
michael@0 | 128 | JS_ASSERT(!script_ && script); |
michael@0 | 129 | script_ = script; |
michael@0 | 130 | script_->setActiveEval(); |
michael@0 | 131 | } |
michael@0 | 132 | |
michael@0 | 133 | bool foundScript() { |
michael@0 | 134 | return !!script_; |
michael@0 | 135 | } |
michael@0 | 136 | |
michael@0 | 137 | HandleScript script() { |
michael@0 | 138 | JS_ASSERT(script_); |
michael@0 | 139 | return script_; |
michael@0 | 140 | } |
michael@0 | 141 | }; |
michael@0 | 142 | |
michael@0 | 143 | enum EvalJSONResult { |
michael@0 | 144 | EvalJSON_Failure, |
michael@0 | 145 | EvalJSON_Success, |
michael@0 | 146 | EvalJSON_NotJSON |
michael@0 | 147 | }; |
michael@0 | 148 | |
michael@0 | 149 | static EvalJSONResult |
michael@0 | 150 | TryEvalJSON(JSContext *cx, JSScript *callerScript, |
michael@0 | 151 | ConstTwoByteChars chars, size_t length, MutableHandleValue rval) |
michael@0 | 152 | { |
michael@0 | 153 | // If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON. |
michael@0 | 154 | // Try the JSON parser first because it's much faster. If the eval string |
michael@0 | 155 | // isn't JSON, JSON parsing will probably fail quickly, so little time |
michael@0 | 156 | // will be lost. |
michael@0 | 157 | // |
michael@0 | 158 | // Don't use the JSON parser if the caller is strict mode code, because in |
michael@0 | 159 | // strict mode object literals must not have repeated properties, and the |
michael@0 | 160 | // JSON parser cheerfully (and correctly) accepts them. If you're parsing |
michael@0 | 161 | // JSON with eval and using strict mode, you deserve to be slow. |
michael@0 | 162 | if (length > 2 && |
michael@0 | 163 | ((chars[0] == '[' && chars[length - 1] == ']') || |
michael@0 | 164 | (chars[0] == '(' && chars[length - 1] == ')')) && |
michael@0 | 165 | (!callerScript || !callerScript->strict())) |
michael@0 | 166 | { |
michael@0 | 167 | // Remarkably, JavaScript syntax is not a superset of JSON syntax: |
michael@0 | 168 | // strings in JavaScript cannot contain the Unicode line and paragraph |
michael@0 | 169 | // terminator characters U+2028 and U+2029, but strings in JSON can. |
michael@0 | 170 | // Rather than force the JSON parser to handle this quirk when used by |
michael@0 | 171 | // eval, we simply don't use the JSON parser when either character |
michael@0 | 172 | // appears in the provided string. See bug 657367. |
michael@0 | 173 | for (const jschar *cp = &chars[1], *end = &chars[length - 2]; ; cp++) { |
michael@0 | 174 | if (*cp == 0x2028 || *cp == 0x2029) |
michael@0 | 175 | break; |
michael@0 | 176 | |
michael@0 | 177 | if (cp == end) { |
michael@0 | 178 | bool isArray = (chars[0] == '['); |
michael@0 | 179 | JSONParser parser(cx, isArray ? chars : chars + 1U, isArray ? length : length - 2, |
michael@0 | 180 | JSONParser::NoError); |
michael@0 | 181 | RootedValue tmp(cx); |
michael@0 | 182 | if (!parser.parse(&tmp)) |
michael@0 | 183 | return EvalJSON_Failure; |
michael@0 | 184 | if (tmp.isUndefined()) |
michael@0 | 185 | return EvalJSON_NotJSON; |
michael@0 | 186 | rval.set(tmp); |
michael@0 | 187 | return EvalJSON_Success; |
michael@0 | 188 | } |
michael@0 | 189 | } |
michael@0 | 190 | } |
michael@0 | 191 | return EvalJSON_NotJSON; |
michael@0 | 192 | } |
michael@0 | 193 | |
michael@0 | 194 | // Define subset of ExecuteType so that casting performs the injection. |
michael@0 | 195 | enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL }; |
michael@0 | 196 | |
michael@0 | 197 | // Common code implementing direct and indirect eval. |
michael@0 | 198 | // |
michael@0 | 199 | // Evaluate call.argv[2], if it is a string, in the context of the given calling |
michael@0 | 200 | // frame, with the provided scope chain, with the semantics of either a direct |
michael@0 | 201 | // or indirect eval (see ES5 10.4.2). If this is an indirect eval, scopeobj |
michael@0 | 202 | // must be a global object. |
michael@0 | 203 | // |
michael@0 | 204 | // On success, store the completion value in call.rval and return true. |
michael@0 | 205 | static bool |
michael@0 | 206 | EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFramePtr caller, |
michael@0 | 207 | HandleObject scopeobj, jsbytecode *pc) |
michael@0 | 208 | { |
michael@0 | 209 | JS_ASSERT((evalType == INDIRECT_EVAL) == !caller); |
michael@0 | 210 | JS_ASSERT((evalType == INDIRECT_EVAL) == !pc); |
michael@0 | 211 | JS_ASSERT_IF(evalType == INDIRECT_EVAL, scopeobj->is<GlobalObject>()); |
michael@0 | 212 | AssertInnerizedScopeChain(cx, *scopeobj); |
michael@0 | 213 | |
michael@0 | 214 | Rooted<GlobalObject*> scopeObjGlobal(cx, &scopeobj->global()); |
michael@0 | 215 | if (!GlobalObject::isRuntimeCodeGenEnabled(cx, scopeObjGlobal)) { |
michael@0 | 216 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_EVAL); |
michael@0 | 217 | return false; |
michael@0 | 218 | } |
michael@0 | 219 | |
michael@0 | 220 | // ES5 15.1.2.1 step 1. |
michael@0 | 221 | if (args.length() < 1) { |
michael@0 | 222 | args.rval().setUndefined(); |
michael@0 | 223 | return true; |
michael@0 | 224 | } |
michael@0 | 225 | if (!args[0].isString()) { |
michael@0 | 226 | args.rval().set(args[0]); |
michael@0 | 227 | return true; |
michael@0 | 228 | } |
michael@0 | 229 | RootedString str(cx, args[0].toString()); |
michael@0 | 230 | |
michael@0 | 231 | // ES5 15.1.2.1 steps 2-8. |
michael@0 | 232 | |
michael@0 | 233 | // Per ES5, indirect eval runs in the global scope. (eval is specified this |
michael@0 | 234 | // way so that the compiler can make assumptions about what bindings may or |
michael@0 | 235 | // may not exist in the current frame if it doesn't see 'eval'.) |
michael@0 | 236 | unsigned staticLevel; |
michael@0 | 237 | RootedValue thisv(cx); |
michael@0 | 238 | if (evalType == DIRECT_EVAL) { |
michael@0 | 239 | JS_ASSERT_IF(caller.isInterpreterFrame(), !caller.asInterpreterFrame()->runningInJit()); |
michael@0 | 240 | staticLevel = caller.script()->staticLevel() + 1; |
michael@0 | 241 | |
michael@0 | 242 | // Direct calls to eval are supposed to see the caller's |this|. If we |
michael@0 | 243 | // haven't wrapped that yet, do so now, before we make a copy of it for |
michael@0 | 244 | // the eval code to use. |
michael@0 | 245 | if (!ComputeThis(cx, caller)) |
michael@0 | 246 | return false; |
michael@0 | 247 | thisv = caller.thisValue(); |
michael@0 | 248 | } else { |
michael@0 | 249 | JS_ASSERT(args.callee().global() == *scopeobj); |
michael@0 | 250 | staticLevel = 0; |
michael@0 | 251 | |
michael@0 | 252 | // Use the global as 'this', modulo outerization. |
michael@0 | 253 | JSObject *thisobj = JSObject::thisObject(cx, scopeobj); |
michael@0 | 254 | if (!thisobj) |
michael@0 | 255 | return false; |
michael@0 | 256 | thisv = ObjectValue(*thisobj); |
michael@0 | 257 | } |
michael@0 | 258 | |
michael@0 | 259 | Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx)); |
michael@0 | 260 | if (!flatStr) |
michael@0 | 261 | return false; |
michael@0 | 262 | |
michael@0 | 263 | size_t length = flatStr->length(); |
michael@0 | 264 | ConstTwoByteChars chars(flatStr->chars(), length); |
michael@0 | 265 | |
michael@0 | 266 | RootedScript callerScript(cx, caller ? caller.script() : nullptr); |
michael@0 | 267 | EvalJSONResult ejr = TryEvalJSON(cx, callerScript, chars, length, args.rval()); |
michael@0 | 268 | if (ejr != EvalJSON_NotJSON) |
michael@0 | 269 | return ejr == EvalJSON_Success; |
michael@0 | 270 | |
michael@0 | 271 | EvalScriptGuard esg(cx); |
michael@0 | 272 | |
michael@0 | 273 | if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame()) |
michael@0 | 274 | esg.lookupInEvalCache(flatStr, callerScript, pc); |
michael@0 | 275 | |
michael@0 | 276 | if (!esg.foundScript()) { |
michael@0 | 277 | RootedScript maybeScript(cx); |
michael@0 | 278 | unsigned lineno; |
michael@0 | 279 | const char *filename; |
michael@0 | 280 | JSPrincipals *originPrincipals; |
michael@0 | 281 | uint32_t pcOffset; |
michael@0 | 282 | DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset, |
michael@0 | 283 | &originPrincipals, |
michael@0 | 284 | evalType == DIRECT_EVAL |
michael@0 | 285 | ? CALLED_FROM_JSOP_EVAL |
michael@0 | 286 | : NOT_CALLED_FROM_JSOP_EVAL); |
michael@0 | 287 | |
michael@0 | 288 | const char *introducerFilename = filename; |
michael@0 | 289 | if (maybeScript && maybeScript->scriptSource()->introducerFilename()) |
michael@0 | 290 | introducerFilename = maybeScript->scriptSource()->introducerFilename(); |
michael@0 | 291 | |
michael@0 | 292 | CompileOptions options(cx); |
michael@0 | 293 | options.setFileAndLine(filename, 1) |
michael@0 | 294 | .setCompileAndGo(true) |
michael@0 | 295 | .setForEval(true) |
michael@0 | 296 | .setNoScriptRval(false) |
michael@0 | 297 | .setOriginPrincipals(originPrincipals) |
michael@0 | 298 | .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset); |
michael@0 | 299 | SourceBufferHolder srcBuf(chars.get(), length, SourceBufferHolder::NoOwnership); |
michael@0 | 300 | JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(), |
michael@0 | 301 | scopeobj, callerScript, options, |
michael@0 | 302 | srcBuf, flatStr, staticLevel); |
michael@0 | 303 | if (!compiled) |
michael@0 | 304 | return false; |
michael@0 | 305 | |
michael@0 | 306 | esg.setNewScript(compiled); |
michael@0 | 307 | } |
michael@0 | 308 | |
michael@0 | 309 | return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType), |
michael@0 | 310 | NullFramePtr() /* evalInFrame */, args.rval().address()); |
michael@0 | 311 | } |
michael@0 | 312 | |
michael@0 | 313 | bool |
michael@0 | 314 | js::DirectEvalStringFromIon(JSContext *cx, |
michael@0 | 315 | HandleObject scopeobj, HandleScript callerScript, |
michael@0 | 316 | HandleValue thisValue, HandleString str, |
michael@0 | 317 | jsbytecode *pc, MutableHandleValue vp) |
michael@0 | 318 | { |
michael@0 | 319 | AssertInnerizedScopeChain(cx, *scopeobj); |
michael@0 | 320 | |
michael@0 | 321 | Rooted<GlobalObject*> scopeObjGlobal(cx, &scopeobj->global()); |
michael@0 | 322 | if (!GlobalObject::isRuntimeCodeGenEnabled(cx, scopeObjGlobal)) { |
michael@0 | 323 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_EVAL); |
michael@0 | 324 | return false; |
michael@0 | 325 | } |
michael@0 | 326 | |
michael@0 | 327 | // ES5 15.1.2.1 steps 2-8. |
michael@0 | 328 | |
michael@0 | 329 | unsigned staticLevel = callerScript->staticLevel() + 1; |
michael@0 | 330 | |
michael@0 | 331 | Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx)); |
michael@0 | 332 | if (!flatStr) |
michael@0 | 333 | return false; |
michael@0 | 334 | |
michael@0 | 335 | size_t length = flatStr->length(); |
michael@0 | 336 | ConstTwoByteChars chars(flatStr->chars(), length); |
michael@0 | 337 | |
michael@0 | 338 | EvalJSONResult ejr = TryEvalJSON(cx, callerScript, chars, length, vp); |
michael@0 | 339 | if (ejr != EvalJSON_NotJSON) |
michael@0 | 340 | return ejr == EvalJSON_Success; |
michael@0 | 341 | |
michael@0 | 342 | EvalScriptGuard esg(cx); |
michael@0 | 343 | |
michael@0 | 344 | esg.lookupInEvalCache(flatStr, callerScript, pc); |
michael@0 | 345 | |
michael@0 | 346 | if (!esg.foundScript()) { |
michael@0 | 347 | RootedScript maybeScript(cx); |
michael@0 | 348 | const char *filename; |
michael@0 | 349 | unsigned lineno; |
michael@0 | 350 | JSPrincipals *originPrincipals; |
michael@0 | 351 | uint32_t pcOffset; |
michael@0 | 352 | DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset, |
michael@0 | 353 | &originPrincipals, CALLED_FROM_JSOP_EVAL); |
michael@0 | 354 | |
michael@0 | 355 | const char *introducerFilename = filename; |
michael@0 | 356 | if (maybeScript && maybeScript->scriptSource()->introducerFilename()) |
michael@0 | 357 | introducerFilename = maybeScript->scriptSource()->introducerFilename(); |
michael@0 | 358 | |
michael@0 | 359 | CompileOptions options(cx); |
michael@0 | 360 | options.setFileAndLine(filename, 1) |
michael@0 | 361 | .setCompileAndGo(true) |
michael@0 | 362 | .setForEval(true) |
michael@0 | 363 | .setNoScriptRval(false) |
michael@0 | 364 | .setOriginPrincipals(originPrincipals) |
michael@0 | 365 | .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset); |
michael@0 | 366 | SourceBufferHolder srcBuf(chars.get(), length, SourceBufferHolder::NoOwnership); |
michael@0 | 367 | JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(), |
michael@0 | 368 | scopeobj, callerScript, options, |
michael@0 | 369 | srcBuf, flatStr, staticLevel); |
michael@0 | 370 | if (!compiled) |
michael@0 | 371 | return false; |
michael@0 | 372 | |
michael@0 | 373 | esg.setNewScript(compiled); |
michael@0 | 374 | } |
michael@0 | 375 | |
michael@0 | 376 | // Primitive 'this' values should have been filtered out by Ion. If boxed, |
michael@0 | 377 | // the calling frame cannot be updated to store the new object. |
michael@0 | 378 | JS_ASSERT(thisValue.isObject() || thisValue.isUndefined() || thisValue.isNull()); |
michael@0 | 379 | |
michael@0 | 380 | return ExecuteKernel(cx, esg.script(), *scopeobj, thisValue, ExecuteType(DIRECT_EVAL), |
michael@0 | 381 | NullFramePtr() /* evalInFrame */, vp.address()); |
michael@0 | 382 | } |
michael@0 | 383 | |
michael@0 | 384 | bool |
michael@0 | 385 | js::DirectEvalValueFromIon(JSContext *cx, |
michael@0 | 386 | HandleObject scopeobj, HandleScript callerScript, |
michael@0 | 387 | HandleValue thisValue, HandleValue evalArg, |
michael@0 | 388 | jsbytecode *pc, MutableHandleValue vp) |
michael@0 | 389 | { |
michael@0 | 390 | // Act as identity on non-strings per ES5 15.1.2.1 step 1. |
michael@0 | 391 | if (!evalArg.isString()) { |
michael@0 | 392 | vp.set(evalArg); |
michael@0 | 393 | return true; |
michael@0 | 394 | } |
michael@0 | 395 | |
michael@0 | 396 | RootedString string(cx, evalArg.toString()); |
michael@0 | 397 | return DirectEvalStringFromIon(cx, scopeobj, callerScript, thisValue, string, pc, vp); |
michael@0 | 398 | } |
michael@0 | 399 | |
michael@0 | 400 | bool |
michael@0 | 401 | js::IndirectEval(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 402 | { |
michael@0 | 403 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 404 | Rooted<GlobalObject*> global(cx, &args.callee().global()); |
michael@0 | 405 | return EvalKernel(cx, args, INDIRECT_EVAL, NullFramePtr(), global, nullptr); |
michael@0 | 406 | } |
michael@0 | 407 | |
michael@0 | 408 | bool |
michael@0 | 409 | js::DirectEval(JSContext *cx, const CallArgs &args) |
michael@0 | 410 | { |
michael@0 | 411 | // Direct eval can assume it was called from an interpreted or baseline frame. |
michael@0 | 412 | ScriptFrameIter iter(cx); |
michael@0 | 413 | AbstractFramePtr caller = iter.abstractFramePtr(); |
michael@0 | 414 | |
michael@0 | 415 | JS_ASSERT(caller.scopeChain()->global().valueIsEval(args.calleev())); |
michael@0 | 416 | JS_ASSERT(JSOp(*iter.pc()) == JSOP_EVAL || |
michael@0 | 417 | JSOp(*iter.pc()) == JSOP_SPREADEVAL); |
michael@0 | 418 | JS_ASSERT_IF(caller.isFunctionFrame(), |
michael@0 | 419 | caller.compartment() == caller.callee()->compartment()); |
michael@0 | 420 | |
michael@0 | 421 | RootedObject scopeChain(cx, caller.scopeChain()); |
michael@0 | 422 | return EvalKernel(cx, args, DIRECT_EVAL, caller, scopeChain, iter.pc()); |
michael@0 | 423 | } |
michael@0 | 424 | |
michael@0 | 425 | bool |
michael@0 | 426 | js::IsAnyBuiltinEval(JSFunction *fun) |
michael@0 | 427 | { |
michael@0 | 428 | return fun->maybeNative() == IndirectEval; |
michael@0 | 429 | } |