bool ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, T *re, JSLinearString *input, const jschar *chars, size_t length, size_t *lastIndex, RegExpExecType type, Value *rval) { LifoAllocScope allocScope(&cx->tempLifoAlloc()); MatchPairs *matchPairs = NULL; RegExpRunStatus status = re->execute(cx, chars, length, lastIndex, allocScope, &matchPairs); switch (status) { case RegExpRunStatus_Error: return false; case RegExpRunStatus_Success_NotFound: *rval = NullValue(); return true; default: JS_ASSERT(status == RegExpRunStatus_Success); JS_ASSERT(matchPairs); } if (res) res->updateFromMatchPairs(cx, input, matchPairs); *lastIndex = matchPairs->pair(0).limit; if (type == RegExpTest) { *rval = BooleanValue(true); return true; } return CreateRegExpMatchResult(cx, input, chars, length, matchPairs, rval); }
bool MatchPairs::initArrayFrom(MatchPairs& copyFrom) { MOZ_ASSERT(copyFrom.pairCount() > 0); if (!allocOrExpandArray(copyFrom.pairCount())) return false; PodCopy(pairs_, copyFrom.pairs_, pairCount_); return true; }
RegExpRunStatus RegExpShared::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex, MatchPairs &matches) { /* Protect inlined chars from root analysis poisoning. */ SkipRoot skip(cx, &chars); /* 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(cx, bytecode, chars, length, start, outputBuf); else result = codeBlock.execute(chars, start, length, (int *)outputBuf).start; #else result = JSC::Yarr::interpret(cx, bytecode, chars, 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 MatchPairs::initArrayFrom(MatchPairs ©From) { JS_ASSERT(copyFrom.pairCount() > 0); if (!allocOrExpandArray(copyFrom.pairCount())) return false; for (size_t i = 0; i < pairCount_; i++) { JS_ASSERT(copyFrom[i].check()); pairs_[i].start = copyFrom[i].start; pairs_[i].limit = copyFrom[i].limit; } return true; }
RegExpRunStatus RegExpShared::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex, MatchPairs **output) { const size_t origLength = length; size_t backingPairCount = RegExpCode::getOutputSize(pairCount()); LifoAlloc &alloc = cx->tempLifoAlloc(); MatchPairs *matchPairs = MatchPairs::create(alloc, pairCount(), backingPairCount); if (!matchPairs) 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 start = *lastIndex; size_t displacement = 0; if (sticky()) { displacement = *lastIndex; chars += displacement; length -= displacement; start = 0; } RegExpRunStatus status = code.execute(cx, chars, length, start, matchPairs->buffer(), backingPairCount); switch (status) { case RegExpRunStatus_Error: return status; case RegExpRunStatus_Success_NotFound: *output = matchPairs; return status; default: JS_ASSERT(status == RegExpRunStatus_Success); } matchPairs->displace(displacement); matchPairs->checkAgainst(origLength); *lastIndex = matchPairs->pair(0).limit; *output = matchPairs; return RegExpRunStatus_Success; }
bool js::CreateRegExpMatchResult(JSContext *cx, HandleString input_, const jschar *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, 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; }
bool js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& matches, MutableHandleValue rval) { MOZ_ASSERT(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 */ /* Get the templateObject that defines the shape and type of the output object */ JSObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx); if (!templateObject) return false; size_t numPairs = matches.length(); MOZ_ASSERT(numPairs > 0); RootedArrayObject arr(cx, NewDenseFullyAllocatedArrayWithTemplate(cx, numPairs, templateObject)); if (!arr) return false; /* Store a Value for each pair. */ for (size_t i = 0; i < numPairs; i++) { const MatchPair& pair = matches[i]; if (pair.isUndefined()) { MOZ_ASSERT(i != 0); /* Since we had a match, first pair must be present. */ arr->setDenseInitializedLength(i + 1); arr->initDenseElement(i, UndefinedValue()); } else { JSLinearString* str = NewDependentString(cx, input, pair.start, pair.length()); if (!str) return false; arr->setDenseInitializedLength(i + 1); arr->initDenseElement(i, StringValue(str)); } } /* Set the |index| property. (TemplateObject positions it in slot 0) */ arr->setSlot(0, Int32Value(matches[0].start)); /* Set the |input| property. (TemplateObject positions it in slot 1) */ arr->setSlot(1, StringValue(input)); #ifdef DEBUG RootedValue test(cx); RootedId id(cx, NameToId(cx->names().index)); if (!NativeGetProperty(cx, arr, id, &test)) return false; MOZ_ASSERT(test == arr->getSlot(0)); id = NameToId(cx->names().input); if (!NativeGetProperty(cx, arr, id, &test)) return false; MOZ_ASSERT(test == arr->getSlot(1)); #endif rval.setObject(*arr); return true; }
bool js::CreateRegExpMatchResult(JSContext *cx, HandleString input_, const jschar *chars, size_t length, MatchPairs &matches, MutableHandleValue rval) { RootedString input(cx, input_); RootedValue undefinedValue(cx, UndefinedValue()); /* * 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 */ if (!input) { input = js_NewStringCopyN<CanGC>(cx, chars, length); if (!input) return false; } size_t numPairs = matches.length(); JS_ASSERT(numPairs > 0); AutoValueVector elements(cx); if (!elements.reserve(numPairs)) return false; /* Accumulate a Value for each pair, in a rooted vector. */ for (size_t i = 0; i < numPairs; ++i) { const MatchPair &pair = matches[i]; if (pair.isUndefined()) { JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */ elements.infallibleAppend(undefinedValue); } else { JSLinearString *str = js_NewDependentString(cx, input, pair.start, pair.length()); if (!str) return false; elements.infallibleAppend(StringValue(str)); } } /* Copy the rooted vector into the array object. */ RootedObject array(cx, NewDenseCopiedArray(cx, elements.length(), elements.begin())); if (!array) return false; /* Set the |index| property. */ RootedValue index(cx, Int32Value(matches[0].start)); if (!DefinePropertyHelper(cx, array, cx->names().index, index)) return false; /* Set the |input| property. */ RootedValue inputVal(cx, StringValue(input)); if (!DefinePropertyHelper(cx, array, cx->names().input, inputVal)) return false; rval.setObject(*array); return true; }
RegExpRunStatus RegExpShared::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex, MatchPairs &matches) { TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); /* Compile the code at point-of-use. */ if (!compileIfNecessary(cx, chars, length)) 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; } #ifndef JS_YARR // Reset the Irregexp backtrack stack if it grows during execution. irregexp::RegExpStackScope stackScope(cx->runtime()); #endif if (canStringMatch) { int res = StringFindPattern(chars+start, length-start, source->chars(), source->length()); if (res == -1) return RegExpRunStatus_Success_NotFound; matches[0].start = res + start; matches[0].limit = res + start + source->length(); matches.displace(displacement); matches.checkAgainst(origLength); *lastIndex = matches[0].limit; return RegExpRunStatus_Success; } #ifdef JS_YARR unsigned result; // Yarr wants plain integers for its output buffer (whatever). JS_STATIC_ASSERT(sizeof(int32_t) == sizeof(int)); JS_STATIC_ASSERT(sizeof(int32_t) == sizeof(unsigned)); #ifdef JS_ION if (codeBlock.isFallBack()) { AutoTraceLog logInterpret(logger, TraceLogger::YarrInterpret); result = JSC::Yarr::interpret(cx, bytecode, chars, length, start, (unsigned *) matches.pairsRaw()); } else { AutoTraceLog logJIT(logger, TraceLogger::YarrJIT); result = codeBlock.execute(chars, start, length, (int *) matches.pairsRaw()).start; } #else // JS_ION { AutoTraceLog logInterpret(logger, TraceLogger::YarrInterpret); result = JSC::Yarr::interpret(cx, bytecode, chars, length, start, (unsigned *) matches.pairsRaw()); } #endif // JS_ION if (result == JSC::Yarr::offsetError) { reportYarrError(cx, nullptr, JSC::Yarr::RuntimeError); return RegExpRunStatus_Error; } if (result == JSC::Yarr::offsetNoMatch) return RegExpRunStatus_Success_NotFound; #else // JS_YARR if (hasByteCode()) { AutoTraceLog logInterpreter(logger, TraceLogger::IrregexpExecute); RegExpRunStatus result = irregexp::InterpretCode(cx, byteCode, chars, start, length, &matches); if (result == RegExpRunStatus_Success) { matches.displace(displacement); matches.checkAgainst(origLength); *lastIndex = matches[0].limit; } return result; } #ifdef JS_ION while (true) { RegExpRunStatus result; { AutoTraceLog logJIT(logger, TraceLogger::IrregexpExecute); result = irregexp::ExecuteCode(cx, jitCode, chars, start, length, &matches); } if (result == RegExpRunStatus_Error) { // The RegExp engine might exit with an exception if an interrupt // was requested. Check this case and retry until a clean result is // obtained. bool interrupted; { JSRuntime::AutoLockForInterrupt lock(cx->runtime()); interrupted = cx->runtime()->interrupt; } if (interrupted) { if (!InvokeInterruptCallback(cx)) return RegExpRunStatus_Error; continue; } js_ReportOverRecursed(cx); return RegExpRunStatus_Error; } if (result == RegExpRunStatus_Success_NotFound) return RegExpRunStatus_Success_NotFound; JS_ASSERT(result == RegExpRunStatus_Success); break; } #else // JS_ION MOZ_CRASH(); #endif // JS_ION #endif // JS_YARR matches.displace(displacement); matches.checkAgainst(origLength); *lastIndex = matches[0].limit; return RegExpRunStatus_Success; }
RegExpRunStatus RegExpShared::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex, MatchPairs &matches) { TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); { /* Compile the code at point-of-use. */ AutoTraceLog logCompile(logger, TraceLogger::YarrCompile); 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()) { AutoTraceLog logInterpret(logger, TraceLogger::YarrInterpret); result = JSC::Yarr::interpret(cx, bytecode, chars, length, start, outputBuf); } else { AutoTraceLog logJIT(logger, TraceLogger::YarrJIT); result = codeBlock.execute(chars, start, length, (int *)outputBuf).start; } #else { AutoTraceLog logInterpret(logger, TraceLogger::YarrInterpret); result = JSC::Yarr::interpret(cx, bytecode, chars, length, start, outputBuf); } #endif if (result == JSC::Yarr::offsetError) { reportYarrError(cx, nullptr, JSC::Yarr::RuntimeError); return RegExpRunStatus_Error; } if (result == JSC::Yarr::offsetNoMatch) return RegExpRunStatus_Success_NotFound; matches.displace(displacement); matches.checkAgainst(origLength); *lastIndex = matches[0].limit; return RegExpRunStatus_Success; }