bool js::CreateRegExpMatchResult(JSContext *cx, HandleString string, MatchPairs &matches, MutableHandleValue rval) { Rooted<JSLinearString*> input(cx, string->ensureLinear(cx)); if (!input) return false; return CreateRegExpMatchResult(cx, input, input->chars(), input->length(), matches, rval); }
bool js::DirectEvalStringFromIon(JSContext* cx, HandleObject scopeobj, HandleScript callerScript, HandleValue thisValue, HandleValue newTargetValue, HandleString str, jsbytecode* pc, MutableHandleValue vp) { 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 steps 2-8. unsigned staticLevel = callerScript->staticLevel() + 1; RootedLinearString linearStr(cx, str->ensureLinear(cx)); if (!linearStr) return false; EvalJSONResult ejr = TryEvalJSON(cx, linearStr, vp); if (ejr != EvalJSON_NotJSON) return ejr == EvalJSON_Success; EvalScriptGuard esg(cx); esg.lookupInEvalCache(linearStr, callerScript, pc); if (!esg.foundScript()) { RootedScript maybeScript(cx); const char* filename; unsigned lineno; bool mutedErrors; uint32_t pcOffset; DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset, &mutedErrors, CALLED_FROM_JSOP_EVAL); const char* introducerFilename = filename; if (maybeScript && maybeScript->scriptSource()->introducerFilename()) introducerFilename = maybeScript->scriptSource()->introducerFilename(); RootedObject enclosing(cx, callerScript->innermostStaticScope(pc)); 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(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, staticLevel); if (!compiled) return false; if (compiled->strict()) staticScope->setStrict(); esg.setNewScript(compiled); } // Primitive 'this' values should have been filtered out by Ion. If boxed, // the calling frame cannot be updated to store the new object. MOZ_ASSERT(thisValue.isObject() || thisValue.isUndefined() || thisValue.isNull()); // When eval'ing strict code in a non-strict context, compute the 'this' // value to use from what the caller passed in. This isn't necessary if // the callee is not strict, as it will compute the non-strict 'this' // value as necessary while it executes. RootedValue nthisValue(cx, thisValue); if (!callerScript->strict() && esg.script()->strict() && !thisValue.isObject()) { JSObject* obj = BoxNonStrictThis(cx, thisValue); if (!obj) return false; nthisValue = ObjectValue(*obj); } return ExecuteKernel(cx, esg.script(), *scopeobj, nthisValue, newTargetValue, ExecuteType(DIRECT_EVAL), NullFramePtr() /* evalInFrame */, vp.address()); }
bool js::DirectEvalStringFromIon(JSContext* cx, HandleObject scopeObj, HandleScript callerScript, HandleValue newTargetValue, HandleString str, jsbytecode* pc, MutableHandleValue vp) { 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 steps 2-8. RootedLinearString linearStr(cx, str->ensureLinear(cx)); if (!linearStr) return false; EvalJSONResult ejr = TryEvalJSON(cx, linearStr, vp); if (ejr != EvalJSON_NotJSON) return ejr == EvalJSON_Success; EvalScriptGuard esg(cx); esg.lookupInEvalCache(linearStr, callerScript, pc); if (!esg.foundScript()) { RootedScript maybeScript(cx); const char* filename; unsigned lineno; bool mutedErrors; uint32_t pcOffset; DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset, &mutedErrors, CALLED_FROM_JSOP_EVAL); const char* introducerFilename = filename; if (maybeScript && maybeScript->scriptSource()->introducerFilename()) introducerFilename = maybeScript->scriptSource()->introducerFilename(); RootedObject enclosing(cx, callerScript->innermostStaticScope(pc)); 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(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); } return ExecuteKernel(cx, esg.script(), *scopeObj, newTargetValue, NullFramePtr() /* evalInFrame */, vp.address()); }
/* ES6 final draft 21.2.5.2.2. */ RegExpRunStatus js::ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string, MatchPairs* matches, RegExpStaticsUpdate staticsUpdate) { /* * WARNING: Despite the presence of spec step comment numbers, this * algorithm isn't consistent with any ES6 version, draft or * otherwise. YOU HAVE BEEN WARNED. */ /* Steps 1-2 performed by the caller. */ Rooted<RegExpObject*> reobj(cx, ®exp->as<RegExpObject>()); RegExpGuard re(cx); if (!reobj->getShared(cx, &re)) return RegExpRunStatus_Error; RegExpStatics* res; if (staticsUpdate == UpdateRegExpStatics) { res = cx->global()->getRegExpStatics(cx); if (!res) return RegExpRunStatus_Error; } else { res = nullptr; } RootedLinearString input(cx, string->ensureLinear(cx)); if (!input) return RegExpRunStatus_Error; /* Step 3. */ size_t length = input->length(); /* Steps 4-5. */ RootedValue lastIndex(cx, reobj->getLastIndex()); int searchIndex; if (lastIndex.isInt32()) { /* Aggressively avoid doubles. */ searchIndex = lastIndex.toInt32(); } else { double d; if (!ToInteger(cx, lastIndex, &d)) return RegExpRunStatus_Error; /* Inlined steps 6-10, 15.a with doubles to detect failure case. */ if (reobj->needUpdateLastIndex() && (d < 0 || d > length)) { /* Steps 15.a.i-ii. */ if (!SetLastIndex(cx, reobj, 0)) return RegExpRunStatus_Error; /* Step 15.a.iii. */ return RegExpRunStatus_Success_NotFound; } searchIndex = int(d); } /* * Steps 6-10. * * Also make sure that we have a MatchPairs for regexps which update their * last index, as we won't compute the last index otherwise. */ Maybe<ScopedMatchPairs> alternateMatches; if (!reobj->needUpdateLastIndex()) { searchIndex = 0; } else if (!matches) { alternateMatches.emplace(&cx->tempLifoAlloc()); matches = &alternateMatches.ref(); } /* Step 15.a. */ if (searchIndex < 0 || size_t(searchIndex) > length) { /* Steps 15.a.i-ii. */ if (!SetLastIndex(cx, reobj, 0)) return RegExpRunStatus_Error; /* Step 15.a.iii. */ return RegExpRunStatus_Success_NotFound; } /* Steps 12-13. */ if (reobj->unicode()) { /* * ES6 21.2.2.2 step 2. * Let listIndex be the index into Input of the character that was * obtained from element index of str. * * In the spec, pattern match is performed with decoded Unicode code * points, but our implementation performs it with UTF-16 encoded * string. In step 2, we should decrement searchIndex (index) if it * points the trail surrogate that has corresponding lead surrogate. * * var r = /\uD83D\uDC38/ug; * r.lastIndex = 1; * var str = "\uD83D\uDC38"; * var result = r.exec(str); // pattern match starts from index 0 * print(result.index); // prints 0 * * Note: this doesn't match the current spec text and result in * different values for `result.index` under certain conditions. * However, the spec will change to match our implementation's * behavior. See https://github.com/tc39/ecma262/issues/128. */ if (IsTrailSurrogateWithLeadSurrogate(cx, input, searchIndex)) searchIndex--; } /* Step 14-29. */ RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, searchIndex, matches); if (status == RegExpRunStatus_Error) return RegExpRunStatus_Error; if (status == RegExpRunStatus_Success_NotFound) { /* Steps 15.a.i-ii. */ if (!SetLastIndex(cx, reobj, 0)) return RegExpRunStatus_Error; } else if (reobj->needUpdateLastIndex()) { /* Steps 18.a-b. */ MOZ_ASSERT(matches && !matches->empty()); if (!SetLastIndex(cx, reobj, (*matches)[0].limit)) return RegExpRunStatus_Error; } return status; }