Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab605333e0 | ||
|
|
72eaed8a9b | ||
|
|
fe90c21ba4 | ||
|
|
298dcabea7 | ||
|
|
7ab5861a26 | ||
|
|
e2f692d743 | ||
|
|
abc1333598 | ||
|
|
8117870eaf | ||
|
|
5bab589ccb | ||
|
|
0d3fc7f8a1 | ||
|
|
40327a14c9 | ||
|
|
a28c4a53e2 | ||
|
|
c4b6831720 | ||
|
|
af1d8cd182 | ||
|
|
b2816e1d45 | ||
|
|
4d8e35eda9 | ||
|
|
fcc2b7b796 | ||
|
|
90f5e4c010 | ||
|
|
07319f6f09 | ||
|
|
75de7405ff | ||
|
|
bf37fe3b85 | ||
|
|
d5562576f6 | ||
|
|
8bf4262f7a |
No files matched your search
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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, ¬Found, &oolEntry))
|
||||
¬Found, &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, ¬Found, &oolEntry))
|
||||
¬Found, &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, ¬Found, &oolEntry))
|
||||
¬Found, &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(¬Found);
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ struct DefaultJitOptions
|
||||
bool eagerCompilation;
|
||||
bool forceInlineCaches;
|
||||
bool limitScriptSize;
|
||||
bool nativeRegExp;
|
||||
bool osr;
|
||||
bool asmJSAtomicsEnable;
|
||||
bool wasmTestMode;
|
||||
|
||||
@@ -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
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_; }
|
||||
|
||||
|
||||
@@ -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)); }
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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); \
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user