import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1230413 (part 1) - Pass a DrawTarget to several PaintFocus functions(). r=mattwoodrow. (5df91490ae)
- Bug 1230413 (part 2) - Change CallbackData::mContext to a DrawTarget.… r=jfkthame. (07ee11cdd3)
- Bug 1211867 - Use the font's NBSP glyph (if present) rather than rendering NBSP using the standard <space> glyph. r=jdaggett (481384c1a3)
- Bug 1230413 (part 3) - Remove unused gfxContext argument from SetSpaceGlyphIfSimple(). r=jfkthame. (6f9194709d)
- Bug 1230413 (part 4) - Remove unused nsRenderingContext argument from ForceBreak() and OptionallyBreak(). r=mattwoodrow. (8d517a64c3)
- Bug 1230413 (part 5) - Pass a DrawTarget to Draw{Dashed,SolidBorder}Segment(). r=mattwoodrow. (0ec1212b6e)
- Bug 1122788 - patch 1 - nsMultiplexInputStream should not use Tell() in debug builds, r=bz (f92a138bc7)
- iBug 1122788 - patch 2 - nsMultiplexInputStream should not use Tell() in debug builds, r=bz (afadbc68ee)
- Bug 1105044: update XULRunner stub path finding to match the updated Mac app package signing requirements. r=bsmedberg (241e69e234)
- Bug 1151506 - move nsIInterfaceInfo::isMainProcessScriptable to the end of the interface's vtable; r=dbaron (a763bdbeb5)
- Bug 1123323 - Ensure that xptiTypelibGuts cannot have a vtable; r=froydnj (c68a87f37d)
- Bug 1223142 - make XPIDL set default nullptrs for optional out parameters, r=bz (a67cafedba)
- Bug 1219246 - use UniquePtr instead of nsAuto*Ptr in xpcom/; r=erahm (c0a9c817ca)
- Bug 1217047 - try harder in IsContractIDRegistered to return a reasonable answer; r=bsmedberg,f=yury (25f42064c0)
- more of Bug 1101651 - Part 2 (bb82515cb6)
- Bug 1159302 - handle ftell errors in nsINIParser::InitFromFile; r=erahm (252bf762da)
- Bug 1159304 - silence Coverity warning about implicit conversion issues in ReadAheadLib; r=erahm (f6ae3d7af0)
- Bug 1233176 - Scalar Replacement: Initialize properties with the default value of the template object. r=shu (5ae139b872)
- Bug 1223023 - Ensure that we have enough ballast space for allocating MoveGroups in BacktrackingAllocator::resolveControlFlow. r=bhackett (ad486853ad)
- Bug 1231575 - "JitSpew: RegAlloc: dump buffer is too small for a kraken spew". r=bhackett (5042256569)
- Bug 1231581 - "IonMonkey: MOZ_CRASH() in BacktrackingAllocator.cpp:LiveRange::toString() might be unreachable." r=bhackett1024 (8453cfdb5a)
- Bug 1231581 - fix C4018 warning in WinXP. r=bhackett1024 (03ae92e018)
- Bug 1228404 - Add OOM test for module parsing and fix a couple of bugs r=shu (a71b84e9a9)
- Bug 1228579 - BacktrackingAllocator: Pack the LUse::Policy next to the LUse pointer of UsePosition. r=bhackett (91b12c5da8)
- Bug 1225203 - Make backtracking register allocator spew / dump output threadsafe r=bhackett (c44f0a04f1)
- Bug 1202784 - Part 1: Refactor SuperElemOperations for use with emitElemOperands. (r=jandem) (44adc3abf1)
- Bug 1202784 -Part 2: Seperate RequireCoercible check from JSOP_TOID. (r=jandem) (531f3e7543)
- Bug 1225026 - Remove support for atomics on Uint8ClampedArray. r=nbp (29c1dd5bc3)
- Bug 1232022 - Part 1: Mark self-hosted intrinsics for later checking. (r=till) (f49b4de863)
- Bug 1232022 - Part 2: Implement JSOP_DEBUGCHECKSELFHOSTED, and use it to check self-hosted callFunction()s. (r=till, r=h4writer) (c9cbb13f0c)
- Bug 1232113 - "Make the format specifiers in JS_snprintf() invocations more portable". r=shu (1c6d9d4cb5)
- Bug 1135377 - Part 1: Implement RegExp unicode flag. r=till, f=anba (afbcec4095)
- Bug 1220457 - Show deprecation warning for non-standard RegExp.multiline. r=till (175aaa139c)
- Bug 1135377 - Part 2: Parse RegExp unicode character in non-CharacterClass. r=till, f=anba (492f380bbb)
- Bug 1135377 - Part 3: Parse RegExp unicode character in CharacterClass. r=till, f=anba (71bba378d2)
- Bug 1135377 - Part 4: Support everything Atom in RegExp with unicode flag. r=till, f=anba (7a25bc6cd6)
- Bug 1135377 - Part 5: Support CharacterClassEscape in RegExp with unicode flag. r=till, f=anba (f368c54be5)
- Bug 1135377 - Part 6: Support ignoreCase for BMP in RegExp with unicode flag. r=till, f=anba (7c7e89b0bd)
- Bug 1135377 - Part 7: Support ignoreCase for non-BMP in RegExp with unicode flag. r=till, f=anba (6841ae2cd6)
- Bug 1135377 - Part 8: Disallow extended pattern in RegExp with unicode flag. r=till, f=anba (a12155c034)
- Bug 1135377 - Part 9: Use RegExp unicode flag in String.prototype.{match,replace,split}. r=till, f=anba (92f310d912)
- Bug 1135377 - Part 10: Decrement index when it points trail surrogate that has corresponding lead surrogate. r=till, f=anba (61ef65c7b2)
- Bug 1135377 - Part 11: Support back reference with unicode flag. r=till, f=anba (777aa5be56)
- Bug 1232113 - "Make the format specifiers in JS_snprintf() invocations more portable". r=n.nethercote (b7a402773b)
- Bug 1232113 - "Make the format specifiers in JS_snprintf() invocations more portable". r=nbp (b3d16188b4)
- Bug 1231386 - Change debugger to use RuntimeAllocPolicy for all its hash tables r=terrence (800f98186f)
- Bug 1231170: TraceLogger - Use size in debugger instead of the current id to track last logged item, r=bbouvier (a0ea950299)
- Bug 1226217 - Use less verbose cycle collection macros for mozilla::devtools::HeapSnapshot; r=bz (36ca65581d)
- Bug 1231763 - Extract breakdown parsing from JS::ubi::Census. r=jimb (698e7f5c2e)
- reapply bit of 1259403 (d6259f391e)
- Bug 1232113 - "Make the format specifiers in JS_snprintf() invocations more portable". r=jdemooij (edb4735fc5)
- Bug 1230747 - Fix element id generation rule for bytecode documentation. r=jwalden (392a974079)
- Bug 1231900 - "API Document for JS_snprintf() is non-consistent with the Implementation". r=jcoppeard (b669cf0291)
- Bug 1231883 - Implement HeapSnapshot.describeNode; r=mrbkap (515a6c4d1e)
- Bug 1232814 - Part 1: Fix a missing early return in NoteWeakEdge; r=sfink (e56620fb5d)
- Bug 1232814 - Part 2: Move LazyScript finalization to the background finalization thread; r=jonco (bb17b5a10d)
- Bug 1234106 - Avoid symbolic links in various directories used by the build system. r=gps (d150b03192)
This commit is contained in:
2023-06-08 09:57:37 +08:00
parent 193db9294d
commit 41532ef15e
177 changed files with 9223 additions and 1174 deletions
+21 -4
View File
@@ -92,8 +92,8 @@ _PTHREAD_LDFLAGS=""
dnl Do not allow objdir == srcdir builds.
dnl ==============================================================
_topsrcdir=`cd \`dirname $0\`; pwd -W 2>/dev/null || pwd`
_objdir=`pwd`
_topsrcdir=`cd \`dirname $0\`; pwd -W 2>/dev/null || pwd -P`
_objdir=`pwd -P`
dnl TODO Don't exempt L10N builds once bug 842760 is resolved.
if test "$_topsrcdir" = "$_objdir" -a "${with_l10n_base+set}" != set; then
@@ -134,7 +134,7 @@ EOF
exit 1
break
fi
MOZ_BUILD_ROOT=`pwd -W 2>/dev/null || pwd`
MOZ_BUILD_ROOT=`pwd -W 2>/dev/null || pwd -P`
DIST="$MOZ_BUILD_ROOT/dist"
MOZ_PYTHON
@@ -157,7 +157,7 @@ if test -n "$L10NBASEDIR"; then
if test "$L10NBASEDIR" = "yes" -o "$L10NBASEDIR" = "no"; then
AC_MSG_ERROR([--with-l10n-base must specify a path])
elif test -d "$L10NBASEDIR"; then
L10NBASEDIR=`cd "$L10NBASEDIR" && pwd`
L10NBASEDIR=`cd "$L10NBASEDIR" && pwd -P`
else
AC_MSG_ERROR([Invalid value --with-l10n-base, $L10NBASEDIR doesn't exist])
fi
@@ -9015,6 +9015,23 @@ export JS_STANDALONE=no
export DIST
export MOZ_LINKER
export ZLIB_IN_MOZGLUE
export MOZ_MEMORY
export AR
export RANLIB
export CPP
export CC
export CXX
export LD
export ARFLAGS
export CPPFLAGS
export CFLAGS
export CXXFLAGS
export LDFLAGS
export HOST_CC
export HOST_CXX
export HOST_CFLAGS
export HOST_CXXFLAGS
export HOST_LDFLAGS
if ! test -e js; then
mkdir js
+1 -1
View File
@@ -382,7 +382,7 @@ function onFilesOpened(message) {
testHasRun();
};
r.onload = function (event) {
todo(false, "nonexistent file shouldn't load! (FIXME: bug 1122788)");
is(false, "nonexistent file shouldn't load! (FIXME: bug 1122788)");
testHasRun();
};
try {
+3 -3
View File
@@ -1165,9 +1165,9 @@ RemoteInputStream::ReallyBlockAndWaitForStream()
#ifdef DEBUG
if (waited && mWeakSeekableStream) {
int64_t position;
MOZ_ASSERT(NS_SUCCEEDED(mWeakSeekableStream->Tell(&position)),
"Failed to determine initial stream position!");
MOZ_ASSERT(!position, "Stream not starting at 0!");
if (NS_SUCCEEDED(mWeakSeekableStream->Tell(&position))) {
MOZ_ASSERT(!position, "Stream not starting at 0!");
}
}
#endif
}
+11
View File
@@ -57,6 +57,17 @@ interface HeapSnapshot {
[Throws]
any takeCensus(object? options);
/**
* Describe `node` with the specified `breakdown`. See the comment above
* `takeCensus` or `js/src/doc/Debugger/Debugger.Memory.md` for detailed
* documentation on breakdowns.
*
* Throws an error when `node` is not the id of a node in the heap snapshot,
* or if the breakdown is invalid.
*/
[Throws]
any describeNode(object breakdown, NodeId node);
/**
* Compute the dominator tree for this heap snapshot.
*
+21 -12
View File
@@ -2389,10 +2389,15 @@ gfxFont::NotifyGlyphsChanged()
}
}
static bool
// If aChar is a "word boundary" for shaped-word caching purposes, return it;
// else return 0.
static char16_t
IsBoundarySpace(char16_t aChar, char16_t aNextChar)
{
return (aChar == ' ' || aChar == 0x00A0) && !IsClusterExtender(aNextChar);
if ((aChar == ' ' || aChar == 0x00A0) && !IsClusterExtender(aNextChar)) {
return aChar;
}
return 0;
}
#ifdef __GNUC__
@@ -2822,7 +2827,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
for (uint32_t i = 0; i <= aRunLength; ++i) {
T ch = nextCh;
nextCh = (i < aRunLength - 1) ? aString[i + 1] : '\n';
bool boundary = IsBoundarySpace(ch, nextCh);
T boundary = IsBoundarySpace(ch, nextCh);
bool invalid = !boundary && gfxFontGroup::IsInvalidChar(ch);
uint32_t length = i - wordStart;
@@ -2884,16 +2889,20 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT :
gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
}
if (!aTextRun->SetSpaceGlyphIfSimple(this, aContext,
aRunStart + i, ch,
orientation))
{
static const uint8_t space = ' ';
if (boundary != ' ' ||
!aTextRun->SetSpaceGlyphIfSimple(this, aRunStart + i, ch,
orientation)) {
// Currently, the only "boundary" characters we recognize are
// space and no-break space, which are both 8-bit, so we force
// that flag (below). If we ever change IsBoundarySpace, we
// may need to revise this.
// Avoid tautological-constant-out-of-range-compare in 8-bit:
DebugOnly<char16_t> boundary16 = boundary;
NS_ASSERTION(boundary16 < 256, "unexpected boundary!");
gfxShapedWord *sw =
GetShapedWord(aContext,
&space, 1,
gfxShapedWord::HashMix(0, ' '), aRunScript, aVertical,
appUnitsPerDevUnit,
GetShapedWord(aContext, &boundary, 1,
gfxShapedWord::HashMix(0, boundary),
aRunScript, aVertical, appUnitsPerDevUnit,
flags | gfxTextRunFactory::TEXT_IS_8BIT, tp);
if (sw) {
aTextRun->CopyGlyphDataFrom(sw, aRunStart + i);
+2 -4
View File
@@ -34,7 +34,6 @@ gfxGraphiteShaper::gfxGraphiteShaper(gfxFont *aFont)
mGrFont(nullptr), mFallbackToSmallCaps(false)
{
mCallbackData.mFont = aFont;
mCallbackData.mShaper = this;
}
gfxGraphiteShaper::~gfxGraphiteShaper()
@@ -50,8 +49,7 @@ gfxGraphiteShaper::GrGetAdvance(const void* appFontHandle, uint16_t glyphid)
{
const CallbackData *cb =
static_cast<const CallbackData*>(appFontHandle);
return FixedToFloat(cb->mFont->GetGlyphWidth(*cb->mContext->GetDrawTarget(),
glyphid));
return FixedToFloat(cb->mFont->GetGlyphWidth(*cb->mDrawTarget, glyphid));
}
static inline uint32_t
@@ -97,7 +95,7 @@ gfxGraphiteShaper::ShapeText(gfxContext *aContext,
return false;
}
mCallbackData.mContext = aContext;
mCallbackData.mDrawTarget = aContext->GetDrawTarget();
const gfxFontStyle *style = mFont->GetStyle();
+4 -3
View File
@@ -8,6 +8,8 @@
#include "gfxFont.h"
#include "mozilla/gfx/2D.h"
struct gr_face;
struct gr_font;
struct gr_segment;
@@ -42,9 +44,8 @@ protected:
gr_font *mGrFont; // owned by the shaper itself
struct CallbackData {
gfxFont *mFont;
gfxGraphiteShaper *mShaper;
gfxContext *mContext;
gfxFont* mFont;
mozilla::gfx::DrawTarget* mDrawTarget;
};
CallbackData mCallbackData;
+2 -2
View File
@@ -335,7 +335,7 @@ gfxHarfBuzzShaper::HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
gfxFont *gfxfont = fcd->mShaper->GetFont();
if (gfxfont->ProvidesGlyphWidths()) {
return gfxfont->GetGlyphWidth(*fcd->mContext->GetDrawTarget(), glyph);
return gfxfont->GetGlyphWidth(*fcd->mDrawTarget, glyph);
}
return fcd->mShaper->GetGlyphHAdvance(glyph);
}
@@ -1475,7 +1475,7 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext,
return false;
}
mCallbackData.mContext = aContext;
mCallbackData.mDrawTarget = aContext->GetDrawTarget();
mUseVerticalPresentationForms = false;
if (!Initialize()) {
+3 -2
View File
@@ -10,6 +10,7 @@
#include "harfbuzz/hb.h"
#include "nsUnicodeProperties.h"
#include "mozilla/gfx/2D.h"
class gfxHarfBuzzShaper : public gfxFontShaper {
public:
@@ -21,8 +22,8 @@ public:
* FontCallbackData struct
*/
struct FontCallbackData {
gfxHarfBuzzShaper *mShaper;
gfxContext *mContext;
gfxHarfBuzzShaper* mShaper;
mozilla::gfx::DrawTarget* mDrawTarget;
};
bool Initialize();
+3 -5
View File
@@ -1321,8 +1321,7 @@ void
gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext,
uint32_t aCharIndex, uint16_t aOrientation)
{
if (SetSpaceGlyphIfSimple(aFont, aContext, aCharIndex, ' ',
aOrientation)) {
if (SetSpaceGlyphIfSimple(aFont, aCharIndex, ' ', aOrientation)) {
return;
}
@@ -1350,9 +1349,8 @@ gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext,
}
bool
gfxTextRun::SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext,
uint32_t aCharIndex, char16_t aSpaceChar,
uint16_t aOrientation)
gfxTextRun::SetSpaceGlyphIfSimple(gfxFont* aFont, uint32_t aCharIndex,
char16_t aSpaceChar, uint16_t aOrientation)
{
uint32_t spaceGlyph = aFont->GetSpaceGlyph();
if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) {
+2 -3
View File
@@ -514,9 +514,8 @@ public:
// Returns true if it was able to set simple glyph data for the space;
// if it returns false, the caller needs to fall back to some other
// means to create the necessary (detailed) glyph data.
bool SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext,
uint32_t aCharIndex, char16_t aSpaceChar,
uint16_t aOrientation);
bool SetSpaceGlyphIfSimple(gfxFont *aFont, uint32_t aCharIndex,
char16_t aSpaceChar, uint16_t aOrientation);
// Record the positions of specific characters that layout may need to
// detect in the textrun, even though it doesn't have an explicit copy
+25 -26
View File
@@ -87,7 +87,7 @@ using CountBasePtr = UniquePtr<CountBase, CountDeleter>;
// Abstract base class for CountType nodes.
struct JS_FRIEND_API(CountType) {
explicit CountType(Census& census) : census(census) { }
explicit CountType() { }
virtual ~CountType() { }
// Destruct a count tree node that this type instance constructed.
@@ -102,14 +102,13 @@ struct JS_FRIEND_API(CountType) {
// Implement the 'count' method for counts returned by this CountType
// instance's 'newCount' method.
virtual bool count(CountBase& count, const Node& node) = 0;
virtual bool count(CountBase& count,
mozilla::MallocSizeOf mallocSizeOf,
const Node& node) = 0;
// Implement the 'report' method for counts returned by this CountType
// instance's 'newCount' method.
virtual bool report(CountBase& count, MutableHandleValue report) = 0;
protected:
Census& census;
virtual bool report(JSContext* cx, CountBase& count, MutableHandleValue report) = 0;
};
using CountTypePtr = UniquePtr<CountType, JS::DeletePolicy<CountType>>;
@@ -129,12 +128,16 @@ class JS_FRIEND_API(CountBase) {
explicit CountBase(CountType& type) : type(type), total_(0) { }
// Categorize and count |node| as appropriate for this count's type.
bool count(const Node& node) { return type.count(*this, node); }
bool count(mozilla::MallocSizeOf mallocSizeOf, const Node& node) {
return type.count(*this, mallocSizeOf, node);
}
// Construct a JavaScript object reporting the counts recorded in this
// count, and store it in |report|. Return true on success, or false on
// failure.
bool report(MutableHandleValue report) { return type.report(*this, report); }
bool report(JSContext* cx, MutableHandleValue report) {
return type.report(cx, *this, report);
}
// Down-cast this CountBase to its true type, based on its 'type' member,
// and run its destructor.
@@ -173,18 +176,6 @@ struct JS_FRIEND_API(Census) {
explicit Census(JSContext* cx) : cx(cx), atomsZone(nullptr) { }
bool init();
// A 'new' work-alike that behaves like TempAllocPolicy: report OOM on this
// census's context, but don't charge the memory allocated to our context's
// GC pressure counters.
template<typename T, typename... Args>
T* new_(Args&&... args) MOZ_HEAP_ALLOCATOR {
void* memory = js_malloc(sizeof(T));
if (MOZ_UNLIKELY(!memory)) {
return nullptr;
}
return new(memory) T(mozilla::Forward<Args>(args)...);
}
};
// A BreadthFirst handler type that conducts a census, using a CountBase to
@@ -192,15 +183,17 @@ struct JS_FRIEND_API(Census) {
class JS_FRIEND_API(CensusHandler) {
Census& census;
CountBasePtr& rootCount;
mozilla::MallocSizeOf mallocSizeOf;
public:
CensusHandler(Census& census, CountBasePtr& rootCount)
CensusHandler(Census& census, CountBasePtr& rootCount, mozilla::MallocSizeOf mallocSizeOf)
: census(census),
rootCount(rootCount)
rootCount(rootCount),
mallocSizeOf(mallocSizeOf)
{ }
bool report(MutableHandleValue report) {
return rootCount->report(report);
bool report(JSContext* cx, MutableHandleValue report) {
return rootCount->report(cx, report);
}
// This class needs to retain no per-node data.
@@ -213,11 +206,17 @@ class JS_FRIEND_API(CensusHandler) {
using CensusTraversal = BreadthFirst<CensusHandler>;
// Examine the census options supplied by the API consumer, and use that to
// build a CountType tree.
// Examine the census options supplied by the API consumer, and (among other
// things) use that to build a CountType tree.
JS_FRIEND_API(bool) ParseCensusOptions(JSContext* cx, Census& census, HandleObject options,
CountTypePtr& outResult);
// Parse the breakdown language (as described in
// js/src/doc/Debugger/Debugger.Memory.md) into a CountTypePtr. A null pointer
// is returned on error and is reported to the cx.
JS_FRIEND_API(CountTypePtr) ParseBreakdown(JSContext* cx, HandleValue breakdownValue);
} // namespace ubi
} // namespace JS
+11 -11
View File
@@ -136,7 +136,7 @@ function ArrayEvery(callbackfn/*, thisArg*/) {
/* Step b */
if (k in O) {
/* Step c. */
if (!callFunction(callbackfn, T, O[k], k, O))
if (!callContentFunction(callbackfn, T, O[k], k, O))
return false;
}
}
@@ -177,7 +177,7 @@ function ArraySome(callbackfn/*, thisArg*/) {
/* Step b */
if (k in O) {
/* Step c. */
if (callFunction(callbackfn, T, O[k], k, O))
if (callContentFunction(callbackfn, T, O[k], k, O))
return true;
}
}
@@ -218,7 +218,7 @@ function ArrayForEach(callbackfn/*, thisArg*/) {
/* Step b */
if (k in O) {
/* Step c. */
callFunction(callbackfn, T, O[k], k, O);
callContentFunction(callbackfn, T, O[k], k, O);
}
}
@@ -261,7 +261,7 @@ function ArrayMap(callbackfn/*, thisArg*/) {
/* Step b */
if (k in O) {
/* Step c.i-iii. */
var mappedValue = callFunction(callbackfn, T, O[k], k, O);
var mappedValue = callContentFunction(callbackfn, T, O[k], k, O);
_DefineDataProperty(A, k, mappedValue);
}
}
@@ -307,7 +307,7 @@ function ArrayFilter(callbackfn/*, thisArg*/) {
/* Steps 11.c.i-ii. */
var kValue = O[k];
/* Steps 11.c.iii-iv. */
var selected = callFunction(callbackfn, T, kValue, k, O);
var selected = callContentFunction(callbackfn, T, kValue, k, O);
/* Step 11.c.v. */
if (selected)
_DefineDataProperty(A, to++, kValue);
@@ -489,7 +489,7 @@ function ArrayFind(predicate/*, thisArg*/) {
/* Steps a-c. */
var kValue = O[k];
/* Steps d-f. */
if (callFunction(predicate, T, kValue, k, O))
if (callContentFunction(predicate, T, kValue, k, O))
return kValue;
}
@@ -523,7 +523,7 @@ function ArrayFindIndex(predicate/*, thisArg*/) {
*/
for (var k = 0; k < len; k++) {
/* Steps a-f. */
if (callFunction(predicate, T, O[k], k, O))
if (callContentFunction(predicate, T, O[k], k, O))
return k;
}
@@ -771,7 +771,7 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) {
// See <https://bugs.ecmascript.org/show_bug.cgi?id=2883>.
while (true) {
// Steps 6.g.i-iii.
var next = callFunction(iterator.next, iterator);
var next = callContentFunction(iterator.next, iterator);
if (!IsObject(next))
ThrowTypeError(JSMSG_NEXT_RETURNED_PRIMITIVE);
@@ -785,7 +785,7 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) {
var nextValue = next.value;
// Steps 6.g.vii-viii.
var mappedValue = mapping ? callFunction(mapfn, thisArg, nextValue, k) : nextValue;
var mappedValue = mapping ? callContentFunction(mapfn, thisArg, nextValue, k) : nextValue;
// Steps 6.g.ix-xi.
_DefineDataProperty(A, k++, mappedValue);
@@ -810,7 +810,7 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) {
var kValue = items[k];
// Steps 16.d-e.
var mappedValue = mapping ? callFunction(mapfn, thisArg, kValue, k) : kValue;
var mappedValue = mapping ? callContentFunction(mapfn, thisArg, kValue, k) : kValue;
// Steps 16.f-g.
_DefineDataProperty(A, k, mappedValue);
@@ -834,5 +834,5 @@ function ArrayToString() {
// Steps 5-6.
if (!IsCallable(func))
return callFunction(std_Object_toString, array);
return callFunction(func, array);
return callContentFunction(func, array);
}
+1 -34
View File
@@ -139,13 +139,6 @@ CompareExchange(Scalar::Type viewType, int32_t oldCandidate, int32_t newCandidat
oldval, newval);
return oldval;
}
case Scalar::Uint8Clamped: {
uint8_t oldval = ClampIntForUint8Array(oldCandidate);
uint8_t newval = ClampIntForUint8Array(newCandidate);
oldval = jit::AtomicOperations::compareExchangeSeqCst(viewData.cast<uint8_t*>() + offset,
oldval, newval);
return oldval;
}
case Scalar::Int16: {
int16_t oldval = (int16_t)oldCandidate;
int16_t newval = (int16_t)newCandidate;
@@ -235,8 +228,7 @@ js::atomics_load(JSContext* cx, unsigned argc, Value* vp)
SharedMem<void*> viewData = view->viewDataShared();
switch (view->type()) {
case Scalar::Uint8:
case Scalar::Uint8Clamped: {
case Scalar::Uint8: {
uint8_t v = jit::AtomicOperations::loadSeqCst(viewData.cast<uint8_t*>() + offset);
r.setInt32(v);
return true;
@@ -300,11 +292,6 @@ ExchangeOrStore(Scalar::Type viewType, int32_t numberValue, SharedMem<void*> vie
INT_OP(viewData.cast<uint8_t*>() + offset, value);
return value;
}
case Scalar::Uint8Clamped: {
uint8_t value = ClampIntForUint8Array(numberValue);
INT_OP(viewData.cast<uint8_t*>() + offset, value);
return value;
}
case Scalar::Int16: {
int16_t value = (int16_t)numberValue;
INT_OP(viewData.cast<int16_t*>() + offset, value);
@@ -406,26 +393,6 @@ AtomicsBinop(JSContext* cx, HandleValue objv, HandleValue idxv, HandleValue valv
r.setInt32(T::operate(viewData.cast<uint8_t*>() + offset, v));
return true;
}
case Scalar::Uint8Clamped: {
// Spec says:
// - clamp the input value
// - perform the operation
// - clamp the result
// - store the result
// This requires a CAS loop.
int32_t value = ClampIntForUint8Array(numberValue);
SharedMem<uint8_t*> loc = viewData.cast<uint8_t*>() + offset;
for (;;) {
uint8_t old = jit::AtomicOperations::loadSafeWhenRacy(loc);
uint8_t result = (uint8_t)ClampIntForUint8Array(T::perform(old, value));
uint8_t tmp = jit::AtomicOperations::compareExchangeSeqCst(loc, old, result);
if (tmp == old) {
r.setInt32(old);
break;
}
}
return true;
}
case Scalar::Int16: {
int16_t v = (int16_t)numberValue;
r.setInt32(T::operate(viewData.cast<int16_t*>() + offset, v));
+2 -2
View File
@@ -11,7 +11,7 @@ var LegacyIteratorWrapperMap = new std_WeakMap();
function LegacyIteratorNext(arg) {
var iter = callFunction(std_WeakMap_get, LegacyIteratorWrapperMap, this);
try {
return { value: callFunction(iter.next, iter, arg), done: false };
return { value: callContentFunction(iter.next, iter, arg), done: false };
} catch (e) {
if (e instanceof std_StopIteration)
return { value: undefined, done: true };
@@ -22,7 +22,7 @@ function LegacyIteratorNext(arg) {
function LegacyIteratorThrow(exn) {
var iter = callFunction(std_WeakMap_get, LegacyIteratorWrapperMap, this);
try {
return { value: callFunction(iter.throw, iter, exn), done: false };
return { value: callContentFunction(iter.throw, iter, exn), done: false };
} catch (e) {
if (e instanceof std_StopIteration)
return { value: undefined, done: true };
+1 -1
View File
@@ -29,7 +29,7 @@ function MapForEach(callbackfn, thisArg = undefined) {
if (result.done)
break;
var entry = result.value;
callFunction(callbackfn, thisArg, entry[1], entry[0], M);
callContentFunction(callbackfn, thisArg, entry[1], entry[0], M);
}
}
+1 -1
View File
@@ -1411,7 +1411,7 @@ const JSFunctionSpec selfhosting_collection_iterator_methods[] = {
bool
js::InitSelfHostingCollectionIteratorFunctions(JSContext* cx, HandleObject obj)
{
return JS_DefineFunctions(cx, obj, selfhosting_collection_iterator_methods);
return DefineFunctions(cx, obj, selfhosting_collection_iterator_methods, AsIntrinsic);
}
/*** JS static utility functions *********************************************/
+1 -1
View File
@@ -57,7 +57,7 @@ function Object_toLocaleString() {
var O = this;
// Step 2.
return callFunction(O.toString, O);
return callContentFunction(O.toString, O);
}
function ObjectDefineSetter(name, setter) {
+108 -3
View File
@@ -14,12 +14,14 @@
#include "jit/InlinableNatives.h"
#include "vm/RegExpStatics.h"
#include "vm/StringBuffer.h"
#include "vm/Unicode.h"
#include "jsobjinlines.h"
#include "vm/NativeObject-inl.h"
using namespace js;
using namespace js::unicode;
using mozilla::ArrayLength;
using mozilla::Maybe;
@@ -174,8 +176,11 @@ RegExpInitialize(JSContext* cx, Handle<RegExpObject*> obj, HandleValue patternVa
/* Steps 9-10. */
CompileOptions options(cx);
frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
if (!irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), pattern))
if (!irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), pattern,
flags & UnicodeFlag))
{
return false;
}
if (staticsUse == UseRegExpStatics) {
RegExpStatics* res = cx->global()->getRegExpStatics(cx);
@@ -557,6 +562,24 @@ regexp_sticky(JSContext* cx, unsigned argc, JS::Value* vp)
return CallNonGenericMethod<IsRegExpObject, regexp_sticky_impl>(cx, args);
}
/* ES6 21.2.5.15. */
MOZ_ALWAYS_INLINE bool
regexp_unicode_impl(JSContext* cx, const CallArgs& args)
{
MOZ_ASSERT(IsRegExpObject(args.thisv()));
/* Steps 4-6. */
args.rval().setBoolean(args.thisv().toObject().as<RegExpObject>().unicode());
return true;
}
static bool
regexp_unicode(JSContext* cx, unsigned argc, JS::Value* vp)
{
/* Steps 1-3. */
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<IsRegExpObject, regexp_unicode_impl>(cx, args);
}
const JSPropertySpec js::regexp_properties[] = {
JS_SELF_HOSTED_GET("flags", "RegExpFlagsGetter", 0),
JS_PSG("global", regexp_global, 0),
@@ -564,6 +587,7 @@ const JSPropertySpec js::regexp_properties[] = {
JS_PSG("multiline", regexp_multiline, 0),
JS_PSG("source", regexp_source, 0),
JS_PSG("sticky", regexp_sticky, 0),
JS_PSG("unicode", regexp_unicode, 0),
JS_PS_END
};
@@ -610,8 +634,6 @@ const JSFunctionSpec js::regexp_methods[] = {
}
DEFINE_STATIC_GETTER(static_input_getter, return res->createPendingInput(cx, args.rval()))
DEFINE_STATIC_GETTER(static_multiline_getter, args.rval().setBoolean(res->multiline());
return true)
DEFINE_STATIC_GETTER(static_lastMatch_getter, return res->createLastMatch(cx, args.rval()))
DEFINE_STATIC_GETTER(static_lastParen_getter, return res->createLastParen(cx, args.rval()))
DEFINE_STATIC_GETTER(static_leftContext_getter, return res->createLeftContext(cx, args.rval()))
@@ -655,6 +677,36 @@ static_input_setter(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static bool
WarnOnceAboutRegExpMultiline(JSContext* cx)
{
if (!cx->compartment()->warnedAboutRegExpMultiline) {
if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
JSMSG_DEPRECATED_REGEXP_MULTILINE))
{
return false;
}
cx->compartment()->warnedAboutRegExpMultiline = true;
}
return true;
}
static bool
static_multiline_getter(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RegExpStatics* res = cx->global()->getRegExpStatics(cx);
if (!res)
return false;
if (!WarnOnceAboutRegExpMultiline(cx))
return false;
args.rval().setBoolean(res->multiline());
return true;
}
static bool
static_multiline_setter(JSContext* cx, unsigned argc, Value* vp)
{
@@ -663,6 +715,9 @@ static_multiline_setter(JSContext* cx, unsigned argc, Value* vp)
if (!res)
return false;
if (!WarnOnceAboutRegExpMultiline(cx))
return false;
bool b = ToBoolean(args.get(0));
res->setMultiline(cx, b);
args.rval().setBoolean(b);
@@ -729,6 +784,29 @@ SetLastIndex(JSContext* cx, Handle<RegExpObject*> reobj, double lastIndex)
return true;
}
template <typename CharT>
static bool
IsTrailSurrogateWithLeadSurrogateImpl(JSContext* cx, HandleLinearString input, size_t index)
{
JS::AutoCheckCannotGC nogc;
MOZ_ASSERT(index > 0 && index < input->length());
const CharT* inputChars = input->chars<CharT>(nogc);
return unicode::IsTrailSurrogate(inputChars[index]) &&
unicode::IsLeadSurrogate(inputChars[index - 1]);
}
static bool
IsTrailSurrogateWithLeadSurrogate(JSContext* cx, HandleLinearString input, int32_t index)
{
if (index <= 0 || size_t(index) >= input->length())
return false;
return input->hasLatin1Chars()
? IsTrailSurrogateWithLeadSurrogateImpl<Latin1Char>(cx, input, index)
: IsTrailSurrogateWithLeadSurrogateImpl<char16_t>(cx, input, index);
}
/* ES6 final draft 21.2.5.2.2. */
RegExpRunStatus
js::ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
@@ -811,6 +889,33 @@ js::ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
return RegExpRunStatus_Success_NotFound;
}
/* Steps 12-13. */
if (reobj->unicode()) {
/*
* ES6 21.2.2.2 step 2.
* Let listIndex be the index into Input of the character that was
* obtained from element index of str.
*
* In the spec, pattern match is performed with decoded Unicode code
* points, but our implementation performs it with UTF-16 encoded
* string. In step 2, we should decrement searchIndex (index) if it
* points the trail surrogate that has corresponding lead surrogate.
*
* var r = /\uD83D\uDC38/ug;
* r.lastIndex = 1;
* var str = "\uD83D\uDC38";
* var result = r.exec(str); // pattern match starts from index 0
* print(result.index); // prints 0
*
* Note: this doesn't match the current spec text and result in
* different values for `result.index` under certain conditions.
* However, the spec will change to match our implementation's
* behavior. See https://github.com/tc39/ecma262/issues/128.
*/
if (IsTrailSurrogateWithLeadSurrogate(cx, input, searchIndex))
searchIndex--;
}
/* Step 14-29. */
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, searchIndex, matches);
if (status == RegExpRunStatus_Error)
+2 -3
View File
@@ -25,9 +25,8 @@ function RegExpFlagsGetter() {
result += "m";
// Steps 13-15.
// TODO: Uncomment these steps when bug 1135377 is fixed.
// if (R.unicode)
// result += "u";
if (R.unicode)
result += "u";
// Steps 16-18.
if (R.sticky)
+1 -1
View File
@@ -29,7 +29,7 @@ function SetForEach(callbackfn, thisArg = undefined) {
if (result.done)
break;
var value = result.value;
callFunction(callbackfn, thisArg, value, value, S);
callContentFunction(callbackfn, thisArg, value, value, S);
}
}
+24 -24
View File
@@ -121,7 +121,7 @@ function TypedArrayEvery(callbackfn, thisArg = undefined) {
var kValue = O[k];
// Steps 9.d.iii-9.d.iv.
var testResult = callFunction(callbackfn, T, kValue, k, O);
var testResult = callContentFunction(callbackfn, T, kValue, k, O);
// Step 9.d.v.
if (!testResult)
@@ -212,7 +212,7 @@ function TypedArrayFilter(callbackfn, thisArg = undefined) {
// Steps 13.b-c.
var kValue = O[k];
// Steps 13.d-e.
var selected = ToBoolean(callFunction(callbackfn, T, kValue, k, O));
var selected = ToBoolean(callContentFunction(callbackfn, T, kValue, k, O));
// Step 13.f.
if (selected) {
// Step 13.f.i.
@@ -264,7 +264,7 @@ function TypedArrayFind(predicate, thisArg = undefined) {
// Steps a-c.
var kValue = O[k];
// Steps d-f.
if (callFunction(predicate, T, kValue, k, O))
if (callContentFunction(predicate, T, kValue, k, O))
return kValue;
}
@@ -299,7 +299,7 @@ function TypedArrayFindIndex(predicate, thisArg = undefined) {
// Steps a (implicit), and g.
for (var k = 0; k < len; k++) {
// Steps a-f.
if (callFunction(predicate, T, O[k], k, O))
if (callContentFunction(predicate, T, O[k], k, O))
return k;
}
@@ -311,8 +311,8 @@ function TypedArrayFindIndex(predicate, thisArg = undefined) {
function TypedArrayForEach(callbackfn, thisArg = undefined) {
// This function is not generic.
if (!IsObject(this) || !IsTypedArray(this)) {
return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
"TypedArrayForEach");
return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
"TypedArrayForEach");
}
// Step 1-2.
@@ -323,9 +323,9 @@ function TypedArrayForEach(callbackfn, thisArg = undefined) {
// Step 5.
if (arguments.length === 0)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'TypedArray.prototype.forEach');
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'TypedArray.prototype.forEach');
if (!IsCallable(callbackfn))
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
// Step 6.
var T = thisArg;
@@ -333,9 +333,9 @@ function TypedArrayForEach(callbackfn, thisArg = undefined) {
// Step 7-8.
// Step 7, 8a (implicit) and 8e.
for (var k = 0; k < len; k++) {
// Step 8b-8c are unnecessary since the condition always holds true for TypedArray.
// Step 8d.
callFunction(callbackfn, T, O[k], k, O);
// Step 8b-8c are unnecessary since the condition always holds true for TypedArray.
// Step 8d.
callContentFunction(callbackfn, T, O[k], k, O);
}
// Step 9.
@@ -526,7 +526,7 @@ function TypedArrayMap(callbackfn, thisArg = undefined) {
// Steps 12, 13.a (implicit) and 13.h.
for (var k = 0; k < len; k++) {
// Steps 13.d-e.
var mappedValue = callFunction(callbackfn, T, O[k], k, O);
var mappedValue = callContentFunction(callbackfn, T, O[k], k, O);
// Steps 13.f-g.
A[k] = mappedValue;
}
@@ -567,7 +567,7 @@ function TypedArrayReduce(callbackfn/*, initialValue*/) {
// Step 11.
// Omit steps 11.b-11.c and the 'if' clause in step 11.d, since there are no holes in typed arrays.
for (; k < len; k++) {
accumulator = callFunction(callbackfn, undefined, accumulator, O[k], k, O);
accumulator = callContentFunction(callbackfn, undefined, accumulator, O[k], k, O);
}
// Step 12.
@@ -606,7 +606,7 @@ function TypedArrayReduceRight(callbackfn/*, initialValue*/) {
// Step 11.
// Omit steps 11.b-11.c and the 'if' clause in step 11.d, since there are no holes in typed arrays.
for (; k >= 0; k--) {
accumulator = callFunction(callbackfn, undefined, accumulator, O[k], k, O);
accumulator = callContentFunction(callbackfn, undefined, accumulator, O[k], k, O);
}
// Step 12.
@@ -715,15 +715,15 @@ function SetFromNonTypedArray(target, array, targetOffset, targetLength, targetB
// Step 24d. This explicit check will be unnecessary when we implement
// throw-on-getting/setting-element-in-detached-buffer semantics.
if (!isShared) {
if (!isShared) {
if (targetBuffer === null) {
// A typed array previously using inline storage may acquire a
// buffer, so we must check with the source.
targetBuffer = ViewedArrayBufferIfReified(target);
// A typed array previously using inline storage may acquire a
// buffer, so we must check with the source.
targetBuffer = ViewedArrayBufferIfReified(target);
}
if (IsDetachedBuffer(targetBuffer))
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
}
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
}
// Step 24e.
target[targetOffset] = kNumber;
@@ -892,7 +892,7 @@ function TypedArraySome(callbackfn, thisArg = undefined) {
var kValue = O[k];
// Steps 9.d.iii-9.d.iv.
var testResult = callFunction(callbackfn, T, kValue, k, O);
var testResult = callContentFunction(callbackfn, T, kValue, k, O);
// Step 9.d.v.
if (testResult)
@@ -1075,7 +1075,7 @@ function TypedArrayFrom(constructor, target, items, mapfn, thisArg) {
// Steps 10.d-e.
while (true) {
// Steps 10.e.i-ii.
var next = callFunction(iterator.next, iterator);
var next = callContentFunction(iterator.next, iterator);
if (!IsObject(next))
ThrowTypeError(JSMSG_NEXT_RETURNED_PRIMITIVE);
@@ -1099,7 +1099,7 @@ function TypedArrayFrom(constructor, target, items, mapfn, thisArg) {
var kValue = values[k];
// Steps 10.j.iii-iv.
var mappedValue = mapping ? callFunction(mapfn, T, kValue, k) : kValue;
var mappedValue = mapping ? callContentFunction(mapfn, T, kValue, k) : kValue;
// Steps 10.j.v-vi.
targetObj[k] = mappedValue;
@@ -1133,7 +1133,7 @@ function TypedArrayFrom(constructor, target, items, mapfn, thisArg) {
var kValue = arrayLike[k];
// Steps 20.d-e.
var mappedValue = mapping ? callFunction(mapfn, T, kValue, k) : kValue;
var mappedValue = mapping ? callContentFunction(mapfn, T, kValue, k) : kValue;
// Steps 20.f-g.
targetObj[k] = mappedValue;
+1 -1
View File
@@ -142,7 +142,7 @@ function GetIterator(obj, method) {
method = GetMethod(obj, std_iterator);
// Steps 3-4.
var iterator = callFunction(method, obj);
var iterator = callContentFunction(method, obj);
// Step 5.
if (!IsObject(iterator))
+3 -3
View File
@@ -73,8 +73,8 @@ _PTHREAD_LDFLAGS=""
dnl Do not allow objdir == srcdir builds
dnl ==============================================================
_topsrcdir=`cd $srcdir; pwd -W 2>/dev/null || pwd`
_objdir=`pwd`
_topsrcdir=`cd $srcdir; pwd -W 2>/dev/null || pwd -P`
_objdir=`pwd -P`
if test "$_topsrcdir" = "$_objdir"
then
@@ -112,7 +112,7 @@ if test "$_conflict_files"; then
exit 1
break
fi
MOZ_BUILD_ROOT=`pwd -W 2>/dev/null || pwd`
MOZ_BUILD_ROOT=`pwd -W 2>/dev/null || pwd -P`
dnl Choose where to put the 'dist' directory.
dnl ==============================================================
+36 -16
View File
@@ -2783,27 +2783,31 @@ BytecodeEmitter::emitNameIncDec(ParseNode* pn)
}
bool
BytecodeEmitter::emitElemOperands(ParseNode* pn, JSOp op)
BytecodeEmitter::emitElemOperands(ParseNode* pn, EmitElemOption opts)
{
MOZ_ASSERT(pn->isArity(PN_BINARY));
if (!emitTree(pn->pn_left))
return false;
if (op == JSOP_CALLELEM && !emit1(JSOP_DUP))
return false;
if (opts == EmitElemOption::IncDec) {
if (!emit1(JSOP_CHECKOBJCOERCIBLE))
return false;
} else if (opts == EmitElemOption::Call) {
if (!emit1(JSOP_DUP))
return false;
}
if (!emitTree(pn->pn_right))
return false;
bool isSetElem = op == JSOP_SETELEM || op == JSOP_STRICTSETELEM;
if (isSetElem && !emit2(JSOP_PICK, 2))
if (opts == EmitElemOption::Set && !emit2(JSOP_PICK, 2))
return false;
return true;
}
bool
BytecodeEmitter::emitSuperElemOperands(ParseNode* pn, SuperElemOptions opts)
BytecodeEmitter::emitSuperElemOperands(ParseNode* pn, EmitElemOption opts)
{
MOZ_ASSERT(pn->isKind(PNK_ELEM) && pn->as<PropertyByValue>().isSuper());
@@ -2817,13 +2821,13 @@ BytecodeEmitter::emitSuperElemOperands(ParseNode* pn, SuperElemOptions opts)
// We need to convert the key to an object id first, so that we do not do
// it inside both the GETELEM and the SETELEM.
if (opts == SuperElem_IncDec && !emit1(JSOP_TOID))
if (opts == EmitElemOption::IncDec && !emit1(JSOP_TOID))
return false;
if (!emitGetThisForSuperBase(pn->pn_left))
return false;
if (opts == SuperElem_Call) {
if (opts == EmitElemOption::Call) {
if (!emit1(JSOP_SWAP))
return false;
@@ -2835,7 +2839,7 @@ BytecodeEmitter::emitSuperElemOperands(ParseNode* pn, SuperElemOptions opts)
if (!emit1(JSOP_SUPERBASE))
return false;
if (opts == SuperElem_Set && !emit2(JSOP_PICK, 3))
if (opts == EmitElemOption::Set && !emit2(JSOP_PICK, 3))
return false;
return true;
@@ -2854,17 +2858,23 @@ BytecodeEmitter::emitElemOpBase(JSOp op)
bool
BytecodeEmitter::emitElemOp(ParseNode* pn, JSOp op)
{
return emitElemOperands(pn, op) && emitElemOpBase(op);
EmitElemOption opts = EmitElemOption::Get;
if (op == JSOP_CALLELEM)
opts = EmitElemOption::Call;
else if (op == JSOP_SETELEM || op == JSOP_STRICTSETELEM)
opts = EmitElemOption::Set;
return emitElemOperands(pn, opts) && emitElemOpBase(op);
}
bool
BytecodeEmitter::emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall)
{
SuperElemOptions opts = SuperElem_Get;
EmitElemOption opts = EmitElemOption::Get;
if (isCall)
opts = SuperElem_Call;
opts = EmitElemOption::Call;
else if (op == JSOP_SETELEM_SUPER || op == JSOP_STRICTSETELEM_SUPER)
opts = SuperElem_Set;
opts = EmitElemOption::Set;
if (!emitSuperElemOperands(pn, opts))
return false;
@@ -2885,10 +2895,10 @@ BytecodeEmitter::emitElemIncDec(ParseNode* pn)
bool isSuper = pn->pn_kid->as<PropertyByValue>().isSuper();
if (isSuper) {
if (!emitSuperElemOperands(pn->pn_kid, SuperElem_IncDec))
if (!emitSuperElemOperands(pn->pn_kid, EmitElemOption::IncDec))
return false;
} else {
if (!emitElemOperands(pn->pn_kid, JSOP_GETELEM))
if (!emitElemOperands(pn->pn_kid, EmitElemOption::IncDec))
return false;
}
@@ -7157,6 +7167,13 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn)
if (!emitTree(funNode))
return false;
#ifdef DEBUG
if (pn2->name() != cx->names().callContentFunction) {
if (!emit1(JSOP_DEBUGCHECKSELFHOSTED))
return false;
}
#endif
ParseNode* thisArg = funNode->pn_next;
if (!emitTree(thisArg))
return false;
@@ -7257,8 +7274,11 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn)
// Calls to "forceInterpreter", "callFunction" or "resumeGenerator"
// in self-hosted code generate inline bytecode.
if (pn2->name() == cx->names().callFunction)
if (pn2->name() == cx->names().callFunction ||
pn2->name() == cx->names().callContentFunction)
{
return emitSelfHostedCallFunction(pn);
}
if (pn2->name() == cx->names().resumeGenerator)
return emitSelfHostedResumeGenerator(pn);
if (pn2->name() == cx->names().forceInterpreter)
+3 -3
View File
@@ -519,7 +519,8 @@ struct BytecodeEmitter
// Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM
// opcode onto the stack in the right order. In the case of SETELEM, the
// value to be assigned must already be pushed.
bool emitElemOperands(ParseNode* pn, JSOp op);
enum class EmitElemOption { Get, Set, Call, IncDec };
bool emitElemOperands(ParseNode* pn, EmitElemOption opts);
bool emitElemOpBase(JSOp op);
bool emitElemOp(ParseNode* pn, JSOp op);
@@ -654,8 +655,7 @@ struct BytecodeEmitter
bool emitClass(ParseNode* pn);
bool emitSuperPropLHS(ParseNode* superBase, bool isCall = false);
bool emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall = false);
enum SuperElemOptions { SuperElem_Get, SuperElem_Set, SuperElem_Call, SuperElem_IncDec };
bool emitSuperElemOperands(ParseNode* pn, SuperElemOptions opts = SuperElem_Get);
bool emitSuperElemOperands(ParseNode* pn, EmitElemOption opts = EmitElemOption::Get);
bool emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall = false);
};
+2
View File
@@ -1597,6 +1597,8 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
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
break;
getChar();
+1 -1
View File
@@ -429,7 +429,7 @@ js::TraceWeakEdge(JSTracer* trc, WeakRef<T>* thingp, const char* name)
{
// Non-marking tracers treat the edge strongly.
if (!trc->isMarkingTracer())
DispatchToTracer(trc, ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name);
return DispatchToTracer(trc, ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name);
NoteWeakEdge(static_cast<GCMarker*>(trc),
ConvertToBase(thingp->unsafeUnbarrieredForTracing()));
+10 -4
View File
@@ -719,9 +719,10 @@ NativeRegExpMacroAssembler::CheckNotBackReference(int start_reg, Label* on_no_ma
}
void
NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, Label* on_no_match)
NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, Label* on_no_match,
bool unicode)
{
JitSpew(SPEW_PREFIX "CheckNotBackReferenceIgnoreCase(%d)", start_reg);
JitSpew(SPEW_PREFIX "CheckNotBackReferenceIgnoreCase(%d, %d)", start_reg, unicode);
Label fallthrough;
@@ -833,8 +834,13 @@ NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, Label
masm.passABIArg(current_character);
masm.passABIArg(current_position);
masm.passABIArg(temp1);
int (*fun)(const char16_t*, const char16_t*, size_t) = CaseInsensitiveCompareStrings;
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun));
if (!unicode) {
int (*fun)(const char16_t*, const char16_t*, size_t) = CaseInsensitiveCompareStrings;
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun));
} else {
int (*fun)(const char16_t*, const char16_t*, size_t) = CaseInsensitiveCompareUCStrings;
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun));
}
masm.storeCallResult(temp0);
masm.PopRegsInMask(volatileRegs);
+1 -1
View File
@@ -104,7 +104,7 @@ class MOZ_STACK_CLASS NativeRegExpMacroAssembler : public RegExpMacroAssembler
void CheckGreedyLoop(jit::Label* on_tos_equals_current_position);
void CheckNotAtStart(jit::Label* on_not_at_start);
void CheckNotBackReference(int start_reg, jit::Label* on_no_match);
void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match);
void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match, bool unicode);
void CheckNotCharacter(unsigned c, jit::Label* on_not_equal);
void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_not_equal);
void CheckNotCharacterAfterMinusAnd(char16_t c, char16_t minus, char16_t and_with,
+3 -1
View File
@@ -138,7 +138,9 @@ class RegExpAssertion : public RegExpTree {
END_OF_LINE,
END_OF_INPUT,
BOUNDARY,
NON_BOUNDARY
NON_BOUNDARY,
NOT_AFTER_LEAD_SURROGATE,
NOT_IN_SURROGATE_PAIR
};
explicit RegExpAssertion(AssertionType type) : assertion_type_(type) { }
virtual void* Accept(RegExpVisitor* visitor, void* data);
+2 -1
View File
@@ -90,7 +90,8 @@ V(CHECK_AT_START, 43, 8) /* bc8 pad24 addr32 */ \
V(CHECK_NOT_AT_START, 44, 8) /* bc8 pad24 addr32 */ \
V(CHECK_GREEDY, 45, 8) /* bc8 pad24 addr32 */ \
V(ADVANCE_CP_AND_GOTO, 46, 8) /* bc8 offset24 addr32 */ \
V(SET_CURRENT_POSITION_FROM_END, 47, 4) /* bc8 idx24 */
V(SET_CURRENT_POSITION_FROM_END, 47, 4) /* bc8 idx24 */ \
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, 48, 8) /* bc8 reg_idx24 addr32 */
#define DECLARE_BYTECODES(name, code, length) \
static const int BC_##name = code;
+246 -50
View File
@@ -72,12 +72,45 @@ static const int kSpaceRanges[] = { '\t', '\r' + 1, ' ', ' ' + 1,
0xFEFF, 0xFF00, 0x10000 };
static const int kSpaceRangeCount = ArrayLength(kSpaceRanges);
static const int kSpaceAndSurrogateRanges[] = { '\t', '\r' + 1, ' ', ' ' + 1,
0x00A0, 0x00A1, 0x1680, 0x1681, 0x180E, 0x180F, 0x2000, 0x200B,
0x2028, 0x202A, 0x202F, 0x2030, 0x205F, 0x2060, 0x3000, 0x3001,
unicode::LeadSurrogateMin, unicode::TrailSurrogateMax + 1,
0xFEFF, 0xFF00, 0x10000 };
static const int kSpaceAndSurrogateRangeCount = ArrayLength(kSpaceAndSurrogateRanges);
static const int kWordRanges[] = {
'0', '9' + 1, 'A', 'Z' + 1, '_', '_' + 1, 'a', 'z' + 1, 0x10000 };
static const int kWordRangeCount = ArrayLength(kWordRanges);
static const int kIgnoreCaseWordRanges[] = {
'0', '9' + 1, 'A', 'Z' + 1, '_', '_' + 1, 'a', 'z' + 1,
0x017F, 0x017F + 1, 0x212A, 0x212A + 1,
0x10000 };
static const int kIgnoreCaseWordCount = ArrayLength(kIgnoreCaseWordRanges);
static const int kWordAndSurrogateRanges[] = {
'0', '9' + 1, 'A', 'Z' + 1, '_', '_' + 1, 'a', 'z' + 1,
unicode::LeadSurrogateMin, unicode::TrailSurrogateMax + 1,
0x10000 };
static const int kWordAndSurrogateRangeCount = ArrayLength(kWordAndSurrogateRanges);
static const int kNegatedIgnoreCaseWordAndSurrogateRanges[] = {
0, '0', '9' + 1, 'A',
'K', 'K' + 1, 'S', 'S' + 1,
'Z' + 1, '_', '_' + 1, 'a',
'k', 'k' + 1, 's', 's' + 1,
'z' + 1, unicode::LeadSurrogateMin,
unicode::TrailSurrogateMax + 1, 0x10000,
0x10000 };
static const int kNegatedIgnoreCaseWordAndSurrogateRangeCount =
ArrayLength(kNegatedIgnoreCaseWordAndSurrogateRanges);
static const int kDigitRanges[] = { '0', '9' + 1, 0x10000 };
static const int kDigitRangeCount = ArrayLength(kDigitRanges);
static const int kSurrogateRanges[] = { 0xd800, 0xe000, 0x10000 };
static const int kDigitAndSurrogateRanges[] = {
'0', '9' + 1,
unicode::LeadSurrogateMin, unicode::TrailSurrogateMax + 1,
0x10000 };
static const int kDigitAndSurrogateRangeCount = ArrayLength(kDigitAndSurrogateRanges);
static const int kSurrogateRanges[] = {
unicode::LeadSurrogateMin, unicode::TrailSurrogateMax + 1,
0x10000 };
static const int kSurrogateRangeCount = ArrayLength(kSurrogateRanges);
static const int kLineTerminatorRanges[] = { 0x000A, 0x000B, 0x000D, 0x000E,
0x2028, 0x202A, 0x10000 };
@@ -164,20 +197,74 @@ CharacterRange::AddClassEscape(LifoAlloc* alloc, char16_t type,
}
}
// Add class escape, excluding surrogate pair range.
void
CharacterRange::AddClassEscapeUnicode(LifoAlloc* alloc, char16_t type,
CharacterRangeVector* ranges, bool ignore_case)
{
switch (type) {
case 's':
case 'd':
return AddClassEscape(alloc, type, ranges);
break;
case 'S':
AddClassNegated(kSpaceAndSurrogateRanges, kSpaceAndSurrogateRangeCount, ranges);
break;
case 'w':
if (ignore_case)
AddClass(kIgnoreCaseWordRanges, kIgnoreCaseWordCount, ranges);
else
AddClassEscape(alloc, type, ranges);
break;
case 'W':
if (ignore_case) {
AddClass(kNegatedIgnoreCaseWordAndSurrogateRanges,
kNegatedIgnoreCaseWordAndSurrogateRangeCount, ranges);
} else {
AddClassNegated(kWordAndSurrogateRanges, kWordAndSurrogateRangeCount, ranges);
}
break;
case 'D':
AddClassNegated(kDigitAndSurrogateRanges, kDigitAndSurrogateRangeCount, ranges);
break;
default:
MOZ_CRASH("Bad type!");
}
}
#define FOR_EACH_NON_ASCII_TO_ASCII_FOLDING(macro) \
/* LATIN CAPITAL LETTER Y WITH DIAERESIS */ \
macro(0x0178, 0x00FF) \
/* LATIN SMALL LETTER LONG S */ \
macro(0x017F, 0x0073) \
/* LATIN CAPITAL LETTER SHARP S */ \
macro(0x1E9E, 0x00DF) \
/* KELVIN SIGN */ \
macro(0x212A, 0x006B) \
/* ANGSTROM SIGN */ \
macro(0x212B, 0x00E5)
// We need to check for the following characters: 0x39c 0x3bc 0x178.
static inline bool
RangeContainsLatin1Equivalents(CharacterRange range)
RangeContainsLatin1Equivalents(CharacterRange range, bool unicode)
{
// TODO(dcarney): this could be a lot more efficient.
/* TODO(dcarney): this could be a lot more efficient. */
if (unicode) {
#define CHECK_RANGE(C, F) \
if (range.Contains(C)) return true;
FOR_EACH_NON_ASCII_TO_ASCII_FOLDING(CHECK_RANGE)
#undef CHECK_RANGE
}
return range.Contains(0x39c) || range.Contains(0x3bc) || range.Contains(0x178);
}
static bool
RangesContainLatin1Equivalents(const CharacterRangeVector& ranges)
RangesContainLatin1Equivalents(const CharacterRangeVector& ranges, bool unicode)
{
for (size_t i = 0; i < ranges.length(); i++) {
// TODO(dcarney): this could be a lot more efficient.
if (RangeContainsLatin1Equivalents(ranges[i]))
if (RangeContainsLatin1Equivalents(ranges[i], unicode))
return true;
}
return false;
@@ -190,27 +277,24 @@ static const size_t kEcma262UnCanonicalizeMaxWidth = 4;
static int
GetCaseIndependentLetters(char16_t character,
bool ascii_subject,
bool unicode,
const char16_t* choices,
size_t choices_length,
char16_t* letters)
{
const char16_t choices[] = {
character,
unicode::ToLowerCase(character),
unicode::ToUpperCase(character)
};
size_t count = 0;
for (size_t i = 0; i < ArrayLength(choices); i++) {
for (size_t i = 0; i < choices_length; i++) {
char16_t c = choices[i];
// The standard requires that non-ASCII characters cannot have ASCII
// character codes in their equivalence class, even though this
// situation occurs multiple times in the unicode tables.
static const unsigned kMaxAsciiCharCode = 127;
if (character > kMaxAsciiCharCode && c <= kMaxAsciiCharCode)
if (!unicode && character > kMaxAsciiCharCode && c <= kMaxAsciiCharCode)
continue;
// Skip characters that can't appear in one byte strings.
if (ascii_subject && c > kMaxOneByteCharCode)
if (!unicode && ascii_subject && c > kMaxOneByteCharCode)
continue;
// Watch for duplicates.
@@ -230,10 +314,45 @@ GetCaseIndependentLetters(char16_t character,
return count;
}
static int
GetCaseIndependentLetters(char16_t character,
bool ascii_subject,
bool unicode,
char16_t* letters)
{
if (unicode) {
const char16_t choices[] = {
character,
unicode::FoldCase(character),
unicode::ReverseFoldCase1(character),
unicode::ReverseFoldCase2(character),
unicode::ReverseFoldCase3(character),
};
return GetCaseIndependentLetters(character, ascii_subject, unicode,
choices, ArrayLength(choices), letters);
}
const char16_t choices[] = {
character,
unicode::ToLowerCase(character),
unicode::ToUpperCase(character)
};
return GetCaseIndependentLetters(character, ascii_subject, unicode,
choices, ArrayLength(choices), letters);
}
static char16_t
ConvertNonLatin1ToLatin1(char16_t c)
ConvertNonLatin1ToLatin1(char16_t c, bool unicode)
{
MOZ_ASSERT(c > kMaxOneByteCharCode);
if (unicode) {
switch (c) {
#define CONVERT(C, F) case C: return F;
FOR_EACH_NON_ASCII_TO_ASCII_FOLDING(CONVERT)
#undef CONVERT
}
}
switch (c) {
// This are equivalent characters in unicode.
case 0x39c:
@@ -248,12 +367,12 @@ ConvertNonLatin1ToLatin1(char16_t c)
}
void
CharacterRange::AddCaseEquivalents(bool is_ascii, CharacterRangeVector* ranges)
CharacterRange::AddCaseEquivalents(bool is_ascii, bool unicode, CharacterRangeVector* ranges)
{
char16_t bottom = from();
char16_t top = to();
if (is_ascii && !RangeContainsLatin1Equivalents(*this)) {
if (is_ascii && !RangeContainsLatin1Equivalents(*this, unicode)) {
if (bottom > kMaxOneByteCharCode)
return;
if (top > kMaxOneByteCharCode)
@@ -262,7 +381,7 @@ CharacterRange::AddCaseEquivalents(bool is_ascii, CharacterRangeVector* ranges)
for (char16_t c = bottom;; c++) {
char16_t chars[kEcma262UnCanonicalizeMaxWidth];
size_t length = GetCaseIndependentLetters(c, is_ascii, chars);
size_t length = GetCaseIndependentLetters(c, is_ascii, unicode, chars);
for (size_t i = 0; i < length; i++) {
char16_t other = chars[i];
@@ -542,7 +661,7 @@ SeqRegExpNode::FillInBMInfo(int offset,
}
RegExpNode*
SeqRegExpNode::FilterASCII(int depth, bool ignore_case)
SeqRegExpNode::FilterASCII(int depth, bool ignore_case, bool unicode)
{
if (info()->replacement_calculated)
return replacement();
@@ -552,13 +671,13 @@ SeqRegExpNode::FilterASCII(int depth, bool ignore_case)
MOZ_ASSERT(!info()->visited);
VisitMarker marker(info());
return FilterSuccessor(depth - 1, ignore_case);
return FilterSuccessor(depth - 1, ignore_case, unicode);
}
RegExpNode*
SeqRegExpNode::FilterSuccessor(int depth, bool ignore_case)
SeqRegExpNode::FilterSuccessor(int depth, bool ignore_case, bool unicode)
{
RegExpNode* next = on_success_->FilterASCII(depth - 1, ignore_case);
RegExpNode* next = on_success_->FilterASCII(depth - 1, ignore_case, unicode);
if (next == nullptr)
return set_replacement(nullptr);
@@ -701,7 +820,7 @@ TextNode::GreedyLoopTextLength()
}
RegExpNode*
TextNode::FilterASCII(int depth, bool ignore_case)
TextNode::FilterASCII(int depth, bool ignore_case, bool unicode)
{
if (info()->replacement_calculated)
return replacement();
@@ -725,7 +844,7 @@ TextNode::FilterASCII(int depth, bool ignore_case)
// Here, we need to check for characters whose upper and lower cases
// are outside the Latin-1 range.
char16_t converted = ConvertNonLatin1ToLatin1(c);
char16_t converted = ConvertNonLatin1ToLatin1(c, unicode);
if (converted == 0) {
// Character is outside Latin-1 completely
return set_replacement(nullptr);
@@ -750,7 +869,7 @@ TextNode::FilterASCII(int depth, bool ignore_case)
ranges[0].to() >= kMaxOneByteCharCode)
{
// This will be handled in a later filter.
if (ignore_case && RangesContainLatin1Equivalents(ranges))
if (ignore_case && RangesContainLatin1Equivalents(ranges, unicode))
continue;
return set_replacement(nullptr);
}
@@ -759,14 +878,14 @@ TextNode::FilterASCII(int depth, bool ignore_case)
ranges[0].from() > kMaxOneByteCharCode)
{
// This will be handled in a later filter.
if (ignore_case && RangesContainLatin1Equivalents(ranges))
if (ignore_case && RangesContainLatin1Equivalents(ranges, unicode))
continue;
return set_replacement(nullptr);
}
}
}
}
return FilterSuccessor(depth - 1, ignore_case);
return FilterSuccessor(depth - 1, ignore_case, unicode);
}
void
@@ -784,7 +903,7 @@ TextNode::CalculateOffsets()
}
}
void TextNode::MakeCaseIndependent(bool is_ascii)
void TextNode::MakeCaseIndependent(bool is_ascii, bool unicode)
{
int element_count = elements().length();
for (int i = 0; i < element_count; i++) {
@@ -800,7 +919,7 @@ void TextNode::MakeCaseIndependent(bool is_ascii)
CharacterRangeVector& ranges = cc->ranges(alloc());
int range_count = ranges.length();
for (int j = 0; j < range_count; j++)
ranges[j].AddCaseEquivalents(is_ascii, &ranges);
ranges[j].AddCaseEquivalents(is_ascii, unicode, &ranges);
}
}
}
@@ -949,7 +1068,7 @@ ChoiceNode::FillInBMInfo(int offset,
}
RegExpNode*
ChoiceNode::FilterASCII(int depth, bool ignore_case)
ChoiceNode::FilterASCII(int depth, bool ignore_case, bool unicode)
{
if (info()->replacement_calculated)
return replacement();
@@ -973,7 +1092,7 @@ ChoiceNode::FilterASCII(int depth, bool ignore_case)
for (int i = 0; i < choice_count; i++) {
GuardedAlternative alternative = alternatives()[i];
RegExpNode* replacement =
alternative.node()->FilterASCII(depth - 1, ignore_case);
alternative.node()->FilterASCII(depth - 1, ignore_case, unicode);
MOZ_ASSERT(replacement != this); // No missing EMPTY_MATCH_CHECK.
if (replacement != nullptr) {
alternatives()[i].set_node(replacement);
@@ -994,7 +1113,7 @@ ChoiceNode::FilterASCII(int depth, bool ignore_case)
new_alternatives.reserve(surviving);
for (int i = 0; i < choice_count; i++) {
RegExpNode* replacement =
alternatives()[i].node()->FilterASCII(depth - 1, ignore_case);
alternatives()[i].node()->FilterASCII(depth - 1, ignore_case, unicode);
if (replacement != nullptr) {
alternatives()[i].set_node(replacement);
AutoEnterOOMUnsafeRegion oomUnsafe;
@@ -1051,7 +1170,7 @@ NegativeLookaheadChoiceNode::GetQuickCheckDetails(QuickCheckDetails* details,
}
RegExpNode*
NegativeLookaheadChoiceNode::FilterASCII(int depth, bool ignore_case)
NegativeLookaheadChoiceNode::FilterASCII(int depth, bool ignore_case, bool unicode)
{
if (info()->replacement_calculated)
return replacement();
@@ -1065,14 +1184,14 @@ NegativeLookaheadChoiceNode::FilterASCII(int depth, bool ignore_case)
// Alternative 0 is the negative lookahead, alternative 1 is what comes
// afterwards.
RegExpNode* node = alternatives()[1].node();
RegExpNode* replacement = node->FilterASCII(depth - 1, ignore_case);
RegExpNode* replacement = node->FilterASCII(depth - 1, ignore_case, unicode);
if (replacement == nullptr)
return set_replacement(nullptr);
alternatives()[1].set_node(replacement);
RegExpNode* neg_node = alternatives()[0].node();
RegExpNode* neg_replacement = neg_node->FilterASCII(depth - 1, ignore_case);
RegExpNode* neg_replacement = neg_node->FilterASCII(depth - 1, ignore_case, unicode);
// If the negative lookahead is always going to fail then
// we don't need to check it.
@@ -1153,7 +1272,7 @@ LoopChoiceNode::FillInBMInfo(int offset,
}
RegExpNode*
LoopChoiceNode::FilterASCII(int depth, bool ignore_case)
LoopChoiceNode::FilterASCII(int depth, bool ignore_case, bool unicode)
{
if (info()->replacement_calculated)
return replacement();
@@ -1166,7 +1285,7 @@ LoopChoiceNode::FilterASCII(int depth, bool ignore_case)
VisitMarker marker(info());
RegExpNode* continue_replacement =
continue_node_->FilterASCII(depth - 1, ignore_case);
continue_node_->FilterASCII(depth - 1, ignore_case, unicode);
// If we can't continue after the loop then there is no sense in doing the
// loop.
@@ -1174,7 +1293,7 @@ LoopChoiceNode::FilterASCII(int depth, bool ignore_case)
return set_replacement(nullptr);
}
return ChoiceNode::FilterASCII(depth - 1, ignore_case);
return ChoiceNode::FilterASCII(depth - 1, ignore_case, unicode);
}
// -------------------------------------------------------------------
@@ -1203,7 +1322,7 @@ void
Analysis::VisitText(TextNode* that)
{
if (ignore_case_)
that->MakeCaseIndependent(is_ascii_);
that->MakeCaseIndependent(is_ascii_, unicode_);
EnsureAnalyzed(that->on_success());
if (!has_failed()) {
that->CalculateOffsets();
@@ -1495,7 +1614,7 @@ class irregexp::RegExpCompiler
{
public:
RegExpCompiler(JSContext* cx, LifoAlloc* alloc, int capture_count,
bool ignore_case, bool is_ascii, bool match_only);
bool ignore_case, bool is_ascii, bool match_only, bool unicode);
int AllocateRegister() {
if (next_register_ >= RegExpMacroAssembler::kMaxRegister) {
@@ -1532,6 +1651,7 @@ class irregexp::RegExpCompiler
inline bool ignore_case() { return ignore_case_; }
inline bool ascii() { return ascii_; }
inline bool unicode() { return unicode_; }
FrequencyCollator* frequency_collator() { return &frequency_collator_; }
int current_expansion_factor() { return current_expansion_factor_; }
@@ -1553,6 +1673,7 @@ class irregexp::RegExpCompiler
bool ignore_case_;
bool ascii_;
bool match_only_;
bool unicode_;
bool reg_exp_too_big_;
int current_expansion_factor_;
FrequencyCollator frequency_collator_;
@@ -1575,12 +1696,13 @@ class RecursionCheck
// Attempts to compile the regexp using an Irregexp code generator. Returns
// a fixed array or a null handle depending on whether it succeeded.
RegExpCompiler::RegExpCompiler(JSContext* cx, LifoAlloc* alloc, int capture_count,
bool ignore_case, bool ascii, bool match_only)
bool ignore_case, bool ascii, bool match_only, bool unicode)
: next_register_(2 * (capture_count + 1)),
recursion_depth_(0),
ignore_case_(ignore_case),
ascii_(ascii),
match_only_(match_only),
unicode_(unicode),
reg_exp_too_big_(false),
current_expansion_factor_(1),
frequency_collator_(),
@@ -1653,7 +1775,8 @@ IsNativeRegExpEnabled(JSContext* cx)
RegExpCode
irregexp::CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* data,
HandleLinearString sample, bool is_global, bool ignore_case,
bool is_ascii, bool match_only, bool force_bytecode, bool sticky)
bool is_ascii, bool match_only, bool force_bytecode, bool sticky,
bool unicode)
{
if ((data->capture_count + 1) * 2 - 1 > RegExpMacroAssembler::kMaxRegister) {
JS_ReportError(cx, "regexp too big");
@@ -1661,7 +1784,8 @@ irregexp::CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData*
}
LifoAlloc& alloc = cx->tempLifoAlloc();
RegExpCompiler compiler(cx, &alloc, data->capture_count, ignore_case, is_ascii, match_only);
RegExpCompiler compiler(cx, &alloc, data->capture_count, ignore_case, is_ascii, match_only,
unicode);
// Sample some characters from the middle of the string.
if (sample->hasLatin1Chars()) {
@@ -1707,18 +1831,18 @@ irregexp::CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData*
}
}
if (is_ascii) {
node = node->FilterASCII(RegExpCompiler::kMaxRecursion, ignore_case);
node = node->FilterASCII(RegExpCompiler::kMaxRecursion, ignore_case, unicode);
// 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->FilterASCII(RegExpCompiler::kMaxRecursion, ignore_case);
node = node->FilterASCII(RegExpCompiler::kMaxRecursion, ignore_case, unicode);
}
}
if (node == nullptr)
node = alloc.newInfallible<EndNode>(&alloc, EndNode::BACKTRACK);
Analysis analysis(cx, ignore_case, is_ascii);
Analysis analysis(cx, ignore_case, is_ascii, unicode);
analysis.EnsureAnalyzed(node);
if (analysis.has_failed()) {
JS_ReportError(cx, analysis.errorMessage());
@@ -2056,6 +2180,10 @@ RegExpAssertion::ToNode(RegExpCompiler* compiler,
result->AddAlternative(end_alternative);
return result;
}
case NOT_AFTER_LEAD_SURROGATE:
return AssertionNode::NotAfterLeadSurrogate(on_success);
case NOT_IN_SURROGATE_PAIR:
return AssertionNode::NotInSurrogatePair(on_success);
default:
MOZ_CRASH("Bad assertion type");
}
@@ -2843,6 +2971,65 @@ EmitHat(RegExpCompiler* compiler, RegExpNode* on_success, Trace* trace)
on_success->Emit(compiler, &new_trace);
}
// Assert that the next character cannot be a part of a surrogate pair.
static void
EmitNotAfterLeadSurrogate(RegExpCompiler* compiler, RegExpNode* on_success, Trace* trace)
{
RegExpMacroAssembler* assembler = compiler->macro_assembler();
// We will be loading the previous character into the current character
// register.
Trace new_trace(*trace);
new_trace.InvalidateCurrentCharacter();
jit::Label ok;
if (new_trace.cp_offset() == 0)
assembler->CheckAtStart(&ok);
// We already checked that we are not at the start of input so it must be
// OK to load the previous character.
assembler->LoadCurrentCharacter(new_trace.cp_offset() - 1, new_trace.backtrack(), false);
assembler->CheckCharacterInRange(unicode::LeadSurrogateMin, unicode::LeadSurrogateMax,
new_trace.backtrack());
assembler->Bind(&ok);
on_success->Emit(compiler, &new_trace);
}
// Assert that the next character is not a trail surrogate that has a
// corresponding lead surrogate.
static void
EmitNotInSurrogatePair(RegExpCompiler* compiler, RegExpNode* on_success, Trace* trace)
{
RegExpMacroAssembler* assembler = compiler->macro_assembler();
jit::Label ok;
assembler->CheckPosition(trace->cp_offset(), &ok);
// We will be loading the next and previous characters into the current
// character register.
Trace new_trace(*trace);
new_trace.InvalidateCurrentCharacter();
if (new_trace.cp_offset() == 0)
assembler->CheckAtStart(&ok);
// First check if next character is a trail surrogate.
assembler->LoadCurrentCharacter(new_trace.cp_offset(), new_trace.backtrack(), false);
assembler->CheckCharacterNotInRange(unicode::TrailSurrogateMin, unicode::TrailSurrogateMax,
&ok);
// Next check if previous character is a lead surrogate.
// We already checked that we are not at the start of input so it must be
// OK to load the previous character.
assembler->LoadCurrentCharacter(new_trace.cp_offset() - 1, new_trace.backtrack(), false);
assembler->CheckCharacterInRange(unicode::LeadSurrogateMin, unicode::LeadSurrogateMax,
new_trace.backtrack());
assembler->Bind(&ok);
on_success->Emit(compiler, &new_trace);
}
// Check for [0-9A-Z_a-z].
static void
EmitWordCheck(RegExpMacroAssembler* assembler,
@@ -2996,6 +3183,12 @@ AssertionNode::Emit(RegExpCompiler* compiler, Trace* trace)
EmitBoundaryCheck(compiler, trace);
return;
}
case NOT_AFTER_LEAD_SURROGATE:
EmitNotAfterLeadSurrogate(compiler, on_success(), trace);
return;
case NOT_IN_SURROGATE_PAIR:
EmitNotInSurrogatePair(compiler, on_success(), trace);
return;
}
on_success()->Emit(compiler, trace);
}
@@ -3524,7 +3717,7 @@ EmitAtomNonLetter(RegExpCompiler* compiler,
RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
bool ascii = compiler->ascii();
char16_t chars[kEcma262UnCanonicalizeMaxWidth];
int length = GetCaseIndependentLetters(c, ascii, chars);
int length = GetCaseIndependentLetters(c, ascii, compiler->unicode(), chars);
if (length < 1) {
// This can't match. Must be an ASCII subject and a non-ASCII character.
// We do not need to do anything since the ASCII pass already handled this.
@@ -3600,7 +3793,7 @@ EmitAtomLetter(RegExpCompiler* compiler,
RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
bool ascii = compiler->ascii();
char16_t chars[kEcma262UnCanonicalizeMaxWidth];
int length = GetCaseIndependentLetters(c, ascii, chars);
int length = GetCaseIndependentLetters(c, ascii, compiler->unicode(), chars);
if (length <= 1) return false;
// We may not need to check against the end of the input string
// if this character lies before a character that matched.
@@ -4469,7 +4662,8 @@ BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace)
MOZ_ASSERT(start_reg_ + 1 == end_reg_);
if (compiler->ignore_case()) {
assembler->CheckNotBackReferenceIgnoreCase(start_reg_,
trace->backtrack());
trace->backtrack(),
compiler->unicode());
} else {
assembler->CheckNotBackReference(start_reg_, trace->backtrack());
}
@@ -4615,6 +4809,7 @@ TextNode::FillInBMInfo(int initial_offset,
char16_t chars[kEcma262UnCanonicalizeMaxWidth];
int length = GetCaseIndependentLetters(character,
bm->max_char() == kMaxOneByteCharCode,
bm->compiler()->unicode(),
chars);
for (int j = 0; j < length; j++)
bm->Set(offset, chars[j]);
@@ -4706,7 +4901,8 @@ TextNode::GetQuickCheckDetails(QuickCheckDetails* details,
}
if (compiler->ignore_case()) {
char16_t chars[kEcma262UnCanonicalizeMaxWidth];
size_t length = GetCaseIndependentLetters(c, compiler->ascii(), chars);
size_t length = GetCaseIndependentLetters(c, compiler->ascii(),
compiler->unicode(), chars);
MOZ_ASSERT(length != 0); // Can only happen if c > char_mask (see above).
if (length == 1) {
// This letter has no case equivalents, so it's nice and simple
+27 -12
View File
@@ -88,7 +88,8 @@ struct RegExpCode
RegExpCode
CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* data,
HandleLinearString sample, bool is_global, bool ignore_case,
bool is_ascii, bool match_only, bool force_bytecode, bool sticky);
bool is_ascii, bool match_only, bool force_bytecode, bool sticky,
bool unicode);
// Note: this may return RegExpRunStatus_Error if an interrupt was requested
// while the code was executing.
@@ -144,6 +145,8 @@ class CharacterRange
{}
static void AddClassEscape(LifoAlloc* alloc, char16_t type, CharacterRangeVector* ranges);
static void AddClassEscapeUnicode(LifoAlloc* alloc, char16_t type,
CharacterRangeVector* ranges, bool ignoreCase);
static inline CharacterRange Singleton(char16_t value) {
return CharacterRange(value, value);
@@ -163,7 +166,7 @@ class CharacterRange
bool is_valid() { return from_ <= to_; }
bool IsEverything(char16_t max) { return from_ == 0 && to_ >= max; }
bool IsSingleton() { return (from_ == to_); }
void AddCaseEquivalents(bool is_ascii, CharacterRangeVector* ranges);
void AddCaseEquivalents(bool is_ascii, bool unicode, CharacterRangeVector* ranges);
static void Split(const LifoAlloc* alloc,
CharacterRangeVector base,
@@ -516,7 +519,7 @@ class RegExpNode
// If we know that the input is ASCII then there are some nodes that can
// never match. This method returns a node that can be substituted for
// itself, or nullptr if the node can never match.
virtual RegExpNode* FilterASCII(int depth, bool ignore_case) { return this; }
virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode) { return this; }
// Helper for FilterASCII.
RegExpNode* replacement() {
@@ -623,14 +626,14 @@ class SeqRegExpNode : public RegExpNode
RegExpNode* on_success() { return on_success_; }
void set_on_success(RegExpNode* node) { on_success_ = node; }
virtual RegExpNode* FilterASCII(int depth, bool ignore_case);
virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode);
virtual bool FillInBMInfo(int offset,
int budget,
BoyerMooreLookahead* bm,
bool not_at_start);
protected:
RegExpNode* FilterSuccessor(int depth, bool ignore_case);
RegExpNode* FilterSuccessor(int depth, bool ignore_case, bool unicode);
private:
RegExpNode* on_success_;
@@ -748,7 +751,7 @@ class TextNode : public SeqRegExpNode
int characters_filled_in,
bool not_at_start);
TextElementVector& elements() { return *elements_; }
void MakeCaseIndependent(bool is_ascii);
void MakeCaseIndependent(bool is_ascii, bool unicode);
virtual int GreedyLoopTextLength();
virtual RegExpNode* GetSuccessorOfOmnivorousTextNode(
RegExpCompiler* compiler);
@@ -757,7 +760,7 @@ class TextNode : public SeqRegExpNode
BoyerMooreLookahead* bm,
bool not_at_start);
void CalculateOffsets();
virtual RegExpNode* FilterASCII(int depth, bool ignore_case);
virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode);
private:
enum TextEmitPassType {
@@ -788,7 +791,9 @@ class AssertionNode : public SeqRegExpNode
AT_START,
AT_BOUNDARY,
AT_NON_BOUNDARY,
AFTER_NEWLINE
AFTER_NEWLINE,
NOT_AFTER_LEAD_SURROGATE,
NOT_IN_SURROGATE_PAIR
};
AssertionNode(AssertionType t, RegExpNode* on_success)
: SeqRegExpNode(on_success), assertion_type_(t)
@@ -809,6 +814,14 @@ class AssertionNode : public SeqRegExpNode
static AssertionNode* AfterNewline(RegExpNode* on_success) {
return on_success->alloc()->newInfallible<AssertionNode>(AFTER_NEWLINE, on_success);
}
static AssertionNode* NotAfterLeadSurrogate(RegExpNode* on_success) {
return on_success->alloc()->newInfallible<AssertionNode>(NOT_AFTER_LEAD_SURROGATE,
on_success);
}
static AssertionNode* NotInSurrogatePair(RegExpNode* on_success) {
return on_success->alloc()->newInfallible<AssertionNode>(NOT_IN_SURROGATE_PAIR,
on_success);
}
virtual void Accept(NodeVisitor* visitor);
virtual void Emit(RegExpCompiler* compiler, Trace* trace);
virtual int EatsAtLeast(int still_to_find, int budget, bool not_at_start);
@@ -1006,7 +1019,7 @@ class ChoiceNode : public RegExpNode
void set_not_at_start() { not_at_start_ = true; }
void set_being_calculated(bool b) { being_calculated_ = b; }
virtual bool try_to_emit_quick_check_for_alternative(int i) { return true; }
virtual RegExpNode* FilterASCII(int depth, bool ignore_case);
virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode);
protected:
int GreedyLoopTextLengthForAlternative(GuardedAlternative* alternative);
@@ -1059,7 +1072,7 @@ class NegativeLookaheadChoiceNode : public ChoiceNode
// characters, but on a negative lookahead the negative branch did not take
// part in that calculation (EatsAtLeast) so the assumptions don't hold.
virtual bool try_to_emit_quick_check_for_alternative(int i) { return i != 0; }
virtual RegExpNode* FilterASCII(int depth, bool ignore_case);
virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode);
};
class LoopChoiceNode : public ChoiceNode
@@ -1088,7 +1101,7 @@ class LoopChoiceNode : public ChoiceNode
RegExpNode* continue_node() { return continue_node_; }
bool body_can_be_zero_length() { return body_can_be_zero_length_; }
virtual void Accept(NodeVisitor* visitor);
virtual RegExpNode* FilterASCII(int depth, bool ignore_case);
virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode);
private:
// AddAlternative is made private for loop nodes because alternatives
@@ -1459,10 +1472,11 @@ class NodeVisitor
class Analysis : public NodeVisitor
{
public:
Analysis(JSContext* cx, bool ignore_case, bool is_ascii)
Analysis(JSContext* cx, bool ignore_case, bool is_ascii, bool unicode)
: cx(cx),
ignore_case_(ignore_case),
is_ascii_(is_ascii),
unicode_(unicode),
error_message_(nullptr)
{}
@@ -1487,6 +1501,7 @@ class Analysis : public NodeVisitor
JSContext* cx;
bool ignore_case_;
bool is_ascii_;
bool unicode_;
const char* error_message_;
Analysis(Analysis&) = delete;
+21
View File
@@ -442,6 +442,27 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha
}
break;
}
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE) {
int from = registers[insn >> BYTECODE_SHIFT];
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
if (from < 0 || len <= 0) {
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE_LENGTH;
break;
}
if (current + len > length) {
pc = byteCode + Load32Aligned(pc + 4);
break;
}
if (CaseInsensitiveCompareUCStrings(chars + from, chars + current,
len * sizeof(CharT)))
{
current += len;
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE_LENGTH;
} else {
pc = byteCode + Load32Aligned(pc + 4);
}
break;
}
BYTECODE(CHECK_AT_START)
if (current == 0)
pc = byteCode + Load32Aligned(pc + 4);
+39 -2
View File
@@ -65,6 +65,38 @@ template int
irregexp::CaseInsensitiveCompareStrings(const char16_t* substring1, const char16_t* substring2,
size_t byteLength);
template <typename CharT>
int
irregexp::CaseInsensitiveCompareUCStrings(const CharT* substring1, const CharT* substring2,
size_t byteLength)
{
MOZ_ASSERT(byteLength % sizeof(CharT) == 0);
size_t length = byteLength / sizeof(CharT);
for (size_t i = 0; i < length; i++) {
char16_t c1 = substring1[i];
char16_t c2 = substring2[i];
if (c1 != c2) {
c1 = unicode::FoldCase(c1);
c2 = unicode::FoldCase(c2);
if (c1 != c2)
return 0;
}
}
return 1;
}
template int
irregexp::CaseInsensitiveCompareUCStrings(const Latin1Char* substring1,
const Latin1Char* substring2,
size_t byteLength);
template int
irregexp::CaseInsensitiveCompareUCStrings(const char16_t* substring1,
const char16_t* substring2,
size_t byteLength);
InterpretedRegExpMacroAssembler::InterpretedRegExpMacroAssembler(LifoAlloc* alloc, RegExpShared* shared,
size_t numSavedRegisters)
: RegExpMacroAssembler(*alloc, shared, numSavedRegisters),
@@ -210,11 +242,16 @@ InterpretedRegExpMacroAssembler::CheckNotBackReference(int start_reg, jit::Label
}
void
InterpretedRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match)
InterpretedRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg,
jit::Label* on_no_match,
bool unicode)
{
MOZ_ASSERT(start_reg >= 0);
MOZ_ASSERT(start_reg <= kMaxRegister);
Emit(BC_CHECK_NOT_BACK_REF_NO_CASE, start_reg);
if (unicode)
Emit(BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE, start_reg);
else
Emit(BC_CHECK_NOT_BACK_REF_NO_CASE, start_reg);
EmitOrLink(on_no_match);
}
+8 -2
View File
@@ -112,7 +112,8 @@ class MOZ_STACK_CLASS RegExpMacroAssembler
virtual void CheckGreedyLoop(jit::Label* on_tos_equals_current_position) = 0;
virtual void CheckNotAtStart(jit::Label* on_not_at_start) = 0;
virtual void CheckNotBackReference(int start_reg, jit::Label* on_no_match) = 0;
virtual void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match) = 0;
virtual void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match,
bool unicode) = 0;
// Check the current character for a match with a literal character. If we
// fail to match then goto the on_failure label. End of input always
@@ -221,6 +222,11 @@ template <typename CharT>
int
CaseInsensitiveCompareStrings(const CharT* substring1, const CharT* substring2, size_t byteLength);
template <typename CharT>
int
CaseInsensitiveCompareUCStrings(const CharT* substring1, const CharT* substring2,
size_t byteLength);
class MOZ_STACK_CLASS InterpretedRegExpMacroAssembler : public RegExpMacroAssembler
{
public:
@@ -241,7 +247,7 @@ class MOZ_STACK_CLASS InterpretedRegExpMacroAssembler : public RegExpMacroAssemb
void CheckGreedyLoop(jit::Label* on_tos_equals_current_position);
void CheckNotAtStart(jit::Label* on_not_at_start);
void CheckNotBackReference(int start_reg, jit::Label* on_no_match);
void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match);
void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match, bool unicode);
void CheckNotCharacter(unsigned c, jit::Label* on_not_equal);
void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_not_equal);
void CheckNotCharacterAfterMinusAnd(char16_t c, char16_t minus, char16_t and_with,
File diff suppressed because it is too large Load Diff
+17 -7
View File
@@ -43,11 +43,12 @@ namespace irregexp {
bool
ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str,
bool multiline, bool match_only,
bool multiline, bool match_only, bool unicode, bool ignore_case,
RegExpCompileData* data);
bool
ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str);
ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str,
bool unicode);
// A BufferedVector is an automatically growing list, just like (and backed
// by) a Vector, that is optimized for the case of adding and removing
@@ -174,7 +175,8 @@ class RegExpParser
{
public:
RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc,
const CharT* chars, const CharT* end, bool multiline_mode);
const CharT* chars, const CharT* end, bool multiline_mode, bool unicode,
bool ignore_case);
RegExpTree* ParsePattern();
RegExpTree* ParseDisjunction();
@@ -184,14 +186,20 @@ class RegExpParser
// out parameters.
bool ParseIntervalQuantifier(int* min_out, int* max_out);
// Parses and returns a single escaped character. The character
// must not be 'b' or 'B' since they are usually handled specially.
widechar ParseClassCharacterEscape();
// Tries to parse the input as a single escaped character. If successful
// it stores the result in the output parameter and returns true.
// Otherwise it throws an error and returns false. The character must not
// be 'b' or 'B' since they are usually handled specially.
bool ParseClassCharacterEscape(widechar* code);
// Checks whether the following is a length-digit hexadecimal number,
// and sets the value if it is.
bool ParseHexEscape(int length, size_t* value);
bool ParseBracedHexEscape(size_t* value);
bool ParseTrailSurrogate(size_t* value);
bool ParseRawSurrogatePair(char16_t* lead, char16_t* trail);
size_t ParseOctalLiteral();
// Tries to parse the input as a back reference. If successful it
@@ -200,7 +208,7 @@ class RegExpParser
// can be reparsed.
bool ParseBackReferenceIndex(int* index_out);
bool ParseClassAtom(char16_t* char_class, CharacterRange* char_range);
bool ParseClassAtom(char16_t* char_class, widechar *value);
RegExpTree* ReportError(unsigned errorNumber);
void Advance();
void Advance(int dist) {
@@ -288,6 +296,8 @@ class RegExpParser
int capture_count_;
bool has_more_;
bool multiline_;
bool unicode_;
bool ignore_case_;
bool simple_;
bool contains_anchor_;
bool is_scanned_for_captures_;
+16 -2
View File
@@ -409,6 +409,19 @@ function testIsLockFree() {
assertEq(Atomics.isLockFree(12), false);
}
function testUint8Clamped(sab) {
var ta = new Uint8ClampedArray(sab);
var thrown = false;
try {
CLONE(testMethod)(ta, 0);
}
catch (e) {
thrown = true;
assertEq(e instanceof TypeError, true);
}
assertEq(thrown, true);
}
function isLittleEndian() {
var xxx = new ArrayBuffer(2);
var xxa = new Int16Array(xxx);
@@ -440,7 +453,6 @@ function runTests() {
// Test that invoking as Atomics.whatever() works, on correct arguments.
CLONE(testMethod)(new Int8Array(sab), 0, 42, 4095);
CLONE(testMethod)(new Uint8Array(sab), 0, 42, 4095);
CLONE(testMethod)(new Uint8ClampedArray(sab), 0, 42, 4095);
CLONE(testMethod)(new Int16Array(sab), 0, 42, 2047);
CLONE(testMethod)(new Uint16Array(sab), 0, 42, 2047);
CLONE(testMethod)(new Int32Array(sab), 0, 42, 1023);
@@ -460,7 +472,6 @@ function runTests() {
CLONE(testFunction)(new Int8Array(sab), 0, 42, 4095);
CLONE(testFunction)(new Uint8Array(sab), 0, 42, 4095);
CLONE(testFunction)(new Uint8ClampedArray(sab), 0, 42, 4095);
CLONE(testFunction)(new Int16Array(sab), 0, 42, 2047);
CLONE(testFunction)(new Uint16Array(sab), 0, 42, 2047);
CLONE(testFunction)(new Int32Array(sab), 0, 42, 1023);
@@ -497,6 +508,9 @@ function runTests() {
testInt16Extremes(new Int16Array(sab));
testUint32(new Uint32Array(sab));
// Test that Uint8ClampedArray is not accepted.
testUint8Clamped(sab);
// Misc ad-hoc tests
adHocExchange();
@@ -0,0 +1,23 @@
// RegExp.multiline access should be warned once and only once.
function testWarn(code) {
enableLastWarning();
var g = newGlobal();
g.code = code;
g.eval('eval(code)');
var warning = getLastWarning();
assertEq(warning !== null, true, "warning should be caught for " + code);
assertEq(warning.name, "SyntaxError");
clearLastWarning();
g.eval('eval(code)');
warning = getLastWarning();
assertEq(warning, null, "warning should not be caught for 2nd ocurrence");
disableLastWarning();
}
testWarn("var a = RegExp.multiline;");
testWarn("RegExp.multiline = true;");
testWarn("var a = RegExp['$*'];");
testWarn("RegExp['$*'] = true;");
+19
View File
@@ -0,0 +1,19 @@
if (!('oomTest' in this))
quit();
function f1() {}
function f2() {}
r = [function() {}, function() {}, [], function() {}, f1, function() {}, f2];
l = [0];
function f3() {
return {
a: 0
};
}
var x = f3();
var h = newGlobal();
var dbg = new Debugger;
dbg.addDebuggee(h);
oomTest(() => getBacktrace({
thisprops: gc()
}));
@@ -0,0 +1,39 @@
// This function uses UCE to test when the if branch is removed by
// IonMonkey. Some optimization such as Scalar Replacement are able to remove
// the scope chain, which can cause issues when the scope chain properties are
// not initialized properly.
var uceFault = function (i) {
if (i % 1500 == 0) {
uceFault = function (i) { return i % 1500 == 0; };
}
return false;
};
function f(i) {
if (uceFault(i) || uceFault(i))
g();
const x = 42;
function g() {
return x;
}
return g;
}
function loop() {
for (; i < 4000; i++)
assertEq(f(i)(), 42);
}
var caught = 0;
var i = 1;
while (i < 4000) {
try {
loop();
} catch(e) {
assertEq(e instanceof ReferenceError, true);
assertEq(i == 1500 || i == 3000, true);
caught += 1;
i++;
}
}
assertEq(caught, 2);
@@ -0,0 +1,26 @@
// OOM tests for module parsing.
if (!('oomTest' in this))
quit();
load(libdir + "dummyModuleResolveHook.js");
const sa =
`export default 20;
export let a = 22;
export function f(x, y) { return x + y }
`;
const sb =
`import x from "a";
import { a as y } from "a";
import * as ns from "a";
ns.f(x, y);
`;
oomTest(() => {
let a = moduleRepo['a'] = parseModule(sa);
let b = moduleRepo['b'] = parseModule(sb);
b.declarationInstantiation();
assertEq(b.evaluation(), 42);
});
@@ -0,0 +1,3 @@
var du = new Debugger();
if (typeof du.drainTraceLogger === "function")
du.drainTraceLogger();
+67 -80
View File
@@ -1006,12 +1006,12 @@ BacktrackingAllocator::tryMergeReusedRegister(VirtualRegister& def, VirtualRegis
if (iter->pos <= inputOf(def.ins()))
continue;
LUse* use = iter->use;
LUse* use = iter->use();
if (FindReusingDefinition(insData[iter->pos], use)) {
def.setMustCopyInput();
return true;
}
if (use->policy() != LUse::ANY && use->policy() != LUse::KEEPALIVE) {
if (iter->usePolicy() != LUse::ANY && iter->usePolicy() != LUse::KEEPALIVE) {
def.setMustCopyInput();
return true;
}
@@ -1134,6 +1134,9 @@ BacktrackingAllocator::mergeAndQueueRegisters()
LiveRange* range = LiveRange::get(*iter);
LiveBundle* bundle = range->bundle();
if (range == bundle->firstRange()) {
if (!alloc().ensureBallast())
return false;
SpillSet* spill = SpillSet::New(alloc());
if (!spill)
return false;
@@ -1225,7 +1228,7 @@ BacktrackingAllocator::processBundle(LiveBundle* bundle)
{
if (JitSpewEnabled(JitSpew_RegAlloc)) {
JitSpew(JitSpew_RegAlloc, "Allocating %s [priority %lu] [weight %lu]",
bundle->toString(), computePriority(bundle), computeSpillWeight(bundle));
bundle->toString().get(), computePriority(bundle), computeSpillWeight(bundle));
}
// A bundle can be processed by doing any of the following:
@@ -1318,7 +1321,7 @@ BacktrackingAllocator::computeRequirement(LiveBundle* bundle,
if (policy == LDefinition::FIXED) {
// Fixed policies get a FIXED requirement.
JitSpew(JitSpew_RegAlloc, " Requirement %s, fixed by definition",
reg.def()->output()->toString());
reg.def()->output()->toString().get());
if (!requirement->merge(Requirement(*reg.def()->output())))
return false;
} else if (reg.ins()->isPhi()) {
@@ -1333,9 +1336,9 @@ BacktrackingAllocator::computeRequirement(LiveBundle* bundle,
// Search uses for requirements.
for (UsePositionIterator iter = range->usesBegin(); iter; iter++) {
LUse::Policy policy = iter->use->policy();
LUse::Policy policy = iter->usePolicy();
if (policy == LUse::FIXED) {
AnyRegister required = GetFixedRegister(reg.def(), iter->use);
AnyRegister required = GetFixedRegister(reg.def(), iter->use());
JitSpew(JitSpew_RegAlloc, " Requirement %s, due to use at %u",
required.name(), iter->pos.bits());
@@ -1394,7 +1397,7 @@ BacktrackingAllocator::tryAllocateRegister(PhysicalRegister& r, LiveBundle* bund
return false;
} else {
JitSpew(JitSpew_RegAlloc, " %s collides with fixed use %s",
rAlias.reg.name(), existing->toString());
rAlias.reg.name(), existing->toString().get());
*pfixed = true;
return true;
}
@@ -1413,13 +1416,13 @@ BacktrackingAllocator::tryAllocateRegister(PhysicalRegister& r, LiveBundle* bund
if (aliasedConflicting.length() == 1) {
LiveBundle* existing = aliasedConflicting[0];
JitSpew(JitSpew_RegAlloc, " %s collides with %s [weight %lu]",
r.reg.name(), existing->toString(), computeSpillWeight(existing));
r.reg.name(), existing->toString().get(), computeSpillWeight(existing));
} else {
JitSpew(JitSpew_RegAlloc, " %s collides with the following", r.reg.name());
for (size_t i = 0; i < aliasedConflicting.length(); i++) {
LiveBundle* existing = aliasedConflicting[i];
JitSpew(JitSpew_RegAlloc, " %s [weight %lu]",
existing->toString(), computeSpillWeight(existing));
existing->toString().get(), computeSpillWeight(existing));
}
}
}
@@ -1456,7 +1459,7 @@ BacktrackingAllocator::evictBundle(LiveBundle* bundle)
{
if (JitSpewEnabled(JitSpew_RegAlloc)) {
JitSpew(JitSpew_RegAlloc, " Evicting %s [priority %lu] [weight %lu]",
bundle->toString(), computePriority(bundle), computeSpillWeight(bundle));
bundle->toString().get(), computePriority(bundle), computeSpillWeight(bundle));
}
AnyRegister reg(bundle->allocation().toRegister());
@@ -1479,9 +1482,9 @@ BacktrackingAllocator::splitAndRequeueBundles(LiveBundle* bundle,
const LiveBundleVector& newBundles)
{
if (JitSpewEnabled(JitSpew_RegAlloc)) {
JitSpew(JitSpew_RegAlloc, " splitting bundle %s into:", bundle->toString());
JitSpew(JitSpew_RegAlloc, " splitting bundle %s into:", bundle->toString().get());
for (size_t i = 0; i < newBundles.length(); i++)
JitSpew(JitSpew_RegAlloc, " %s", newBundles[i]->toString());
JitSpew(JitSpew_RegAlloc, " %s", newBundles[i]->toString().get());
}
// Remove all ranges in the old bundle from their register's list.
@@ -1722,6 +1725,9 @@ BacktrackingAllocator::resolveControlFlow()
if (mir->shouldCancel("Backtracking Resolve Control Flow (vreg loop)"))
return false;
if (!alloc().ensureBallast())
return false;
for (LiveRange::RegisterLinkIterator iter = reg.rangesBegin(); iter; ) {
LiveRange* range = LiveRange::get(*iter);
@@ -1858,11 +1864,11 @@ BacktrackingAllocator::isReusedInput(LUse* use, LNode* ins, bool considerCopy)
}
bool
BacktrackingAllocator::isRegisterUse(LUse* use, LNode* ins, bool considerCopy)
BacktrackingAllocator::isRegisterUse(UsePosition* use, LNode* ins, bool considerCopy)
{
switch (use->policy()) {
switch (use->usePolicy()) {
case LUse::ANY:
return isReusedInput(use, ins, considerCopy);
return isReusedInput(use->use(), ins, considerCopy);
case LUse::REGISTER:
case LUse::FIXED:
@@ -1917,7 +1923,7 @@ BacktrackingAllocator::reifyAllocations()
}
for (UsePositionIterator iter(range->usesBegin()); iter; iter++) {
LAllocation* alloc = iter->use;
LAllocation* alloc = iter->use();
*alloc = range->bundle()->allocation();
// For any uses which feed into MUST_REUSE_INPUT definitions,
@@ -2213,69 +2219,55 @@ BacktrackingAllocator::annotateMoveGroups()
// Debugging methods
/////////////////////////////////////////////////////////////////////
#ifdef DEBUG
#ifdef JS_JITSPEW
const char*
UniqueChars
LiveRange::toString() const
{
// Not reentrant!
static char buf[2000];
AutoEnterOOMUnsafeRegion oomUnsafe;
char* cursor = buf;
char* end = cursor + sizeof(buf);
char* buf = JS_smprintf("v%u [%u,%u)", hasVreg() ? vreg() : 0, from().bits(), to().bits());
int n = JS_snprintf(cursor, end - cursor, "v%u [%u,%u)",
hasVreg() ? vreg() : 0, from().bits(), to().bits());
if (n < 0) MOZ_CRASH();
cursor += n;
if (buf && bundle() && !bundle()->allocation().isBogus())
buf = JS_sprintf_append(buf, " %s", bundle()->allocation().toString().get());
if (bundle() && !bundle()->allocation().isBogus()) {
n = JS_snprintf(cursor, end - cursor, " %s", bundle()->allocation().toString());
if (n < 0) MOZ_CRASH();
cursor += n;
}
if (buf && hasDefinition())
buf = JS_sprintf_append(buf, " (def)");
if (hasDefinition()) {
n = JS_snprintf(cursor, end - cursor, " (def)");
if (n < 0) MOZ_CRASH();
cursor += n;
}
for (UsePositionIterator iter = usesBegin(); buf && iter; iter++)
buf = JS_sprintf_append(buf, " %s@%u", iter->use()->toString().get(), iter->pos.bits());
for (UsePositionIterator iter = usesBegin(); iter; iter++) {
n = JS_snprintf(cursor, end - cursor, " %s@%u", iter->use->toString(), iter->pos.bits());
if (n < 0) MOZ_CRASH();
cursor += n;
}
if (!buf)
oomUnsafe.crash("LiveRange::toString()");
return buf;
return UniqueChars(buf);
}
const char*
UniqueChars
LiveBundle::toString() const
{
// Not reentrant!
static char buf[2000];
AutoEnterOOMUnsafeRegion oomUnsafe;
char* cursor = buf;
char* end = cursor + sizeof(buf);
char *buf = JS_smprintf("");
for (LiveRange::BundleLinkIterator iter = rangesBegin(); iter; iter++) {
int n = JS_snprintf(cursor, end - cursor, "%s %s",
(iter == rangesBegin()) ? "" : " ##",
LiveRange::get(*iter)->toString());
if (n < 0) MOZ_CRASH();
cursor += n;
for (LiveRange::BundleLinkIterator iter = rangesBegin(); buf && iter; iter++) {
buf = JS_smprintf(buf, "%s %s",
(iter == rangesBegin()) ? "" : " ##",
LiveRange::get(*iter)->toString().get());
}
return buf;
if (!buf)
oomUnsafe.crash("LiveBundle::toString()");
return UniqueChars(buf);
}
#endif // DEBUG
#endif // JS_JITSPEW
void
BacktrackingAllocator::dumpVregs()
{
#ifdef DEBUG
#ifdef JS_JITSPEW
MOZ_ASSERT(!vregs[0u].hasRanges());
fprintf(stderr, "Live ranges by virtual register:\n");
@@ -2286,7 +2278,7 @@ BacktrackingAllocator::dumpVregs()
for (LiveRange::RegisterLinkIterator iter = reg.rangesBegin(); iter; iter++) {
if (iter != reg.rangesBegin())
fprintf(stderr, " ## ");
fprintf(stderr, "%s", LiveRange::get(*iter)->toString());
fprintf(stderr, "%s", LiveRange::get(*iter)->toString().get());
}
fprintf(stderr, "\n");
}
@@ -2303,7 +2295,7 @@ BacktrackingAllocator::dumpVregs()
for (LiveRange::BundleLinkIterator iter = bundle->rangesBegin(); iter; iter++) {
if (iter != bundle->rangesBegin())
fprintf(stderr, " ## ");
fprintf(stderr, "%s", LiveRange::get(*iter)->toString());
fprintf(stderr, "%s", LiveRange::get(*iter)->toString().get());
}
fprintf(stderr, "\n");
}
@@ -2315,12 +2307,12 @@ BacktrackingAllocator::dumpVregs()
void
BacktrackingAllocator::dumpFixedRanges()
{
#ifdef DEBUG
fprintf(stderr, "Live ranges by physical register: %s\n", callRanges->toString());
#endif // DEBUG
#ifdef JS_JITSPEW
fprintf(stderr, "Live ranges by physical register: %s\n", callRanges->toString().get());
#endif // JS_JITSPEW
}
#ifdef DEBUG
#ifdef JS_JITSPEW
struct BacktrackingAllocator::PrintLiveRange
{
bool& first_;
@@ -2333,7 +2325,7 @@ struct BacktrackingAllocator::PrintLiveRange
first_ = false;
else
fprintf(stderr, " /");
fprintf(stderr, " %s", range->toString());
fprintf(stderr, " %s", range->toString().get());
}
};
#endif
@@ -2341,7 +2333,7 @@ struct BacktrackingAllocator::PrintLiveRange
void
BacktrackingAllocator::dumpAllocations()
{
#ifdef DEBUG
#ifdef JS_JITSPEW
fprintf(stderr, "Allocations:\n");
dumpVregs();
@@ -2358,7 +2350,7 @@ BacktrackingAllocator::dumpAllocations()
}
fprintf(stderr, "\n");
#endif // DEBUG
#endif // JS_JITSPEW
}
///////////////////////////////////////////////////////////////////////////////
@@ -2395,7 +2387,7 @@ BacktrackingAllocator::minimalUse(LiveRange* range, UsePosition* use)
// Whether this is a minimal range capturing |use|.
LNode* ins = insData[use->pos];
return (range->from() == inputOf(ins)) &&
(range->to() == (use->use->usedAtStart() ? outputOf(ins) : outputOf(ins).next()));
(range->to() == (use->use()->usedAtStart() ? outputOf(ins) : outputOf(ins).next()));
}
bool
@@ -2426,9 +2418,8 @@ BacktrackingAllocator::minimalBundle(LiveBundle* bundle, bool* pfixed)
for (UsePositionIterator iter = range->usesBegin(); iter; iter++) {
if (iter != range->usesBegin())
multiple = true;
LUse* use = iter->use;
switch (use->policy()) {
switch (iter->usePolicy()) {
case LUse::FIXED:
if (fixed)
return false;
@@ -2483,9 +2474,7 @@ BacktrackingAllocator::computeSpillWeight(LiveBundle* bundle)
}
for (UsePositionIterator iter = range->usesBegin(); iter; iter++) {
LUse* use = iter->use;
switch (use->policy()) {
switch (iter->usePolicy()) {
case LUse::ANY:
usesTotal += 1000;
break;
@@ -2560,7 +2549,7 @@ BacktrackingAllocator::trySplitAcrossHotcode(LiveBundle* bundle, bool* success)
return true;
}
JitSpew(JitSpew_RegAlloc, " split across hot range %s", hotRange->toString());
JitSpew(JitSpew_RegAlloc, " split across hot range %s", hotRange->toString().get());
// Tweak the splitting method when compiling asm.js code to look at actual
// uses within the hot/cold code. This heuristic is in place as the below
@@ -2677,7 +2666,6 @@ BacktrackingAllocator::trySplitAfterLastRegisterUse(LiveBundle* bundle, LiveBund
}
for (UsePositionIterator iter(range->usesBegin()); iter; iter++) {
LUse* use = iter->use;
LNode* ins = insData[iter->pos];
// Uses in the bundle should be sorted.
@@ -2685,7 +2673,7 @@ BacktrackingAllocator::trySplitAfterLastRegisterUse(LiveBundle* bundle, LiveBund
lastUse = inputOf(ins);
if (!conflict || outputOf(ins) < conflict->firstRange()->from()) {
if (isRegisterUse(use, ins, /* considerCopy = */ true)) {
if (isRegisterUse(*iter, ins, /* considerCopy = */ true)) {
lastRegisterFrom = inputOf(ins);
lastRegisterTo = iter->pos.next();
}
@@ -2744,11 +2732,10 @@ BacktrackingAllocator::trySplitBeforeFirstRegisterUse(LiveBundle* bundle, LiveBu
LiveRange* range = LiveRange::get(*iter);
for (UsePositionIterator iter(range->usesBegin()); iter; iter++) {
LUse* use = iter->use;
LNode* ins = insData[iter->pos];
if (!conflict || outputOf(ins) >= conflictEnd) {
if (isRegisterUse(use, ins, /* considerCopy = */ true)) {
if (isRegisterUse(*iter, ins, /* considerCopy = */ true)) {
firstRegisterFrom = inputOf(ins);
break;
}
@@ -2774,7 +2761,7 @@ BacktrackingAllocator::trySplitBeforeFirstRegisterUse(LiveBundle* bundle, LiveBu
// When splitting a bundle according to a list of split positions, return
// whether a use or range at |pos| should use a different bundle than the last
// position this was called for.
// position this was called for.
static bool
UseNewBundle(const SplitPositionVector& splitPositions, CodePosition pos,
size_t* activeSplitPosition)
@@ -2913,7 +2900,7 @@ BacktrackingAllocator::splitAt(LiveBundle* bundle, const SplitPositionVector& sp
// finished must be associated with the range for that definition.
if (isRegisterDefinition(range) && use->pos <= minimalDefEnd(insData[range->from()])) {
activeRange->addUse(use);
} else if (isRegisterUse(use->use, ins)) {
} else if (isRegisterUse(use, ins)) {
// Place this register use into a different bundle from the
// last one if there are any split points between the two uses.
// UseNewBundle always returns true if we are splitting at all
@@ -2924,8 +2911,8 @@ BacktrackingAllocator::splitAt(LiveBundle* bundle, const SplitPositionVector& sp
if (UseNewBundle(splitPositions, use->pos, &activeSplitPosition) &&
(!activeRange->hasUses() ||
activeRange->usesBegin()->pos != use->pos ||
activeRange->usesBegin()->use->policy() == LUse::FIXED ||
use->use->policy() == LUse::FIXED))
activeRange->usesBegin()->usePolicy() == LUse::FIXED ||
use->usePolicy() == LUse::FIXED))
{
activeBundle = LiveBundle::New(alloc(), bundle->spillSet(), spillBundle);
if (!newBundles.append(activeBundle))
+37 -13
View File
@@ -126,11 +126,38 @@ class Requirement
struct UsePosition : public TempObject,
public InlineForwardListNode<UsePosition>
{
LUse* use;
private:
// Packed LUse* with a copy of the LUse::Policy value, in order to avoid
// making cache misses while reaching out to the policy value.
uintptr_t use_;
void setUse(LUse* use) {
// Assert that we can safely pack the LUse policy in the last 2 bits of
// the LUse pointer.
static_assert((LUse::ANY | LUse::REGISTER | LUse::FIXED | LUse::KEEPALIVE) <= 0x3,
"Cannot pack the LUse::Policy value on 32 bits architectures.");
// RECOVERED_INPUT is used by snapshots and ignored when building the
// liveness information. Thus we can safely assume that no such value
// would be seen.
MOZ_ASSERT(use->policy() != LUse::RECOVERED_INPUT);
use_ = uintptr_t(use) | (use->policy() & 0x3);
}
public:
CodePosition pos;
LUse* use() const {
return reinterpret_cast<LUse*>(use_ & ~0x3);
}
LUse::Policy usePolicy() const {
LUse::Policy policy = LUse::Policy(use_ & 0x3);
MOZ_ASSERT(use()->policy() == policy);
return policy;
}
UsePosition(LUse* use, CodePosition pos) :
use(use),
pos(pos)
{
// Verify that the usedAtStart() flag is consistent with the
@@ -140,6 +167,7 @@ struct UsePosition : public TempObject,
pos.subpos() == (use->usedAtStart()
? CodePosition::INPUT
: CodePosition::OUTPUT));
setUse(use);
}
};
@@ -316,11 +344,9 @@ class LiveRange : public TempObject
hasDefinition_ = true;
}
// Return a string describing this range. This is not re-entrant!
#ifdef DEBUG
const char* toString() const;
#else
const char* toString() const { return "???"; }
#ifdef JS_JITSPEW
// Return a string describing this range.
UniqueChars toString() const;
#endif
// Comparator for use in range splay trees.
@@ -439,11 +465,9 @@ class LiveBundle : public TempObject
return spillParent_;
}
// Return a string describing this bundle. This is not re-entrant!
#ifdef DEBUG
const char* toString() const;
#else
const char* toString() const { return "???"; }
#ifdef JS_JITSPEW
// Return a string describing this bundle.
UniqueChars toString() const;
#endif
};
@@ -672,7 +696,7 @@ class BacktrackingAllocator : protected RegisterAllocator
bool spill(LiveBundle* bundle);
bool isReusedInput(LUse* use, LNode* ins, bool considerCopy);
bool isRegisterUse(LUse* use, LNode* ins, bool considerCopy = false);
bool isRegisterUse(UsePosition* use, LNode* ins, bool considerCopy = false);
bool isRegisterDefinition(LiveRange* range);
bool pickStackSlot(SpillSet* spill);
bool insertAllRanges(LiveRangeSet& set, LiveBundle* bundle);
+46 -5
View File
@@ -3588,8 +3588,7 @@ BaselineCompiler::emit_JSOP_RETRVAL()
return emitReturn();
}
typedef bool (*ToIdFn)(JSContext*, HandleScript, jsbytecode*, HandleValue, HandleValue,
MutableHandleValue);
typedef bool (*ToIdFn)(JSContext*, HandleScript, jsbytecode*, HandleValue, MutableHandleValue);
static const VMFunction ToIdInfo = FunctionInfo<ToIdFn>(js::ToIdOperation);
bool
@@ -3605,10 +3604,7 @@ BaselineCompiler::emit_JSOP_TOID()
prepareVMCall();
masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R1);
pushArg(R0);
pushArg(R1);
pushArg(ImmPtr(pc));
pushArg(ImmGCPtr(script));
@@ -3621,6 +3617,32 @@ BaselineCompiler::emit_JSOP_TOID()
return true;
}
typedef bool (*ThrowObjectCoercibleFn)(JSContext*, HandleValue);
static const VMFunction ThrowObjectCoercibleInfo = FunctionInfo<ThrowObjectCoercibleFn>(ThrowObjectCoercible);
bool
BaselineCompiler::emit_JSOP_CHECKOBJCOERCIBLE()
{
frame.syncStack(0);
masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
Label fail, done;
masm.branchTestUndefined(Assembler::Equal, R0, &fail);
masm.branchTestNull(Assembler::NotEqual, R0, &done);
masm.bind(&fail);
prepareVMCall();
pushArg(R0);
if (!callVM(ThrowObjectCoercibleInfo))
return false;
masm.bind(&done);
return true;
}
typedef JSString* (*ToStringFn)(JSContext*, HandleValue);
static const VMFunction ToStringInfo = FunctionInfo<ToStringFn>(ToStringSlow);
@@ -4239,3 +4261,22 @@ BaselineCompiler::emit_JSOP_RESUME()
return true;
}
typedef bool (*CheckSelfHostedFn)(JSContext*, HandleValue);
static const VMFunction CheckSelfHostedInfo = FunctionInfo<CheckSelfHostedFn>(js::Debug_CheckSelfHosted);
bool
BaselineCompiler::emit_JSOP_DEBUGCHECKSELFHOSTED()
{
#ifdef DEBUG
frame.syncStack(0);
masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
prepareVMCall();
pushArg(R0);
if (!callVM(CheckSelfHostedInfo))
return false;
#endif
return true;
}
+3 -1
View File
@@ -219,7 +219,9 @@ namespace jit {
_(JSOP_INITHIDDENPROP_SETTER) \
_(JSOP_INITHIDDENELEM) \
_(JSOP_INITHIDDENELEM_GETTER) \
_(JSOP_INITHIDDENELEM_SETTER)
_(JSOP_INITHIDDENELEM_SETTER) \
_(JSOP_CHECKOBJCOERCIBLE) \
_(JSOP_DEBUGCHECKSELFHOSTED)
class BaselineCompiler : public BaselineCompilerSpecific
{
+1 -1
View File
@@ -97,7 +97,7 @@ C1Spewer::spewRanges(GenericPrinter& out, BacktrackingAllocator* regalloc, LNode
for (LiveRange::RegisterLinkIterator iter = vreg->rangesBegin(); iter; iter++) {
LiveRange* range = LiveRange::get(*iter);
out.printf("%d object \"", id);
out.printf("%s", range->bundle()->allocation().toString());
out.printf("%s", range->bundle()->allocation().toString().get());
out.printf("\" %d -1", id);
out.printf(" [%u, %u[", range->from().bits(), range->to().bits());
for (UsePositionIterator usePos(range->usesBegin()); usePos; usePos++)
+28 -2
View File
@@ -9063,7 +9063,7 @@ CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool)
masm.jump(ool->rejoin());
}
typedef bool (*ToIdFn)(JSContext*, HandleScript, jsbytecode*, HandleValue, HandleValue,
typedef bool (*ToIdFn)(JSContext*, HandleScript, jsbytecode*, HandleValue,
MutableHandleValue);
static const VMFunction ToIdInfo = FunctionInfo<ToIdFn>(ToIdOperation);
@@ -9078,7 +9078,6 @@ CodeGenerator::visitToIdV(LToIdV* lir)
OutOfLineCode* ool = oolCallVM(ToIdInfo, lir,
ArgList(ImmGCPtr(current->mir()->info().script()),
ImmPtr(lir->mir()->resumePoint()->pc()),
ToValue(lir, LToIdV::Object),
ToValue(lir, LToIdV::Index)),
StoreValueTo(out));
@@ -10371,6 +10370,33 @@ CodeGenerator::visitCheckReturn(LCheckReturn* ins)
masm.bind(&noChecks);
}
typedef bool (*ThrowObjCoercibleFn)(JSContext*, HandleValue);
static const VMFunction ThrowObjectCoercibleInfo = FunctionInfo<ThrowObjCoercibleFn>(ThrowObjectCoercible);
void
CodeGenerator::visitCheckObjCoercible(LCheckObjCoercible* ins)
{
ValueOperand checkValue = ToValue(ins, LCheckObjCoercible::CheckValue);
Label fail, done;
masm.branchTestNull(Assembler::Equal, checkValue, &fail);
masm.branchTestUndefined(Assembler::NotEqual, checkValue, &done);
masm.bind(&fail);
pushArg(checkValue);
callVM(ThrowObjectCoercibleInfo, ins);
masm.bind(&done);
}
typedef bool (*CheckSelfHostedFn)(JSContext*, HandleValue);
static const VMFunction CheckSelfHostedInfo = FunctionInfo<CheckSelfHostedFn>(js::Debug_CheckSelfHosted);
void
CodeGenerator::visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins)
{
ValueOperand checkValue = ToValue(ins, LDebugCheckSelfHosted::CheckValue);
pushArg(checkValue);
callVM(CheckSelfHostedInfo, ins);
}
void
CodeGenerator::visitRandom(LRandom* ins)
{
+2
View File
@@ -345,6 +345,8 @@ class CodeGenerator : public CodeGeneratorSpecific
void visitNewTarget(LNewTarget* ins);
void visitArrowNewTarget(LArrowNewTarget* ins);
void visitCheckReturn(LCheckReturn* ins);
void visitCheckObjCoercible(LCheckObjCoercible* ins);
void visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins);
void visitCheckOverRecursed(LCheckOverRecursed* lir);
void visitCheckOverRecursedFailure(CheckOverRecursedFailure* ool);
+9 -10
View File
@@ -3123,17 +3123,17 @@ jit::Invalidate(JSContext* cx, const RecompileInfoVector& invalid, bool resetUse
cancelOffThread);
}
bool
void
jit::IonScript::invalidate(JSContext* cx, bool resetUses, const char* reason)
{
JitSpew(JitSpew_IonInvalidate, " Invalidate IonScript %p: %s", this, reason);
// RecompileInfoVector has inline space for at least one element.
RecompileInfoVector list;
if (!list.append(recompileInfo())) {
ReportOutOfMemory(cx);
return false;
}
MOZ_RELEASE_ASSERT(list.reserve(1));
list.infallibleAppend(recompileInfo());
Invalidate(cx, list, resetUses, true);
return true;
}
bool
@@ -3162,12 +3162,11 @@ jit::Invalidate(JSContext* cx, JSScript* script, bool resetUses, bool cancelOffT
js_free(buf);
}
// RecompileInfoVector has inline space for at least one element.
RecompileInfoVector scripts;
MOZ_ASSERT(script->hasIonScript());
if (!scripts.append(script->ionScript()->recompileInfo())) {
ReportOutOfMemory(cx);
return false;
}
MOZ_RELEASE_ASSERT(scripts.reserve(1));
scripts.infallibleAppend(script->ionScript()->recompileInfo());
Invalidate(cx, scripts, resetUses, cancelOffThread);
return true;
+69 -12
View File
@@ -2106,6 +2106,22 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_NEWTARGET:
return jsop_newtarget();
case JSOP_CHECKOBJCOERCIBLE:
return jsop_checkobjcoercible();
case JSOP_DEBUGCHECKSELFHOSTED:
{
#ifdef DEBUG
MDebugCheckSelfHosted* check = MDebugCheckSelfHosted::New(alloc(), current->pop());
current->add(check);
current->push(check);
if (!resumeAfter(check))
return false;
#endif
return true;
}
#ifdef DEBUG
case JSOP_PUSHBLOCKSCOPE:
case JSOP_FRESHENBLOCKSCOPE:
@@ -8157,11 +8173,9 @@ IonBuilder::getStaticName(JSObject* staticObject, PropertyName* name, bool* psuc
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticKey,
name, types, /* updateObserved = */ true);
JSObject* singleton = types->maybeSingleton();
MIRType knownType = types->getKnownMIRType();
if (barrier == BarrierKind::NoBarrier) {
// Try to inline properties holding a known constant object.
JSObject* singleton = types->maybeSingleton();
if (singleton) {
if (testSingletonProperty(staticObject, id) == singleton)
return pushConstant(ObjectValue(*singleton));
@@ -8171,8 +8185,23 @@ IonBuilder::getStaticName(JSObject* staticObject, PropertyName* name, bool* psuc
Value constantValue;
if (property.constant(constraints(), &constantValue))
return pushConstant(constantValue);
}
if (!loadStaticSlot(staticObject, barrier, types, property.maybeTypes()->definiteSlot())) {
*psucceeded = false;
return false;
}
return true;
}
bool
IonBuilder::loadStaticSlot(JSObject* staticObject, BarrierKind barrier, TemporaryTypeSet* types,
uint32_t slot)
{
if (barrier == BarrierKind::NoBarrier) {
// Try to inline properties that can only have one value.
MIRType knownType = types->getKnownMIRType();
if (knownType == MIRType_Undefined)
return pushConstant(UndefinedValue());
if (knownType == MIRType_Null)
@@ -8185,13 +8214,7 @@ IonBuilder::getStaticName(JSObject* staticObject, PropertyName* name, bool* psuc
if (barrier != BarrierKind::NoBarrier)
rvalType = MIRType_Value;
if (!loadSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject),
rvalType, barrier, types)) {
*psucceeded = false;
return false;
}
return true;
return loadSlot(obj, slot, NumFixedSlots(staticObject), rvalType, barrier, types);
}
// Whether a write of the given value may need a post-write barrier for GC purposes.
@@ -8395,7 +8418,16 @@ IonBuilder::jsop_getimport(PropertyName* name)
if (!getStaticName(targetEnv, localName, &emitted))
return false;
MOZ_ASSERT(emitted);
if (!emitted) {
// This can happen if we don't have type information.
TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(targetEnv);
TemporaryTypeSet* types = bytecodeTypes(pc);
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticKey,
name, types, /* updateObserved = */ true);
if (!loadStaticSlot(targetEnv, barrier, types, shape->slot()))
return false;
}
// In the rare case where this import hasn't been initialized already (we
// have an import cycle where modules reference each other's imports), emit
@@ -10310,6 +10342,31 @@ IonBuilder::jsop_rest()
return true;
}
bool
IonBuilder::jsop_checkobjcoercible()
{
MDefinition* toCheck = current->peek(-1);
if (!toCheck->mightBeType(MIRType_Undefined) &&
!toCheck->mightBeType(MIRType_Null))
{
toCheck->setImplicitlyUsedUnchecked();
return true;
}
MOZ_ASSERT(toCheck->type() == MIRType_Value ||
toCheck->type() == MIRType_Null ||
toCheck->type() == MIRType_Undefined);
// If we want to squeeze more perf here, we can throw without checking,
// if IsNullOrUndefined(toCheck->type()). Since this is a failure case,
// it should be OK.
MCheckObjCoercible* check = MCheckObjCoercible::New(alloc(), current->pop());
current->add(check);
current->push(check);
return resumeAfter(check);
}
uint32_t
IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed)
{
@@ -13027,7 +13084,7 @@ IonBuilder::jsop_toid()
return true;
MDefinition* index = current->pop();
MToId* ins = MToId::New(alloc(), current->peek(-1), index);
MToId* ins = MToId::New(alloc(), index);
current->add(ins);
current->push(ins);
+3
View File
@@ -683,6 +683,8 @@ class IonBuilder
bool jsop_compare(JSOp op, MDefinition* left, MDefinition* right);
bool getStaticName(JSObject* staticObject, PropertyName* name, bool* psucceeded,
MDefinition* lexicalCheck = nullptr);
bool loadStaticSlot(JSObject* staticObject, BarrierKind barrier, TemporaryTypeSet* types,
uint32_t slot);
bool setStaticName(JSObject* staticObject, PropertyName* name);
bool jsop_getgname(PropertyName* name);
bool jsop_getname(PropertyName* name);
@@ -738,6 +740,7 @@ class IonBuilder
bool jsop_setaliasedvar(ScopeCoordinate sc);
bool jsop_debugger();
bool jsop_newtarget();
bool jsop_checkobjcoercible();
/* Inlining. */
+1 -1
View File
@@ -533,7 +533,7 @@ struct IonScript
}
// Invalidate the current compilation.
bool invalidate(JSContext* cx, bool resetUses, const char* reason);
void invalidate(JSContext* cx, bool resetUses, const char* reason);
size_t invalidationCount() const {
return invalidationCount_;
+1 -1
View File
@@ -371,7 +371,7 @@ JSONSpewer::spewRanges(BacktrackingAllocator* regalloc)
beginObject();
property("allocation");
out_.printf("\"%s\"", range->bundle()->allocation().toString());
out_.printf("\"%s\"", range->bundle()->allocation().toString().get());
integerProperty("start", range->from().bits());
integerProperty("end", range->to().bits());
endObject();
+2 -5
View File
@@ -2195,11 +2195,8 @@ SnapshotIterator::initInstructionResults(MaybeReadFallback& fallback)
// same reason, we need to recompile without optimizing away the
// observable stack slots. The script would later be recompiled to have
// support for Argument objects.
if (fallback.consequence == MaybeReadFallback::Fallback_Invalidate &&
!ionScript_->invalidate(cx, /* resetUses = */ false, "Observe recovered instruction."))
{
return false;
}
if (fallback.consequence == MaybeReadFallback::Fallback_Invalidate)
ionScript_->invalidate(cx, /* resetUses = */ false, "Observe recovered instruction.");
// Register the list of result on the activation. We need to do that
// before we initialize the list such as if any recover instruction
+66 -70
View File
@@ -358,109 +358,107 @@ static const char * const TypeChars[] =
#endif
};
static void
PrintDefinition(char* buf, size_t size, const LDefinition& def)
{
char* cursor = buf;
char* end = buf + size;
cursor += JS_snprintf(cursor, end - cursor, "v%u", def.virtualRegister());
cursor += JS_snprintf(cursor, end - cursor, "<%s>", TypeChars[def.type()]);
if (def.policy() == LDefinition::FIXED)
cursor += JS_snprintf(cursor, end - cursor, ":%s", def.output()->toString());
else if (def.policy() == LDefinition::MUST_REUSE_INPUT)
cursor += JS_snprintf(cursor, end - cursor, ":tied(%u)", def.getReusedInput());
}
const char*
UniqueChars
LDefinition::toString() const
{
// Not reentrant!
static char buf[40];
AutoEnterOOMUnsafeRegion oomUnsafe;
if (isBogusTemp())
return "bogus";
char* buf;
if (isBogusTemp()) {
buf = JS_smprintf("bogus");
} else {
buf = JS_smprintf("v%u<%s>", virtualRegister(), TypeChars[type()]);
if (buf) {
if (policy() == LDefinition::FIXED)
buf = JS_sprintf_append(buf, ":%s", output()->toString().get());
else if (policy() == LDefinition::MUST_REUSE_INPUT)
buf = JS_sprintf_append(buf, ":tied(%u)", getReusedInput());
}
}
PrintDefinition(buf, sizeof(buf), *this);
return buf;
if (!buf)
oomUnsafe.crash("LDefinition::toString()");
return UniqueChars(buf);
}
static void
PrintUse(char* buf, size_t size, const LUse* use)
static char*
PrintUse(const LUse* use)
{
switch (use->policy()) {
case LUse::REGISTER:
JS_snprintf(buf, size, "v%d:r", use->virtualRegister());
break;
return JS_smprintf("v%d:r", use->virtualRegister());
case LUse::FIXED:
JS_snprintf(buf, size, "v%d:%s", use->virtualRegister(),
AnyRegister::FromCode(use->registerCode()).name());
break;
return JS_smprintf("v%d:%s", use->virtualRegister(),
AnyRegister::FromCode(use->registerCode()).name());
case LUse::ANY:
JS_snprintf(buf, size, "v%d:r?", use->virtualRegister());
break;
return JS_smprintf("v%d:r?", use->virtualRegister());
case LUse::KEEPALIVE:
JS_snprintf(buf, size, "v%d:*", use->virtualRegister());
break;
return JS_smprintf("v%d:*", use->virtualRegister());
case LUse::RECOVERED_INPUT:
JS_snprintf(buf, size, "v%d:**", use->virtualRegister());
break;
return JS_smprintf("v%d:**", use->virtualRegister());
default:
MOZ_CRASH("invalid use policy");
}
}
const char*
UniqueChars
LAllocation::toString() const
{
// Not reentrant!
static char buf[40];
AutoEnterOOMUnsafeRegion oomUnsafe;
if (isBogus())
return "bogus";
switch (kind()) {
case LAllocation::CONSTANT_VALUE:
case LAllocation::CONSTANT_INDEX:
return "c";
case LAllocation::GPR:
JS_snprintf(buf, sizeof(buf), "%s", toGeneralReg()->reg().name());
return buf;
case LAllocation::FPU:
JS_snprintf(buf, sizeof(buf), "%s", toFloatReg()->reg().name());
return buf;
case LAllocation::STACK_SLOT:
JS_snprintf(buf, sizeof(buf), "stack:%d", toStackSlot()->slot());
return buf;
case LAllocation::ARGUMENT_SLOT:
JS_snprintf(buf, sizeof(buf), "arg:%d", toArgument()->index());
return buf;
case LAllocation::USE:
PrintUse(buf, sizeof(buf), toUse());
return buf;
default:
MOZ_CRASH("what?");
char* buf;
if (isBogus()) {
buf = JS_smprintf("bogus");
} else {
switch (kind()) {
case LAllocation::CONSTANT_VALUE:
case LAllocation::CONSTANT_INDEX:
buf = JS_smprintf("c");
break;
case LAllocation::GPR:
buf = JS_smprintf("%s", toGeneralReg()->reg().name());
break;
case LAllocation::FPU:
buf = JS_smprintf("%s", toFloatReg()->reg().name());
break;
case LAllocation::STACK_SLOT:
buf = JS_smprintf("stack:%d", toStackSlot()->slot());
break;
case LAllocation::ARGUMENT_SLOT:
buf = JS_smprintf("arg:%d", toArgument()->index());
break;
case LAllocation::USE:
buf = PrintUse(toUse());
break;
default:
MOZ_CRASH("what?");
}
}
if (!buf)
oomUnsafe.crash("LAllocation::toString()");
return UniqueChars(buf);
}
void
LAllocation::dump() const
{
fprintf(stderr, "%s\n", toString());
fprintf(stderr, "%s\n", toString().get());
}
void
LDefinition::dump() const
{
fprintf(stderr, "%s\n", toString());
fprintf(stderr, "%s\n", toString().get());
}
void
LNode::printOperands(GenericPrinter& out)
{
for (size_t i = 0, e = numOperands(); i < e; i++) {
out.printf(" (%s)", getOperand(i)->toString());
out.printf(" (%s)", getOperand(i)->toString().get());
if (i != numOperands() - 1)
out.printf(",");
}
@@ -490,7 +488,7 @@ LNode::dump(GenericPrinter& out)
if (numDefs() != 0) {
out.printf("{");
for (size_t i = 0; i < numDefs(); i++) {
out.printf("%s", getDef(i)->toString());
out.printf("%s", getDef(i)->toString().get());
if (i != numDefs() - 1)
out.printf(", ");
}
@@ -503,7 +501,7 @@ LNode::dump(GenericPrinter& out)
if (numTemps()) {
out.printf(" t=(");
for (size_t i = 0; i < numTemps(); i++) {
out.printf("%s", getTemp(i)->toString());
out.printf("%s", getTemp(i)->toString().get());
if (i != numTemps() - 1)
out.printf(", ");
}
@@ -599,9 +597,7 @@ LMoveGroup::printOperands(GenericPrinter& out)
{
for (size_t i = 0; i < numMoves(); i++) {
const LMove& move = getMove(i);
// Use two printfs, as LAllocation::toString is not reentrant.
out.printf(" [%s", move.from().toString());
out.printf(" -> %s", move.to().toString());
out.printf(" [%s -> %s", move.from().toString().get(), move.to().toString().get());
#ifdef DEBUG
out.printf(", %s", TypeChars[move.type()]);
#endif
+2 -2
View File
@@ -183,7 +183,7 @@ class LAllocation : public TempObject
return bits_;
}
const char* toString() const;
UniqueChars toString() const;
bool aliases(const LAllocation& other) const;
void dump() const;
@@ -577,7 +577,7 @@ class LDefinition
}
}
const char* toString() const;
UniqueChars toString() const;
void dump() const;
};
+27 -2
View File
@@ -1097,8 +1097,7 @@ void
LIRGenerator::visitToId(MToId* ins)
{
LToIdV* lir = new(alloc()) LToIdV(tempDouble());
useBox(lir, LToIdV::Object, ins->lhs());
useBox(lir, LToIdV::Index, ins->rhs());
useBox(lir, LToIdV::Index, ins->input());
defineBox(lir, ins);
assignSafepoint(lir, ins);
}
@@ -4331,6 +4330,32 @@ LIRGenerator::visitCheckReturn(MCheckReturn* ins)
redefine(ins, retVal);
}
void
LIRGenerator::visitCheckObjCoercible(MCheckObjCoercible* ins)
{
MDefinition* checkVal = ins->checkValue();
MOZ_ASSERT(checkVal->type() == MIRType_Value);
LCheckObjCoercible* lir = new(alloc()) LCheckObjCoercible();
useBoxAtStart(lir, LCheckObjCoercible::CheckValue, checkVal);
redefine(ins, checkVal);
add(lir, ins);
assignSafepoint(lir, ins);
}
void
LIRGenerator::visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins)
{
MDefinition* checkVal = ins->checkValue();
MOZ_ASSERT(checkVal->type() == MIRType_Value);
LDebugCheckSelfHosted* lir = new (alloc()) LDebugCheckSelfHosted();
useBoxAtStart(lir, LDebugCheckSelfHosted::CheckValue, checkVal);
redefine(ins, checkVal);
add(lir, ins);
assignSafepoint(lir, ins);
}
static void
SpewResumePoint(MBasicBlock* block, MInstruction* ins, MResumePoint* resumePoint)
{
+2
View File
@@ -310,6 +310,8 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitAtomicIsLockFree(MAtomicIsLockFree* ins);
void visitGuardSharedTypedArray(MGuardSharedTypedArray* ins);
void visitCheckReturn(MCheckReturn* ins);
void visitCheckObjCoercible(MCheckObjCoercible* ins);
void visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins);
};
} // namespace jit
+1 -1
View File
@@ -2967,7 +2967,7 @@ IonBuilder::atomicsMeetsPreconditions(CallInfo& callInfo, Scalar::Type* arrayTyp
// be.
return checkResult == DontCheckAtomicResult || getInlineReturnType() == MIRType_Double;
default:
// Excludes floating types and Uint8Clamped
// Excludes floating types and Uint8Clamped.
return false;
}
}
+47 -4
View File
@@ -3993,7 +3993,6 @@ OperandIndexMap::init(TempAllocator& alloc, JSObject* templateObject)
const UnboxedLayout& layout =
templateObject->as<UnboxedPlainObject>().layoutDontCheckGeneration();
// 0 is used as an error code.
const UnboxedLayout::PropertyVector& properties = layout.properties();
MOZ_ASSERT(properties.length() < 255);
@@ -4068,8 +4067,54 @@ MObjectState::init(TempAllocator& alloc, MDefinition* obj)
return true;
}
bool
MObjectState::initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal)
{
JSObject* templateObject = templateObjectOf(object());
// Initialize all the slots of the object state with the value contained in
// the template object. This is needed to account values which are baked in
// the template objects and not visible in IonMonkey, such as the
// uninitialized-lexical magic value of call objects.
if (templateObject->is<UnboxedPlainObject>()) {
UnboxedPlainObject& unboxedObject = templateObject->as<UnboxedPlainObject>();
const UnboxedLayout& layout = unboxedObject.layoutDontCheckGeneration();
const UnboxedLayout::PropertyVector& properties = layout.properties();
for (size_t i = 0; i < properties.length(); i++) {
Value val = unboxedObject.getValue(properties[i], /* maybeUninitialized = */ true);
MDefinition *def = undefinedVal;
if (!val.isUndefined()) {
MConstant* ins = val.isObject() ?
MConstant::NewConstraintlessObject(alloc, &val.toObject()) :
MConstant::New(alloc, val);
block()->insertBefore(this, ins);
def = ins;
}
initSlot(i, def);
}
} else {
NativeObject& nativeObject = templateObject->as<NativeObject>();
MOZ_ASSERT(nativeObject.slotSpan() == numSlots());
for (size_t i = 0; i < numSlots(); i++) {
Value val = nativeObject.getSlot(i);
MDefinition *def = undefinedVal;
if (!val.isUndefined()) {
MConstant* ins = val.isObject() ?
MConstant::NewConstraintlessObject(alloc, &val.toObject()) :
MConstant::New(alloc, val);
block()->insertBefore(this, ins);
def = ins;
}
initSlot(i, def);
}
}
return true;
}
MObjectState*
MObjectState::New(TempAllocator& alloc, MDefinition* obj, MDefinition* undefinedVal)
MObjectState::New(TempAllocator& alloc, MDefinition* obj)
{
JSObject* templateObject = templateObjectOf(obj);
MOZ_ASSERT(templateObject, "Unexpected object creation.");
@@ -4084,8 +4129,6 @@ MObjectState::New(TempAllocator& alloc, MDefinition* obj, MDefinition* undefined
MObjectState* res = new(alloc) MObjectState(templateObject, operandIndex);
if (!res || !res->init(alloc, obj))
return nullptr;
for (size_t i = 0; i < res->numSlots(); i++)
res->initSlot(i, undefinedVal);
return res;
}
+59 -12
View File
@@ -3399,9 +3399,13 @@ class MObjectState
// on bailout.
static JSObject* templateObjectOf(MDefinition* obj);
static MObjectState* New(TempAllocator& alloc, MDefinition* obj, MDefinition* undefinedVal);
static MObjectState* New(TempAllocator& alloc, MDefinition* obj);
static MObjectState* Copy(TempAllocator& alloc, MObjectState* state);
// As we might do read of uninitialized properties, we have to copy the
// initial values from the template object.
bool initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal);
MDefinition* object() const {
return getOperand(0);
}
@@ -5372,11 +5376,11 @@ class MTypeOf
};
class MToId
: public MBinaryInstruction,
: public MUnaryInstruction,
public BoxInputsPolicy::Data
{
MToId(MDefinition* object, MDefinition* index)
: MBinaryInstruction(object, index)
explicit MToId(MDefinition* index)
: MUnaryInstruction(index)
{
setResultType(MIRType_Value);
}
@@ -5384,8 +5388,8 @@ class MToId
public:
INSTRUCTION_HEADER(ToId)
static MToId* New(TempAllocator& alloc, MDefinition* object, MDefinition* index) {
return new(alloc) MToId(object, index);
static MToId* New(TempAllocator& alloc, MDefinition* index) {
return new(alloc) MToId(index);
}
};
@@ -13223,8 +13227,7 @@ class MCompareExchangeTypedArrayElement
}
bool isByteArray() const {
return (arrayType_ == Scalar::Int8 ||
arrayType_ == Scalar::Uint8 ||
arrayType_ == Scalar::Uint8Clamped);
arrayType_ == Scalar::Uint8);
}
MDefinition* elements() {
return getOperand(0);
@@ -13278,8 +13281,7 @@ class MAtomicExchangeTypedArrayElement
bool isByteArray() const {
return (arrayType_ == Scalar::Int8 ||
arrayType_ == Scalar::Uint8 ||
arrayType_ == Scalar::Uint8Clamped);
arrayType_ == Scalar::Uint8);
}
MDefinition* elements() {
return getOperand(0);
@@ -13330,8 +13332,7 @@ class MAtomicTypedArrayElementBinop
bool isByteArray() const {
return (arrayType_ == Scalar::Int8 ||
arrayType_ == Scalar::Uint8 ||
arrayType_ == Scalar::Uint8Clamped);
arrayType_ == Scalar::Uint8);
}
AtomicOp operation() const {
return op_;
@@ -13363,6 +13364,52 @@ class MDebugger : public MNullaryInstruction
}
};
class MCheckObjCoercible
: public MUnaryInstruction,
public BoxInputsPolicy::Data
{
explicit MCheckObjCoercible(MDefinition* toCheck)
: MUnaryInstruction(toCheck)
{
setGuard();
setResultType(MIRType_Value);
setResultTypeSet(toCheck->resultTypeSet());
}
public:
INSTRUCTION_HEADER(CheckObjCoercible)
static MCheckObjCoercible* New(TempAllocator& alloc, MDefinition* toCheck) {
return new(alloc) MCheckObjCoercible(toCheck);
}
MDefinition* checkValue() {
return getOperand(0);
}
};
class MDebugCheckSelfHosted
: public MUnaryInstruction,
public BoxInputsPolicy::Data
{
explicit MDebugCheckSelfHosted(MDefinition* toCheck)
: MUnaryInstruction(toCheck)
{
setGuard();
setResultType(MIRType_Value);
setResultTypeSet(toCheck->resultTypeSet());
}
public:
INSTRUCTION_HEADER(DebugCheckSelfHosted)
static MDebugCheckSelfHosted* New(TempAllocator& alloc, MDefinition* toCheck) {
return new(alloc) MDebugCheckSelfHosted(toCheck);
}
MDefinition* checkValue() {
return getOperand(0);
}
};
class MAsmJSNeg
: public MUnaryInstruction,
public NoTypePolicy::Data
+3 -1
View File
@@ -279,7 +279,9 @@ namespace jit {
_(Debugger) \
_(NewTarget) \
_(ArrowNewTarget) \
_(CheckReturn)
_(CheckReturn) \
_(CheckObjCoercible) \
_(DebugCheckSelfHosted)
// Forward declarations of MIR types.
#define FORWARD_DECLARE(op) class M##op;
+22 -22
View File
@@ -291,7 +291,7 @@ AllocationIntegrityState::checkSafepointAllocation(LInstruction* ins,
case LDefinition::OBJECT:
if (populateSafepoints) {
JitSpew(JitSpew_RegAlloc, "Safepoint object v%u i%u %s",
vreg, ins->id(), alloc.toString());
vreg, ins->id(), alloc.toString().get());
if (!safepoint->addGcPointer(alloc))
return false;
}
@@ -300,7 +300,7 @@ AllocationIntegrityState::checkSafepointAllocation(LInstruction* ins,
case LDefinition::SLOTS:
if (populateSafepoints) {
JitSpew(JitSpew_RegAlloc, "Safepoint slots v%u i%u %s",
vreg, ins->id(), alloc.toString());
vreg, ins->id(), alloc.toString().get());
if (!safepoint->addSlotsOrElementsPointer(alloc))
return false;
}
@@ -314,7 +314,7 @@ AllocationIntegrityState::checkSafepointAllocation(LInstruction* ins,
case LDefinition::TYPE:
if (populateSafepoints) {
JitSpew(JitSpew_RegAlloc, "Safepoint type v%u i%u %s",
vreg, ins->id(), alloc.toString());
vreg, ins->id(), alloc.toString().get());
if (!safepoint->addNunboxType(vreg, alloc))
return false;
}
@@ -322,7 +322,7 @@ AllocationIntegrityState::checkSafepointAllocation(LInstruction* ins,
case LDefinition::PAYLOAD:
if (populateSafepoints) {
JitSpew(JitSpew_RegAlloc, "Safepoint payload v%u i%u %s",
vreg, ins->id(), alloc.toString());
vreg, ins->id(), alloc.toString().get());
if (!safepoint->addNunboxPayload(vreg, alloc))
return false;
}
@@ -332,7 +332,7 @@ AllocationIntegrityState::checkSafepointAllocation(LInstruction* ins,
case LDefinition::BOX:
if (populateSafepoints) {
JitSpew(JitSpew_RegAlloc, "Safepoint boxed value v%u i%u %s",
vreg, ins->id(), alloc.toString());
vreg, ins->id(), alloc.toString().get());
if (!safepoint->addBoxedValue(alloc))
return false;
}
@@ -391,9 +391,9 @@ AllocationIntegrityState::dump()
fprintf(stderr, "[%u,%u Phi] [def %s] ",
input.bits(),
output.bits(),
phi->getDef(0)->toString());
phi->getDef(0)->toString().get());
for (size_t j = 0; j < phi->numOperands(); j++)
fprintf(stderr, " [use %s]", info.inputs[j].toString());
fprintf(stderr, " [use %s]", info.inputs[j].toString().get());
fprintf(stderr, "\n");
}
@@ -412,29 +412,29 @@ AllocationIntegrityState::dump()
if (ins->isMoveGroup()) {
LMoveGroup* group = ins->toMoveGroup();
for (int i = group->numMoves() - 1; i >= 0; i--) {
// Use two printfs, as LAllocation::toString is not reentrant.
fprintf(stderr, " [%s", group->getMove(i).from().toString());
fprintf(stderr, " -> %s]", group->getMove(i).to().toString());
fprintf(stderr, " [%s -> %s]",
group->getMove(i).from().toString().get(),
group->getMove(i).to().toString().get());
}
fprintf(stderr, "\n");
continue;
}
for (size_t i = 0; i < ins->numDefs(); i++)
fprintf(stderr, " [def %s]", ins->getDef(i)->toString());
fprintf(stderr, " [def %s]", ins->getDef(i)->toString().get());
for (size_t i = 0; i < ins->numTemps(); i++) {
LDefinition* temp = ins->getTemp(i);
if (!temp->isBogusTemp())
fprintf(stderr, " [temp v%u %s]", info.temps[i].virtualRegister(),
temp->toString());
temp->toString().get());
}
size_t index = 0;
for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
fprintf(stderr, " [use %s", info.inputs[index++].toString());
fprintf(stderr, " [use %s", info.inputs[index++].toString().get());
if (!alloc->isConstant())
fprintf(stderr, " %s", alloc->toString());
fprintf(stderr, " %s", alloc->toString().get());
fprintf(stderr, "]");
}
@@ -462,7 +462,7 @@ AllocationIntegrityState::dump()
for (size_t i = 0; i < seenOrdered.length(); i++) {
IntegrityItem item = seenOrdered[i];
fprintf(stderr, " block %u reg v%u alloc %s\n",
item.block->mir()->id(), item.vreg, item.alloc.toString());
item.block->mir()->id(), item.vreg, item.alloc.toString().get());
}
}
@@ -538,9 +538,9 @@ RegisterAllocator::dumpInstructions()
fprintf(stderr, "[%u,%u Phi] [def %s]",
inputOf(phi).bits(),
outputOf(phi).bits(),
phi->getDef(0)->toString());
phi->getDef(0)->toString().get());
for (size_t j = 0; j < phi->numOperands(); j++)
fprintf(stderr, " [use %s]", phi->getOperand(j)->toString());
fprintf(stderr, " [use %s]", phi->getOperand(j)->toString().get());
fprintf(stderr, "\n");
}
@@ -556,25 +556,25 @@ RegisterAllocator::dumpInstructions()
LMoveGroup* group = ins->toMoveGroup();
for (int i = group->numMoves() - 1; i >= 0; i--) {
// Use two printfs, as LAllocation::toString is not reentant.
fprintf(stderr, " [%s", group->getMove(i).from().toString());
fprintf(stderr, " -> %s]", group->getMove(i).to().toString());
fprintf(stderr, " [%s", group->getMove(i).from().toString().get());
fprintf(stderr, " -> %s]", group->getMove(i).to().toString().get());
}
fprintf(stderr, "\n");
continue;
}
for (size_t i = 0; i < ins->numDefs(); i++)
fprintf(stderr, " [def %s]", ins->getDef(i)->toString());
fprintf(stderr, " [def %s]", ins->getDef(i)->toString().get());
for (size_t i = 0; i < ins->numTemps(); i++) {
LDefinition* temp = ins->getTemp(i);
if (!temp->isBogusTemp())
fprintf(stderr, " [temp %s]", temp->toString());
fprintf(stderr, " [temp %s]", temp->toString().get());
}
for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
if (!alloc->isBogus())
fprintf(stderr, " [use %s]", alloc->toString());
fprintf(stderr, " [use %s]", alloc->toString().get());
}
fprintf(stderr, "\n");
+5 -1
View File
@@ -349,12 +349,16 @@ ObjectMemoryView::initStartingState(BlockState** pState)
startBlock_->insertBefore(obj_, undefinedVal_);
// Create a new block state and insert at it at the location of the new object.
BlockState* state = BlockState::New(alloc_, obj_, undefinedVal_);
BlockState* state = BlockState::New(alloc_, obj_);
if (!state)
return false;
startBlock_->insertAfter(obj_, state);
// Initialize the properties of the object state.
if (!state->initFromTemplateObject(alloc_, undefinedVal_))
return false;
// Hold out of resume point until it is visited.
state->setInWorklist();
+9
View File
@@ -1311,6 +1311,15 @@ BaselineThrowUninitializedThis(JSContext* cx, BaselineFrame* frame)
return ThrowUninitializedThis(cx, frame);
}
bool
ThrowObjectCoercible(JSContext* cx, HandleValue v)
{
MOZ_ASSERT(v.isUndefined() || v.isNull());
MOZ_ALWAYS_FALSE(ToObjectSlow(cx, v, false));
return false;
}
bool
BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue res)
{
+2
View File
@@ -738,6 +738,8 @@ bool ThrowRuntimeLexicalError(JSContext* cx, unsigned errorNumber);
bool BaselineThrowUninitializedThis(JSContext* cx, BaselineFrame* frame);
bool ThrowBadDerivedReturn(JSContext* cx, HandleValue v);
bool ThrowObjectCoercible(JSContext* cx, HandleValue v);
bool BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue res);
} // namespace jit
-2
View File
@@ -1742,7 +1742,6 @@ CodeGeneratorARM::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType
MOZ_ASSERT(flagTemp != InvalidReg);
MOZ_ASSERT_IF(arrayType == Scalar::Uint32, outTemp != InvalidReg);
// Uint8Clamped is explicitly not supported here
switch (arrayType) {
case Scalar::Int8:
switch (op) {
@@ -1908,7 +1907,6 @@ CodeGeneratorARM::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType
{
MOZ_ASSERT(flagTemp != InvalidReg);
// Uint8Clamped is explicitly not supported here
switch (arrayType) {
case Scalar::Int8:
case Scalar::Uint8:
-6
View File
@@ -4828,9 +4828,6 @@ MacroAssemblerARMCompat::compareExchangeToTypedIntArray(Scalar::Type arrayType,
case Scalar::Uint8:
compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
break;
case Scalar::Uint8Clamped:
compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
break;
case Scalar::Int16:
compareExchange16SignExtend(mem, oldval, newval, output.gpr());
break;
@@ -4873,9 +4870,6 @@ MacroAssemblerARMCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType, c
case Scalar::Uint8:
atomicExchange8ZeroExtend(mem, value, output.gpr());
break;
case Scalar::Uint8Clamped:
atomicExchange8ZeroExtend(mem, value, output.gpr());
break;
case Scalar::Int16:
atomicExchange16SignExtend(mem, value, output.gpr());
break;
@@ -273,9 +273,6 @@ MacroAssemblerCompat::compareExchangeToTypedIntArray(Scalar::Type arrayType, con
case Scalar::Uint8:
compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
break;
case Scalar::Uint8Clamped:
compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
break;
case Scalar::Int16:
compareExchange16SignExtend(mem, oldval, newval, output.gpr());
break;
@@ -318,9 +315,6 @@ MacroAssemblerCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType, cons
case Scalar::Uint8:
atomicExchange8ZeroExtend(mem, value, output.gpr());
break;
case Scalar::Uint8Clamped:
atomicExchange8ZeroExtend(mem, value, output.gpr());
break;
case Scalar::Int16:
atomicExchange16SignExtend(mem, value, output.gpr());
break;
@@ -2042,7 +2042,6 @@ CodeGeneratorMIPSShared::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type ar
MOZ_ASSERT(flagTemp != InvalidReg);
MOZ_ASSERT_IF(arrayType == Scalar::Uint32, outTemp != InvalidReg);
// Uint8Clamped is explicitly not supported here
switch (arrayType) {
case Scalar::Int8:
switch (op) {
@@ -2213,7 +2212,6 @@ CodeGeneratorMIPSShared::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type ar
{
MOZ_ASSERT(flagTemp != InvalidReg);
// Uint8Clamped is explicitly not supported here
switch (arrayType) {
case Scalar::Int8:
case Scalar::Uint8:
@@ -2434,9 +2434,6 @@ MacroAssemblerMIPSCompat::compareExchangeToTypedIntArray(Scalar::Type arrayType,
case Scalar::Uint8:
compareExchange8ZeroExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
break;
case Scalar::Uint8Clamped:
compareExchange8ZeroExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
break;
case Scalar::Int16:
compareExchange16SignExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
break;
@@ -2483,9 +2480,6 @@ MacroAssemblerMIPSCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType,
case Scalar::Uint8:
atomicExchange8ZeroExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
break;
case Scalar::Uint8Clamped:
atomicExchange8ZeroExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
break;
case Scalar::Int16:
atomicExchange16SignExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
break;
@@ -2537,9 +2537,6 @@ MacroAssemblerMIPS64Compat::compareExchangeToTypedIntArray(Scalar::Type arrayTyp
case Scalar::Uint8:
compareExchange8ZeroExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
break;
case Scalar::Uint8Clamped:
compareExchange8ZeroExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
break;
case Scalar::Int16:
compareExchange16SignExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
break;
@@ -2586,9 +2583,6 @@ MacroAssemblerMIPS64Compat::atomicExchangeToTypedIntArray(Scalar::Type arrayType
case Scalar::Uint8:
atomicExchange8ZeroExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
break;
case Scalar::Uint8Clamped:
atomicExchange8ZeroExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
break;
case Scalar::Int16:
atomicExchange16SignExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
break;
+18 -3
View File
@@ -1276,7 +1276,7 @@ class LTypeOfV : public LInstructionHelper<1, BOX_PIECES, 1>
}
};
class LToIdV : public LInstructionHelper<BOX_PIECES, 2 * BOX_PIECES, 1>
class LToIdV : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 1>
{
public:
LIR_HEADER(ToIdV)
@@ -1286,8 +1286,7 @@ class LToIdV : public LInstructionHelper<BOX_PIECES, 2 * BOX_PIECES, 1>
setTemp(0, temp);
}
static const size_t Object = 0;
static const size_t Index = BOX_PIECES;
static const size_t Index = 0;
MToId* mir() const {
return mir_->toToId();
@@ -7397,6 +7396,22 @@ class LCheckReturn : public LCallInstructionHelper<BOX_PIECES, 2 * BOX_PIECES, 0
LIR_HEADER(CheckReturn)
};
class LCheckObjCoercible : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
{
public:
static const size_t CheckValue = 0;
LIR_HEADER(CheckObjCoercible)
};
class LDebugCheckSelfHosted : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
{
public:
static const size_t CheckValue = 0;
LIR_HEADER(DebugCheckSelfHosted)
};
} // namespace jit
} // namespace js
+3 -1
View File
@@ -367,6 +367,8 @@
_(Debugger) \
_(NewTarget) \
_(ArrowNewTarget) \
_(CheckReturn)
_(CheckReturn) \
_(CheckObjCoercible) \
_(DebugCheckSelfHosted)
#endif /* jit_shared_LOpcodes_shared_h */
@@ -3366,7 +3366,6 @@ void
CodeGeneratorX86Shared::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value,
const T& mem, Register temp1, Register temp2, AnyRegister output)
{
// Uint8Clamped is explicitly not supported here
switch (arrayType) {
case Scalar::Int8:
switch (op) {
@@ -3526,7 +3525,6 @@ void
CodeGeneratorX86Shared::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value,
const T& mem)
{
// Uint8Clamped is explicitly not supported here
switch (arrayType) {
case Scalar::Int8:
case Scalar::Uint8:
@@ -156,9 +156,6 @@ MacroAssemblerX86Shared::compareExchangeToTypedIntArray(Scalar::Type arrayType,
case Scalar::Uint8:
compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
break;
case Scalar::Uint8Clamped:
compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
break;
case Scalar::Int16:
compareExchange16SignExtend(mem, oldval, newval, output.gpr());
break;
@@ -201,9 +198,6 @@ MacroAssemblerX86Shared::atomicExchangeToTypedIntArray(Scalar::Type arrayType, c
case Scalar::Uint8:
atomicExchange8ZeroExtend(mem, value, output.gpr());
break;
case Scalar::Uint8Clamped:
atomicExchange8ZeroExtend(mem, value, output.gpr());
break;
case Scalar::Int16:
atomicExchange16SignExtend(mem, value, output.gpr());
break;
+10
View File
@@ -107,6 +107,7 @@ MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL, 1, JSEXN_TYPEERR, "{0}.prototype is not a
MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|")
MSG_DEF(JSMSG_UNINITIALIZED_THIS, 1, JSEXN_REFERENCEERR, "|this| used uninitialized in {0} class constructor")
MSG_DEF(JSMSG_BAD_DERIVED_RETURN, 1, JSEXN_TYPEERR, "derived class constructor returned invalid value {0}")
MSG_DEF(JSMSG_NOT_SELFHOSTED, 1, JSEXN_TYPEERR, "callFunction() used on non self-hosted builtin {0}")
// JSON
MSG_DEF(JSMSG_JSON_BAD_PARSE, 3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data")
@@ -446,14 +447,23 @@ MSG_DEF(JSMSG_INVALID_TIME_ZONE, 1, JSEXN_RANGEERR, "invalid time zone in
MSG_DEF(JSMSG_UNDEFINED_CURRENCY, 0, JSEXN_TYPEERR, "undefined currency in NumberFormat() with currency style")
// RegExp
MSG_DEF(JSMSG_BACK_REF_OUT_OF_RANGE, 0, JSEXN_SYNTAXERR, "back reference out of range in regular expression")
MSG_DEF(JSMSG_BAD_CLASS_RANGE, 0, JSEXN_SYNTAXERR, "invalid range in character class")
MSG_DEF(JSMSG_DEPRECATED_REGEXP_MULTILINE, 0, JSEXN_SYNTAXERR, "RegExp.multiline is deprecated. Use m flag instead")
MSG_DEF(JSMSG_ESCAPE_AT_END_OF_REGEXP, 0, JSEXN_SYNTAXERR, "\\ at end of pattern")
MSG_DEF(JSMSG_INVALID_DECIMAL_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid decimal escape in regular expression")
MSG_DEF(JSMSG_INVALID_GROUP, 0, JSEXN_SYNTAXERR, "invalid regexp group")
MSG_DEF(JSMSG_INVALID_IDENTITY_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid identity escape in regular expression")
MSG_DEF(JSMSG_INVALID_UNICODE_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid unicode escape in regular expression")
MSG_DEF(JSMSG_MISSING_PAREN, 0, JSEXN_SYNTAXERR, "unterminated parenthetical")
MSG_DEF(JSMSG_NEWREGEXP_FLAGGED, 0, JSEXN_TYPEERR, "can't supply flags when constructing one RegExp from another")
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_TOO_MANY_PARENS, 0, JSEXN_INTERNALERR, "too many parentheses in regular expression")
MSG_DEF(JSMSG_UNICODE_OVERFLOW, 0, JSEXN_SYNTAXERR, "unicode codepoint should not be greater than 0x10FFFF in regular expression")
MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression")
MSG_DEF(JSMSG_UNTERM_CLASS, 0, JSEXN_SYNTAXERR, "unterminated character class")
+2 -101
View File
@@ -3109,7 +3109,7 @@ PropertySpecNameToSymbolCode(const char* name)
return JS::SymbolCode(u - 1);
}
static bool
bool
PropertySpecNameToId(JSContext* cx, const char* name, MutableHandleId id,
js::PinningBehavior pin = js::DoNotPinAtom)
{
@@ -3626,87 +3626,6 @@ JS_IsConstructor(JSFunction* fun)
return fun->isConstructor();
}
static bool
GenericNativeMethodDispatcher(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
const JSFunctionSpec* fs = (JSFunctionSpec*)
args.callee().as<JSFunction>().getExtendedSlot(0).toPrivate();
MOZ_ASSERT((fs->flags & JSFUN_GENERIC_NATIVE) != 0);
if (argc < 1) {
ReportMissingArg(cx, args.calleev(), 0);
return false;
}
/*
* Copy all actual (argc) arguments down over our |this| parameter, vp[1],
* which is almost always the class constructor object, e.g. Array. Then
* call the corresponding prototype native method with our first argument
* passed as |this|.
*/
memmove(vp + 1, vp + 2, argc * sizeof(Value));
/* Clear the last parameter in case too few arguments were passed. */
vp[2 + --argc].setUndefined();
return fs->call.op(cx, argc, vp);
}
static bool
DefineFunctionFromSpec(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs, unsigned flags)
{
GetterOp gop;
SetterOp sop;
if (flags & JSFUN_STUB_GSOPS) {
// JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
// the defined property's attributes.
flags &= ~JSFUN_STUB_GSOPS;
gop = nullptr;
sop = nullptr;
} else {
gop = obj->getClass()->getProperty;
sop = obj->getClass()->setProperty;
MOZ_ASSERT(gop != JS_PropertyStub);
MOZ_ASSERT(sop != JS_StrictPropertyStub);
}
RootedId id(cx);
if (!PropertySpecNameToId(cx, fs->name, &id))
return false;
// Define a generic arity N+1 static method for the arity N prototype
// method if flags contains JSFUN_GENERIC_NATIVE.
if (flags & JSFUN_GENERIC_NATIVE) {
// We require that any consumers using JSFUN_GENERIC_NATIVE stash
// the prototype and constructor in the global slots before invoking
// JS_DefineFunctions on the proto.
JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
MOZ_ASSERT(obj == &obj->global().getPrototype(key).toObject());
RootedObject ctor(cx, &obj->global().getConstructor(key).toObject());
flags &= ~JSFUN_GENERIC_NATIVE;
JSFunction* fun = DefineFunction(cx, ctor, id,
GenericNativeMethodDispatcher,
fs->nargs + 1, flags,
gc::AllocKind::FUNCTION_EXTENDED);
if (!fun)
return false;
// As jsapi.h notes, fs must point to storage that lives as long
// as fun->object lives.
fun->setExtendedSlot(0, PrivateValue(const_cast<JSFunctionSpec*>(fs)));
}
JSFunction* fun = NewFunctionFromSpec(cx, fs, id);
if (!fun)
return false;
RootedValue funVal(cx, ObjectValue(*fun));
return DefineProperty(cx, obj, id, funVal, gop, sop, flags & ~JSFUN_FLAGS_MASK);
}
JS_PUBLIC_API(bool)
JS_DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs,
PropertyDefinitionBehavior behavior)
@@ -3716,25 +3635,7 @@ JS_DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs,
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj);
for (; fs->name; fs++) {
unsigned flags = fs->flags;
switch (behavior) {
case DefineAllProperties:
break;
case OnlyDefineLateProperties:
if (!(flags & JSPROP_DEFINE_LATE))
continue;
break;
default:
MOZ_ASSERT(behavior == DontDefineLateProperties);
if (flags & JSPROP_DEFINE_LATE)
continue;
}
if (!DefineFunctionFromSpec(cx, obj, fs, flags & ~JSPROP_DEFINE_LATE))
return false;
}
return true;
return DefineFunctions(cx, obj, fs, NotIntrinsic, behavior);
}
JS_PUBLIC_API(JSFunction*)
+1
View File
@@ -4954,6 +4954,7 @@ JS_ObjectIsDate(JSContext* cx, JS::HandleObject obj, bool* isDate);
#define JSREG_GLOB 0x02u /* global exec, creates array of matches */
#define JSREG_MULTILINE 0x04u /* treat ^ and $ as begin and end of line */
#define JSREG_STICKY 0x08u /* only match starting at lastIndex */
#define JSREG_UNICODE 0x10u /* unicode */
extern JS_PUBLIC_API(JSObject*)
JS_NewRegExpObject(JSContext* cx, JS::HandleObject obj, const char* bytes, size_t length,
+1
View File
@@ -50,6 +50,7 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
marked(true),
warnedAboutFlagsArgument(false),
warnedAboutExprClosure(false),
warnedAboutRegExpMultiline(false),
addonId(options.addonIdOrNull()),
#ifdef DEBUG
firedOnNewGlobalObject(false),
+1
View File
@@ -278,6 +278,7 @@ struct JSCompartment
bool marked;
bool warnedAboutFlagsArgument;
bool warnedAboutExprClosure;
bool warnedAboutRegExpMultiline;
// A null add-on ID means that the compartment is not associated with an
// add-on.
+10 -1
View File
@@ -187,7 +187,6 @@ class JSFunction : public js::NativeObject
bool isExprBody() const { return flags() & EXPR_BODY; }
bool hasGuessedAtom() const { return flags() & HAS_GUESSED_ATOM; }
bool isLambda() const { return flags() & LAMBDA; }
bool isSelfHostedBuiltin() const { return flags() & SELF_HOSTED; }
bool hasRest() const { return flags() & HAS_REST; }
bool isInterpretedLazy() const { return flags() & INTERPRETED_LAZY; }
bool hasScript() const { return flags() & INTERPRETED; }
@@ -209,6 +208,10 @@ class JSFunction : public js::NativeObject
bool hasResolvedLength() const { return flags() & RESOLVED_LENGTH; }
bool hasResolvedName() const { return flags() & RESOLVED_NAME; }
bool isSelfHostedOrIntrinsic() const { return flags() & SELF_HOSTED; }
bool isSelfHostedBuiltin() const { return isSelfHostedOrIntrinsic() && !isNative(); }
bool isIntrinsic() const { return isSelfHostedOrIntrinsic() && isNative(); }
bool hasJITCode() const {
if (!hasScript())
return false;
@@ -263,11 +266,17 @@ class JSFunction : public js::NativeObject
}
void setIsSelfHostedBuiltin() {
MOZ_ASSERT(isInterpreted());
MOZ_ASSERT(!isSelfHostedBuiltin());
flags_ |= SELF_HOSTED;
// Self-hosted functions should not be constructable.
flags_ &= ~CONSTRUCTOR;
}
void setIsIntrinsic() {
MOZ_ASSERT(isNative());
MOZ_ASSERT(!isIntrinsic());
flags_ |= SELF_HOSTED;
}
void setIsFunctionPrototype() {
MOZ_ASSERT(!isFunctionPrototype());
+6 -2
View File
@@ -348,8 +348,7 @@ static const AllocKind IncrementalPhaseStrings[] = {
};
static const AllocKind IncrementalPhaseScripts[] = {
AllocKind::SCRIPT,
AllocKind::LAZY_SCRIPT
AllocKind::SCRIPT
};
static const AllocKind IncrementalPhaseJitCode[] = {
@@ -377,6 +376,10 @@ static const AllocKind BackgroundPhaseObjects[] = {
AllocKind::OBJECT16_BACKGROUND
};
static const AllocKind BackgroundPhaseScripts[] = {
AllocKind::LAZY_SCRIPT
};
static const AllocKind BackgroundPhaseStringsAndSymbols[] = {
AllocKind::FAT_INLINE_STRING,
AllocKind::STRING,
@@ -391,6 +394,7 @@ static const AllocKind BackgroundPhaseShapes[] = {
};
static const FinalizePhase BackgroundFinalizePhases[] = {
PHASE(BackgroundPhaseScripts, gcstats::PHASE_SWEEP_SCRIPT),
PHASE(BackgroundPhaseObjects, gcstats::PHASE_SWEEP_OBJECT),
PHASE(BackgroundPhaseStringsAndSymbols, gcstats::PHASE_SWEEP_STRING),
PHASE(BackgroundPhaseShapes, gcstats::PHASE_SWEEP_SHAPE)
+1 -1
View File
@@ -167,7 +167,7 @@ IsBackgroundFinalized(AllocKind kind)
false, /* AllocKind::OBJECT16 */
true, /* AllocKind::OBJECT16_BACKGROUND */
false, /* AllocKind::SCRIPT */
false, /* AllocKind::LAZY_SCRIPT */
true, /* AllocKind::LAZY_SCRIPT */
true, /* AllocKind::SHAPE */
true, /* AllocKind::ACCESSOR_SHAPE */
true, /* AllocKind::BASE_SHAPE */
+112
View File
@@ -2870,6 +2870,118 @@ js::HasDataProperty(JSContext* cx, NativeObject* obj, jsid id, Value* vp)
return false;
}
static bool
GenericNativeMethodDispatcher(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
const JSFunctionSpec* fs = (JSFunctionSpec*)
args.callee().as<JSFunction>().getExtendedSlot(0).toPrivate();
MOZ_ASSERT((fs->flags & JSFUN_GENERIC_NATIVE) != 0);
if (argc < 1) {
ReportMissingArg(cx, args.calleev(), 0);
return false;
}
/*
* Copy all actual (argc) arguments down over our |this| parameter, vp[1],
* which is almost always the class constructor object, e.g. Array. Then
* call the corresponding prototype native method with our first argument
* passed as |this|.
*/
memmove(vp + 1, vp + 2, argc * sizeof(Value));
/* Clear the last parameter in case too few arguments were passed. */
vp[2 + --argc].setUndefined();
return fs->call.op(cx, argc, vp);
}
extern bool
PropertySpecNameToId(JSContext* cx, const char* name, MutableHandleId id,
js::PinningBehavior pin = js::DoNotPinAtom);
static bool
DefineFunctionFromSpec(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs, unsigned flags,
DefineAsIntrinsic intrinsic)
{
GetterOp gop;
SetterOp sop;
if (flags & JSFUN_STUB_GSOPS) {
// JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
// the defined property's attributes.
flags &= ~JSFUN_STUB_GSOPS;
gop = nullptr;
sop = nullptr;
} else {
gop = obj->getClass()->getProperty;
sop = obj->getClass()->setProperty;
MOZ_ASSERT(gop != JS_PropertyStub);
MOZ_ASSERT(sop != JS_StrictPropertyStub);
}
RootedId id(cx);
if (!PropertySpecNameToId(cx, fs->name, &id))
return false;
// Define a generic arity N+1 static method for the arity N prototype
// method if flags contains JSFUN_GENERIC_NATIVE.
if (flags & JSFUN_GENERIC_NATIVE) {
// We require that any consumers using JSFUN_GENERIC_NATIVE stash
// the prototype and constructor in the global slots before invoking
// JS_DefineFunctions on the proto.
JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
MOZ_ASSERT(obj == &obj->global().getPrototype(key).toObject());
RootedObject ctor(cx, &obj->global().getConstructor(key).toObject());
flags &= ~JSFUN_GENERIC_NATIVE;
JSFunction* fun = DefineFunction(cx, ctor, id,
GenericNativeMethodDispatcher,
fs->nargs + 1, flags,
gc::AllocKind::FUNCTION_EXTENDED);
if (!fun)
return false;
fun->setExtendedSlot(0, PrivateValue(const_cast<JSFunctionSpec*>(fs)));
}
JSFunction* fun = NewFunctionFromSpec(cx, fs, id);
if (!fun)
return false;
if (intrinsic == AsIntrinsic)
fun->setIsIntrinsic();
RootedValue funVal(cx, ObjectValue(*fun));
return DefineProperty(cx, obj, id, funVal, gop, sop, flags & ~JSFUN_FLAGS_MASK);
}
bool
js::DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs,
DefineAsIntrinsic intrinsic, PropertyDefinitionBehavior behavior)
{
for (; fs->name; fs++) {
unsigned flags = fs->flags;
switch (behavior) {
case DefineAllProperties:
break;
case OnlyDefineLateProperties:
if (!(flags & JSPROP_DEFINE_LATE))
continue;
break;
default:
MOZ_ASSERT(behavior == DontDefineLateProperties);
if (flags & JSPROP_DEFINE_LATE)
continue;
}
if (!DefineFunctionFromSpec(cx, obj, fs, flags & ~JSPROP_DEFINE_LATE, intrinsic))
return false;
}
return true;
}
/*** ToPrimitive *************************************************************/
+14
View File
@@ -982,6 +982,20 @@ LookupProperty(JSContext* cx, HandleObject obj, PropertyName* name,
extern bool
HasOwnProperty(JSContext* cx, HandleObject obj, HandleId id, bool* result);
/**
* This enum is used to select whether the defined functions should be marked as
* builtin native instrinsics for self-hosted code.
*/
enum DefineAsIntrinsic {
NotIntrinsic,
AsIntrinsic
};
extern bool
DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs,
DefineAsIntrinsic intrinsic,
PropertyDefinitionBehavior behavior = DefineAllProperties);
/*
* Set a watchpoint: a synchronous callback when the given property of the
* given object is set.
+2 -2
View File
@@ -996,8 +996,8 @@ JS_snprintf(char* out, uint32_t outlen, const char* fmt, ...)
va_list ap;
int rv;
MOZ_ASSERT(int32_t(outlen) > 0);
if (int32_t(outlen) <= 0)
MOZ_ASSERT(outlen > 0);
if (outlen == 0)
return 0;
va_start(ap, fmt);
+5 -2
View File
@@ -25,14 +25,17 @@
** %g - float
*/
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/SizePrintfMacros.h"
#include <stdarg.h>
#include "jstypes.h"
/*
** sprintf into a fixed size buffer. Guarantees that a NUL is at the end
** of the buffer. Returns the length of the written output, NOT including
** the NUL, or (uint32_t)-1 if an error occurs.
** of the buffer. The return value is the length of the written output,
** NOT including the NUL, which is guaranteed less than "outlen" on success.
*/
extern JS_PUBLIC_API(uint32_t) JS_snprintf(char* out, uint32_t outlen, const char* fmt, ...);
+1 -2
View File
@@ -3962,8 +3962,7 @@ JSScript::traceChildren(JSTracer* trc)
void
LazyScript::finalize(FreeOp* fop)
{
if (table_)
fop->free_(table_);
fop->free_(table_);
}
size_t
+52 -8
View File
@@ -2271,6 +2271,41 @@ DoMatchLocal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLine
return true;
}
/* ES6 21.2.5.2.3. */
static size_t
AdvanceStringIndex(HandleLinearString input, size_t length, size_t index, bool unicode)
{
/* Steps 1-3 (implicit). */
/* Step 4: If input is latin1, there is no surrogate pair. */
if (!unicode || input->hasLatin1Chars())
return index + 1;
JS::AutoCheckCannotGC nogc;
const char16_t* S = input->twoByteChars(nogc);
/* Step 6. */
if (index + 1 >= length)
return index + 1;
/* Step 7. */
char16_t first = S[index];
/* Step 8. */
if (!unicode::IsLeadSurrogate(first))
return index + 1;
/* Step 9. */
char16_t second = S[index + 1];
/* Step 10. */
if (!unicode::IsTrailSurrogate(second))
return index + 1;
/* Step 11. */
return index + 2;
}
/* ES5 15.5.4.10 step 8. */
static bool
DoMatchGlobal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLinearString input,
@@ -2326,6 +2361,7 @@ DoMatchGlobal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLin
ScopedMatchPairs matches(&cx->tempLifoAlloc());
size_t charsLen = input->length();
RegExpShared& re = g.regExp();
bool unicode = re.unicode();
for (size_t searchIndex = 0; searchIndex <= charsLen; ) {
if (!CheckForInterrupt(cx))
return false;
@@ -2343,7 +2379,9 @@ DoMatchGlobal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLin
MatchPair& match = matches[0];
// Steps 8f(iii)(1-3).
searchIndex = match.isEmpty() ? match.limit + 1 : match.limit;
searchIndex = match.isEmpty()
? AdvanceStringIndex(input, charsLen, match.limit, unicode)
: match.limit;
// Step 8f(iii)(4-5).
JSLinearString* str = NewDependentString(cx, input, match.start, match.length());
@@ -2610,6 +2648,7 @@ static bool
DoMatchForReplaceGlobal(JSContext* cx, RegExpStatics* res, HandleLinearString linearStr,
RegExpShared& re, ReplaceData& rdata, size_t* rightContextOffset)
{
bool unicode = re.unicode();
size_t charsLen = linearStr->length();
ScopedMatchPairs matches(&cx->tempLifoAlloc());
for (size_t count = 0, searchIndex = 0; searchIndex <= charsLen; ++count) {
@@ -2624,7 +2663,9 @@ DoMatchForReplaceGlobal(JSContext* cx, RegExpStatics* res, HandleLinearString li
break;
MatchPair& match = matches[0];
searchIndex = match.isEmpty() ? match.limit + 1 : match.limit;
searchIndex = match.isEmpty()
? AdvanceStringIndex(linearStr, charsLen, match.limit, unicode)
: match.limit;
*rightContextOffset = match.limit;
if (!res->updateFromMatchPairs(cx, linearStr, matches))
@@ -3224,6 +3265,7 @@ StrReplaceRegexpRemove(JSContext* cx, HandleString str, RegExpShared& re)
size_t lazyIndex = 0; /* Index before last successful match. */
/* Accumulate StringRanges for unmatched substrings. */
bool unicode = re.unicode();
while (startIndex <= charsLen) {
if (!CheckForInterrupt(cx))
return nullptr;
@@ -3244,7 +3286,9 @@ StrReplaceRegexpRemove(JSContext* cx, HandleString str, RegExpShared& re)
lazyIndex = lastIndex;
lastIndex = match.limit;
startIndex = match.isEmpty() ? match.limit + 1 : match.limit;
startIndex = match.isEmpty()
? AdvanceStringIndex(linearStr, charsLen, match.limit, unicode)
: match.limit;
/* Non-global removal executes at most once. */
if (!re.global())
@@ -3627,7 +3671,7 @@ class SplitMatchResult {
template<class Matcher>
static JSObject*
SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher& splitMatch,
HandleObjectGroup group)
HandleObjectGroup group, bool unicode)
{
size_t strLength = str->length();
SplitMatchResult result;
@@ -3692,7 +3736,7 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher
/* Step 13(c)(ii). */
if (endIndex == lastEndIndex) {
index++;
index = AdvanceStringIndex(str, strLength, index, unicode);
continue;
}
@@ -3921,14 +3965,14 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp)
aobj = CharSplitHelper(cx, linearStr, limit, group);
} else {
SplitStringMatcher matcher(cx, sepstr);
aobj = SplitHelper(cx, linearStr, limit, matcher, group);
aobj = SplitHelper(cx, linearStr, limit, matcher, group, false);
}
} else {
RegExpStatics* res = cx->global()->getRegExpStatics(cx);
if (!res)
return false;
SplitRegExpMatcher matcher(*re, res);
aobj = SplitHelper(cx, linearStr, limit, matcher, group);
aobj = SplitHelper(cx, linearStr, limit, matcher, group, re->unicode());
}
if (!aobj)
return false;
@@ -3956,7 +4000,7 @@ js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, H
return CharSplitHelper(cx, linearStr, limit, group);
SplitStringMatcher matcher(cx, linearSep);
return SplitHelper(cx, linearStr, limit, matcher, group);
return SplitHelper(cx, linearStr, limit, matcher, group, false);
}
/*
@@ -0,0 +1,31 @@
var test = `
// #1
function base() { }
base.prototype = {
test() {
--super[1];
}
}
var d = new base();
d.test();
// #2
class test2 {
test() {
super[1]++;
}
}
var d = new test2();
d.test()
`;
if (classesEnabled())
eval(test);
if (typeof reportCompare === 'function')
reportCompare(0,0,"OK");
+2 -5
View File
@@ -8,8 +8,9 @@ var getters = [
"global",
"ignoreCase",
"multiline",
"source",
"sticky",
//"unicode",
"unicode",
];
for (var name of getters) {
@@ -20,9 +21,5 @@ for (var name of getters) {
assertEq("get" in desc, true);
}
// When the /u flag is supported, remove this comment and the next line, and
// uncomment "unicode" in |props| above.
assertThrowsInstanceOf(() => RegExp("", "mygui").flags, SyntaxError);
if (typeof reportCompare === "function")
reportCompare(true, true);
+2 -5
View File
@@ -8,17 +8,14 @@ var props = [
"ignoreCase",
"multiline",
"sticky",
//"unicode",
"unicode",
];
testThrows(RegExp.prototype);
test(/foo/iymg, [true, true, true, true, false]);
test(RegExp(""), [false, false, false, false, false]);
test(RegExp("", "mygi"), [true, true, true, true, false]);
// When the /u flag is supported, remove the following line, uncomment the
// next line, and uncomment "unicode" in |props| above.
assertThrowsInstanceOf(() => RegExp("", "mygui").flags, SyntaxError);
// test(RegExp("", "mygiu"), [true, true, true, true, true]);
test(RegExp("", "mygiu"), [true, true, true, true, true]);
testThrowsGeneric();
testThrowsGeneric(1);
+3 -7
View File
@@ -7,16 +7,12 @@ assertEq(RegExp.prototype.flags, "");
assertEq(/foo/iymg.flags, "gimy");
assertEq(RegExp("").flags, "");
assertEq(RegExp("", "mygi").flags, "gimy");
// TODO: Uncomment lines 12, 16, 19 and remove lines 11, 15, 18 when bug 1135377 is fixed.
assertThrowsInstanceOf(() => RegExp("", "mygui").flags, SyntaxError);
// assertEq(RegExp("", "mygui").flags, "gimuy");
assertEq(RegExp("", "mygui").flags, "gimuy");
assertEq(genericFlags({}), "");
assertEq(genericFlags({ignoreCase: true}), "i");
assertEq(genericFlags({sticky:1, unicode:1, global: 0}), "y");
// assertEq(genericFlags({sticky:1, unicode:1, global: 0}), "uy");
assertEq(genericFlags({sticky:1, unicode:1, global: 0}), "uy");
assertEq(genericFlags({__proto__: {multiline: true}}), "m");
assertEq(genericFlags(new Proxy({}, {get(){return true}})), "gimy");
// assertEq(genericFlags(new Proxy({}, {get(){return true}})), "gimuy");
assertEq(genericFlags(new Proxy({}, {get(){return true}})), "gimuy");
assertThrowsInstanceOf(() => genericFlags(), TypeError);
assertThrowsInstanceOf(() => genericFlags(1), TypeError);
@@ -0,0 +1,39 @@
var BUGNUMBER = 1135377;
var summary = "Implement RegExp unicode flag -- back reference should not match lead surrogate that has corresponding trail surrogate.";
print(BUGNUMBER + ": " + summary);
// The last character of back reference is not a surrogate.
assertEqArray(/foo(.+)bar\1/u.exec("fooAbarA\uDC00"),
["fooAbarA", "A"]);
assertEqArray(/foo(.+)bar\1/u.exec("fooAbarA\uD834"),
["fooAbarA", "A"]);
assertEqArray(/foo(.+)bar\1/u.exec("fooAbarAA"),
["fooAbarA", "A"]);
assertEqArray(/foo(.+)bar\1/u.exec("fooAbarA"),
["fooAbarA", "A"]);
// The last character of back reference is a lead surrogate.
assertEq(/foo(.+)bar\1/u.exec("foo\uD834bar\uD834\uDC00"), null);
assertEqArray(/foo(.+)bar\1/u.exec("foo\uD834bar\uD834\uD834"),
["foo\uD834bar\uD834", "\uD834"]);
assertEqArray(/foo(.+)bar\1/u.exec("foo\uD834bar\uD834A"),
["foo\uD834bar\uD834", "\uD834"]);
assertEqArray(/foo(.+)bar\1/u.exec("foo\uD834bar\uD834"),
["foo\uD834bar\uD834", "\uD834"]);
// The last character of back reference is a trail surrogate.
assertEqArray(/foo(.+)bar\1/u.exec("foo\uDC00bar\uDC00\uDC00"),
["foo\uDC00bar\uDC00", "\uDC00"]);
assertEqArray(/foo(.+)bar\1/u.exec("foo\uDC00bar\uDC00\uD834"),
["foo\uDC00bar\uDC00", "\uDC00"]);
assertEqArray(/foo(.+)bar\1/u.exec("foo\uDC00bar\uDC00A"),
["foo\uDC00bar\uDC00", "\uDC00"]);
assertEqArray(/foo(.+)bar\1/u.exec("foo\uDC00bar\uDC00"),
["foo\uDC00bar\uDC00", "\uDC00"]);
// Pattern should not match to surrogate pair partially.
assertEq(/^(.+)\1$/u.exec("\uDC00foobar\uD834\uDC00foobar\uD834"), null);
if (typeof reportCompare === "function")
reportCompare(true, true);
@@ -0,0 +1,166 @@
var BUGNUMBER = 1135377;
var summary = "Implement RegExp unicode flag -- braced pattern in RegExpUnicodeEscapeSequence.";
print(BUGNUMBER + ": " + summary);
// ==== standalone ====
assertEqArray(/\u{41}/u.exec("ABC"),
["A"]);
assertEqArray(/\u{41}/.exec("ABC" + "u".repeat(41)),
["u".repeat(41)]);
assertEqArray(/\u{4A}/u.exec("JKL"),
["J"]);
assertEqArray(/\u{4A}/.exec("JKLu{4A}"),
["u{4A}"]);
assertEqArray(/\u{1F438}/u.exec("\u{1F438}"),
["\u{1F438}"]);
assertEqArray(/\u{1F438}/.exec("u{1F438}"),
["u{1F438}"]);
assertEqArray(/\u{0}/u.exec("\u{0}"),
["\u{0}"]);
assertEqArray(/\u{10FFFF}/u.exec("\u{10FFFF}"),
["\u{10FFFF}"]);
assertEqArray(/\u{10ffff}/u.exec("\u{10FFFF}"),
["\u{10FFFF}"]);
// leading 0
assertEqArray(/\u{0000000000000000000000}/u.exec("\u{0}"),
["\u{0}"]);
assertEqArray(/\u{000000000000000010FFFF}/u.exec("\u{10FFFF}"),
["\u{10FFFF}"]);
// RegExp constructor
assertEqArray(new RegExp("\\u{0}", "u").exec("\u{0}"),
["\u{0}"]);
assertEqArray(new RegExp("\\u{41}", "u").exec("ABC"),
["A"]);
assertEqArray(new RegExp("\\u{1F438}", "u").exec("\u{1F438}"),
["\u{1F438}"]);
assertEqArray(new RegExp("\\u{10FFFF}", "u").exec("\u{10FFFF}"),
["\u{10FFFF}"]);
assertEqArray(new RegExp("\\u{0000000000000000}", "u").exec("\u{0}"),
["\u{0}"]);
assertEqArray(eval(`/\\u{${"0".repeat(Math.pow(2, 24)) + "1234"}}/u`).exec("\u{1234}"),
["\u{1234}"]);
assertEqArray(new RegExp(`\\u{${"0".repeat(Math.pow(2, 24)) + "1234"}}`, "u").exec("\u{1234}"),
["\u{1234}"]);
// ==== ? ====
assertEqArray(/\u{1F438}?/u.exec("\u{1F438}"),
["\u{1F438}"]);
assertEqArray(/\u{1F438}?/u.exec(""),
[""]);
// lead-only target
assertEqArray(/\u{1F438}?/u.exec("\uD83D"),
[""]);
// RegExp constructor
assertEqArray(new RegExp("\\u{1F438}?", "u").exec("\u{1F438}"),
["\u{1F438}"]);
assertEqArray(new RegExp("\\u{1F438}?", "u").exec(""),
[""]);
assertEqArray(new RegExp("\\u{1F438}?", "u").exec("\uD83D"),
[""]);
// ==== + ====
assertEqArray(/\u{1F438}+/u.exec("\u{1F438}"),
["\u{1F438}"]);
assertEqArray(/\u{1F438}+/u.exec("\u{1F438}\u{1F438}"),
["\u{1F438}\u{1F438}"]);
assertEq(/\u{1F438}+/u.exec(""),
null);
// lead-only target
assertEq(/\u{1F438}+/u.exec("\uD83D"),
null);
assertEqArray(/\u{1F438}+/u.exec("\uD83D\uDC38\uDC38"),
["\uD83D\uDC38"]);
// ==== * ====
assertEqArray(/\u{1F438}*/u.exec("\u{1F438}"),
["\u{1F438}"]);
assertEqArray(/\u{1F438}*/u.exec("\u{1F438}\u{1F438}"),
["\u{1F438}\u{1F438}"]);
assertEqArray(/\u{1F438}*/u.exec(""),
[""]);
// lead-only target
assertEqArray(/\u{1F438}*/u.exec("\uD83D"),
[""]);
assertEqArray(/\u{1F438}*/u.exec("\uD83D\uDC38\uDC38"),
["\uD83D\uDC38"]);
// ==== lead-only ====
// match only non-surrogate pair
assertEqArray(/\u{D83D}/u.exec("\uD83D\uDBFF"),
["\uD83D"]);
assertEq(/\u{D83D}/u.exec("\uD83D\uDC00"),
null);
assertEq(/\u{D83D}/u.exec("\uD83D\uDFFF"),
null);
assertEqArray(/\u{D83D}/u.exec("\uD83D\uE000"),
["\uD83D"]);
// match before non-tail char
assertEqArray(/\u{D83D}/u.exec("\uD83D"),
["\uD83D"]);
assertEqArray(/\u{D83D}/u.exec("\uD83DA"),
["\uD83D"]);
// ==== trail-only ====
// match only non-surrogate pair
assertEqArray(/\u{DC38}/u.exec("\uD7FF\uDC38"),
["\uDC38"]);
assertEq(/\u{DC38}/u.exec("\uD800\uDC38"),
null);
assertEq(/\u{DC38}/u.exec("\uDBFF\uDC38"),
null);
assertEqArray(/\u{DC38}/u.exec("\uDC00\uDC38"),
["\uDC38"]);
// match after non-lead char
assertEqArray(/\u{DC38}/u.exec("\uDC38"),
["\uDC38"]);
assertEqArray(/\u{DC38}/u.exec("A\uDC38"),
["\uDC38"]);
// ==== wrong patterns ====
assertThrowsInstanceOf(() => eval(`/\\u{-1}/u`), SyntaxError);
assertThrowsInstanceOf(() => eval(`/\\u{0.0}/u`), SyntaxError);
assertThrowsInstanceOf(() => eval(`/\\u{G}/u`), SyntaxError);
assertThrowsInstanceOf(() => eval(`/\\u{}/u`), SyntaxError);
assertThrowsInstanceOf(() => eval(`/\\u{{/u`), SyntaxError);
assertThrowsInstanceOf(() => eval(`/\\u{/u`), SyntaxError);
assertThrowsInstanceOf(() => eval(`/\\u{110000}/u`), SyntaxError);
assertThrowsInstanceOf(() => eval(`/\\u{00110000}/u`), SyntaxError);
assertThrowsInstanceOf(() => eval(`/\\u{100000000000000000000000000000}/u`), SyntaxError);
assertThrowsInstanceOf(() => eval(`/\\u{FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF}/u`), SyntaxError);
assertThrowsInstanceOf(() => eval(`/\\u{ FFFF}/u`), SyntaxError);
assertThrowsInstanceOf(() => eval(`/\\u{FFFF }/u`), SyntaxError);
assertThrowsInstanceOf(() => eval(`/\\u{FF FF}/u`), SyntaxError);
assertThrowsInstanceOf(() => eval(`/\\u{F F F F}/u`), SyntaxError);
assertThrowsInstanceOf(() => eval(`/\\u{100000001}/u`), SyntaxError);
// surrogate pair with braced
assertEq(/\u{D83D}\u{DC38}+/u.exec("\uD83D\uDC38\uDC38"),
null);
assertEq(/\uD83D\u{DC38}+/u.exec("\uD83D\uDC38\uDC38"),
null);
assertEq(/\u{D83D}\uDC38+/u.exec("\uD83D\uDC38\uDC38"),
null);
if (typeof reportCompare === "function")
reportCompare(true, true);

Some files were not shown because too many files have changed in this diff Show More