mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 13:23:07 +00:00
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:
+21
-4
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 *********************************************/
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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 ==============================================================
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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_;
|
||||
|
||||
@@ -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;");
|
||||
@@ -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();
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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. */
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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*)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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 *************************************************************/
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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");
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user