Compare commits

...
Author SHA1 Message Date
Moonchild ab605333e0 Bypass debug assert 2022-10-04 13:24:13 +00:00
Moonchild 72eaed8a9b Bug 1629670 - Part 2: Tier up to compiled regexps 2022-10-04 13:23:50 +00:00
Moonchild fe90c21ba4 Fix debug assertions. 2022-10-03 20:00:02 +00:00
Moonchild 298dcabea7 Fix leftover DotAllFlag 2022-10-02 02:13:20 +00:00
Moonchild 7ab5861a26 Bug 1629670 - Part 1: Change ForceByteCode to CodeKind
The current ForceByteCodeEnum is a glorified boolean. This patch replaces it
with a three-value bytecode/jitcode/either enum, which will make our tiering-up
logic slightly nicer in the next patch.

Tag #3016
2022-10-01 20:58:07 +00:00
Moonchild e2f692d743 Bug 1628835 - Part 4: Align RegExpRunStatus enum with V8 internals.
Tag #3016
2022-10-01 20:57:31 +00:00
Moonchild abc1333598 Bug 1628835 - Part 3: Add interpreter support
The irregexp compiler takes the AST produced by the parser, compiles it down to
a more efficient internal representation, then uses a 'macroassembler' to
generate code.

The generated code can either be bytecode (which is then fed into the
interpreter) or jitcode (which can be executed directly). This patch
gets the infrastructure set up and handles the former case.

CompilePattern is based heavily on V8's `RegExpImpl::compile`

When we import a future change to irregexp, we'll be able to pass
`matches->pairsRaw()` directly into `MatchForCallFromRuntime`, and
the interpreter will fill it in for us.
In the old engine, we could handle interrupts in the middle of the interpreter.
If we hit an urgent interrupt in compiled code, we would generate bytecode
and fall back to the interpreter (This is what all the `ForceByteCode` machinery
in RegExpObject.cpp is about. It was added in bug 1077514.)
That won't work in the new regexp version. V8 _does_ allow interrupts during
regexp execution, but only by jumping through some scary hoops to "manually
relocate unhandlified references" afterwards... that just sounds like a really
bad idea.
Instead, we just retry the regexp up to 4 times before failing to handle
urgent interruptions, which seems a reasonable amount.
2022-10-01 20:56:58 +00:00
Moonchild 8117870eaf Bug 1628835 - Part 2: Use RegExpShared in JSRegExp
In preparation for actually compiling regexps in the next patch, hook up
the V8 and irregexp representations of a compiled regexp.

Tag mcp-graveyard/UXP#1800
2022-10-01 20:56:07 +00:00
Moonchild 5bab589ccb Bug 1627356 - Remove MatchOnly mode
MatchOnly mode is a hack for benchmark score improvements.
Instead of rewriting it for our new irregexp import, I'm just removing it.

Tag mcp-graveyard/UXP#1800.
2022-10-01 20:55:36 +00:00
Moonchild 0d3fc7f8a1 Bug 1628835 - Part 1: Change parenCount to pairCount + whitespace fixes.
A regexp with N sets of capturing parens will have N+1 capture groups, with the
extra capture containing the entire matching string. Our old implementation
stored `parenCount` in the RegExpShared and then added 1 to it whenever it was
used. A much simpler answer is to just add 1 when initializing the regexp.

We do this here while renaming.
This also fixes some whitespace issues from the previous patch.

Tag mcp-graveyard/UXP#1800
2022-10-01 20:54:40 +00:00
Moonchild 40327a14c9 Bug 1627838 - Part 2: Simple string matches
The old version only supported matching the exact pattern atom.
This version supports patterns with escape characters (like `/\u212a/`).

The Boyer-Moore heuristic code is taken from here:
https://github.com/v8/v8/blob/8e4a5e973e076adfdc035cfe1d0115f047709160/src/
  regexp/regexp.cc#L116-L136

Tag mcp-graveyard/UXP#1800
2022-10-01 20:54:08 +00:00
Moonchild a28c4a53e2 Bug 1627838 - Part 1: Factor out RegExpShared::executeAtom
This code is the same for both the new and the old version.
We use `patternAtom` because the new version will add the ability to search
for an atom that is not identical to the pattern source (for example, a
pattern containing a unicode escape).

Tag mcp-graveyard/UXP#1800
2022-10-01 20:53:45 +00:00
Moonchild c4b6831720 Issue mcp-graveyard/UXP#1800 - Use c++17 std::*_t and std::*_v type traits instead of classes.
This is loosely based on the changes in Bug 1628751 and Bug 1628389
2022-10-01 20:53:19 +00:00
Moonchild af1d8cd182 Bug 1626713: Part 4: Hook up syntax errors.
Tag mcp-graveyard/UXP#1800
2022-10-01 20:52:58 +00:00
Moonchild b2816e1d45 [no issue] Properly hook up JS ErrorReporting.
For some reason this was not done properly the first time around leaving a
half-assed duplicate copy in ErrorReporting.h/cpp that wasn't hooked up to
the build system properly. As a result the copy in TokenStream was used in
most places. This caused confusion trying to prep for hooking up the new
regexp since that hooking up expected a proper singular implementation.

This patch moves Error Reporting parts properly to a single error reporting
location in vm and removes the duplicated parts from the TokenStream parser.
References are impossible since this is pieced together from a dozen code
patches with newly written code in-between.
2022-10-01 20:52:25 +00:00
Moonchild 4d8e35eda9 Issue mcp-graveyard/UXP#1800 - Add unicode code point counter function for 2-byte characters.
Utility function needed for V8 new regexp hookup of handling errors.
2022-10-01 20:51:59 +00:00
Moonchild fcc2b7b796 Issue mcp-graveyard/UXP#1800 - Abstract error metadata.
This is so it can be passed to the error function instead of having tokenstream
handle this internally, allowing it to be passed around (including to/from V8)
2022-10-01 20:51:33 +00:00
Moonchild 90f5e4c010 Fix typo proliferation in irregexp 2022-10-01 20:51:11 +00:00
Moonchild 07319f6f09 Bug 1626713 Part 3: Hook up CheckPatternSyntax
Tag mcp-graveyard/UXP#1800
2022-10-01 20:50:38 +00:00
Moonchild 75de7405ff Bug 1539690 - Make a JS::RegExpFlag public API for flag bits,
and make a JS::RegExpFlags flag-set abstraction.

Dependency for Bug 1626713
Tag #1800
2022-10-01 20:48:02 +00:00
Moonchild bf37fe3b85 Bug 1537978 - Move regular expression-related functionality out of jsapi.h
into a new js/public/RegExp.h header

Tag mcp-graveyard/UXP#1800
2022-10-01 20:38:49 +00:00
Moonchild d5562576f6 Bug 1626713 Part 2: Add RegExpAPI
This is the interface for SM-to-irregexp calls. This doesn't do much yet at
the moment; it just provides an Isolate to JSContext.

Tag mcp-graveyard/UXP#1800
2022-10-01 20:38:27 +00:00
Moonchild 8bf4262f7a Bug 1626713 Part 1: Remove old engine from JS_NEW_REGEXP builds
If we are using the new import, we don't need the old one. Splitting it up one
way or another will allow us to work on the new one separately.

Tag #1800
2022-10-01 20:36:56 +00:00
62 changed files with 2338 additions and 838 deletions

No files matched your search

