RegExpRunStatus RegExpShared::executeMatchOnly(JSContext *cx, StableCharPtr chars, size_t length, size_t *lastIndex, MatchPair &match) { /* Compile the code at point-of-use. */ if (!compileMatchOnlyIfNecessary(cx)) return RegExpRunStatus_Error; const size_t origLength = length; size_t start = *lastIndex; size_t displacement = 0; if (sticky()) { displacement = start; chars += displacement; length -= displacement; start = 0; } #if ENABLE_YARR_JIT if (!codeBlock.isFallBack()) { MatchResult result = codeBlock.execute(chars.get(), start, length); if (!result) return RegExpRunStatus_Success_NotFound; match = MatchPair(result.start, result.end); match.displace(displacement); *lastIndex = match.limit; return RegExpRunStatus_Success; } #endif /* * The JIT could not be used, so fall back to the Yarr interpreter. * Unfortunately, the interpreter does not have a MatchOnly mode, so a * temporary output vector must be provided. */ JS_ASSERT(hasBytecode()); ScopedMatchPairs matches(&cx->tempLifoAlloc()); if (!matches.initArray(pairCount())) return RegExpRunStatus_Error; unsigned result = JSC::Yarr::interpret(bytecode, chars.get(), length, start, matches.rawBuf()); if (result == JSC::Yarr::offsetNoMatch) return RegExpRunStatus_Success_NotFound; matches.displace(displacement); matches.checkAgainst(origLength); *lastIndex = matches[0].limit; match = MatchPair(result, matches[0].limit); return RegExpRunStatus_Success; }
RegExpRunStatus RegExpShared::execute(JSContext *cx, StableCharPtr chars, size_t length, size_t *lastIndex, MatchPairs &matches) { /* Compile the code at point-of-use. */ if (!compileIfNecessary(cx)) return RegExpRunStatus_Error; /* Ensure sufficient memory for output vector. */ if (!matches.initArray(pairCount())) return RegExpRunStatus_Error; /* * |displacement| emulates sticky mode by matching from this offset * into the char buffer and subtracting the delta off at the end. */ size_t origLength = length; size_t start = *lastIndex; size_t displacement = 0; if (sticky()) { displacement = start; chars += displacement; length -= displacement; start = 0; } unsigned *outputBuf = matches.rawBuf(); unsigned result; #if ENABLE_YARR_JIT if (codeBlock.isFallBack()) result = JSC::Yarr::interpret(bytecode, chars.get(), length, start, outputBuf); else result = codeBlock.execute(chars.get(), start, length, (int *)outputBuf).start; #else result = JSC::Yarr::interpret(bytecode, chars.get(), length, start, outputBuf); #endif if (result == JSC::Yarr::offsetNoMatch) return RegExpRunStatus_Success_NotFound; matches.displace(displacement); matches.checkAgainst(origLength); *lastIndex = matches[0].limit; return RegExpRunStatus_Success; }
bool js::CreateRegExpMatchResult(JSContext *cx, HandleString input_, StableCharPtr chars, size_t length, MatchPairs &matches, MutableHandleValue rval) { RootedString 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 */ RootedObject array(cx, NewDenseEmptyArray(cx)); if (!array) return false; if (!input) { input = js_NewStringCopyN<CanGC>(cx, chars.get(), length); if (!input) return false; } RegExpMatchBuilder builder(cx, array); RootedValue undefinedValue(cx, UndefinedValue()); size_t numPairs = matches.length(); JS_ASSERT(numPairs > 0); for (size_t i = 0; i < numPairs; ++i) { const MatchPair &pair = matches[i]; RootedString captured(cx); if (pair.isUndefined()) { JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */ if (!builder.append(i, undefinedValue)) return false; } else { captured = js_NewDependentString(cx, input, pair.start, pair.length()); RootedValue value(cx, StringValue(captured)); if (!captured || !builder.append(i, value)) return false; } } if (!builder.setIndex(matches[0].start) || !builder.setInput(input)) return false; rval.setObject(*array); return true; }
RegExpRunStatus RegExpCode::execute(JSContext *cx, StableCharPtr chars, size_t length, size_t start, int *output, size_t outputCount) { int result; #if ENABLE_YARR_JIT (void) cx; /* Unused. */ if (codeBlock.isFallBack()) result = JSC::Yarr::interpret(byteCode, chars.get(), start, length, output); else result = JSC::Yarr::execute(codeBlock, chars.get(), start, length, output); #else result = JSC::Yarr::interpret(byteCode, chars.get(), start, length, output); #endif if (result == -1) return RegExpRunStatus_Success_NotFound; JS_ASSERT(result >= 0); return RegExpRunStatus_Success; }
RegExpRunStatus RegExpCode::execute(JSContext *cx, StableCharPtr chars, size_t length, size_t start, int *output, size_t outputCount) { unsigned result; #if ENABLE_YARR_JIT (void) cx; /* Unused. */ if (codeBlock.isFallBack()) { result = JSC::Yarr::interpret(byteCode, chars.get(), length, start, reinterpret_cast<unsigned *>(output)); } else { result = codeBlock.execute(chars.get(), start, length, output).start; } #else result = JSC::Yarr::interpret(byteCode, chars.get(), length, start, reinterpret_cast<unsigned *>(output)); #endif if (result == JSC::Yarr::offsetNoMatch) return RegExpRunStatus_Success_NotFound; return RegExpRunStatus_Success; }
bool frontend::ParseScript(JSContext *cx, HandleObject scopeChain, const CompileOptions &options, StableCharPtr chars, size_t length) { if (!CheckLength(cx, length)) return false; Parser<SyntaxParseHandler> parser(cx, options, chars.get(), length, /* foldConstants = */ false); if (!parser.init()) { cx->clearPendingException(); return false; } GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx)); ParseContext<SyntaxParseHandler> pc(&parser, NULL, &globalsc, 0, /* bodyid = */ 0); if (!pc.init()) { cx->clearPendingException(); return false; } for (;;) { TokenKind tt = parser.tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); cx->clearPendingException(); return false; } if (!parser.statement()) { cx->clearPendingException(); return false; } } return true; }
bool JSStructuredCloneReader::startRead(Value *vp) { uint32_t tag, data; if (!in.readPair(&tag, &data)) return false; switch (tag) { case SCTAG_NULL: vp->setNull(); break; case SCTAG_UNDEFINED: vp->setUndefined(); break; case SCTAG_BOOLEAN: case SCTAG_BOOLEAN_OBJECT: vp->setBoolean(!!data); if (tag == SCTAG_BOOLEAN_OBJECT && !js_PrimitiveToObject(context(), vp)) return false; break; case SCTAG_STRING: case SCTAG_STRING_OBJECT: { JSString *str = readString(data); if (!str) return false; vp->setString(str); if (tag == SCTAG_STRING_OBJECT && !js_PrimitiveToObject(context(), vp)) return false; break; } case SCTAG_NUMBER_OBJECT: { double d; if (!in.readDouble(&d) || !checkDouble(d)) return false; vp->setDouble(d); if (!js_PrimitiveToObject(context(), vp)) return false; break; } case SCTAG_DATE_OBJECT: { double d; if (!in.readDouble(&d) || !checkDouble(d)) return false; if (!IsNaN(d) && d != TimeClip(d)) { JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "date"); return false; } JSObject *obj = js_NewDateObjectMsec(context(), d); if (!obj) return false; vp->setObject(*obj); break; } case SCTAG_REGEXP_OBJECT: { RegExpFlag flags = RegExpFlag(data); uint32_t tag2, nchars; if (!in.readPair(&tag2, &nchars)) return false; if (tag2 != SCTAG_STRING) { JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "regexp"); return false; } JSString *str = readString(nchars); if (!str) return false; JSStableString *stable = str->ensureStable(context()); if (!stable) return false; size_t length = stable->length(); const StableCharPtr chars = stable->chars(); RegExpObject *reobj = RegExpObject::createNoStatics(context(), chars.get(), length, flags, NULL); if (!reobj) return false; vp->setObject(*reobj); break; } case SCTAG_ARRAY_OBJECT: case SCTAG_OBJECT_OBJECT: { JSObject *obj = (tag == SCTAG_ARRAY_OBJECT) ? NewDenseEmptyArray(context()) : NewBuiltinClassInstance(context(), &ObjectClass); if (!obj || !objs.append(ObjectValue(*obj))) return false; vp->setObject(*obj); break; } case SCTAG_BACK_REFERENCE_OBJECT: { if (data >= allObjs.length()) { JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "invalid back reference in input"); return false; } *vp = allObjs[data]; return true; } case SCTAG_TRANSFER_MAP_HEADER: // A map header cannot be here but just at the beginning of the buffer. JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "invalid input"); return false; case SCTAG_TRANSFER_MAP: // A map cannot be here but just at the beginning of the buffer. JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "invalid input"); return false; case SCTAG_ARRAY_BUFFER_OBJECT: if (!readArrayBuffer(data, vp)) return false; break; case SCTAG_TYPED_ARRAY_OBJECT: // readTypedArray adds the array to allObjs uint64_t arrayType; if (!in.read(&arrayType)) return false; return readTypedArray(arrayType, data, vp); break; default: { if (tag <= SCTAG_FLOAT_MAX) { double d = ReinterpretPairAsDouble(tag, data); if (!checkDouble(d)) return false; vp->setNumber(d); break; } if (SCTAG_TYPED_ARRAY_V1_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_V1_MAX) { // A v1-format typed array // readTypedArray adds the array to allObjs return readTypedArray(TagToV1ArrayType(tag), data, vp, true); } if (!callbacks || !callbacks->read) { JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "unsupported type"); return false; } JSObject *obj = callbacks->read(context(), this, tag, data, closure); if (!obj) return false; vp->setObject(*obj); } } if (vp->isObject() && !allObjs.append(*vp)) return false; return true; }
bool js::DirectEvalFromIon(JSContext *cx, HandleObject scopeobj, HandleScript callerScript, HandleValue thisValue, HandleString str, MutableHandleValue vp) { 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 steps 2-8. unsigned staticLevel = callerScript->staticLevel + 1; Rooted<JSStableString*> stableStr(cx, str->ensureStable(cx)); if (!stableStr) return false; StableCharPtr chars = stableStr->chars(); size_t length = stableStr->length(); EvalJSONResult ejr = TryEvalJSON(cx, callerScript, chars, length, vp); if (ejr != EvalJSON_NotJSON) return ejr == EvalJSON_Success; EvalScriptGuard esg(cx); // Ion will not perform cross compartment direct eval calls. JSPrincipals *principals = cx->compartment->principals; esg.lookupInEvalCache(stableStr, callerScript->function(), staticLevel); if (!esg.foundScript()) { unsigned lineno; const char *filename; JSPrincipals *originPrincipals; CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals, CALLED_FROM_JSOP_EVAL); CompileOptions options(cx); options.setFileAndLine(filename, lineno) .setCompileAndGo(true) .setNoScriptRval(false) .setPrincipals(principals) .setOriginPrincipals(originPrincipals); UnrootedScript compiled = frontend::CompileScript(cx, scopeobj, callerScript, options, chars.get(), length, stableStr, staticLevel); if (!compiled) return false; 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. JS_ASSERT(thisValue.isObject() || thisValue.isUndefined() || thisValue.isNull()); return ExecuteKernel(cx, esg.script(), *scopeobj, thisValue, ExecuteType(DIRECT_EVAL), NullFramePtr() /* evalInFrame */, vp.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) { 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()); }