diff --git a/js/src/irregexp/RegExpEngine.cpp b/js/src/irregexp/RegExpEngine.cpp index 32366f343501..a24c979ed1ae 100644 --- a/js/src/irregexp/RegExpEngine.cpp +++ b/js/src/irregexp/RegExpEngine.cpp @@ -1650,7 +1650,7 @@ IsNativeRegExpEnabled(JSContext *cx) RegExpCode irregexp::CompilePattern(JSContext *cx, RegExpShared *shared, RegExpCompileData *data, HandleLinearString sample, bool is_global, bool ignore_case, - bool is_ascii, bool match_only) + bool is_ascii, bool match_only, bool force_bytecode) { if ((data->capture_count + 1) * 2 - 1 > RegExpMacroAssembler::kMaxRegister) { JS_ReportError(cx, "regexp too big"); @@ -1727,7 +1727,7 @@ irregexp::CompilePattern(JSContext *cx, RegExpShared *shared, RegExpCompileData Maybe interpreted_assembler; RegExpMacroAssembler *assembler; - if (IsNativeRegExpEnabled(cx)) { + if (IsNativeRegExpEnabled(cx) && !force_bytecode) { NativeRegExpMacroAssembler::Mode mode = is_ascii ? NativeRegExpMacroAssembler::ASCII : NativeRegExpMacroAssembler::CHAR16; diff --git a/js/src/irregexp/RegExpEngine.h b/js/src/irregexp/RegExpEngine.h index be7f25d0c16f..d177a1fa76a3 100644 --- a/js/src/irregexp/RegExpEngine.h +++ b/js/src/irregexp/RegExpEngine.h @@ -88,7 +88,7 @@ struct RegExpCode RegExpCode CompilePattern(JSContext *cx, RegExpShared *shared, RegExpCompileData *data, HandleLinearString sample, bool is_global, bool ignore_case, - bool is_ascii, bool match_only); + bool is_ascii, bool match_only, bool force_bytecode); // Note: this may return RegExpRunStatus_Error if an interrupt was requested // while the code was executing. diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index e355829e4a9d..6e2ac67120dc 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -455,14 +455,15 @@ RegExpShared::trace(JSTracer *trc) } bool -RegExpShared::compile(JSContext *cx, HandleLinearString input, CompilationMode mode) +RegExpShared::compile(JSContext *cx, HandleLinearString input, + CompilationMode mode, ForceByteCodeEnum force) { TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); AutoTraceLog logCompile(logger, TraceLogger::IrregexpCompile); if (!sticky()) { RootedAtom pattern(cx, source); - return compile(cx, pattern, input, mode); + return compile(cx, pattern, input, mode, force); } /* @@ -485,12 +486,12 @@ RegExpShared::compile(JSContext *cx, HandleLinearString input, CompilationMode m if (!fakeySource) return false; - return compile(cx, fakeySource, input, mode); + return compile(cx, fakeySource, input, mode, force); } bool RegExpShared::compile(JSContext *cx, HandleAtom pattern, HandleLinearString input, - CompilationMode mode) + CompilationMode mode, ForceByteCodeEnum force) { if (!ignoreCase() && !StringHasRegExpMetaChars(pattern)) { canStringMatch = true; @@ -517,25 +518,30 @@ RegExpShared::compile(JSContext *cx, HandleAtom pattern, HandleLinearString inpu false /* global() */, ignoreCase(), input->hasLatin1Chars(), - mode == MatchOnly); + mode == MatchOnly, + force == ForceByteCode); if (code.empty()) return false; MOZ_ASSERT(!code.jitCode || !code.byteCode); + MOZ_ASSERT_IF(force == ForceByteCode, code.byteCode); RegExpCompilation &compilation = this->compilation(mode, input->hasLatin1Chars()); - compilation.jitCode = code.jitCode; - compilation.byteCode = code.byteCode; + if (code.jitCode) + compilation.jitCode = code.jitCode; + else if (code.byteCode) + compilation.byteCode = code.byteCode; return true; } bool -RegExpShared::compileIfNecessary(JSContext *cx, HandleLinearString input, CompilationMode mode) +RegExpShared::compileIfNecessary(JSContext *cx, HandleLinearString input, + CompilationMode mode, ForceByteCodeEnum force) { - if (isCompiled(mode, input->hasLatin1Chars()) || canStringMatch) + if (isCompiled(mode, input->hasLatin1Chars(), force) || canStringMatch) return true; - return compile(cx, input, mode); + return compile(cx, input, mode, force); } RegExpRunStatus @@ -547,7 +553,7 @@ RegExpShared::execute(JSContext *cx, HandleLinearString input, size_t start, CompilationMode mode = matches ? Normal : MatchOnly; /* Compile the code at point-of-use. */ - if (!compileIfNecessary(cx, input, mode)) + if (!compileIfNecessary(cx, input, mode, DontForceByteCode)) return RegExpRunStatus_Error; /* @@ -591,35 +597,15 @@ RegExpShared::execute(JSContext *cx, HandleLinearString input, size_t start, return RegExpRunStatus_Success; } - if (uint8_t *byteCode = compilation(mode, input->hasLatin1Chars()).byteCode) { - AutoTraceLog logInterpreter(logger, TraceLogger::IrregexpExecute); + do { + jit::JitCode *code = compilation(mode, input->hasLatin1Chars()).jitCode; + if (!code) + break; - AutoStableStringChars inputChars(cx); - if (!inputChars.init(cx, input)) - return RegExpRunStatus_Error; - - RegExpRunStatus result; - if (inputChars.isLatin1()) { - const Latin1Char *chars = inputChars.latin1Range().start().get() + charsOffset; - result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches); - } else { - const char16_t *chars = inputChars.twoByteRange().start().get() + charsOffset; - result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches); - } - - if (result == RegExpRunStatus_Success && matches) { - matches->displace(displacement); - matches->checkAgainst(origLength); - } - return result; - } - - while (true) { RegExpRunStatus result; { AutoTraceLog logJIT(logger, TraceLogger::IrregexpExecute); AutoCheckCannotGC nogc; - jit::JitCode *code = compilation(mode, input->hasLatin1Chars()).jitCode; if (input->hasLatin1Chars()) { const Latin1Char *chars = input->latin1Chars(nogc) + charsOffset; result = irregexp::ExecuteCode(cx, code, chars, start, length, matches); @@ -631,8 +617,10 @@ RegExpShared::execute(JSContext *cx, HandleLinearString input, size_t start, 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. + // was requested. If this happens, break out and retry the regexp + // in the bytecode interpreter, which can execute while tolerating + // future interrupts. Otherwise, if we keep getting interrupted we + // will never finish executing the regexp. bool interrupted; { JSRuntime::AutoLockForInterrupt lock(cx->runtime()); @@ -642,7 +630,7 @@ RegExpShared::execute(JSContext *cx, HandleLinearString input, size_t start, if (interrupted) { if (!InvokeInterruptCallback(cx)) return RegExpRunStatus_Error; - continue; + break; } js_ReportOverRecursed(cx); @@ -653,14 +641,39 @@ RegExpShared::execute(JSContext *cx, HandleLinearString input, size_t start, return RegExpRunStatus_Success_NotFound; MOZ_ASSERT(result == RegExpRunStatus_Success); - break; + + if (matches) { + matches->displace(displacement); + matches->checkAgainst(origLength); + } + return RegExpRunStatus_Success; + } while (false); + + // Compile bytecode for the RegExp if necessary. + if (!compileIfNecessary(cx, input, mode, ForceByteCode)) + return RegExpRunStatus_Error; + + uint8_t *byteCode = compilation(mode, input->hasLatin1Chars()).byteCode; + AutoTraceLog logInterpreter(logger, TraceLogger::IrregexpExecute); + + AutoStableStringChars inputChars(cx); + if (!inputChars.init(cx, input)) + return RegExpRunStatus_Error; + + RegExpRunStatus result; + if (inputChars.isLatin1()) { + const Latin1Char *chars = inputChars.latin1Range().start().get() + charsOffset; + result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches); + } else { + const char16_t *chars = inputChars.twoByteRange().start().get() + charsOffset; + result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches); } - if (matches) { + if (result == RegExpRunStatus_Success && matches) { matches->displace(displacement); matches->checkAgainst(origLength); } - return RegExpRunStatus_Success; + return result; } size_t diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index 521ffe57415d..2ed572a26bcf 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -106,6 +106,11 @@ class RegExpShared MatchOnly }; + enum ForceByteCodeEnum { + DontForceByteCode, + ForceByteCode + }; + private: friend class RegExpCompartment; friend class RegExpStatics; @@ -120,7 +125,9 @@ class RegExpShared RegExpCompilation() : byteCode(nullptr) {} ~RegExpCompilation() { js_free(byteCode); } - bool compiled() const { return jitCode || byteCode; } + bool compiled(ForceByteCodeEnum force = DontForceByteCode) const { + return byteCode || (force == DontForceByteCode && jitCode); + } }; /* Source to the RegExp, for lazy compilation. */ @@ -145,10 +152,13 @@ class RegExpShared Vector tables; /* Internal functions. */ - bool compile(JSContext *cx, HandleLinearString input, CompilationMode mode); - bool compile(JSContext *cx, HandleAtom pattern, HandleLinearString input, CompilationMode mode); + bool compile(JSContext *cx, HandleLinearString input, + CompilationMode mode, ForceByteCodeEnum force); + bool compile(JSContext *cx, HandleAtom pattern, HandleLinearString input, + CompilationMode mode, ForceByteCodeEnum force); - bool compileIfNecessary(JSContext *cx, HandleLinearString input, CompilationMode mode); + bool compileIfNecessary(JSContext *cx, HandleLinearString input, + CompilationMode mode, ForceByteCodeEnum force); const RegExpCompilation &compilation(CompilationMode mode, bool latin1) const { return compilationArray[CompilationIndex(mode, latin1)]; @@ -189,8 +199,9 @@ class RegExpShared bool multiline() const { return flags & MultilineFlag; } bool sticky() const { return flags & StickyFlag; } - bool isCompiled(CompilationMode mode, bool latin1) const { - return compilation(mode, latin1).compiled(); + bool isCompiled(CompilationMode mode, bool latin1, + ForceByteCodeEnum force = DontForceByteCode) const { + return compilation(mode, latin1).compiled(force); } bool isCompiled() const { return isCompiled(Normal, true) || isCompiled(Normal, false)