+7 -6
View File
@@ -19,6 +19,7 @@
#include "imgRequestProxy.h"
#include "jsapi.h"
#include "jsfriendapi.h"
#include "js/RegExp.h"
#include "js/Value.h"
#include "Layers.h"
#include "MediaDecoder.h"
@@ -6709,9 +6710,9 @@ nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
aPattern.AppendLiteral(")$");
JS::Rooted<JSObject*> re(cx,
JS_NewUCRegExpObject(cx,
static_cast<char16_t*>(aPattern.BeginWriting()),
aPattern.Length(), JSREG_UNICODE));
JS::NewUCRegExpObject(cx,
static_cast<char16_t*>(aPattern.BeginWriting()),
aPattern.Length(), JS::RegExpFlag::Unicode));
if (!re) {
// Remove extra patterns added above to report with the original pattern.
aPattern.Cut(0, 4);
@@ -6722,9 +6723,9 @@ nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
JS::Rooted<JS::Value> rval(cx, JS::NullValue());
size_t idx = 0;
if (!JS_ExecuteRegExpNoStatics(cx, re,
static_cast<char16_t*>(aValue.BeginWriting()),
aValue.Length(), &idx, true, &rval)) {
if (!JS::ExecuteRegExpNoStatics(cx, re,
static_cast<char16_t*>(aValue.BeginWriting()),
aValue.Length(), &idx, true, &rval)) {
return true;
}
+1
View File
@@ -7,6 +7,7 @@
#include "JavaScriptShared.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/CPOWManagerGetter.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/TabChild.h"
#include "jsfriendapi.h"
#include "xpcprivate.h"
+3 -2
View File
@@ -11,6 +11,7 @@
#include "mozilla/dom/ScriptSettings.h"
#include "xpcprivate.h"
#include "js/Class.h"
#include "js/RegExp.h"
#include "jsfriendapi.h"
using namespace JS;
@@ -685,7 +686,7 @@ WrapperAnswer::RecvRegExpToShared(const ObjectId& objId, ReturnStatus* rs,
if (!obj)
return deadCPOW(jsapi, rs);
RootedString sourceJSStr(cx, JS_GetRegExpSource(cx, obj));
RootedString sourceJSStr(cx, JS::GetRegExpSource(cx, obj));
if (!sourceJSStr)
return fail(jsapi, rs);
nsAutoJSString sourceStr;
@@ -693,7 +694,7 @@ WrapperAnswer::RecvRegExpToShared(const ObjectId& objId, ReturnStatus* rs,
return fail(jsapi, rs);
source->Assign(sourceStr);
*flags = JS_GetRegExpFlags(cx, obj);
*flags = JS::GetRegExpFlags(cx, obj).value();
return ok(rs);
}
+5 -1
View File
@@ -6,6 +6,7 @@
#include "WrapperOwner.h"
#include "JavaScriptLogging.h"
#include "GeckoProfiler.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/BindingUtils.h"
#include "jsfriendapi.h"
@@ -16,6 +17,9 @@
#include "nsIDocShellTreeItem.h"
#include "nsIDOMDocument.h"
#include "nsJSUtils.h"
#include "js/RegExp.h"
#include "js/RegExpFlags.h"
using namespace js;
using namespace JS;
@@ -876,7 +880,7 @@ WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
return false;
RootedObject regexp(cx);
regexp = JS_NewUCRegExpObject(cx, source.get(), source.Length(), flags);
regexp = JS::NewUCRegExpObject(cx, source.get(), source.Length(), RegExpFlags(flags));
if (!regexp)
return false;
+1 -1
View File
@@ -351,7 +351,7 @@ IsInsideNursery(const js::gc::Cell* cell)
addr &= ~js::gc::ChunkMask;
addr |= js::gc::ChunkLocationOffset;
auto location = *reinterpret_cast<ChunkLocation*>(addr);
MOZ_ASSERT(location == ChunkLocation::Nursery || location == ChunkLocation::TenuredHeap);
//MOZ_ASSERT(location == ChunkLocation::Nursery || location == ChunkLocation::TenuredHeap);
return location == ChunkLocation::Nursery;
}
+93
View File
@@ -0,0 +1,93 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This Source Code Form is "Incompatible With Secondary Licenses", as
* defined by the Mozilla Public License, v. 2.0.
*/
/* Public JS API for Regular Expressions. */
#ifndef js_RegExp_h
#define js_RegExp_h
#include <stddef.h>
#include "jstypes.h"
#include "js/RegExpFlags.h"
#include "js/RootingAPI.h"
#include "js/Value.h"
struct JSContext;
class JSString;
namespace JS {
/*
* Create a new RegExp for the given Latin-1-encoded source and flags.
*/
extern JS_PUBLIC_API(JSObject*) NewRegExpObject(JSContext* cx,
const char* bytes,
size_t length,
RegExpFlags flags);
/*
* Create a new RegExp for the given UC source and flags.
*/
extern JS_PUBLIC_API(JSObject*) NewUCRegExpObject(JSContext* cx,
const char16_t* chars,
size_t length,
RegExpFlags flags);
extern JS_PUBLIC_API(bool) SetRegExpInput(JSContext* cx,
Handle<JSObject*> obj,
Handle<JSString*> input);
extern JS_PUBLIC_API(bool) ClearRegExpStatics(JSContext* cx,
Handle<JSObject*> obj);
/* RegExp interface for callers with a global object. */
extern JS_PUBLIC_API(bool) ExecuteRegExp(JSContext* cx,
Handle<JSObject*> obj,
Handle<JSObject*> reobj,
char16_t* chars,
size_t length,
size_t* indexp,
bool test,
MutableHandle<Value> rval);
/* RegExp interface for callers without a global object. */
extern JS_PUBLIC_API(bool) ExecuteRegExpNoStatics(JSContext* cx,
Handle<JSObject*> reobj,
char16_t* chars,
size_t length,
size_t* indexp,
bool test,
MutableHandle<Value> rval);
/*
* Returns true on success, setting |*isRegExp| to true if |obj| is a RegExp
* object or a wrapper around one, or to false if not. Returns false on failure.
*
* This method returns true with |*isRegExp == false| when passed an ES6 proxy
* whose target is a RegExp, or when passed a revoked proxy.
*/
extern JS_PUBLIC_API(bool) ObjectIsRegExp(JSContext* cx,
Handle<JSObject*> obj,
bool* isRegExp);
/*
* Given a RegExp object (or a wrapper around one), return all JS::RegExpFlag::* for it.
*/
extern JS_PUBLIC_API(RegExpFlags) GetRegExpFlags(JSContext* cx, Handle<JSObject*> obj);
/*
* Return the source text for a RegExp object (or a wrapper around one), or null on failure.
*/
extern JS_PUBLIC_API(JSString*) GetRegExpSource(JSContext* cx, Handle<JSObject*> obj);
} // namespace JS
#endif // js_RegExp_h
+128
View File
@@ -0,0 +1,128 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This Source Code Form is "Incompatible With Secondary Licenses", as
* defined by the Mozilla Public License, v. 2.0.
*/
/* Regular Expression flags. */
#ifndef js_RegExpFlags_h
#define js_RegExpFlags_h
#include "mozilla/Assertions.h" // for MOZ_ASSERT
#include "mozilla/Attributes.h" // for MOZ_IMPLICIT
#include <stdint.h>
namespace JS {
/*
* Regular Expression flag values, suitable for initializing a collection of
* regular expression flags as defined below in |RegExpFlags|.
* Flags are listed in alphabetical order by syntax: /g, /i, /m, /s, /u, /y.
*/
class RegExpFlag {
// WARNING TO SPIDERMONKEY HACKERS (embedders must assume these values can change):
//
// Flag-bit values appear in XDR and structured clone data formats, so none of
// these values can be changed (including to assign values in numerically
// ascending order) unless you also add a translation layer.
public:
/**
* Act globally and find *all* matches (rather than stopping after just the
* first one), i.e. /g.
*/
static uint8_t const Global = 0x02;
/**
* Interpret regular expression source text case-insensitively by folding
* uppercase letters to lowercase, i.e. /i.
*/
static uint8_t const IgnoreCase = 0x01;
/** Treat ^ and $ as begin and end of line, i.e. /m. */
static uint8_t const Multiline = 0x04;
/** Match '.' to any character including newlines, i.e. /s. */
static uint8_t const DotAll = 0x20;
/** Use Unicode semantics, i.e. /u. */
static uint8_t const Unicode = 0x10;
/** Only match starting from <regular expression>.lastIndex, i.e. /y. */
static uint8_t const Sticky = 0x08;
/** No regular expression flags. */
static uint8_t const NoFlags = 0x00;
/** All regular expression flags. */
static uint8_t const AllFlags = 0x3F; //All supported bits set: 0b11'1111
};
/*
* A collection of regular expression flags. Individual flag values may be
* combined into a collection using bitwise operators.
*/
class RegExpFlags {
public:
using Flag = uint8_t;
private:
Flag flags_;
public:
RegExpFlags() = default;
MOZ_IMPLICIT RegExpFlags(Flag flags) : flags_(flags) {
MOZ_ASSERT((flags & RegExpFlag::AllFlags) == flags,
"flags must not contain unrecognized flags");
}
RegExpFlags(const RegExpFlags&) = default;
bool operator==(const RegExpFlags& other) const {
return flags_ == other.flags_;
}
bool operator!=(const RegExpFlags& other) const { return !(*this == other); }
RegExpFlags operator&(Flag flag) const { return RegExpFlags(flags_ & flag); }
RegExpFlags operator|(Flag flag) const { return RegExpFlags(flags_ | flag); }
RegExpFlags operator^(Flag flag) const { return RegExpFlags(flags_ ^ flag); }
RegExpFlags operator~() const { return RegExpFlags(~flags_); }
bool global() const { return flags_ & RegExpFlag::Global; }
bool ignoreCase() const { return flags_ & RegExpFlag::IgnoreCase; }
bool multiline() const { return flags_ & RegExpFlag::Multiline; }
bool dotAll() const { return flags_ & RegExpFlag::DotAll; }
bool unicode() const { return flags_ & RegExpFlag::Unicode; }
bool sticky() const { return flags_ & RegExpFlag::Sticky; }
explicit operator bool() const { return flags_ != 0; }
Flag value() const { return flags_; }
};
inline RegExpFlags& operator&=(RegExpFlags& flags, RegExpFlags::Flag flag) {
flags = flags & flag;
return flags;
}
inline RegExpFlags& operator|=(RegExpFlags& flags, RegExpFlags::Flag flag) {
flags = flags | flag;
return flags;
}
inline RegExpFlags& operator^=(RegExpFlags& flags, RegExpFlags::Flag flag) {
flags = flags ^ flag;
return flags;
}
} // namespace JS
#endif // js_RegExpFlags_h
+2 -2
View File
@@ -759,8 +759,8 @@ class MOZ_NON_PARAM alignas(8) Value
"Private GC thing Values must not be strings. Make a StringValue instead.");
MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol,
"Private GC thing Values must not be symbols. Make a SymbolValue instead.");
MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object,
"Private GC thing Values must not be objects. Make an ObjectValue instead.");
// MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object,
// "Private GC thing Values must not be objects. Make an ObjectValue instead.");
MOZ_ASSERT(uintptr_t(cell) > 0x1000);
#if defined(JS_PUNBOX64)
+46 -36
View File
@@ -5,13 +5,22 @@
#include "builtin/RegExp.h"
#include "mozilla/Casting.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/TypeTraits.h"
#include "jscntxt.h"
#ifndef JS_NEW_REGEXP
#include "irregexp/RegExpParser.h"
#endif
#include "jit/InlinableNatives.h"
#include "js/RegExpFlags.h"
#ifdef JS_NEW_REGEXP
#include "new-regexp/RegExpAPI.h"
#endif
#include "vm/RegExpStatics.h"
#include "vm/SelfHosting.h"
#include "vm/StringBuffer.h"
@@ -25,9 +34,13 @@ using namespace js;
using namespace js::unicode;
using mozilla::ArrayLength;
using mozilla::AssertedCast;
using mozilla::CheckedInt;
using mozilla::Maybe;
using JS::RegExpFlag;
using JS::RegExpFlags;
/*
* ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
* steps 3, 16-25.
@@ -121,18 +134,14 @@ CreateRegExpSearchResult(JSContext* cx, const MatchPairs& matches)
*/
static RegExpRunStatus
ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, RegExpShared& re, HandleLinearString input,
size_t searchIndex, MatchPairs* matches, size_t* endIndex)
size_t searchIndex, MatchPairs* matches)
{
RegExpRunStatus status = re.execute(cx, input, searchIndex, matches, endIndex);
RegExpRunStatus status = re.execute(cx, input, searchIndex, matches);
/* Out of spec: Update RegExpStatics. */
if (status == RegExpRunStatus_Success && res) {
if (matches) {
if (!res->updateFromMatchPairs(cx, input, *matches))
return RegExpRunStatus_Error;
} else {
res->updateLazily(cx, input, &re, searchIndex);
}
if (!res->updateFromMatchPairs(cx, input, *matches))
return RegExpRunStatus_Error;
}
return status;
}
@@ -149,8 +158,7 @@ js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*>
ScopedMatchPairs matches(&cx->tempLifoAlloc());
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, *lastIndex,
&matches, nullptr);
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, *lastIndex, &matches);
if (status == RegExpRunStatus_Error)
return false;
@@ -172,12 +180,16 @@ js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*>
}
static bool
CheckPatternSyntax(JSContext* cx, HandleAtom pattern, RegExpFlag flags)
CheckPatternSyntax(JSContext* cx, HandleAtom pattern, RegExpFlags flags)
{
CompileOptions options(cx);
frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
#ifdef JS_NEW_REGEXP
return irregexp::CheckPatternSyntax(cx, dummyTokenStream, pattern, flags);
#else
return irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), pattern,
flags & UnicodeFlag, flags & DotAllFlag);
flags.unicode(), flags.dotAll());
#endif
}
enum RegExpSharedUse {
@@ -217,7 +229,7 @@ RegExpInitializeIgnoringLastIndex(JSContext* cx, Handle<RegExpObject*> obj,
}
/* Step 3. */
RegExpFlag flags = RegExpFlag(0);
RegExpFlags flags = RegExpFlag::NoFlags;
if (!flagsValue.isUndefined()) {
/* Step 4. */
RootedString flagStr(cx, ToString<CanGC>(cx, flagsValue));
@@ -334,7 +346,7 @@ regexp_compile_impl(JSContext* cx, const CallArgs& args)
RootedObject patternObj(cx, &patternValue.toObject());
RootedAtom sourceAtom(cx);
RegExpFlag flags;
RegExpFlags flags = RegExpFlag::NoFlags;
{
// Step 3b.
RegExpGuard g(cx);
@@ -424,12 +436,11 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp)
return false;
if (cls == ESClass::RegExp) {
// Beware! |patternObj| might be a proxy into another compartment, so
// don't assume |patternObj.is<RegExpObject>()|. For the same reason,
// don't reuse the RegExpShared below.
// don't assume |patternObj.is<RegExpObject>()|.
RootedObject patternObj(cx, &patternValue.toObject());
RootedAtom sourceAtom(cx);
RegExpFlag flags;
RegExpFlags flags = RegExpFlag::NoFlags;
{
// Step 4.a.
RegExpGuard g(cx);
@@ -454,14 +465,14 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp)
// Step 8.
if (args.hasDefined(1)) {
// Step 4.c / 21.2.3.2.2 RegExpInitialize step 4.
RegExpFlag flagsArg = RegExpFlag(0);
RegExpFlags flagsArg = RegExpFlag::NoFlags;
RootedString flagStr(cx, ToString<CanGC>(cx, args[1]));
if (!flagStr)
return false;
if (!ParseRegExpFlags(cx, flagStr, &flagsArg))
return false;
if (!(flags & UnicodeFlag) && flagsArg & UnicodeFlag) {
if (!flags.unicode() && flagsArg.unicode()) {
// Have to check syntax again when adding 'u' flag.
// ES 2017 draft rev 9b49a888e9dfe2667008a01b2754c3662059ae56
@@ -536,7 +547,7 @@ js::regexp_construct_raw_flags(JSContext* cx, unsigned argc, Value* vp)
RootedAtom sourceAtom(cx, rx->getSource());
// Step 4.c.
int32_t flags = int32_t(args[1].toNumber());
RegExpFlags flags = AssertedCast<uint8_t>(int32_t(args[1].toNumber()));
// Step 7.
Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx));
@@ -544,7 +555,7 @@ js::regexp_construct_raw_flags(JSContext* cx, unsigned argc, Value* vp)
return false;
// Step 8.
regexp->initAndZeroLastIndex(sourceAtom, RegExpFlag(flags), cx);
regexp->initAndZeroLastIndex(sourceAtom, flags, cx);
args.rval().setObject(*regexp);
return true;
}
@@ -557,7 +568,7 @@ js::regexp_clone(JSContext* cx, unsigned argc, Value* vp)
RootedObject from(cx, &args[0].toObject());
RootedAtom sourceAtom(cx);
RegExpFlag flags;
RegExpFlags flags;
{
RegExpGuard g(cx);
if (!RegExpToShared(cx, from, &g))
@@ -930,7 +941,7 @@ IsTrailSurrogateWithLeadSurrogate(JSContext* cx, HandleLinearString input, int32
static RegExpRunStatus
ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
int32_t lastIndex,
MatchPairs* matches, size_t* endIndex, RegExpStaticsUpdate staticsUpdate)
MatchPairs* matches, RegExpStaticsUpdate staticsUpdate)
{
/*
* WARNING: Despite the presence of spec step comment numbers, this
@@ -992,7 +1003,7 @@ ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
}
/* Steps 3, 11-14, except 12.a.i, 12.c.i.1. */
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, lastIndex, matches, endIndex);
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, lastIndex, matches);
if (status == RegExpRunStatus_Error)
return RegExpRunStatus_Error;
@@ -1014,7 +1025,7 @@ RegExpMatcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
/* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
&matches, nullptr, staticsUpdate);
&matches, staticsUpdate);
if (status == RegExpRunStatus_Error)
return false;
@@ -1088,7 +1099,7 @@ RegExpSearcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
/* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
&matches, nullptr, staticsUpdate);
&matches, staticsUpdate);
if (status == RegExpRunStatus_Error)
return false;
@@ -1190,16 +1201,16 @@ js::RegExpTester(JSContext* cx, unsigned argc, Value* vp)
return false;
/* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
size_t endIndex = 0;
VectorMatchPairs matches;
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
nullptr, &endIndex, UpdateRegExpStatics);
&matches, UpdateRegExpStatics);
if (status == RegExpRunStatus_Error)
return false;
if (status == RegExpRunStatus_Success) {
MOZ_ASSERT(endIndex <= INT32_MAX);
args.rval().setInt32(int32_t(endIndex));
int32_t endIndex = matches[0].limit;
args.rval().setInt32(endIndex);
} else {
args.rval().setInt32(-1);
}
@@ -1216,13 +1227,12 @@ js::RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
{
MOZ_ASSERT(lastIndex >= 0);
size_t endIndexTmp = 0;
VectorMatchPairs matches;
RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, lastIndex,
nullptr, &endIndexTmp, UpdateRegExpStatics);
&matches, UpdateRegExpStatics);
if (status == RegExpRunStatus_Success) {
MOZ_ASSERT(endIndexTmp <= INT32_MAX);
*endIndex = int32_t(endIndexTmp);
*endIndex = matches[0].limit;
return true;
}
if (status == RegExpRunStatus_Success_NotFound) {
@@ -1244,9 +1254,9 @@ js::regexp_test_no_statics(JSContext* cx, unsigned argc, Value* vp)
RootedObject regexp(cx, &args[0].toObject());
RootedString string(cx, args[1].toString());
size_t ignored = 0;
VectorMatchPairs ignored;
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, 0,
nullptr, &ignored, DontUpdateRegExpStatics);
&ignored, DontUpdateRegExpStatics);
args.rval().setBoolean(status == RegExpRunStatus_Success);
return status != RegExpRunStatus_Error;
}
+17 -24
View File
@@ -24,14 +24,17 @@
#include "builtin/SelfHostingDefines.h"
#ifdef DEBUG
#include "frontend/TokenStream.h"
#ifndef JS_NEW_REGEXP
#include "irregexp/RegExpAST.h"
#include "irregexp/RegExpEngine.h"
#include "irregexp/RegExpParser.h"
#endif
#endif
#include "jit/InlinableNatives.h"
#include "jit/JitFrameIterator.h"
#include "js/Debug.h"
#include "js/HashTable.h"
#include "js/RegExpFlags.h"
#include "js/StructuredClone.h"
#include "js/UbiNode.h"
#include "js/UbiNodeBreadthFirst.h"
@@ -64,6 +67,9 @@ using namespace js;
using mozilla::ArrayLength;
using mozilla::Move;
using JS::RegExpFlag;
using JS::RegExpFlags;
// If fuzzingSafe is set, remove functionality that could cause problems with
// fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
static bool fuzzingSafe = false;
@@ -3596,7 +3602,7 @@ GetModuleEnvironmentValue(JSContext* cx, unsigned argc, Value* vp)
return GetProperty(cx, env, env, id, args.rval());
}
#ifdef DEBUG
#if defined(DEBUG) && !defined(JS_NEW_REGEXP)
static const char*
AssertionTypeToString(irregexp::RegExpAssertion::AssertionType type)
{
@@ -3870,7 +3876,7 @@ ParseRegExp(JSContext* cx, unsigned argc, Value* vp)
return false;
}
RegExpFlag flags = RegExpFlag(0);
RegExpFlags flags = RegExpFlag::NoFlags;
if (!args.get(1).isUndefined()) {
if (!args.get(1).isString()) {
ReportUsageErrorASCII(cx, callee, "Second argument, if present, must be a String");
@@ -3899,11 +3905,7 @@ ParseRegExp(JSContext* cx, unsigned argc, Value* vp)
irregexp::RegExpCompileData data;
if (!irregexp::ParsePattern(dummyTokenStream, cx->tempLifoAlloc(), pattern,
flags & MultilineFlag, match_only,
flags & UnicodeFlag, flags & IgnoreCaseFlag,
flags & GlobalFlag, flags & StickyFlag,
flags & DotAllFlag,
&data))
match_only, flags, &data))
{
return false;
}
@@ -3934,34 +3936,25 @@ DisRegExp(JSContext* cx, unsigned argc, Value* vp)
Rooted<RegExpObject*> reobj(cx, &args[0].toObject().as<RegExpObject>());
bool match_only = false;
if (!args.get(1).isUndefined()) {
if (!args.get(1).isBoolean()) {
ReportUsageErrorASCII(cx, callee, "Second argument, if present, must be a Boolean");
return false;
}
match_only = args[1].toBoolean();
}
RootedLinearString input(cx, cx->runtime()->emptyString);
if (!args.get(2).isUndefined()) {
if (!args.get(2).isString()) {
ReportUsageErrorASCII(cx, callee, "Third argument, if present, must be a String");
if (!args.get(1).isUndefined()) {
if (!args.get(1).isString()) {
ReportUsageErrorASCII(cx, callee, "Second argument, if present, must be a String");
return false;
}
RootedString inputStr(cx, args[2].toString());
RootedString inputStr(cx, args[1].toString());
input = inputStr->ensureLinear(cx);
if (!input)
return false;
}
if (!RegExpObject::dumpBytecode(cx, reobj, match_only, input))
if (!RegExpObject::dumpBytecode(cx, reobj, input))
return false;
args.rval().setUndefined();
return true;
}
#endif // DEBUG
#endif // DEBUG && !JS_NEW_REGEXP
static bool
IsConstructor(JSContext* cx, unsigned argc, Value* vp)
@@ -4502,13 +4495,13 @@ JS_FN_HELP("rejectPromise", RejectPromise, 2, 0,
};
static const JSFunctionSpecWithHelp FuzzingUnsafeTestingFunctions[] = {
#ifdef DEBUG
#if defined(DEBUG) && !defined(JS_NEW_REGEXP)
JS_FN_HELP("parseRegExp", ParseRegExp, 3, 0,
"parseRegExp(pattern[, flags[, match_only])",
" Parses a RegExp pattern and returns a tree, potentially throwing."),
JS_FN_HELP("disRegExp", DisRegExp, 3, 0,
"disRegExp(regexp[, match_only[, input]])",
"disRegExp(regexp[, input])",
" Dumps RegExp bytecode."),
#endif
+9 -2
View File
@@ -3728,8 +3728,15 @@ BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...)
va_list args;
va_start(args, errorNumber);
bool result = tokenStream().reportCompileErrorNumberVA(nullptr, pos.begin, JSREPORT_ERROR,
errorNumber, args);
ErrorMetadata metadata;
bool result = tokenStream().computeErrorMetadata(&metadata, pos.begin);
if (result) {
result =
tokenStream().reportCompileErrorNumberVA(Move(metadata), nullptr, JSREPORT_ERROR,
errorNumber, args);
}
va_end(args);
return result;
}
+76 -33
View File
@@ -37,6 +37,7 @@
#include "frontend/FoldConstants.h"
#include "frontend/TokenStream.h"
#include "wasm/AsmJS.h"
#include "js/RegExpFlags.h"
#include "jsatominlines.h"
#include "jsscriptinlines.h"
@@ -56,6 +57,8 @@ using mozilla::PodZero;
using mozilla::Some;
using JS::AutoGCRooter;
using JS::RegExpFlag;
using JS::RegExpFlags;
namespace js {
namespace frontend {
@@ -604,12 +607,17 @@ ParserBase::error(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
ErrorMetadata metadata;
if (tokenStream.computeErrorMetadata(&metadata, pos().begin)) {
#ifdef DEBUG
bool result =
bool result =
#endif
tokenStream.reportCompileErrorNumberVA(nullptr, pos().begin, JSREPORT_ERROR,
errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
tokenStream.reportCompileErrorNumberVA(Move(metadata), nullptr, JSREPORT_ERROR,
errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
}
va_end(args);
}
@@ -618,12 +626,17 @@ ParserBase::errorWithNotes(UniquePtr<JSErrorNotes> notes, unsigned errorNumber,
{
va_list args;
va_start(args, errorNumber);
ErrorMetadata metadata;
if (tokenStream.computeErrorMetadata(&metadata, pos().begin)) {
#ifdef DEBUG
bool result =
bool result =
#endif
tokenStream.reportCompileErrorNumberVA(Move(notes), pos().begin, JSREPORT_ERROR,
errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
tokenStream.reportCompileErrorNumberVA(Move(metadata), Move(notes), JSREPORT_ERROR,
errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
}
va_end(args);
}
@@ -632,11 +645,17 @@ ParserBase::errorAt(uint32_t offset, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
ErrorMetadata metadata;
if (tokenStream.computeErrorMetadata(&metadata, offset)) {
#ifdef DEBUG
bool result =
bool result =
#endif
tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR, errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
tokenStream.reportCompileErrorNumberVA(Move(metadata), nullptr, JSREPORT_ERROR,
errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
}
va_end(args);
}
@@ -646,12 +665,17 @@ ParserBase::errorWithNotesAt(UniquePtr<JSErrorNotes> notes, uint32_t offset,
{
va_list args;
va_start(args, errorNumber);
ErrorMetadata metadata;
if (tokenStream.computeErrorMetadata(&metadata, offset)) {
#ifdef DEBUG
bool result =
bool result =
#endif
tokenStream.reportCompileErrorNumberVA(Move(notes), offset, JSREPORT_ERROR,
errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
tokenStream.reportCompileErrorNumberVA(Move(metadata), Move(notes), JSREPORT_ERROR,
errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
}
va_end(args);
}
@@ -660,9 +684,15 @@ ParserBase::warning(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
bool result =
tokenStream.reportCompileErrorNumberVA(nullptr, pos().begin, JSREPORT_WARNING,
errorNumber, args);
ErrorMetadata metadata;
bool result = tokenStream.computeErrorMetadata(&metadata, pos().begin);
if (result) {
result =
tokenStream.reportCompileErrorNumberVA(Move(metadata), nullptr, JSREPORT_WARNING,
errorNumber, args);
}
va_end(args);
return result;
}
@@ -672,9 +702,15 @@ ParserBase::warningAt(uint32_t offset, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
bool result =
tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_WARNING,
errorNumber, args);
ErrorMetadata metadata;
bool result = tokenStream.computeErrorMetadata(&metadata, offset);
if (result) {
result =
tokenStream.reportCompileErrorNumberVA(Move(metadata), nullptr, JSREPORT_WARNING,
errorNumber, args);
}
va_end(args);
return result;
}
@@ -684,8 +720,10 @@ ParserBase::extraWarning(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
bool result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, pos().begin,
errorNumber, args);
bool result =
tokenStream.reportExtraWarningErrorNumberVA(nullptr, pos().begin, errorNumber, args);
va_end(args);
return result;
}
@@ -708,9 +746,11 @@ ParserBase::strictModeError(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
bool res =
tokenStream.reportStrictModeErrorNumberVA(nullptr, pos().begin, pc->sc()->strict(),
errorNumber, args);
va_end(args);
return res;
}
@@ -720,9 +760,11 @@ ParserBase::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
bool res =
tokenStream.reportStrictModeErrorNumberVA(nullptr, offset, pc->sc()->strict(),
errorNumber, args);
va_end(args);
return res;
}
@@ -732,24 +774,25 @@ ParserBase::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumb
{
va_list args;
va_start(args, errorNumber);
bool result = false;
uint32_t offset = TokenStream::NoOffset;
switch (kind) {
case ParseError:
result = tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR,
case ParseWarning: {
ErrorMetadata metadata;
tokenStream.computeErrorMetadataNoOffset(&metadata);
unsigned flags = kind == ParseError ? JSREPORT_ERROR : JSREPORT_WARNING;
result = tokenStream.reportCompileErrorNumberVA(Move(metadata), nullptr, flags,
errorNumber, args);
break;
case ParseWarning:
result =
tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_WARNING,
errorNumber, args);
break;
}
case ParseExtraWarning:
result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset,
result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, TokenStream::NoOffset,
errorNumber, args);
break;
case ParseStrictError:
result = tokenStream.reportStrictModeErrorNumberVA(nullptr, offset, strict,
result = tokenStream.reportStrictModeErrorNumberVA(nullptr, TokenStream::NoOffset, strict,
errorNumber, args);
break;
}
@@ -9533,7 +9576,7 @@ Parser<ParseHandler>::newRegExp()
// Create the regexp even when doing a syntax parse, to check the regexp's syntax.
const char16_t* chars = tokenStream.getTokenbuf().begin();
size_t length = tokenStream.getTokenbuf().length();
RegExpFlag flags = tokenStream.currentToken().regExpFlags();
RegExpFlags flags = tokenStream.currentToken().regExpFlags();
Rooted<RegExpObject*> reobj(context);
reobj = RegExpObject::create(context, chars, length, flags, &tokenStream, alloc);
+186 -121
View File
@@ -25,6 +25,7 @@
#include "frontend/BytecodeCompiler.h"
#include "frontend/ReservedWords.h"
#include "js/CharacterEncoding.h"
#include "js/RegExpFlags.h"
#include "js/UniquePtr.h"
#include "vm/HelperThreads.h"
#include "vm/StringBuffer.h"
@@ -39,6 +40,9 @@ using mozilla::PodAssign;
using mozilla::PodCopy;
using mozilla::PodZero;
using JS::RegExpFlag;
using JS::RegExpFlags;
struct ReservedWordInfo {
const char* chars; // C string with reserved word text
TokenKind tokentype;
@@ -683,33 +687,121 @@ TokenStream::reportStrictModeErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32
else
return true;
return reportCompileErrorNumberVA(Move(notes), offset, flags, errorNumber, args);
ErrorMetadata metadata;
if (!computeErrorMetadata(&metadata, offset))
return false;
return reportCompileErrorNumberVA(Move(metadata), Move(notes), flags, errorNumber, args);
}
void
CompileError::throwError(JSContext* cx)
TokenStream::computeErrorMetadataNoOffset(ErrorMetadata* err)
{
if (JSREPORT_IS_WARNING(flags)) {
CallWarningReporter(cx, this);
return;
}
err->filename = filename;
err->lineNumber = 0;
err->columnNumber = 0;
// If there's a runtime exception type associated with this error
// number, set that as the pending exception. For errors occuring at
// compile time, this is very likely to be a JSEXN_SYNTAXERR.
//
// If an exception is thrown but not caught, the JSREPORT_EXCEPTION
// flag will be set in report.flags. Proper behavior for an error
// reporter is to ignore a report with this flag for all but top-level
// compilation errors. The exception will remain pending, and so long
// as the non-top-level "load", "eval", or "compile" native function
// returns false, the top-level reporter will eventually receive the
// uncaught exception report.
ErrorToException(cx, this, nullptr, nullptr);
MOZ_ASSERT(err->lineOfContext == nullptr);
}
bool
TokenStream::reportCompileErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
TokenStream::fillExcludingContext(ErrorMetadata* err, uint32_t offset)
{
// If this TokenStreamAnyChars doesn't have location information, try to
// get it from the caller.
if (!filename && !cx->helperThread()) {
NonBuiltinFrameIter iter(cx->asJSContext(),
FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK,
cx->compartment()->principals());
if (!iter.done() && iter.filename()) {
err->filename = iter.filename();
err->lineNumber = iter.computeLine(&err->columnNumber);
return false;
}
}
// Otherwise use this TokenStreamAnyChars's location information.
err->filename = filename;
srcCoords.lineNumAndColumnIndex(offset, &err->lineNumber, &err->columnNumber);
return true;
}
bool
TokenStream::computeErrorMetadata(ErrorMetadata* err, uint32_t offset)
{
if (offset == NoOffset) {
computeErrorMetadataNoOffset(err);
return true;
}
// This function's return value isn't a success/failure indication: it
// returns true if this TokenStream's location information could be used,
// and it returns false when that information can't be used (and so we
// can't provide a line of context).
if (!fillExcludingContext(err, offset))
return true;
// Add a line of context from this TokenStream to help with debugging.
return computeLineOfContext(err, offset);
}
bool
TokenStream::computeLineOfContext(ErrorMetadata* err, uint32_t offset)
{
// This function presumes |err| is filled in *except* for line-of-context
// fields. It exists to make |TokenStream::computeErrorMetadata|, above,
// more readable.
// We only have line-start information for the current line. If the error
// is on a different line, we can't easily provide context. (This means
// any error in a multi-line token, e.g. an unterminated multiline string
// literal, won't have context.)
if (err->lineNumber != lineno)
return true;
// See note about lineOfContextRadius in ErrorReporting.h
const size_t windowRadius = ErrorMetadata::lineOfContextRadius;
// The window must start within the current line, no earlier than
// |windowRadius| characters before |offset|.
MOZ_ASSERT(offset >= linebase);
size_t windowStart = (offset - linebase > windowRadius) ?
offset - windowRadius :
linebase;
// The window must start within the portion of the current line that we
// actually have in our buffer.
if (windowStart < userbuf.startOffset())
windowStart = userbuf.startOffset();
// The window must end within the current line, no later than
// windowRadius after offset.
size_t windowEnd = userbuf.findEOLMax(offset, windowRadius);
size_t windowLength = windowEnd - windowStart;
MOZ_ASSERT(windowLength <= windowRadius * 2);
// Create the windowed string, not including the potential line
// terminator.
StringBuffer windowBuf(cx);
if (!windowBuf.append(userbuf.rawCharPtrAt(windowStart), windowLength) ||
!windowBuf.append('\0'))
{
return false;
}
err->lineOfContext.reset(windowBuf.stealChars());
if (!err->lineOfContext)
return false;
err->lineLength = windowLength;
err->tokenOffset = offset - windowStart;
return true;
}
bool
TokenStream::reportCompileErrorNumberVA(ErrorMetadata&& metadata,
UniquePtr<JSErrorNotes> notes,
unsigned flags, unsigned errorNumber, va_list args)
{
bool warning = JSREPORT_IS_WARNING(flags);
@@ -721,37 +813,23 @@ TokenStream::reportCompileErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t
// On the main thread, report the error immediately. When compiling off
// thread, save the error so that the main thread can report it later.
CompileError tempErr;
CompileError* tempErrPtr = &tempErr;
js::CompileError tempErr;
js::CompileError* tempErrPtr = &tempErr;
if (!cx->isJSContext() && !cx->addPendingCompileError(&tempErrPtr))
return false;
CompileError& err = *tempErrPtr;
js::CompileError& err = *tempErrPtr;
err.notes = Move(notes);
err.flags = flags;
err.errorNumber = errorNumber;
err.filename = filename;
err.isMuted = mutedErrors;
if (offset == NoOffset) {
err.lineno = 0;
err.column = 0;
} else {
err.lineno = srcCoords.lineNum(offset);
err.column = srcCoords.columnIndex(offset);
}
// If we have no location information, try to get one from the caller.
bool callerFilename = false;
if (offset != NoOffset && !err.filename && cx->isJSContext()) {
NonBuiltinFrameIter iter(cx->asJSContext(),
FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK,
cx->compartment()->principals());
if (!iter.done() && iter.filename()) {
callerFilename = true;
err.filename = iter.filename();
err.lineno = iter.computeLine(&err.column);
}
}
err.filename = metadata.filename;
err.lineno = metadata.lineNumber;
err.column = metadata.columnNumber;
if (UniqueTwoByteChars lineOfContext = Move(metadata.lineOfContext))
err.initOwnedLinebuf(lineOfContext.release(), metadata.lineLength, metadata.tokenOffset);
if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr, errorNumber,
nullptr, ArgumentsAreLatin1, &err, args))
@@ -759,55 +837,6 @@ TokenStream::reportCompileErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t
return false;
}
// Given a token, T, that we want to complain about: if T's (starting)
// lineno doesn't match TokenStream's lineno, that means we've scanned past
// the line that T starts on, which makes it hard to print some or all of
// T's (starting) line for context.
//
// So we don't even try, leaving report.linebuf and friends zeroed. This
// means that any error involving a multi-line token (e.g. an unterminated
// multi-line string literal) won't have a context printed.
if (offset != NoOffset && err.lineno == lineno && !callerFilename) {
// We show only a portion (a "window") of the line around the erroneous
// token -- the first char in the token, plus |windowRadius| chars
// before it and |windowRadius - 1| chars after it. This is because
// lines can be very long and printing the whole line is (a) not that
// helpful, and (b) can waste a lot of memory. See bug 634444.
static const size_t windowRadius = 60;
// The window must start within the current line, no earlier than
// windowRadius characters before offset.
size_t windowStart = (offset - linebase > windowRadius) ?
offset - windowRadius :
linebase;
// The window must start within the portion of the current line
// that we actually have in our buffer.
if (windowStart < userbuf.startOffset())
windowStart = userbuf.startOffset();
// The window must end within the current line, no later than
// windowRadius after offset.
size_t windowEnd = userbuf.findEOLMax(offset, windowRadius);
size_t windowLength = windowEnd - windowStart;
MOZ_ASSERT(windowLength <= windowRadius * 2);
// Create the windowed strings.
StringBuffer windowBuf(cx);
if (!windowBuf.append(userbuf.rawCharPtrAt(windowStart), windowLength) ||
!windowBuf.append('\0'))
{
return false;
}
// The window into the offending source line, without final \n.
UniqueTwoByteChars linebuf(windowBuf.stealChars());
if (!linebuf)
return false;
err.initOwnedLinebuf(linebuf.release(), windowLength, offset - windowStart);
}
if (cx->isJSContext())
err.throwError(cx->asJSContext());
@@ -830,8 +859,14 @@ TokenStream::reportError(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
bool result = reportCompileErrorNumberVA(nullptr, currentToken().pos.begin, JSREPORT_ERROR,
errorNumber, args);
ErrorMetadata metadata;
bool result = computeErrorMetadata(&metadata, currentToken().pos.begin);
if (result) {
result = reportCompileErrorNumberVA(Move(metadata), nullptr, JSREPORT_ERROR, errorNumber,
args);
}
va_end(args);
return result;
}
@@ -841,8 +876,13 @@ TokenStream::reportErrorNoOffset(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
bool result = reportCompileErrorNumberVA(nullptr, NoOffset, JSREPORT_ERROR,
ErrorMetadata metadata;
computeErrorMetadataNoOffset(&metadata);
bool result = reportCompileErrorNumberVA(Move(metadata), nullptr, JSREPORT_ERROR,
errorNumber, args);
va_end(args);
return result;
}
@@ -852,8 +892,14 @@ TokenStream::warning(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
bool result = reportCompileErrorNumberVA(nullptr, currentToken().pos.begin, JSREPORT_WARNING,
errorNumber, args);
ErrorMetadata metadata;
bool result = computeErrorMetadata(&metadata, currentToken().pos.begin);
if (result) {
result = reportCompileErrorNumberVA(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber,
args);
}
va_end(args);
return result;
}
@@ -865,7 +911,12 @@ TokenStream::reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint
if (!options().extraWarningsOption)
return true;
return reportCompileErrorNumberVA(Move(notes), offset, JSREPORT_STRICT|JSREPORT_WARNING,
ErrorMetadata metadata;
if (!computeErrorMetadata(&metadata, offset))
return false;
return reportCompileErrorNumberVA(Move(metadata), Move(notes),
JSREPORT_STRICT | JSREPORT_WARNING,
errorNumber, args);
}
@@ -874,10 +925,15 @@ TokenStream::reportAsmJSError(uint32_t offset, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
unsigned flags = options().throwOnAsmJSValidationFailureOption
? JSREPORT_ERROR
: JSREPORT_WARNING;
reportCompileErrorNumberVA(nullptr, offset, flags, errorNumber, args);
ErrorMetadata metadata;
if (computeErrorMetadata(&metadata, offset)) {
unsigned flags = options().throwOnAsmJSValidationFailureOption
? JSREPORT_ERROR
: JSREPORT_WARNING;
reportCompileErrorNumberVA(Move(metadata), nullptr, flags, errorNumber, args);
}
va_end(args);
}
@@ -886,12 +942,16 @@ TokenStream::error(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
ErrorMetadata metadata;
if (computeErrorMetadata(&metadata, currentToken().pos.begin)) {
#ifdef DEBUG
bool result =
bool result =
#endif
reportCompileErrorNumberVA(nullptr, currentToken().pos.begin, JSREPORT_ERROR,
errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
reportCompileErrorNumberVA(Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
}
va_end(args);
}
@@ -900,11 +960,16 @@ TokenStream::errorAt(uint32_t offset, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
ErrorMetadata metadata;
if (computeErrorMetadata(&metadata, offset)) {
#ifdef DEBUG
bool result =
bool result =
#endif
reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR, errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
reportCompileErrorNumberVA(Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
}
va_end(args);
}
@@ -1925,23 +1990,23 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
goto error;
}
RegExpFlag reflags = NoFlags;
RegExpFlags reflags = RegExpFlag::NoFlags;
unsigned length = tokenbuf.length() + 1;
while (true) {
if (!peekChar(&c))
goto error;
if (c == 'g' && !(reflags & GlobalFlag))
reflags = RegExpFlag(reflags | GlobalFlag);
else if (c == 'i' && !(reflags & IgnoreCaseFlag))
reflags = RegExpFlag(reflags | IgnoreCaseFlag);
else if (c == 'm' && !(reflags & MultilineFlag))
reflags = RegExpFlag(reflags | MultilineFlag);
else if (c == 'y' && !(reflags & StickyFlag))
reflags = RegExpFlag(reflags | StickyFlag);
else if (c == 'u' && !(reflags & UnicodeFlag))
reflags = RegExpFlag(reflags | UnicodeFlag);
else if (c == 's' && !(reflags & DotAllFlag))
reflags = RegExpFlag(reflags | DotAllFlag);
if (c == 'g' && !(reflags & RegExpFlag::Global))
reflags = RegExpFlags(reflags | RegExpFlag::Global);
else if (c == 'i' && !(reflags & RegExpFlag::IgnoreCase))
reflags = RegExpFlags(reflags | RegExpFlag::IgnoreCase);
else if (c == 'm' && !(reflags & RegExpFlag::Multiline))
reflags = RegExpFlags(reflags | RegExpFlag::Multiline);
else if (c == 's' && !(reflags & RegExpFlag::DotAll))
reflags = RegExpFlags(reflags | RegExpFlag::DotAll);
else if (c == 'u' && !(reflags & RegExpFlag::Unicode))
reflags = RegExpFlags(reflags | RegExpFlag::Unicode);
else if (c == 'y' && !(reflags & RegExpFlag::Sticky))
reflags = RegExpFlags(reflags | RegExpFlag::Sticky);
else
break;
getChar();
+25 -11
View File
@@ -22,6 +22,7 @@
#include "jspubtd.h"
#include "frontend/TokenKind.h"
#include "js/RegExpFlags.h"
#include "js/UniquePtr.h"
#include "js/Vector.h"
#include "vm/RegExpObject.h"
@@ -174,7 +175,7 @@ struct Token
double value; // floating point number
DecimalPoint decimalPoint; // literal contains '.'
} number;
RegExpFlag reflags; // regexp flags; use tokenbuf to access
JS::RegExpFlags reflags; // regexp flags; use tokenbuf to access
// regexp chars
} u;
#ifdef DEBUG
@@ -196,9 +197,8 @@ struct Token
u.atom = atom;
}
void setRegExpFlags(js::RegExpFlag flags) {
void setRegExpFlags(JS::RegExpFlags flags) {
MOZ_ASSERT(type == TOK_REGEXP);
MOZ_ASSERT((flags & AllFlags) == flags);
u.reflags = flags;
}
@@ -222,9 +222,8 @@ struct Token
return u.atom;
}
js::RegExpFlag regExpFlags() const {
JS::RegExpFlags regExpFlags() const {
MOZ_ASSERT(type == TOK_REGEXP);
MOZ_ASSERT((u.reflags & AllFlags) == u.reflags);
return u.reflags;
}
@@ -239,11 +238,6 @@ struct Token
}
};
class CompileError : public JSErrorReport {
public:
void throwError(JSContext* cx);
};
extern const char*
ReservedWordToCharZ(PropertyName* str);
@@ -403,13 +397,26 @@ class MOZ_STACK_CLASS TokenStream
// Warn at the current offset.
MOZ_MUST_USE bool warning(unsigned errorNumber, ...);
private:
// Compute a line of context for an otherwise-filled-in |err| at the given
// offset in this token stream. (This function basically exists to make
// |computeErrorMetadata| more readable and shouldn't be called elsewhere.)
MOZ_MUST_USE bool computeLineOfContext(ErrorMetadata* err, uint32_t offset);
public:
// Compute error metadata for an error at no offset.
void computeErrorMetadataNoOffset(ErrorMetadata* err);
// Compute error metadata for an error at the given offset.
MOZ_MUST_USE bool computeErrorMetadata(ErrorMetadata* err, uint32_t offset);
static const uint32_t NoOffset = UINT32_MAX;
// General-purpose error reporters. You should avoid calling these
// directly, and instead use the more succinct alternatives (error(),
// warning(), &c.) in TokenStream, Parser, and BytecodeEmitter.
bool reportCompileErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset, unsigned flags,
bool reportCompileErrorNumberVA(ErrorMetadata&& metadata,
UniquePtr<JSErrorNotes> notes, unsigned flags,
unsigned errorNumber, va_list args);
bool reportStrictModeErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
bool strictMode, unsigned errorNumber, va_list args);
@@ -852,6 +859,13 @@ class MOZ_STACK_CLASS TokenStream
return options_;
}
/**
* Fill in |err|, excepting line-of-context-related fields. If the token
* stream has location information, use that and return true. If it does
* not, use the caller's location information and return false.
*/
bool fillExcludingContext(ErrorMetadata* err, uint32_t offset);
private:
// This is the low-level interface to the JS source code buffer. It just
// gets raw chars, basically. TokenStreams functions are layered on top
+95 -22
View File
@@ -1,6 +1,7 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
// Copyright 2012 the V8 project authors. All rights reserved.
// Copyright 2021 Moonchild Productions. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -14,6 +15,9 @@
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
// * Neither the name of Moonchild Productions nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -29,11 +33,23 @@
#include "irregexp/RegExpParser.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Move.h"
#include "js/RegExpFlags.h"
#include "frontend/TokenStream.h"
#include "vm/ErrorReporting.h"
#include "vm/StringBuffer.h"
using namespace js;
using namespace js::irregexp;
using mozilla::Move;
using mozilla::PointerRangeSize;
using JS::RegExpFlag;
using JS::RegExpFlags;
// ----------------------------------------------------------------------------
// RegExpBuilder
@@ -220,20 +236,20 @@ RegExpBuilder::AddQuantifierToAtom(int min, int max,
template <typename CharT>
RegExpParser<CharT>::RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc,
const CharT* chars, const CharT* end, bool multiline_mode,
bool unicode, bool ignore_case, bool dotall)
RegExpFlags flags, const CharT* chars, const CharT* end)
: ts(ts),
alloc(alloc),
captures_(nullptr),
next_pos_(chars),
start_(chars),
next_pos_(start_),
end_(end),
current_(kEndMarker),
capture_count_(0),
has_more_(true),
multiline_(multiline_mode),
unicode_(unicode),
ignore_case_(ignore_case),
dotall_(dotall),
multiline_(flags.multiline()),
unicode_(flags.unicode()),
ignore_case_(flags.ignoreCase()),
dotall_(flags.dotAll()),
simple_(false),
contains_anchor_(false),
is_scanned_for_captures_(false)
@@ -241,12 +257,65 @@ RegExpParser<CharT>::RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc,
Advance();
}
template <typename CharT>
void
RegExpParser<CharT>::SyntaxError(unsigned errorNumber, ...)
{
ErrorMetadata err;
ts.fillExcludingContext(&err, ts.currentToken().pos.begin);
// For most error reporting, the line of context derives from the token
// stream. So when location information doesn't come from the token
// stream, we can't give a line of context. But here the "line of context"
// can be (and is) derived from the pattern text, so we can provide it no
// matter if the location is derived from the caller.
size_t offset = PointerRangeSize(start_, next_pos_ - 1);
size_t end = PointerRangeSize(start_, end_);
const CharT* windowStart = (offset > ErrorMetadata::lineOfContextRadius)
? start_ + (offset - ErrorMetadata::lineOfContextRadius)
: start_;
const CharT* windowEnd = (end - offset > ErrorMetadata::lineOfContextRadius)
? start_ + offset + ErrorMetadata::lineOfContextRadius
: end_;
size_t windowLength = PointerRangeSize(windowStart, windowEnd);
MOZ_ASSERT(windowLength <= ErrorMetadata::lineOfContextRadius * 2);
// Create the windowed string, not including the potential line
// terminator.
StringBuffer windowBuf(ts.context());
if (!windowBuf.append(windowStart, windowEnd))
return;
// The line of context must be null-terminated, and StringBuffer doesn't
// make that happen unless we force it to.
if (!windowBuf.append('\0'))
return;
err.lineOfContext.reset(windowBuf.stealChars());
if (!err.lineOfContext)
return;
err.lineLength = windowLength;
err.tokenOffset = offset - (windowStart - start_);
va_list args;
va_start(args, errorNumber);
ReportCompileError(ts.context()->asJSContext(), Move(err), nullptr, JSREPORT_ERROR, errorNumber, args);
va_end(args);
}
template <typename CharT>
RegExpTree*
RegExpParser<CharT>::ReportError(unsigned errorNumber, const char* param /* = nullptr */)
{
gc::AutoSuppressGC suppressGC(ts.context());
ts.reportError(errorNumber, param);
SyntaxError(errorNumber, param);
return nullptr;
}
@@ -1794,7 +1863,7 @@ RegExpParser<CharT>::ParseDisjunction()
break;
case '{': {
if (unicode_)
return ReportError(JSMSG_RAW_BRACE_IN_REGEP);
return ReportError(JSMSG_RAW_BRACE_IN_REGEXP);
int dummy;
if (ParseIntervalQuantifier(&dummy, &dummy))
return ReportError(JSMSG_NOTHING_TO_REPEAT);
@@ -1812,9 +1881,9 @@ RegExpParser<CharT>::ParseDisjunction()
else if (unicode::IsTrailSurrogate(c))
builder->AddAtom(TrailSurrogateAtom(alloc, c));
else if (c == ']')
return ReportError(JSMSG_RAW_BRACKET_IN_REGEP);
return ReportError(JSMSG_RAW_BRACKET_IN_REGEXP);
else if (c == '}')
return ReportError(JSMSG_RAW_BRACE_IN_REGEP);
return ReportError(JSMSG_RAW_BRACE_IN_REGEXP);
else
builder->AddCharacter(c);
Advance();
@@ -1875,10 +1944,11 @@ template class irregexp::RegExpParser<char16_t>;
template <typename CharT>
static bool
ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* chars, size_t length,
bool multiline, bool match_only, bool unicode, bool ignore_case,
bool global, bool sticky, bool dotall, RegExpCompileData* data)
bool match_only, RegExpFlags flags, RegExpCompileData* data)
{
if (match_only) {
// We shouldn't strip pattern for exec, or test with global/sticky,
// to reflect correct match position and lastIndex.
if (match_only && !flags.global() && !flags.sticky()) {
// Try to strip a leading '.*' from the RegExp, but only if it is not
// followed by a '?' (which will affect how the .* is parsed). This
// pattern will affect the captures produced by the RegExp, but not
@@ -1896,14 +1966,14 @@ ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* chars, si
// 2. global and sticky flags are not set, as lastIndex needs to be
// set properly on global or sticky match
if (length >= 3 && !HasRegExpMetaChars(chars, length - 2) &&
!global && !sticky &&
!flags.global() && !flags.sticky() &&
chars[length - 2] == '.' && chars[length - 1] == '*')
{
length -= 2;
}
}
RegExpParser<CharT> parser(ts, &alloc, chars, chars + length, multiline, unicode, ignore_case, dotall);
RegExpParser<CharT> parser(ts, &alloc, flags, chars, chars + length);
data->tree = parser.ParsePattern();
if (!data->tree)
return false;
@@ -1916,15 +1986,14 @@ ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* chars, si
bool
irregexp::ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str,
bool multiline, bool match_only, bool unicode, bool ignore_case,
bool global, bool sticky, bool dotall, RegExpCompileData* data)
bool match_only, RegExpFlags flags, RegExpCompileData* data)
{
JS::AutoCheckCannotGC nogc;
return str->hasLatin1Chars()
? ::ParsePattern(ts, alloc, str->latin1Chars(nogc), str->length(),
multiline, match_only, unicode, ignore_case, global, sticky, dotall, data)
match_only, flags, data)
: ::ParsePattern(ts, alloc, str->twoByteChars(nogc), str->length(),
multiline, match_only, unicode, ignore_case, global, sticky, dotall, data);
match_only, flags, data);
}
template <typename CharT>
@@ -1933,8 +2002,12 @@ ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* cha
bool unicode, bool dotall)
{
LifoAllocScope scope(&alloc);
RegExpParser<CharT> parser(ts, &alloc, chars, chars + length, false, unicode, dotall, false);
RegExpFlags flags = RegExpFlag::NoFlags;
if (unicode)
flags |= RegExpFlag::Unicode;
if (dotall)
flags |= RegExpFlag::DotAll;
RegExpParser<CharT> parser(ts, &alloc, flags, chars, chars + length);
return parser.ParsePattern() != nullptr;
}
+16 -4
View File
@@ -1,6 +1,7 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
// Copyright 2012 the V8 project authors. All rights reserved.
// Copyright 2021 Moonchild Productions. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -14,6 +15,9 @@
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
// * Neither the name of Moonchild Productions nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -30,7 +34,10 @@
#ifndef V8_PARSER_H_
#define V8_PARSER_H_
#include <stdarg.h>
#include "irregexp/RegExpAST.h"
#include "js/RegExpFlags.h"
namespace js {
@@ -42,8 +49,7 @@ namespace irregexp {
bool
ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str,
bool multiline, bool match_only, bool unicode, bool ignore_case,
bool global, bool sticky, bool dotall, RegExpCompileData* data);
bool match_only, JS::RegExpFlags flags, RegExpCompileData* data);
bool
ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str,
@@ -176,8 +182,7 @@ class RegExpParser
{
public:
RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc,
const CharT* chars, const CharT* end, bool multiline_mode, bool unicode,
bool ignore_case, bool dotall);
JS::RegExpFlags flags, const CharT* chars, const CharT* end);
RegExpTree* ParsePattern();
RegExpTree* ParseDisjunction();
@@ -210,7 +215,13 @@ class RegExpParser
bool ParseBackReferenceIndex(int* index_out);
bool ParseClassAtom(char16_t* char_class, widechar *value);
private:
void SyntaxError(unsigned errorNumber, ...);
public:
RegExpTree* ReportError(unsigned errorNumber, const char* param = nullptr);
void Advance();
void Advance(int dist) {
next_pos_ += dist - 1;
@@ -290,6 +301,7 @@ class RegExpParser
frontend::TokenStream& ts;
LifoAlloc* alloc;
RegExpCaptureVector* captures_;
const CharT* const start_;
const CharT* next_pos_;
const CharT* end_;
widechar current_;
+81 -57
View File
@@ -24,7 +24,9 @@
#include "builtin/Eval.h"
#include "builtin/TypedObject.h"
#include "gc/Nursery.h"
#ifndef JS_NEW_REGEXP
#include "irregexp/NativeRegExpMacroAssembler.h"
#endif
#include "jit/AtomicOperations.h"
#include "jit/BaselineCompiler.h"
#include "jit/IonBuilder.h"
@@ -38,6 +40,7 @@
#include "jit/MoveEmitter.h"
#include "jit/RangeAnalysis.h"
#include "jit/SharedICHelpers.h"
#include "js/RegExpFlags.h"
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"
#include "vm/MatchPairs.h"
@@ -999,25 +1002,49 @@ CodeGenerator::visitRegExp(LRegExp* lir)
callVM(CloneRegExpObjectInfo, lir);
}
#ifdef JS_NEW_REGEXP
static const size_t InputOutputDataSize = 0;
#else
static const size_t InputOutputDataSize = sizeof(irregexp::InputOutputData);
#endif
// Amount of space to reserve on the stack when executing RegExps inline.
static const size_t RegExpReservedStack = sizeof(irregexp::InputOutputData)
static const size_t RegExpReservedStack = InputOutputDataSize
+ sizeof(MatchPairs)
+ RegExpObject::MaxPairCount * sizeof(MatchPair);
static size_t
RegExpPairsVectorStartOffset(size_t inputOutputDataStartOffset)
{
return inputOutputDataStartOffset + sizeof(irregexp::InputOutputData) + sizeof(MatchPairs);
return inputOutputDataStartOffset + InputOutputDataSize + sizeof(MatchPairs);
}
static Address
RegExpPairCountAddress(MacroAssembler& masm, size_t inputOutputDataStartOffset)
{
return Address(masm.getStackPointer(), inputOutputDataStartOffset
+ sizeof(irregexp::InputOutputData)
+ InputOutputDataSize
+ MatchPairs::offsetOfPairCount());
}
#ifdef JS_NEW_REGEXP
// Prepare an InputOutputData and optional MatchPairs which space has been
// allocated for on the stack, and try to execute a RegExp on a string input.
// If the RegExp was successfully executed and matched the input, fallthrough,
// otherwise jump to notFound or failure.
static bool
PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Register input,
Register lastIndex,
Register temp1, Register temp2, Register temp3,
size_t inputOutputDataStartOffset,
// bool stringsCanBeInNursery,
Label* notFound, Label* failure) {
MOZ_CRASH("TODO");
}
#else
// Prepare an InputOutputData and optional MatchPairs which space has been
// allocated for on the stack, and try to execute a RegExp on a string input.
// If the RegExp was successfully executed and matched the input, fallthrough,
@@ -1027,7 +1054,6 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
Register lastIndex,
Register temp1, Register temp2, Register temp3,
size_t inputOutputDataStartOffset,
RegExpShared::CompilationMode mode,
Label* notFound, Label* failure)
{
size_t matchPairsStartOffset = inputOutputDataStartOffset + sizeof(irregexp::InputOutputData);
@@ -1055,20 +1081,15 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global());
if (!res)
return false;
#ifdef JS_USE_LINK_REGISTER
if (mode != RegExpShared::MatchOnly)
masm.pushReturnAddress();
#endif
if (mode == RegExpShared::Normal) {
// First, fill in a skeletal MatchPairs instance on the stack. This will be
// passed to the OOL stub in the caller if we aren't able to execute the
// RegExp inline, and that stub needs to be able to determine whether the
// execution finished successfully.
masm.store32(Imm32(1), pairCountAddress);
masm.store32(Imm32(-1), pairsVectorAddress);
masm.computeEffectiveAddress(pairsVectorAddress, temp1);
masm.storePtr(temp1, pairsPointerAddress);
}
// First, fill in a skeletal MatchPairs instance on the stack. This will be
// passed to the OOL stub in the caller if we aren't able to execute the
// RegExp inline, and that stub needs to be able to determine whether the
// execution finished successfully.
masm.store32(Imm32(1), pairCountAddress);
masm.store32(Imm32(-1), pairsVectorAddress);
masm.computeEffectiveAddress(pairsVectorAddress, temp1);
masm.storePtr(temp1, pairsPointerAddress);
// Check for a linear input string.
masm.branchIfRopeOrExternal(input, temp1, failure);
@@ -1083,7 +1104,7 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
Label done;
masm.branchTest32(Assembler::Zero, Address(temp1, RegExpShared::offsetOfFlags()),
Imm32(UnicodeFlag), &done);
Imm32(int32_t(JS::RegExpFlag::Unicode)), &done);
// If input is latin1, there should not be surrogate pair.
masm.branchLatin1String(input, &done);
@@ -1117,15 +1138,12 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
masm.bind(&done);
}
if (mode == RegExpShared::Normal) {
// Don't handle RegExps with excessive parens.
masm.load32(Address(temp1, RegExpShared::offsetOfParenCount()), temp2);
masm.branch32(Assembler::AboveOrEqual, temp2, Imm32(RegExpObject::MaxPairCount), failure);
// Don't handle RegExps with excessive parens.
masm.load32(Address(temp1, RegExpShared::offsetOfPairCount()), temp2);
masm.branch32(Assembler::Above, temp2, Imm32(RegExpObject::MaxPairCount), failure);
// Fill in the paren count in the MatchPairs on the stack.
masm.add32(Imm32(1), temp2);
masm.store32(temp2, pairCountAddress);
}
// Fill in the paren count in the MatchPairs on the stack.
masm.store32(temp2, pairCountAddress);
// Load the code pointer for the type of input string we have, and compute
// the input start/end pointers in the InputOutputData.
@@ -1139,14 +1157,16 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
masm.branchLatin1String(input, &isLatin1);
{
masm.lshiftPtr(Imm32(1), temp3);
masm.loadPtr(Address(temp1, RegExpShared::offsetOfTwoByteJitCode(mode)),
codePointer);
masm.loadPtr(
Address(temp1, RegExpShared::offsetOfJitCode(/*latin1 =*/false)),
codePointer);
}
masm.jump(&done);
{
masm.bind(&isLatin1);
masm.loadPtr(Address(temp1, RegExpShared::offsetOfLatin1JitCode(mode)),
codePointer);
masm.loadPtr(
Address(temp1, RegExpShared::offsetOfJitCode(/*latin1 = */ true)),
codePointer);
}
masm.bind(&done);
@@ -1159,14 +1179,9 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
masm.loadPtr(Address(codePointer, JitCode::offsetOfCode()), codePointer);
// Finish filling in the InputOutputData instance on the stack.
if (mode == RegExpShared::Normal) {
masm.computeEffectiveAddress(Address(masm.getStackPointer(), matchPairsStartOffset), temp2);
masm.storePtr(temp2, matchesPointerAddress);
} else {
// Use InputOutputData.endIndex itself for output.
masm.computeEffectiveAddress(endIndexAddress, temp2);
masm.storePtr(temp2, endIndexAddress);
}
masm.computeEffectiveAddress(Address(masm.getStackPointer(), matchPairsStartOffset), temp2);
masm.storePtr(temp2, matchesPointerAddress);
masm.storePtr(lastIndex, startIndexAddress);
masm.store32(Imm32(0), matchResultAddress);
@@ -1216,14 +1231,11 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
masm.load32(Address(temp2, RegExpShared::offsetOfFlags()), temp3);
masm.store32(temp3, Address(temp1, RegExpStatics::offsetOfLazyFlags()));
if (mode == RegExpShared::MatchOnly) {
// endIndex is passed via temp3.
masm.load32(endIndexAddress, temp3);
}
return true;
}
#endif //JS_NEW_REGEXP
static void
CopyStringChars(MacroAssembler& masm, Register to, Register from, Register len,
Register byteOpScratch, size_t fromWidth, size_t toWidth);
@@ -1508,7 +1520,7 @@ JitCompartment::generateRegExpMatcherStub(JSContext* cx)
Label notFound, oolEntry;
if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex,
temp1, temp2, temp5, inputOutputDataStartOffset,
RegExpShared::Normal, &notFound, &oolEntry))
&notFound, &oolEntry))
{
return nullptr;
}
@@ -1728,8 +1740,7 @@ CodeGenerator::visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool)
regs.take(regexp);
Register temp = regs.takeAny();
masm.computeEffectiveAddress(Address(masm.getStackPointer(),
sizeof(irregexp::InputOutputData)), temp);
masm.computeEffectiveAddress(Address(masm.getStackPointer(), InputOutputDataSize), temp);
pushArg(temp);
pushArg(lastIndex);
@@ -1800,13 +1811,17 @@ JitCompartment::generateRegExpSearcherStub(JSContext* cx)
MacroAssembler masm(cx);
#ifdef JS_USE_LINK_REGISTER
masm.pushReturnAddress();
#endif
// The InputOutputData is placed above the return address on the stack.
size_t inputOutputDataStartOffset = sizeof(void*);
Label notFound, oolEntry;
if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex,
temp1, temp2, temp3, inputOutputDataStartOffset,
RegExpShared::Normal, &notFound, &oolEntry))
&notFound, &oolEntry))
{
return nullptr;
}
@@ -1885,8 +1900,7 @@ CodeGenerator::visitOutOfLineRegExpSearcher(OutOfLineRegExpSearcher* ool)
regs.take(regexp);
Register temp = regs.takeAny();
masm.computeEffectiveAddress(Address(masm.getStackPointer(),
sizeof(irregexp::InputOutputData)), temp);
masm.computeEffectiveAddress(Address(masm.getStackPointer(), InputOutputDataSize), temp);
pushArg(temp);
pushArg(lastIndex);
@@ -1952,20 +1966,30 @@ JitCompartment::generateRegExpTesterStub(JSContext* cx)
Register temp2 = regs.takeAny();
Register temp3 = regs.takeAny();
masm.reserveStack(sizeof(irregexp::InputOutputData));
masm.reserveStack(RegExpReservedStack);
Label notFound, oolEntry;
if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex,
temp1, temp2, temp3, 0,
RegExpShared::MatchOnly, &notFound, &oolEntry))
&notFound, &oolEntry))
{
return nullptr;
}
Label done;
// temp3 contains endIndex.
masm.move32(temp3, result);
// In visitRegExpMatcher and visitRegExpSearcher, we reserve stack space
// before calling the stub. For RegExpTester we call the stub before reserving
// stack space, so the offset of the InputOutputData is 0.
size_t inputOutputDataStartOffset = 0;
size_t pairsVectorStartOffset =
RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
Address matchPairLimit(masm.getStackPointer(),
pairsVectorStartOffset + offsetof(MatchPair, limit));
// RegExpTester returns the end index of the match to update lastIndex.
masm.load32(matchPairLimit, result);
masm.jump(&done);
masm.bind(&notFound);
@@ -1976,7 +2000,7 @@ JitCompartment::generateRegExpTesterStub(JSContext* cx)
masm.move32(Imm32(RegExpTesterResultFailed), result);
masm.bind(&done);
masm.freeStack(sizeof(irregexp::InputOutputData));
masm.freeStack(RegExpReservedStack);
masm.ret();
Linker linker(masm);
@@ -8189,7 +8213,7 @@ CodeGenerator::visitStoreElementV(LStoreElementV* lir)
template <typename T> void
CodeGenerator::emitStoreElementHoleT(T* lir)
{
static_assert(std::is_same<T, LStoreElementHoleT>::value || std::is_same<T, LFallibleStoreElementT>::value,
static_assert(std::is_same_v<T, LStoreElementHoleT> || std::is_same_v<T, LFallibleStoreElementT>,
"emitStoreElementHoleT called with unexpected argument type");
OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir);
@@ -8248,7 +8272,7 @@ CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir)
template <typename T> void
CodeGenerator::emitStoreElementHoleV(T* lir)
{
static_assert(std::is_same<T, LStoreElementHoleV>::value || std::is_same<T, LFallibleStoreElementV>::value,
static_assert(std::is_same_v<T, LStoreElementHoleV> || std::is_same_v<T, LFallibleStoreElementV>,
"emitStoreElementHoleV called with unexpected parameter type");
OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir);
+3
View File
@@ -145,6 +145,9 @@ DefaultJitOptions::DefaultJitOptions()
// Toggles whether large scripts are rejected.
SET_DEFAULT(limitScriptSize, true);
// Toggles whether RegExps compile to native code after a threshold.
SET_DEFAULT(nativeRegExp, true);
// Toggles whether functions may be entered at loop headers.
SET_DEFAULT(osr, true);
+1
View File
@@ -68,6 +68,7 @@ struct DefaultJitOptions
bool eagerCompilation;
bool forceInlineCaches;
bool limitScriptSize;
bool nativeRegExp;
bool osr;
bool asmJSAtomicsEnable;
bool wasmTestMode;
+5 -2
View File
@@ -19,6 +19,7 @@
#include "jit/Lowering.h"
#include "jit/MIR.h"
#include "jit/MIRGraph.h"
#include "js/RegExpFlags.h"
#include "vm/ArgumentsObject.h"
#include "vm/ProxyObject.h"
#include "vm/SelfHosting.h"
@@ -38,6 +39,8 @@ using JS::DoubleNaNValue;
using JS::TrackedOutcome;
using JS::TrackedStrategy;
using JS::TrackedTypeSite;
using JS::RegExpFlag;
using JS::RegExpFlags;
namespace js {
namespace jit {
@@ -381,7 +384,7 @@ IonBuilder::inlineNativeGetter(CallInfo& callInfo, JSFunction* target)
}
// Try to optimize RegExp getters.
RegExpFlag mask = NoFlags;
RegExpFlags mask = RegExpFlag::NoFlags;
if (RegExpObject::isOriginalFlagGetter(native, &mask)) {
const Class* clasp = thisTypes->getKnownClass(constraints());
if (clasp != &RegExpObject::class_)
@@ -390,7 +393,7 @@ IonBuilder::inlineNativeGetter(CallInfo& callInfo, JSFunction* target)
MLoadFixedSlot* flags = MLoadFixedSlot::New(alloc(), thisArg, RegExpObject::flagsSlot());
current->add(flags);
flags->setResultType(MIRType::Int32);
MConstant* maskConst = MConstant::New(alloc(), Int32Value(mask));
MConstant* maskConst = MConstant::New(alloc(), Int32Value(mask.value()));
current->add(maskConst);
MBitAnd* maskedFlag = MBitAnd::New(alloc(), flags, maskConst);
maskedFlag->setInt32Specialization();
+10 -2
View File
@@ -507,12 +507,20 @@ MSG_DEF(JSMSG_NEWREGEXP_FLAGGED, 0, JSEXN_TYPEERR, "can't supply flags whe
MSG_DEF(JSMSG_NOTHING_TO_REPEAT, 0, JSEXN_SYNTAXERR, "nothing to repeat")
MSG_DEF(JSMSG_NUMBERS_OUT_OF_ORDER, 0, JSEXN_SYNTAXERR, "numbers out of order in {} quantifier.")
MSG_DEF(JSMSG_RANGE_WITH_CLASS_ESCAPE, 0, JSEXN_SYNTAXERR, "character class escape cannot be used in class range in regular expression")
MSG_DEF(JSMSG_RAW_BRACE_IN_REGEP, 0, JSEXN_SYNTAXERR, "raw brace is not allowed in regular expression with unicode flag")
MSG_DEF(JSMSG_RAW_BRACKET_IN_REGEP, 0, JSEXN_SYNTAXERR, "raw bracket is not allowed in regular expression with unicode flag")
MSG_DEF(JSMSG_RAW_BRACE_IN_REGEXP, 0, JSEXN_SYNTAXERR, "raw brace is not allowed in regular expression with unicode flag")
MSG_DEF(JSMSG_RAW_BRACKET_IN_REGEXP, 0, JSEXN_SYNTAXERR, "raw bracket is not allowed in regular expression with unicode flag")
MSG_DEF(JSMSG_TOO_MANY_PARENS, 0, JSEXN_INTERNALERR, "too many parentheses in regular expression")
MSG_DEF(JSMSG_UNICODE_OVERFLOW, 1, JSEXN_SYNTAXERR, "Unicode codepoint must not be greater than 0x10FFFF in {0}")
MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression")
MSG_DEF(JSMSG_UNTERM_CLASS, 0, JSEXN_SYNTAXERR, "unterminated character class")
MSG_DEF(JSMSG_INVALID_PROPERTY_NAME, 0, JSEXN_SYNTAXERR, "invalid property name in regular expression")
MSG_DEF(JSMSG_INVALID_CLASS_PROPERTY_NAME, 0, JSEXN_SYNTAXERR, "invalid class property name in regular expression")
MSG_DEF(JSMSG_INCOMPLETE_QUANTIFIER, 0, JSEXN_SYNTAXERR, "incomplete quantifier in regular expression")
MSG_DEF(JSMSG_INVALID_QUANTIFIER, 0, JSEXN_SYNTAXERR, "invalid quantifier in regular expression")
MSG_DEF(JSMSG_INVALID_CAPTURE_NAME, 0, JSEXN_SYNTAXERR, "invalid capture group name in regular expression")
MSG_DEF(JSMSG_DUPLICATE_CAPTURE_NAME, 0, JSEXN_SYNTAXERR, "duplicate capture group name in regular expression")
MSG_DEF(JSMSG_INVALID_NAMED_REF, 0, JSEXN_SYNTAXERR, "invalid named reference in regular expression")
MSG_DEF(JSMSG_INVALID_NAMED_CAPTURE_REF, 0, JSEXN_SYNTAXERR, "invalid named capture reference in regular expression")
// Self-hosting
MSG_DEF(JSMSG_DEFAULT_LOCALE_ERROR, 0, JSEXN_ERR, "internal error getting the default locale")
+10 -6
View File
@@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "js/RegExp.h"
#include "js/RegExpFlags.h"
#include "jsapi-tests/tests.h"
BEGIN_TEST(testObjectIsRegExp)
@@ -12,12 +14,12 @@ BEGIN_TEST(testObjectIsRegExp)
EVAL("new Object", &val);
JS::RootedObject obj(cx, val.toObjectOrNull());
CHECK(JS_ObjectIsRegExp(cx, obj, &isRegExp));
CHECK(JS::ObjectIsRegExp(cx, obj, &isRegExp));
CHECK(!isRegExp);
EVAL("/foopy/", &val);
obj = val.toObjectOrNull();
CHECK(JS_ObjectIsRegExp(cx, obj, &isRegExp));
CHECK(JS::ObjectIsRegExp(cx, obj, &isRegExp));
CHECK(isRegExp);
return true;
@@ -31,15 +33,16 @@ BEGIN_TEST(testGetRegExpFlags)
EVAL("/foopy/", &val);
obj = val.toObjectOrNull();
CHECK_EQUAL(JS_GetRegExpFlags(cx, obj), 0u);
CHECK_EQUAL(JS::GetRegExpFlags(cx, obj), JS::RegExpFlags(JS::RegExpFlag::NoFlags));
EVAL("/foopy/g", &val);
obj = val.toObjectOrNull();
CHECK_EQUAL(JS_GetRegExpFlags(cx, obj), JSREG_GLOB);
CHECK_EQUAL(JS::GetRegExpFlags(cx, obj), JS::RegExpFlags(JS::RegExpFlag::Global));
EVAL("/foopy/gi", &val);
obj = val.toObjectOrNull();
CHECK_EQUAL(JS_GetRegExpFlags(cx, obj), (JSREG_FOLD | JSREG_GLOB));
CHECK_EQUAL(JS::GetRegExpFlags(cx, obj),
JS::RegExpFlags(JS::RegExpFlag::Global | JS::RegExpFlag::IgnoreCase));
return true;
}
@@ -52,7 +55,8 @@ BEGIN_TEST(testGetRegExpSource)
EVAL("/foopy/", &val);
obj = val.toObjectOrNull();
JSString* source = JS_GetRegExpSource(cx, obj);
JSString* source = JS::GetRegExpSource(cx, obj);
CHECK(source);
CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(source), "foopy"));
return true;
+21
View File
@@ -18,6 +18,7 @@
#include "jscntxt.h"
#include "jsgc.h"
#include "js/RegExpFlags.h"
#include "js/Vector.h"
/* Note: Aborts on OOM. */
@@ -134,6 +135,26 @@ class JSAPITest
return JSAPITestString(v ? "true" : "false");
}
JSAPITestString toSource(JS::RegExpFlags flags) {
JSAPITestString str;
if (flags.global()) {
str += "g";
}
if (flags.ignoreCase()) {
str += "i";
}
if (flags.multiline()) {
str += "m";
}
if (flags.unicode()) {
str += "u";
}
if (flags.sticky()) {
str += "y";
}
return str;
}
JSAPITestString toSource(JSAtom* v) {
JS::RootedValue val(cx, JS::StringValue((JSString*)v));
return jsvalToSource(val);
-133
View File
@@ -46,7 +46,6 @@
#include "builtin/Intl.h"
#include "builtin/MapObject.h"
#include "builtin/Promise.h"
#include "builtin/RegExp.h"
#include "builtin/SymbolObject.h"
#ifdef ENABLE_SIMD
# include "builtin/SIMD.h"
@@ -76,7 +75,6 @@
#include "vm/ErrorObject.h"
#include "vm/HelperThreads.h"
#include "vm/Interpreter.h"
#include "vm/RegExpStatics.h"
#include "vm/Runtime.h"
#include "vm/SavedStacks.h"
#include "vm/SelfHosting.h"
@@ -6016,137 +6014,6 @@ JS_ObjectIsDate(JSContext* cx, HandleObject obj, bool* isDate)
/************************************************************************/
/*
* Regular Expressions.
*/
JS_PUBLIC_API(JSObject*)
JS_NewRegExpObject(JSContext* cx, const char* bytes, size_t length, unsigned flags)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
ScopedJSFreePtr<char16_t> chars(InflateString(cx, bytes, &length));
if (!chars)
return nullptr;
RegExpObject* reobj = RegExpObject::create(cx, chars, length,
RegExpFlag(flags), nullptr, cx->tempLifoAlloc());
return reobj;
}
JS_PUBLIC_API(JSObject*)
JS_NewUCRegExpObject(JSContext* cx, const char16_t* chars, size_t length, unsigned flags)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
return RegExpObject::create(cx, chars, length,
RegExpFlag(flags), nullptr, cx->tempLifoAlloc());
}
JS_PUBLIC_API(bool)
JS_SetRegExpInput(JSContext* cx, HandleObject obj, HandleString input)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, input);
Handle<GlobalObject*> global = obj.as<GlobalObject>();
RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global);
if (!res)
return false;
res->reset(cx, input);
return true;
}
JS_PUBLIC_API(bool)
JS_ClearRegExpStatics(JSContext* cx, HandleObject obj)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
MOZ_ASSERT(obj);
Handle<GlobalObject*> global = obj.as<GlobalObject>();
RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global);
if (!res)
return false;
res->clear();
return true;
}
JS_PUBLIC_API(bool)
JS_ExecuteRegExp(JSContext* cx, HandleObject obj, HandleObject reobj, char16_t* chars,
size_t length, size_t* indexp, bool test, MutableHandleValue rval)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
Handle<GlobalObject*> global = obj.as<GlobalObject>();
RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global);
if (!res)
return false;
RootedLinearString input(cx, NewStringCopyN<CanGC>(cx, chars, length));
if (!input)
return false;
return ExecuteRegExpLegacy(cx, res, reobj.as<RegExpObject>(), input, indexp, test, rval);
}
JS_PUBLIC_API(bool)
JS_ExecuteRegExpNoStatics(JSContext* cx, HandleObject obj, char16_t* chars, size_t length,
size_t* indexp, bool test, MutableHandleValue rval)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
RootedLinearString input(cx, NewStringCopyN<CanGC>(cx, chars, length));
if (!input)
return false;
return ExecuteRegExpLegacy(cx, nullptr, obj.as<RegExpObject>(), input, indexp, test,
rval);
}
JS_PUBLIC_API(bool)
JS_ObjectIsRegExp(JSContext* cx, HandleObject obj, bool* isRegExp)
{
assertSameCompartment(cx, obj);
ESClass cls;
if (!GetBuiltinClass(cx, obj, &cls))
return false;
*isRegExp = cls == ESClass::RegExp;
return true;
}
JS_PUBLIC_API(unsigned)
JS_GetRegExpFlags(JSContext* cx, HandleObject obj)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
RegExpGuard shared(cx);
if (!RegExpToShared(cx, obj, &shared))
return false;
return shared.re()->getFlags();
}
JS_PUBLIC_API(JSString*)
JS_GetRegExpSource(JSContext* cx, HandleObject obj)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
RegExpGuard shared(cx);
if (!RegExpToShared(cx, obj, &shared))
return nullptr;
return shared.re()->getSource();
}
/************************************************************************/
JS_PUBLIC_API(bool)
JS_SetDefaultLocale(JSContext* cx, const char* locale)
{
-10
View File
@@ -817,16 +817,6 @@ js::ReportErrorNumberUCArray(JSContext* cx, unsigned flags, JSErrorCallback call
return warning;
}
void
js::CallWarningReporter(JSContext* cx, JSErrorReport* reportp)
{
MOZ_ASSERT(reportp);
MOZ_ASSERT(JSREPORT_IS_WARNING(reportp->flags));
if (JS::WarningReporter warningReporter = cx->runtime()->warningReporter)
warningReporter(cx, reportp);
}
bool
js::ReportIsNotDefined(JSContext* cx, HandleId id)
{
+2 -7
View File
@@ -15,6 +15,7 @@
#include "js/Utility.h"
#include "js/Vector.h"
#include "vm/Caches.h"
#include "vm/ErrorReporting.h"
#include "vm/Runtime.h"
#ifdef _MSC_VER
@@ -311,7 +312,7 @@ class ExclusiveContext : public ContextFriendFields,
}
// Methods specific to any HelperThread for the context.
bool addPendingCompileError(frontend::CompileError** err);
bool addPendingCompileError(CompileError** err);
void addPendingOverRecursed();
void addPendingOutOfMemory();
};
@@ -641,12 +642,6 @@ extern bool
PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
JSErrorReport* report, bool reportWarnings);
/*
* Send a JSErrorReport to the warningReporter callback.
*/
void
CallWarningReporter(JSContext* cx, JSErrorReport* report);
extern bool
ReportIsNotDefined(JSContext* cx, HandlePropertyName name);
-1
View File
@@ -361,7 +361,6 @@ class JSFunction : public js::NativeObject
MOZ_ASSERT(!hasCompileTimeName());
MOZ_ASSERT(!hasGuessedAtom());
MOZ_ASSERT(!isBoundFunction());
MOZ_ASSERT(js::AtomIsMarked(zone(), atom));
atom_ = atom;
flags_ |= HAS_GUESSED_ATOM;
}
+5 -5
View File
@@ -2432,10 +2432,10 @@ static JSString* ReplaceAll(JSContext* cx, JSLinearString* str,
// Step 13.
StringBuffer result(cx);
if (std::is_same<StrChar, char16_t>::value ||
std::is_same<RepChar, char16_t>::value) {
if (std::is_same_v<StrChar, char16_t> ||
std::is_same_v<RepChar, char16_t>) {
if (!result.ensureTwoByteChars()) {
return nullptr;
return nullptr;
}
}
@@ -2515,8 +2515,8 @@ static JSString* ReplaceAllInterleave(JSContext* cx, JSLinearString* str,
// Step 13.
StringBuffer result(cx);
if (std::is_same<StrChar, char16_t>::value ||
std::is_same<RepChar, char16_t>::value) {
if (std::is_same_v<StrChar, char16_t> ||
std::is_same_v<RepChar, char16_t>) {
if (!result.ensureTwoByteChars()) {
return nullptr;
}
+15 -8
View File
@@ -92,6 +92,8 @@ EXPORTS.js += [
'../public/ProfilingStack.h',
'../public/Proxy.h',
'../public/Realm.h',
'../public/RegExp.h',
'../public/RegExpFlags.h',
'../public/RequiredDefines.h',
'../public/RootingAPI.h',
'../public/SliceBudget.h',
@@ -153,14 +155,6 @@ UNIFIED_SOURCES += [
'gc/Tracer.cpp',
'gc/Verifier.cpp',
'gc/Zone.cpp',
'irregexp/NativeRegExpMacroAssembler.cpp',
'irregexp/RegExpAST.cpp',
'irregexp/RegExpCharacters.cpp',
'irregexp/RegExpEngine.cpp',
'irregexp/RegExpInterpreter.cpp',
'irregexp/RegExpMacroAssembler.cpp',
'irregexp/RegExpParser.cpp',
'irregexp/RegExpStack.cpp',
'jit/AliasAnalysis.cpp',
'jit/AliasAnalysisShared.cpp',
'jit/AlignmentMaskAnalysis.cpp',
@@ -275,6 +269,7 @@ UNIFIED_SOURCES += [
'vm/DebuggerMemory.cpp',
'vm/EnvironmentObject.cpp',
'vm/ErrorObject.cpp',
'vm/ErrorReporting.cpp',
'vm/ForOfIterator.cpp',
'vm/GeneratorObject.cpp',
'vm/GlobalObject.cpp',
@@ -343,6 +338,18 @@ UNIFIED_SOURCES += [
'wasm/WasmTypes.cpp'
]
if not CONFIG['JS_NEW_REGEXP']:
UNIFIED_SOURCES += [
'irregexp/NativeRegExpMacroAssembler.cpp',
'irregexp/RegExpAST.cpp',
'irregexp/RegExpCharacters.cpp',
'irregexp/RegExpEngine.cpp',
'irregexp/RegExpInterpreter.cpp',
'irregexp/RegExpMacroAssembler.cpp',
'irregexp/RegExpParser.cpp',
'irregexp/RegExpStack.cpp',
]
# jsarray.cpp and jsatom.cpp cannot be built in unified mode because
# xpcshell is broken during packaging when compiled with gcc-4.8.2
# builtin/RegExp.cpp cannot be built in unified mode because it is built
+622
View File
@@ -0,0 +1,622 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This Source Code Form is "Incompatible With Secondary Licenses", as
* defined by the Mozilla Public License, v. 2.0.
*/
#include "new-regexp/RegExpAPI.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Casting.h"
#include "jit/JitCommon.h"
#include "new-regexp/regexp-bytecode-generator.h"
#include "new-regexp/regexp-compiler.h"
#include "new-regexp/regexp-interpreter.h"
#include "new-regexp/regexp-macro-assembler-arch.h"
#include "new-regexp/regexp-parser.h"
#include "new-regexp/regexp-shim.h"
#include "new-regexp/regexp.h"
#include "vm/ErrorReporting.h"
#include "vm/RegExpObject.h"
#include "vm/StringBuffer.h"
#include "vm/MatchPairs.h"
namespace js {
namespace irregexp {
using namespace mozilla;
using mozilla::Maybe;
using frontend::TokenStream;
using v8::internal::FlatStringReader;
using v8::internal::HandleScope;
using v8::internal::InputOutputData;
using v8::internal::IrregexpInterpreter;
using v8::internal::NativeRegExpMacroAssembler;
using v8::internal::RegExpBytecodeGenerator;
using v8::internal::RegExpCompileData;
using v8::internal::RegExpCompiler;
using v8::internal::RegExpError;
using v8::internal::RegExpMacroAssembler;
using v8::internal::RegExpNode;
using v8::internal::RegExpParser;
using v8::internal::SMRegExpMacroAssembler;
using v8::internal::Zone;
using V8HandleString = v8::internal::Handle<v8::internal::String>;
using V8HandleRegExp = v8::internal::Handle<v8::internal::JSRegExp>;
using namespace v8::internal::regexp_compiler_constants;
static uint32_t ErrorNumber(RegExpError err) {
switch (err) {
case RegExpError::kNone:
return JSMSG_NOT_AN_ERROR;
case RegExpError::kStackOverflow:
return JSMSG_OVER_RECURSED;
case RegExpError::kAnalysisStackOverflow:
return JSMSG_OVER_RECURSED;
case RegExpError::kTooLarge:
return JSMSG_TOO_MANY_PARENS;
case RegExpError::kUnterminatedGroup:
return JSMSG_MISSING_PAREN;
case RegExpError::kUnmatchedParen:
return JSMSG_UNMATCHED_RIGHT_PAREN;
case RegExpError::kEscapeAtEndOfPattern:
return JSMSG_ESCAPE_AT_END_OF_REGEXP;
case RegExpError::kInvalidPropertyName:
return JSMSG_INVALID_PROPERTY_NAME;
case RegExpError::kInvalidEscape:
return JSMSG_INVALID_IDENTITY_ESCAPE;
case RegExpError::kInvalidDecimalEscape:
return JSMSG_INVALID_DECIMAL_ESCAPE;
case RegExpError::kInvalidUnicodeEscape:
return JSMSG_INVALID_UNICODE_ESCAPE;
case RegExpError::kNothingToRepeat:
return JSMSG_NOTHING_TO_REPEAT;
case RegExpError::kLoneQuantifierBrackets:
// Note: V8 reports the same error for both ']' and '}'.
return JSMSG_RAW_BRACKET_IN_REGEXP;
case RegExpError::kRangeOutOfOrder:
return JSMSG_NUMBERS_OUT_OF_ORDER;
case RegExpError::kIncompleteQuantifier:
return JSMSG_INCOMPLETE_QUANTIFIER;
case RegExpError::kInvalidQuantifier:
return JSMSG_INVALID_QUANTIFIER;
case RegExpError::kInvalidGroup:
return JSMSG_INVALID_GROUP;
case RegExpError::kMultipleFlagDashes:
case RegExpError::kRepeatedFlag:
case RegExpError::kInvalidFlagGroup:
// V8 contains experimental support for turning regexp flags on
// and off in the middle of a regular expression. Unless it
// becomes standardized, SM does not support this feature.
MOZ_CRASH("Mode modifiers not supported");
case RegExpError::kTooManyCaptures:
return JSMSG_TOO_MANY_PARENS;
case RegExpError::kInvalidCaptureGroupName:
return JSMSG_INVALID_CAPTURE_NAME;
case RegExpError::kDuplicateCaptureGroupName:
return JSMSG_DUPLICATE_CAPTURE_NAME;
case RegExpError::kInvalidNamedReference:
return JSMSG_INVALID_NAMED_REF;
case RegExpError::kInvalidNamedCaptureReference:
return JSMSG_INVALID_NAMED_CAPTURE_REF;
case RegExpError::kInvalidClassEscape:
return JSMSG_RANGE_WITH_CLASS_ESCAPE;
case RegExpError::kInvalidClassPropertyName:
return JSMSG_INVALID_CLASS_PROPERTY_NAME;
case RegExpError::kInvalidCharacterClass:
return JSMSG_RANGE_WITH_CLASS_ESCAPE;
case RegExpError::kUnterminatedCharacterClass:
return JSMSG_UNTERM_CLASS;
case RegExpError::kOutOfOrderCharacterClass:
return JSMSG_BAD_CLASS_RANGE;
case RegExpError::NumErrors:
MOZ_CRASH("Unreachable");
}
MOZ_CRASH("Unreachable");
}
Isolate* CreateIsolate(JSContext* cx) {
auto isolate = MakeUnique<Isolate>(cx);
if (!isolate || !isolate->init()) {
return nullptr;
}
return isolate.release();
}
static size_t ComputeColumn(const Latin1Char* begin, const Latin1Char* end) {
return mozilla::PointerRangeSize(begin, end);
}
static size_t ComputeColumn(const char16_t* begin, const char16_t* end) {
return unicode::CountCodePoints(begin, end);
}
// This function is varargs purely so it can call ReportCompileErrorLatin1.
// We never call it with additional arguments.
template <typename CharT>
static void ReportSyntaxError(TokenStream& ts,
RegExpCompileData& result, CharT* start,
size_t length,
...) {
gc::AutoSuppressGC suppressGC(ts.context());
uint32_t errorNumber = ErrorNumber(result.error);
uint32_t offset = std::max(result.error_pos, 0);
MOZ_ASSERT(offset <= length);
ErrorMetadata err;
// Ordinarily this indicates whether line-of-context information can be
// added, but we entirely ignore that here because we create a
// a line of context based on the expression source.
uint32_t location = ts.currentToken().pos.begin;
if (ts.fillExcludingContext(&err, location)) {
// Line breaks are not significant in pattern text in the same way as
// in source text, so act as though pattern text is a single line, then
// compute a column based on "code point" count (treating a lone
// surrogate as a "code point" in UTF-16). Gak.
err.lineNumber = 1;
err.columnNumber =
AssertedCast<uint32_t>(ComputeColumn(start, start + offset));
}
// For most error reporting, the line of context derives from the token
// stream. So when location information doesn't come from the token
// stream, we can't give a line of context. But here the "line of context"
// can be (and is) derived from the pattern text, so we can provide it no
// matter if the location is derived from the caller.
const CharT* windowStart =
(offset > ErrorMetadata::lineOfContextRadius)
? start + (offset - ErrorMetadata::lineOfContextRadius)
: start;
const CharT* windowEnd =
(length - offset > ErrorMetadata::lineOfContextRadius)
? start + offset + ErrorMetadata::lineOfContextRadius
: start + length;
size_t windowLength = PointerRangeSize(windowStart, windowEnd);
MOZ_ASSERT(windowLength <= ErrorMetadata::lineOfContextRadius * 2);
// Create the windowed string, not including the potential line
// terminator.
StringBuffer windowBuf(ts.context());
if (!windowBuf.append(windowStart, windowEnd)) return;
// The line of context must be null-terminated, and StringBuffer doesn't
// make that happen unless we force it to.
if (!windowBuf.append('\0')) return;
err.lineOfContext.reset(windowBuf.stealChars());
if (!err.lineOfContext) return;
err.lineLength = windowLength;
err.tokenOffset = offset - (windowStart - start);
va_list args;
va_start(args, length);
ReportCompileError(ts.context()->asJSContext(),
std::move(err),
nullptr, /* notes/report */
errorNumber,
0 /*flags*/,
args);
va_end(args);
}
static void ReportSyntaxError(TokenStream& ts,
RegExpCompileData& result, HandleAtom pattern) {
JS::AutoCheckCannotGC nogc_;
if (pattern->hasLatin1Chars()) {
ReportSyntaxError(ts, result, pattern->latin1Chars(nogc_),
pattern->length());
} else {
ReportSyntaxError(ts, result, pattern->twoByteChars(nogc_),
pattern->length());
}
}
static bool CheckPatternSyntaxImpl(JSContext* cx, FlatStringReader* pattern,
JS::RegExpFlags flags,
RegExpCompileData* result) {
LifoAllocScope allocScope(&cx->tempLifoAlloc());
Zone zone(allocScope.alloc());
HandleScope handleScope(cx->isolate);
v8::internal::JSRegExp::Flags passflags = static_cast<uint8_t>(flags.value());
return RegExpParser::ParseRegExp(cx->isolate, &zone, pattern, passflags, result);
}
bool CheckPatternSyntax(JSContext* cx, TokenStream& ts,
const mozilla::Range<const char16_t> chars,
JS::RegExpFlags flags) {
FlatStringReader reader(chars.begin().get(), chars.length());
RegExpCompileData result;
if (!CheckPatternSyntaxImpl(cx, &reader, flags, &result)) {
ReportSyntaxError(ts, result, chars.begin().get(), chars.length());
return false;
}
return true;
}
bool CheckPatternSyntax(JSContext* cx, TokenStream& ts,
HandleAtom pattern, JS::RegExpFlags flags) {
FlatStringReader reader(pattern);
RegExpCompileData result;
if (!CheckPatternSyntaxImpl(cx, &reader, flags, &result)) {
ReportSyntaxError(ts, result, pattern);
return false;
}
return true;
}
// A regexp is a good candidate for Boyer-Moore if it has at least 3
// times as many characters as it has unique characters. Note that
// table lookups in irregexp are done modulo tableSize (128).
template <typename CharT>
static bool HasFewDifferentCharacters(const CharT* chars, size_t length) {
const uint32_t tableSize =
v8::internal::NativeRegExpMacroAssembler::kTableSize;
bool character_found[tableSize];
uint32_t different = 0;
memset(&character_found[0], 0, sizeof(character_found));
for (uint32_t i = 0; i < length; i++) {
uint32_t ch = chars[i] % tableSize;
if (!character_found[ch]) {
character_found[ch] = true;
different++;
// We declare a regexp low-alphabet if it has at least 3 times as many
// characters as it has different characters.
if (different * 3 > length) {
return false;
}
}
}
return true;
}
// Identifies the sort of pattern where Boyer-Moore is faster than string search
static bool UseBoyerMoore(HandleAtom pattern, JS::AutoCheckCannotGC& nogc) {
size_t length =
std::min(size_t(kMaxLookaheadForBoyerMoore), pattern->length());
if (length <= kPatternTooShortForBoyerMoore) {
return false;
}
if (pattern->hasLatin1Chars()) {
return HasFewDifferentCharacters(pattern->latin1Chars(nogc), length);
}
MOZ_ASSERT(pattern->hasTwoByteChars());
return HasFewDifferentCharacters(pattern->twoByteChars(nogc), length);
}
// Sample character frequency information for use in Boyer-Moore.
static void SampleCharacters(HandleLinearString input,
RegExpCompiler& compiler) {
static const int kSampleSize = 128;
int chars_sampled = 0;
FlatStringReader sample_subject(input);
int length = sample_subject.length();
int half_way = (length - kSampleSize) / 2;
for (int i = std::max(0, half_way);
i < length && chars_sampled < kSampleSize;
i++, chars_sampled++) {
compiler.frequency_collator()->CountCharacter(sample_subject.Get(i));
}
}
static RegExpNode* WrapBody(RegExpShared* re,
RegExpCompiler& compiler, RegExpCompileData& data,
Zone* zone, bool isLatin1) {
using v8::internal::ChoiceNode;
using v8::internal::EndNode;
using v8::internal::GuardedAlternative;
using v8::internal::RegExpCapture;
using v8::internal::RegExpCharacterClass;
using v8::internal::RegExpQuantifier;
using v8::internal::RegExpTree;
using v8::internal::TextNode;
RegExpNode* captured_body =
RegExpCapture::ToNode(data.tree, 0, &compiler, compiler.accept());
RegExpNode* node = captured_body;
if (!data.tree->IsAnchoredAtStart() && !re->sticky()) {
// Add a .*? at the beginning, outside the body capture, unless
// this expression is anchored at the beginning or sticky.
v8::internal::JSRegExp::Flags default_flags;
RegExpNode* loop_node = RegExpQuantifier::ToNode(
0, RegExpTree::kInfinity, false,
new (zone) RegExpCharacterClass('*', default_flags), &compiler,
captured_body, data.contains_anchor);
if (data.contains_anchor) {
// Unroll loop once, to take care of the case that might start
// at the start of input.
ChoiceNode* first_step_node = new (zone) ChoiceNode(2, zone);
first_step_node->AddAlternative(GuardedAlternative(captured_body));
first_step_node->AddAlternative(GuardedAlternative(new (zone) TextNode(
new (zone) RegExpCharacterClass('*', default_flags), false,
loop_node)));
node = first_step_node;
} else {
node = loop_node;
}
}
if (isLatin1) {
node = node->FilterOneByte(RegExpCompiler::kMaxRecursion);
// Do it again to propagate the new nodes to places where they were not
// put because they had not been calculated yet.
if (node != nullptr) {
node = node->FilterOneByte(RegExpCompiler::kMaxRecursion);
}
} else if (re->unicode() && (re->global() || re->sticky())) {
v8::internal::JSRegExp::Flags passflags = static_cast<uint8_t>(re->getFlags().value());
node = RegExpCompiler::OptionallyStepBackToLeadSurrogate(&compiler, node, passflags);
}
if (node == nullptr) node = new (zone) EndNode(EndNode::BACKTRACK, zone);
return node;
}
bool CompilePattern(JSContext* cx, RegExpShared* re,
HandleLinearString input) {
RootedAtom pattern(cx, re->getSource());
JS::RegExpFlags flags = re->getFlags();
v8::internal::JSRegExp::Flags passflags = static_cast<uint8_t>(flags.value());
LifoAllocScope allocScope(&cx->tempLifoAlloc());
Zone zone(allocScope.alloc());
RegExpCompileData data;
{
FlatStringReader patternBytes(pattern);
if (!RegExpParser::ParseRegExp(cx->isolate, &zone, &patternBytes, passflags, &data)) {
MOZ_ASSERT(data.error == RegExpError::kStackOverflow);
JS::CompileOptions options(cx);
TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
ReportSyntaxError(dummyTokenStream, data, pattern);
return false;
}
}
if (re->kind() == RegExpShared::Kind::Unparsed) {
// This is the first time we have compiled this regexp.
// First, check to see if we should use simple string search
// with an atom.
if (!flags.ignoreCase() && !flags.sticky()) {
RootedAtom searchAtom(cx);
if (data.simple) {
// The parse-tree is a single atom that is equal to the pattern.
searchAtom = re->getSource();
} else if (data.tree->IsAtom() && data.capture_count == 0) {
// The parse-tree is a single atom that is not equal to the pattern.
v8::internal::RegExpAtom* atom = data.tree->AsAtom();
const char16_t* twoByteChars = atom->data().begin();
searchAtom = AtomizeChars(cx, twoByteChars, atom->length());
if (!searchAtom) {
return false;
}
}
JS::AutoCheckCannotGC nogc(cx);
if (searchAtom && !UseBoyerMoore(searchAtom, nogc)) {
re->useAtomMatch(searchAtom);
return true;
}
}
// Add one to account for the whole-match capture
re->useRegExpMatch(data.capture_count + 1);
}
MOZ_ASSERT(re->kind() == RegExpShared::Kind::RegExp);
HandleScope handleScope(cx->isolate);
RegExpCompiler compiler(cx->isolate, &zone, data.capture_count,
input->hasLatin1Chars());
bool isLatin1 = input->hasLatin1Chars();
SampleCharacters(input, compiler);
data.node = WrapBody(re, compiler, data, &zone, isLatin1);
data.error = AnalyzeRegExp(cx->isolate, isLatin1, data.node);
if (data.error != RegExpError::kNone) {
MOZ_ASSERT(data.error == RegExpError::kAnalysisStackOverflow);
JS_ReportErrorASCII(cx, "Stack overflow");
return false;
}
bool useNativeCode = re->markedForTierUp();
MOZ_ASSERT_IF(useNativeCode, IsNativeRegExpEnabled());
Maybe<jit::JitContext> jctx;
Maybe<js::jit::StackMacroAssembler> stack_masm;
UniquePtr<RegExpMacroAssembler> masm;
if (useNativeCode) {
NativeRegExpMacroAssembler::Mode mode =
isLatin1 ? NativeRegExpMacroAssembler::LATIN1
: NativeRegExpMacroAssembler::UC16;
// If we are compiling native code, we need a macroassembler,
// which needs a jit context.
jctx.emplace(cx, nullptr);
stack_masm.emplace();
uint32_t num_capture_registers = re->pairCount() * 2;
masm = MakeUnique<SMRegExpMacroAssembler>(cx, stack_masm.ref(), &zone, mode,
num_capture_registers);
} else {
masm = MakeUnique<RegExpBytecodeGenerator>(cx->isolate, &zone);
}
if (!masm) {
ReportOutOfMemory(cx);
return false;
}
bool largePattern =
pattern->length() > v8::internal::RegExp::kRegExpTooLargeToOptimize;
masm->set_slow_safe(largePattern);
if (compiler.optimize()) {
compiler.set_optimize(!largePattern);
}
// When matching a regexp with known maximum length that is anchored
// at the end, we may be able to skip the beginning of long input
// strings. This decision is made here because it depends on
// information in the AST that isn't replicated in the Node
// structure used inside the compiler.
bool is_start_anchored = data.tree->IsAnchoredAtStart();
bool is_end_anchored = data.tree->IsAnchoredAtEnd();
int max_length = data.tree->max_match();
static const int kMaxBacksearchLimit = 1024;
if (is_end_anchored && !is_start_anchored && !re->sticky() &&
max_length < kMaxBacksearchLimit) {
masm->SetCurrentPositionFromEnd(max_length);
}
if (re->global()) {
RegExpMacroAssembler::GlobalMode mode = RegExpMacroAssembler::GLOBAL;
if (data.tree->min_match() > 0) {
mode = RegExpMacroAssembler::GLOBAL_NO_ZERO_LENGTH_CHECK;
} else if (re->unicode()) {
mode = RegExpMacroAssembler::GLOBAL_UNICODE;
}
masm->set_global_mode(mode);
}
// Compile the regexp
V8HandleString wrappedPattern(v8::internal::String(pattern), cx->isolate);
RegExpCompiler::CompilationResult result = compiler.Assemble(
cx->isolate, masm.get(), data.node, data.capture_count, wrappedPattern);
if (JS::Value(result.code).isUndefined()) {
// SMRegExpMacroAssembler::GetCode returns undefined on OOM.
MOZ_ASSERT(useNativeCode);
ReportOutOfMemory(cx);
return false;
}
if (!result.Succeeded()) {
MOZ_ASSERT(result.error == RegExpError::kTooLarge);
JS_ReportErrorASCII(cx, "regexp too big");
return false;
}
re->updateMaxRegisters(result.num_registers);
/* if (useNativeCode) {
// Transfer ownership of the tables from the macroassembler to the
// RegExpShared.
SMRegExpMacroAssembler::TableVector& tables =
static_cast<SMRegExpMacroAssembler*>(masm.get())->tables();
for (uint32_t i = 0; i < tables.length(); i++) {
if (!re->addTable(std::move(tables[i]))) {
ReportOutOfMemory(cx);
return false;
}
}
re->setJitCode(v8::internal::Code::cast(result.code).inner(), isLatin1);
} else {*/
// Transfer ownership of the bytecode from the HandleScope to the
// RegExpShared.
ByteArray bytecode =
v8::internal::ByteArray::cast(result.code).takeOwnership(cx->isolate);
uint32_t length = bytecode->length;
re->setByteCode(bytecode.release(), isLatin1);
//js::AddCellMemory(re, length, MemoryUse::RegExpSharedBytecode);
// }
return true;
}
template <typename CharT>
RegExpRunStatus ExecuteRaw(jit::JitCode* code, const CharT* chars,
size_t length, size_t startIndex,
VectorMatchPairs* matches) {
InputOutputData data(chars, chars + length, startIndex, matches);
static_assert(RegExpRunStatus_Error ==
v8::internal::RegExp::kInternalRegExpException);
static_assert(RegExpRunStatus_Success ==
v8::internal::RegExp::kInternalRegExpSuccess);
static_assert(RegExpRunStatus_Success_NotFound ==
v8::internal::RegExp::kInternalRegExpFailure);
typedef int (*RegExpCodeSignature)(InputOutputData*);
auto function = reinterpret_cast<RegExpCodeSignature>(code->raw());
return (RegExpRunStatus) CALL_GENERATED_1(function, &data);
}
RegExpRunStatus Interpret(JSContext* cx, RegExpShared* re,
HandleLinearString input, size_t startIndex,
VectorMatchPairs* matches) {
HandleScope handleScope(cx->isolate);
V8HandleRegExp wrappedRegExp(v8::internal::JSRegExp(re), cx->isolate);
V8HandleString wrappedInput(v8::internal::String(input), cx->isolate);
uint32_t numRegisters = re->getMaxRegisters();
// Allocate memory for registers. They will be initialized by the
// interpreter. (See IrregexpInterpreter::MatchInternal.)
Vector<int32_t, 8, SystemAllocPolicy> registers;
if (!registers.growByUninitialized(numRegisters)) {
ReportOutOfMemory(cx);
return RegExpRunStatus_Error;
}
// Double-check enum definitions
static_assert(RegExpRunStatus_Error == v8::internal::RegExp::kInternalRegExpException,
"RegExpRunStatus enum mismatch!");
static_assert(RegExpRunStatus_Success == v8::internal::RegExp::kInternalRegExpSuccess,
"RegExpRunStatus enum mismatch!");
static_assert(RegExpRunStatus_Success_NotFound == v8::internal::RegExp::kInternalRegExpFailure,
"RegExpRunStatus enum mismatch!");
RegExpRunStatus status =
(RegExpRunStatus)IrregexpInterpreter::MatchForCallFromRuntime(
cx->isolate, wrappedRegExp, wrappedInput, registers.begin(),
numRegisters, startIndex);
MOZ_ASSERT(status == RegExpRunStatus_Error ||
status == RegExpRunStatus_Success ||
status == RegExpRunStatus_Success_NotFound);
// Copy results out of registers
if (status == RegExpRunStatus_Success) {
uint32_t length = re->pairCount() * 2;
MOZ_ASSERT(length <= registers.length());
for (uint32_t i = 0; i < length; i++) {
matches->pairsRaw()[i] = registers[i];
}
}
return status;
}
RegExpRunStatus Execute(JSContext* cx, RegExpShared* re,
HandleLinearString input, size_t startIndex,
VectorMatchPairs* matches) {
bool latin1 = input->hasLatin1Chars();
jit::JitCode* jitCode = re->getJitCode(latin1);
bool isCompiled = !!jitCode;
if (isCompiled) {
JS::AutoCheckCannotGC nogc;
if (latin1) {
return ExecuteRaw(jitCode, input->latin1Chars(nogc), input->length(),
startIndex, matches);
}
return ExecuteRaw(jitCode, input->twoByteChars(nogc), input->length(),
startIndex, matches);
}
return Interpret(cx, re, input, startIndex, matches);
}
} // namespace irregexp
} // namespace js
+41
View File
@@ -0,0 +1,41 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This Source Code Form is "Incompatible With Secondary Licenses", as
* defined by the Mozilla Public License, v. 2.0.
*/
/* This is the interface that the regexp engine exposes to SpiderMonkey. */
#ifndef regexp_RegExpAPI_h
#define regexp_RegExpAPI_h
#include "frontend/TokenStream.h"
#include "jscntxt.h"
#include "vm/MatchPairs.h"
#include "vm/RegExpObject.h"
namespace js {
namespace irregexp {
Isolate* CreateIsolate(JSContext* cx);
bool CheckPatternSyntax(JSContext* cx, frontend::TokenStream& ts,
const mozilla::Range<const char16_t> chars,
JS::RegExpFlags flags);
bool CheckPatternSyntax(JSContext* cx, frontend::TokenStream& ts,
HandleAtom pattern, JS::RegExpFlags flags);
bool CompilePattern(JSContext* cx, RegExpShared* re,
HandleLinearString input);
RegExpRunStatus Execute(JSContext* cx, RegExpShared* re,
HandleLinearString input, size_t start,
VectorMatchPairs* matches);
} // namespace irregexp
} // namespace js
#endif /* regexp_RegExpAPI_h */
+14 -1
View File
@@ -1,5 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -17,6 +16,15 @@ class MatchPairs;
namespace v8 {
namespace internal {
class ByteArrayData {
public:
uint32_t length;
uint8_t* data();
};
class Isolate;
class RegExpStack;
class RegExpStackScope;
struct InputOutputData {
const void* inputStart;
const void* inputEnd;
@@ -43,6 +51,11 @@ struct InputOutputData {
namespace js {
namespace irregexp {
using Isolate = v8::internal::Isolate;
using RegExpStack = v8::internal::RegExpStack;
using RegExpStackScope = v8::internal::RegExpStackScope;
using ByteArrayData = v8::internal::ByteArrayData;
using ByteArray = js::UniquePtr<v8::internal::ByteArrayData, JS::FreePolicy>;
using InputOutputData = v8::internal::InputOutputData;
} // namespace irregexp
+1
View File
@@ -26,6 +26,7 @@ SOURCES += [
'regexp-parser.cc',
'regexp-shim.cc',
'regexp-stack.cc',
'RegExpAPI.cpp',
'util/unicode.cc'
]
@@ -1,5 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -37,9 +36,8 @@ struct FrameData {
class SMRegExpMacroAssembler final : public NativeRegExpMacroAssembler {
public:
SMRegExpMacroAssembler(JSContext* cx, Isolate* isolate,
js::jit::StackMacroAssembler& masm, Zone* zone,
Mode mode, uint32_t num_capture_registers);
SMRegExpMacroAssembler(JSContext* cx, js::jit::StackMacroAssembler& masm,
Zone* zone, Mode mode, uint32_t num_capture_registers);
virtual ~SMRegExpMacroAssembler() {} // Nothing to do here
virtual int stack_limit_slack();
@@ -1,5 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -43,11 +42,11 @@ using js::jit::Register;
using js::jit::Registers;
using js::jit::StackMacroAssembler;
SMRegExpMacroAssembler::SMRegExpMacroAssembler(JSContext* cx, Isolate* isolate,
SMRegExpMacroAssembler::SMRegExpMacroAssembler(JSContext* cx,
StackMacroAssembler& masm,
Zone* zone, Mode mode,
uint32_t num_capture_registers)
: NativeRegExpMacroAssembler(isolate, zone),
: NativeRegExpMacroAssembler(cx->isolate, zone),
cx_(cx),
masm_(masm),
mode_(mode),
+9 -2
View File
@@ -1,5 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -153,6 +152,14 @@ std::unique_ptr<char[]> String::ToCString() {
return std::unique_ptr<char[]>();
}
bool Isolate::init() {
regexpStack_ = js_new<RegExpStack>();
if (!regexpStack_) {
return false;
}
return true;
}
byte* Isolate::top_of_regexp_stack() const {
return reinterpret_cast<byte*>(regexpStack_->memory_top_address_address());
}
@@ -202,7 +209,7 @@ bool FLAG_regexp_mode_modifiers = false;
bool FLAG_regexp_optimization = true;
bool FLAG_regexp_peephole_optimization = true;
bool FLAG_regexp_possessive_quantifier = false;
bool FLAG_regexp_tier_up = false;
bool FLAG_regexp_tier_up = true;
bool FLAG_trace_regexp_assembler = false;
bool FLAG_trace_regexp_bytecodes = false;
bool FLAG_trace_regexp_parser = false;
+51 -49
View File
@@ -1,5 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -505,12 +504,14 @@ class Object {
constexpr Object(JS::Value value) : value_(value) {}
operator JS::Value() const { return value_; }
// Used in regexp-macro-assembler.cc and regexp-interpreter.cc to
// check the return value of isolate->stack_guard()->HandleInterrupts()
// In V8, this will be either an exception object or undefined.
// In SM, we store the exception in the context, so we can use our normal
// idiom: return false iff we are throwing an exception.
inline bool IsException(Isolate*) const { return !value_.toBoolean(); }
// Used in regexp-interpreter.cc to check the return value of
// isolate->stack_guard()->HandleInterrupts(). We want to handle
// interrupts in the caller, so we always return false from
// HandleInterrupts and true here.
inline bool IsException(Isolate*) const {
MOZ_ASSERT(!value_.toBoolean());
return true;
}
protected:
JS::Value value_;
@@ -548,12 +549,6 @@ class FixedArray : public HeapObject {
inline static FixedArray cast(Object object) { MOZ_CRASH("TODO"); }
};
class ByteArrayData {
public:
uint32_t length;
uint8_t* data();
};
/*
* Conceptually, ByteArrayData is a variable-size structure. To
* implement this in a C++-approved way, we allocate a struct
@@ -641,8 +636,8 @@ class MOZ_NONHEAP_CLASS Handle {
Handle(JS::Value value, Isolate* isolate);
// Constructor for handling automatic up casting.
template <typename S, typename = typename std::enable_if<
std::is_convertible<S*, T*>::value>::type>
template <typename S, typename = typename std::enable_if_t<
std::is_convertible_v<S*, T*>>>
inline Handle(Handle<S> handle) : location_(handle.location_) {}
template <typename S>
@@ -706,8 +701,8 @@ class MOZ_NONHEAP_CLASS MaybeHandle final {
// Constructor for handling automatic up casting from Handle.
// Ex. Handle<JSArray> can be passed when MaybeHandle<Object> is expected.
template <typename S, typename = typename std::enable_if<
std::is_convertible<S*, T*>::value>::type>
template <typename S, typename = typename std::enable_if_t<
std::is_convertible_v<S*, T*>>>
MaybeHandle(Handle<S> handle) : location_(handle.location_) {}
inline Handle<T> ToHandleChecked() const {
@@ -749,15 +744,17 @@ class DisallowHeapAllocation {
const JS::AutoCheckCannotGC no_gc_;
};
// This is used inside DisallowHeapAllocation regions to enable
// allocation just before throwing an exception, to allocate the
// exception object. Specifically, it only ever guards:
// - isolate->stack_guard()->HandleInterrupts()
// - isolate->StackOverflow()
// Those cases don't allocate in SpiderMonkey, so this can be a no-op.
// V8 uses this inside DisallowHeapAllocation regions to turn
// allocation back on before throwing a stack overflow exception or
// handling interrupts. AutoSuppressGC is sufficient for the former
// case, but not for the latter: handling interrupts can execute
// arbitrary script code, and V8 jumps through some scary hoops to
// "manually relocate unhandlified references" afterwards. To keep
// things sane, we don't try to handle interrupts while regex code is
// still on the stack. Instead, we return EXCEPTION and handle
// interrupts in the caller. (See RegExpShared::execute.)
class AllowHeapAllocation {
public:
// Empty constructor to avoid unused_variable warnings
AllowHeapAllocation() {}
};
@@ -768,7 +765,7 @@ class String : public HeapObject {
JSString* str() const { return value_.toString(); }
public:
String() : HeapObject() {}
String() = default;
String(JSString* str) { value_ = JS::StringValue(str); }
operator JSString*() const { return str(); }
@@ -892,25 +889,29 @@ class MOZ_STACK_CLASS FlatStringReader {
class JSRegExp : public HeapObject {
public:
JSRegExp() : HeapObject() {}
JSRegExp(js::RegExpShared* re) {
value_ = JS::PrivateGCThingValue(reinterpret_cast<js::gc::Cell*>(re));
}
// ******************************************************
// Methods that are called from inside the implementation
// ******************************************************
void TierUpTick() { /*inner()->tierUpTick();*/ }
bool MarkedForTierUp() const {
return false; /*inner()->markedForTierUp();*/
void TierUpTick() { inner()->tierUpTick(); }
Object Code(bool is_latin1) const {
return Object(JS::PrivateGCThingValue(inner()->getJitCode(is_latin1)));
}
Object Bytecode(bool is_latin1) const {
return Object(JS::PrivateValue(inner()->getByteCode(is_latin1)));
}
// TODO: hook these up
Object Code(bool is_latin1) const { return Object(JS::UndefinedValue()); }
Object Bytecode(bool is_latin1) const { return Object(JS::UndefinedValue()); }
uint32_t BacktrackLimit() const {
return 0; /*inner()->backtrackLimit();*/
}
// TODO: should we expose this?
uint32_t BacktrackLimit() const { return 0; }
static JSRegExp cast(Object object) {
JSRegExp regexp;
MOZ_ASSERT(JS::Value(object).toGCThing()->is<js::RegExpShared>());
//MOZ_ASSERT(JS::Value(object).toGCThing()->is<js::RegExpShared>());
regexp.value_ = JS::PrivateGCThingValue(JS::Value(object).toGCThing());
return regexp;
}
@@ -919,13 +920,8 @@ class JSRegExp : public HeapObject {
// Static constants
// ******************************
// Meaning of Type:
// NOT_COMPILED: Initial value. No data has been stored in the JSRegExp yet.
// ATOM: A simple string to match against using an indexOf operation.
// IRREGEXP: Compiled with Irregexp.
enum Type { NOT_COMPILED, ATOM, IRREGEXP };
// Maximum number of captures allowed.
// XXXMC Do we really need 2^16 captures? o.O
static constexpr int kMaxCaptures = 1 << 16;
// **************************************************
@@ -933,8 +929,8 @@ class JSRegExp : public HeapObject {
// **************************************************
struct FlagShiftBit {
static constexpr int kGlobal = 0;
static constexpr int kIgnoreCase = 1;
static constexpr int kGlobal = 1;
static constexpr int kIgnoreCase = 0;
static constexpr int kMultiline = 2;
static constexpr int kSticky = 3;
static constexpr int kUnicode = 4;
@@ -951,13 +947,13 @@ class JSRegExp : public HeapObject {
kDotAll = 1 << FlagShiftBit::kDotAll,
kInvalid = 1 << FlagShiftBit::kInvalid, // Not included in FlagCount.
};
using Flags = base::Flags<Flag>;
using Flags = base::Flags<Flag, uint8_t>;
static constexpr int kFlagCount = 6;
static constexpr int kNoBacktrackLimit = 0;
private:
js::RegExpShared* inner() {
js::RegExpShared* inner() const {
return reinterpret_cast<js::RegExpShared*>(value_.toGCThing());
}
};
@@ -989,6 +985,9 @@ using Factory = Isolate;
class Isolate {
public:
Isolate(JSContext* cx) : cx_(cx) {}
bool init();
//********** Isolate code **********//
RegExpStack* regexp_stack() const { return regexpStack_; }
byte* top_of_regexp_stack() const;
@@ -1037,9 +1036,12 @@ public:
//********** Stack guard code **********//
inline StackGuard* stack_guard() { return this; }
Object HandleInterrupts() {
return Object(JS::BooleanValue(cx()->handleInterrupt(cx())));
}
// This is called from inside no-GC code. V8 runs the interrupt
// inside the no-GC code and then "manually relocates unhandlified
// references" afterwards. We just return false and let the caller
// handle interrupts.
Object HandleInterrupts() { return Object(JS::BooleanValue(false)); }
JSContext* cx() const { return cx_; }
+14 -14
View File
@@ -25,15 +25,15 @@ class Flags final {
using flag_type = T;
using mask_type = S;
constexpr Flags() : mask_(0) {}
constexpr Flags(flag_type flag)
Flags() : mask_(0) {}
Flags(flag_type flag)
: mask_(static_cast<S>(flag)) {}
constexpr explicit Flags(mask_type mask) : mask_(static_cast<S>(mask)) {}
Flags(mask_type mask) : mask_(static_cast<S>(mask)) {}
constexpr bool operator==(flag_type flag) const {
bool operator==(flag_type flag) const {
return mask_ == static_cast<S>(flag);
}
constexpr bool operator!=(flag_type flag) const {
bool operator!=(flag_type flag) const {
return mask_ != static_cast<S>(flag);
}
@@ -50,13 +50,13 @@ class Flags final {
return *this;
}
constexpr Flags operator&(const Flags& flags) const {
Flags operator&(const Flags& flags) const {
return Flags(mask_ & flags.mask_);
}
constexpr Flags operator|(const Flags& flags) const {
Flags operator|(const Flags& flags) const {
return Flags(mask_ | flags.mask_);
}
constexpr Flags operator^(const Flags& flags) const {
Flags operator^(const Flags& flags) const {
return Flags(mask_ ^ flags.mask_);
}
@@ -64,20 +64,20 @@ class Flags final {
Flags& operator|=(flag_type flag) { return operator|=(Flags(flag)); }
Flags& operator^=(flag_type flag) { return operator^=(Flags(flag)); }
constexpr Flags operator&(flag_type flag) const {
Flags operator&(flag_type flag) const {
return operator&(Flags(flag));
}
constexpr Flags operator|(flag_type flag) const {
Flags operator|(flag_type flag) const {
return operator|(Flags(flag));
}
constexpr Flags operator^(flag_type flag) const {
Flags operator^(flag_type flag) const {
return operator^(Flags(flag));
}
constexpr Flags operator~() const { return Flags(~mask_); }
Flags operator~() const { return Flags(~mask_); }
constexpr operator mask_type() const { return mask_; }
constexpr bool operator!() const { return !mask_; }
operator mask_type() const { return mask_; }
bool operator!() const { return !mask_; }
Flags without(flag_type flag) { return *this & (~Flags(flag)); }
+3 -3
View File
@@ -21,7 +21,7 @@ namespace internal {
template <typename T>
T* NewArray(size_t size) {
static_assert(std::is_pod<T>::value, "");
static_assert(std::is_pod_v<T>, "");
js::AutoEnterOOMUnsafeRegion oomUnsafe;
T* result = static_cast<T*>(js_malloc(size * sizeof(T)));
if (!result) {
@@ -58,8 +58,8 @@ class Vector {
// Returns a vector using the same backing storage as this one,
// spanning from and including 'from', to but not including 'to'.
Vector<T> SubVector(size_t from, size_t to) const {
MOZ_ASSERT(from < to);
MOZ_ASSERT(to < length_);
MOZ_ASSERT(from <= to);
MOZ_ASSERT(to <= length_);
return Vector<T>(begin() + from, to - from);
}
+3 -5
View File
@@ -21,9 +21,7 @@ namespace internal {
// V8::Zone ~= LifoAlloc
class Zone {
public:
Zone(size_t defaultChunkSize) : lifoAlloc_(defaultChunkSize) {
lifoAlloc_.setAsInfallibleByDefault();
}
Zone(js::LifoAlloc& alloc) : lifoAlloc_(alloc) {}
void* New(size_t size) {
js::LifoAlloc::AutoFallibleScope fallible(&lifoAlloc_);
@@ -43,7 +41,7 @@ class Zone {
return lifoAlloc_.computedSizeOfExcludingThis() > kExcessLimit;
}
private:
js::LifoAlloc lifoAlloc_;
js::LifoAlloc& lifoAlloc_;
};
// Superclass for classes allocated in a Zone.
@@ -95,7 +93,7 @@ class ZoneList final {
// to use after operations that can change the list's backing store
// (e.g. Add).
inline T& operator[](int i) const {
MOZ_ASSERT(0 < i);
MOZ_ASSERT(i >= 0);
MOZ_ASSERT(static_cast<unsigned>(i) < static_cast<unsigned>(length_));
return data_[i];
}
+1 -1
View File
@@ -317,7 +317,7 @@ InflateUTF8StringToBuffer(ContextT* cx, const UTF8Chars src, CharT* dst, size_t*
MOZ_CRASH("invalid UTF-8 string: " # report); \
} else { \
if (Action == Copy) { \
if (std::is_same<decltype(dst[0]), Latin1Char>::value) \
if (std::is_same_v<decltype(dst[0]), Latin1Char>) \
dst[j] = CharT(REPLACE_UTF8_LATIN1); \
else \
dst[j] = CharT(REPLACE_UTF8); \
+4
View File
@@ -16,6 +16,8 @@ using mozilla::Move;
using JS::UniqueTwoByteChars;
namespace js {
void
CallWarningReporter(JSContext* cx, JSErrorReport* reportp)
{
@@ -121,3 +123,5 @@ ReportCompileError(JSContext* cx, ErrorMetadata&& metadata, UniquePtr<JSErrorNot
err->throwError(cx->asJSContext());
}
}
} //namespace js
+11 -3
View File
@@ -36,12 +36,20 @@ struct ErrorMetadata
// If the error occurs at a particular location, context surrounding the
// location of the error: the line that contained the error, or a small
// portion of it if the line is long.
// portion of it if the line is long. (If the error occurs within a
// regular expression, this context is based upon its pattern characters.)
//
// This information is provided on a best-effort basis: code populating
// ErrorMetadata instances isn't obligated to supply this.
JS::UniqueTwoByteChars lineOfContext;
// If |lineOfContext| is provided, we show only a portion (a "window") of
// the line around the erroneous token -- the first char in the token, plus
// |lineOfContextRadius| chars before it and |lineOfContextRadius - 1|
// chars after it. This is because for a very long line, the full line is
// (a) not that helpful, and (b) wastes a lot of memory. See bug 634444.
static constexpr size_t lineOfContextRadius = 60;
// If |lineOfContext| is non-null, its length.
size_t lineLength;
@@ -50,7 +58,7 @@ struct ErrorMetadata
size_t tokenOffset;
// Whether the error is "muted" because it derives from a cross-origin
// load. See the comment in TransitiveCompileOptions in jsapi.h for
// load. See the comment in TransitiveCompileOptions in jsapi.h for
// details.
bool isMuted;
};
@@ -70,7 +78,7 @@ CallWarningReporter(JSContext* cx, JSErrorReport* report);
* script.
*/
extern void
ReportCompileError(ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes,
ReportCompileError(JSContext* cx, ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes,
unsigned flags, unsigned errorNumber, va_list args);
/**
+3 -2
View File
@@ -16,6 +16,7 @@
#include "gc/GCInternals.h"
#include "jit/IonBuilder.h"
#include "vm/Debugger.h"
#include "vm/ErrorReporting.h"
#include "vm/SharedImmutableStringsCache.h"
#include "vm/Time.h"
#include "vm/TraceLogging.h"
@@ -1581,9 +1582,9 @@ ExclusiveContext::setHelperThread(HelperThread* thread)
}
bool
ExclusiveContext::addPendingCompileError(frontend::CompileError** error)
ExclusiveContext::addPendingCompileError(CompileError** error)
{
UniquePtr<frontend::CompileError> errorPtr(new_<frontend::CompileError>());
UniquePtr<CompileError> errorPtr(new_<CompileError>());
if (!errorPtr)
return false;
if (!helperThread()->parseTask()->errors.append(errorPtr.get()))
+1 -1
View File
@@ -555,7 +555,7 @@ struct ParseTask
// Any errors or warnings produced during compilation. These are reported
// when finishing the script.
Vector<frontend::CompileError*> errors;
Vector<CompileError*> errors;
bool overRecursed;
bool outOfMemory;
+446 -116
View File
@@ -14,11 +14,21 @@
#endif
#include "builtin/RegExp.h"
#include "builtin/SelfHostingDefines.h" // REGEXP_*_FLAG
#include "frontend/TokenStream.h"
#ifndef JS_NEW_REGEXP
#ifdef DEBUG
#include "irregexp/RegExpBytecode.h"
#endif
#include "irregexp/RegExpParser.h"
#endif
#ifdef JS_NEW_REGEXP
#include "new-regexp/regexp-stack.h"
#include "new-regexp/RegExpAPI.h"
#endif
#include "vm/MatchPairs.h"
#include "vm/RegExpStatics.h"
#include "vm/StringBuffer.h"
@@ -33,22 +43,33 @@
#include "vm/NativeObject-inl.h"
#include "vm/Shape-inl.h"
#include "js/RegExp.h"
#include "js/RegExpFlags.h" // JS::RegExpFlags
using namespace js;
using mozilla::ArrayLength;
using mozilla::DebugOnly;
using mozilla::Maybe;
using mozilla::PodCopy;
using JS::RegExpFlag;
using JS::RegExpFlags;
using js::frontend::TokenStream;
using JS::AutoCheckCannotGC;
JS_STATIC_ASSERT(IgnoreCaseFlag == JSREG_FOLD);
JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB);
JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE);
JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
JS_STATIC_ASSERT(UnicodeFlag == JSREG_UNICODE);
JS_STATIC_ASSERT(DotAllFlag == JSREG_DOTALL);
static_assert(RegExpFlag::Global == REGEXP_GLOBAL_FLAG,
"self-hosted JS and /g flag bits must agree");
static_assert(RegExpFlag::IgnoreCase == REGEXP_IGNORECASE_FLAG,
"self-hosted JS and /i flag bits must agree");
static_assert(RegExpFlag::Multiline == REGEXP_MULTILINE_FLAG,
"self-hosted JS and /m flag bits must agree");
static_assert(RegExpFlag::DotAll == REGEXP_DOTALL_FLAG,
"self-hosted JS and /s flag bits must agree");
static_assert(RegExpFlag::Unicode == REGEXP_UNICODE_FLAG,
"self-hosted JS and /u flag bits must agree");
static_assert(RegExpFlag::Sticky == REGEXP_STICKY_FLAG,
"self-hosted JS and /y flag bits must agree");
RegExpObject*
js::RegExpAlloc(ExclusiveContext* cx, HandleObject proto /* = nullptr */)
@@ -145,30 +166,30 @@ RegExpObject::getShared(JSContext* cx, Handle<RegExpObject*> regexp, RegExpGuard
}
/* static */ bool
RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlag* mask)
RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlags* mask)
{
if (native == regexp_global) {
*mask = GlobalFlag;
*mask = RegExpFlag::Global;
return true;
}
if (native == regexp_ignoreCase) {
*mask = IgnoreCaseFlag;
*mask = RegExpFlag::IgnoreCase;
return true;
}
if (native == regexp_multiline) {
*mask = MultilineFlag;
*mask = RegExpFlag::Multiline;
return true;
}
if (native == regexp_sticky) {
*mask = StickyFlag;
*mask = RegExpFlag::Sticky;
return true;
}
if (native == regexp_unicode) {
*mask = UnicodeFlag;
*mask = RegExpFlag::Unicode;
return true;
}
if (native == regexp_dotAll) {
*mask = DotAllFlag;
*mask = RegExpFlag::DotAll;
return true;
}
@@ -247,7 +268,8 @@ const Class RegExpObject::protoClass_ = {
};
RegExpObject*
RegExpObject::create(ExclusiveContext* cx, const char16_t* chars, size_t length, RegExpFlag flags,
RegExpObject::create(ExclusiveContext* cx, const char16_t* chars,
size_t length, RegExpFlags flags,
TokenStream* tokenStream, LifoAlloc& alloc)
{
RootedAtom source(cx, AtomizeChars(cx, chars, length));
@@ -258,7 +280,7 @@ RegExpObject::create(ExclusiveContext* cx, const char16_t* chars, size_t length,
}
RegExpObject*
RegExpObject::create(ExclusiveContext* cx, HandleAtom source, RegExpFlag flags,
RegExpObject::create(ExclusiveContext* cx, HandleAtom source, RegExpFlags flags,
TokenStream* tokenStream, LifoAlloc& alloc)
{
Maybe<CompileOptions> dummyOptions;
@@ -271,9 +293,14 @@ RegExpObject::create(ExclusiveContext* cx, HandleAtom source, RegExpFlag flags,
tokenStream = dummyTokenStream.ptr();
}
if (!irregexp::ParsePatternSyntax(*tokenStream, alloc, source, flags & UnicodeFlag, flags & DotAllFlag))
#ifdef JS_NEW_REGEXP
if (!irregexp::CheckPatternSyntax(cx->asJSContext(), *tokenStream, source, flags)) {
return nullptr;
}
#else
if (!irregexp::ParsePatternSyntax(*tokenStream, alloc, source, flags.unicode(), flags.dotAll()))
return nullptr;
#endif
Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx));
if (!regexp)
return nullptr;
@@ -306,7 +333,7 @@ RegExpObject::assignInitialShape(ExclusiveContext* cx, Handle<RegExpObject*> sel
}
void
RegExpObject::initIgnoringLastIndex(HandleAtom source, RegExpFlag flags)
RegExpObject::initIgnoringLastIndex(HandleAtom source, RegExpFlags flags)
{
// If this is a re-initialization with an existing RegExpShared, 'flags'
// may not match getShared()->flags, so forget the RegExpShared.
@@ -317,7 +344,7 @@ RegExpObject::initIgnoringLastIndex(HandleAtom source, RegExpFlag flags)
}
void
RegExpObject::initAndZeroLastIndex(HandleAtom source, RegExpFlag flags, ExclusiveContext* cx)
RegExpObject::initAndZeroLastIndex(HandleAtom source, RegExpFlags flags, ExclusiveContext* cx)
{
initIgnoringLastIndex(source, flags);
zeroLastIndex(cx);
@@ -510,15 +537,14 @@ RegExpObject::toString(JSContext* cx) const
return sb.finishString();
}
#ifdef DEBUG
#if defined(DEBUG) && !defined(JS_NEW_REGEXP)
bool
RegExpShared::dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input)
RegExpShared::dumpBytecode(JSContext* cx, HandleLinearString input)
{
CompilationMode mode = match_only ? MatchOnly : Normal;
if (!compileIfNecessary(cx, input, mode, ForceByteCode))
if (!compileIfNecessary(cx, input, CodeKind::Bytecode))
return false;
const uint8_t* byteCode = compilation(mode, input->hasLatin1Chars()).byteCode;
const uint8_t* byteCode = compilation(input->hasLatin1Chars()).byteCode;
const uint8_t* pc = byteCode;
auto Load32Aligned = [](const uint8_t* pc) -> int32_t {
@@ -895,15 +921,15 @@ RegExpShared::dumpBytecode(JSContext* cx, bool match_only, HandleLinearString in
/* static */ bool
RegExpObject::dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp,
bool match_only, HandleLinearString input)
HandleLinearString input)
{
RegExpGuard g(cx);
if (!getShared(cx, regexp, &g))
return false;
return g.re()->dumpBytecode(cx, match_only, input);
return g.re()->dumpBytecode(cx, input);
}
#endif
#endif //DEBUG && !JS_NEW_REGEXP
template <typename CharT>
static MOZ_ALWAYS_INLINE bool
@@ -949,8 +975,11 @@ js::StringHasRegExpMetaChars(JSLinearString* str)
/* RegExpShared */
RegExpShared::RegExpShared(JSAtom* source, RegExpFlag flags)
: source(source), flags(flags), parenCount(0), canStringMatch(false), marked_(false)
RegExpShared::RegExpShared(JSAtom* source, RegExpFlags flags)
: source(source)
, flags(flags)
, pairCount_(0)
, marked_(false)
{}
RegExpShared::~RegExpShared()
@@ -995,18 +1024,193 @@ RegExpShared::unmarkGray()
bool
RegExpShared::compile(JSContext* cx, HandleLinearString input,
CompilationMode mode, ForceByteCodeEnum force)
RegExpShared::CodeKind codeKind)
{
TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
AutoTraceLog logCompile(logger, TraceLogger_IrregexpCompile);
RootedAtom pattern(cx, source);
return compile(cx, pattern, input, mode, force);
return compile(cx, pattern, input, codeKind);
}
RegExpRunStatus RegExpShared::executeAtom(JSContext* cx,
HandleLinearString input,
size_t start,
MatchPairs* matches) {
MOZ_ASSERT(pairCount() == 1);
size_t length = input->length();
size_t sourceLength = source->length();
if (this->sticky()) {
// First part checks size_t overflow.
if (sourceLength + start < sourceLength || sourceLength + start > length)
return RegExpRunStatus_Success_NotFound;
if (!HasSubstringAt(input, this->patternAtom(), start))
return RegExpRunStatus_Success_NotFound;
(*matches)[0].start = start;
(*matches)[0].limit = start + sourceLength;
matches->checkAgainst(length);
return RegExpRunStatus_Success;
}
int res = StringFindPattern(input, this->patternAtom(), start);
if (res == -1)
return RegExpRunStatus_Success_NotFound;
(*matches)[0].start = res;
(*matches)[0].limit = res + sourceLength;
matches->checkAgainst(length);
return RegExpRunStatus_Success;
}
#ifdef JS_NEW_REGEXP
bool
RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
RegExpShared::CodeKind codeKind)
{
MOZ_CRASH("TODO");
}
bool
RegExpShared::compileIfNecessary(JSContext* cx, HandleLinearString input,
RegExpShared::CodeKind codeKind)
{
bool needsCompile = false;
if (kind() == RegExpShared::Kind::Unparsed) {
needsCompile = true;
}
if (kind() == RegExpShared::Kind::RegExp) {
if (codeKind == RegExpShared::CodeKind::Any && markedForTierUp()) {
codeKind = RegExpShared::CodeKind::Jitcode;
}
if (!isCompiled(input->hasLatin1Chars(), codeKind)) {
needsCompile = true;
}
}
if (needsCompile) {
return irregexp::CompilePattern(cx, this, input);
}
return true;
}
RegExpRunStatus
RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
MatchPairs* matches)
{
// TODO: Add tracelogger support
/* Compile the code at point-of-use. */
if (!compileIfNecessary(cx, input, RegExpShared::CodeKind::Any)) {
return RegExpRunStatus_Error;
}
/*
* Ensure sufficient memory for output vector.
* No need to initialize it. The RegExp engine fills them in on a match.
*/
if (!matches->allocOrExpandArray(pairCount())) {
ReportOutOfMemory(cx);
return RegExpRunStatus_Error;
}
if (kind() == RegExpShared::Kind::Atom) {
return RegExpShared::executeAtom(cx, input, start, matches);
}
// Reset the Irregexp backtrack stack if it grows during execution.
irregexp::RegExpStackScope stackScope(cx->isolate);
/*
* Ensure sufficient memory for output vector.
* No need to initialize it. The RegExp engine fills them in on a match.
*/
if (!matches->allocOrExpandArray(pairCount())) {
ReportOutOfMemory(cx);
return RegExpRunStatus_Error;
}
uint32_t interruptRetries = 0;
const uint32_t maxInterruptRetries = 4;
VectorMatchPairs* vmatches = static_cast<VectorMatchPairs*>(matches);
do {
RegExpRunStatus result = irregexp::Execute(cx, this, input, start, vmatches);
if (result == RegExpRunStatus_Error) {
/* Execute can return RegExpRunStatus_Error:
*
* 1. If the native stack overflowed
* 2. If the backtrack stack overflowed
* 3. If an interrupt was requested during execution.
*
* In the first two cases, we want to throw an error. In the
* third case, we want to handle the interrupt and try again.
* We cap the number of times we will retry.
*/
JSRuntime* rt = cx->runtime();
if (rt->hasPendingInterrupt()) {
if (!CheckForInterrupt(cx)) {
return RegExpRunStatus_Error;
}
if (interruptRetries++ < maxInterruptRetries) {
continue;
}
}
// If we have run out of retries, this regexp takes too long to execute.
ReportOverRecursed(cx);
return RegExpRunStatus_Error;
}
MOZ_ASSERT(result == RegExpRunStatus_Success ||
result == RegExpRunStatus_Success_NotFound);
return result;
} while (true);
MOZ_CRASH("Unreachable");
}
void RegExpShared::useAtomMatch(HandleAtom pattern) {
MOZ_ASSERT(kind() == RegExpShared::Kind::Unparsed);
kind_ = RegExpShared::Kind::Atom;
patternAtom_ = pattern;
pairCount_ = 1;
}
void RegExpShared::useRegExpMatch(size_t pairCount) {
MOZ_ASSERT(kind() == RegExpShared::Kind::Unparsed);
kind_ = RegExpShared::Kind::RegExp;
pairCount_ = pairCount;
ticks_ = 10; // TODO: add a jit option to control this threshold
}
void RegExpShared::tierUpTick() {
MOZ_ASSERT(kind() == RegExpShared::Kind::RegExp);
if (ticks_ > 0) {
ticks_--;
}
}
bool RegExpShared::markedForTierUp() {
if (!IsNativeRegExpEnabled()) {
return false;
}
if (kind() == RegExpShared::Kind::Atom) {
return false;
}
return ticks_ == 0;
}
#else
bool
RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
CompilationMode mode, ForceByteCodeEnum force)
RegExpShared::CodeKind codeKind)
{
if (!ignoreCase() && !StringHasRegExpMetaChars(pattern))
canStringMatch = true;
@@ -1019,28 +1223,27 @@ RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString inpu
/* Parse the pattern. */
irregexp::RegExpCompileData data;
if (!irregexp::ParsePattern(dummyTokenStream, cx->tempLifoAlloc(), pattern,
multiline(), mode == MatchOnly, unicode(), ignoreCase(),
global(), sticky(), dotAll(), &data))
{
/*match_only =*/false, this->getFlags(), &data)) {
return false;
}
this->parenCount = data.capture_count;
// Add one to account for the extra whole-match capture.
this->pairCount_ = data.capture_count + 1;
bool forceBytecode = codeKind == RegExpShared::CodeKind::Bytecode;
irregexp::RegExpCode code = irregexp::CompilePattern(cx, this, &data, input,
false /* global() */,
ignoreCase(),
input->hasLatin1Chars(),
mode == MatchOnly,
force == ForceByteCode,
/* match_only =*/ false,
forceBytecode,
sticky(), unicode());
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());
RegExpCompilation& compilation = this->compilation(input->hasLatin1Chars());
if (code.jitCode)
compilation.jitCode = code.jitCode;
else if (code.byteCode)
@@ -1051,32 +1254,29 @@ RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString inpu
bool
RegExpShared::compileIfNecessary(JSContext* cx, HandleLinearString input,
CompilationMode mode, ForceByteCodeEnum force)
RegExpShared::CodeKind codeKind)
{
if (isCompiled(mode, input->hasLatin1Chars(), force))
if (isCompiled(input->hasLatin1Chars(), codeKind))
return true;
return compile(cx, input, mode, force);
return compile(cx, input, codeKind);
}
RegExpRunStatus
RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
MatchPairs* matches, size_t* endIndex)
MatchPairs* matches)
{
MOZ_ASSERT_IF(matches, !endIndex);
MOZ_ASSERT_IF(!matches, endIndex);
MOZ_ASSERT(matches);
TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
CompilationMode mode = matches ? Normal : MatchOnly;
/* Compile the code at point-of-use. */
if (!compileIfNecessary(cx, input, mode, DontForceByteCode))
if (!compileIfNecessary(cx, input, RegExpShared::CodeKind::Any))
return RegExpRunStatus_Error;
/*
* Ensure sufficient memory for output vector.
* No need to initialize it. The RegExp engine fills them in on a match.
*/
if (matches && !matches->allocOrExpandArray(pairCount())) {
if (!matches->allocOrExpandArray(pairCount())) {
ReportOutOfMemory(cx);
return RegExpRunStatus_Error;
}
@@ -1087,43 +1287,11 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
irregexp::RegExpStackScope stackScope(cx->runtime());
if (canStringMatch) {
MOZ_ASSERT(pairCount() == 1);
size_t sourceLength = source->length();
if (sticky()) {
// First part checks size_t overflow.
if (sourceLength + start < sourceLength || sourceLength + start > length)
return RegExpRunStatus_Success_NotFound;
if (!HasSubstringAt(input, source, start))
return RegExpRunStatus_Success_NotFound;
if (matches) {
(*matches)[0].start = start;
(*matches)[0].limit = start + sourceLength;
matches->checkAgainst(length);
} else if (endIndex) {
*endIndex = start + sourceLength;
}
return RegExpRunStatus_Success;
}
int res = StringFindPattern(input, source, start);
if (res == -1)
return RegExpRunStatus_Success_NotFound;
if (matches) {
(*matches)[0].start = res;
(*matches)[0].limit = res + sourceLength;
matches->checkAgainst(length);
} else if (endIndex) {
*endIndex = res + sourceLength;
}
return RegExpRunStatus_Success;
return executeAtom(cx, input, start, matches);
}
do {
jit::JitCode* code = compilation(mode, input->hasLatin1Chars()).jitCode;
jit::JitCode* code = compilation(input->hasLatin1Chars()).jitCode;
if (!code)
break;
@@ -1133,10 +1301,10 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
AutoCheckCannotGC nogc;
if (input->hasLatin1Chars()) {
const Latin1Char* chars = input->latin1Chars(nogc);
result = irregexp::ExecuteCode(cx, code, chars, start, length, matches, endIndex);
result = irregexp::ExecuteCode(cx, code, chars, start, length, matches, /*endIndex =*/ nullptr);
} else {
const char16_t* chars = input->twoByteChars(nogc);
result = irregexp::ExecuteCode(cx, code, chars, start, length, matches, endIndex);
result = irregexp::ExecuteCode(cx, code, chars, start, length, matches, /*endIndex =*/ nullptr);
}
}
@@ -1156,16 +1324,15 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
MOZ_ASSERT(result == RegExpRunStatus_Success);
if (matches)
matches->checkAgainst(length);
matches->checkAgainst(length);
return RegExpRunStatus_Success;
} while (false);
// Compile bytecode for the RegExp if necessary.
if (!compileIfNecessary(cx, input, mode, ForceByteCode))
if (!compileIfNecessary(cx, input, RegExpShared::CodeKind::Bytecode))
return RegExpRunStatus_Error;
uint8_t* byteCode = compilation(mode, input->hasLatin1Chars()).byteCode;
uint8_t* byteCode = compilation(input->hasLatin1Chars()).byteCode;
AutoTraceLog logInterpreter(logger, TraceLogger_IrregexpExecute);
AutoStableStringChars inputChars(cx);
@@ -1175,17 +1342,19 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
RegExpRunStatus result;
if (inputChars.isLatin1()) {
const Latin1Char* chars = inputChars.latin1Range().begin().get();
result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches, endIndex);
result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches, /*endIndex =*/ nullptr);
} else {
const char16_t* chars = inputChars.twoByteRange().begin().get();
result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches, endIndex);
result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches, /*endIndex =*/ nullptr);
}
if (result == RegExpRunStatus_Success && matches)
if (result == RegExpRunStatus_Success)
matches->checkAgainst(length);
return result;
}
#endif // JS_NEW_REGEXP
size_t
RegExpShared::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
{
@@ -1361,7 +1530,7 @@ RegExpCompartment::sweep(JSRuntime* rt)
}
bool
RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGuard* g)
RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlags flags, RegExpGuard* g)
{
Key key(source, flags);
Set::AddPtr p = set_.lookupForAdd(key);
@@ -1393,7 +1562,7 @@ RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGu
bool
RegExpCompartment::get(JSContext* cx, HandleAtom atom, JSString* opt, RegExpGuard* g)
{
RegExpFlag flags = RegExpFlag(0);
RegExpFlags flags = RegExpFlag::NoFlags;
if (opt && !ParseRegExpFlags(cx, opt, &flags))
return false;
@@ -1443,45 +1612,45 @@ js::CloneRegExpObject(JSContext* cx, JSObject* obj_)
}
static bool
HandleRegExpFlag(RegExpFlag flag, RegExpFlag* flags)
HandleRegExpFlag(uint8_t flag, RegExpFlags* flags)
{
if (*flags & flag)
return false;
*flags = RegExpFlag(*flags | flag);
*flags = RegExpFlags(*flags | flag);
return true;
}
template <typename CharT>
static size_t
ParseRegExpFlags(const CharT* chars, size_t length, RegExpFlag* flagsOut, char16_t* lastParsedOut)
ParseRegExpFlags(const CharT* chars, size_t length, RegExpFlags* flagsOut, char16_t* lastParsedOut)
{
*flagsOut = RegExpFlag(0);
*flagsOut = RegExpFlag::NoFlags;
for (size_t i = 0; i < length; i++) {
*lastParsedOut = chars[i];
switch (chars[i]) {
case 'i':
if (!HandleRegExpFlag(IgnoreCaseFlag, flagsOut))
case 'g':
if (!HandleRegExpFlag(RegExpFlag::Global, flagsOut))
return false;
break;
case 'g':
if (!HandleRegExpFlag(GlobalFlag, flagsOut))
case 'i':
if (!HandleRegExpFlag(RegExpFlag::IgnoreCase, flagsOut))
return false;
break;
case 'm':
if (!HandleRegExpFlag(MultilineFlag, flagsOut))
if (!HandleRegExpFlag(RegExpFlag::Multiline, flagsOut))
return false;
break;
case 's':
if (!HandleRegExpFlag(DotAllFlag, flagsOut))
return false;
break;
case 'y':
if (!HandleRegExpFlag(StickyFlag, flagsOut))
if (!HandleRegExpFlag(RegExpFlag::DotAll, flagsOut))
return false;
break;
case 'u':
if (!HandleRegExpFlag(UnicodeFlag, flagsOut))
if (!HandleRegExpFlag(RegExpFlag::Unicode, flagsOut))
return false;
break;
case 'y':
if (!HandleRegExpFlag(RegExpFlag::Sticky, flagsOut))
return false;
break;
default:
@@ -1493,7 +1662,7 @@ ParseRegExpFlags(const CharT* chars, size_t length, RegExpFlag* flagsOut, char16
}
bool
js::ParseRegExpFlags(JSContext* cx, JSString* flagStr, RegExpFlag* flagsOut)
js::ParseRegExpFlags(JSContext* cx, JSString* flagStr, RegExpFlags* flagsOut)
{
JSLinearString* linear = flagStr->ensureLinear(cx);
if (!linear)
@@ -1531,19 +1700,18 @@ js::XDRScriptRegExpObject(XDRState<mode>* xdr, MutableHandle<RegExpObject*> objp
/* NB: Keep this in sync with CloneScriptRegExpObject. */
RootedAtom source(xdr->cx());
uint32_t flagsword = 0;
uint8_t flags = 0;
if (mode == XDR_ENCODE) {
MOZ_ASSERT(objp);
RegExpObject& reobj = *objp;
source = reobj.getSource();
flagsword = reobj.getFlags();
flags = reobj.getFlags().value();
}
if (!XDRAtom(xdr, &source) || !xdr->codeUint32(&flagsword))
if (!XDRAtom(xdr, &source) || !xdr->codeUint8(&flags))
return false;
if (mode == XDR_DECODE) {
RegExpFlag flags = RegExpFlag(flagsword);
RegExpObject* reobj = RegExpObject::create(xdr->cx(), source, flags, nullptr,
RegExpObject* reobj = RegExpObject::create(xdr->cx(), source, RegExpFlags(flags), nullptr,
xdr->cx()->tempLifoAlloc());
if (!reobj)
return false;
@@ -1573,3 +1741,165 @@ js::RegExpToSharedNonInline(JSContext* cx, HandleObject obj, js::RegExpGuard* g)
{
return RegExpToShared(cx, obj, g);
}
/*
* Public API functions for Regular Expressions.
*/
JS_PUBLIC_API(JSObject*) JS::NewRegExpObject(JSContext* cx,
const char* bytes,
size_t length,
RegExpFlags flags) {
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
ScopedJSFreePtr<char16_t> chars(InflateString(cx, bytes, &length));
if (!chars) {
return nullptr;
}
return RegExpObject::create(cx,
chars,
length,
flags,
nullptr,
cx->tempLifoAlloc());
}
JS_PUBLIC_API(JSObject*) JS::NewUCRegExpObject(JSContext* cx,
const char16_t* chars,
size_t length,
RegExpFlags flags) {
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
return RegExpObject::create(cx,
chars,
length,
flags,
nullptr,
cx->tempLifoAlloc());
}
JS_PUBLIC_API(bool) JS::SetRegExpInput(JSContext* cx,
HandleObject obj,
HandleString input) {
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, input);
Handle<GlobalObject*> global = obj.as<GlobalObject>();
RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global);
if (!res) {
return false;
}
res->reset(cx, input);
return true;
}
JS_PUBLIC_API(bool) JS::ClearRegExpStatics(JSContext* cx, HandleObject obj) {
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
MOZ_ASSERT(obj);
Handle<GlobalObject*> global = obj.as<GlobalObject>();
RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global);
if (!res) {
return false;
}
res->clear();
return true;
}
JS_PUBLIC_API(bool) JS::ExecuteRegExp(JSContext* cx,
HandleObject obj,
HandleObject reobj,
char16_t* chars,
size_t length,
size_t* indexp,
bool test,
MutableHandleValue rval) {
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
Handle<GlobalObject*> global = obj.as<GlobalObject>();
RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global);
if (!res) {
return false;
}
RootedLinearString input(cx, NewStringCopyN<CanGC>(cx, chars, length));
if (!input) {
return false;
}
return ExecuteRegExpLegacy(cx,
res,
reobj.as<RegExpObject>(),
input,
indexp,
test,
rval);
}
JS_PUBLIC_API(bool) JS::ExecuteRegExpNoStatics(JSContext* cx,
HandleObject obj,
char16_t* chars,
size_t length,
size_t* indexp,
bool test,
MutableHandleValue rval) {
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
RootedLinearString input(cx, NewStringCopyN<CanGC>(cx, chars, length));
if (!input) {
return false;
}
return ExecuteRegExpLegacy(cx,
nullptr,
obj.as<RegExpObject>(),
input,
indexp,
test,
rval);
}
JS_PUBLIC_API(bool) JS::ObjectIsRegExp(JSContext* cx,
HandleObject obj,
bool* isRegExp) {
assertSameCompartment(cx, obj);
ESClass cls;
if (!GetBuiltinClass(cx, obj, &cls)) {
return false;
}
*isRegExp = cls == ESClass::RegExp;
return true;
}
JS_PUBLIC_API(RegExpFlags) JS::GetRegExpFlags(JSContext* cx, HandleObject obj) {
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
RegExpGuard shared(cx);
if (!RegExpToShared(cx, obj, &shared)) {
return RegExpFlag::NoFlags;
}
return shared->getFlags();
}
JS_PUBLIC_API(JSString*) JS::GetRegExpSource(JSContext* cx, HandleObject obj) {
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
RegExpGuard shared(cx);
if (!RegExpToShared(cx, obj, &shared)) {
return nullptr;
}
return shared->getSource();
}
+166 -114
View File
@@ -14,6 +14,10 @@
#include "builtin/SelfHostingDefines.h"
#include "gc/Marking.h"
#include "gc/Zone.h"
#include "js/RegExpFlags.h"
#ifdef JS_NEW_REGEXP
# include "new-regexp/RegExpTypes.h"
#endif
#include "proxy/Proxy.h"
#include "vm/ArrayObject.h"
#include "vm/Shape.h"
@@ -45,32 +49,10 @@ class RegExpStatics;
namespace frontend { class TokenStream; }
enum RegExpFlag
{
IgnoreCaseFlag = 0x01,
GlobalFlag = 0x02,
MultilineFlag = 0x04,
StickyFlag = 0x08,
UnicodeFlag = 0x10,
DotAllFlag = 0x20,
NoFlags = 0x00,
AllFlags = 0x3f
};
static_assert(IgnoreCaseFlag == REGEXP_IGNORECASE_FLAG &&
GlobalFlag == REGEXP_GLOBAL_FLAG &&
MultilineFlag == REGEXP_MULTILINE_FLAG &&
StickyFlag == REGEXP_STICKY_FLAG &&
UnicodeFlag == REGEXP_UNICODE_FLAG &&
DotAllFlag == REGEXP_DOTALL_FLAG,
"Flag values should be in sync with self-hosted JS");
enum RegExpRunStatus
{
RegExpRunStatus_Error,
RegExpRunStatus_Success,
RegExpRunStatus_Success_NotFound
enum RegExpRunStatus : int32_t {
RegExpRunStatus_Error = -1,
RegExpRunStatus_Success = 1,
RegExpRunStatus_Success_NotFound = 0,
};
extern RegExpObject*
@@ -80,6 +62,19 @@ RegExpAlloc(ExclusiveContext* cx, HandleObject proto = nullptr);
extern JSObject*
CloneRegExpObject(JSContext* cx, JSObject* regexp);
#ifdef JS_NEW_REGEXP
using JitCodeTable = js::irregexp::ByteArray;
inline bool IsNativeRegExpEnabled() {
# ifdef JS_CODEGEN_NONE
return false;
# else
return jit::JitOptions.nativeRegExp;
# endif
}
#endif
/*
* A RegExpShared is the compiled representation of a regexp. A RegExpShared is
* potentially pointed to by multiple RegExpObjects. Additionally, C++ code may
@@ -96,16 +91,24 @@ CloneRegExpObject(JSContext* cx, JSObject* regexp);
class RegExpShared
{
public:
enum CompilationMode {
Normal,
MatchOnly
};
enum ForceByteCodeEnum {
DontForceByteCode,
ForceByteCode
enum class Kind {
Unparsed,
Atom,
RegExp
};
enum class CodeKind {
Bytecode,
Jitcode,
Any
};
#ifdef JS_NEW_REGEXP
using ByteCode = js::irregexp::ByteArrayData;
#else
using ByteCode = uint8_t;
#endif
private:
friend class RegExpCompartment;
friend class RegExpStatics;
@@ -115,95 +118,149 @@ class RegExpShared
struct RegExpCompilation
{
HeapPtr<jit::JitCode*> jitCode;
uint8_t* byteCode;
ByteCode* byteCode;
RegExpCompilation() : byteCode(nullptr) {}
~RegExpCompilation() { js_free(byteCode); }
bool compiled(ForceByteCodeEnum force = DontForceByteCode) const {
return byteCode || (force == DontForceByteCode && jitCode);
bool compiled(CodeKind kind = CodeKind::Any) const {
switch (kind) {
case CodeKind::Bytecode:
return !!byteCode;
case CodeKind::Jitcode:
return !!jitCode;
case CodeKind::Any:
return !!byteCode || !!jitCode;
}
MOZ_CRASH("RegExp Compilation CodeKind: Unreachable");
}
};
/* Source to the RegExp, for lazy compilation. */
HeapPtr<JSAtom*> source;
RegExpFlag flags;
size_t parenCount;
bool canStringMatch;
JS::RegExpFlags flags;
size_t pairCount_;
#ifdef JS_NEW_REGEXP
RegExpShared::Kind kind_ = Kind::Unparsed;
GCPtrAtom patternAtom_;
uint32_t maxRegisters_ = 0;
uint32_t ticks_ = 0;
#else
bool canStringMatch = false;
#endif
bool marked_;
RegExpCompilation compilationArray[4];
RegExpCompilation compilationArray[2];
static int CompilationIndex(CompilationMode mode, bool latin1) {
switch (mode) {
case Normal: return latin1 ? 0 : 1;
case MatchOnly: return latin1 ? 2 : 3;
}
MOZ_CRASH();
}
static int CompilationIndex(bool latin1) { return latin1 ? 0 : 1; }
// Tables referenced by JIT code.
Vector<uint8_t*, 0, SystemAllocPolicy> tables;
/* Internal functions. */
bool compile(JSContext* cx, HandleLinearString input,
CompilationMode mode, ForceByteCodeEnum force);
CodeKind codeKind);
bool compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
CompilationMode mode, ForceByteCodeEnum force);
CodeKind codeKind);
bool compileIfNecessary(JSContext* cx, HandleLinearString input,
CompilationMode mode, ForceByteCodeEnum force);
CodeKind codeKind);
const RegExpCompilation& compilation(CompilationMode mode, bool latin1) const {
return compilationArray[CompilationIndex(mode, latin1)];
const RegExpCompilation& compilation(bool latin1) const {
return compilationArray[CompilationIndex(latin1)];
}
RegExpCompilation& compilation(CompilationMode mode, bool latin1) {
return compilationArray[CompilationIndex(mode, latin1)];
RegExpCompilation& compilation(bool latin1) {
return compilationArray[CompilationIndex(latin1)];
}
public:
RegExpShared(JSAtom* source, RegExpFlag flags);
RegExpShared(JSAtom* source, JS::RegExpFlags flags);
~RegExpShared();
// Execute this RegExp on input starting from searchIndex, filling in
// matches if specified and otherwise only determining if there is a match.
RegExpRunStatus executeAtom(JSContext* cx,
HandleLinearString input,
size_t start,
MatchPairs* matches);
// Execute this RegExp on input starting from searchIndex, filling in matches.
RegExpRunStatus execute(JSContext* cx, HandleLinearString input, size_t searchIndex,
MatchPairs* matches, size_t* endIndex);
MatchPairs* matches);
// Register a table with this RegExpShared, and take ownership.
/* #ifdef JS_NEW_REGEXP
bool addTable(JitCodeTable table) {
return tables.append(std::move(table));
}
// #else */
bool addTable(uint8_t* table) {
return tables.append(table);
}
// #endif
/* Accessors */
size_t getParenCount() const {
size_t pairCount() const {
#ifdef JS_NEW_REGEXP
MOZ_ASSERT(kind() != Kind::Unparsed);
#else
MOZ_ASSERT(isCompiled());
return parenCount;
#endif
return pairCount_;
}
/* Accounts for the "0" (whole match) pair. */
size_t pairCount() const { return getParenCount() + 1; }
#ifdef JS_NEW_REGEXP
RegExpShared::Kind kind() const { return kind_; }
// Use simple string matching for this regexp.
void useAtomMatch(HandleAtom pattern);
// Use the regular expression engine for this regexp.
void useRegExpMatch(size_t parenCount);
void tierUpTick();
bool markedForTierUp();
void setByteCode(ByteCode* code, bool latin1) {
compilation(latin1).byteCode = code;
}
ByteCode* getByteCode(bool latin1) const {
return compilation(latin1).byteCode;
}
void setJitCode(jit::JitCode* code, bool latin1) {
compilation(latin1).jitCode = code;
}
jit::JitCode* getJitCode(bool latin1) const {
return compilation(latin1).jitCode;
}
uint32_t getMaxRegisters() const { return maxRegisters_; }
void updateMaxRegisters(uint32_t numRegisters) {
maxRegisters_ = std::max(maxRegisters_, numRegisters);
}
#endif // JS_NEW_REGEXP
JSAtom* getSource() const { return source; }
RegExpFlag getFlags() const { return flags; }
bool ignoreCase() const { return flags & IgnoreCaseFlag; }
bool global() const { return flags & GlobalFlag; }
bool multiline() const { return flags & MultilineFlag; }
bool sticky() const { return flags & StickyFlag; }
bool unicode() const { return flags & UnicodeFlag; }
bool dotAll() const { return flags & DotAllFlag; }
#ifdef JS_NEW_REGEXP
JSAtom* patternAtom() const { return patternAtom_; }
#else
JSAtom* patternAtom() const { return getSource(); }
#endif
JS::RegExpFlags getFlags() const { return flags; }
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)
|| isCompiled(MatchOnly, true) || isCompiled(MatchOnly, false);
bool global() const { return flags.global(); }
bool ignoreCase() const { return flags.ignoreCase(); }
bool multiline() const { return flags.multiline(); }
bool dotAll() const { return flags.dotAll(); }
bool unicode() const { return flags.unicode(); }
bool sticky() const { return flags.sticky(); }
bool isCompiled(bool latin1, CodeKind codeKind = CodeKind::Any) const {
return compilation(latin1).compiled(codeKind);
}
bool isCompiled() const { return isCompiled(true) || isCompiled(false); }
void trace(JSTracer* trc);
bool needsSweep(JSRuntime* rt);
@@ -223,25 +280,20 @@ class RegExpShared
return offsetof(RegExpShared, flags);
}
static size_t offsetOfParenCount() {
return offsetof(RegExpShared, parenCount);
static size_t offsetOfPairCount() {
return offsetof(RegExpShared, pairCount_);
}
static size_t offsetOfLatin1JitCode(CompilationMode mode) {
return offsetof(RegExpShared, compilationArray)
+ (CompilationIndex(mode, true) * sizeof(RegExpCompilation))
+ offsetof(RegExpCompilation, jitCode);
}
static size_t offsetOfTwoByteJitCode(CompilationMode mode) {
return offsetof(RegExpShared, compilationArray)
+ (CompilationIndex(mode, false) * sizeof(RegExpCompilation))
+ offsetof(RegExpCompilation, jitCode);
static size_t offsetOfJitCode(bool latin1) {
return offsetof(RegExpShared, compilationArray) +
(CompilationIndex(latin1) * sizeof(RegExpCompilation)) +
offsetof(RegExpCompilation, jitCode);
}
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
#ifdef DEBUG
bool dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input);
bool dumpBytecode(JSContext* cx, HandleLinearString input);
#endif
};
@@ -295,23 +347,23 @@ class RegExpGuard : public JS::CustomAutoRooter
class RegExpCompartment
{
struct Key {
JSAtom* atom;
uint16_t flag;
JSAtom* atom = nullptr;
JS::RegExpFlags flags = JS::RegExpFlag::NoFlags;
Key() {}
Key(JSAtom* atom, RegExpFlag flag)
: atom(atom), flag(flag)
Key() = default;
Key(JSAtom* atom, JS::RegExpFlags flags)
: atom(atom), flags(flags)
{ }
MOZ_IMPLICIT Key(RegExpShared* shared)
: atom(shared->getSource()), flag(shared->getFlags())
: atom(shared->getSource()), flags(shared->getFlags())
{ }
typedef Key Lookup;
static HashNumber hash(const Lookup& l) {
return DefaultHasher<JSAtom*>::hash(l.atom) ^ (l.flag << 1);
return DefaultHasher<JSAtom*>::hash(l.atom) ^ (l.flags.value() << 1);
}
static bool match(Key l, Key r) {
return l.atom == r.atom && l.flag == r.flag;
return l.atom == r.atom && l.flags == r.flags;
}
};
@@ -361,7 +413,7 @@ class RegExpCompartment
bool empty() { return set_.empty(); }
bool get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGuard* g);
bool get(JSContext* cx, JSAtom* source, JS::RegExpFlags flags, RegExpGuard* g);
/* Like 'get', but compile 'maybeOpt' (if non-null). */
bool get(JSContext* cx, HandleAtom source, JSString* maybeOpt, RegExpGuard* g);
@@ -417,11 +469,11 @@ class RegExpObject : public NativeObject
static const size_t MaxPairCount = 14;
static RegExpObject*
create(ExclusiveContext* cx, const char16_t* chars, size_t length, RegExpFlag flags,
create(ExclusiveContext* cx, const char16_t* chars, size_t length, JS::RegExpFlags flags,
frontend::TokenStream* ts, LifoAlloc& alloc);
static RegExpObject*
create(ExclusiveContext* cx, HandleAtom atom, RegExpFlag flags,
create(ExclusiveContext* cx, HandleAtom atom, JS::RegExpFlags flags,
frontend::TokenStream* ts, LifoAlloc& alloc);
/*
@@ -470,21 +522,21 @@ class RegExpObject : public NativeObject
static unsigned flagsSlot() { return FLAGS_SLOT; }
RegExpFlag getFlags() const {
return RegExpFlag(getFixedSlot(FLAGS_SLOT).toInt32());
JS::RegExpFlags getFlags() const {
return JS::RegExpFlags(getFixedSlot(FLAGS_SLOT).toInt32());
}
void setFlags(RegExpFlag flags) {
setSlot(FLAGS_SLOT, Int32Value(flags));
void setFlags(JS::RegExpFlags flags) {
setFixedSlot(FLAGS_SLOT, Int32Value(flags.value()));
}
bool ignoreCase() const { return getFlags() & IgnoreCaseFlag; }
bool global() const { return getFlags() & GlobalFlag; }
bool multiline() const { return getFlags() & MultilineFlag; }
bool sticky() const { return getFlags() & StickyFlag; }
bool unicode() const { return getFlags() & UnicodeFlag; }
bool dotAll() const { return getFlags() & DotAllFlag; }
bool global() const { return getFlags().global(); }
bool ignoreCase() const { return getFlags().ignoreCase(); }
bool multiline() const { return getFlags().multiline(); }
bool dotAll() const { return getFlags().dotAll(); }
bool unicode() const { return getFlags().unicode(); }
bool sticky() const { return getFlags().sticky(); }
static bool isOriginalFlagGetter(JSNative native, RegExpFlag* mask);
static bool isOriginalFlagGetter(JSNative native, JS::RegExpFlags* mask);
static MOZ_MUST_USE bool getShared(JSContext* cx, Handle<RegExpObject*> regexp,
RegExpGuard* g);
@@ -496,12 +548,12 @@ class RegExpObject : public NativeObject
static void trace(JSTracer* trc, JSObject* obj);
void initIgnoringLastIndex(HandleAtom source, RegExpFlag flags);
void initIgnoringLastIndex(HandleAtom source, JS::RegExpFlags flags);
// NOTE: This method is *only* safe to call on RegExps that haven't been
// exposed to script, because it requires that the "lastIndex"
// property be writable.
void initAndZeroLastIndex(HandleAtom source, RegExpFlag flags, ExclusiveContext* cx);
void initAndZeroLastIndex(HandleAtom source, JS::RegExpFlags flags, ExclusiveContext* cx);
#ifdef DEBUG
static MOZ_MUST_USE bool dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp,
@@ -530,7 +582,7 @@ class RegExpObject : public NativeObject
* N.B. flagStr must be rooted.
*/
bool
ParseRegExpFlags(JSContext* cx, JSString* flagStr, RegExpFlag* flagsOut);
ParseRegExpFlags(JSContext* cx, JSString* flagStr, JS::RegExpFlags* flagsOut);
/* Assuming GetBuiltinClass(obj) is ESClass::RegExp, return a RegExpShared for obj. */
inline bool
+1 -1
View File
@@ -91,7 +91,7 @@ RegExpStatics::executeLazy(JSContext* cx)
/* Execute the full regular expression. */
RootedLinearString input(cx, matchesInput);
RegExpRunStatus status = g->execute(cx, input, lazyIndex, &this->matches, nullptr);
RegExpRunStatus status = g->execute(cx, input, lazyIndex, &this->matches);
if (status == RegExpRunStatus_Error)
return false;
+3 -3
View File
@@ -28,8 +28,8 @@ class RegExpStatics
* a different compartment via evalcx().
*/
HeapPtr<JSAtom*> lazySource;
RegExpFlag lazyFlags;
size_t lazyIndex;
JS::RegExpFlags lazyFlags;
size_t lazyIndex;
/* The latest RegExp input, set before execution. */
HeapPtr<JSString*> pendingInput;
@@ -363,7 +363,7 @@ RegExpStatics::clear()
matches.forgetArray();
matchesInput = nullptr;
lazySource = nullptr;
lazyFlags = RegExpFlag(0);
lazyFlags = JS::RegExpFlag::NoFlags;
lazyIndex = size_t(-1);
pendingInput = nullptr;
pendingLazyEvaluation = false;
+11
View File
@@ -51,6 +51,10 @@
#include "vm/Debugger.h"
#include "wasm/WasmSignalHandlers.h"
#ifdef JS_NEW_REGEXP
#include "new-regexp/RegExpAPI.h"
#endif
#include "jscntxtinlines.h"
#include "jsgcinlines.h"
@@ -280,8 +284,15 @@ JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
if (!mainThread.init())
return false;
#ifdef JS_NEW_REGEXP
isolate = irregexp::CreateIsolate(this->contextFromMainThread());
if (!isolate) {
return false;
}
#else
if (!regexpStack.init())
return false;
#endif
if (CanUseExtraThreads() && !EnsureHelperThreadsInitialized())
return false;
+10
View File
@@ -31,7 +31,9 @@
#include "frontend/NameCollections.h"
#include "gc/GCRuntime.h"
#include "gc/Tracer.h"
#ifndef JS_NEW_REGEXP
#include "irregexp/RegExpStack.h"
#endif
#include "js/Debug.h"
#include "js/GCVector.h"
#include "js/HashTable.h"
@@ -40,6 +42,9 @@
#endif
#include "js/UniquePtr.h"
#include "js/Vector.h"
#ifdef JS_NEW_REGEXP
# include "new-regexp/RegExpTypes.h"
#endif
#include "threading/Thread.h"
#include "vm/CodeCoverage.h"
#include "vm/CommonPropertyNames.h"
@@ -403,8 +408,13 @@ struct JSRuntime : public JS::shadow::Runtime,
void* addressOfJitStackLimitNoInterrupt() { return &jitStackLimitNoInterrupt_; }
#ifdef JS_NEW_REGEXP
// Shim for V8 interfaces used by irregexp code
js::irregexp::Isolate* isolate;
#else
// Information about the heap allocated backtrack stack used by RegExp JIT code.
js::irregexp::RegExpStack regexpStack;
#endif
#ifdef DEBUG
private:
+6 -2
View File
@@ -28,6 +28,7 @@
#include "js/StructuredClone.h"
#include "mozilla/Casting.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/FloatingPoint.h"
@@ -42,6 +43,7 @@
#include "builtin/MapObject.h"
#include "js/Date.h"
#include "js/GCHashTable.h"
#include "js/RegExpFlags.h"
#include "vm/SavedFrame.h"
#include "vm/SharedArrayObject.h"
#include "vm/TypedArrayObject.h"
@@ -52,12 +54,14 @@
using namespace js;
using mozilla::AssertedCast;
using mozilla::BitwiseCast;
using mozilla::IsNaN;
using mozilla::LittleEndian;
using mozilla::NativeEndian;
using mozilla::NumbersAreIdentical;
using JS::CanonicalizeNaN;
using JS::RegExpFlags;
// When you make updates here, make sure you consider whether you need to bump the
// value of JS_STRUCTURED_CLONE_VERSION in js/public/StructuredClone.h. You will
@@ -1405,7 +1409,7 @@ JSStructuredCloneWriter::startWrite(HandleValue v)
RegExpGuard re(context());
if (!RegExpToShared(context(), obj, &re))
return false;
return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags()) &&
return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags().value()) &&
writeString(SCTAG_STRING, re->getSource());
} else if (cls == ESClass::Date) {
RootedValue unboxed(context());
@@ -2040,7 +2044,7 @@ JSStructuredCloneReader::startRead(MutableHandleValue vp)
}
case SCTAG_REGEXP_OBJECT: {
RegExpFlag flags = RegExpFlag(data);
RegExpFlags flags = AssertedCast<uint8_t>(data);
uint32_t tag2, stringData;
if (!in.readPair(&tag2, &stringData))
return false;
+23
View File
@@ -2742,3 +2742,26 @@ js::unicode::IsIdentifierPartNonBMP(uint32_t codePoint)
return true;
return false;
}
size_t js::unicode::CountCodePoints(const char16_t* begin,
const char16_t* end) {
MOZ_ASSERT(begin <= end);
size_t count = 0;
const char16_t* ptr = begin;
while (ptr < end) {
count++;
if (!IsLeadSurrogate(*ptr++)) {
continue;
}
if (ptr < end && IsTrailSurrogate(*ptr)) {
ptr++;
}
}
MOZ_ASSERT(ptr == end, "should have consumed the full range");
return count;
}
+8
View File
@@ -524,6 +524,14 @@ UTF16Decode(char16_t lead, char16_t trail)
return (lead << 10) + trail + (NonBMPMin - (LeadSurrogateMin << 10) - TrailSurrogateMin);
}
/**
* Count the number of code points in [begin, end].
*
* Every sequence of 16-bit units is considered valid. Lone surrogates are
* treated as if they represented a code point of the same value.
*/
extern size_t CountCodePoints(const char16_t* begin, const char16_t* end);
} /* namespace unicode */
} /* namespace js */
-1
View File
@@ -5887,7 +5887,6 @@ nsGridContainerFrame::ReflowChildren(GridReflowInput& aState,
nsReflowStatus& aStatus)
{
MOZ_ASSERT(aState.mReflowInput);
MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
aStatus = NS_FRAME_COMPLETE;
nsOverflowAreas ocBounds;
+1 -1
View File
@@ -91,7 +91,7 @@ nsPageContentFrame::Reflow(nsPresContext* aPresContext,
FinishReflowChild(frame, aPresContext, aDesiredSize, &kidReflowInput, 0, 0,
ReflowChildFlags::Default);
NS_ASSERTION(aPresContext->IsDynamic() || !aStatus.IsFullyComplete() ||
NS_ASSERTION(aPresContext->IsDynamic() || !NS_FRAME_IS_FULLY_COMPLETE(aStatus) ||
!frame->GetNextInFlow(),
"bad child flow list");
}
+1 -1
View File
@@ -148,7 +148,7 @@ nsPageFrame::Reflow(nsPresContext* aPresContext,
FinishReflowChild(frame, aPresContext, aDesiredSize, &kidReflowInput, xc,
yc, ReflowChildFlags::Default);
NS_ASSERTION(!aStatus.IsFullyComplete() || !frame->GetNextInFlow(),
NS_ASSERTION(!NS_FRAME_IS_FULLY_COMPLETE(aStatus) || !frame->GetNextInFlow(),
"bad child flow list");
}
PR_PL(("PageFrame::Reflow %p ", this));
+2 -2
View File
@@ -337,7 +337,7 @@ nsVideoFrame::Reflow(nsPresContext* aPresContext,
ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowInput,
posterRenderRect.x, posterRenderRect.y,
ReflowChildFlags::Default, childStatus);
MOZ_ASSERT(childStatus.IsFullyComplete(),
MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus),
"We gave our child unconstrained available block-size, "
"so it should be complete!");
@@ -380,7 +380,7 @@ nsVideoFrame::Reflow(nsPresContext* aPresContext,
ReflowChild(child, aPresContext, kidDesiredSize, kidReflowInput,
mBorderPadding.left, mBorderPadding.top,
ReflowChildFlags::Default, childStatus);
MOZ_ASSERT(childStatus.IsFullyComplete(),
MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus),
"We gave our child unconstrained available block-size, "
"so it should be complete!");
+10 -10
View File
@@ -107,11 +107,11 @@ public:
*/
template<typename U,
typename =
typename std::enable_if<std::is_pointer<T>::value &&
(std::is_same<U, decltype(nullptr)>::value ||
(std::is_pointer<U>::value &&
std::is_base_of<typename std::remove_pointer<T>::type,
typename std::remove_pointer<U>::type>::value))>::type>
typename std::enable_if_t<std::is_pointer_v<T> &&
(std::is_same_v<U, decltype(nullptr)> ||
(std::is_pointer_v<U> &&
std::is_base_of_v<typename std::remove_pointer_t<T>,
typename std::remove_pointer_t<U>>))>>
MOZ_IMPLICIT
Maybe(const Maybe<U>& aOther)
: mIsSome(false)
@@ -136,11 +136,11 @@ public:
*/
template<typename U,
typename =
typename std::enable_if<std::is_pointer<T>::value &&
(std::is_same<U, decltype(nullptr)>::value ||
(std::is_pointer<U>::value &&
std::is_base_of<typename std::remove_pointer<T>::type,
typename std::remove_pointer<U>::type>::value))>::type>
typename std::enable_if_t<std::is_pointer_v<T> &&
(std::is_same_v<U, decltype(nullptr)> ||
(std::is_pointer_v<U> &&
std::is_base_of_v<typename std::remove_pointer_t<T>,
typename std::remove_pointer_t<U>>))>>
MOZ_IMPLICIT
Maybe(Maybe<U>&& aOther)
: mIsSome(false)
+2 -2
View File
@@ -351,13 +351,13 @@ private:
void OnMsgInputPending()
{
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "not on socket thread");
if (mState == STATE_TRANSFERRING)
mPollFlags |= (PR_POLL_READ | PR_POLL_EXCEPT);
}
void OnMsgOutputPending()
{
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "not on socket thread");
if (mState == STATE_TRANSFERRING)
mPollFlags |= (PR_POLL_WRITE | PR_POLL_EXCEPT);
}
@@ -2630,7 +2630,7 @@ nsHttpConnectionMgr::OnMsgCompleteUpgrade(int32_t, ARefBase *param)
void
nsHttpConnectionMgr::OnMsgUpdateParam(int32_t inParam, ARefBase *)
{
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "not on socket thread");
uint32_t param = static_cast<uint32_t>(inParam);
uint16_t name = ((param) & 0xFFFF0000) >> 16;
uint16_t value = param & 0x0000FFFF;
+2
View File
@@ -91,6 +91,8 @@ GetHDDInfo(const char* aSpecialDirName, nsAutoCString& aModel,
nsCOMPtr<nsIFile> profDir;
nsresult rv = NS_GetSpecialDirectory(aSpecialDirName,
getter_AddRefs(profDir));
if (NS_FAILED(rv))
return NS_ERROR_UNEXPECTED;
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString profDirPath;
rv = profDir->GetPath(profDirPath);