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

- Bug 1253094, part 8 - Stop using DebugOnly for class/struct members in uriloader/. r=bz (15566e1146)
- Bug 1253094, part 9 - Stop using DebugOnly for class/struct members in xpcom/. r=froydnj (9fb881be79)
- Bug 1253094, part 10 - Stop using DebugOnly for class/struct members in memory/. r=njn (5fd563e632)
- Bug 1248843 - Make it clearer that DebugOnly uses up space even in optimized, non-DEBUG builds. r=Waldo (8e5e6e6a01)
- Bug 1253094, part 11 - Make DebugOnly a MOZ_STACK_CLASS. r=Waldo (7cee0c3c03)
- Bug 1246116 - BaldrMonkey: Wasm validation for block and loop. r=luke (0da84fb8fe)
- Bug 1252498 - Baldr: add Wasm object behind pref, default off (r=jorendorff) (b554912a96)
- Bug 1256988 - Fix #endif comments for MOZ_WIDGET_GTK. r=chmanchester (28928d1d58)
- Bug 724538 - Regenerate Unicode property data with updated script. r=emk (50e43bb897)
- Bug 1232665 - initialize class members: mLastPrefLang and mLastPrefFirstFont. r=jfkthame (8a62f92809)
- Bug 1248248 - Don't break glyph run for orientation mismatch before a cluster-extender. r=xidorn (f114f65903)
- Bug 1252432 part 1 - Implement wasm i32.wrap. r=luke (54d1e634b6)
- Bug 1252432 part 2 - Implement wasm i64.extend_s and i64.extend_u. r=bbouvier (d673455188)
- Bug 1252432 part 3 - Implement wasm i64.trunc_s and i64.trunc_u. r=sunfish (41dd8d7272)
- Bug 1253115 - BaldrMonkey: Convert AsmJSHeapAccess offsets to unsigned. r=luke (5cb02e4832)
- Bug 1253115 - BaldrMonkey: Refactor AsmJS load/store infrastructure. r=luke (ef75bae281)
- Bug 1243583 - ensure transition events are dispatched to all the relevant subdocuments, r=dholbert (05026b75bb)
- Bug 1240985 - IPC fuzzer (r=gabor) (e825e77187)
- Bug 1248750 - Eliminate intentional IPC crashes (r=dvander) (0ace690c3b)
- Bug 1242609 - Implement PeekMessage to get some messages earlier. r=billm (4985fc8394)
- Bug 1257314 - Properly lock in IPC PeekMessages. r=dvander a=topcrash (6fe1db48f4)
- Bug 1242609 - Use PeekMessages to get the most recent DisplayPort request. r=kats (12374eafba)
- Bug 1254471 - Fix MessageChannel.cpp error unused variable transaction. r=billm (6a74186673)
- Bug 1251482 - Remove remaining references to MOZILLA_XPCOMRT_API from xpcom. r=froydnj (b691ca31f4)
- Bug 1251473 - Remove libxpcomrt library. r=froydnj (faed80b0ed)
- Bug 1249787 - BaldrMonkey: Add the testcase, which was mistakenly omitted from the main push. r=luke (1ef533365a)
- Bug 1250556: Require Store value expression to have the opcode's type; r=sunfish (b8363b4fc6)
- Bug 1250955: Guard against unimplemented i64 opcodes; r=jandem (98689ea7da)
- Bug 1253137 - Baldr: update version uint32 to match BinaryEncoding.md (r=sunfish) (c75d60370a)
- Bug 1253681 - BaldrMonkey: Update to the current official opcode encodings. r=luke (2e69d5780b)
- Bug 1252019: Don't patch profiling entries for the BadIndirectCall exit; r=luke (6f336d796c)
- Bug 1253137 - Baldr: update section header structure to match BinaryEncoding.md, part 1 (r=sunfish) (cd3e204373)
- Bug 1246116 - BaldrMonkey: Wasm validation for block and loop. r=luke (c594d15189)
- Bug 1253137 - Baldr: update memory exports to match BinaryEncoding.md (r=sunfish) (73fd37ee3b)
This commit is contained in:
2024-02-12 09:50:49 +08:00
parent 06d40aca85
commit cc394d8cae
112 changed files with 3956 additions and 2799 deletions
+2 -2
View File
@@ -204,7 +204,7 @@ static void LoadExtraSharedLibs()
}
}
}
#endif //MOZ_WIDGET_GTK2
#endif //MOZ_WIDGET_GTK == 2
/* nsPluginsDir implementation */
@@ -302,7 +302,7 @@ nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary)
#else
*outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
pLibrary = *outLibrary;
#endif // MOZ_WIDGET_GTK2
#endif // MOZ_WIDGET_GTK == 2
#ifdef DEBUG
printf("LoadPlugin() %s returned %lx\n",
+1
View File
@@ -303,6 +303,7 @@ LoadRuntimeOptions(const char* aPrefName, void* /* aClosure */)
// Runtime options.
JS::RuntimeOptions runtimeOptions;
runtimeOptions.setAsmJS(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asmjs")))
.setWasm(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm")))
.setThrowOnAsmJSValidationFailure(GetWorkerPref<bool>(
NS_LITERAL_CSTRING("throw_on_asmjs_validation_failure")))
.setBaseline(GetWorkerPref<bool>(NS_LITERAL_CSTRING("baselinejit")))
+14 -7
View File
@@ -40,9 +40,10 @@ using dom::TabParent;
uint64_t APZCCallbackHelper::sLastTargetAPZCNotificationInputBlock = uint64_t(-1);
static void
AdjustDisplayPortForScrollDelta(mozilla::layers::FrameMetrics& aFrameMetrics,
const CSSPoint& aActualScrollOffset)
void
APZCCallbackHelper::AdjustDisplayPortForScrollDelta(
mozilla::layers::FrameMetrics& aFrameMetrics,
const CSSPoint& aActualScrollOffset)
{
// Correct the display-port by the difference between the requested scroll
// offset and the resulting scroll offset after setting the requested value.
@@ -97,9 +98,7 @@ ScrollFrameTo(nsIScrollableFrame* aFrame, const CSSPoint& aPoint, bool& aSuccess
// Also if the scrollable frame got a scroll request from a higher priority origin
// since the last layers update, then we don't want to push our scroll request
// because we'll clobber that one, which is bad.
bool scrollInProgress = aFrame->IsProcessingAsyncScroll()
|| nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin())
|| aFrame->LastSmoothScrollOrigin();
bool scrollInProgress = APZCCallbackHelper::IsScrollInProgress(aFrame);
if (!scrollInProgress) {
aFrame->ScrollToCSSPixelsApproximate(targetScrollPosition, nsGkAtoms::apz);
geckoScrollPosition = CSSPoint::FromAppUnits(aFrame->GetScrollPosition());
@@ -143,7 +142,7 @@ ScrollFrame(nsIContent* aContent,
} else {
// Correct the display port due to the difference between mScrollOffset and the
// actual scroll offset.
AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
}
} else {
// For whatever reason we couldn't update the scroll offset on the scroll frame,
@@ -918,6 +917,14 @@ APZCCallbackHelper::NotifyFlushComplete(nsIPresShell* aShell)
observerService->NotifyObservers(nullptr, "apz-repaints-flushed", nullptr);
}
/* static */ bool
APZCCallbackHelper::IsScrollInProgress(nsIScrollableFrame* aFrame)
{
return aFrame->IsProcessingAsyncScroll()
|| nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin())
|| aFrame->LastSmoothScrollOrigin();
}
} // namespace layers
} // namespace mozilla
+12
View File
@@ -15,6 +15,7 @@
class nsIContent;
class nsIDocument;
class nsIPresShell;
class nsIScrollableFrame;
class nsIWidget;
template<class T> struct already_AddRefed;
template<class T> class nsCOMPtr;
@@ -163,6 +164,17 @@ public:
/* Notify content that the repaint flush is complete. */
static void NotifyFlushComplete(nsIPresShell* aShell);
static void
AdjustDisplayPortForScrollDelta(mozilla::layers::FrameMetrics& aFrameMetrics,
const CSSPoint& aActualScrollOffset);
/*
* Check if the scrollable frame is currently in the middle of an async
* or smooth scroll. We want to discard certain scroll input if this is
* true to prevent clobbering higher priority origins.
*/
static bool
IsScrollInProgress(nsIScrollableFrame* aFrame);
private:
static uint64_t sLastTargetAPZCNotificationInputBlock;
};
+1 -1
View File
@@ -1681,4 +1681,4 @@ ApplyGdkScreenFontOptions(FcPattern *aPattern)
cairo_ft_font_options_substitute(options, aPattern);
}
#endif // MOZ_WIDGET_GTK2
#endif // MOZ_WIDGET_GTK
+1 -1
View File
@@ -2262,7 +2262,7 @@ ApplyGdkScreenFontOptions(FcPattern *aPattern)
cairo_ft_font_options_substitute(options, aPattern);
}
#endif // MOZ_WIDGET_GTK2
#endif // MOZ_WIDGET_GTK
#ifdef USE_SKIA
already_AddRefed<mozilla::gfx::GlyphRenderingOptions>
+1
View File
@@ -176,6 +176,7 @@ private:
DECL_GFX_PREF(Live, "apz.overscroll.stop_velocity_threshold", APZOverscrollStopVelocityThreshold, float, 0.01f);
DECL_GFX_PREF(Live, "apz.overscroll.stretch_factor", APZOverscrollStretchFactor, float, 0.5f);
DECL_GFX_PREF(Live, "apz.paint_skipping.enabled", APZPaintSkipping, bool, true);
DECL_GFX_PREF(Live, "apz.peek_messages.enabled", APZPeekMessages, bool, true);
DECL_GFX_PREF(Live, "apz.printtree", APZPrintTree, bool, false);
DECL_GFX_PREF(Live, "apz.record_checkerboarding", APZRecordCheckerboarding, bool, false);
DECL_GFX_PREF(Live, "apz.test.logging_enabled", APZTestLoggingEnabled, bool, false);
+5 -2
View File
@@ -1579,7 +1579,9 @@ gfxFontGroup::gfxFontGroup(const FontFamilyList& aFontFamilyList,
, mDevToCssSize(aDevToCssSize)
, mUserFontSet(aUserFontSet)
, mTextPerf(aTextPerf)
, mLastPrefLang(eFontPrefLang_Western)
, mPageLang(gfxPlatformFontList::GetFontPrefLangFor(aStyle->language))
, mLastPrefFirstFont(false)
, mSkipDrawing(false)
, mSkipUpdateUserFonts(false)
{
@@ -2962,10 +2964,11 @@ void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
lastRangeIndex++;
prevFont = font;
} else {
// if font has changed, make a new range
// if font or orientation has changed, make a new range...
// unless ch is a variation selector (bug 1248248)
gfxTextRange& prevRange = aRanges[lastRangeIndex];
if (prevRange.font != font || prevRange.matchType != matchType ||
prevRange.orientation != orient) {
(prevRange.orientation != orient && !IsClusterExtender(ch))) {
// close out the previous range
prevRange.end = origI;
aRanges.AppendElement(gfxTextRange(origI, i + 1,
File diff suppressed because it is too large Load Diff
+128 -80
View File
@@ -11,7 +11,7 @@
*/
/*
* Created on Tue Nov 17 07:34:16 2015 from UCD data files with version info:
* Created on Thu Nov 26 21:24:13 2015 from UCD data files with version info:
*
# Date: 2015-06-16, 20:24:00 GMT [KW]
@@ -64,6 +64,7 @@ Standard.
#pragma pack(1)
#if !ENABLE_INTL_API
struct nsCharProps1 {
unsigned char mMirrorOffsetIndex:5;
@@ -71,7 +72,19 @@ struct nsCharProps1 {
unsigned char mCombiningClass:8;
};
#endif
#if ENABLE_INTL_API
struct nsCharProps2 {
unsigned char mPairedBracketType:2;
unsigned char mVertOrient:2;
unsigned char mXidmod:4;
};
#endif
#if !ENABLE_INTL_API
struct nsCharProps2 {
unsigned char mScriptCode:8;
@@ -83,6 +96,7 @@ struct nsCharProps2 {
unsigned char mVertOrient:2;
};
#endif
#pragma pack()
@@ -136,92 +150,126 @@ enum {
MOZ_SCRIPT_BRAILLE = 46,
MOZ_SCRIPT_CYPRIOT = 47,
MOZ_SCRIPT_LIMBU = 48,
MOZ_SCRIPT_OSMANYA = 49,
MOZ_SCRIPT_SHAVIAN = 50,
MOZ_SCRIPT_LINEAR_B = 51,
MOZ_SCRIPT_LINEAR_B = 49,
MOZ_SCRIPT_OSMANYA = 50,
MOZ_SCRIPT_SHAVIAN = 51,
MOZ_SCRIPT_TAI_LE = 52,
MOZ_SCRIPT_UGARITIC = 53,
MOZ_SCRIPT_NEW_TAI_LUE = 54,
MOZ_SCRIPT_KATAKANA_OR_HIRAGANA = 54,
MOZ_SCRIPT_BUGINESE = 55,
MOZ_SCRIPT_GLAGOLITIC = 56,
MOZ_SCRIPT_TIFINAGH = 57,
MOZ_SCRIPT_KHAROSHTHI = 57,
MOZ_SCRIPT_SYLOTI_NAGRI = 58,
MOZ_SCRIPT_OLD_PERSIAN = 59,
MOZ_SCRIPT_KHAROSHTHI = 60,
MOZ_SCRIPT_UNKNOWN = 61,
MOZ_SCRIPT_NEW_TAI_LUE = 59,
MOZ_SCRIPT_TIFINAGH = 60,
MOZ_SCRIPT_OLD_PERSIAN = 61,
MOZ_SCRIPT_BALINESE = 62,
MOZ_SCRIPT_CUNEIFORM = 63,
MOZ_SCRIPT_PHOENICIAN = 64,
MOZ_SCRIPT_PHAGS_PA = 65,
MOZ_SCRIPT_NKO = 66,
MOZ_SCRIPT_KAYAH_LI = 67,
MOZ_SCRIPT_LEPCHA = 68,
MOZ_SCRIPT_REJANG = 69,
MOZ_SCRIPT_SUNDANESE = 70,
MOZ_SCRIPT_SAURASHTRA = 71,
MOZ_SCRIPT_CHAM = 72,
MOZ_SCRIPT_OL_CHIKI = 73,
MOZ_SCRIPT_VAI = 74,
MOZ_SCRIPT_CARIAN = 75,
MOZ_SCRIPT_LYCIAN = 76,
MOZ_SCRIPT_LYDIAN = 77,
MOZ_SCRIPT_AVESTAN = 78,
MOZ_SCRIPT_BAMUM = 79,
MOZ_SCRIPT_EGYPTIAN_HIEROGLYPHS = 80,
MOZ_SCRIPT_IMPERIAL_ARAMAIC = 81,
MOZ_SCRIPT_INSCRIPTIONAL_PAHLAVI = 82,
MOZ_SCRIPT_INSCRIPTIONAL_PARTHIAN = 83,
MOZ_SCRIPT_JAVANESE = 84,
MOZ_SCRIPT_KAITHI = 85,
MOZ_SCRIPT_LISU = 86,
MOZ_SCRIPT_MEETEI_MAYEK = 87,
MOZ_SCRIPT_OLD_SOUTH_ARABIAN = 88,
MOZ_SCRIPT_OLD_TURKIC = 89,
MOZ_SCRIPT_SAMARITAN = 90,
MOZ_SCRIPT_TAI_THAM = 91,
MOZ_SCRIPT_TAI_VIET = 92,
MOZ_SCRIPT_BATAK = 93,
MOZ_SCRIPT_BRAHMI = 94,
MOZ_SCRIPT_MANDAIC = 95,
MOZ_SCRIPT_CHAKMA = 96,
MOZ_SCRIPT_MEROITIC_CURSIVE = 97,
MOZ_SCRIPT_MEROITIC_HIEROGLYPHS = 98,
MOZ_SCRIPT_MIAO = 99,
MOZ_SCRIPT_SHARADA = 100,
MOZ_SCRIPT_SORA_SOMPENG = 101,
MOZ_SCRIPT_TAKRI = 102,
MOZ_SCRIPT_BASSA_VAH = 103,
MOZ_SCRIPT_CAUCASIAN_ALBANIAN = 104,
MOZ_SCRIPT_DUPLOYAN = 105,
MOZ_SCRIPT_ELBASAN = 106,
MOZ_SCRIPT_GRANTHA = 107,
MOZ_SCRIPT_KHOJKI = 108,
MOZ_SCRIPT_KHUDAWADI = 109,
MOZ_SCRIPT_LINEAR_A = 110,
MOZ_SCRIPT_MAHAJANI = 111,
MOZ_SCRIPT_MANICHAEAN = 112,
MOZ_SCRIPT_MENDE_KIKAKUI = 113,
MOZ_SCRIPT_MODI = 114,
MOZ_SCRIPT_MRO = 115,
MOZ_SCRIPT_NABATAEAN = 116,
MOZ_SCRIPT_OLD_NORTH_ARABIAN = 117,
MOZ_SCRIPT_OLD_PERMIC = 118,
MOZ_SCRIPT_PAHAWH_HMONG = 119,
MOZ_SCRIPT_PALMYRENE = 120,
MOZ_SCRIPT_PAU_CIN_HAU = 121,
MOZ_SCRIPT_PSALTER_PAHLAVI = 122,
MOZ_SCRIPT_SIDDHAM = 123,
MOZ_SCRIPT_TIRHUTA = 124,
MOZ_SCRIPT_WARANG_CITI = 125,
MOZ_SCRIPT_AHOM = 126,
MOZ_SCRIPT_ANATOLIAN_HIEROGLYPHS = 127,
MOZ_SCRIPT_HATRAN = 128,
MOZ_SCRIPT_MULTANI = 129,
MOZ_SCRIPT_OLD_HUNGARIAN = 130,
MOZ_SCRIPT_SIGNWRITING = 131,
MOZ_SCRIPT_MATHEMATICAL_NOTATION = 132,
MOZ_SCRIPT_BATAK = 63,
MOZ_SCRIPT_BLISSYMBOLS = 64,
MOZ_SCRIPT_BRAHMI = 65,
MOZ_SCRIPT_CHAM = 66,
MOZ_SCRIPT_CIRTH = 67,
MOZ_SCRIPT_OLD_CHURCH_SLAVONIC_CYRILLIC = 68,
MOZ_SCRIPT_DEMOTIC_EGYPTIAN = 69,
MOZ_SCRIPT_HIERATIC_EGYPTIAN = 70,
MOZ_SCRIPT_EGYPTIAN_HIEROGLYPHS = 71,
MOZ_SCRIPT_KHUTSURI = 72,
MOZ_SCRIPT_SIMPLIFIED_HAN = 73,
MOZ_SCRIPT_TRADITIONAL_HAN = 74,
MOZ_SCRIPT_PAHAWH_HMONG = 75,
MOZ_SCRIPT_OLD_HUNGARIAN = 76,
MOZ_SCRIPT_HARAPPAN_INDUS = 77,
MOZ_SCRIPT_JAVANESE = 78,
MOZ_SCRIPT_KAYAH_LI = 79,
MOZ_SCRIPT_LATIN_FRAKTUR = 80,
MOZ_SCRIPT_LATIN_GAELIC = 81,
MOZ_SCRIPT_LEPCHA = 82,
MOZ_SCRIPT_LINEAR_A = 83,
MOZ_SCRIPT_MANDAIC = 84,
MOZ_SCRIPT_MAYAN_HIEROGLYPHS = 85,
MOZ_SCRIPT_MEROITIC_HIEROGLYPHS = 86,
MOZ_SCRIPT_NKO = 87,
MOZ_SCRIPT_OLD_TURKIC = 88,
MOZ_SCRIPT_OLD_PERMIC = 89,
MOZ_SCRIPT_PHAGS_PA = 90,
MOZ_SCRIPT_PHOENICIAN = 91,
MOZ_SCRIPT_MIAO = 92,
MOZ_SCRIPT_RONGORONGO = 93,
MOZ_SCRIPT_SARATI = 94,
MOZ_SCRIPT_ESTRANGELO_SYRIAC = 95,
MOZ_SCRIPT_WESTERN_SYRIAC = 96,
MOZ_SCRIPT_EASTERN_SYRIAC = 97,
MOZ_SCRIPT_TENGWAR = 98,
MOZ_SCRIPT_VAI = 99,
MOZ_SCRIPT_VISIBLE_SPEECH = 100,
MOZ_SCRIPT_CUNEIFORM = 101,
MOZ_SCRIPT_UNWRITTEN_LANGUAGES = 102,
MOZ_SCRIPT_UNKNOWN = 103,
MOZ_SCRIPT_CARIAN = 104,
MOZ_SCRIPT_JAPANESE = 105,
MOZ_SCRIPT_TAI_THAM = 106,
MOZ_SCRIPT_LYCIAN = 107,
MOZ_SCRIPT_LYDIAN = 108,
MOZ_SCRIPT_OL_CHIKI = 109,
MOZ_SCRIPT_REJANG = 110,
MOZ_SCRIPT_SAURASHTRA = 111,
MOZ_SCRIPT_SIGNWRITING = 112,
MOZ_SCRIPT_SUNDANESE = 113,
MOZ_SCRIPT_MOON = 114,
MOZ_SCRIPT_MEETEI_MAYEK = 115,
MOZ_SCRIPT_IMPERIAL_ARAMAIC = 116,
MOZ_SCRIPT_AVESTAN = 117,
MOZ_SCRIPT_CHAKMA = 118,
MOZ_SCRIPT_KOREAN = 119,
MOZ_SCRIPT_KAITHI = 120,
MOZ_SCRIPT_MANICHAEAN = 121,
MOZ_SCRIPT_INSCRIPTIONAL_PAHLAVI = 122,
MOZ_SCRIPT_PSALTER_PAHLAVI = 123,
MOZ_SCRIPT_BOOK_PAHLAVI = 124,
MOZ_SCRIPT_INSCRIPTIONAL_PARTHIAN = 125,
MOZ_SCRIPT_SAMARITAN = 126,
MOZ_SCRIPT_TAI_VIET = 127,
MOZ_SCRIPT_MATHEMATICAL_NOTATION = 128,
MOZ_SCRIPT_SYMBOLS = 129,
MOZ_SCRIPT_BAMUM = 130,
MOZ_SCRIPT_LISU = 131,
MOZ_SCRIPT_NAKHI_GEBA = 132,
MOZ_SCRIPT_OLD_SOUTH_ARABIAN = 133,
MOZ_SCRIPT_BASSA_VAH = 134,
MOZ_SCRIPT_DUPLOYAN = 135,
MOZ_SCRIPT_ELBASAN = 136,
MOZ_SCRIPT_GRANTHA = 137,
MOZ_SCRIPT_KPELLE = 138,
MOZ_SCRIPT_LOMA = 139,
MOZ_SCRIPT_MENDE_KIKAKUI = 140,
MOZ_SCRIPT_MEROITIC_CURSIVE = 141,
MOZ_SCRIPT_OLD_NORTH_ARABIAN = 142,
MOZ_SCRIPT_NABATAEAN = 143,
MOZ_SCRIPT_PALMYRENE = 144,
MOZ_SCRIPT_KHUDAWADI = 145,
MOZ_SCRIPT_WARANG_CITI = 146,
MOZ_SCRIPT_AFAKA = 147,
MOZ_SCRIPT_JURCHEN = 148,
MOZ_SCRIPT_MRO = 149,
MOZ_SCRIPT_NUSHU = 150,
MOZ_SCRIPT_SHARADA = 151,
MOZ_SCRIPT_SORA_SOMPENG = 152,
MOZ_SCRIPT_TAKRI = 153,
MOZ_SCRIPT_TANGUT = 154,
MOZ_SCRIPT_WOLEAI = 155,
MOZ_SCRIPT_ANATOLIAN_HIEROGLYPHS = 156,
MOZ_SCRIPT_KHOJKI = 157,
MOZ_SCRIPT_TIRHUTA = 158,
MOZ_SCRIPT_CAUCASIAN_ALBANIAN = 159,
MOZ_SCRIPT_MAHAJANI = 160,
MOZ_SCRIPT_AHOM = 161,
MOZ_SCRIPT_HATRAN = 162,
MOZ_SCRIPT_MODI = 163,
MOZ_SCRIPT_MULTANI = 164,
MOZ_SCRIPT_PAU_CIN_HAU = 165,
MOZ_SCRIPT_SIDDHAM = 166,
MOZ_NUM_SCRIPT_CODES = 133,
MOZ_NUM_SCRIPT_CODES = 167,
MOZ_SCRIPT_INVALID = -1
};
+364 -210
View File
@@ -281,6 +281,192 @@ private:
CxxStackFrame& operator=(const CxxStackFrame&) = delete;
};
class AutoEnterTransaction
{
public:
explicit AutoEnterTransaction(MessageChannel *aChan,
int32_t aMsgSeqno,
int32_t aTransactionID,
int aPriority)
: mChan(aChan),
mActive(true),
mOutgoing(true),
mPriority(aPriority),
mSeqno(aMsgSeqno),
mTransaction(aTransactionID),
mNext(mChan->mTransactionStack)
{
mChan->mMonitor->AssertCurrentThreadOwns();
mChan->mTransactionStack = this;
}
explicit AutoEnterTransaction(MessageChannel *aChan, const IPC::Message &aMessage)
: mChan(aChan),
mActive(true),
mOutgoing(false),
mPriority(aMessage.priority()),
mSeqno(aMessage.seqno()),
mTransaction(aMessage.transaction_id()),
mNext(mChan->mTransactionStack)
{
mChan->mMonitor->AssertCurrentThreadOwns();
if (!aMessage.is_sync()) {
mActive = false;
return;
}
mChan->mTransactionStack = this;
}
~AutoEnterTransaction() {
mChan->mMonitor->AssertCurrentThreadOwns();
if (mActive) {
mChan->mTransactionStack = mNext;
}
}
void Cancel() {
AutoEnterTransaction *cur = mChan->mTransactionStack;
MOZ_RELEASE_ASSERT(cur == this);
while (cur && cur->mPriority != IPC::Message::PRIORITY_NORMAL) {
// Note that, in the following situation, we will cancel multiple
// transactions:
// 1. Parent sends high prio message P1 to child.
// 2. Child sends high prio message C1 to child.
// 3. Child dispatches P1, parent blocks.
// 4. Child cancels.
// In this case, both P1 and C1 are cancelled. The parent will
// remove C1 from its queue when it gets the cancellation message.
MOZ_RELEASE_ASSERT(cur->mActive);
cur->mActive = false;
cur = cur->mNext;
}
mChan->mTransactionStack = cur;
MOZ_RELEASE_ASSERT(IsComplete());
}
bool AwaitingSyncReply() const {
MOZ_RELEASE_ASSERT(mActive);
if (mOutgoing) {
return true;
}
return mNext ? mNext->AwaitingSyncReply() : false;
}
int AwaitingSyncReplyPriority() const {
MOZ_RELEASE_ASSERT(mActive);
if (mOutgoing) {
return mPriority;
}
return mNext ? mNext->AwaitingSyncReplyPriority() : 0;
}
bool DispatchingSyncMessage() const {
MOZ_RELEASE_ASSERT(mActive);
if (!mOutgoing) {
return true;
}
return mNext ? mNext->DispatchingSyncMessage() : false;
}
int DispatchingSyncMessagePriority() const {
MOZ_RELEASE_ASSERT(mActive);
if (!mOutgoing) {
return mPriority;
}
return mNext ? mNext->DispatchingSyncMessagePriority() : 0;
}
int Priority() const {
MOZ_RELEASE_ASSERT(mActive);
return mPriority;
}
int32_t SequenceNumber() const {
MOZ_RELEASE_ASSERT(mActive);
return mSeqno;
}
int32_t TransactionID() const {
MOZ_RELEASE_ASSERT(mActive);
return mTransaction;
}
void ReceivedReply(const IPC::Message& aMessage) {
MOZ_RELEASE_ASSERT(aMessage.seqno() == mSeqno);
MOZ_RELEASE_ASSERT(aMessage.transaction_id() == mTransaction);
MOZ_RELEASE_ASSERT(!mReply);
IPC_LOG("Reply received on worker thread: seqno=%d", mSeqno);
mReply = new IPC::Message(aMessage);
MOZ_RELEASE_ASSERT(IsComplete());
}
void HandleReply(const IPC::Message& aMessage) {
AutoEnterTransaction *cur = mChan->mTransactionStack;
MOZ_RELEASE_ASSERT(cur == this);
while (cur) {
MOZ_RELEASE_ASSERT(cur->mActive);
if (aMessage.seqno() == cur->mSeqno) {
cur->ReceivedReply(aMessage);
break;
}
cur = cur->mNext;
MOZ_RELEASE_ASSERT(cur);
}
}
bool IsComplete() {
return !mActive || mReply;
}
bool IsOutgoing() {
return mOutgoing;
}
bool IsCanceled() {
return !mActive;
}
bool IsBottom() const {
return !mNext;
}
bool IsError() {
MOZ_RELEASE_ASSERT(mReply);
return mReply->is_reply_error();
}
nsAutoPtr<IPC::Message> GetReply() {
return Move(mReply);
}
private:
MessageChannel *mChan;
// Active is true if this transaction is on the mChan->mTransactionStack
// stack. Generally we're not on the stack if the transaction was canceled
// or if it was for a message that doesn't require transactions (an async
// message).
bool mActive;
// Is this stack frame for an outgoing message?
bool mOutgoing;
// Properties of the message being sent/received.
int mPriority;
int32_t mSeqno;
int32_t mTransaction;
// Next item in mChan->mTransactionStack.
AutoEnterTransaction *mNext;
// Pointer the a reply received for this message, if one was received.
nsAutoPtr<IPC::Message> mReply;
};
MessageChannel::MessageChannel(MessageListener *aListener)
: mListener(aListener),
mChannelState(ChannelClosed),
@@ -293,17 +479,11 @@ MessageChannel::MessageChannel(MessageListener *aListener)
mInTimeoutSecondHalf(false),
mNextSeqno(0),
mLastSendError(SyncSendError::SendSuccess),
mAwaitingSyncReply(false),
mAwaitingSyncReplyPriority(0),
mDispatchingSyncMessage(false),
mDispatchingSyncMessagePriority(0),
mDispatchingAsyncMessage(false),
mDispatchingAsyncMessagePriority(0),
mCurrentTransaction(0),
mPendingSendPriorities(0),
mTransactionStack(nullptr),
mTimedOutMessageSeqno(0),
mTimedOutMessagePriority(0),
mRecvdErrors(0),
mRemoteStackDepthGuess(false),
mSawInterruptOutMsg(false),
mIsWaitingForIncoming(false),
@@ -344,6 +524,51 @@ MessageChannel::~MessageChannel()
Clear();
}
// This function returns the current transaction ID. Since the notion of a
// "current transaction" can be hard to define when messages race with each
// other and one gets canceled and the other doesn't, we require that this
// function is only called when the current transaction is known to be for a
// high priority message. In that case, we know for sure what the caller is
// looking for.
int32_t
MessageChannel::CurrentHighPriorityTransaction() const
{
mMonitor->AssertCurrentThreadOwns();
if (!mTransactionStack) {
return 0;
}
MOZ_RELEASE_ASSERT(mTransactionStack->Priority() == IPC::Message::PRIORITY_HIGH);
return mTransactionStack->TransactionID();
}
bool
MessageChannel::AwaitingSyncReply() const
{
mMonitor->AssertCurrentThreadOwns();
return mTransactionStack ? mTransactionStack->AwaitingSyncReply() : false;
}
int
MessageChannel::AwaitingSyncReplyPriority() const
{
mMonitor->AssertCurrentThreadOwns();
return mTransactionStack ? mTransactionStack->AwaitingSyncReplyPriority() : 0;
}
bool
MessageChannel::DispatchingSyncMessage() const
{
mMonitor->AssertCurrentThreadOwns();
return mTransactionStack ? mTransactionStack->DispatchingSyncMessage() : false;
}
int
MessageChannel::DispatchingSyncMessagePriority() const
{
mMonitor->AssertCurrentThreadOwns();
return mTransactionStack ? mTransactionStack->DispatchingSyncMessagePriority() : 0;
}
static void
PrintErrorMessage(Side side, const char* channelName, const char* msg)
{
@@ -405,7 +630,6 @@ MessageChannel::Clear()
// Free up any memory used by pending messages.
mPending.clear();
mRecvd = nullptr;
mOutOfTurnReplies.clear();
while (!mDeferred.empty()) {
mDeferred.pop();
@@ -622,7 +846,7 @@ MessageChannel::ShouldDeferMessage(const Message& aMsg)
// child's message comes in, we can pretend the child hasn't quite
// finished sending it yet. Since the message is sync, we know that the
// child hasn't moved on yet.
return mSide == ParentSide && aMsg.transaction_id() != mCurrentTransaction;
return mSide == ParentSide && aMsg.transaction_id() != CurrentHighPriorityTransaction();
}
// Predicate that is true for messages that should be consolidated if 'compress' is set.
@@ -659,33 +883,17 @@ MessageChannel::OnMessageReceivedFromLink(const Message& aMsg)
return;
}
MOZ_RELEASE_ASSERT(aMsg.transaction_id() == mCurrentTransaction);
MOZ_RELEASE_ASSERT(AwaitingSyncReply());
MOZ_RELEASE_ASSERT(!mRecvd);
MOZ_RELEASE_ASSERT(!mTimedOutMessageSeqno);
// Rather than storing errors in mRecvd, we mark them in
// mRecvdErrors. We need a counter because multiple replies can arrive
// when a timeout happens, as in the following example. Imagine the
// child is running slowly. The parent sends a sync message P1. It times
// out. The child eventually sends a sync message C1. While waiting for
// the C1 response, the child dispatches P1. In doing so, it sends sync
// message C2. At that point, it's valid for the parent to send error
// responses for both C1 and C2.
if (aMsg.is_reply_error()) {
mRecvdErrors++;
NotifyWorkerThread();
return;
}
mRecvd = new Message(aMsg);
mTransactionStack->HandleReply(aMsg);
NotifyWorkerThread();
return;
}
// Prioritized messages cannot be compressed.
MOZ_RELEASE_ASSERT(aMsg.compress_type() == IPC::Message::COMPRESSION_NONE ||
aMsg.priority() == IPC::Message::PRIORITY_NORMAL);
aMsg.priority() == IPC::Message::PRIORITY_NORMAL);
bool compress = false;
if (aMsg.compress_type() == IPC::Message::COMPRESSION_ENABLED) {
@@ -768,9 +976,24 @@ MessageChannel::OnMessageReceivedFromLink(const Message& aMsg)
}
void
MessageChannel::ProcessPendingRequests(int seqno, int transaction)
MessageChannel::PeekMessages(msgid_t aMsgId, mozilla::function<void(const Message& aMsg)> aInvoke)
{
IPC_LOG("ProcessPendingRequests for seqno=%d, xid=%d", seqno, transaction);
MonitorAutoLock lock(*mMonitor);
for (MessageQueue::iterator it = mPending.begin(); it != mPending.end(); it++) {
Message &msg = *it;
if (msg.type() == aMsgId) {
aInvoke(msg);
}
}
}
void
MessageChannel::ProcessPendingRequests(AutoEnterTransaction& aTransaction)
{
IPC_LOG("ProcessPendingRequests for seqno=%d, xid=%d",
aTransaction.SequenceNumber(), aTransaction.TransactionID());
// Loop until there aren't any more priority messages to process.
for (;;) {
@@ -779,7 +1002,7 @@ MessageChannel::ProcessPendingRequests(int seqno, int transaction)
// operating with weird state (as if no Send is in progress). That could
// cause even normal priority sync messages to be processed (but not
// normal priority async messages), which would break message ordering.
if (WasTransactionCanceled(transaction)) {
if (aTransaction.IsCanceled()) {
return;
}
@@ -788,8 +1011,8 @@ MessageChannel::ProcessPendingRequests(int seqno, int transaction)
for (MessageQueue::iterator it = mPending.begin(); it != mPending.end(); ) {
Message &msg = *it;
MOZ_RELEASE_ASSERT(mCurrentTransaction == transaction,
"Calling ShouldDeferMessage when cancelled");
MOZ_RELEASE_ASSERT(!aTransaction.IsCanceled(),
"Calling ShouldDeferMessage when cancelled");
bool defer = ShouldDeferMessage(msg);
// Only log the interesting messages.
@@ -806,38 +1029,19 @@ MessageChannel::ProcessPendingRequests(int seqno, int transaction)
it++;
}
if (toProcess.empty())
if (toProcess.empty()) {
break;
}
// Processing these messages could result in more messages, so we
// loop around to check for more afterwards.
for (auto it = toProcess.begin(); it != toProcess.end(); it++)
for (auto it = toProcess.begin(); it != toProcess.end(); it++) {
ProcessPendingRequest(*it);
}
}
}
bool
MessageChannel::WasTransactionCanceled(int transaction)
{
if (transaction != mCurrentTransaction) {
// Imagine this scenario:
// 1. Child sends high prio sync message H1.
// 2. Parent sends reply to H1.
// 3. Parent sends high prio sync message H2.
// 4. Child's link thread receives H1 reply and H2 before worker wakes up.
// 5. Child dispatches H2 while still waiting for H1 reply.
// 6. Child cancels H2.
//
// In this case H1 will also be considered cancelled. However, its
// reply is still sitting in mRecvd, which can trip up later Sends. So
// we null it out here.
mRecvd = nullptr;
return true;
}
return false;
}
bool
MessageChannel::Send(Message* aMsg, Message* aReply)
{
@@ -866,8 +1070,7 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
return false;
}
if (mCurrentTransaction &&
DispatchingSyncMessagePriority() == IPC::Message::PRIORITY_NORMAL &&
if (DispatchingSyncMessagePriority() == IPC::Message::PRIORITY_NORMAL &&
msg->priority() > IPC::Message::PRIORITY_NORMAL)
{
// Don't allow sending CPOWs while we're dispatching a sync message.
@@ -877,9 +1080,8 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
return false;
}
if (mCurrentTransaction &&
(DispatchingSyncMessagePriority() == IPC::Message::PRIORITY_URGENT ||
DispatchingAsyncMessagePriority() == IPC::Message::PRIORITY_URGENT))
if (DispatchingSyncMessagePriority() == IPC::Message::PRIORITY_URGENT ||
DispatchingAsyncMessagePriority() == IPC::Message::PRIORITY_URGENT)
{
// Generally only the parent dispatches urgent messages. And the only
// sync messages it can send are high-priority. Mainly we want to ensure
@@ -890,27 +1092,24 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
return false;
}
if (mCurrentTransaction &&
(msg->priority() < DispatchingSyncMessagePriority() ||
msg->priority() < AwaitingSyncReplyPriority()))
if (msg->priority() < DispatchingSyncMessagePriority() ||
msg->priority() < AwaitingSyncReplyPriority())
{
MOZ_RELEASE_ASSERT(DispatchingSyncMessage() || DispatchingAsyncMessage());
IPC_LOG("Cancel from Send");
CancelMessage *cancel = new CancelMessage(mCurrentTransaction);
CancelTransaction(mCurrentTransaction);
CancelMessage *cancel = new CancelMessage(CurrentHighPriorityTransaction());
CancelTransaction(CurrentHighPriorityTransaction());
mLink->SendMessage(cancel);
}
IPC_ASSERT(msg->is_sync(), "can only Send() sync messages here");
if (mCurrentTransaction) {
IPC_ASSERT(msg->priority() >= DispatchingSyncMessagePriority(),
"can't send sync message of a lesser priority than what's being dispatched");
IPC_ASSERT(AwaitingSyncReplyPriority() <= msg->priority(),
"nested sync message sends must be of increasing priority");
IPC_ASSERT(DispatchingSyncMessagePriority() != IPC::Message::PRIORITY_URGENT,
"not allowed to send messages while dispatching urgent messages");
}
IPC_ASSERT(msg->priority() >= DispatchingSyncMessagePriority(),
"can't send sync message of a lesser priority than what's being dispatched");
IPC_ASSERT(AwaitingSyncReplyPriority() <= msg->priority(),
"nested sync message sends must be of increasing priority");
IPC_ASSERT(DispatchingSyncMessagePriority() != IPC::Message::PRIORITY_URGENT,
"not allowed to send messages while dispatching urgent messages");
IPC_ASSERT(DispatchingAsyncMessagePriority() != IPC::Message::PRIORITY_URGENT,
"not allowed to send messages while dispatching urgent messages");
@@ -927,27 +1126,28 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
int prio = msg->priority();
msgid_t replyType = msg->type() + 1;
AutoSetValue<bool> replies(mAwaitingSyncReply, true);
AutoSetValue<int> prioSet(mAwaitingSyncReplyPriority, prio);
AutoEnterTransaction transact(this, seqno);
AutoEnterTransaction *stackTop = mTransactionStack;
int prios = mPendingSendPriorities | (1 << prio);
AutoSetValue<int> priosSet(mPendingSendPriorities, prios);
int32_t transaction = mCurrentTransaction;
// If the most recent message on the stack is high priority, then our
// message should nest inside that and we use the same transaction
// ID. Otherwise we need a new transaction ID (so we use the seqno of the
// message we're sending).
bool nest = stackTop && stackTop->Priority() == IPC::Message::PRIORITY_HIGH;
int32_t transaction = nest ? stackTop->TransactionID() : seqno;
msg->set_transaction_id(transaction);
IPC_LOG("Send seqno=%d, xid=%d, pending=%d", seqno, transaction, prios);
bool handleWindowsMessages = mListener->HandleWindowsMessages(*aMsg);
AutoEnterTransaction transact(this, seqno, transaction, prio);
IPC_LOG("Send seqno=%d, xid=%d", seqno, transaction);
mLink->SendMessage(msg.forget());
while (true) {
ProcessPendingRequests(seqno, transaction);
if (WasTransactionCanceled(transaction)) {
IPC_LOG("Other side canceled seqno=%d, xid=%d", seqno, transaction);
mLastSendError = SyncSendError::CancelledAfterSend;
return false;
MOZ_RELEASE_ASSERT(!transact.IsCanceled());
ProcessPendingRequests(transact);
if (transact.IsComplete()) {
break;
}
if (!Connected()) {
ReportConnectionError("MessageChannel::Send");
@@ -955,55 +1155,38 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
return false;
}
// See if we've received a reply.
if (mRecvdErrors) {
IPC_LOG("Error: seqno=%d, xid=%d", seqno, transaction);
mRecvdErrors--;
mLastSendError = SyncSendError::ReplyError;
return false;
}
if (mRecvd) {
IPC_LOG("Got reply: seqno=%d, xid=%d", seqno, transaction);
break;
}
MOZ_RELEASE_ASSERT(!mTimedOutMessageSeqno);
MOZ_RELEASE_ASSERT(!transact.IsComplete());
MOZ_RELEASE_ASSERT(mTransactionStack == &transact);
MOZ_RELEASE_ASSERT(mCurrentTransaction == transaction);
bool maybeTimedOut = !WaitForSyncNotify(handleWindowsMessages);
if (mListener->NeedArtificialSleep()) {
MonitorAutoUnlock unlock(*mMonitor);
mListener->ArtificialSleep();
}
if (!Connected()) {
ReportConnectionError("MessageChannel::SendAndWait");
mLastSendError = SyncSendError::DisconnectedDuringSend;
return false;
}
if (WasTransactionCanceled(transaction)) {
IPC_LOG("Other side canceled seqno=%d, xid=%d", seqno, transaction);
mLastSendError = SyncSendError::CancelledAfterSend;
return false;
if (transact.IsCanceled()) {
break;
}
MOZ_RELEASE_ASSERT(mTransactionStack == &transact);
// We only time out a message if it initiated a new transaction (i.e.,
// if neither side has any other message Sends on the stack).
bool canTimeOut = transaction == seqno;
bool canTimeOut = transact.IsBottom();
if (maybeTimedOut && canTimeOut && !ShouldContinueFromTimeout()) {
// Since ShouldContinueFromTimeout drops the lock, we need to
// re-check all our conditions here. We shouldn't time out if any of
// these things happen because there won't be a reply to the timed
// out message in these cases.
if (WasTransactionCanceled(transaction)) {
IPC_LOG("Other side canceled seqno=%d, xid=%d", seqno, transaction);
mLastSendError = SyncSendError::CancelledAfterSend;
return false;
}
if (mRecvdErrors) {
mRecvdErrors--;
mLastSendError = SyncSendError::ReplyError;
return false;
}
if (mRecvd) {
if (transact.IsComplete()) {
break;
}
@@ -1014,18 +1197,36 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
mLastSendError = SyncSendError::TimedOut;
return false;
}
if (transact.IsCanceled()) {
break;
}
}
MOZ_RELEASE_ASSERT(mRecvd);
MOZ_RELEASE_ASSERT(mRecvd->is_reply(), "expected reply");
MOZ_RELEASE_ASSERT(!mRecvd->is_reply_error());
MOZ_RELEASE_ASSERT(mRecvd->seqno() == seqno);
MOZ_RELEASE_ASSERT(mRecvd->type() == replyType, "wrong reply type");
MOZ_RELEASE_ASSERT(mRecvd->is_sync());
if (transact.IsCanceled()) {
IPC_LOG("Other side canceled seqno=%d, xid=%d", seqno, transaction);
mLastSendError = SyncSendError::CancelledAfterSend;
return false;
}
*aReply = Move(*mRecvd);
mRecvd = nullptr;
mLastSendError = SyncSendError::SendSuccess;
if (transact.IsError()) {
IPC_LOG("Error: seqno=%d, xid=%d", seqno, transaction);
mLastSendError = SyncSendError::ReplyError;
return false;
}
IPC_LOG("Got reply: seqno=%d, xid=%d", seqno, transaction);
nsAutoPtr<Message> reply = transact.GetReply();
MOZ_RELEASE_ASSERT(reply);
MOZ_RELEASE_ASSERT(reply->is_reply(), "expected reply");
MOZ_RELEASE_ASSERT(!reply->is_reply_error());
MOZ_RELEASE_ASSERT(reply->seqno() == seqno);
MOZ_RELEASE_ASSERT(reply->type() == replyType, "wrong reply type");
MOZ_RELEASE_ASSERT(reply->is_sync());
*aReply = Move(*reply);
return true;
}
@@ -1254,16 +1455,6 @@ MessageChannel::ProcessPendingRequest(const Message &aUrgent)
AssertWorkerThread();
mMonitor->AssertCurrentThreadOwns();
// Note that it is possible we could have sent a sync message at
// the same time the parent process sent an urgent message, and
// therefore mPendingUrgentRequest is set *and* mRecvd is set as
// well, because the link thread received both before the worker
// thread woke up.
//
// In this case, we process the urgent message first, but we need
// to save the reply.
nsAutoPtr<Message> savedReply(mRecvd.forget());
IPC_LOG("Process pending: seqno=%d, xid=%d", aUrgent.seqno(), aUrgent.transaction_id());
DispatchMessage(aUrgent);
@@ -1272,13 +1463,6 @@ MessageChannel::ProcessPendingRequest(const Message &aUrgent)
return false;
}
// In between having dispatched our reply to the parent process, and
// re-acquiring the monitor, the parent process could have already
// processed that reply and sent the reply to our sync message. If so,
// our saved reply should be empty.
IPC_ASSERT(!mRecvd || !savedReply, "unknown reply");
if (!mRecvd)
mRecvd = savedReply.forget();
return true;
}
@@ -1354,8 +1538,6 @@ MessageChannel::OnMaybeDequeueOne()
return false;
}
// We should not be in a transaction yet if we're not blocked.
MOZ_RELEASE_ASSERT(mCurrentTransaction == 0);
DispatchMessage(recvd);
return true;
@@ -1376,27 +1558,33 @@ MessageChannel::DispatchMessage(const Message &aMsg)
AutoEnterTransaction transaction(this, aMsg);
int id = aMsg.transaction_id();
MOZ_RELEASE_ASSERT(!aMsg.is_sync() || id == mCurrentTransaction);
MOZ_RELEASE_ASSERT(!aMsg.is_sync() || id == transaction.TransactionID());
{
MonitorAutoUnlock unlock(*mMonitor);
CxxStackFrame frame(*this, IN_MESSAGE, &aMsg);
mListener->ArtificialSleep();
if (aMsg.is_sync())
DispatchSyncMessage(aMsg, *getter_Transfers(reply));
else if (aMsg.is_interrupt())
DispatchInterruptMessage(aMsg, 0);
else
DispatchAsyncMessage(aMsg);
mListener->ArtificialSleep();
}
if (mCurrentTransaction != id) {
if (reply && transaction.IsCanceled()) {
// The transaction has been canceled. Don't send a reply.
IPC_LOG("Nulling out reply due to cancellation, seqno=%d, xid=%d", aMsg.seqno(), id);
reply = nullptr;
}
}
if (reply && ChannelConnected == mChannelState) {
IPC_LOG("Sending reply seqno=%d, xid=%d", aMsg.seqno(), aMsg.transaction_id());
mLink->SendMessage(reply.forget());
}
}
@@ -1416,8 +1604,6 @@ MessageChannel::DispatchSyncMessage(const Message& aMsg, Message*& aReply)
Result rv;
{
AutoSetValue<MessageChannel*> blocked(blockingVar, this);
AutoSetValue<bool> sync(mDispatchingSyncMessage, true);
AutoSetValue<int> prioSet(mDispatchingSyncMessagePriority, prio);
rv = mListener->OnMessageReceived(aMsg, aReply);
}
@@ -1640,6 +1826,15 @@ MessageChannel::WaitResponse(bool aWaitTimedOut)
bool
MessageChannel::WaitForSyncNotify(bool /* aHandleWindowsMessages */)
{
#ifdef DEBUG
// WARNING: We don't release the lock here. We can't because the link thread
// could signal at this time and we would miss it. Instead we require
// ArtificialTimeout() to be extremely simple.
if (mListener->ArtificialTimeout()) {
return false;
}
#endif
PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ?
PR_INTERVAL_NO_TIMEOUT :
PR_MillisecondsToInterval(mTimeoutMs);
@@ -1676,6 +1871,7 @@ MessageChannel::ShouldContinueFromTimeout()
{
MonitorAutoUnlock unlock(*mMonitor);
cont = mListener->OnReplyTimeout();
mListener->ArtificialSleep();
}
static enum { UNKNOWN, NOT_DEBUGGING, DEBUGGING } sDebuggingChildren = UNKNOWN;
@@ -2109,38 +2305,7 @@ MessageChannel::CancelTransaction(int transaction)
// tampered with (by us). If so, they don't reset the variable to the old
// value.
IPC_LOG("CancelTransaction: xid=%d prios=%d", transaction, mPendingSendPriorities);
if (mPendingSendPriorities & (1 << IPC::Message::PRIORITY_NORMAL)) {
// This isn't an assert so much as an intentional crash because we're in
// a situation that we don't know how to recover from: The child is
// awaiting a reply to a normal-priority sync message. The transaction
// that this message initiated has now been canceled. That could only
// happen if a CPOW raced with the sync message and was dispatched by
// the child while the child was awaiting the sync reply; at some point
// while dispatching the CPOW, the transaction was canceled.
//
// Notes:
//
// 1. We don't want to cancel the normal-priority sync message along
// with the CPOWs because the browser relies on these messages working
// reliably.
//
// 2. Ideally we would like to avoid dispatching CPOWs while awaiting a
// sync response. This isn't possible though. To avoid deadlock, the
// parent would have to dispatch the sync message while waiting for the
// CPOW response. However, it wouldn't have dispatched async messages at
// that time, so we would have a message ordering bug. Dispatching the
// async messages first causes other hard-to-handle situations (what if
// they send CPOWs?).
//
// 3. We would like to be able to cancel the CPOWs but not the sync
// message. However, that would leave both the parent and the child
// running code at the same time, all while the sync message is still
// outstanding. That can cause a problem where message replies are
// received out of order.
mListener->IntentionalCrash();
}
IPC_LOG("CancelTransaction: xid=%d", transaction);
// An unusual case: We timed out a transaction which the other side then
// cancelled. In this case we just leave the timedout state and try to
@@ -2154,17 +2319,13 @@ MessageChannel::CancelTransaction(int transaction)
// 2. Parent times out H.
// 3. Child dispatches H and sends nested message H' (same transaction).
// 4. Parent dispatches H' and cancels.
MOZ_RELEASE_ASSERT(!mCurrentTransaction || mCurrentTransaction == transaction);
mCurrentTransaction = 0;
// During a timeout Send should always fail.
MOZ_RELEASE_ASSERT(!mAwaitingSyncReply);
MOZ_RELEASE_ASSERT(!mTransactionStack || mTransactionStack->TransactionID() == transaction);
if (mTransactionStack) {
mTransactionStack->Cancel();
}
} else {
MOZ_RELEASE_ASSERT(mCurrentTransaction == transaction);
mCurrentTransaction = 0;
mAwaitingSyncReply = false;
mAwaitingSyncReplyPriority = 0;
MOZ_RELEASE_ASSERT(mTransactionStack->TransactionID() == transaction);
mTransactionStack->Cancel();
}
bool foundSync = false;
@@ -2173,11 +2334,8 @@ MessageChannel::CancelTransaction(int transaction)
// If there was a race between the parent and the child, then we may
// have a queued sync message. We want to drop this message from the
// queue since it will get cancelled along with the transaction being
// cancelled. We don't bother doing this for normal priority messages
// because the child is just going to crash in that case, and we want to
// avoid processing messages out of order in the short time before it
// crashes.
// queue since if will get cancelled along with the transaction being
// cancelled. This happens if the message in the queue is high priority.
if (msg.is_sync() && msg.priority() != IPC::Message::PRIORITY_NORMAL) {
MOZ_RELEASE_ASSERT(!foundSync);
MOZ_RELEASE_ASSERT(msg.transaction_id() != transaction);
@@ -2187,36 +2345,32 @@ MessageChannel::CancelTransaction(int transaction)
continue;
}
// There may be messages in the queue that we expected to process from
// ProcessPendingRequests. However, Send will no longer call that
// function once it's been canceled. So we may need to process these
// messages in the normal event loop instead.
mWorkerLoop->PostTask(FROM_HERE, new DequeueTask(mDequeueOneTask));
it++;
}
}
// We could also zero out mDispatchingSyncMessage here. However, that would
// cause a race because mDispatchingSyncMessage is a worker-thread-only
// field and we can be called on the I/O thread. Luckily, we can check to
// see if mCurrentTransaction is 0 before examining DispatchSyncMessage.
bool
MessageChannel::IsInTransaction() const
{
MonitorAutoLock lock(*mMonitor);
return !!mTransactionStack;
}
void
MessageChannel::CancelCurrentTransaction()
{
MonitorAutoLock lock(*mMonitor);
if (mCurrentTransaction) {
if (DispatchingSyncMessagePriority() >= IPC::Message::PRIORITY_HIGH) {
if (DispatchingSyncMessagePriority() == IPC::Message::PRIORITY_URGENT ||
DispatchingAsyncMessagePriority() == IPC::Message::PRIORITY_URGENT)
{
mListener->IntentionalCrash();
}
IPC_LOG("Cancel requested: current xid=%d", mCurrentTransaction);
IPC_LOG("Cancel requested: current xid=%d", CurrentHighPriorityTransaction());
MOZ_RELEASE_ASSERT(DispatchingSyncMessage());
CancelMessage *cancel = new CancelMessage(mCurrentTransaction);
CancelTransaction(mCurrentTransaction);
CancelMessage *cancel = new CancelMessage(CurrentHighPriorityTransaction());
CancelTransaction(CurrentHighPriorityTransaction());
mLink->SendMessage(cancel);
}
}
+18 -86
View File
@@ -11,6 +11,7 @@
#include "base/basictypes.h"
#include "base/message_loop.h"
#include "mozilla/Function.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Monitor.h"
#include "mozilla/Vector.h"
@@ -57,6 +58,8 @@ enum class SyncSendError {
ReplyError,
};
class AutoEnterTransaction;
class MessageChannel : HasResultCodes
{
friend class ProcessLink;
@@ -107,6 +110,11 @@ class MessageChannel : HasResultCodes
mAbortOnError = abort;
}
// Call aInvoke for each pending message of type aId.
// XXX: You must get permission from an IPC peer to use this function
// since it requires custom deserialization and re-orders events.
void PeekMessages(Message::msgid_t aId, mozilla::function<void(const Message& aMsg)> aInvoke);
// Misc. behavioral traits consumers can request for this channel
enum ChannelFlags {
REQUIRE_DEFAULT = 0,
@@ -156,7 +164,7 @@ class MessageChannel : HasResultCodes
return !mCxxStackFrames.empty();
}
bool IsInTransaction() const { return mCurrentTransaction != 0; }
bool IsInTransaction() const;
void CancelCurrentTransaction();
/**
@@ -263,7 +271,7 @@ class MessageChannel : HasResultCodes
bool InterruptEventOccurred();
bool HasPendingEvents();
void ProcessPendingRequests(int seqno, int transaction);
void ProcessPendingRequests(AutoEnterTransaction& aTransaction);
bool ProcessPendingRequest(const Message &aUrgent);
void MaybeUndeferIncall();
@@ -366,15 +374,6 @@ class MessageChannel : HasResultCodes
return mInterruptStack.size();
}
// Returns true if we're blocking waiting for a reply.
bool AwaitingSyncReply() const {
mMonitor->AssertCurrentThreadOwns();
return mAwaitingSyncReply;
}
int AwaitingSyncReplyPriority() const {
mMonitor->AssertCurrentThreadOwns();
return mAwaitingSyncReplyPriority;
}
bool AwaitingInterruptReply() const {
mMonitor->AssertCurrentThreadOwns();
return !mInterruptStack.empty();
@@ -404,17 +403,7 @@ class MessageChannel : HasResultCodes
};
friend class AutoEnterWaitForIncoming;
// Returns true if we're dispatching a sync message's callback.
bool DispatchingSyncMessage() const {
AssertWorkerThread();
return mDispatchingSyncMessage;
}
int DispatchingSyncMessagePriority() const {
AssertWorkerThread();
return mDispatchingSyncMessagePriority;
}
// Returns true if we're dispatching an async message's callback.
bool DispatchingAsyncMessage() const {
AssertWorkerThread();
return mDispatchingAsyncMessage;
@@ -560,15 +549,6 @@ class MessageChannel : HasResultCodes
T mNew;
};
// Worker thread only.
bool mAwaitingSyncReply;
int mAwaitingSyncReplyPriority;
// Set while we are dispatching a synchronous message. Only for use on the
// worker thread.
bool mDispatchingSyncMessage;
int mDispatchingSyncMessagePriority;
bool mDispatchingAsyncMessage;
int mDispatchingAsyncMessagePriority;
@@ -590,56 +570,16 @@ class MessageChannel : HasResultCodes
// To ensure IDs are unique, we use sequence numbers for transaction IDs,
// which grow in opposite directions from child to parent.
// The current transaction ID.
int32_t mCurrentTransaction;
friend class AutoEnterTransaction;
AutoEnterTransaction *mTransactionStack;
// This field describes the priorities of the sync Send calls that are
// currently on stack. If a Send call for a message with priority P is on
// the C stack, then mPendingSendPriorities & (1 << P) will be
// non-zero. Note that cancelled Send calls are not removed from this
// bitfield (until they return).
int mPendingSendPriorities;
int32_t CurrentHighPriorityTransaction() const;
class AutoEnterTransaction
{
public:
explicit AutoEnterTransaction(MessageChannel *aChan, int32_t aMsgSeqno)
: mChan(aChan),
mNewTransaction(INT32_MAX),
mOldTransaction(mChan->mCurrentTransaction)
{
mChan->mMonitor->AssertCurrentThreadOwns();
if (mChan->mCurrentTransaction == 0) {
mNewTransaction = aMsgSeqno;
mChan->mCurrentTransaction = aMsgSeqno;
}
}
explicit AutoEnterTransaction(MessageChannel *aChan, const Message &aMessage)
: mChan(aChan),
mNewTransaction(aMessage.transaction_id()),
mOldTransaction(mChan->mCurrentTransaction)
{
mChan->mMonitor->AssertCurrentThreadOwns();
bool AwaitingSyncReply() const;
int AwaitingSyncReplyPriority() const;
if (!aMessage.is_sync())
return;
MOZ_DIAGNOSTIC_ASSERT(
!(mChan->mSide == ParentSide && mOldTransaction != aMessage.transaction_id()) ||
!mOldTransaction || aMessage.priority() > mChan->AwaitingSyncReplyPriority());
mChan->mCurrentTransaction = aMessage.transaction_id();
}
~AutoEnterTransaction() {
mChan->mMonitor->AssertCurrentThreadOwns();
if (mChan->mCurrentTransaction == mNewTransaction) {
mChan->mCurrentTransaction = mOldTransaction;
}
}
private:
MessageChannel *mChan;
int32_t mNewTransaction, mOldTransaction;
};
bool DispatchingSyncMessage() const;
int DispatchingSyncMessagePriority() const;
// If a sync message times out, we store its sequence number here. Any
// future sync messages will fail immediately. Once the reply for original
@@ -657,14 +597,6 @@ class MessageChannel : HasResultCodes
int32_t mTimedOutMessageSeqno;
int mTimedOutMessagePriority;
// If waiting for the reply to a sync out-message, it will be saved here
// on the I/O thread and then read and cleared by the worker thread.
nsAutoPtr<Message> mRecvd;
// If a sync message reply that is an error arrives, we increment this
// counter rather than storing it in mRecvd.
size_t mRecvdErrors;
// Queue of all incoming messages, except for replies to sync and urgent
// messages, which are delivered directly to mRecvd, and any pending urgent
// incall, which is stored in mPendingUrgentRequest.
+27
View File
@@ -81,6 +81,33 @@ class MessageListener
MOZ_CRASH("Intentional IPDL crash");
}
// The code here is only useful for fuzzing. It should not be used for any
// other purpose.
#ifdef DEBUG
// Returns true if we should simulate a timeout.
// WARNING: This is a testing-only function that is called with the
// MessageChannel monitor held. Don't do anything fancy here or we could
// deadlock.
virtual bool ArtificialTimeout() {
return false;
}
// Returns true if we want to cause the worker thread to sleep with the
// monitor unlocked.
virtual bool NeedArtificialSleep() {
return false;
}
// This function should be implemented to sleep for some amount of time on
// the worker thread. Will only be called if NeedArtificialSleep() returns
// true.
virtual void ArtificialSleep() {}
#else
bool ArtificialTimeout() { return false; }
bool NeedArtificialSleep() { return false; }
void ArtificialSleep() {}
#endif
virtual void OnEnteredCxxStack() {
NS_RUNTIMEABORT("default impl shouldn't be invoked");
}
+21
View File
@@ -0,0 +1,21 @@
namespace mozilla {
namespace _ipdltest {
prio(normal upto urgent) sync protocol PTestDemon
{
child:
async Start();
both:
async AsyncMessage(int n);
prio(high) sync HiPrioSyncMessage();
parent:
sync SyncMessage(int n);
prio(urgent) async UrgentAsyncMessage(int n);
prio(urgent) sync UrgentSyncMessage(int n);
};
} // namespace _ipdltest
} // namespace mozilla
+420
View File
@@ -0,0 +1,420 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=4 ts=4 et :
*/
#include "TestDemon.h"
#include <stdlib.h>
#include <sys/time.h>
#include "IPDLUnitTests.h" // fail etc.
#if defined(OS_POSIX)
#include <unistd.h>
#else
#include <windows.h>
#endif
template<>
struct RunnableMethodTraits<mozilla::_ipdltest::TestDemonParent>
{
static void RetainCallee(mozilla::_ipdltest::TestDemonParent* obj) { }
static void ReleaseCallee(mozilla::_ipdltest::TestDemonParent* obj) { }
};
template<>
struct RunnableMethodTraits<mozilla::_ipdltest::TestDemonChild>
{
static void RetainCallee(mozilla::_ipdltest::TestDemonChild* obj) { }
static void ReleaseCallee(mozilla::_ipdltest::TestDemonChild* obj) { }
};
namespace mozilla {
namespace _ipdltest {
const int kMaxStackHeight = 4;
static LazyLogModule sLogModule("demon");
#define DEMON_LOG(args...) MOZ_LOG(sLogModule, LogLevel::Debug, (args))
static int gStackHeight = 0;
static bool gFlushStack = false;
static int
Choose(int count)
{
return random() % count;
}
//-----------------------------------------------------------------------------
// parent
TestDemonParent::TestDemonParent()
: mDone(false),
mIncoming(),
mOutgoing()
{
MOZ_COUNT_CTOR(TestDemonParent);
}
TestDemonParent::~TestDemonParent()
{
MOZ_COUNT_DTOR(TestDemonParent);
}
void
TestDemonParent::Main()
{
if (!getenv("MOZ_TEST_IPC_DEMON")) {
QuitParent();
return;
}
srandom(time(nullptr));
DEMON_LOG("Start demon");
if (!SendStart())
fail("sending Start");
RunUnlimitedSequence();
}
#ifdef DEBUG
bool
TestDemonParent::ShouldContinueFromReplyTimeout()
{
return Choose(2) == 0;
}
bool
TestDemonParent::ArtificialTimeout()
{
return Choose(5) == 0;
}
void
TestDemonParent::ArtificialSleep()
{
if (Choose(2) == 0) {
// Sleep for anywhere from 0 to 100 milliseconds.
unsigned micros = Choose(100) * 1000;
#ifdef OS_POSIX
usleep(micros);
#else
Sleep(micros / 1000);
#endif
}
}
#endif
bool
TestDemonParent::RecvAsyncMessage(const int& n)
{
DEMON_LOG("Start RecvAsync [%d]", n);
MOZ_ASSERT(n == mIncoming[0]);
mIncoming[0]++;
RunLimitedSequence();
DEMON_LOG("End RecvAsync [%d]", n);
return true;
}
bool
TestDemonParent::RecvHiPrioSyncMessage()
{
DEMON_LOG("Start RecvHiPrioSyncMessage");
RunLimitedSequence();
DEMON_LOG("End RecvHiPrioSyncMessage");
return true;
}
bool
TestDemonParent::RecvSyncMessage(const int& n)
{
DEMON_LOG("Start RecvSync [%d]", n);
MOZ_ASSERT(n == mIncoming[0]);
mIncoming[0]++;
RunLimitedSequence(ASYNC_ONLY);
DEMON_LOG("End RecvSync [%d]", n);
return true;
}
bool
TestDemonParent::RecvUrgentAsyncMessage(const int& n)
{
DEMON_LOG("Start RecvUrgentAsyncMessage [%d]", n);
MOZ_ASSERT(n == mIncoming[2]);
mIncoming[2]++;
RunLimitedSequence(ASYNC_ONLY);
DEMON_LOG("End RecvUrgentAsyncMessage [%d]", n);
return true;
}
bool
TestDemonParent::RecvUrgentSyncMessage(const int& n)
{
DEMON_LOG("Start RecvUrgentSyncMessage [%d]", n);
MOZ_ASSERT(n == mIncoming[2]);
mIncoming[2]++;
RunLimitedSequence(ASYNC_ONLY);
DEMON_LOG("End RecvUrgentSyncMessage [%d]", n);
return true;
}
void
TestDemonParent::RunUnlimitedSequence()
{
if (mDone) {
return;
}
gFlushStack = false;
DoAction();
MessageLoop::current()->PostTask(FROM_HERE,
NewRunnableMethod(this, &TestDemonParent::RunUnlimitedSequence));
}
void
TestDemonParent::RunLimitedSequence(int flags)
{
if (gStackHeight >= kMaxStackHeight) {
return;
}
gStackHeight++;
int count = Choose(20);
for (int i = 0; i < count; i++) {
if (!DoAction(flags)) {
gFlushStack = true;
}
if (gFlushStack) {
gStackHeight--;
return;
}
}
gStackHeight--;
}
static bool
AllowAsync(int outgoing, int incoming)
{
return incoming >= outgoing - 5;
}
bool
TestDemonParent::DoAction(int flags)
{
if (flags & ASYNC_ONLY) {
if (AllowAsync(mOutgoing[0], mIncoming[0])) {
DEMON_LOG("SendAsyncMessage [%d]", mOutgoing[0]);
return SendAsyncMessage(mOutgoing[0]++);
} else {
return true;
}
} else {
switch (Choose(3)) {
case 0:
if (AllowAsync(mOutgoing[0], mIncoming[0])) {
DEMON_LOG("SendAsyncMessage [%d]", mOutgoing[0]);
return SendAsyncMessage(mOutgoing[0]++);
} else {
return true;
}
case 1: {
DEMON_LOG("Start SendHiPrioSyncMessage");
bool r = SendHiPrioSyncMessage();
DEMON_LOG("End SendHiPrioSyncMessage result=%d", r);
return r;
}
case 2:
DEMON_LOG("Cancel");
GetIPCChannel()->CancelCurrentTransaction();
return true;
}
}
MOZ_CRASH();
return false;
}
//-----------------------------------------------------------------------------
// child
TestDemonChild::TestDemonChild()
: mIncoming(),
mOutgoing()
{
MOZ_COUNT_CTOR(TestDemonChild);
}
TestDemonChild::~TestDemonChild()
{
MOZ_COUNT_DTOR(TestDemonChild);
}
bool
TestDemonChild::RecvStart()
{
srandom(time(nullptr));
DEMON_LOG("RecvStart");
RunUnlimitedSequence();
return true;
}
#ifdef DEBUG
void
TestDemonChild::ArtificialSleep()
{
if (Choose(2) == 0) {
// Sleep for anywhere from 0 to 100 milliseconds.
unsigned micros = Choose(100) * 1000;
#ifdef OS_POSIX
usleep(micros);
#else
Sleep(micros / 1000);
#endif
}
}
#endif
bool
TestDemonChild::RecvAsyncMessage(const int& n)
{
DEMON_LOG("Start RecvAsyncMessage [%d]", n);
MOZ_ASSERT(n == mIncoming[0]);
mIncoming[0]++;
RunLimitedSequence();
DEMON_LOG("End RecvAsyncMessage [%d]", n);
return true;
}
bool
TestDemonChild::RecvHiPrioSyncMessage()
{
DEMON_LOG("Start RecvHiPrioSyncMessage");
RunLimitedSequence();
DEMON_LOG("End RecvHiPrioSyncMessage");
return true;
}
void
TestDemonChild::RunUnlimitedSequence()
{
gFlushStack = false;
DoAction();
MessageLoop::current()->PostTask(FROM_HERE,
NewRunnableMethod(this, &TestDemonChild::RunUnlimitedSequence));
}
void
TestDemonChild::RunLimitedSequence()
{
if (gStackHeight >= kMaxStackHeight) {
return;
}
gStackHeight++;
int count = Choose(20);
for (int i = 0; i < count; i++) {
if (!DoAction()) {
gFlushStack = true;
}
if (gFlushStack) {
gStackHeight--;
return;
}
}
gStackHeight--;
}
bool
TestDemonChild::DoAction()
{
switch (Choose(6)) {
case 0:
if (AllowAsync(mOutgoing[0], mIncoming[0])) {
DEMON_LOG("SendAsyncMessage [%d]", mOutgoing[0]);
return SendAsyncMessage(mOutgoing[0]++);
} else {
return true;
}
case 1: {
DEMON_LOG("Start SendHiPrioSyncMessage");
bool r = SendHiPrioSyncMessage();
DEMON_LOG("End SendHiPrioSyncMessage result=%d", r);
return r;
}
case 2: {
DEMON_LOG("Start SendSyncMessage [%d]", mOutgoing[0]);
bool r = SendSyncMessage(mOutgoing[0]++);
switch (GetIPCChannel()->LastSendError()) {
case SyncSendError::PreviousTimeout:
case SyncSendError::SendingCPOWWhileDispatchingSync:
case SyncSendError::SendingCPOWWhileDispatchingUrgent:
case SyncSendError::NotConnectedBeforeSend:
case SyncSendError::CancelledBeforeSend:
mOutgoing[0]--;
break;
default:
break;
}
DEMON_LOG("End SendSyncMessage result=%d", r);
return r;
}
case 3:
DEMON_LOG("SendUrgentAsyncMessage [%d]", mOutgoing[2]);
return SendUrgentAsyncMessage(mOutgoing[2]++);
case 4: {
DEMON_LOG("Start SendUrgentSyncMessage [%d]", mOutgoing[2]);
bool r = SendUrgentSyncMessage(mOutgoing[2]++);
switch (GetIPCChannel()->LastSendError()) {
case SyncSendError::PreviousTimeout:
case SyncSendError::SendingCPOWWhileDispatchingSync:
case SyncSendError::SendingCPOWWhileDispatchingUrgent:
case SyncSendError::NotConnectedBeforeSend:
case SyncSendError::CancelledBeforeSend:
mOutgoing[2]--;
break;
default:
break;
}
DEMON_LOG("End SendUrgentSyncMessage result=%d", r);
return r;
}
case 5:
DEMON_LOG("Cancel");
GetIPCChannel()->CancelCurrentTransaction();
return true;
}
MOZ_CRASH();
return false;
}
} // namespace _ipdltest
} // namespace mozilla
+106
View File
@@ -0,0 +1,106 @@
#ifndef mozilla__ipdltest_TestDemon_h
#define mozilla__ipdltest_TestDemon_h 1
#include "mozilla/_ipdltest/IPDLUnitTests.h"
#include "mozilla/_ipdltest/PTestDemonParent.h"
#include "mozilla/_ipdltest/PTestDemonChild.h"
using namespace mozilla::ipc;
namespace mozilla {
namespace _ipdltest {
class TestDemonParent :
public PTestDemonParent
{
public:
TestDemonParent();
virtual ~TestDemonParent();
static bool RunTestInProcesses() { return true; }
static bool RunTestInThreads() { return true; }
void Main();
#ifdef DEBUG
bool ShouldContinueFromReplyTimeout() override;
bool ArtificialTimeout() override;
bool NeedArtificialSleep() override { return true; }
void ArtificialSleep() override;
#endif
bool RecvAsyncMessage(const int& n) override;
bool RecvHiPrioSyncMessage() override;
bool RecvSyncMessage(const int& n) override;
bool RecvUrgentAsyncMessage(const int& n) override;
bool RecvUrgentSyncMessage(const int& n) override;
virtual void ActorDestroy(ActorDestroyReason why) override
{
mDone = true;
printf("Parent ActorDestroy\n");
passed("ok");
QuitParent();
}
private:
bool mDone;
int mIncoming[3];
int mOutgoing[3];
enum {
ASYNC_ONLY = 1,
};
void RunUnlimitedSequence();
void RunLimitedSequence(int flags = 0);
bool DoAction(int flags = 0);
};
class TestDemonChild :
public PTestDemonChild
{
public:
TestDemonChild();
virtual ~TestDemonChild();
bool RecvStart() override;
#ifdef DEBUG
bool NeedArtificialSleep() override { return true; }
void ArtificialSleep() override;
#endif
bool RecvAsyncMessage(const int& n) override;
bool RecvHiPrioSyncMessage() override;
virtual void ActorDestroy(ActorDestroyReason why) override
{
_exit(0);
}
virtual void IntentionalCrash() override
{
_exit(0);
}
private:
int mIncoming[3];
int mOutgoing[3];
void RunUnlimitedSequence();
void RunLimitedSequence();
bool DoAction();
};
} // namespace _ipdltest
} // namespace mozilla
#endif // ifndef mozilla__ipdltest_TestDemon_h
+2
View File
@@ -20,6 +20,7 @@ SOURCES += [
'TestCancel.cpp',
'TestCrashCleanup.cpp',
'TestDataStructures.cpp',
'TestDemon.cpp',
'TestDesc.cpp',
'TestEndpointBridgeMain.cpp',
'TestEndpointOpens.cpp',
@@ -73,6 +74,7 @@ IPDL_SOURCES += [
'PTestDataStructures.ipdl',
'PTestDataStructuresCommon.ipdlh',
'PTestDataStructuresSub.ipdl',
'PTestDemon.ipdl',
'PTestDesc.ipdl',
'PTestDescSub.ipdl',
'PTestDescSubsub.ipdl',
+26 -43
View File
@@ -3606,10 +3606,12 @@ IsLiteralOrConstInt(FunctionValidator& f, ParseNode* pn, uint32_t* u32)
}
static const int32_t NoMask = -1;
static const bool YesSimd = true;
static const bool NoSimd = false;
static bool
CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
Scalar::Type* viewType, int32_t* mask)
bool isSimd, Scalar::Type* viewType, int32_t* mask)
{
if (!viewName->isKind(PNK_NAME))
return f.fail(viewName, "base of array access must be a typed array view name");
@@ -3623,7 +3625,8 @@ CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr
uint32_t index;
if (IsLiteralOrConstInt(f, indexExpr, &index)) {
uint64_t byteOffset = uint64_t(index) << TypedArrayShift(*viewType);
if (!f.m().tryConstantAccess(byteOffset, TypedArrayElemSize(*viewType)))
uint64_t width = isSimd ? Simd128DataSize : TypedArrayElemSize(*viewType);
if (!f.m().tryConstantAccess(byteOffset, width))
return f.fail(indexExpr, "constant index out of range");
*mask = NoMask;
@@ -3655,12 +3658,12 @@ CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr
if (!pointerType.isIntish())
return f.failf(pointerNode, "%s is not a subtype of int", pointerType.toChars());
} else {
// For legacy compatibility, accept Int8/Uint8 accesses with no shift.
// For SIMD access, and legacy scalar access compatibility, accept
// Int8/Uint8 accesses with no shift.
if (TypedArrayShift(*viewType) != 0)
return f.fail(indexExpr, "index expression isn't shifted; must be an Int8/Uint8 access");
MOZ_ASSERT(*mask == NoMask);
bool folded = false;
ParseNode* pointerNode = indexExpr;
@@ -3668,7 +3671,7 @@ CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr
if (!CheckExpr(f, pointerNode, &pointerType))
return false;
if (folded) {
if (isSimd) {
if (!pointerType.isIntish())
return f.failf(pointerNode, "%s is not a subtype of intish", pointerType.toChars());
} else {
@@ -3682,7 +3685,7 @@ CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr
static bool
CheckAndPrepareArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
Scalar::Type* viewType, int32_t* mask)
bool isSimd, Scalar::Type* viewType)
{
// asm.js doesn't have constant offsets, so just encode a 0.
if (!f.encoder().writeVarU32(0))
@@ -3696,7 +3699,8 @@ CheckAndPrepareArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode*
if (!f.encoder().writePatchableExpr(&prepareAt))
return false;
if (!CheckArrayAccess(f, viewName, indexExpr, viewType, mask))
int32_t mask;
if (!CheckArrayAccess(f, viewName, indexExpr, isSimd, viewType, &mask))
return false;
// asm.js only has naturally-aligned accesses.
@@ -3704,9 +3708,9 @@ CheckAndPrepareArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode*
// Don't generate the mask op if there is no need for it which could happen for
// a shift of zero or a SIMD access.
if (*mask != NoMask) {
if (mask != NoMask) {
f.encoder().patchExpr(prepareAt, Expr::I32And);
return f.writeInt32Lit(*mask);
return f.writeInt32Lit(mask);
}
f.encoder().patchExpr(prepareAt, Expr::Id);
@@ -3717,13 +3721,12 @@ static bool
CheckLoadArray(FunctionValidator& f, ParseNode* elem, Type* type)
{
Scalar::Type viewType;
int32_t mask;
size_t opcodeAt;
if (!f.encoder().writePatchableExpr(&opcodeAt))
return false;
if (!CheckAndPrepareArrayAccess(f, ElemBase(elem), ElemIndex(elem), &viewType, &mask))
if (!CheckAndPrepareArrayAccess(f, ElemBase(elem), ElemIndex(elem), NoSimd, &viewType))
return false;
switch (viewType) {
@@ -3767,8 +3770,7 @@ CheckStoreArray(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type
return false;
Scalar::Type viewType;
int32_t mask;
if (!CheckAndPrepareArrayAccess(f, ElemBase(lhs), ElemIndex(lhs), &viewType, &mask))
if (!CheckAndPrepareArrayAccess(f, ElemBase(lhs), ElemIndex(lhs), NoSimd, &viewType))
return false;
Type rhsType;
@@ -4063,9 +4065,9 @@ CheckMathMinMax(FunctionValidator& f, ParseNode* callNode, bool isMax, Type* typ
static bool
CheckSharedArrayAtomicAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
Scalar::Type* viewType, int32_t* mask)
Scalar::Type* viewType)
{
if (!CheckAndPrepareArrayAccess(f, viewName, indexExpr, viewType, mask))
if (!CheckAndPrepareArrayAccess(f, viewName, indexExpr, NoSimd, viewType))
return false;
// The global will be sane, CheckArrayAccess checks it.
@@ -4121,8 +4123,7 @@ CheckAtomicsLoad(FunctionValidator& f, ParseNode* call, Type* type)
return false;
Scalar::Type viewType;
int32_t mask;
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &mask))
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
return false;
f.encoder().patchU8(viewTypeAt, uint8_t(viewType));
@@ -4146,8 +4147,7 @@ CheckAtomicsStore(FunctionValidator& f, ParseNode* call, Type* type)
return false;
Scalar::Type viewType;
int32_t mask;
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &mask))
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
return false;
Type rhsType;
@@ -4180,8 +4180,7 @@ CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, AtomicOp op
return false;
Scalar::Type viewType;
int32_t mask;
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &mask))
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
return false;
Type valueArgType;
@@ -4229,8 +4228,7 @@ CheckAtomicsCompareExchange(FunctionValidator& f, ParseNode* call, Type* type)
return false;
Scalar::Type viewType;
int32_t mask;
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &mask))
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
return false;
Type oldValueArgType;
@@ -4268,8 +4266,7 @@ CheckAtomicsExchange(FunctionValidator& f, ParseNode* call, Type* type)
return false;
Scalar::Type viewType;
int32_t mask;
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &mask))
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
return false;
Type valueArgType;
@@ -5117,28 +5114,14 @@ CheckSimdLoadStoreArgs(FunctionValidator& f, ParseNode* call)
if (!view->isKind(PNK_NAME))
return f.fail(view, "expected Uint8Array view as SIMD.*.load/store first argument");
const ModuleValidator::Global* global = f.lookupGlobal(view->name());
if (!global ||
global->which() != ModuleValidator::Global::ArrayView ||
global->viewType() != Scalar::Uint8)
{
return f.fail(view, "expected Uint8Array view as SIMD.*.load/store first argument");
}
ParseNode* indexExpr = NextNode(view);
uint32_t indexLit;
if (IsLiteralOrConstInt(f, indexExpr, &indexLit)) {
if (!f.m().tryConstantAccess(indexLit, Simd128DataSize))
return f.fail(indexExpr, "constant index out of range");
return f.writeInt32Lit(indexLit);
}
Type indexType;
if (!CheckExpr(f, indexExpr, &indexType))
Scalar::Type viewType;
if (!CheckAndPrepareArrayAccess(f, view, indexExpr, YesSimd, &viewType))
return false;
if (!indexType.isIntish())
return f.failf(indexExpr, "%s is not a subtype of intish", indexType.toChars());
if (viewType != Scalar::Uint8)
return f.fail(view, "expected Uint8Array view as SIMD.*.load/store first argument");
return true;
}
+257 -167
View File
@@ -18,6 +18,8 @@
#include "asmjs/Wasm.h"
#include "mozilla/CheckedInt.h"
#include "jsprf.h"
#include "asmjs/WasmGenerator.h"
@@ -30,6 +32,7 @@
using namespace js;
using namespace js::wasm;
using mozilla::CheckedInt;
using mozilla::IsNaN;
typedef Handle<WasmModuleObject*> HandleWasmModule;
@@ -65,11 +68,12 @@ class FunctionDecoder
ModuleGenerator& mg_;
FunctionGenerator& fg_;
uint32_t funcIndex_;
uint32_t blockDepth_;
public:
FunctionDecoder(JSContext* cx, Decoder& d, ModuleGenerator& mg, FunctionGenerator& fg,
uint32_t funcIndex)
: cx_(cx), d_(d), mg_(mg), fg_(fg), funcIndex_(funcIndex)
: cx_(cx), d_(d), mg_(mg), fg_(fg), funcIndex_(funcIndex), blockDepth_(0)
{}
JSContext* cx() const { return cx_; }
Decoder& d() const { return d_; }
@@ -81,6 +85,18 @@ class FunctionDecoder
bool fail(const char* str) {
return Fail(cx_, d_, str);
}
MOZ_WARN_UNUSED_RESULT bool pushLabel() {
++blockDepth_;
return blockDepth_ != 0;
}
void popLabel() {
MOZ_ASSERT(blockDepth_ != 0);
--blockDepth_;
}
MOZ_WARN_UNUSED_RESULT bool isLabelInBounds(uint32_t depth) {
return depth < blockDepth_;
}
};
static bool
@@ -288,8 +304,16 @@ DecodeSetLocal(FunctionDecoder& f, ExprType expected)
}
static bool
DecodeBlock(FunctionDecoder& f, ExprType expected)
DecodeBlock(FunctionDecoder& f, bool isLoop, ExprType expected)
{
if (!f.pushLabel())
return f.fail("nesting overflow");
if (isLoop) {
if (!f.pushLabel())
return f.fail("nesting overflow");
}
uint32_t numExprs;
if (!f.d().readVarU32(&numExprs))
return f.fail("unable to read block's number of expressions");
@@ -306,6 +330,11 @@ DecodeBlock(FunctionDecoder& f, ExprType expected)
return false;
}
if (isLoop)
f.popLabel();
f.popLabel();
return true;
}
@@ -381,10 +410,37 @@ static bool
DecodeStore(FunctionDecoder& f, ExprType expected, ExprType type)
{
return DecodeLoadStoreAddress(f) &&
DecodeExpr(f, expected) &&
DecodeExpr(f, type) &&
CheckType(f, type, expected);
}
static bool
DecodeBr(FunctionDecoder& f, ExprType expected)
{
uint32_t relativeDepth;
if (!f.d().readVarU32(&relativeDepth))
return false;
if (!f.isLabelInBounds(relativeDepth))
return f.fail("branch depth exceeds current nesting level");
return CheckType(f, ExprType::Void, expected);
}
static bool
DecodeBrIf(FunctionDecoder& f, ExprType expected)
{
uint32_t relativeDepth;
if (!f.d().readVarU32(&relativeDepth))
return false;
if (!f.isLabelInBounds(relativeDepth))
return f.fail("branch depth exceeds current nesting level");
return CheckType(f, ExprType::Void, expected) &&
DecodeExpr(f, ExprType::I32);
}
static bool
DecodeReturn(FunctionDecoder& f)
{
@@ -421,7 +477,9 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
case Expr::SetLocal:
return DecodeSetLocal(f, expected);
case Expr::Block:
return DecodeBlock(f, expected);
return DecodeBlock(f, /* isLoop */ false, expected);
case Expr::Loop:
return DecodeBlock(f, /* isLoop */ true, expected);
case Expr::If:
return DecodeIfElse(f, /* hasElse */ false, expected);
case Expr::IfElse:
@@ -538,8 +596,7 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
case Expr::F64Ge:
return DecodeComparisonOperator(f, expected, ExprType::F64);
case Expr::I32WrapI64:
return f.fail("NYI: i64") &&
DecodeConversionOperator(f, expected, ExprType::I32, ExprType::I64);
return DecodeConversionOperator(f, expected, ExprType::I32, ExprType::I64);
case Expr::I32TruncSF32:
case Expr::I32TruncUF32:
return DecodeConversionOperator(f, expected, ExprType::I32, ExprType::F32);
@@ -550,17 +607,15 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
return DecodeConversionOperator(f, expected, ExprType::I32, ExprType::F64);
case Expr::I64ExtendSI32:
case Expr::I64ExtendUI32:
return f.fail("NYI: i64") &&
DecodeConversionOperator(f, expected, ExprType::I64, ExprType::I32);
return DecodeConversionOperator(f, expected, ExprType::I64, ExprType::I32);
case Expr::I64TruncSF32:
case Expr::I64TruncUF32:
return f.fail("NYI: i64") &&
DecodeConversionOperator(f, expected, ExprType::I64, ExprType::F32);
return DecodeConversionOperator(f, expected, ExprType::I64, ExprType::F32);
case Expr::I64TruncSF64:
case Expr::I64TruncUF64:
return DecodeConversionOperator(f, expected, ExprType::I64, ExprType::F64);
case Expr::I64ReinterpretF64:
return f.fail("NYI: i64") &&
DecodeConversionOperator(f, expected, ExprType::I64, ExprType::F64);
return f.fail("NYI: i64");
case Expr::F32ConvertSI32:
case Expr::F32ConvertUI32:
return DecodeConversionOperator(f, expected, ExprType::F32, ExprType::I32);
@@ -595,7 +650,8 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
case Expr::I64LoadMem16U:
case Expr::I64LoadMem32S:
case Expr::I64LoadMem32U:
return DecodeLoad(f, expected, ExprType::I64);
return f.fail("NYI: i64") &&
DecodeLoad(f, expected, ExprType::I64);
case Expr::F32LoadMem:
return DecodeLoad(f, expected, ExprType::F32);
case Expr::F64LoadMem:
@@ -614,6 +670,10 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
return DecodeStore(f, expected, ExprType::F32);
case Expr::F64StoreMem:
return DecodeStore(f, expected, ExprType::F64);
case Expr::Br:
return DecodeBr(f, expected);
case Expr::BrIf:
return DecodeBrIf(f, expected);
case Expr::Return:
return DecodeReturn(f);
default:
@@ -623,40 +683,6 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
return f.fail("bad expression code");
}
static bool
DecodeFuncBody(JSContext* cx, Decoder& d, ModuleGenerator& mg, FunctionGenerator& fg,
uint32_t funcIndex)
{
const uint8_t* bodyBegin = d.currentPosition();
FunctionDecoder f(cx, d, mg, fg, funcIndex);
uint32_t numExprs;
if (!d.readVarU32(&numExprs))
return Fail(cx, d, "expected number of function body expressions");
if (numExprs) {
for (size_t i = 0; i < numExprs - 1; i++) {
if (!DecodeExpr(f, ExprType::Void))
return false;
}
if (!DecodeExpr(f, f.ret()))
return false;
} else {
if (!CheckType(f, ExprType::Void, f.ret()))
return false;
}
const uint8_t* bodyEnd = d.currentPosition();
uintptr_t bodyLength = bodyEnd - bodyBegin;
if (!fg.bytecode().resize(bodyLength))
return false;
memcpy(fg.bytecode().begin(), bodyBegin, bodyLength);
return true;
}
/*****************************************************************************/
// dynamic link data
@@ -683,12 +709,11 @@ typedef HashSet<const DeclaredSig*, SigHashPolicy> SigSet;
static bool
DecodeSignatureSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
{
if (!d.readCStringIf(SigLabel))
return true;
uint32_t sectionStart;
if (!d.startSection(&sectionStart))
return Fail(cx, d, "expected signature section byte size");
if (!d.startSection(SigLabel, &sectionStart))
return Fail(cx, d, "failed to start section");
if (sectionStart == Decoder::NotStarted)
return true;
uint32_t numSigs;
if (!d.readVarU32(&numSigs))
@@ -761,12 +786,11 @@ DecodeSignatureIndex(JSContext* cx, Decoder& d, const ModuleGeneratorData& init,
static bool
DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
{
if (!d.readCStringIf(DeclLabel))
return true;
uint32_t sectionStart;
if (!d.startSection(&sectionStart))
return Fail(cx, d, "expected decl section byte size");
if (!d.startSection(DeclLabel, &sectionStart))
return Fail(cx, d, "failed to start section");
if (sectionStart == Decoder::NotStarted)
return true;
uint32_t numDecls;
if (!d.readVarU32(&numDecls))
@@ -792,12 +816,11 @@ DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
static bool
DecodeTableSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
{
if (!d.readCStringIf(TableLabel))
return true;
uint32_t sectionStart;
if (!d.startSection(&sectionStart))
return Fail(cx, d, "expected table section byte size");
if (!d.startSection(TableLabel, &sectionStart))
return Fail(cx, d, "failed to start section");
if (sectionStart == Decoder::NotStarted)
return true;
if (!d.readVarU32(&init->numTableElems))
return Fail(cx, d, "expected number of table elems");
@@ -893,12 +916,11 @@ DecodeImport(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVec
static bool
DecodeImportSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVector* importNames)
{
if (!d.readCStringIf(ImportLabel))
return true;
uint32_t sectionStart;
if (!d.startSection(&sectionStart))
return Fail(cx, d, "expected import section byte size");
if (!d.startSection(ImportLabel, &sectionStart))
return Fail(cx, d, "failed to start section");
if (sectionStart == Decoder::NotStarted)
return true;
for (uint32_t i = 0; !d.readCStringIf(EndLabel); i++) {
if (i >= MaxImports)
@@ -921,34 +943,45 @@ static bool
DecodeMemorySection(JSContext* cx, Decoder& d, ModuleGenerator& mg,
MutableHandle<ArrayBufferObject*> heap)
{
if (!d.readCStringIf(MemoryLabel))
uint32_t sectionStart;
if (!d.startSection(MemoryLabel, &sectionStart))
return Fail(cx, d, "failed to start section");
if (sectionStart == Decoder::NotStarted)
return true;
uint32_t sectionStart;
if (!d.startSection(&sectionStart))
return Fail(cx, d, "expected memory section byte size");
if (!d.readCStringIf(InitialLabel))
return Fail(cx, d, "expected memory section initial field");
uint32_t initialHeapSize;
if (!d.readVarU32(&initialHeapSize))
uint32_t initialSizePages;
if (!d.readVarU32(&initialSizePages))
return Fail(cx, d, "expected initial memory size");
if (initialHeapSize < PageSize || initialHeapSize % PageSize != 0)
return Fail(cx, d, "initial memory size not a multiple of 0x10000");
if (initialHeapSize > INT32_MAX)
CheckedInt<int32_t> initialSize = initialSizePages;
initialSize *= PageSize;
if (!initialSize.isValid())
return Fail(cx, d, "initial memory size too big");
if (!d.readCStringIf(EndLabel))
return Fail(cx, d, "expected end field of memory section");
uint32_t maxSizePages;
if (!d.readVarU32(&maxSizePages))
return Fail(cx, d, "expected initial memory size");
CheckedInt<int32_t> maxSize = maxSizePages;
maxSize *= PageSize;
if (!maxSize.isValid())
return Fail(cx, d, "initial memory size too big");
uint8_t exported;
if (!d.readFixedU8(&exported))
return Fail(cx, d, "expected exported byte");
if (exported) {
UniqueChars fieldName = DuplicateString("memory");
if (!fieldName || !mg.addMemoryExport(Move(fieldName)))
return false;
}
if (!d.finishSection(sectionStart))
return Fail(cx, d, "memory section byte size mismatch");
bool signalsForOOB = CompileArgs(cx).useSignalHandlersForOOB;
heap.set(ArrayBufferObject::createForWasm(cx, initialHeapSize, signalsForOOB));
heap.set(ArrayBufferObject::createForWasm(cx, initialSize.value(), signalsForOOB));
if (!heap)
return false;
@@ -999,46 +1032,29 @@ DecodeFunctionExport(JSContext* cx, Decoder& d, ModuleGenerator& mg, CStringSet*
return mg.declareExport(Move(fieldName), funcIndex);
}
static bool
DecodeMemoryExport(JSContext* cx, Decoder& d, ModuleGenerator& mg, CStringSet* dupSet)
{
if (!mg.usesHeap())
return Fail(cx, d, "cannot export memory with no memory section");
UniqueChars fieldName = DecodeFieldName(cx, d, dupSet);
if (!fieldName)
return false;
return mg.addMemoryExport(Move(fieldName));
}
static bool
DecodeExportsSection(JSContext* cx, Decoder& d, ModuleGenerator& mg)
{
if (!d.readCStringIf(ExportLabel))
return true;
uint32_t sectionStart;
if (!d.startSection(&sectionStart))
return Fail(cx, d, "expected export section byte size");
if (!d.startSection(ExportLabel, &sectionStart))
return Fail(cx, d, "failed to start section");
if (sectionStart == Decoder::NotStarted)
return true;
CStringSet dupSet(cx);
if (!dupSet.init())
return false;
for (uint32_t i = 0; !d.readCStringIf(EndLabel); i++) {
if (i >= MaxExports)
return Fail(cx, d, "too many exports");
uint32_t numExports;
if (!d.readVarU32(&numExports))
return false;
if (d.readCStringIf(FuncLabel)) {
if (!DecodeFunctionExport(cx, d, mg, &dupSet))
return false;
} else if (d.readCStringIf(MemoryLabel)) {
if (!DecodeMemoryExport(cx, d, mg, &dupSet))
return false;
} else {
return Fail(cx, d, "unexpected export subsection");
}
if (numExports > MaxExports)
return Fail(cx, d, "too many exports");
for (uint32_t i = 0; i < numExports; i++) {
if (!DecodeFunctionExport(cx, d, mg, &dupSet))
return false;
}
if (!d.finishSection(sectionStart))
@@ -1048,7 +1064,7 @@ DecodeExportsSection(JSContext* cx, Decoder& d, ModuleGenerator& mg)
}
static bool
DecodeFunctionSection(JSContext* cx, Decoder& d, ModuleGenerator& mg, uint32_t funcIndex)
DecodeFunctionBody(JSContext* cx, Decoder& d, ModuleGenerator& mg, uint32_t funcIndex)
{
int64_t before = PRMJ_Now();
@@ -1056,10 +1072,6 @@ DecodeFunctionSection(JSContext* cx, Decoder& d, ModuleGenerator& mg, uint32_t f
if (!mg.startFuncDef(d.currentOffset(), &fg))
return false;
uint32_t sectionStart;
if (!d.startSection(&sectionStart))
return Fail(cx, d, "expected func section byte size");
const DeclaredSig& sig = mg.funcSig(funcIndex);
for (ValType type : sig.args()) {
if (!fg.addLocal(type))
@@ -1078,11 +1090,35 @@ DecodeFunctionSection(JSContext* cx, Decoder& d, ModuleGenerator& mg, uint32_t f
return false;
}
if (!DecodeFuncBody(cx, d, mg, fg, funcIndex))
const uint8_t* bodyBegin = d.currentPosition();
FunctionDecoder f(cx, d, mg, fg, funcIndex);
uint32_t numExprs;
if (!d.readVarU32(&numExprs))
return Fail(cx, d, "expected number of function body expressions");
if (numExprs) {
for (size_t i = 0; i < numExprs - 1; i++) {
if (!DecodeExpr(f, ExprType::Void))
return false;
}
if (!DecodeExpr(f, f.ret()))
return false;
} else {
if (!CheckType(f, ExprType::Void, f.ret()))
return false;
}
MOZ_ASSERT(!f.isLabelInBounds(0));
const uint8_t* bodyEnd = d.currentPosition();
uintptr_t bodyLength = bodyEnd - bodyBegin;
if (!fg.bytecode().resize(bodyLength))
return false;
if (!d.finishSection(sectionStart))
return Fail(cx, d, "func section byte size mismatch");
memcpy(fg.bytecode().begin(), bodyBegin, bodyLength);
int64_t after = PRMJ_Now();
unsigned generateTime = (after - before) / PRMJ_USEC_PER_MSEC;
@@ -1091,43 +1127,45 @@ DecodeFunctionSection(JSContext* cx, Decoder& d, ModuleGenerator& mg, uint32_t f
}
static bool
DecodeFunctionSections(JSContext* cx, Decoder& d, ModuleGenerator& mg)
DecodeFunctionBodiesSection(JSContext* cx, Decoder& d, ModuleGenerator& mg)
{
if (!mg.startFuncDefs())
return false;
uint32_t funcIndex = 0;
uint32_t sectionStart;
if (!d.startSection(FuncLabel, &sectionStart))
return Fail(cx, d, "failed to start section");
for (; d.readCStringIf(FuncLabel); funcIndex++) {
if (funcIndex >= mg.numFuncSigs())
return Fail(cx, d, "more function definitions than declarations");
if (sectionStart == Decoder::NotStarted) {
if (mg.numFuncSigs() != 0)
return Fail(cx, d, "expected function bodies");
if (!DecodeFunctionSection(cx, d, mg, funcIndex))
return mg.finishFuncDefs();
}
for (uint32_t funcIndex = 0; funcIndex < mg.numFuncSigs(); funcIndex++) {
if (!DecodeFunctionBody(cx, d, mg, funcIndex))
return false;
}
if (funcIndex < mg.numFuncSigs())
return Fail(cx, d, "fewer function definitions than declarations");
if (!d.finishSection(sectionStart))
return Fail(cx, d, "function section byte size mismatch");
if (!mg.finishFuncDefs())
return false;
return true;
return mg.finishFuncDefs();
}
static bool
DecodeDataSection(JSContext* cx, Decoder& d, Handle<ArrayBufferObject*> heap)
{
if (!d.readCStringIf(DataLabel))
uint32_t sectionStart;
if (!d.startSection(DataLabel, &sectionStart))
return Fail(cx, d, "failed to start section");
if (sectionStart == Decoder::NotStarted)
return true;
if (!heap)
return Fail(cx, d, "data section requires a memory section");
uint32_t sectionStart;
if (!d.startSection(&sectionStart))
return Fail(cx, d, "expected data section byte size");
uint8_t* const heapBase = heap->dataPointer();
uint32_t const heapLength = heap->byteLength();
uint32_t prevEnd = 0;
@@ -1164,31 +1202,6 @@ DecodeDataSection(JSContext* cx, Decoder& d, Handle<ArrayBufferObject*> heap)
return true;
}
static bool
DecodeUnknownSection(JSContext* cx, Decoder& d)
{
UniqueChars sectionName = d.readCString();
if (!sectionName)
return Fail(cx, d, "failed to read section name");
if (!strcmp(sectionName.get(), SigLabel) ||
!strcmp(sectionName.get(), ImportLabel) ||
!strcmp(sectionName.get(), DeclLabel) ||
!strcmp(sectionName.get(), TableLabel) ||
!strcmp(sectionName.get(), MemoryLabel) ||
!strcmp(sectionName.get(), ExportLabel) ||
!strcmp(sectionName.get(), FuncLabel) ||
!strcmp(sectionName.get(), DataLabel))
{
return Fail(cx, d, "known section out of order");
}
if (!d.skipSection())
return Fail(cx, d, "unable to skip unknown section");
return true;
}
static bool
DecodeModule(JSContext* cx, UniqueChars file, const uint8_t* bytes, uint32_t length,
ImportNameVector* importNames, UniqueExportMap* exportMap,
@@ -1229,7 +1242,7 @@ DecodeModule(JSContext* cx, UniqueChars file, const uint8_t* bytes, uint32_t len
if (!DecodeExportsSection(cx, d, mg))
return false;
if (!DecodeFunctionSections(cx, d, mg))
if (!DecodeFunctionBodiesSection(cx, d, mg))
return false;
if (!DecodeDataSection(cx, d, heap))
@@ -1238,8 +1251,8 @@ DecodeModule(JSContext* cx, UniqueChars file, const uint8_t* bytes, uint32_t len
CacheableCharsVector funcNames;
while (!d.readCStringIf(EndLabel)) {
if (!DecodeUnknownSection(cx, d))
return false;
if (!d.skipSection())
return Fail(cx, d, "unable to skip unknown section");
}
if (!d.done())
@@ -1376,3 +1389,80 @@ wasm::Eval(JSContext* cx, Handle<ArrayBufferObject*> code,
return true;
}
static bool
InstantiateModule(JSContext* cx, unsigned argc, Value* vp)
{
MOZ_ASSERT(cx->runtime()->options().wasm());
CallArgs args = CallArgsFromVp(argc, vp);
if (!args.get(0).isObject() || !args.get(0).toObject().is<ArrayBufferObject>()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
return false;
}
RootedObject importObj(cx);
if (!args.get(1).isUndefined()) {
if (!args.get(1).isObject()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMPORT_ARG);
return false;
}
importObj = &args[1].toObject();
}
Rooted<ArrayBufferObject*> code(cx, &args[0].toObject().as<ArrayBufferObject>());
RootedObject exportObj(cx);
if (!Eval(cx, code, importObj, &exportObj))
return false;
args.rval().setObject(*exportObj);
return true;
}
#if JS_HAS_TOSOURCE
static bool
wasm_toSource(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setString(cx->names().Wasm);
return true;
}
#endif
static const JSFunctionSpec wasm_static_methods[] = {
#if JS_HAS_TOSOURCE
JS_FN(js_toSource_str, wasm_toSource, 0, 0),
#endif
JS_FN("instantiateModule", InstantiateModule, 1, 0),
JS_FS_END
};
const Class js::WasmClass = {
js_Wasm_str,
JSCLASS_HAS_CACHED_PROTO(JSProto_Wasm)
};
JSObject*
js::InitWasmClass(JSContext* cx, HandleObject global)
{
MOZ_ASSERT(cx->runtime()->options().wasm());
RootedObject proto(cx, global->as<GlobalObject>().getOrCreateObjectPrototype(cx));
if (!proto)
return nullptr;
RootedObject Wasm(cx, NewObjectWithGivenProto(cx, &WasmClass, proto, SingletonObject));
if (!Wasm)
return nullptr;
if (!JS_DefineProperty(cx, global, js_Wasm_str, Wasm, JSPROP_RESOLVING))
return nullptr;
if (!JS_DefineFunctions(cx, Wasm, wasm_static_methods))
return nullptr;
global->as<GlobalObject>().setConstructor(JSProto_Wasm, ObjectValue(*Wasm));
return Wasm;
}
+15 -4
View File
@@ -19,7 +19,10 @@
#ifndef wasm_h
#define wasm_h
#include "NamespaceImports.h"
#include "gc/Rooting.h"
#include "js/Class.h"
namespace js {
@@ -31,8 +34,8 @@ namespace wasm {
bool
HasCompilerSupport(ExclusiveContext* cx);
// The WebAssembly spec hard-codes the virtual page size to be 64KiB and limits
// forces the linear memory to always be a multiple of 64KiB.
// The WebAssembly spec hard-codes the virtual page size to be 64KiB and
// requires linear memory to always be a multiple of 64KiB.
static const unsigned PageSize = 64 * 1024;
// When signal handling is used for bounds checking, MappedSize bytes are
@@ -46,10 +49,18 @@ static const uint64_t MappedSize = 2 * Uint32Range + PageSize;
// Compiles the given binary wasm module given the ArrayBufferObject
// and links the module's imports with the given import object.
bool
Eval(JSContext* cx, JS::Handle<ArrayBufferObject*> code,
JS::HandleObject importObj, JS::MutableHandleObject exportObj);
Eval(JSContext* cx, Handle<ArrayBufferObject*> code, HandleObject importObj,
MutableHandleObject exportObj);
} // namespace wasm
// Initialization of the Wasm global object and its properties.
extern const Class WasmClass;
JSObject*
InitWasmClass(JSContext* cx, HandleObject global);
} // namespace js
#endif // namespace wasm_h
+188 -171
View File
@@ -40,7 +40,9 @@ static const unsigned MaxArgsPerFunc = 4 * 1024;
// Module header constants
static const uint32_t MagicNumber = 0x6d736100; // "\0asm"
static const uint32_t EncodingVersion = -1; // experimental
static const uint32_t EncodingVersion = 0xa; // will change while iterating toward release,
// change to 1 at release, and hopefully never
// change after that
// Names:
static const char SigLabel[] = "sig";
@@ -52,194 +54,198 @@ static const char ExportLabel[] = "export";
static const char FuncLabel[] = "func";
static const char DataLabel[] = "data";
static const char SegmentLabel[] = "segment";
static const char InitialLabel[] = "initial";
static const char EndLabel[] = "";
enum class Expr : uint16_t
{
// Control opcodes
Nop,
Block,
Loop,
If,
IfElse,
Select,
Br,
BrIf,
BrTable,
Return,
Unreachable,
Nop = 0x00,
Block = 0x01,
Loop = 0x02,
If = 0x03,
IfElse = 0x04,
Select = 0x05,
Br = 0x06,
BrIf = 0x07,
BrTable = 0x08,
Return = 0x14,
Unreachable = 0x15,
// Calls
Call,
CallIndirect,
CallImport,
Call = 0x12,
CallIndirect = 0x13,
CallImport = 0x09,
// Constants and calls
I32Const,
I64Const,
F64Const,
F32Const,
GetLocal,
SetLocal,
LoadGlobal,
StoreGlobal,
I32Const = 0x0a,
I64Const = 0x0b,
F64Const = 0x0c,
F32Const = 0x0d,
GetLocal = 0x0e,
SetLocal = 0x0f,
// I32 opcodes
I32Add,
I32Sub,
I32Mul,
I32DivS,
I32DivU,
I32RemS,
I32RemU,
I32Or,
I32And,
I32Xor,
I32Shl,
I32ShrU,
I32ShrS,
I32Eq,
I32Ne,
I32LtS,
I32LeS,
I32LtU,
I32LeU,
I32GtS,
I32GeS,
I32GtU,
I32GeU,
I32Clz,
I32Ctz,
I32Popcnt,
I32Add = 0x40,
I32Sub = 0x41,
I32Mul = 0x42,
I32DivS = 0x43,
I32DivU = 0x44,
I32RemS = 0x45,
I32RemU = 0x46,
I32Or = 0x48,
I32And = 0x47,
I32Xor = 0x49,
I32Shl = 0x4a,
I32ShrU = 0x4b,
I32ShrS = 0x4c,
I32Eq = 0x4d,
I32Ne = 0x4e,
I32LtS = 0x4f,
I32LeS = 0x50,
I32LtU = 0x51,
I32LeU = 0x52,
I32GtS = 0x53,
I32GeS = 0x54,
I32GtU = 0x55,
I32GeU = 0x56,
I32Clz = 0x57,
I32Ctz = 0x58,
I32Popcnt = 0x59,
// I64 opcodes
I64Add,
I64Sub,
I64Mul,
I64DivS,
I64DivU,
I64RemS,
I64RemU,
I64Or,
I64And,
I64Xor,
I64Shl,
I64ShrU,
I64ShrS,
I64Eq,
I64Ne,
I64LtS,
I64LeS,
I64LtU,
I64LeU,
I64GtS,
I64GeS,
I64GtU,
I64GeU,
I64Clz,
I64Ctz,
I64Popcnt,
I64Add = 0x5b,
I64Sub = 0x5c,
I64Mul = 0x5d,
I64DivS = 0x5e,
I64DivU = 0x5f,
I64RemS = 0x60,
I64RemU = 0x61,
I64Or = 0x63,
I64And = 0x62,
I64Xor = 0x64,
I64Shl = 0x65,
I64ShrU = 0x66,
I64ShrS = 0x67,
I64Eq = 0x68,
I64Ne = 0x69,
I64LtS = 0x6a,
I64LeS = 0x6b,
I64LtU = 0x6c,
I64LeU = 0x6d,
I64GtS = 0x6e,
I64GeS = 0x6f,
I64GtU = 0x70,
I64GeU = 0x71,
I64Clz = 0x72,
I64Ctz = 0x73,
I64Popcnt = 0x74,
// F32 opcodes
F32Add,
F32Sub,
F32Mul,
F32Div,
F32Min,
F32Max,
F32Abs,
F32Neg,
F32CopySign,
F32Ceil,
F32Floor,
F32Trunc,
F32Nearest,
F32Sqrt,
F32Eq,
F32Ne,
F32Lt,
F32Le,
F32Gt,
F32Ge,
F32Add = 0x75,
F32Sub = 0x76,
F32Mul = 0x77,
F32Div = 0x78,
F32Min = 0x79,
F32Max = 0x7a,
F32Abs = 0x7b,
F32Neg = 0x7c,
F32CopySign = 0x7d,
F32Ceil = 0x7e,
F32Floor = 0x7f,
F32Trunc = 0x80,
F32Nearest = 0x81,
F32Sqrt = 0x82,
F32Eq = 0x83,
F32Ne = 0x84,
F32Lt = 0x85,
F32Le = 0x86,
F32Gt = 0x87,
F32Ge = 0x88,
// F64 opcodes
F64Add,
F64Sub,
F64Mul,
F64Div,
F64Min,
F64Max,
F64Abs,
F64Neg,
F64CopySign,
F64Ceil,
F64Floor,
F64Trunc,
F64Nearest,
F64Sqrt,
F64Eq,
F64Ne,
F64Lt,
F64Le,
F64Gt,
F64Ge,
F64Add = 0x89,
F64Sub = 0x8a,
F64Mul = 0x8b,
F64Div = 0x8c,
F64Min = 0x8d,
F64Max = 0x8e,
F64Abs = 0x8f,
F64Neg = 0x90,
F64CopySign = 0x91,
F64Ceil = 0x92,
F64Floor = 0x93,
F64Trunc = 0x94,
F64Nearest = 0x95,
F64Sqrt = 0x96,
F64Eq = 0x97,
F64Ne = 0x98,
F64Lt = 0x99,
F64Le = 0x9a,
F64Gt = 0x9b,
F64Ge = 0x9c,
// Conversions
I32WrapI64,
I64ExtendSI32,
I64ExtendUI32,
I32TruncSF32,
I32TruncSF64,
I32TruncUF32,
I32TruncUF64,
I64TruncSF32,
I64TruncSF64,
I64TruncUF32,
I64TruncUF64,
F32ConvertSI32,
F32ConvertUI32,
F64ConvertSI32,
F64ConvertUI32,
F32ConvertSI64,
F32ConvertUI64,
F64ConvertSI64,
F64ConvertUI64,
F32DemoteF64,
F64PromoteF32,
I32ReinterpretF32,
F32ReinterpretI32,
I64ReinterpretF64,
F64ReinterpretI64,
I32WrapI64 = 0xa1,
I64ExtendSI32 = 0xa6,
I64ExtendUI32 = 0xa7,
I32TruncSF32 = 0x9d,
I32TruncSF64 = 0x9e,
I32TruncUF32 = 0x9f,
I32TruncUF64 = 0xa0,
I64TruncSF32 = 0xa2,
I64TruncSF64 = 0xa3,
I64TruncUF32 = 0xa4,
I64TruncUF64 = 0xa5,
F32ConvertSI32 = 0xa8,
F32ConvertUI32 = 0xa9,
F64ConvertSI32 = 0xae,
F64ConvertUI32 = 0xaf,
F32ConvertSI64 = 0xaa,
F32ConvertUI64 = 0xab,
F64ConvertSI64 = 0xb0,
F64ConvertUI64 = 0xb1,
F32DemoteF64 = 0xac,
F64PromoteF32 = 0xb2,
I32ReinterpretF32 = 0xb4,
F32ReinterpretI32 = 0xad,
I64ReinterpretF64 = 0xb5,
F64ReinterpretI64 = 0xb3,
// Load/store operations
I32LoadMem8S,
I32LoadMem8U,
I32LoadMem16S,
I32LoadMem16U,
I32LoadMem,
I64LoadMem8S,
I64LoadMem8U,
I64LoadMem16S,
I64LoadMem16U,
I64LoadMem32S,
I64LoadMem32U,
I64LoadMem,
F32LoadMem,
F64LoadMem,
I32LoadMem8S = 0x20,
I32LoadMem8U = 0x21,
I32LoadMem16S = 0x22,
I32LoadMem16U = 0x23,
I32LoadMem = 0x2a,
I64LoadMem8S = 0x24,
I64LoadMem8U = 0x25,
I64LoadMem16S = 0x26,
I64LoadMem16U = 0x27,
I64LoadMem32S = 0x28,
I64LoadMem32U = 0x29,
I64LoadMem = 0x2b,
F32LoadMem = 0x2c,
F64LoadMem = 0x2d,
I32StoreMem8,
I32StoreMem16,
I64StoreMem8,
I64StoreMem16,
I64StoreMem32,
I32StoreMem,
I64StoreMem,
F32StoreMem,
F64StoreMem,
I32StoreMem8 = 0x2e,
I32StoreMem16 = 0x2f,
I64StoreMem8 = 0x30,
I64StoreMem16 = 0x31,
I64StoreMem32 = 0x32,
I32StoreMem = 0x33,
I64StoreMem = 0x34,
F32StoreMem = 0x35,
F64StoreMem = 0x36,
// Special operators
MemorySize = 0x3b,
GrowMemory = 0x39,
// asm.js specific
Id,
Id = 0xc0, // Encodings from here down are specific to baldr.
LoadGlobal,
StoreGlobal,
I32Min,
I32Max,
@@ -443,8 +449,9 @@ class Encoder
// require backpatching since the size of the section is only known at the
// end while the size's uint32 must be stored at the beginning.
MOZ_WARN_UNUSED_RESULT bool startSection(size_t* offset) {
return writePatchableVarU32(offset);
MOZ_WARN_UNUSED_RESULT bool startSection(const char* name, size_t* offset) {
return writePatchableVarU32(offset) &&
writeCString(name);
}
void finishSection(size_t offset) {
return patchVarU32(offset, bytecode_.length() - offset - varU32ByteLength(offset));
@@ -668,10 +675,20 @@ class Decoder
// See "section" description in Encoder.
MOZ_WARN_UNUSED_RESULT bool startSection(uint32_t* startOffset) {
*startOffset = currentOffset();
uint32_t unused;
return readVarU32(&unused);
static const uint32_t NotStarted = UINT32_MAX;
MOZ_WARN_UNUSED_RESULT bool startSection(const char* name, uint32_t* startOffset) {
const uint8_t* before = cur_;
uint32_t numBytes;
if (!readVarU32(&numBytes) || bytesRemain() < numBytes)
return false;
if (!readCStringIf(name)) {
cur_ = before;
*startOffset = NotStarted;
return true;
}
*startOffset = before - beg_;
return true;
}
MOZ_WARN_UNUSED_RESULT bool finishSection(uint32_t startOffset) {
uint32_t currentOffset = cur_ - beg_;
+138 -79
View File
@@ -452,6 +452,25 @@ class FunctionCompiler
return ins;
}
MDefinition* extendI32(MDefinition* op, bool isUnsigned)
{
if (inDeadCode())
return nullptr;
MExtendInt32ToInt64* ins = MExtendInt32ToInt64::NewAsmJS(alloc(), op, isUnsigned);
curBlock_->add(ins);
return ins;
}
template <class T>
MDefinition* truncate(MDefinition* op, bool isUnsigned)
{
if (inDeadCode())
return nullptr;
T* ins = T::NewAsmJS(alloc(), op, isUnsigned);
curBlock_->add(ins);
return ins;
}
MDefinition* compare(MDefinition* lhs, MDefinition* rhs, JSOp op, MCompare::CompareType type)
{
if (inDeadCode())
@@ -468,46 +487,49 @@ class FunctionCompiler
curBlock_->setSlot(info().localSlot(slot), def);
}
MDefinition* loadHeap(Scalar::Type accessType, MDefinition* ptr)
MDefinition* loadHeap(MDefinition* base,
const MAsmJSHeapAccess& access)
{
if (inDeadCode())
return nullptr;
MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD loads should use loadSimdHeap");
MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), accessType, ptr);
MOZ_ASSERT(!Scalar::isSimdType(access.accessType()), "SIMD loads should use loadSimdHeap");
MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), base, access);
curBlock_->add(load);
return load;
}
MDefinition* loadSimdHeap(Scalar::Type accessType, MDefinition* ptr, unsigned numElems)
MDefinition* loadSimdHeap(MDefinition* base, const MAsmJSHeapAccess& access)
{
if (inDeadCode())
return nullptr;
MOZ_ASSERT(Scalar::isSimdType(accessType), "loadSimdHeap can only load from a SIMD view");
MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, numElems);
MOZ_ASSERT(Scalar::isSimdType(access.accessType()),
"loadSimdHeap can only load from a SIMD view");
MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), base, access);
curBlock_->add(load);
return load;
}
void storeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v)
void storeHeap(MDefinition* base, const MAsmJSHeapAccess& access, MDefinition* v)
{
if (inDeadCode())
return;
MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD stores should use storeSimdHeap");
MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v);
MOZ_ASSERT(!Scalar::isSimdType(access.accessType()),
"SIMD stores should use storeSimdHeap");
MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), base, access, v);
curBlock_->add(store);
}
void storeSimdHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v,
unsigned numElems)
void storeSimdHeap(MDefinition* base, const MAsmJSHeapAccess& access, MDefinition* v)
{
if (inDeadCode())
return;
MOZ_ASSERT(Scalar::isSimdType(accessType), "storeSimdHeap can only load from a SIMD view");
MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v, numElems);
MOZ_ASSERT(Scalar::isSimdType(access.accessType()),
"storeSimdHeap can only load from a SIMD view");
MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), base, access, v);
curBlock_->add(store);
}
@@ -519,59 +541,59 @@ class FunctionCompiler
curBlock_->add(ins);
}
MDefinition* atomicLoadHeap(Scalar::Type accessType, MDefinition* ptr)
MDefinition* atomicLoadHeap(MDefinition* base, const MAsmJSHeapAccess& access)
{
if (inDeadCode())
return nullptr;
MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, /* numElems */ 0,
MembarBeforeLoad, MembarAfterLoad);
MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), base, access);
curBlock_->add(load);
return load;
}
void atomicStoreHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v)
void atomicStoreHeap(MDefinition* base, const MAsmJSHeapAccess& access,
MDefinition* v)
{
if (inDeadCode())
return;
MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v,
/* numElems = */ 0,
MembarBeforeStore, MembarAfterStore);
MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), base, access, v);
curBlock_->add(store);
}
MDefinition* atomicCompareExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* oldv,
MDefinition* newv)
MDefinition* atomicCompareExchangeHeap(MDefinition* base, const MAsmJSHeapAccess& access,
MDefinition* oldv, MDefinition* newv)
{
if (inDeadCode())
return nullptr;
MAsmJSCompareExchangeHeap* cas =
MAsmJSCompareExchangeHeap::New(alloc(), accessType, ptr, oldv, newv);
MAsmJSCompareExchangeHeap::New(alloc(), base, access, oldv, newv);
curBlock_->add(cas);
return cas;
}
MDefinition* atomicExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* value)
MDefinition* atomicExchangeHeap(MDefinition* base, const MAsmJSHeapAccess& access,
MDefinition* value)
{
if (inDeadCode())
return nullptr;
MAsmJSAtomicExchangeHeap* cas =
MAsmJSAtomicExchangeHeap::New(alloc(), accessType, ptr, value);
MAsmJSAtomicExchangeHeap::New(alloc(), base, access, value);
curBlock_->add(cas);
return cas;
}
MDefinition* atomicBinopHeap(js::jit::AtomicOp op, Scalar::Type accessType, MDefinition* ptr,
MDefinition* atomicBinopHeap(js::jit::AtomicOp op,
MDefinition* base, const MAsmJSHeapAccess& access,
MDefinition* v)
{
if (inDeadCode())
return nullptr;
MAsmJSAtomicBinopHeap* binop =
MAsmJSAtomicBinopHeap::New(alloc(), op, accessType, ptr, v);
MAsmJSAtomicBinopHeap::New(alloc(), op, base, access, v);
curBlock_->add(binop);
return binop;
}
@@ -1340,26 +1362,32 @@ EmitLoadGlobal(FunctionCompiler& f, ExprType type, MDefinition** def)
static bool EmitExpr(FunctionCompiler&, ExprType, MDefinition**);
static bool
EmitLoadStoreAddress(FunctionCompiler& f, Scalar::Type viewType, uint32_t* offset,
uint32_t* align, MDefinition** base)
EmitHeapAddress(FunctionCompiler& f, MDefinition** base, MAsmJSHeapAccess* access)
{
*offset = f.readVarU32();
MOZ_ASSERT(*offset == 0, "Non-zero offsets not supported yet");
uint32_t offset = f.readVarU32();
MOZ_ASSERT(offset == 0, "Non-zero offsets not supported yet");
access->setOffset(offset);
*align = f.readVarU32();
uint32_t align = f.readVarU32();
access->setAlign(align);
if (!EmitExpr(f, ExprType::I32, base))
return false;
// TODO Remove this (and the viewType param) after implementing unaligned
// loads/stores.
if (f.mg().isAsmJS())
if (f.mg().isAsmJS()) {
MOZ_ASSERT(offset == 0 && "asm.js validation does not produce load/store offsets");
return true;
}
int32_t maskVal = ~(Scalar::byteSize(viewType) - 1);
int32_t maskVal = ~(Scalar::byteSize(access->accessType()) - 1);
if (maskVal == -1)
return true;
offset &= maskVal;
access->setOffset(offset);
MDefinition* mask = f.constant(Int32Value(maskVal), MIRType_Int32);
*base = f.bitwise<MBitAnd>(*base, mask, MIRType_Int32);
return true;
@@ -1368,20 +1396,20 @@ EmitLoadStoreAddress(FunctionCompiler& f, Scalar::Type viewType, uint32_t* offse
static bool
EmitLoad(FunctionCompiler& f, Scalar::Type viewType, MDefinition** def)
{
uint32_t offset, align;
MDefinition* ptr;
if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &ptr))
MDefinition* base;
MAsmJSHeapAccess access(viewType);
if (!EmitHeapAddress(f, &base, &access))
return false;
*def = f.loadHeap(viewType, ptr);
*def = f.loadHeap(base, access);
return true;
}
static bool
EmitStore(FunctionCompiler& f, Scalar::Type viewType, MDefinition** def)
{
uint32_t offset, align;
MDefinition* ptr;
if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &ptr))
MDefinition* base;
MAsmJSHeapAccess access(viewType);
if (!EmitHeapAddress(f, &base, &access))
return false;
MDefinition* rhs = nullptr;
@@ -1403,7 +1431,7 @@ EmitStore(FunctionCompiler& f, Scalar::Type viewType, MDefinition** def)
default: MOZ_CRASH("unexpected scalar type");
}
f.storeHeap(viewType, ptr, rhs);
f.storeHeap(base, access, rhs);
*def = rhs;
return true;
}
@@ -1412,9 +1440,9 @@ static bool
EmitStoreWithCoercion(FunctionCompiler& f, Scalar::Type rhsType, Scalar::Type viewType,
MDefinition **def)
{
uint32_t offset, align;
MDefinition* ptr;
if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &ptr))
MDefinition* base;
MAsmJSHeapAccess access(viewType);
if (!EmitHeapAddress(f, &base, &access))
return false;
MDefinition* rhs = nullptr;
@@ -1431,7 +1459,7 @@ EmitStoreWithCoercion(FunctionCompiler& f, Scalar::Type rhsType, Scalar::Type vi
MOZ_CRASH("unexpected coerced store");
}
f.storeHeap(viewType, ptr, coerced);
f.storeHeap(base, access, coerced);
*def = rhs;
return true;
}
@@ -1485,12 +1513,12 @@ EmitAtomicsLoad(FunctionCompiler& f, MDefinition** def)
{
Scalar::Type viewType = Scalar::Type(f.readU8());
uint32_t offset, align;
MDefinition* index;
if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &index))
MDefinition* base;
MAsmJSHeapAccess access(viewType, 0, MembarBeforeLoad, MembarAfterLoad);
if (!EmitHeapAddress(f, &base, &access))
return false;
*def = f.atomicLoadHeap(viewType, index);
*def = f.atomicLoadHeap(base, access);
return true;
}
@@ -1499,15 +1527,15 @@ EmitAtomicsStore(FunctionCompiler& f, MDefinition** def)
{
Scalar::Type viewType = Scalar::Type(f.readU8());
uint32_t offset, align;
MDefinition* index;
if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &index))
MDefinition* base;
MAsmJSHeapAccess access(viewType, 0, MembarBeforeStore, MembarAfterStore);
if (!EmitHeapAddress(f, &base, &access))
return false;
MDefinition* value;
if (!EmitExpr(f, ExprType::I32, &value))
return false;
f.atomicStoreHeap(viewType, index, value);
f.atomicStoreHeap(base, access, value);
*def = value;
return true;
}
@@ -1518,15 +1546,15 @@ EmitAtomicsBinOp(FunctionCompiler& f, MDefinition** def)
Scalar::Type viewType = Scalar::Type(f.readU8());
js::jit::AtomicOp op = js::jit::AtomicOp(f.readU8());
uint32_t offset, align;
MDefinition* index;
if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &index))
MDefinition* base;
MAsmJSHeapAccess access(viewType);
if (!EmitHeapAddress(f, &base, &access))
return false;
MDefinition* value;
if (!EmitExpr(f, ExprType::I32, &value))
return false;
*def = f.atomicBinopHeap(op, viewType, index, value);
*def = f.atomicBinopHeap(op, base, access, value);
return true;
}
@@ -1535,9 +1563,9 @@ EmitAtomicsCompareExchange(FunctionCompiler& f, MDefinition** def)
{
Scalar::Type viewType = Scalar::Type(f.readU8());
uint32_t offset, align;
MDefinition* index;
if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &index))
MDefinition* base;
MAsmJSHeapAccess access(viewType);
if (!EmitHeapAddress(f, &base, &access))
return false;
MDefinition* oldValue;
@@ -1546,7 +1574,7 @@ EmitAtomicsCompareExchange(FunctionCompiler& f, MDefinition** def)
MDefinition* newValue;
if (!EmitExpr(f, ExprType::I32, &newValue))
return false;
*def = f.atomicCompareExchangeHeap(viewType, index, oldValue, newValue);
*def = f.atomicCompareExchangeHeap(base, access, oldValue, newValue);
return true;
}
@@ -1555,15 +1583,15 @@ EmitAtomicsExchange(FunctionCompiler& f, MDefinition** def)
{
Scalar::Type viewType = Scalar::Type(f.readU8());
uint32_t offset, align;
MDefinition* index;
if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &index))
MDefinition* base;
MAsmJSHeapAccess access(viewType);
if (!EmitHeapAddress(f, &base, &access))
return false;
MDefinition* value;
if (!EmitExpr(f, ExprType::I32, &value))
return false;
*def = f.atomicExchangeHeap(viewType, index, value);
*def = f.atomicExchangeHeap(base, access, value);
return true;
}
@@ -1932,11 +1960,12 @@ EmitSimdLoad(FunctionCompiler& f, ExprType type, unsigned numElems, MDefinition*
if (!numElems)
numElems = defaultNumElems;
MDefinition* index;
if (!EmitExpr(f, ExprType::I32, &index))
MDefinition* base;
MAsmJSHeapAccess access(viewType, numElems);
if (!EmitHeapAddress(f, &base, &access))
return false;
*def = f.loadSimdHeap(viewType, index, numElems);
*def = f.loadSimdHeap(base, access);
return true;
}
@@ -1949,15 +1978,16 @@ EmitSimdStore(FunctionCompiler& f, ExprType type, unsigned numElems, MDefinition
if (!numElems)
numElems = defaultNumElems;
MDefinition* index;
if (!EmitExpr(f, ExprType::I32, &index))
MDefinition* base;
MAsmJSHeapAccess access(viewType, numElems);
if (!EmitHeapAddress(f, &base, &access))
return false;
MDefinition* vec;
if (!EmitExpr(f, type, &vec))
return false;
f.storeSimdHeap(viewType, index, vec, numElems);
f.storeSimdHeap(base, access, vec);
*def = vec;
return true;
}
@@ -2285,6 +2315,27 @@ EmitBitwiseNot(FunctionCompiler& f, MDefinition** def)
return true;
}
static bool
EmitExtendI32(FunctionCompiler& f, bool isUnsigned, MDefinition** def)
{
MDefinition* in;
if (!EmitExpr(f, ExprType::I32, &in))
return false;
*def = f.extendI32(in, isUnsigned);
return true;
}
template<class T>
static bool
EmitTruncate(FunctionCompiler& f, ExprType type, bool isUnsigned, MDefinition** def)
{
MDefinition* in;
if (!EmitExpr(f, type, &in))
return false;
*def = f.truncate<T>(in, isUnsigned);
return true;
}
static bool
EmitSimdOp(FunctionCompiler& f, ExprType type, SimdOperation op, SimdSign sign, MDefinition** def)
{
@@ -2642,6 +2693,8 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def)
case Expr::I32TruncSF64:
case Expr::I32TruncUF64:
return EmitUnary<MTruncateToInt32>(f, ExprType::F64, def);
case Expr::I32WrapI64:
return EmitUnary<MWrapInt64ToInt32>(f, ExprType::I64, def);
case Expr::I32Clz:
return EmitUnary<MClz>(f, ExprType::I32, def);
case Expr::I32Ctz:
@@ -2728,6 +2781,17 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def)
// I64
case Expr::I64Const:
return EmitLiteral(f, ExprType::I64, def);
case Expr::I64ExtendSI32:
case Expr::I64ExtendUI32:
return EmitExtendI32(f, IsUnsigned(op == Expr::I64ExtendUI32), def);
case Expr::I64TruncSF32:
case Expr::I64TruncUF32:
return EmitTruncate<MTruncateToInt64>(f, ExprType::F32,
IsUnsigned(op == Expr::I64TruncUF32), def);
case Expr::I64TruncSF64:
case Expr::I64TruncUF64:
return EmitTruncate<MTruncateToInt64>(f, ExprType::F64,
IsUnsigned(op == Expr::I64TruncUF64), def);
case Expr::I64Or:
return EmitBitwise<MBitOr>(f, ExprType::I64, def);
case Expr::I64And:
@@ -2887,13 +2951,6 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def)
case Expr::F64CopySign:
case Expr::F64Nearest:
case Expr::F64Trunc:
case Expr::I32WrapI64:
case Expr::I64ExtendSI32:
case Expr::I64ExtendUI32:
case Expr::I64TruncSF32:
case Expr::I64TruncSF64:
case Expr::I64TruncUF32:
case Expr::I64TruncUF64:
case Expr::F32ConvertSI64:
case Expr::F32ConvertUI64:
case Expr::F64ConvertSI64:
@@ -2916,6 +2973,8 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def)
case Expr::I64Clz:
case Expr::I64Ctz:
case Expr::I64Popcnt:
case Expr::MemorySize:
case Expr::GrowMemory:
MOZ_CRASH("NYI");
case Expr::Unreachable:
break;
+9 -5
View File
@@ -800,10 +800,13 @@ Module::setProfilingEnabled(JSContext* cx, bool enabled)
}
// Update the function-pointer tables to point to profiling prologues.
for (FuncPtrTable& funcPtrTable : funcPtrTables_) {
auto array = reinterpret_cast<void**>(globalData() + funcPtrTable.globalDataOffset);
for (size_t i = 0; i < funcPtrTable.numElems; i++) {
for (FuncPtrTable& table : funcPtrTables_) {
auto array = reinterpret_cast<void**>(globalData() + table.globalDataOffset);
for (size_t i = 0; i < table.numElems; i++) {
const CodeRange* codeRange = lookupCodeRange(array[i]);
// Don't update entries for the BadIndirectCall exit.
if (codeRange->isErrorExit())
continue;
void* from = code() + codeRange->funcNonProfilingEntry();
void* to = code() + codeRange->funcProfilingEntry();
if (!enabled)
@@ -1057,8 +1060,9 @@ Module::staticallyLink(ExclusiveContext* cx, const StaticLinkData& linkData)
auto array = reinterpret_cast<void**>(globalData() + table.globalDataOffset);
for (size_t i = 0; i < table.elemOffsets.length(); i++) {
uint8_t* elem = code() + table.elemOffsets[i];
if (profilingEnabled_)
elem = code() + lookupCodeRange(elem)->funcProfilingEntry();
const CodeRange* codeRange = lookupCodeRange(elem);
if (profilingEnabled_ && !codeRange->isErrorExit())
elem = code() + codeRange->funcProfilingEntry();
array[i] = elem;
}
}
+3
View File
@@ -249,6 +249,9 @@ class CodeRange
bool isImportExit() const {
return kind() == ImportJitExit || kind() == ImportInterpExit;
}
bool isErrorExit() const {
return kind() == ErrorExit;
}
uint32_t funcProfilingEntry() const {
MOZ_ASSERT(isFunction());
return begin();
+70 -67
View File
@@ -78,6 +78,9 @@ class WasmName
return true;
return EqualChars(begin(), rhs.begin(), length());
}
bool operator!=(WasmName rhs) const {
return !(*this == rhs);
}
};
class WasmRef
@@ -562,14 +565,18 @@ typedef WasmAstVector<WasmAstSegment*> WasmAstSegmentVector;
class WasmAstMemory : public WasmAstNode
{
uint32_t initialSize_;
Maybe<uint32_t> maxSize_;
WasmAstSegmentVector segments_;
public:
explicit WasmAstMemory(uint32_t initialSize, WasmAstSegmentVector&& segments)
explicit WasmAstMemory(uint32_t initialSize, Maybe<uint32_t> maxSize,
WasmAstSegmentVector&& segments)
: initialSize_(initialSize),
maxSize_(maxSize),
segments_(Move(segments))
{}
uint32_t initialSize() const { return initialSize_; }
const Maybe<uint32_t>& maxSize() const { return maxSize_; }
const WasmAstSegmentVector& segments() const { return segments_; }
};
@@ -2896,6 +2903,11 @@ ParseMemory(WasmParseContext& c)
if (!c.ts.match(WasmToken::Index, &initialSize, c.error))
return nullptr;
Maybe<uint32_t> maxSize;
WasmToken token;
if (c.ts.getIf(WasmToken::Index, &token))
maxSize.emplace(token.index());
WasmAstSegmentVector segments(c.lifo);
while (c.ts.getIf(WasmToken::OpenParen)) {
WasmAstSegment* segment = ParseSegment(c);
@@ -2905,7 +2917,7 @@ ParseMemory(WasmParseContext& c)
return nullptr;
}
return new(c.lifo) WasmAstMemory(initialSize.index(), Move(segments));
return new(c.lifo) WasmAstMemory(initialSize.index(), maxSize, Move(segments));
}
static WasmAstImport*
@@ -2946,6 +2958,10 @@ ParseExport(WasmParseContext& c)
case WasmToken::Name:
return new(c.lifo) WasmAstExport(name.text(), WasmRef(exportee.name(), WasmNoIndex));
case WasmToken::Memory:
if (name.text() != WasmName(MOZ_UTF16("memory"), 6)) {
c.ts.generateError(exportee, c.error);
return nullptr;
}
return new(c.lifo) WasmAstExport(name.text());
default:
break;
@@ -3455,7 +3471,7 @@ EncodeExpr(Encoder& e, WasmAstExpr& expr);
static bool
EncodeBlock(Encoder& e, WasmAstBlock& b)
{
if (!e.writeExpr(Expr::Block))
if (!e.writeExpr(b.expr()))
return false;
size_t numExprs = b.exprs().length();
@@ -3708,11 +3724,8 @@ EncodeSignatureSection(Encoder& e, WasmAstModule& module)
if (module.sigs().empty())
return true;
if (!e.writeCString(SigLabel))
return false;
size_t offset;
if (!e.startSection(&offset))
if (!e.startSection(SigLabel, &offset))
return false;
if (!e.writeVarU32(module.sigs().length()))
@@ -3741,11 +3754,8 @@ EncodeDeclarationSection(Encoder& e, WasmAstModule& module)
if (module.funcs().empty())
return true;
if (!e.writeCString(DeclLabel))
return false;
size_t offset;
if (!e.startSection(&offset))
if (!e.startSection(DeclLabel, &offset))
return false;
if (!e.writeVarU32(module.funcs().length()))
@@ -3789,11 +3799,8 @@ EncodeImportSection(Encoder& e, WasmAstModule& module)
if (module.imports().empty())
return true;
if (!e.writeCString(ImportLabel))
return false;
size_t offset;
if (!e.startSection(&offset))
if (!e.startSection(ImportLabel, &offset))
return false;
for (WasmAstImport* imp : module.imports()) {
@@ -3816,22 +3823,28 @@ EncodeMemorySection(Encoder& e, WasmAstModule& module)
if (!module.maybeMemory())
return true;
if (!e.writeCString(MemoryLabel))
return false;
size_t offset;
if (!e.startSection(&offset))
if (!e.startSection(MemoryLabel, &offset))
return false;
WasmAstMemory& memory = *module.maybeMemory();
if (!e.writeCString(InitialLabel))
return false;
if (!e.writeVarU32(memory.initialSize()))
return false;
if (!e.writeCString(EndLabel))
uint32_t maxSize = memory.maxSize() ? *memory.maxSize() : memory.initialSize();
if (!e.writeVarU32(maxSize))
return false;
uint8_t exported = 0;
for (WasmAstExport* exp : module.exports()) {
if (exp->kind() == WasmAstExportKind::Memory) {
exported = 1;
break;
}
}
if (!e.writeU8(exported))
return false;
e.finishSection(offset);
@@ -3850,48 +3863,36 @@ EncodeFunctionExport(Encoder& e, WasmAstExport& exp)
return true;
}
static bool
EncodeMemoryExport(Encoder& e, WasmAstExport& exp)
{
if (!EncodeCString(e, exp.name()))
return false;
return true;
}
static bool
EncodeExportSection(Encoder& e, WasmAstModule& module)
{
if (module.exports().empty())
uint32_t numFuncExports = 0;
for (WasmAstExport* exp : module.exports()) {
if (exp->kind() == WasmAstExportKind::Func)
numFuncExports++;
}
if (!numFuncExports)
return true;
if (!e.writeCString(ExportLabel))
size_t offset;
if (!e.startSection(ExportLabel, &offset))
return false;
size_t offset;
if (!e.startSection(&offset))
if (!e.writeVarU32(numFuncExports))
return false;
for (WasmAstExport* exp : module.exports()) {
switch (exp->kind()) {
case WasmAstExportKind::Func:
if (!e.writeCString(FuncLabel))
return false;
if (!EncodeFunctionExport(e, *exp))
return false;
break;
case WasmAstExportKind::Memory:
if (!e.writeCString(MemoryLabel))
return false;
if (!EncodeMemoryExport(e, *exp))
return false;
break;
continue;
}
}
if (!e.writeCString(EndLabel))
return false;
e.finishSection(offset);
return true;
}
@@ -3902,11 +3903,8 @@ EncodeTableSection(Encoder& e, WasmAstModule& module)
if (!module.maybeTable())
return true;
if (!e.writeCString(TableLabel))
return false;
size_t offset;
if (!e.startSection(&offset))
if (!e.startSection(TableLabel, &offset))
return false;
if (!e.writeVarU32(module.maybeTable()->elems().length()))
@@ -3922,15 +3920,8 @@ EncodeTableSection(Encoder& e, WasmAstModule& module)
}
static bool
EncodeFunctionSection(Encoder& e, WasmAstFunc& func)
EncodeFunctionBody(Encoder& e, WasmAstFunc& func)
{
if (!e.writeCString(FuncLabel))
return false;
size_t offset;
if (!e.startSection(&offset))
return false;
if (!e.writeVarU32(func.vars().length()))
return false;
@@ -3947,8 +3938,25 @@ EncodeFunctionSection(Encoder& e, WasmAstFunc& func)
return false;
}
e.finishSection(offset);
return true;
}
static bool
EncodeFunctionBodiesSection(Encoder& e, WasmAstModule& module)
{
if (module.funcs().empty())
return true;
size_t offset;
if (!e.startSection(FuncLabel, &offset))
return false;
for (WasmAstFunc* func : module.funcs()) {
if (!EncodeFunctionBody(e, *func))
return false;
}
e.finishSection(offset);
return true;
}
@@ -3989,11 +3997,8 @@ EncodeDataSection(Encoder& e, WasmAstModule& module)
const WasmAstSegmentVector& segments = module.maybeMemory()->segments();
if (!e.writeCString(DataLabel))
return false;
size_t offset;
if (!e.startSection(&offset))
if (!e.startSection(DataLabel, &offset))
return false;
for (WasmAstSegment* segment : segments) {
@@ -4043,10 +4048,8 @@ EncodeModule(WasmAstModule& module)
if (!EncodeExportSection(e, module))
return nullptr;
for (WasmAstFunc* func : module.funcs()) {
if (!EncodeFunctionSection(e, *func))
return nullptr;
}
if (!EncodeFunctionBodiesSection(e, module))
return nullptr;
if (!EncodeDataSection(e, module))
return nullptr;
+1 -41
View File
@@ -493,42 +493,7 @@ static bool
WasmIsSupported(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setBoolean(wasm::HasCompilerSupport(cx));
return true;
}
static bool
WasmEval(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject callee(cx, &args.callee());
if (args.length() < 1 || args.length() > 2) {
ReportUsageError(cx, callee, "Wrong number of arguments");
return false;
}
if (!args[0].isObject() || !args[0].toObject().is<ArrayBufferObject>()) {
ReportUsageError(cx, callee, "First argument must be an ArrayBuffer");
return false;
}
RootedObject importObj(cx);
if (!args.get(1).isUndefined()) {
if (!args.get(1).isObject()) {
ReportUsageError(cx, callee, "Second argument, if present, must be an Object");
return false;
}
importObj = &args[1].toObject();
}
Rooted<ArrayBufferObject*> code(cx, &args[0].toObject().as<ArrayBufferObject>());
RootedObject exportObj(cx);
if (!wasm::Eval(cx, code, importObj, &exportObj))
return false;
args.rval().setObject(*exportObj);
args.rval().setBoolean(wasm::HasCompilerSupport(cx) && cx->runtime()->options().wasm());
return true;
}
@@ -3691,11 +3656,6 @@ gc::ZealModeHelpText),
"wasmIsSupported()",
" Returns a boolean indicating whether WebAssembly is supported on the current device."),
JS_FN_HELP("wasmEval", WasmEval, 2, 0,
"wasmEval(buffer, imports)",
" Compiles the given binary wasm module given by 'buffer' (which must be an ArrayBuffer)\n"
" and links the module's imports with the given 'imports' object."),
JS_FN_HELP("wasmTextToBinary", WasmTextToBinary, 1, 0,
"wasmTextToBinary(str)",
" Translates the given text wasm module into its binary encoding."),
+1 -1
View File
@@ -4,7 +4,7 @@ if (!wasmIsSupported())
load(libdir + "asserts.js");
function wasmEvalText(str, imports) {
return wasmEval(wasmTextToBinary(str), imports);
return Wasm.instantiateModule(wasmTextToBinary(str), imports);
}
function mismatchError(actual, expect) {
+232
View File
@@ -0,0 +1,232 @@
load(libdir + "wasm.js");
if (!wasmIsSupported())
quit();
function testConst(type, str, expect) {
assertEq(wasmEvalText('(module (func (result ' + type + ') (' + type + '.const ' + str + ')) (export "" 0))')(), expect);
}
function testConstError(type, str) {
// For now at least, we don't distinguish between parse errors and OOMs.
assertErrorMessage(() => wasmEvalText('(module (func (result ' + type + ') (' + type + '.const ' + str + ')) (export "" 0))')(), Error, /parsing wasm text/);
}
testConst('i32', '0', 0);
testConst('i32', '-0', 0);
testConst('i32', '23', 23);
testConst('i32', '-23', -23);
testConst('i32', '0x23', 35);
testConst('i32', '-0x23', -35);
testConst('i32', '2147483647', 2147483647);
testConst('i32', '4294967295', -1);
testConst('i32', '-2147483648', -2147483648);
testConst('i32', '0x7fffffff', 2147483647);
testConst('i32', '0x80000000', -2147483648);
testConst('i32', '-0x80000000', -2147483648);
testConst('i32', '0xffffffff', -1);
//testConst('i64', '0', 0); // TODO: NYI
//testConst('i64', '-0', 0); // TODO: NYI
//testConst('i64', '23', 23); // TODO: NYI
//testConst('i64', '-23', -23); // TODO: NYI
//testConst('i64', '0x23', 35); // TODO: NYI
//testConst('i64', '-0x23', -35); // TODO: NYI
//testConst('i64', '9223372036854775807', 9223372036854775807); // TODO: NYI
//testConst('i64', '18446744073709551615', -1); // TODO: NYI
//testConst('i64', '-9223372036854775808', -9223372036854775808); // TODO: NYI
//testConst('i64', '0x7fffffffffffffff', 9223372036854775807); // TODO: NYI
//testConst('i64', '0x8000000000000000', -9223372036854775808); // TODO: NYI
//testConst('i64', '-0x8000000000000000', -9223372036854775808); // TODO: NYI
//testConst('i64', '0xffffffffffffffff', -1); // TODO: NYI
testConst('f32', '0.0', 0.0);
testConst('f32', '-0.0', -0.0);
testConst('f32', '0x0.0', 0.0);
testConst('f32', '-0x0.0', -0.0);
testConst('f32', '0x0.0p0', 0.0);
testConst('f32', '-0x0.0p0', -0.0);
testConst('f32', 'infinity', Infinity);
testConst('f32', '-infinity', -Infinity);
testConst('f32', '+infinity', Infinity);
testConst('f32', 'nan', NaN);
//testConst('f32', '-nan', NaN); // TODO: NYI
testConst('f32', '+nan', NaN);
//testConst('f32', 'nan:0x789', NaN); // TODO: NYI
//testConst('f32', '-nan:0x789', NaN); // TODO: NYI
//testConst('f32', '+nan:0x789', NaN); // TODO: NYI
testConst('f32', '0x01p-149', 1.401298464324817e-45);
testConst('f32', '0x1p-149', 1.401298464324817e-45);
testConst('f32', '0x1p-150', 0);
testConst('f32', '0x2p-150', 1.401298464324817e-45);
testConst('f32', '0x1.2p-149', 1.401298464324817e-45);
testConst('f32', '0x2.0p-149', 2.802596928649634e-45);
testConst('f32', '0x2.2p-149', 2.802596928649634e-45);
testConst('f32', '0x01p-148', 2.802596928649634e-45);
testConst('f32', '0x0.1p-148', 0);
testConst('f32', '0x0.1p-145', 1.401298464324817e-45);
testConst('f32', '0x1p-148', 2.802596928649634e-45);
testConst('f32', '0x1.111p-148', 2.802596928649634e-45);
testConst('f32', '0x1.2p-148', 2.802596928649634e-45);
testConst('f32', '0x2.0p-148', 5.605193857299268e-45);
testConst('f32', '0x2.2p-148', 5.605193857299268e-45);
testConst('f32', '0x1p-147', 5.605193857299268e-45);
testConst('f32', '0x1p-126', 1.1754943508222875e-38);
testConst('f32', '0x0.1fffffep+131', 3.4028234663852886e+38);
testConst('f32', '0x1.fffffep+127', 3.4028234663852886e+38);
testConst('f32', '0x2.0p+127', Infinity);
testConst('f32', '0x1.fffffep+128', Infinity);
testConst('f32', '0x0.1fffffep+128', 4.2535293329816107e+37);
testConst('f32', '0x1p2', 4);
testConst('f32', '0x10p2', 64);
testConst('f32', '0x100p2', 1024);
testConst('f32', '0x2p2', 8);
testConst('f32', '0x4p2', 16);
testConst('f32', '0x1p3', 8);
testConst('f32', '0x1p4', 16);
testConst('f32', '-0x1p+3', -8);
testConst('f32', '0x3p-2', .75);
testConst('f32', '-0x76.54p-32', -2.7550413506105542e-8);
testConst('f32', '0xf.ffffffffffffffffp+123', 170141183460469231731687303715884105728);
testConst('f32', '0xf.ffffffffffffffffp+124', Infinity);
testConst('f32', '1.1754943508222875e-38', 1.1754943508222875e-38);
testConst('f32', '3.4028234663852886e+38', 3.4028234663852886e+38);
testConst('f32', '1.1754943508222875e-35', 1.1754943508222875e-35);
testConst('f32', '3.4028234663852886e+35', 3.4028234346940236e+35);
testConst('f32', '1.1754943508222875e-30', 1.1754943508222875e-30);
testConst('f32', '3.4028234663852886e+30', 3.4028233462973677e+30);
testConst('f32', '4.0', 4);
testConst('f32', '-8.', -8);
testConst('f32', '-2.7550413506105542e-8', -2.7550413506105542e-8);
testConst('f32', '2.138260e+05', 2.138260e+05);
testConst('f32', '3.891074380317903e-33', 3.891074380317903e-33);
testConst('f32', '-9465807272673280.0', -9465807272673280);
testConst('f32', '1076.1376953125', 1076.1376953125);
testConst('f32', '-13364.1376953125', -13364.1376953125);
testConst('f32', '4.133607864379883', 4.133607864379883);
testConst('f32', '2.0791168212890625', 2.0791168212890625);
testConst('f32', '0.000002414453774690628', 0.000002414453774690628);
testConst('f32', '0.5312881469726562', 0.5312881469726562);
testConst('f32', '5.570960e+05', 5.570960e+05);
testConst('f32', '5.066758603788912e-7', 5.066758603788912e-7);
testConst('f32', '-5.066758603788912e-7', -5.066758603788912e-7);
testConst('f32', '1.875000e-01', 1.875000e-01);
testConst('f32', '-0x1.b021fb98e9a17p-104', -8.322574059965897e-32);
testConst('f32', '0x1.08de5bf3f784cp-129', 1.5202715065429227e-39);
testConst('f32', '0x1.d50b969fbbfb3p+388', Infinity);
testConst('f32', '0x3434.2p4', 2.138260e+05);
testConst('f32', '0x1434.2p-120', 3.891074380317903e-33);
testConst('f32', '-0x0434.234p43', -9465807272673280);
testConst('f32', '0x0434.234p0', 1076.1376953125);
testConst('f32', '-0x3434.234p0', -13364.1376953125);
testConst('f32', '0x4.22342p0', 4.133607864379883);
testConst('f32', '0x30000p-20', 1.875000e-01);
testConst('f32', '0x0.533fcccp-125', 7.645233588931088e-39);
testConst('f32', '0', 0);
testConst('f64', '0.0', 0.0);
testConst('f64', '-0.0', -0.0);
testConst('f64', '0x0.0', 0.0);
testConst('f64', '-0x0.0', -0.0);
testConst('f64', '0x0.0p0', 0.0);
testConst('f64', '-0x0.0p0', -0.0);
testConst('f64', 'infinity', Infinity);
testConst('f64', '-infinity', -Infinity);
testConst('f64', '+infinity', Infinity);
testConst('f64', 'nan', NaN);
//testConst('f64', '-nan', NaN); // TODO: NYI
testConst('f64', '+nan', NaN);
//testConst('f64', 'nan:0x789', NaN); // TODO: NYI
//testConst('f64', '-nan:0x789', NaN); // TODO: NYI
//testConst('f64', '+nan:0x789', NaN); // TODO: NYI
testConst('f64', '0x01p-149', 1.401298464324817e-45);
testConst('f64', '0x1p-149', 1.401298464324817e-45);
testConst('f64', '0x1p-150', 7.006492321624085e-46);
testConst('f64', '0x2p-150', 1.401298464324817e-45);
testConst('f64', '0x1.2p-149', 1.5764607723654192e-45);
testConst('f64', '0x2.0p-149', 2.802596928649634e-45);
testConst('f64', '0x2.2p-149', 2.977759236690236e-45);
testConst('f64', '0x01p-148', 2.802596928649634e-45);
testConst('f64', '0x0.1p-148', 1.7516230804060213e-46);
testConst('f64', '0x0.1p-145', 1.401298464324817e-45);
testConst('f64', '0x1p-148', 2.802596928649634e-45);
testConst('f64', '0x1.111p-148', 2.9893911087085575e-45);
testConst('f64', '0x1.2p-148', 3.1529215447308384e-45);
testConst('f64', '0x2.0p-148', 5.605193857299268e-45);
testConst('f64', '0x2.2p-148', 5.955518473380473e-45);
testConst('f64', '0x1p-147', 5.605193857299268e-45);
testConst('f64', '0x1p-126', 1.1754943508222875e-38);
testConst('f64', '0x0.1fffffep+131', 3.4028234663852886e+38);
testConst('f64', '0x1.fffffep+127', 3.4028234663852886e+38);
testConst('f64', '0x2.0p+127', 3.402823669209385e+38);
testConst('f64', '0x1.fffffep+128', 6.805646932770577e+38);
testConst('f64', '0x0.1fffffep+128', 4.2535293329816107e+37);
testConst('f64', '0x1p2', 4);
testConst('f64', '0x10p2', 64);
testConst('f64', '0x100p2', 1024);
testConst('f64', '0x2p2', 8);
testConst('f64', '0x4p2', 16);
testConst('f64', '0x1p3', 8);
testConst('f64', '0x1p4', 16);
testConst('f64', '-0x1p+3', -8);
testConst('f64', '0x3p-2', .75);
testConst('f64', '-0x76.54p-32', -2.7550413506105542e-8);
testConst('f64', '1.1754943508222875e-38', 1.1754943508222875e-38);
testConst('f64', '3.4028234663852886e+38', 3.4028234663852886e+38);
testConst('f64', '1.1754943508222875e-35', 1.1754943508222875e-35);
testConst('f64', '3.4028234663852886e+35', 3.4028234663852886e+35);
testConst('f64', '1.1754943508222875e-30', 1.1754943508222875e-30);
testConst('f64', '3.4028234663852886e+30', 3.402823466385289e+30);
testConst('f64', '4.0', 4);
testConst('f64', '-8.', -8);
testConst('f64', '-2.7550413506105542e-8', -2.7550413506105542e-8);
testConst('f64', '2.138260e+05', 2.138260e+05);
testConst('f64', '3.891074380317903e-33', 3.891074380317903e-33);
testConst('f64', '-9465807272673280.0', -9465807272673280);
testConst('f64', '1076.1376953125', 1076.1376953125);
testConst('f64', '-13364.1376953125', -13364.1376953125);
testConst('f64', '4.133607864379883', 4.133607864379883);
testConst('f64', '2.0791168212890625', 2.0791168212890625);
testConst('f64', '0.000002414453774690628', 0.000002414453774690628);
testConst('f64', '0.5312881469726562', 0.5312881469726562);
testConst('f64', '5.570960e+05', 5.570960e+05);
testConst('f64', '5.066758603788912e-7', 5.066758603788912e-7);
testConst('f64', '-5.066758603788912e-7', -5.066758603788912e-7);
testConst('f64', '1.875000e-01', 1.875000e-01);
testConst('f64', '0x3434.2p4', 2.138260e+05);
testConst('f64', '0x1434.2p-120', 3.891074380317903e-33);
testConst('f64', '-0x0434.234p43', -9465807272673280);
testConst('f64', '0x0434.234p0', 1076.1376953125);
testConst('f64', '-0x3434.234p0', -13364.1376953125);
testConst('f64', '0x4.22342p0', 4.133607864379883);
testConst('f64', '0x4.2882000p-1', 2.0791168212890625);
testConst('f64', '0x30000p-20', 1.875000e-01);
testConst('f64', '0x2f05.000bef2113p-1036', 1.634717678224908e-308);
testConst('f64', '0x24c6.004d0deaa3p-1036', 1.2784941357502007e-308);
testConst('f64', '0', 0);
testConstError('i32', '');
testConstError('i32', '0.0');
testConstError('i32', 'not an i32');
testConstError('i32', '4294967296');
testConstError('i32', '-2147483649');
//testConstError('i64', ''); // TODO: NYI
//testConstError('i64', '0.0'); // TODO: NYI
//testConstError('i64', 'not an i64'); // TODO: NYI
//testConstError('i64', '9223372036854775808'); // TODO: NYI
//testConstError('i64', '-9223372036854775809'); // TODO: NYI
testConstError('f32', '');
testConstError('f32', 'not an f32');
testConstError('f32', 'nan:');
testConstError('f32', 'nan:0');
testConstError('f32', 'nan:0x');
testConstError('f32', 'nan:0x0');
testConstError('f64', '');
testConstError('f64', 'not an f64');
testConstError('f64', 'nan:');
testConstError('f64', 'nan:0');
testConstError('f64', 'nan:0x');
testConstError('f64', 'nan:0x0');
@@ -147,14 +147,21 @@ assertThrowsInstanceOf(() => wasmEvalText('(module (func (return (i32.const 1)))
// ----------------------------------------------------------------------------
// br / br_if
wasmTextToBinary('(module (func (br 0)))');
wasmTextToBinary('(module (func (block (br 0))))');
wasmTextToBinary('(module (func (block $l (br $l))))');
wasmTextToBinary('(module (func (br_if 0 (i32.const 0))))');
wasmTextToBinary('(module (func (result i32) (block (br_if 0 (i32.const 1)))))');
wasmTextToBinary('(module (func (result i32) (block $l (br_if $l (i32.const 1)))))');
wasmTextToBinary('(module (func (block $l (block $m (br $l)))))');
wasmTextToBinary('(module (func (block $l (block $m (br $m)))))');
assertErrorMessage(() => wasmEvalText('(module (func (br 1)))'), TypeError, /branch depth exceeds current nesting level/);
assertErrorMessage(() => wasmEvalText('(module (func (br_if 1 (i32.const 0))))'), TypeError, /branch depth exceeds current nesting level/);
assertErrorMessage(() => wasmEvalText('(module (func (result i32) (block (br 0))) (export "" 0))'), TypeError, mismatchError("void", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (result i32) (block (br_if 0 (i32.const 0)))) (export "" 0))'), TypeError, mismatchError("void", "i32"));
// ----------------------------------------------------------------------------
// loop
+140 -9
View File
@@ -3,13 +3,30 @@ load(libdir + "wasm.js");
if (!wasmIsSupported())
quit();
function mismatchError(actual, expect) {
var str = "type mismatch: expression has type " + actual + " but expected " + expect;
return RegExp(str);
}
function testConversion(resultType, opcode, paramType, op, expect) {
assertEq(wasmEvalText('(module (func (param ' + paramType + ') (result ' + resultType + ') (' + resultType + '.' + opcode + '/' + paramType + ' (get_local 0))) (export "" 0))')(op), expect);
if (paramType === 'i64') {
// i64 cannot be imported, so we use a wrapper function.
assertEq(wasmEvalText(`(module
(func (param i64) (result ${resultType}) (${resultType}.${opcode}/i64 (get_local 0)))
(func (result ${resultType}) (call 0 (i64.const ${op})))
(export "" 1))`)(), expect);
// The same, but now the input is a constant.
assertEq(wasmEvalText(`(module
(func (result ${resultType}) (${resultType}.${opcode}/i64 (i64.const ${op})))
(export "" 0))`)(), expect);
} else if (resultType === 'i64') {
assertEq(wasmEvalText(`(module
(func (param ${paramType}) (result i64) (i64.${opcode}/${paramType} (get_local 0)))
(func (result i32) (i64.eq (i64.const ${expect}) (call 0 (${paramType}.const ${op}))))
(export "" 1))`)(), 1);
// The same, but now the input is a constant.
assertEq(wasmEvalText(`(module
(func (result i64) (i64.${opcode}/${paramType} (${paramType}.const ${op})))
(func (result i32) (i64.eq (i64.const ${expect}) (call 0)))
(export "" 1))`)(), 1);
} else {
assertEq(wasmEvalText('(module (func (param ' + paramType + ') (result ' + resultType + ') (' + resultType + '.' + opcode + '/' + paramType + ' (get_local 0))) (export "" 0))')(op), expect);
}
// TODO: i64 NYI
for (var bad of ['i32', 'f32', 'f64']) {
@@ -26,15 +43,129 @@ function testConversion(resultType, opcode, paramType, op, expect) {
}
}
//testConversion('i32', 'wrap', 'i64', 4294967336, 40); // TODO: NYI
if (getBuildConfiguration().x64) {
testConversion('i32', 'wrap', 'i64', 4294967336, 40);
testConversion('i32', 'wrap', 'i64', -10, -10);
testConversion('i32', 'wrap', 'i64', "0xffffffff7fffffff", 0x7fffffff);
testConversion('i32', 'wrap', 'i64', "0xffffffff00000000", 0);
testConversion('i32', 'wrap', 'i64', "0xfffffffeffffffff", -1);
testConversion('i32', 'wrap', 'i64', "0x1234567801abcdef", 0x01abcdef);
testConversion('i32', 'wrap', 'i64', "0x8000000000000002", 2);
testConversion('i64', 'extend_s', 'i32', 0, 0);
testConversion('i64', 'extend_s', 'i32', 1234, 1234);
testConversion('i64', 'extend_s', 'i32', -567, -567);
testConversion('i64', 'extend_s', 'i32', 0x7fffffff, "0x000000007fffffff");
testConversion('i64', 'extend_s', 'i32', 0x80000000, "0xffffffff80000000");
testConversion('i64', 'extend_u', 'i32', 0, 0);
testConversion('i64', 'extend_u', 'i32', 1234, 1234);
testConversion('i64', 'extend_u', 'i32', -567, "0x00000000fffffdc9");
testConversion('i64', 'extend_u', 'i32', -1, "0x00000000ffffffff");
testConversion('i64', 'extend_u', 'i32', 0x7fffffff, "0x000000007fffffff");
testConversion('i64', 'extend_u', 'i32', 0x80000000, "0x0000000080000000");
testConversion('i64', 'trunc_s', 'f64', 0.0, 0);
testConversion('i64', 'trunc_s', 'f64', "-0.0", 0);
testConversion('i64', 'trunc_s', 'f64', 1.0, 1);
testConversion('i64', 'trunc_s', 'f64', 1.1, 1);
testConversion('i64', 'trunc_s', 'f64', 1.5, 1);
testConversion('i64', 'trunc_s', 'f64', 1.99, 1);
testConversion('i64', 'trunc_s', 'f64', 40.1, 40);
testConversion('i64', 'trunc_s', 'f64', -1.0, -1);
testConversion('i64', 'trunc_s', 'f64', -1.1, -1);
testConversion('i64', 'trunc_s', 'f64', -1.5, -1);
testConversion('i64', 'trunc_s', 'f64', -1.99, -1);
testConversion('i64', 'trunc_s', 'f64', -2.0, -2);
testConversion('i64', 'trunc_s', 'f64', 4294967296.1, "4294967296");
testConversion('i64', 'trunc_s', 'f64', -4294967296.8, "-4294967296");
testConversion('i64', 'trunc_s', 'f64', 9223372036854774784.8, "9223372036854774784");
testConversion('i64', 'trunc_s', 'f64', -9223372036854775808.3, "-9223372036854775808");
testConversion('i64', 'trunc_u', 'f64', 0.0, 0);
testConversion('i64', 'trunc_u', 'f64', "-0.0", 0);
testConversion('i64', 'trunc_u', 'f64', 1.0, 1);
testConversion('i64', 'trunc_u', 'f64', 1.1, 1);
testConversion('i64', 'trunc_u', 'f64', 1.5, 1);
testConversion('i64', 'trunc_u', 'f64', 1.99, 1);
testConversion('i64', 'trunc_u', 'f64', -0.9, 0);
testConversion('i64', 'trunc_u', 'f64', 40.1, 40);
testConversion('i64', 'trunc_u', 'f64', 4294967295, "0xffffffff");
testConversion('i64', 'trunc_u', 'f64', 4294967296.1, "4294967296");
testConversion('i64', 'trunc_u', 'f64', 1e8, "100000000");
testConversion('i64', 'trunc_u', 'f64', 1e16, "10000000000000000");
testConversion('i64', 'trunc_u', 'f64', 9223372036854775808, "-9223372036854775808");
testConversion('i64', 'trunc_u', 'f64', 18446744073709549568.1, -2048);
testConversion('i64', 'trunc_s', 'f32', 0.0, 0);
testConversion('i64', 'trunc_s', 'f32', "-0.0", 0);
testConversion('i64', 'trunc_s', 'f32', 1.0, 1);
testConversion('i64', 'trunc_s', 'f32', 1.1, 1);
testConversion('i64', 'trunc_s', 'f32', 1.5, 1);
testConversion('i64', 'trunc_s', 'f32', 1.99, 1);
testConversion('i64', 'trunc_s', 'f32', 40.1, 40);
testConversion('i64', 'trunc_s', 'f32', -1.0, -1);
testConversion('i64', 'trunc_s', 'f32', -1.1, -1);
testConversion('i64', 'trunc_s', 'f32', -1.5, -1);
testConversion('i64', 'trunc_s', 'f32', -1.99, -1);
testConversion('i64', 'trunc_s', 'f32', -2.0, -2);
testConversion('i64', 'trunc_s', 'f32', 4294967296.1, "4294967296");
testConversion('i64', 'trunc_s', 'f32', -4294967296.8, "-4294967296");
testConversion('i64', 'trunc_s', 'f32', 9223371487098961920.0, "9223371487098961920");
testConversion('i64', 'trunc_s', 'f32', -9223372036854775808.3, "-9223372036854775808");
testConversion('i64', 'trunc_u', 'f32', 0.0, 0);
testConversion('i64', 'trunc_u', 'f32', "-0.0", 0);
testConversion('i64', 'trunc_u', 'f32', 1.0, 1);
testConversion('i64', 'trunc_u', 'f32', 1.1, 1);
testConversion('i64', 'trunc_u', 'f32', 1.5, 1);
testConversion('i64', 'trunc_u', 'f32', 1.99, 1);
testConversion('i64', 'trunc_u', 'f32', -0.9, 0);
testConversion('i64', 'trunc_u', 'f32', 40.1, 40);
testConversion('i64', 'trunc_u', 'f32', 1e8, "100000000");
testConversion('i64', 'trunc_u', 'f32', 4294967296, "4294967296");
testConversion('i64', 'trunc_u', 'f32', 18446742974197923840.0, "-1099511627776");
// TODO: these should trap.
testConversion('i64', 'trunc_s', 'f64', 9223372036854775808.0, "0x8000000000000000");
testConversion('i64', 'trunc_s', 'f64', -9223372036854777856.0, "0x8000000000000000");
testConversion('i64', 'trunc_s', 'f64', "nan", "0x8000000000000000");
testConversion('i64', 'trunc_s', 'f64', "infinity", "0x8000000000000000");
testConversion('i64', 'trunc_s', 'f64', "-infinity", "0x8000000000000000");
testConversion('i64', 'trunc_u', 'f64', -1, "0x8000000000000000");
testConversion('i64', 'trunc_u', 'f64', 18446744073709551616.0, "0x8000000000000000");
testConversion('i64', 'trunc_u', 'f64', "nan", "0x8000000000000000");
testConversion('i64', 'trunc_u', 'f64', "infinity", "0x8000000000000000");
testConversion('i64', 'trunc_u', 'f64', "-infinity", "0x8000000000000000");
testConversion('i64', 'trunc_s', 'f32', 9223372036854775808.0, "0x8000000000000000");
testConversion('i64', 'trunc_s', 'f32', -9223372036854777856.0, "0x8000000000000000");
testConversion('i64', 'trunc_s', 'f32', "nan", "0x8000000000000000");
testConversion('i64', 'trunc_s', 'f32', "infinity", "0x8000000000000000");
testConversion('i64', 'trunc_s', 'f32', "-infinity", "0x8000000000000000");
testConversion('i64', 'trunc_u', 'f32', 18446744073709551616.0, "0x8000000000000000");
testConversion('i64', 'trunc_u', 'f32', -1, "0x8000000000000000");
testConversion('i64', 'trunc_u', 'f32', "nan", "0x8000000000000000");
testConversion('i64', 'trunc_u', 'f32', "infinity", "0x8000000000000000");
testConversion('i64', 'trunc_u', 'f32', "-infinity", "0x8000000000000000");
} else {
// Sleeper test: once i64 works on more platforms, remove this if-else.
try {
testConversion('i32', 'wrap', 'i64', 4294967336, 40);
assertEq(0, 1);
} catch(e) {
assertEq(e.toString().indexOf("NYI on this platform") >= 0, true);
}
}
testConversion('i32', 'trunc_s', 'f32', 40.1, 40);
testConversion('i32', 'trunc_u', 'f32', 40.1, 40);
testConversion('i32', 'trunc_s', 'f64', 40.1, 40);
testConversion('i32', 'trunc_u', 'f64', 40.1, 40);
//testConversion('i32', 'reinterpret', 'f32', 40.1, 1109419622); // TODO: NYI
//testConversion('i64', 'extend_s', 'i32', -2, -2); / TODO: NYI
//testConversion('i64', 'extend_u', 'i32', -2, 0xfffffffffffffffc); / TODO: NYI
//testConversion('i64', 'trunc_s', 'f32', 40.1, 40); // TODO: NYI
//testConversion('i64', 'trunc_u', 'f32', 40.1, 40); // TODO: NYI
//testConversion('i64', 'trunc_s', 'f64', 40.1, 40); // TODO: NYI
+37 -12
View File
@@ -3,15 +3,10 @@ load(libdir + "wasm.js");
if (!wasmIsSupported())
quit();
function mismatchError(actual, expect) {
var str = "type mismatch: expression has type " + actual + " but expected " + expect;
return RegExp(str);
}
function testLoad(type, ext, base, offset, align, expect) {
assertEq(wasmEvalText(
'(module' +
' (memory 0x10000' +
' (memory 1' +
' (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
' )' +
@@ -28,7 +23,7 @@ function testLoad(type, ext, base, offset, align, expect) {
function testStore(type, ext, base, offset, align, value) {
assertEq(wasmEvalText(
'(module' +
' (memory 0x10000' +
' (memory 1' +
' (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
' )' +
@@ -46,7 +41,7 @@ function testStore(type, ext, base, offset, align, value) {
function testLoadError(type, ext, base, offset, align, errorMsg) {
assertErrorMessage(() => wasmEvalText(
'(module' +
' (memory 0x10000' +
' (memory 1' +
' (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
' )' +
@@ -63,7 +58,7 @@ function testLoadError(type, ext, base, offset, align, errorMsg) {
function testStoreError(type, ext, base, offset, align, errorMsg) {
assertErrorMessage(() => wasmEvalText(
'(module' +
' (memory 0x10000' +
' (memory 1' +
' (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
' )' +
@@ -102,6 +97,18 @@ testLoad('i32', '8_s', 16, 0, 0, -0x10);
testLoad('i32', '8_u', 16, 0, 0, 0xf0);
testLoad('i32', '16_s', 16, 0, 0, -0xe10);
testLoad('i32', '16_u', 16, 0, 0, 0xf1f0);
// When these tests fail, uncomment the load/store tests below.
function testLoadNYI(ext) {
assertErrorMessage(() => wasmEvalText(`(module (memory 1) (func (i64.load${ext} (i32.const 0))))`), TypeError, /NYI/);
}
testLoadNYI('');
testLoadNYI('8_s');
testLoadNYI('8_u');
testLoadNYI('16_s');
testLoadNYI('16_u');
testLoadNYI('32_s');
testLoadNYI('32_u');
//testLoad('i64', '8_s', 16, 0, 0, -0x8); // TODO: i64 NYI
//testLoad('i64', '8_u', 16, 0, 0, 0x8); // TODO: i64 NYI
//testLoad('i64', '16_s', 16, 0, 0, -0x707); // TODO: i64 NYI
@@ -113,10 +120,22 @@ testStore('i32', '', 0, 0, 0, -0x3f3e2c2c);
//testStore('i32', '', 1, 0, 0, -0x3f3e2c2c); // TODO: unaligned NYI
//testStore('i32', '', 0, 1, 0, 0xc0c1d3d4); // TODO: offset NYI
//testStore('i32', '', 1, 1, 4, 0xc0c1d3d4); // TODO: offset NYI
function testStoreNYI(ext) {
assertErrorMessage(() => wasmEvalText(`(module (memory 1) (func (i64.store${ext} (i32.const 0) (i32.const 0))))`), TypeError, /NYI/);
}
testStoreNYI('');
testStoreNYI('8');
testStoreNYI('16');
testStoreNYI('32');
//testStore('i64', '', 0, 0, 0, 0xc0c1d3d4e6e7090a); // TODO: i64 NYI
//testStore('i64', '', 1, 0, 0, 0xc0c1d3d4e6e7090a); // TODO: i64 NYI
//testStore('i64', '', 0, 1, 0, 0xc0c1d3d4e6e7090a); // TODO: i64 NYI
//testStore('i64', '', 1, 1, 4, 0xc0c1d3d4e6e7090a); // TODO: i64 NYI
//testStore('i64', '8', 0, 0, 0, 0x23); // TODO: i64 NYI
//testStore('i64', '16', 0, 0, 0, 0x23); // TODO: i64 NYI
//testStore('i64', '32', 0, 0, 0, 0x23); // TODO: i64 NYI
testStore('f32', '', 0, 0, 0, 0.01234566979110241);
//testStore('f32', '', 1, 0, 0, 0.01234566979110241); // TODO: unaligned NYI
//testStore('f32', '', 0, 1, 0, 0.01234567); // TODO: offsets NYI
@@ -128,9 +147,15 @@ testStore('f64', '', 0, 0, 0, 0.89012345);
testStore('i32', '8', 0, 0, 0, 0x23);
testStore('i32', '16', 0, 0, 0, 0x2345);
//testStore('i64', '8', 0, 0, 0, 0x23); // TODO: i64 NYI
//testStore('i64', '16', 0, 0, 0, 0x23); // TODO: i64 NYI
//testStore('i64', '32', 0, 0, 0, 0x23); // TODO: i64 NYI
testLoadError('i32', '', 0, 0, 3, /memory access alignment must be a power of two/);
testStoreError('i32', '', 0, 0, 3, /memory access alignment must be a power of two/);
assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (f64.store offset=0 (i32.const 0) (i32.const 0))))'), TypeError, mismatchError("i32", "f64"));
assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (f64.store offset=0 (i32.const 0) (f32.const 0))))'), TypeError, mismatchError("f32", "f64"));
assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (f32.store offset=0 (i32.const 0) (i32.const 0))))'), TypeError, mismatchError("i32", "f32"));
assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (f32.store offset=0 (i32.const 0) (f64.const 0))))'), TypeError, mismatchError("f64", "f32"));
assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (i32.store offset=0 (i32.const 0) (f32.const 0))))'), TypeError, mismatchError("f32", "i32"));
assertErrorMessage(() => wasmEvalText('(module (memory 1) (func (i32.store offset=0 (i32.const 0) (f64.const 0))))'), TypeError, mismatchError("f64", "i32"));
+95 -48
View File
@@ -96,8 +96,8 @@ if (!hasI64) {
// ----------------------------------------------------------------------------
// imports
assertErrorMessage(() => wasmEvalText('(module (import "a" "b"))', 1), Error, /Second argument, if present, must be an Object/);
assertErrorMessage(() => wasmEvalText('(module (import "a" "b"))', null), Error, /Second argument, if present, must be an Object/);
assertErrorMessage(() => wasmEvalText('(module (import "a" "b"))', 1), Error, /second argument, if present, must be an object/);
assertErrorMessage(() => wasmEvalText('(module (import "a" "b"))', null), Error, /second argument, if present, must be an object/);
const noImportObj = /no import object given/;
const notObject = /import object field is not an Object/;
@@ -132,69 +132,102 @@ wasmEvalText('(module (import $foo "a" "" (result f64)))', {a: ()=> {}});
// ----------------------------------------------------------------------------
// memory
wasmEvalText('(module (memory 65536))');
wasmEvalText('(module (memory 131072))');
assertErrorMessage(() => wasmEvalText('(module (memory 0))'), TypeError, /not a multiple of 0x10000/);
assertErrorMessage(() => wasmEvalText('(module (memory 1))'), TypeError, /not a multiple of 0x10000/);
assertErrorMessage(() => wasmEvalText('(module (memory 65535))'), TypeError, /not a multiple of 0x10000/);
assertErrorMessage(() => wasmEvalText('(module (memory 131071))'), TypeError, /not a multiple of 0x10000/);
assertErrorMessage(() => wasmEvalText('(module (memory 2147483648))'), TypeError, /initial memory size too big/);
wasmEvalText('(module (memory 0))');
wasmEvalText('(module (memory 1))');
assertErrorMessage(() => wasmEvalText('(module (memory 65536))'), TypeError, /initial memory size too big/);
assertErrorMessage(() => wasmEvalText('(module (memory 32768))'), TypeError, /initial memory size too big/);
// May OOM, but must not crash:
try {
wasmEvalText('(module (memory 2147418112))');
wasmEvalText('(module (memory 32767))');
} catch (e) {
print(e);
assertEq(String(e).indexOf("out of memory") != -1, true);
}
assertErrorMessage(() => wasmEvalText('(module (export "" memory))'), TypeError, /no memory section/);
// Tests to reinstate pending a switch back to "real" memory exports:
//
//assertErrorMessage(() => wasmEvalText('(module (export "" memory))'), TypeError, /no memory section/);
//
//var buf = wasmEvalText('(module (memory 1) (export "" memory))');
//assertEq(buf instanceof ArrayBuffer, true);
//assertEq(buf.byteLength, 65536);
//
//assertErrorMessage(() => wasmEvalText('(module (memory 1) (export "a" memory) (export "a" memory))'), TypeError, /duplicate export/);
//assertErrorMessage(() => wasmEvalText('(module (memory 1) (func) (export "a" memory) (export "a" 0))'), TypeError, /duplicate export/);
//var {a, b} = wasmEvalText('(module (memory 1) (export "a" memory) (export "b" memory))');
//assertEq(a instanceof ArrayBuffer, true);
//assertEq(a, b);
//
//var obj = wasmEvalText('(module (memory 1) (func (result i32) (i32.const 42)) (func (nop)) (export "a" memory) (export "b" 0) (export "c" 1))');
//assertEq(obj.a instanceof ArrayBuffer, true);
//assertEq(obj.b instanceof Function, true);
//assertEq(obj.c instanceof Function, true);
//assertEq(obj.a.byteLength, 65536);
//assertEq(obj.b(), 42);
//assertEq(obj.c(), undefined);
//
//var obj = wasmEvalText('(module (memory 1) (func (result i32) (i32.const 42)) (export "" memory) (export "a" 0) (export "b" 0))');
//assertEq(obj instanceof ArrayBuffer, true);
//assertEq(obj.a instanceof Function, true);
//assertEq(obj.b instanceof Function, true);
//assertEq(obj.a, obj.b);
//assertEq(obj.byteLength, 65536);
//assertEq(obj.a(), 42);
//
//var buf = wasmEvalText('(module (memory 1 (segment 0 "")) (export "" memory))');
//assertEq(new Uint8Array(buf)[0], 0);
//
//var buf = wasmEvalText('(module (memory 1 (segment 65536 "")) (export "" memory))');
//assertEq(new Uint8Array(buf)[0], 0);
//
//var buf = wasmEvalText('(module (memory 1 (segment 0 "a")) (export "" memory))');
//assertEq(new Uint8Array(buf)[0], 'a'.charCodeAt(0));
//
//var buf = wasmEvalText('(module (memory 1 (segment 0 "a") (segment 2 "b")) (export "" memory))');
//assertEq(new Uint8Array(buf)[0], 'a'.charCodeAt(0));
//assertEq(new Uint8Array(buf)[1], 0);
//assertEq(new Uint8Array(buf)[2], 'b'.charCodeAt(0));
//
//var buf = wasmEvalText('(module (memory 1 (segment 65535 "c")) (export "" memory))');
//assertEq(new Uint8Array(buf)[0], 0);
//assertEq(new Uint8Array(buf)[65535], 'c'.charCodeAt(0));
//
//assertErrorMessage(() => wasmEvalText('(module (memory 1 (segment 65536 "a")) (export "" memory))'), TypeError, /data segment does not fit/);
//assertErrorMessage(() => wasmEvalText('(module (memory 1 (segment 65535 "ab")) (export "" memory))'), TypeError, /data segment does not fit/);
var buf = wasmEvalText('(module (memory 65536) (export "" memory))');
var buf = wasmEvalText('(module (memory 1) (export "memory" memory))').memory;
assertEq(buf instanceof ArrayBuffer, true);
assertEq(buf.byteLength, 65536);
assertErrorMessage(() => wasmEvalText('(module (memory 65536) (export "a" memory) (export "a" memory))'), TypeError, /duplicate export/);
assertErrorMessage(() => wasmEvalText('(module (memory 65536) (func) (export "a" memory) (export "a" 0))'), TypeError, /duplicate export/);
var {a, b} = wasmEvalText('(module (memory 65536) (export "a" memory) (export "b" memory))');
assertEq(a instanceof ArrayBuffer, true);
assertEq(a, b);
var obj = wasmEvalText('(module (memory 65536) (func (result i32) (i32.const 42)) (func (nop)) (export "a" memory) (export "b" 0) (export "c" 1))');
assertEq(obj.a instanceof ArrayBuffer, true);
var obj = wasmEvalText('(module (memory 1) (func (result i32) (i32.const 42)) (func (nop)) (export "memory" memory) (export "b" 0) (export "c" 1))');
assertEq(obj.memory instanceof ArrayBuffer, true);
assertEq(obj.b instanceof Function, true);
assertEq(obj.c instanceof Function, true);
assertEq(obj.a.byteLength, 65536);
assertEq(obj.memory.byteLength, 65536);
assertEq(obj.b(), 42);
assertEq(obj.c(), undefined);
var obj = wasmEvalText('(module (memory 65536) (func (result i32) (i32.const 42)) (export "" memory) (export "a" 0) (export "b" 0))');
assertEq(obj instanceof ArrayBuffer, true);
assertEq(obj.a instanceof Function, true);
assertEq(obj.b instanceof Function, true);
assertEq(obj.a, obj.b);
assertEq(obj.byteLength, 65536);
assertEq(obj.a(), 42);
var buf = wasmEvalText('(module (memory 65536 (segment 0 "")) (export "" memory))');
var buf = wasmEvalText('(module (memory 1 (segment 0 "")) (export "memory" memory))').memory;
assertEq(new Uint8Array(buf)[0], 0);
var buf = wasmEvalText('(module (memory 65536 (segment 65536 "")) (export "" memory))');
var buf = wasmEvalText('(module (memory 1 (segment 65536 "")) (export "memory" memory))').memory;
assertEq(new Uint8Array(buf)[0], 0);
var buf = wasmEvalText('(module (memory 65536 (segment 0 "a")) (export "" memory))');
var buf = wasmEvalText('(module (memory 1 (segment 0 "a")) (export "memory" memory))').memory;
assertEq(new Uint8Array(buf)[0], 'a'.charCodeAt(0));
var buf = wasmEvalText('(module (memory 65536 (segment 0 "a") (segment 2 "b")) (export "" memory))');
var buf = wasmEvalText('(module (memory 1 (segment 0 "a") (segment 2 "b")) (export "memory" memory))').memory;
assertEq(new Uint8Array(buf)[0], 'a'.charCodeAt(0));
assertEq(new Uint8Array(buf)[1], 0);
assertEq(new Uint8Array(buf)[2], 'b'.charCodeAt(0));
var buf = wasmEvalText('(module (memory 65536 (segment 65535 "c")) (export "" memory))');
var buf = wasmEvalText('(module (memory 1 (segment 65535 "c")) (export "memory" memory))').memory;
assertEq(new Uint8Array(buf)[0], 0);
assertEq(new Uint8Array(buf)[65535], 'c'.charCodeAt(0));
assertErrorMessage(() => wasmEvalText('(module (memory 65536 (segment 65536 "a")) (export "" memory))'), TypeError, /data segment does not fit/);
assertErrorMessage(() => wasmEvalText('(module (memory 65536 (segment 65535 "ab")) (export "" memory))'), TypeError, /data segment does not fit/);
assertErrorMessage(() => wasmEvalText('(module (memory 1 (segment 65536 "a")) (export "memory" memory))'), TypeError, /data segment does not fit/);
assertErrorMessage(() => wasmEvalText('(module (memory 1 (segment 65535 "ab")) (export "memory" memory))'), TypeError, /data segment does not fit/);
// ----------------------------------------------------------------------------
// locals
@@ -327,26 +360,40 @@ var {v2i, i2i, i2v} = wasmEvalText(`(module
(export "i2v" 8)
)`);
const badIndirectCall = /wasm indirect call signature mismatch/;
assertEq(v2i(0), 13);
assertEq(v2i(1), 42);
assertErrorMessage(() => v2i(2), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => v2i(3), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => v2i(4), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => v2i(5), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => v2i(2), Error, badIndirectCall);
assertErrorMessage(() => v2i(3), Error, badIndirectCall);
assertErrorMessage(() => v2i(4), Error, badIndirectCall);
assertErrorMessage(() => v2i(5), Error, badIndirectCall);
assertErrorMessage(() => i2i(0), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => i2i(1), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => i2i(0), Error, badIndirectCall);
assertErrorMessage(() => i2i(1), Error, badIndirectCall);
assertEq(i2i(2, 100), 101);
assertEq(i2i(3, 100), 102);
assertEq(i2i(4, 100), 103);
assertEq(i2i(5, 100), 104);
assertErrorMessage(() => i2v(0), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => i2v(1), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => i2v(2), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => i2v(3), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => i2v(4), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => i2v(5), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => i2v(0), Error, badIndirectCall);
assertErrorMessage(() => i2v(1), Error, badIndirectCall);
assertErrorMessage(() => i2v(2), Error, badIndirectCall);
assertErrorMessage(() => i2v(3), Error, badIndirectCall);
assertErrorMessage(() => i2v(4), Error, badIndirectCall);
assertErrorMessage(() => i2v(5), Error, badIndirectCall);
{
enableSPSProfiling();
wasmEvalText(`(
module
(func (result i32) (i32.const 0))
(func)
(table 1 0)
(export "" 0)
)`)();
disableSPSProfiling();
}
for (bad of [6, 7, 100, Math.pow(2,31)-1, Math.pow(2,31), Math.pow(2,31)+1, Math.pow(2,32)-2, Math.pow(2,32)-1]) {
assertThrowsInstanceOf(() => v2i(bad), RangeError);
+32 -26
View File
@@ -1,16 +1,16 @@
load(libdir + "wasm.js");
// MagicNumber = 0x4d534100
const magic0 = 0;
const magic1 = 97; // 'a'
const magic2 = 115; // 's'
const magic3 = 109; // 'm'
// MagicNumber = 0x6d736100;
const magic0 = 0x00; // '\0'
const magic1 = 0x61; // 'a'
const magic2 = 0x73; // 's'
const magic3 = 0x6d; // 'm'
// EncodingVersion = -1 (to be changed to 1 at some point in the future)
const ver0 = 0xff;
const ver1 = 0xff;
const ver2 = 0xff;
const ver3 = 0xff;
// EncodingVersion = 10 (to be changed to 1 at some point in the future)
const ver0 = 0x0a;
const ver1 = 0x00;
const ver2 = 0x00;
const ver3 = 0x00;
// Section names
const sigLabel = "sig";
@@ -24,7 +24,7 @@ const dataLabel = "data";
const magicError = /failed to match magic number/;
const versionError = /failed to match binary version/;
const extraError = /failed to consume all bytes of module/;
const sectionError = /failed to read section name/;
const sectionError = /failed to start section/;
const I32Code = 0;
const I64Code = 1;
@@ -53,6 +53,8 @@ function moduleHeaderThen(...rest) {
return [magic0, magic1, magic2, magic3, ver0, ver1, ver2, ver3, ...rest];
}
const wasmEval = Wasm.instantiateModule;
assertErrorMessage(() => wasmEval(toBuf([])), TypeError, magicError);
assertErrorMessage(() => wasmEval(toBuf([42])), TypeError, magicError);
assertErrorMessage(() => wasmEval(toBuf([magic0, magic1, magic2])), TypeError, magicError);
@@ -75,8 +77,8 @@ function cstring(name) {
function moduleWithSections(sectionArray) {
var bytes = moduleHeaderThen();
for (let section of sectionArray) {
bytes.push(...varU32(section.name.length + 1 + section.body.length));
bytes.push(...cstring(section.name));
bytes.push(...varU32(section.body.length));
bytes.push(...section.body);
}
bytes.push(0);
@@ -103,12 +105,16 @@ function declSection(decls) {
return { name: declLabel, body };
}
function funcSection(func) {
var body = [];
var locals = varU32(func.locals.length);
function funcBody(func) {
var body = varU32(func.locals.length);
for (let local of func.locals)
locals.push(...varU32(local));
body = body.concat(locals, func.body);
body.push(...varU32(local));
body.push(...varU32(func.body.length));
return body.concat(...func.body);
}
function bodySection(bodies) {
var body = [].concat(...bodies);
return { name: funcLabel, body };
}
@@ -134,7 +140,7 @@ function tableSection(elems) {
const v2vSig = {args:[], ret:VoidCode};
const i2vSig = {args:[I32Code], ret:VoidCode};
const v2vFunc = funcSection({locals:[], body:[0]});
const v2vBody = funcBody({locals:[], body:[]});
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigLabel, body: U32MAX_LEB, } ]))), TypeError, /too many signatures/);
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigLabel, body: [1, ...U32MAX_LEB], } ]))), TypeError, /too many arguments in signature/);
@@ -153,8 +159,8 @@ assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[1
assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([]), declSection([0])]))), TypeError, /signature index out of range/);
assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([1])]))), TypeError, /signature index out of range/);
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0])]))), TypeError, /fewer function definitions than declarations/);
wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), v2vFunc])));
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0])]))), TypeError, /expected function bodies/);
wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([v2vBody])])));
assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), {name: importLabel, body:[]}]))), TypeError);
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([importSection([{sigIndex:0, module:"a", func:"b"}])]))), TypeError, /signature index out of range/);
@@ -166,14 +172,14 @@ wasmEval(toBuf(moduleWithSections([
sigSection([v2vSig]),
importSection([{sigIndex:0, module:"a", func:""}]),
declSection([0]),
v2vFunc])), {a:()=>{}});
bodySection([v2vBody])])), {a:()=>{}});
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: dataLabel, body: [], } ]))), TypeError, /data section requires a memory section/);
wasmEval(toBuf(moduleWithSections([tableSection([])])));
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([tableSection([0])]))), TypeError, /table element out of range/);
wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0]), v2vFunc])));
wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0,0]), v2vFunc])));
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0,1]), v2vFunc]))), TypeError, /table element out of range/);
wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0,0,0]), tableSection([0,1,0,2]), v2vFunc, v2vFunc, v2vFunc])));
wasmEval(toBuf(moduleWithSections([sigSection([v2vSig,i2vSig]), declSection([0,0,1]), tableSection([0,1,2]), v2vFunc, v2vFunc, v2vFunc])));
wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0]), bodySection([v2vBody])])));
wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0,0]), bodySection([v2vBody])])));
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0,1]), bodySection([v2vBody])]))), TypeError, /table element out of range/);
wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0,0,0]), tableSection([0,1,0,2]), bodySection([v2vBody, v2vBody, v2vBody])])));
wasmEval(toBuf(moduleWithSections([sigSection([v2vSig,i2vSig]), declSection([0,0,1]), tableSection([0,1,2]), bodySection([v2vBody, v2vBody, v2vBody])])));
+21
View File
@@ -0,0 +1,21 @@
load(libdir + "wasm.js");
// This is fac-opt from fac.wast in the official testsuite, changed to use
// i32 instead of i64.
assertEq(wasmEvalText(`(module
(func $fac-opt (param i32) (result i32)
(local i32)
(set_local 1 (i32.const 1))
(block
(br_if 0 (i32.lt_s (get_local 0) (i32.const 2)))
(loop
(set_local 1 (i32.mul (get_local 1) (get_local 0)))
(set_local 0 (i32.add (get_local 0) (i32.const -1)))
(br_if 0 (i32.gt_s (get_local 0) (i32.const 1)))
)
)
(get_local 1)
)
(export "" 0)
)`)(10), 3628800);
+2 -2
View File
@@ -82,9 +82,9 @@ AlignmentMaskAnalysis::analyze()
// or MAsmJSAtomicBinopHeap, because the backend and the OOB
// mechanism don't support non-zero offsets for them yet.
if (i->isAsmJSLoadHeap())
AnalyzeAsmHeapAddress(i->toAsmJSLoadHeap()->ptr(), graph_);
AnalyzeAsmHeapAddress(i->toAsmJSLoadHeap()->base(), graph_);
else if (i->isAsmJSStoreHeap())
AnalyzeAsmHeapAddress(i->toAsmJSStoreHeap()->ptr(), graph_);
AnalyzeAsmHeapAddress(i->toAsmJSStoreHeap()->base(), graph_);
}
}
return true;
+15 -18
View File
@@ -104,20 +104,17 @@ template<typename MAsmJSHeapAccessType>
bool
EffectiveAddressAnalysis::tryAddDisplacement(MAsmJSHeapAccessType* ins, int32_t o)
{
// Compute the new offset. Check for overflow and negative. In theory it
// ought to be possible to support negative offsets, but it'd require
// more elaborate bounds checking mechanisms than we currently have.
MOZ_ASSERT(ins->offset() >= 0);
int32_t newOffset = uint32_t(ins->offset()) + o;
if (newOffset < 0)
// Compute the new offset. Check for overflow.
uint32_t oldOffset = ins->offset();
uint32_t newOffset = oldOffset + o;
if (o < 0 ? (newOffset >= oldOffset) : (newOffset < oldOffset))
return false;
// Compute the new offset to the end of the access. Check for overflow
// and negative here also.
int32_t newEnd = uint32_t(newOffset) + ins->byteSize();
if (newEnd < 0)
// here also.
uint32_t newEnd = newOffset + ins->byteSize();
if (newEnd < newOffset)
return false;
MOZ_ASSERT(uint32_t(newEnd) >= uint32_t(newOffset));
// Determine the range of valid offsets which can be folded into this
// instruction and check whether our computed offset is within that range.
@@ -134,19 +131,19 @@ template<typename MAsmJSHeapAccessType>
void
EffectiveAddressAnalysis::analyzeAsmHeapAccess(MAsmJSHeapAccessType* ins)
{
MDefinition* ptr = ins->ptr();
MDefinition* base = ins->base();
if (ptr->isConstant()) {
if (base->isConstant()) {
// Look for heap[i] where i is a constant offset, and fold the offset.
// By doing the folding now, we simplify the task of codegen; the offset
// is always the address mode immediate. This also allows it to avoid
// a situation where the sum of a constant pointer value and a non-zero
// offset doesn't actually fit into the address mode immediate.
int32_t imm = ptr->toConstant()->toInt32();
int32_t imm = base->toConstant()->toInt32();
if (imm != 0 && tryAddDisplacement(ins, imm)) {
MInstruction* zero = MConstant::New(graph_.alloc(), Int32Value(0));
ins->block()->insertBefore(ins, zero);
ins->replacePtr(zero);
ins->replaceBase(zero);
}
// If the index is within the minimum heap length, we can optimize
@@ -156,18 +153,18 @@ EffectiveAddressAnalysis::analyzeAsmHeapAccess(MAsmJSHeapAccessType* ins)
if (end >= imm && (uint32_t)end <= mir_->minAsmJSHeapLength())
ins->removeBoundsCheck();
}
} else if (ptr->isAdd()) {
} else if (base->isAdd()) {
// Look for heap[a+i] where i is a constant offset, and fold the offset.
// Alignment masks have already been moved out of the way by the
// Alignment Mask Analysis pass.
MDefinition* op0 = ptr->toAdd()->getOperand(0);
MDefinition* op1 = ptr->toAdd()->getOperand(1);
MDefinition* op0 = base->toAdd()->getOperand(0);
MDefinition* op1 = base->toAdd()->getOperand(1);
if (op0->isConstant())
mozilla::Swap(op0, op1);
if (op1->isConstant()) {
int32_t imm = op1->toConstant()->toInt32();
if (tryAddDisplacement(ins, imm))
ins->replacePtr(op0);
ins->replaceBase(op0);
}
}
}
+12
View File
@@ -2093,6 +2093,18 @@ LIRGenerator::visitTruncateToInt32(MTruncateToInt32* truncate)
}
}
void
LIRGenerator::visitWrapInt64ToInt32(MWrapInt64ToInt32* ins)
{
define(new(alloc()) LWrapInt64ToInt32(useInt64AtStart(ins->input())), ins);
}
void
LIRGenerator::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins)
{
defineInt64(new(alloc()) LExtendInt32ToInt64(useAtStart(ins->input())), ins);
}
void
LIRGenerator::visitToString(MToString* ins)
{
+2
View File
@@ -158,6 +158,8 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitToFloat32(MToFloat32* convert);
void visitToInt32(MToInt32* convert);
void visitTruncateToInt32(MTruncateToInt32* truncate);
void visitWrapInt64ToInt32(MWrapInt64ToInt32* ins);
void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
void visitToString(MToString* convert);
void visitToObjectOrNull(MToObjectOrNull* convert);
void visitRegExp(MRegExp* ins);
+28 -3
View File
@@ -3722,6 +3722,31 @@ MTruncateToInt32::foldsTo(TempAllocator& alloc)
return this;
}
MDefinition*
MWrapInt64ToInt32::foldsTo(TempAllocator& alloc)
{
MDefinition* input = this->input();
if (input->isConstant()) {
int64_t c = input->toConstant()->toInt64();
return MConstant::New(alloc, Int32Value(int32_t(c)));
}
return this;
}
MDefinition*
MExtendInt32ToInt64::foldsTo(TempAllocator& alloc)
{
MDefinition* input = this->input();
if (input->isConstant()) {
int32_t c = input->toConstant()->toInt32();
int64_t res = isUnsigned() ? int64_t(uint32_t(c)) : int64_t(c);
return MConstant::NewInt64(alloc, res);
}
return this;
}
MDefinition*
MToDouble::foldsTo(TempAllocator& alloc)
{
@@ -4524,10 +4549,10 @@ MAsmJSLoadHeap::mightAlias(const MDefinition* def) const
const MAsmJSStoreHeap* store = def->toAsmJSStoreHeap();
if (store->accessType() != accessType())
return true;
if (!ptr()->isConstant() || !store->ptr()->isConstant())
if (!base()->isConstant() || !store->base()->isConstant())
return true;
const MConstant* otherPtr = store->ptr()->toConstant();
return ptr()->toConstant()->equals(otherPtr);
const MConstant* otherBase = store->base()->toConstant();
return base()->toConstant()->equals(otherBase);
}
return true;
}
+154 -55
View File
@@ -5264,6 +5264,101 @@ class MAsmJSUnsignedToFloat32
bool canProduceFloat32() const override { return true; }
};
class MWrapInt64ToInt32
: public MUnaryInstruction,
public NoTypePolicy::Data
{
explicit MWrapInt64ToInt32(MDefinition* def)
: MUnaryInstruction(def)
{
setResultType(MIRType_Int32);
setMovable();
}
public:
INSTRUCTION_HEADER(WrapInt64ToInt32)
static MWrapInt64ToInt32* NewAsmJS(TempAllocator& alloc, MDefinition* def) {
return new(alloc) MWrapInt64ToInt32(def);
}
MDefinition* foldsTo(TempAllocator& alloc) override;
bool congruentTo(const MDefinition* ins) const override {
return congruentIfOperandsEqual(ins);
}
AliasSet getAliasSet() const override {
return AliasSet::None();
}
};
class MExtendInt32ToInt64
: public MUnaryInstruction,
public NoTypePolicy::Data
{
bool isUnsigned_;
MExtendInt32ToInt64(MDefinition* def, bool isUnsigned)
: MUnaryInstruction(def),
isUnsigned_(isUnsigned)
{
setResultType(MIRType_Int64);
setMovable();
}
public:
INSTRUCTION_HEADER(ExtendInt32ToInt64)
static MExtendInt32ToInt64* NewAsmJS(TempAllocator& alloc, MDefinition* def, bool isUnsigned) {
return new(alloc) MExtendInt32ToInt64(def, isUnsigned);
}
bool isUnsigned() const { return isUnsigned_; }
MDefinition* foldsTo(TempAllocator& alloc) override;
bool congruentTo(const MDefinition* ins) const override {
if (!ins->isExtendInt32ToInt64())
return false;
if (ins->toExtendInt32ToInt64()->isUnsigned_ != isUnsigned_)
return false;
return congruentIfOperandsEqual(ins);
}
AliasSet getAliasSet() const override {
return AliasSet::None();
}
};
class MTruncateToInt64
: public MUnaryInstruction,
public NoTypePolicy::Data
{
bool isUnsigned_;
MTruncateToInt64(MDefinition* def, bool isUnsigned)
: MUnaryInstruction(def),
isUnsigned_(isUnsigned)
{
setResultType(MIRType_Int64);
setMovable();
}
public:
INSTRUCTION_HEADER(TruncateToInt64)
static MTruncateToInt64* NewAsmJS(TempAllocator& alloc, MDefinition* def, bool isUnsigned) {
return new(alloc) MTruncateToInt64(def, isUnsigned);
}
bool isUnsigned() const { return isUnsigned_; }
bool congruentTo(const MDefinition* ins) const override {
if (!ins->isTruncateToInt64())
return false;
if (ins->toTruncateToInt64()->isUnsigned_ != isUnsigned_)
return false;
return congruentIfOperandsEqual(ins);
}
AliasSet getAliasSet() const override {
return AliasSet::None();
}
};
// Converts a primitive (either typed or untyped) to an int32. If the input is
// not primitive at runtime, a bailout occurs. If the input cannot be converted
// to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs.
@@ -13798,7 +13893,8 @@ class MAsmJSNeg
class MAsmJSHeapAccess
{
int32_t offset_;
uint32_t offset_;
uint32_t align_;
Scalar::Type accessType_ : 8;
bool needsBoundsCheck_;
unsigned numSimdElems_;
@@ -13810,6 +13906,7 @@ class MAsmJSHeapAccess
MemoryBarrierBits barrierBefore = MembarNobits,
MemoryBarrierBits barrierAfter = MembarNobits)
: offset_(0),
align_(Scalar::byteSize(accessType)),
accessType_(accessType),
needsBoundsCheck_(true),
numSimdElems_(numSimdElems),
@@ -13819,8 +13916,9 @@ class MAsmJSHeapAccess
MOZ_ASSERT(numSimdElems <= ScalarTypeToLength(accessType));
}
int32_t offset() const { return offset_; }
int32_t endOffset() const { return offset() + byteSize(); }
uint32_t offset() const { return offset_; }
uint32_t endOffset() const { return offset() + byteSize(); }
uint32_t align() const { return align_; }
Scalar::Type accessType() const { return accessType_; }
unsigned byteSize() const {
return Scalar::isSimdType(accessType())
@@ -13830,10 +13928,13 @@ class MAsmJSHeapAccess
bool needsBoundsCheck() const { return needsBoundsCheck_; }
void removeBoundsCheck() { needsBoundsCheck_ = false; }
unsigned numSimdElems() const { MOZ_ASSERT(Scalar::isSimdType(accessType_)); return numSimdElems_; }
void setOffset(int32_t o) {
MOZ_ASSERT(o >= 0);
void setOffset(uint32_t o) {
offset_ = o;
}
void setAlign(uint32_t a) {
MOZ_ASSERT(mozilla::IsPowerOfTwo(a));
align_ = a;
}
MemoryBarrierBits barrierBefore() const { return barrierBefore_; }
MemoryBarrierBits barrierAfter() const { return barrierAfter_; }
bool isAtomicAccess() const { return (barrierBefore_|barrierAfter_) != MembarNobits; }
@@ -13844,17 +13945,16 @@ class MAsmJSLoadHeap
public MAsmJSHeapAccess,
public NoTypePolicy::Data
{
MAsmJSLoadHeap(Scalar::Type accessType, MDefinition* ptr, unsigned numSimdElems,
MemoryBarrierBits before, MemoryBarrierBits after)
: MUnaryInstruction(ptr),
MAsmJSHeapAccess(accessType, numSimdElems, before, after)
MAsmJSLoadHeap(MDefinition* base, const MAsmJSHeapAccess& access)
: MUnaryInstruction(base),
MAsmJSHeapAccess(access)
{
if (before|after)
if (access.barrierBefore()|access.barrierAfter())
setGuard(); // Not removable
else
setMovable();
switch (accessType) {
switch (access.accessType()) {
case Scalar::Int8:
case Scalar::Uint8:
case Scalar::Int16:
@@ -13884,17 +13984,14 @@ class MAsmJSLoadHeap
public:
INSTRUCTION_HEADER(AsmJSLoadHeap)
static MAsmJSLoadHeap* New(TempAllocator& alloc, Scalar::Type accessType,
MDefinition* ptr, unsigned numSimdElems = 0,
MemoryBarrierBits barrierBefore = MembarNobits,
MemoryBarrierBits barrierAfter = MembarNobits)
static MAsmJSLoadHeap* New(TempAllocator& alloc, MDefinition* base,
const MAsmJSHeapAccess& access)
{
return new(alloc) MAsmJSLoadHeap(accessType, ptr, numSimdElems,
barrierBefore, barrierAfter);
return new(alloc) MAsmJSLoadHeap(base, access);
}
MDefinition* ptr() const { return getOperand(0); }
void replacePtr(MDefinition* newPtr) { replaceOperand(0, newPtr); }
MDefinition* base() const { return getOperand(0); }
void replaceBase(MDefinition* newBase) { replaceOperand(0, newBase); }
bool congruentTo(const MDefinition* ins) const override;
AliasSet getAliasSet() const override {
@@ -13912,29 +14009,27 @@ class MAsmJSStoreHeap
public MAsmJSHeapAccess,
public NoTypePolicy::Data
{
MAsmJSStoreHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v,
unsigned numSimdElems, MemoryBarrierBits before, MemoryBarrierBits after)
: MBinaryInstruction(ptr, v),
MAsmJSHeapAccess(accessType, numSimdElems, before, after)
MAsmJSStoreHeap(MDefinition* base, const MAsmJSHeapAccess& access,
MDefinition* v)
: MBinaryInstruction(base, v),
MAsmJSHeapAccess(access)
{
if (before|after)
if (access.barrierBefore()|access.barrierAfter())
setGuard(); // Not removable
}
public:
INSTRUCTION_HEADER(AsmJSStoreHeap)
static MAsmJSStoreHeap* New(TempAllocator& alloc, Scalar::Type accessType,
MDefinition* ptr, MDefinition* v, unsigned numSimdElems = 0,
MemoryBarrierBits barrierBefore = MembarNobits,
MemoryBarrierBits barrierAfter = MembarNobits)
static MAsmJSStoreHeap* New(TempAllocator& alloc,
MDefinition* base, const MAsmJSHeapAccess& access,
MDefinition* v)
{
return new(alloc) MAsmJSStoreHeap(accessType, ptr, v, numSimdElems,
barrierBefore, barrierAfter);
return new(alloc) MAsmJSStoreHeap(base, access, v);
}
MDefinition* ptr() const { return getOperand(0); }
void replacePtr(MDefinition* newPtr) { replaceOperand(0, newPtr); }
MDefinition* base() const { return getOperand(0); }
void replaceBase(MDefinition* newBase) { replaceOperand(0, newBase); }
MDefinition* value() const { return getOperand(1); }
AliasSet getAliasSet() const override {
@@ -13947,10 +14042,10 @@ class MAsmJSCompareExchangeHeap
public MAsmJSHeapAccess,
public NoTypePolicy::Data
{
MAsmJSCompareExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* oldv,
MDefinition* newv)
: MTernaryInstruction(ptr, oldv, newv),
MAsmJSHeapAccess(accessType)
MAsmJSCompareExchangeHeap(MDefinition* base, const MAsmJSHeapAccess& access,
MDefinition* oldv, MDefinition* newv)
: MTernaryInstruction(base, oldv, newv),
MAsmJSHeapAccess(access)
{
setGuard(); // Not removable
setResultType(MIRType_Int32);
@@ -13959,14 +14054,14 @@ class MAsmJSCompareExchangeHeap
public:
INSTRUCTION_HEADER(AsmJSCompareExchangeHeap)
static MAsmJSCompareExchangeHeap* New(TempAllocator& alloc, Scalar::Type accessType,
MDefinition* ptr, MDefinition* oldv,
MDefinition* newv)
static MAsmJSCompareExchangeHeap* New(TempAllocator& alloc,
MDefinition* base, const MAsmJSHeapAccess& access,
MDefinition* oldv, MDefinition* newv)
{
return new(alloc) MAsmJSCompareExchangeHeap(accessType, ptr, oldv, newv);
return new(alloc) MAsmJSCompareExchangeHeap(base, access, oldv, newv);
}
MDefinition* ptr() const { return getOperand(0); }
MDefinition* base() const { return getOperand(0); }
MDefinition* oldValue() const { return getOperand(1); }
MDefinition* newValue() const { return getOperand(2); }
@@ -13980,9 +14075,10 @@ class MAsmJSAtomicExchangeHeap
public MAsmJSHeapAccess,
public NoTypePolicy::Data
{
MAsmJSAtomicExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* value)
: MBinaryInstruction(ptr, value),
MAsmJSHeapAccess(accessType)
MAsmJSAtomicExchangeHeap(MDefinition* base, const MAsmJSHeapAccess& access,
MDefinition* value)
: MBinaryInstruction(base, value),
MAsmJSHeapAccess(access)
{
setGuard(); // Not removable
setResultType(MIRType_Int32);
@@ -13991,13 +14087,14 @@ class MAsmJSAtomicExchangeHeap
public:
INSTRUCTION_HEADER(AsmJSAtomicExchangeHeap)
static MAsmJSAtomicExchangeHeap* New(TempAllocator& alloc, Scalar::Type accessType,
MDefinition* ptr, MDefinition* value)
static MAsmJSAtomicExchangeHeap* New(TempAllocator& alloc,
MDefinition* base, const MAsmJSHeapAccess& access,
MDefinition* value)
{
return new(alloc) MAsmJSAtomicExchangeHeap(accessType, ptr, value);
return new(alloc) MAsmJSAtomicExchangeHeap(base, access, value);
}
MDefinition* ptr() const { return getOperand(0); }
MDefinition* base() const { return getOperand(0); }
MDefinition* value() const { return getOperand(1); }
AliasSet getAliasSet() const override {
@@ -14012,9 +14109,10 @@ class MAsmJSAtomicBinopHeap
{
AtomicOp op_;
MAsmJSAtomicBinopHeap(AtomicOp op, Scalar::Type accessType, MDefinition* ptr, MDefinition* v)
: MBinaryInstruction(ptr, v),
MAsmJSHeapAccess(accessType),
MAsmJSAtomicBinopHeap(AtomicOp op, MDefinition* base, const MAsmJSHeapAccess& access,
MDefinition* v)
: MBinaryInstruction(base, v),
MAsmJSHeapAccess(access),
op_(op)
{
setGuard(); // Not removable
@@ -14024,14 +14122,15 @@ class MAsmJSAtomicBinopHeap
public:
INSTRUCTION_HEADER(AsmJSAtomicBinopHeap)
static MAsmJSAtomicBinopHeap* New(TempAllocator& alloc, AtomicOp op, Scalar::Type accessType,
MDefinition* ptr, MDefinition* v)
static MAsmJSAtomicBinopHeap* New(TempAllocator& alloc, AtomicOp op,
MDefinition* base, const MAsmJSHeapAccess& access,
MDefinition* v)
{
return new(alloc) MAsmJSAtomicBinopHeap(op, accessType, ptr, v);
return new(alloc) MAsmJSAtomicBinopHeap(op, base, access, v);
}
AtomicOp operation() const { return op_; }
MDefinition* ptr() const { return getOperand(0); }
MDefinition* base() const { return getOperand(0); }
MDefinition* value() const { return getOperand(1); }
AliasSet getAliasSet() const override {
+1
View File
@@ -229,6 +229,7 @@ class MIRGenerator
bool needsAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* access) const;
size_t foldableOffsetRange(const MAsmJSHeapAccess* access) const;
size_t foldableOffsetRange(bool accessNeedsBoundsCheck) const;
private:
GraphSpewer gs_;
+7 -1
View File
@@ -125,6 +125,12 @@ MIRGenerator::needsAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* access) const
size_t
MIRGenerator::foldableOffsetRange(const MAsmJSHeapAccess* access) const
{
return foldableOffsetRange(access->needsBoundsCheck());
}
size_t
MIRGenerator::foldableOffsetRange(bool accessNeedsBoundsCheck) const
{
// This determines whether it's ok to fold up to WasmImmediateSize
// offsets, instead of just WasmCheckedImmediateSize.
@@ -148,7 +154,7 @@ MIRGenerator::foldableOffsetRange(const MAsmJSHeapAccess* access) const
// On 32-bit platforms, if we've proven the access is in bounds after
// 32-bit wrapping, we can fold full offsets because they're added with
// 32-bit arithmetic.
if (sizeof(intptr_t) == sizeof(int32_t) && !access->needsBoundsCheck())
if (sizeof(intptr_t) == sizeof(int32_t) && !accessNeedsBoundsCheck)
return WasmImmediateRange;
// Otherwise, only allow the checked size. This is always less than the
+3
View File
@@ -118,6 +118,9 @@ namespace jit {
_(ToFloat32) \
_(ToInt32) \
_(TruncateToInt32) \
_(TruncateToInt64) \
_(WrapInt64ToInt32) \
_(ExtendInt32ToInt64) \
_(ToString) \
_(ToObjectOrNull) \
_(NewArray) \
+43 -30
View File
@@ -486,37 +486,41 @@ LIRGeneratorARM::visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins)
void
LIRGeneratorARM::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
{
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
LAllocation ptrAlloc;
MOZ_ASSERT(ins->offset() == 0);
// For the ARM it is best to keep the 'ptr' in a register if a bounds check is needed.
if (ptr->isConstant() && !ins->needsBoundsCheck()) {
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
LAllocation baseAlloc;
// For the ARM it is best to keep the 'base' in a register if a bounds check is needed.
if (base->isConstant() && !ins->needsBoundsCheck()) {
// A bounds check is only skipped for a positive index.
MOZ_ASSERT(ptr->toConstant()->toInt32() >= 0);
ptrAlloc = LAllocation(ptr->toConstant());
MOZ_ASSERT(base->toConstant()->toInt32() >= 0);
baseAlloc = LAllocation(base->toConstant());
} else {
ptrAlloc = useRegisterAtStart(ptr);
baseAlloc = useRegisterAtStart(base);
}
define(new(alloc()) LAsmJSLoadHeap(ptrAlloc), ins);
define(new(alloc()) LAsmJSLoadHeap(baseAlloc), ins);
}
void
LIRGeneratorARM::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
{
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
LAllocation ptrAlloc;
MOZ_ASSERT(ins->offset() == 0);
if (ptr->isConstant() && !ins->needsBoundsCheck()) {
MOZ_ASSERT(ptr->toConstant()->toInt32() >= 0);
ptrAlloc = LAllocation(ptr->toConstant());
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
LAllocation baseAlloc;
if (base->isConstant() && !ins->needsBoundsCheck()) {
MOZ_ASSERT(base->toConstant()->toInt32() >= 0);
baseAlloc = LAllocation(base->toConstant());
} else {
ptrAlloc = useRegisterAtStart(ptr);
baseAlloc = useRegisterAtStart(base);
}
add(new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value())), ins);
add(new(alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value())), ins);
}
void
@@ -678,13 +682,14 @@ void
LIRGeneratorARM::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
{
MOZ_ASSERT(ins->accessType() < Scalar::Float32);
MOZ_ASSERT(ins->offset() == 0);
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
if (byteSize(ins->accessType()) != 4 && !HasLDSTREXBHD()) {
LAsmJSCompareExchangeCallout* lir =
new(alloc()) LAsmJSCompareExchangeCallout(useRegisterAtStart(ptr),
new(alloc()) LAsmJSCompareExchangeCallout(useRegisterAtStart(base),
useRegisterAtStart(ins->oldValue()),
useRegisterAtStart(ins->newValue()));
defineReturn(lir, ins);
@@ -692,7 +697,7 @@ LIRGeneratorARM::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
}
LAsmJSCompareExchangeHeap* lir =
new(alloc()) LAsmJSCompareExchangeHeap(useRegister(ptr),
new(alloc()) LAsmJSCompareExchangeHeap(useRegister(base),
useRegister(ins->oldValue()),
useRegister(ins->newValue()));
@@ -702,32 +707,34 @@ LIRGeneratorARM::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
void
LIRGeneratorARM::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins)
{
MOZ_ASSERT(ins->ptr()->type() == MIRType_Int32);
MOZ_ASSERT(ins->base()->type() == MIRType_Int32);
MOZ_ASSERT(ins->accessType() < Scalar::Float32);
MOZ_ASSERT(ins->offset() == 0);
const LAllocation ptr = useRegisterAtStart(ins->ptr());
const LAllocation base = useRegisterAtStart(ins->base());
const LAllocation value = useRegisterAtStart(ins->value());
if (byteSize(ins->accessType()) < 4 && !HasLDSTREXBHD()) {
// Call out on ARMv6.
defineReturn(new(alloc()) LAsmJSAtomicExchangeCallout(ptr, value), ins);
defineReturn(new(alloc()) LAsmJSAtomicExchangeCallout(base, value), ins);
return;
}
define(new(alloc()) LAsmJSAtomicExchangeHeap(ptr, value), ins);
define(new(alloc()) LAsmJSAtomicExchangeHeap(base, value), ins);
}
void
LIRGeneratorARM::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
{
MOZ_ASSERT(ins->accessType() < Scalar::Float32);
MOZ_ASSERT(ins->offset() == 0);
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
if (byteSize(ins->accessType()) != 4 && !HasLDSTREXBHD()) {
LAsmJSAtomicBinopCallout* lir =
new(alloc()) LAsmJSAtomicBinopCallout(useRegisterAtStart(ptr),
new(alloc()) LAsmJSAtomicBinopCallout(useRegisterAtStart(base),
useRegisterAtStart(ins->value()));
defineReturn(lir, ins);
return;
@@ -735,7 +742,7 @@ LIRGeneratorARM::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
if (!ins->hasUses()) {
LAsmJSAtomicBinopHeapForEffect* lir =
new(alloc()) LAsmJSAtomicBinopHeapForEffect(useRegister(ptr),
new(alloc()) LAsmJSAtomicBinopHeapForEffect(useRegister(base),
useRegister(ins->value()),
/* flagTemp= */ temp());
add(lir, ins);
@@ -743,7 +750,7 @@ LIRGeneratorARM::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
}
LAsmJSAtomicBinopHeap* lir =
new(alloc()) LAsmJSAtomicBinopHeap(useRegister(ptr),
new(alloc()) LAsmJSAtomicBinopHeap(useRegister(base),
useRegister(ins->value()),
/* temp = */ LDefinition::BogusTemp(),
/* flagTemp= */ temp());
@@ -773,3 +780,9 @@ LIRGeneratorARM::visitRandom(MRandom* ins)
temp());
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
}
void
LIRGeneratorARM::visitTruncateToInt64(MTruncateToInt64* ins)
{
MOZ_CRASH("NY");
}
+1
View File
@@ -112,6 +112,7 @@ class LIRGeneratorARM : public LIRGeneratorShared
void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
void visitSubstr(MSubstr* ins);
void visitRandom(MRandom* ins);
void visitTruncateToInt64(MTruncateToInt64* ins);
};
typedef LIRGeneratorARM LIRGeneratorSpecific;
+6
View File
@@ -335,3 +335,9 @@ LIRGeneratorARM64::visitRandom(MRandom* ins)
temp());
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
}
void
LIRGeneratorARM64::visitTruncateToInt64(MTruncateToInt64* ins)
{
MOZ_CRASH("NY");
}
+1
View File
@@ -115,6 +115,7 @@ class LIRGeneratorARM64 : public LIRGeneratorShared
void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
void visitSubstr(MSubstr* ins);
void visitRandom(MRandom* ins);
void visitTruncateToInt64(MTruncateToInt64* ins);
};
typedef LIRGeneratorARM64 LIRGeneratorSpecific;
+42 -27
View File
@@ -322,36 +322,40 @@ LIRGeneratorMIPSShared::visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins
void
LIRGeneratorMIPSShared::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
{
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
LAllocation ptrAlloc;
MOZ_ASSERT(ins->offset() == 0);
// For MIPS it is best to keep the 'ptr' in a register if a bounds check
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
LAllocation baseAlloc;
// For MIPS it is best to keep the 'base' in a register if a bounds check
// is needed.
if (ptr->isConstant() && !ins->needsBoundsCheck()) {
if (base->isConstant() && !ins->needsBoundsCheck()) {
// A bounds check is only skipped for a positive index.
MOZ_ASSERT(ptr->toConstant()->toInt32() >= 0);
ptrAlloc = LAllocation(ptr->toConstant());
MOZ_ASSERT(base->toConstant()->toInt32() >= 0);
baseAlloc = LAllocation(base->toConstant());
} else
ptrAlloc = useRegisterAtStart(ptr);
baseAlloc = useRegisterAtStart(base);
define(new(alloc()) LAsmJSLoadHeap(ptrAlloc), ins);
define(new(alloc()) LAsmJSLoadHeap(baseAlloc), ins);
}
void
LIRGeneratorMIPSShared::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
{
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
LAllocation ptrAlloc;
MOZ_ASSERT(ins->offset() == 0);
if (ptr->isConstant() && !ins->needsBoundsCheck()) {
MOZ_ASSERT(ptr->toConstant()->toInt32() >= 0);
ptrAlloc = LAllocation(ptr->toConstant());
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
LAllocation baseAlloc;
if (base->isConstant() && !ins->needsBoundsCheck()) {
MOZ_ASSERT(base->toConstant()->toInt32() >= 0);
baseAlloc = LAllocation(base->toConstant());
} else
ptrAlloc = useRegisterAtStart(ptr);
baseAlloc = useRegisterAtStart(base);
add(new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value())), ins);
add(new(alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value())), ins);
}
void
@@ -408,6 +412,7 @@ LIRGeneratorMIPSShared::visitCompareExchangeTypedArrayElement(MCompareExchangeTy
{
MOZ_ASSERT(ins->arrayType() != Scalar::Float32);
MOZ_ASSERT(ins->arrayType() != Scalar::Float64);
MOZ_ASSERT(ins->offset() == 0);
MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
@@ -436,6 +441,7 @@ void
LIRGeneratorMIPSShared::visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins)
{
MOZ_ASSERT(ins->arrayType() <= Scalar::Uint32);
MOZ_ASSERT(ins->offset() == 0);
MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
@@ -465,12 +471,13 @@ void
LIRGeneratorMIPSShared::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
{
MOZ_ASSERT(ins->accessType() < Scalar::Float32);
MOZ_ASSERT(ins->offset() == 0);
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
LAsmJSCompareExchangeHeap* lir =
new(alloc()) LAsmJSCompareExchangeHeap(useRegister(ptr),
new(alloc()) LAsmJSCompareExchangeHeap(useRegister(base),
useRegister(ins->oldValue()),
useRegister(ins->newValue()),
/* valueTemp= */ temp(),
@@ -483,9 +490,10 @@ LIRGeneratorMIPSShared::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap*
void
LIRGeneratorMIPSShared::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins)
{
MOZ_ASSERT(ins->ptr()->type() == MIRType_Int32);
MOZ_ASSERT(ins->base()->type() == MIRType_Int32);
MOZ_ASSERT(ins->offset() == 0);
const LAllocation ptr = useRegister(ins->ptr());
const LAllocation base = useRegister(ins->base());
const LAllocation value = useRegister(ins->value());
// The output may not be used but will be clobbered regardless,
@@ -493,7 +501,7 @@ LIRGeneratorMIPSShared::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* i
// use the output register as a temp.
LAsmJSAtomicExchangeHeap* lir =
new(alloc()) LAsmJSAtomicExchangeHeap(ptr, value,
new(alloc()) LAsmJSAtomicExchangeHeap(base, value,
/* valueTemp= */ temp(),
/* offsetTemp= */ temp(),
/* maskTemp= */ temp());
@@ -504,13 +512,14 @@ void
LIRGeneratorMIPSShared::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
{
MOZ_ASSERT(ins->accessType() < Scalar::Float32);
MOZ_ASSERT(ins->offset() == 0);
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
if (!ins->hasUses()) {
LAsmJSAtomicBinopHeapForEffect* lir =
new(alloc()) LAsmJSAtomicBinopHeapForEffect(useRegister(ptr),
new(alloc()) LAsmJSAtomicBinopHeapForEffect(useRegister(base),
useRegister(ins->value()),
/* flagTemp= */ temp(),
/* valueTemp= */ temp(),
@@ -521,7 +530,7 @@ LIRGeneratorMIPSShared::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
}
LAsmJSAtomicBinopHeap* lir =
new(alloc()) LAsmJSAtomicBinopHeap(useRegister(ptr),
new(alloc()) LAsmJSAtomicBinopHeap(useRegister(base),
useRegister(ins->value()),
/* temp= */ LDefinition::BogusTemp(),
/* flagTemp= */ temp(),
@@ -574,3 +583,9 @@ LIRGeneratorMIPSShared::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayEleme
/* maskTemp= */ temp());
define(lir, ins);
}
void
LIRGeneratorMIPSShared::visitTruncateToInt64(MTruncateToInt64* ins)
{
MOZ_CRASH("NY");
}
@@ -96,6 +96,7 @@ class LIRGeneratorMIPSShared : public LIRGeneratorShared
void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
void visitSubstr(MSubstr* ins);
void visitTruncateToInt64(MTruncateToInt64* ins);
};
} // namespace jit
+1 -1
View File
@@ -94,7 +94,7 @@ class LIRGeneratorNone : public LIRGeneratorShared
void visitSubstr(MSubstr*) { MOZ_CRASH(); }
void visitSimdBinaryArith(js::jit::MSimdBinaryArith*) { MOZ_CRASH(); }
void visitRandom(js::jit::MRandom*) { MOZ_CRASH(); }
void visitTruncateToInt64(MTruncateToInt64*) { MOZ_CRASH(); }
};
typedef LIRGeneratorNone LIRGeneratorSpecific;
+26
View File
@@ -3963,6 +3963,32 @@ class LTruncateFToInt32 : public LInstructionHelper<1, 1, 1>
}
};
class LWrapInt64ToInt32 : public LInstructionHelper<1, INT64_PIECES, 0>
{
public:
LIR_HEADER(WrapInt64ToInt32)
static const size_t Input = 0;
explicit LWrapInt64ToInt32(const LInt64Allocation& input) {
setInt64Operand(Input, input);
}
};
class LExtendInt32ToInt64 : public LInstructionHelper<INT64_PIECES, 1, 0>
{
public:
LIR_HEADER(ExtendInt32ToInt64)
explicit LExtendInt32ToInt64(const LAllocation& input) {
setOperand(0, input);
}
const MExtendInt32ToInt64* mir() {
return mir_->toExtendInt32ToInt64();
}
};
// Convert a boolean value to a string.
class LBooleanToString : public LInstructionHelper<1, 1, 0>
{
+2
View File
@@ -189,6 +189,8 @@
_(Float32ToInt32) \
_(TruncateDToInt32) \
_(TruncateFToInt32) \
_(WrapInt64ToInt32) \
_(ExtendInt32ToInt64) \
_(BooleanToString) \
_(IntToString) \
_(DoubleToString) \
+6
View File
@@ -752,6 +752,12 @@ LIRGeneratorShared::useInt64(MDefinition* mir, bool useAtStart)
#endif
}
LInt64Allocation
LIRGeneratorShared::useInt64AtStart(MDefinition* mir)
{
return useInt64(mir, /* useAtStart = */ true);
}
LInt64Allocation
LIRGeneratorShared::useInt64Register(MDefinition* mir, bool useAtStart)
{
+1
View File
@@ -182,6 +182,7 @@ class LIRGeneratorShared : public MDefinitionVisitor
// Returns an int64 allocation for an Int64-typed instruction.
inline LInt64Allocation useInt64(MDefinition* mir, LUse::Policy policy, bool useAtStart);
inline LInt64Allocation useInt64(MDefinition* mir, bool useAtStart = false);
inline LInt64Allocation useInt64AtStart(MDefinition* mir);
inline LInt64Allocation useInt64OrConstant(MDefinition* mir, bool useAtStart = false);
inline LInt64Allocation useInt64Register(MDefinition* mir, bool useAtStart = false);
+3
View File
@@ -447,6 +447,9 @@ class Assembler : public AssemblerX86Shared
}
void movslq(const Operand& src, Register dest) {
switch (src.kind()) {
case Operand::REG:
masm.movslq_rr(src.reg(), dest.encoding());
break;
case Operand::MEM_REG_DISP:
masm.movslq_mr(src.disp(), src.base(), dest.encoding());
break;
+96
View File
@@ -1091,3 +1091,99 @@ CodeGeneratorX64::visitTruncateFToInt32(LTruncateFToInt32* ins)
// call a stub if it fails.
emitTruncateFloat32(input, output, ins->mir());
}
void
CodeGeneratorX64::visitWrapInt64ToInt32(LWrapInt64ToInt32* lir)
{
const LAllocation* input = lir->getOperand(0);
Register output = ToRegister(lir->output());
masm.movl(ToOperand(input), output);
}
void
CodeGeneratorX64::visitExtendInt32ToInt64(LExtendInt32ToInt64* lir)
{
const LAllocation* input = lir->getOperand(0);
Register output = ToRegister(lir->output());
if (lir->mir()->isUnsigned())
masm.movl(ToOperand(input), output);
else
masm.movslq(ToOperand(input), output);
}
void
CodeGeneratorX64::visitTruncateToInt64(LTruncateToInt64* lir)
{
FloatRegister input = ToFloatRegister(lir->input());
Register output = ToRegister(lir->output());
MIRType inputType = lir->mir()->input()->type();
bool isUnsigned = lir->mir()->isUnsigned();
// We should trap on invalid inputs, but for now we just return
// 0x8000000000000000. Note that we can remove some unnecessary jumps
// once we get rid of this trap Label.
Label trap;
Label done;
if (isUnsigned) {
FloatRegister tempDouble = ToFloatRegister(lir->temp());
// If the input < INT64_MAX, vcvttsd2sq will do the right thing, so
// we use it directly. Else, we subtract INT64_MAX, convert to int64,
// and then add INT64_MAX to the result.
if (inputType == MIRType_Double) {
Label isLarge;
masm.loadConstantDouble(double(0x8000000000000000), ScratchDoubleReg);
masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, ScratchDoubleReg, &isLarge);
masm.vcvttsd2sq(input, output);
masm.branchTestPtr(Assembler::Signed, output, output, &trap);
masm.jump(&done);
masm.bind(&isLarge);
masm.moveDouble(input, tempDouble);
masm.subDouble(ScratchDoubleReg, tempDouble);
masm.vcvttsd2sq(tempDouble, output);
masm.branchTestPtr(Assembler::Signed, output, output, &trap);
masm.or64(Imm64(0x8000000000000000), Register64(output));
masm.jump(&done);
} else {
MOZ_ASSERT(inputType == MIRType_Float32);
Label isLarge;
masm.loadConstantFloat32(float(0x8000000000000000), ScratchDoubleReg);
masm.branchFloat(Assembler::DoubleGreaterThanOrEqual, input, ScratchDoubleReg, &isLarge);
masm.vcvttss2sq(input, output);
masm.branchTestPtr(Assembler::Signed, output, output, &trap);
masm.jump(&done);
masm.bind(&isLarge);
masm.moveFloat32(input, tempDouble);
masm.vsubss(ScratchDoubleReg, tempDouble, tempDouble);
masm.vcvttss2sq(tempDouble, output);
masm.branchTestPtr(Assembler::Signed, output, output, &trap);
masm.or64(Imm64(0x8000000000000000), Register64(output));
masm.jump(&done);
}
} else {
if (inputType == MIRType_Double) {
masm.vcvttsd2sq(input, output);
masm.cmpq(Imm32(1), output);
masm.j(Assembler::Overflow, &trap);
masm.jump(&done);
} else {
MOZ_ASSERT(inputType == MIRType_Float32);
masm.vcvttss2sq(input, output);
masm.cmpq(Imm32(1), output);
masm.j(Assembler::Overflow, &trap);
masm.jump(&done);
}
}
masm.bind(&trap);
masm.movePtr(ImmWord(0x8000000000000000), output);
masm.bind(&done);
}
+3
View File
@@ -53,6 +53,9 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared
void visitUDivOrMod64(LUDivOrMod64* lir);
void visitTruncateDToInt32(LTruncateDToInt32* ins);
void visitTruncateFToInt32(LTruncateFToInt32* ins);
void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
void visitTruncateToInt64(LTruncateToInt64* lir);
void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
void visitAsmJSCall(LAsmJSCall* ins);
+19
View File
@@ -164,6 +164,25 @@ class LUDivOrMod64 : public LBinaryMath<1>
}
};
class LTruncateToInt64 : public LInstructionHelper<1, 1, 1>
{
public:
LIR_HEADER(TruncateToInt64);
LTruncateToInt64(const LAllocation& in, const LDefinition& temp) {
setOperand(0, in);
setTemp(0, temp);
}
MTruncateToInt64* mir() const {
return mir_->toTruncateToInt64();
}
const LDefinition* temp() {
return getTemp(0);
}
};
} // namespace jit
} // namespace js
+1
View File
@@ -13,6 +13,7 @@
_(DivOrModConstantI) \
_(DivOrModI64) \
_(UDivOrMod64) \
_(TruncateToInt64) \
_(SimdValueInt32x4) \
_(SimdValueFloat32x4) \
_(UDivOrMod) \
+33 -23
View File
@@ -158,29 +158,29 @@ LIRGeneratorX64::visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins)
void
LIRGeneratorX64::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
{
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
// For simplicity, require a register if we're going to emit a bounds-check
// branch, so that we don't have special cases for constants.
LAllocation ptrAlloc = gen->needsAsmJSBoundsCheckBranch(ins)
? useRegisterAtStart(ptr)
: useRegisterOrZeroAtStart(ptr);
LAllocation baseAlloc = gen->needsAsmJSBoundsCheckBranch(ins)
? useRegisterAtStart(base)
: useRegisterOrZeroAtStart(base);
define(new(alloc()) LAsmJSLoadHeap(ptrAlloc), ins);
define(new(alloc()) LAsmJSLoadHeap(baseAlloc), ins);
}
void
LIRGeneratorX64::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
{
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
// For simplicity, require a register if we're going to emit a bounds-check
// branch, so that we don't have special cases for constants.
LAllocation ptrAlloc = gen->needsAsmJSBoundsCheckBranch(ins)
? useRegisterAtStart(ptr)
: useRegisterOrZeroAtStart(ptr);
LAllocation baseAlloc = gen->needsAsmJSBoundsCheckBranch(ins)
? useRegisterAtStart(base)
: useRegisterOrZeroAtStart(base);
LAsmJSStoreHeap* lir = nullptr; // initialize to silence GCC warning
switch (ins->accessType()) {
@@ -190,13 +190,13 @@ LIRGeneratorX64::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
case Scalar::Uint16:
case Scalar::Int32:
case Scalar::Uint32:
lir = new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterOrConstantAtStart(ins->value()));
lir = new(alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterOrConstantAtStart(ins->value()));
break;
case Scalar::Float32:
case Scalar::Float64:
case Scalar::Float32x4:
case Scalar::Int32x4:
lir = new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value()));
lir = new(alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value()));
break;
case Scalar::Uint8Clamped:
case Scalar::MaxTypedArrayViewType:
@@ -208,8 +208,8 @@ LIRGeneratorX64::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
void
LIRGeneratorX64::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
{
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
// The output may not be used but will be clobbered regardless, so
// pin the output to eax.
@@ -220,7 +220,7 @@ LIRGeneratorX64::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
const LAllocation newval = useRegister(ins->newValue());
LAsmJSCompareExchangeHeap* lir =
new(alloc()) LAsmJSCompareExchangeHeap(useRegister(ptr), oldval, newval);
new(alloc()) LAsmJSCompareExchangeHeap(useRegister(base), oldval, newval);
defineFixed(lir, ins, LAllocation(AnyRegister(eax)));
}
@@ -228,9 +228,9 @@ LIRGeneratorX64::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
void
LIRGeneratorX64::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins)
{
MOZ_ASSERT(ins->ptr()->type() == MIRType_Int32);
MOZ_ASSERT(ins->base()->type() == MIRType_Int32);
const LAllocation ptr = useRegister(ins->ptr());
const LAllocation base = useRegister(ins->base());
const LAllocation value = useRegister(ins->value());
// The output may not be used but will be clobbered regardless,
@@ -238,15 +238,15 @@ LIRGeneratorX64::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins)
// use the output register as a temp.
LAsmJSAtomicExchangeHeap* lir =
new(alloc()) LAsmJSAtomicExchangeHeap(ptr, value);
new(alloc()) LAsmJSAtomicExchangeHeap(base, value);
define(lir, ins);
}
void
LIRGeneratorX64::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
{
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
// Case 1: the result of the operation is not used.
//
@@ -255,7 +255,7 @@ LIRGeneratorX64::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
if (!ins->hasUses()) {
LAsmJSAtomicBinopHeapForEffect* lir =
new(alloc()) LAsmJSAtomicBinopHeapForEffect(useRegister(ptr),
new(alloc()) LAsmJSAtomicBinopHeapForEffect(useRegister(base),
useRegisterOrConstant(ins->value()));
add(lir, ins);
return;
@@ -296,7 +296,7 @@ LIRGeneratorX64::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
}
LAsmJSAtomicBinopHeap* lir =
new(alloc()) LAsmJSAtomicBinopHeap(useRegister(ptr),
new(alloc()) LAsmJSAtomicBinopHeap(useRegister(base),
value,
bitOp ? temp() : LDefinition::BogusTemp());
@@ -385,3 +385,13 @@ LIRGeneratorX64::lowerUMod64(MMod* mod)
tempFixed(rax));
defineInt64Fixed(lir, mod, LInt64Allocation(LAllocation(AnyRegister(rdx))));
}
void
LIRGeneratorX64::visitTruncateToInt64(MTruncateToInt64* ins)
{
MDefinition* opd = ins->input();
MOZ_ASSERT(opd->type() == MIRType_Double || opd->type() == MIRType_Float32);
LDefinition maybeTemp = ins->isUnsigned() ? tempDouble() : LDefinition::BogusTemp();
defineInt64(new(alloc()) LTruncateToInt64(useRegister(opd), maybeTemp), ins);
}
+1
View File
@@ -59,6 +59,7 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared
void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
void visitSubstr(MSubstr* ins);
void visitRandom(MRandom* ins);
void visitTruncateToInt64(MTruncateToInt64* ins);
};
typedef LIRGeneratorX64 LIRGeneratorSpecific;
+1 -1
View File
@@ -662,7 +662,7 @@ CodeGeneratorX86::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
void
CodeGeneratorX86::asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg, bool boundsCheck,
int32_t offset, int32_t endOffset)
uint32_t offset, uint32_t endOffset)
{
uint32_t maybeCmpOffset = wasm::HeapAccess::NoLengthCheck;
+1 -1
View File
@@ -74,7 +74,7 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared
private:
void asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg, bool boundsCheck,
int32_t offset, int32_t endOffset);
uint32_t offset, uint32_t endOffset);
};
typedef CodeGeneratorX86 CodeGeneratorSpecific;
+29 -23
View File
@@ -206,35 +206,35 @@ LIRGeneratorX86::visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins)
void
LIRGeneratorX86::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
{
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
// For simplicity, require a register if we're going to emit a bounds-check
// branch, so that we don't have special cases for constants.
LAllocation ptrAlloc = gen->needsAsmJSBoundsCheckBranch(ins)
? useRegisterAtStart(ptr)
: useRegisterOrZeroAtStart(ptr);
LAllocation baseAlloc = gen->needsAsmJSBoundsCheckBranch(ins)
? useRegisterAtStart(base)
: useRegisterOrZeroAtStart(base);
define(new(alloc()) LAsmJSLoadHeap(ptrAlloc), ins);
define(new(alloc()) LAsmJSLoadHeap(baseAlloc), ins);
}
void
LIRGeneratorX86::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
{
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
// For simplicity, require a register if we're going to emit a bounds-check
// branch, so that we don't have special cases for constants.
LAllocation ptrAlloc = gen->needsAsmJSBoundsCheckBranch(ins)
? useRegisterAtStart(ptr)
: useRegisterOrZeroAtStart(ptr);
LAllocation baseAlloc = gen->needsAsmJSBoundsCheckBranch(ins)
? useRegisterAtStart(base)
: useRegisterOrZeroAtStart(base);
LAsmJSStoreHeap* lir = nullptr;
switch (ins->accessType()) {
case Scalar::Int8: case Scalar::Uint8:
// See comment for LIRGeneratorX86::useByteOpRegister.
lir = new(alloc()) LAsmJSStoreHeap(ptrAlloc, useFixed(ins->value(), eax));
lir = new(alloc()) LAsmJSStoreHeap(baseAlloc, useFixed(ins->value(), eax));
break;
case Scalar::Int16: case Scalar::Uint16:
case Scalar::Int32: case Scalar::Uint32:
@@ -242,7 +242,7 @@ LIRGeneratorX86::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
case Scalar::Float32x4: case Scalar::Int32x4:
// For now, don't allow constant values. The immediate operand
// affects instruction layout which affects patching.
lir = new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value()));
lir = new(alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value()));
break;
case Scalar::Uint8Clamped:
case Scalar::MaxTypedArrayViewType:
@@ -280,8 +280,8 @@ LIRGeneratorX86::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
{
MOZ_ASSERT(ins->accessType() < Scalar::Float32);
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
bool byteArray = byteSize(ins->accessType()) == 1;
@@ -302,7 +302,7 @@ LIRGeneratorX86::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
const LAllocation newval = byteArray ? useFixed(ins->newValue(), ebx) : useRegister(ins->newValue());
LAsmJSCompareExchangeHeap* lir =
new(alloc()) LAsmJSCompareExchangeHeap(useRegister(ptr), oldval, newval);
new(alloc()) LAsmJSCompareExchangeHeap(useRegister(base), oldval, newval);
lir->setAddrTemp(temp());
defineFixed(lir, ins, LAllocation(AnyRegister(eax)));
@@ -311,13 +311,13 @@ LIRGeneratorX86::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
void
LIRGeneratorX86::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins)
{
MOZ_ASSERT(ins->ptr()->type() == MIRType_Int32);
MOZ_ASSERT(ins->base()->type() == MIRType_Int32);
const LAllocation ptr = useRegister(ins->ptr());
const LAllocation base = useRegister(ins->base());
const LAllocation value = useRegister(ins->value());
LAsmJSAtomicExchangeHeap* lir =
new(alloc()) LAsmJSAtomicExchangeHeap(ptr, value);
new(alloc()) LAsmJSAtomicExchangeHeap(base, value);
lir->setAddrTemp(temp());
if (byteSize(ins->accessType()) == 1)
@@ -331,8 +331,8 @@ LIRGeneratorX86::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
{
MOZ_ASSERT(ins->accessType() < Scalar::Float32);
MDefinition* ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType_Int32);
bool byteArray = byteSize(ins->accessType()) == 1;
@@ -348,7 +348,7 @@ LIRGeneratorX86::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
else
value = useRegisterOrConstant(ins->value());
LAsmJSAtomicBinopHeapForEffect* lir =
new(alloc()) LAsmJSAtomicBinopHeapForEffect(useRegister(ptr), value);
new(alloc()) LAsmJSAtomicBinopHeapForEffect(useRegister(base), value);
lir->setAddrTemp(temp());
add(lir, ins);
return;
@@ -403,7 +403,7 @@ LIRGeneratorX86::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
}
LAsmJSAtomicBinopHeap* lir =
new(alloc()) LAsmJSAtomicBinopHeap(useRegister(ptr), value, tempDef);
new(alloc()) LAsmJSAtomicBinopHeap(useRegister(base), value, tempDef);
lir->setAddrTemp(temp());
if (byteArray || bitOp)
@@ -458,3 +458,9 @@ LIRGeneratorX86::visitRandom(MRandom* ins)
temp());
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
}
void
LIRGeneratorX86::visitTruncateToInt64(MTruncateToInt64* ins)
{
MOZ_CRASH("NY");
}
+1
View File
@@ -63,6 +63,7 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared
void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
void visitSubstr(MSubstr* ins);
void visitRandom(MRandom* ins);
void visitTruncateToInt64(MTruncateToInt64* ins);
void lowerPhi(MPhi* phi);
static bool allowTypedElementHoleCheck() {
+2
View File
@@ -367,6 +367,8 @@ MSG_DEF(JSMSG_WASM_FAIL, 1, JSEXN_TYPEERR, "wasm error: {0}")
MSG_DEF(JSMSG_WASM_DECODE_FAIL, 2, JSEXN_TYPEERR, "wasm validation error at offset {0}: {1}")
MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {0}")
MSG_DEF(JSMSG_WASM_BAD_IND_CALL, 0, JSEXN_ERR, "wasm indirect call signature mismatch")
MSG_DEF(JSMSG_WASM_BAD_BUF_ARG, 0, JSEXN_TYPEERR, "first argument must be an ArrayBuffer")
MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG, 0, JSEXN_TYPEERR, "second argument, if present, must be an object")
// Proxy
MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
+12
View File
@@ -1102,6 +1102,7 @@ class JS_PUBLIC_API(RuntimeOptions) {
: baseline_(true),
ion_(true),
asmJS_(true),
wasm_(false),
throwOnAsmJSValidationFailure_(false),
nativeRegExp_(true),
unboxedArrays_(false),
@@ -1149,6 +1150,16 @@ class JS_PUBLIC_API(RuntimeOptions) {
return *this;
}
bool wasm() const { return wasm_; }
RuntimeOptions& setWasm(bool flag) {
wasm_ = flag;
return *this;
}
RuntimeOptions& toggleWasm() {
wasm_ = !wasm_;
return *this;
}
bool throwOnAsmJSValidationFailure() const { return throwOnAsmJSValidationFailure_; }
RuntimeOptions& setThrowOnAsmJSValidationFailure(bool flag) {
throwOnAsmJSValidationFailure_ = flag;
@@ -1229,6 +1240,7 @@ class JS_PUBLIC_API(RuntimeOptions) {
bool baseline_ : 1;
bool ion_ : 1;
bool asmJS_ : 1;
bool wasm_ : 1;
bool throwOnAsmJSValidationFailure_ : 1;
bool nativeRegExp_ : 1;
bool unboxedArrays_ : 1;
+4 -3
View File
@@ -106,9 +106,10 @@ IF_BDATA(real,imaginary)(TypedObject, 40, InitTypedObjectModuleObj
real(Reflect, 41, InitReflect, nullptr) \
IF_SIMD(real,imaginary)(SIMD, 42, InitSimdClass, OCLASP(Simd)) \
real(WeakSet, 43, InitWeakSetClass, OCLASP(WeakSet)) \
real(TypedArray, 44, InitViaClassSpec, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \
IF_SAB(real,imaginary)(Atomics, 45, InitAtomicsClass, OCLASP(Atomics)) \
real(SavedFrame, 46, InitViaClassSpec, &js::SavedFrame::class_) \
real(TypedArray, 44, InitViaClassSpec, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \
IF_SAB(real,imaginary)(Atomics, 45, InitAtomicsClass, OCLASP(Atomics)) \
real(SavedFrame, 46, InitViaClassSpec, &js::SavedFrame::class_) \
real(Wasm, 47, InitWasmClass, CLASP(Wasm)) \
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
+2
View File
@@ -6572,6 +6572,7 @@ SetRuntimeOptions(JSRuntime* rt, const OptionParser& op)
JS::RuntimeOptionsRef(rt).setBaseline(enableBaseline)
.setIon(enableIon)
.setAsmJS(enableAsmJS)
.setWasm(true)
.setNativeRegExp(enableNativeRegExp)
.setUnboxedArrays(enableUnboxedArrays);
@@ -6829,6 +6830,7 @@ SetWorkerRuntimeOptions(JSRuntime* rt)
JS::RuntimeOptionsRef(rt).setBaseline(enableBaseline)
.setIon(enableIon)
.setAsmJS(enableAsmJS)
.setWasm(true)
.setNativeRegExp(enableNativeRegExp)
.setUnboxedArrays(enableUnboxedArrays);
rt->setOffthreadIonCompilationEnabled(offthreadCompilation);
+2 -2
View File
@@ -373,7 +373,7 @@ AllocateWasmMappedMemory(uint32_t numBytes)
if (!data)
return nullptr;
if (!VirtualAlloc(data, numBytes, MEM_COMMIT, PAGE_READWRITE)) {
if (numBytes && !VirtualAlloc(data, numBytes, MEM_COMMIT, PAGE_READWRITE)) {
VirtualFree(data, 0, MEM_RELEASE);
return nullptr;
}
@@ -398,7 +398,7 @@ AllocateWasmMappedMemory(uint32_t numBytes)
if (data == MAP_FAILED)
return nullptr;
if (mprotect(data, numBytes, PROT_READ | PROT_WRITE)) {
if (numBytes && mprotect(data, numBytes, PROT_READ | PROT_WRITE)) {
munmap(data, wasm::MappedSize);
return nullptr;
}
+4
View File
@@ -15,6 +15,7 @@
#include "jsprototypes.h"
#include "jsweakmap.h"
#include "asmjs/Wasm.h"
#include "builtin/AtomicsObject.h"
#include "builtin/Eval.h"
#if EXPOSE_INTL_API
@@ -93,6 +94,9 @@ js::GlobalObject::getTypedObjectModule() const {
/* static */ bool
GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key)
{
if (key == JSProto_Wasm)
return !cx->runtime()->options().wasm();
#ifdef ENABLE_SHARED_ARRAY_BUFFER
// Return true if the given constructor has been disabled at run-time.
switch (key) {
+2
View File
@@ -1540,6 +1540,7 @@ ReloadPrefsCallback(const char* pref, void* data)
bool useBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "baselinejit") && !safeMode;
bool useIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion") && !safeMode;
bool useAsmJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "asmjs") && !safeMode;
bool useWasm = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm") && !safeMode;
bool throwOnAsmJSValidationFailure = Preferences::GetBool(JS_OPTIONS_DOT_STR
"throw_on_asmjs_validation_failure");
bool useNativeRegExp = Preferences::GetBool(JS_OPTIONS_DOT_STR "native_regexp") && !safeMode;
@@ -1574,6 +1575,7 @@ ReloadPrefsCallback(const char* pref, void* data)
JS::RuntimeOptionsRef(rt).setBaseline(useBaseline)
.setIon(useIon)
.setAsmJS(useAsmJS)
.setWasm(useWasm)
.setThrowOnAsmJSValidationFailure(throwOnAsmJSValidationFailure)
.setNativeRegExp(useNativeRegExp)
.setAsyncStack(useAsyncStack)
+2 -1
View File
@@ -939,7 +939,8 @@ ProcessArgsForCompartment(JSContext* cx, char** argv, int argc)
break;
case 'I':
RuntimeOptionsRef(cx).toggleIon()
.toggleAsmJS();
.toggleAsmJS()
.toggleWasm();
break;
}
}
+81 -1
View File
@@ -14,10 +14,13 @@
#include "mozilla/EventDispatcher.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/gfx/PathHelpers.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include "mozilla/layers/PAPZ.h"
#include "mozilla/Likely.h"
#include "mozilla/Maybe.h"
#include "mozilla/unused.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/unused.h"
#include "nsCharTraits.h"
#include "nsFontMetrics.h"
#include "nsPresContext.h"
@@ -9099,3 +9102,80 @@ nsLayoutUtils::GetBoundingContentRect(const nsIContent* aContent,
}
return result;
}
static already_AddRefed<nsIPresShell>
GetPresShell(const nsIContent* aContent)
{
nsCOMPtr<nsIPresShell> result;
if (nsIDocument* doc = aContent->GetComposedDoc()) {
result = doc->GetShell();
}
return result.forget();
}
static void UpdateDisplayPortMarginsForPendingMetrics(FrameMetrics& aMetrics) {
nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId());
if (!content) {
return;
}
nsCOMPtr<nsIPresShell> shell = GetPresShell(content);
if (!shell) {
return;
}
MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());
if (gfxPrefs::APZAllowZooming() && aMetrics.IsRootContent()) {
// See APZCCallbackHelper::UpdateRootFrame for details.
float presShellResolution = shell->GetResolution();
if (presShellResolution != aMetrics.GetPresShellResolution()) {
return;
}
}
nsIScrollableFrame* frame = nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId());
if (!frame) {
return;
}
if (APZCCallbackHelper::IsScrollInProgress(frame)) {
// If these conditions are true, then the UpdateFrame
// message may be ignored by the main-thread, so we
// shouldn't update the displayport based on it.
return;
}
DisplayPortMarginsPropertyData* currentData =
static_cast<DisplayPortMarginsPropertyData*>(content->GetProperty(nsGkAtoms::DisplayPortMargins));
if (!currentData || currentData->mPriority > 0) {
return;
}
CSSPoint frameScrollOffset = CSSPoint::FromAppUnits(frame->GetScrollPosition());
APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics, frameScrollOffset);
content->SetProperty(nsGkAtoms::DisplayPortMargins,
new DisplayPortMarginsPropertyData(aMetrics.GetDisplayPortMargins(), 0),
nsINode::DeleteProperty<DisplayPortMarginsPropertyData>);
}
/* static */ void
nsLayoutUtils::UpdateDisplayPortMarginsFromPendingMessages() {
if (mozilla::dom::ContentChild::GetSingleton() &&
mozilla::dom::ContentChild::GetSingleton()->GetIPCChannel()) {
mozilla::dom::ContentChild::GetSingleton()->GetIPCChannel()->PeekMessages(
mozilla::layers::PAPZ::Msg_UpdateFrame__ID,
[](const IPC::Message& aMsg) {
void* iter = nullptr;
FrameMetrics frame;
if (!IPC::ReadParam(&aMsg, &iter, &frame)) {
MOZ_ASSERT(false);
return;
}
UpdateDisplayPortMarginsForPendingMetrics(frame);
});
}
}
+10
View File
@@ -185,6 +185,16 @@ public:
*/
static bool HasDisplayPort(nsIContent* aContent);
/**
* Go through the IPC Channel and update displayport margins for content
* elements based on UpdateFrame messages. The messages are left in the
* queue and will be fully processed when dequeued. The aim is to paint
* the most up-to-date displayport without waiting for these message to
* go through the message queue.
*/
static void UpdateDisplayPortMarginsFromPendingMessages();
/**
* @return the display port for the given element which should be used for
* visibility testing purposes.
+36 -29
View File
@@ -1472,35 +1472,10 @@ TakeFrameRequestCallbacksFrom(nsIDocument* aDocument,
}
static bool
DispatchAnimationEventsOnSubDocuments(nsIDocument* aDocument,
void* aRefreshDriver)
CollectDocuments(nsIDocument* aDocument, void* aDocArray)
{
nsIPresShell* shell = aDocument->GetShell();
if (!shell) {
return true;
}
RefPtr<nsPresContext> context = shell->GetPresContext();
if (!context || context->RefreshDriver() != aRefreshDriver) {
return true;
}
nsCOMPtr<nsIDocument> kungFuDeathGrip(aDocument);
context->TransitionManager()->SortEvents();
context->AnimationManager()->SortEvents();
// Dispatch transition events first since transitions conceptually sit
// below animations in terms of compositing order.
context->TransitionManager()->DispatchEvents();
// Check that the presshell has not been destroyed
if (context->GetPresShell()) {
context->AnimationManager()->DispatchEvents();
}
aDocument->EnumerateSubDocuments(DispatchAnimationEventsOnSubDocuments,
aRefreshDriver);
static_cast<nsCOMArray<nsIDocument>*>(aDocArray)->AppendObject(aDocument);
aDocument->EnumerateSubDocuments(CollectDocuments, aDocArray);
return true;
}
@@ -1511,7 +1486,32 @@ nsRefreshDriver::DispatchAnimationEvents()
return;
}
DispatchAnimationEventsOnSubDocuments(mPresContext->Document(), this);
nsCOMArray<nsIDocument> documents;
CollectDocuments(mPresContext->Document(), &documents);
for (int32_t i = 0; i < documents.Count(); ++i) {
nsIDocument* doc = documents[i];
nsIPresShell* shell = doc->GetShell();
if (!shell) {
continue;
}
RefPtr<nsPresContext> context = shell->GetPresContext();
if (!context || context->RefreshDriver() != this) {
continue;
}
context->TransitionManager()->SortEvents();
context->AnimationManager()->SortEvents();
// Dispatch transition events first since transitions conceptually sit
// below animations in terms of compositing order.
context->TransitionManager()->DispatchEvents();
// Check that the presshell has not been destroyed
if (context->GetPresShell()) {
context->AnimationManager()->DispatchEvents();
}
}
}
void
@@ -1668,6 +1668,13 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
gfxPlatform::GetPlatform()->SchedulePaintIfDeviceReset();
// We want to process any pending APZ metrics ahead of their positions
// in the queue. This will prevent us from spending precious time
// painting a stale displayport.
if (gfxPrefs::APZPeekMessages()) {
nsLayoutUtils::UpdateDisplayPortMarginsFromPendingMessages();
}
/*
* The timer holds a reference to |this| while calling |Notify|.
* However, implementations of |WillRefresh| are permitted to destroy
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<style>
@font-face {
font-family: test;
src: url('../fonts/gw1270797.ttf') format("truetype");
}
html {
font-size: 72pt;
font-family: test, sans-serif;
writing-mode: vertical-rl;
text-orientation: mixed;
}
</style>
</head>
<body>
&#xFA19;&#xFA19;&#xFA19;&#xFA19;
</body>
</html>
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<style>
@font-face {
font-family: test;
src: url('../fonts/gw1270797.ttf') format("truetype");
}
html {
font-size: 72pt;
font-family: test, sans-serif;
writing-mode: vertical-rl;
text-orientation: mixed;
}
</style>
</head>
<body>
&#xFA19;&#x795E;&#xE0100;&#x795E;&#xE0103;&#x795E;&#xFE00;
</body>
</html>
@@ -166,6 +166,8 @@ fuzzy-if(gtkWidget||B2G,255,6) fuzzy-if(cocoaWidget,65,69) == 1193519-sideways-l
== 1216747-1.html 1216747-1-ref.html
!= 1216747-1.html 1216747-1-notref.html
HTTP(..) == 1248248-1-orientation-break-glyphrun.html 1248248-1-orientation-break-glyphrun-ref.html
# Suite of tests from Gérard Talbot in bug 1079151
include abspos/reftest.list
+9 -2
View File
@@ -79,7 +79,12 @@ private:
class Singleton
{
public:
Singleton() : mValue(ReplaceMalloc::GetDMDFuncs()), mInitialized(true) {}
Singleton()
: mValue(ReplaceMalloc::GetDMDFuncs())
#ifdef DEBUG
, mInitialized(true)
#endif
{}
DMDFuncs* Get()
{
@@ -89,7 +94,9 @@ private:
private:
DMDFuncs* mValue;
DebugOnly<bool> mInitialized;
#ifdef DEBUG
bool mInitialized;
#endif
};
// This singleton pointer must be defined on the program side. In Gecko,
+7 -5
View File
@@ -24,17 +24,19 @@ namespace mozilla {
* DebugOnly<bool> check = func();
* MOZ_ASSERT(check);
*
* more concisely than declaring |check| conditional on #ifdef DEBUG, but also
* without allocating storage space for |check| in release builds.
* more concisely than declaring |check| conditional on #ifdef DEBUG.
*
* DebugOnly instances can only be coerced to T in debug builds. In release
* builds they don't have a value, so type coercion is not well defined.
*
* Note that DebugOnly instances still take up one byte of space, plus padding,
* when used as members of structs.
* NOTE: DebugOnly instances still take up one byte of space, plus padding, even
* in optimized, non-DEBUG builds (see bug 1253094 comment 37 for more info).
* For this reason the class is MOZ_STACK_CLASS to prevent consumers using
* DebugOnly for struct/class members and unwittingly inflating the size of
* their objects in release builds.
*/
template<typename T>
class DebugOnly
class MOZ_STACK_CLASS DebugOnly
{
public:
#ifdef DEBUG
+3
View File
@@ -606,6 +606,8 @@ pref("apz.overscroll.stop_distance_threshold", "5.0");
pref("apz.overscroll.stop_velocity_threshold", "0.01");
pref("apz.overscroll.stretch_factor", "0.35");
pref("apz.paint_skipping.enabled", true);
// Fetch displayport updates early from the message queue
pref("apz.peek_messages.enabled", true);
// Whether to print the APZC tree for debugging
pref("apz.printtree", false);
@@ -1427,6 +1429,7 @@ pref("javascript.options.strict.debug", true);
pref("javascript.options.baselinejit", true);
pref("javascript.options.ion", true);
pref("javascript.options.asmjs", true);
pref("javascript.options.wasm", false);
pref("javascript.options.native_regexp", true);
pref("javascript.options.parallel_parsing", true);
#if !defined(RELEASE_BUILD) && !defined(ANDROID) && !defined(MOZ_B2G) && !defined(XP_IOS)
+1 -1
View File
@@ -1103,7 +1103,7 @@ nsUnknownContentTypeDialog.prototype = {
localHandlerApp.executable = fp.file;
this.chosenApp = localHandlerApp;
}
#endif // MOZ_WIDGET_GTK3
#endif // MOZ_WIDGET_GTK == 3
}
this.finishChooseApp();
},
@@ -40,7 +40,9 @@ ExternalHelperAppParent::ExternalHelperAppParent(
const int64_t& aContentLength)
: mURI(DeserializeURI(uri))
, mPending(false)
#ifdef DEBUG
, mDiverted(false)
#endif
, mIPCClosed(false)
, mLoadFlags(0)
, mStatus(NS_OK)
@@ -153,7 +155,9 @@ ExternalHelperAppParent::RecvDivertToParentUsing(PChannelDiverterParent* diverte
MOZ_ASSERT(diverter);
auto p = static_cast<mozilla::net::ChannelDiverterParent*>(diverter);
p->DivertTo(this);
#ifdef DEBUG
mDiverted = true;
#endif
Unused << p->Send__delete__(p);
return true;
}
@@ -76,7 +76,9 @@ private:
nsCOMPtr<nsIStreamListener> mListener;
nsCOMPtr<nsIURI> mURI;
bool mPending;
DebugOnly<bool> mDiverted;
#ifdef DEBUG
bool mDiverted;
#endif
bool mIPCClosed;
nsLoadFlags mLoadFlags;
nsresult mStatus;
+5 -5
View File
@@ -747,7 +747,7 @@ nsWindow::Destroy(void)
if (gPluginFocusWindow == this) {
gPluginFocusWindow->LoseNonXEmbedPluginFocus();
}
#endif /* MOZ_X11 && MOZ_WIDGET_GTK2 */
#endif /* MOZ_X11 && MOZ_WIDGET_GTK == 2 && defined(MOZ_X11) */
GtkWidget *owningWidget = GetMozContainerWidget();
if (mShell) {
@@ -2595,7 +2595,7 @@ nsWindow::OnMotionNotifyEvent(GdkEventMotion *aEvent)
RefPtr<nsWindow> kungFuDeathGrip = gPluginFocusWindow;
gPluginFocusWindow->LoseNonXEmbedPluginFocus();
}
#endif /* MOZ_WIDGET_GTK2 */
#endif /* MOZ_WIDGET_GTK == 2 */
}
#endif /* MOZ_X11 */
@@ -2923,7 +2923,7 @@ nsWindow::OnContainerFocusOutEvent(GdkEventFocus *aEvent)
RefPtr<nsWindow> kungFuDeathGrip = gPluginFocusWindow;
gPluginFocusWindow->LoseNonXEmbedPluginFocus();
}
#endif /* MOZ_X11 && MOZ_WIDGET_GTK2 */
#endif /* MOZ_X11 && MOZ_WIDGET_GTK == 2 */
if (gFocusWindow) {
RefPtr<nsWindow> kungFuDeathGrip = gFocusWindow;
@@ -4491,7 +4491,7 @@ nsWindow::ApplyTransparencyBitmap()
gtk_widget_shape_combine_region(mShell, maskRegion);
cairo_region_destroy(maskRegion);
cairo_surface_destroy(maskBitmap);
#endif // MOZ_WIDGET_GTK2
#endif // MOZ_WIDGET_GTK == 2
#endif // MOZ_X11
}
@@ -5364,7 +5364,7 @@ expose_event_cb(GtkWidget *widget, cairo_t *cr)
return FALSE;
}
#endif //MOZ_WIDGET_GTK2
#endif //MOZ_WIDGET_GTK == 2
static gboolean
configure_event_cb(GtkWidget *widget,
-9
View File
@@ -65,12 +65,7 @@ public:
* @param aName The name of the module.
* @return A log module for the given name. This may be shared.
*/
#if !defined(MOZILLA_XPCOMRT_API)
static LogModule* Get(const char* aName);
#else
// For simplicity, libxpcomrt doesn't supoort logging.
static LogModule* Get(const char* aName) { return nullptr; }
#endif
static void Init();
@@ -176,13 +171,9 @@ inline bool log_test(const LogModule* module, LogLevel level) {
return module && module->ShouldLog(level);
}
#if !defined(MOZILLA_XPCOMRT_API)
void log_print(const LogModule* aModule,
LogLevel aLevel,
const char* aFmt, ...);
#else
inline void log_print(const LogModule* aModule, LogLevel aLevel, const char* aFmt, ...) {}
#endif
} // namespace detail
} // namespace mozilla
+8 -2
View File
@@ -1266,7 +1266,9 @@ private:
nsAutoPtr<CCGraphBuilder> mBuilder;
RefPtr<nsCycleCollectorLogger> mLogger;
DebugOnly<void*> mThread;
#ifdef DEBUG
void* mThread;
#endif
nsCycleCollectorParams mParams;
@@ -3389,7 +3391,9 @@ nsCycleCollector::nsCycleCollector() :
mScanInProgress(false),
mJSRuntime(nullptr),
mIncrementalPhase(IdlePhase),
#ifdef DEBUG
mThread(NS_GetCurrentThread()),
#endif
mWhiteNodeCount(0),
mBeforeUnlinkCB(nullptr),
mForgetSkippableCB(nullptr),
@@ -4005,11 +4009,13 @@ nsCycleCollector_suspectedCount()
bool
nsCycleCollector_init()
{
static DebugOnly<bool> sInitialized;
#ifdef DEBUG
static bool sInitialized;
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(!sInitialized, "Called twice!?");
sInitialized = true;
#endif
return sCollectorData.init();
}
+4 -9
View File
@@ -333,9 +333,8 @@ NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr,
if (sMultiprocessDescription) {
PrintToBuffer("%s ", sMultiprocessDescription);
}
#if !defined(MOZILLA_XPCOMRT_API)
PrintToBuffer("%d] ", base::GetCurrentProcId());
#endif // !defined(MOZILLA_XPCOMRT_API)
PrintToBuffer("%s: ", sevString);
@@ -381,7 +380,7 @@ NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr,
return;
case NS_DEBUG_ABORT: {
#if defined(MOZ_CRASHREPORTER) && !defined(MOZILLA_XPCOMRT_API)
#if defined(MOZ_CRASHREPORTER)
// Updating crash annotations in the child causes us to do IPC. This can
// really cause trouble if we're asserting from within IPC code. So we
// have to do without the annotations in that case.
@@ -398,7 +397,7 @@ NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr,
#if defined(DEBUG) && defined(_WIN32)
RealBreak();
#endif
#if defined(DEBUG) && !defined(MOZILLA_XPCOMRT_API)
#if defined(DEBUG)
nsTraceRefcnt::WalkTheStack(stderr);
#endif
Abort(buf.buffer);
@@ -423,15 +422,11 @@ NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr,
return;
case NS_ASSERT_STACK:
#if !defined(MOZILLA_XPCOMRT_API)
nsTraceRefcnt::WalkTheStack(stderr);
#endif // !defined(MOZILLA_XPCOMRT_API)
return;
case NS_ASSERT_STACK_AND_ABORT:
#if !defined(MOZILLA_XPCOMRT_API)
nsTraceRefcnt::WalkTheStack(stderr);
#endif // !defined(MOZILLA_XPCOMRT_API)
// Fall through to abort
MOZ_FALLTHROUGH;
@@ -603,7 +598,7 @@ NS_ErrorAccordingToNSPR()
void
NS_ABORT_OOM(size_t aSize)
{
#if defined(MOZ_CRASHREPORTER) && !defined(MOZILLA_XPCOMRT_API)
#if defined(MOZ_CRASHREPORTER)
CrashReporter::AnnotateOOMAllocationSize(aSize);
#endif
MOZ_CRASH();
+1 -1
View File
@@ -515,7 +515,7 @@ nsresult RegisterNonJSSizeOfTab(NonJSSizeOfTabFn aSizeOfTabFn);
}
#if defined(MOZ_DMD) && !defined(MOZILLA_XPCOMRT_API)
#if defined(MOZ_DMD)
#if !defined(MOZ_MEMORY)
#error "MOZ_DMD requires MOZ_MEMORY"
#endif
+5 -1
View File
@@ -222,7 +222,9 @@ doGCCCDump(const nsCString& aInputStr)
bool
SetupFifo()
{
static DebugOnly<bool> fifoCallbacksRegistered = false;
#ifdef DEBUG
static bool fifoCallbacksRegistered = false;
#endif
if (!FifoWatcher::MaybeCreate()) {
return false;
@@ -243,7 +245,9 @@ SetupFifo()
fw->RegisterCallback(NS_LITERAL_CSTRING("abbreviated gc log"),
doGCCCDump);
#ifdef DEBUG
fifoCallbacksRegistered = true;
#endif
return true;
}
-22
View File
@@ -5,10 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileLocation.h"
#if !defined(MOZILLA_XPCOMRT_API)
#include "nsZipArchive.h"
#include "nsURLHelper.h"
#endif // !defined(MOZILLA_XPCOMRT_API)
namespace mozilla {
@@ -36,11 +34,9 @@ FileLocation::FileLocation(const FileLocation& aFile, const char* aPath)
if (aFile.mBaseFile) {
Init(aFile.mBaseFile, aFile.mPath.get());
}
#if !defined(MOZILLA_XPCOMRT_API)
else {
Init(aFile.mBaseZip, aFile.mPath.get());
}
#endif
if (aPath) {
int32_t i = mPath.RFindChar('/');
if (kNotFound == i) {
@@ -78,9 +74,7 @@ FileLocation::FileLocation(const FileLocation& aFile, const char* aPath)
void
FileLocation::Init(nsIFile* aFile)
{
#if !defined(MOZILLA_XPCOMRT_API)
mBaseZip = nullptr;
#endif //!defined(MOZILLA_XPCOMRT_API)
mBaseFile = aFile;
mPath.Truncate();
}
@@ -88,9 +82,7 @@ FileLocation::Init(nsIFile* aFile)
void
FileLocation::Init(nsIFile* aFile, const char* aPath)
{
#if !defined(MOZILLA_XPCOMRT_API)
mBaseZip = nullptr;
#endif // !defined(MOZILLA_XPCOMRT_API)
mBaseFile = aFile;
mPath = aPath;
}
@@ -98,9 +90,7 @@ FileLocation::Init(nsIFile* aFile, const char* aPath)
void
FileLocation::Init(nsZipArchive* aZip, const char* aPath)
{
#if !defined(MOZILLA_XPCOMRT_API)
mBaseZip = aZip;
#endif // !defined(MOZILLA_XPCOMRT_API)
mBaseFile = nullptr;
mPath = aPath;
}
@@ -108,7 +98,6 @@ FileLocation::Init(nsZipArchive* aZip, const char* aPath)
void
FileLocation::GetURIString(nsACString& aResult) const
{
#if !defined(MOZILLA_XPCOMRT_API)
if (mBaseFile) {
net_GetURLSpecFromActualFile(mBaseFile, aResult);
} else if (mBaseZip) {
@@ -120,13 +109,11 @@ FileLocation::GetURIString(nsACString& aResult) const
aResult += "!/";
aResult += mPath;
}
#endif // !defined(MOZILLA_XPCOMRT_API)
}
already_AddRefed<nsIFile>
FileLocation::GetBaseFile()
{
#if !defined(MOZILLA_XPCOMRT_API)
if (IsZip() && mBaseZip) {
RefPtr<nsZipHandle> handler = mBaseZip->GetFD();
if (handler) {
@@ -134,7 +121,6 @@ FileLocation::GetBaseFile()
}
return nullptr;
}
#endif // !defined(MOZILLA_XPCOMRT_API)
nsCOMPtr<nsIFile> file = mBaseFile;
return file.forget();
@@ -154,7 +140,6 @@ FileLocation::Equals(const FileLocation& aFile) const
const FileLocation* a = this;
const FileLocation* b = &aFile;
#if !defined(MOZILLA_XPCOMRT_API)
if (a->mBaseZip) {
RefPtr<nsZipHandle> handler = a->mBaseZip->GetFD();
a = &handler->mFile;
@@ -163,7 +148,6 @@ FileLocation::Equals(const FileLocation& aFile) const
RefPtr<nsZipHandle> handler = b->mBaseZip->GetFD();
b = &handler->mFile;
}
#endif // !defined(MOZILLA_XPCOMRT_API)
return a->Equals(*b);
}
@@ -171,7 +155,6 @@ FileLocation::Equals(const FileLocation& aFile) const
nsresult
FileLocation::GetData(Data& aData)
{
#if !defined(MOZILLA_XPCOMRT_API)
if (!IsZip()) {
return mBaseFile->OpenNSPRFileDesc(PR_RDONLY, 0444, &aData.mFd.rwget());
}
@@ -184,7 +167,6 @@ FileLocation::GetData(Data& aData)
if (aData.mItem) {
return NS_OK;
}
#endif // !defined(MOZILLA_XPCOMRT_API)
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
}
@@ -204,12 +186,10 @@ FileLocation::Data::GetSize(uint32_t* aResult)
*aResult = fileInfo.size;
return NS_OK;
}
#if !defined(MOZILLA_XPCOMRT_API)
else if (mItem) {
*aResult = mItem->RealSize();
return NS_OK;
}
#endif // !defined(MOZILLA_XPCOMRT_API)
return NS_ERROR_NOT_INITIALIZED;
}
@@ -227,7 +207,6 @@ FileLocation::Data::Copy(char* aBuf, uint32_t aLen)
}
return NS_OK;
}
#if !defined(MOZILLA_XPCOMRT_API)
else if (mItem) {
nsZipCursor cursor(mItem, mZip, reinterpret_cast<uint8_t*>(aBuf),
aLen, true);
@@ -239,7 +218,6 @@ FileLocation::Data::Copy(char* aBuf, uint32_t aLen)
}
return NS_OK;
}
#endif // !defined(MOZILLA_XPCOMRT_API)
return NS_ERROR_NOT_INITIALIZED;
}
-8
View File
@@ -89,11 +89,7 @@ public:
* Boolean value corresponding to whether the file location is initialized
* or not.
*/
#if defined(MOZILLA_XPCOMRT_API)
explicit operator bool() const { return mBaseFile; }
#else
explicit operator bool() const { return mBaseFile || mBaseZip; }
#endif // defined(MOZILLA_XPCOMRT_API)
/**
* Returns whether another FileLocation points to the same resource
@@ -117,9 +113,7 @@ public:
nsresult Copy(char* aBuf, uint32_t aLen);
protected:
friend class FileLocation;
#if !defined(MOZILLA_XPCOMRT_API)
nsZipItem* mItem;
#endif // !defined(MOZILLA_XPCOMRT_API)
RefPtr<nsZipArchive> mZip;
mozilla::AutoFDClose mFd;
};
@@ -131,9 +125,7 @@ public:
nsresult GetData(Data& aData);
private:
nsCOMPtr<nsIFile> mBaseFile;
#if !defined(MOZILLA_XPCOMRT_API)
RefPtr<nsZipArchive> mBaseZip;
#endif // !defined(MOZILLA_XPCOMRT_API)
nsCString mPath;
}; /* class FileLocation */
-4
View File
@@ -5,7 +5,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// IWYU pragma: private, include "mozilla/Services.h"
#if !defined(MOZILLA_XPCOMRT_API)
#ifdef ACCESSIBILITY
MOZ_SERVICE(AccessibilityService, nsIAccessibilityService,
"@mozilla.org/accessibilityService;1")
@@ -20,10 +19,8 @@ MOZ_SERVICE(XULOverlayProviderService, nsIXULOverlayProvider,
"@mozilla.org/chrome/chrome-registry;1")
MOZ_SERVICE(IOService, nsIIOService,
"@mozilla.org/network/io-service;1")
#endif // !defined(MOZILLA_XPCOMRT_API)
MOZ_SERVICE(ObserverService, nsIObserverService,
"@mozilla.org/observer-service;1")
#if !defined(MOZILLA_XPCOMRT_API)
MOZ_SERVICE(StringBundleService, nsIStringBundleService,
"@mozilla.org/intl/stringbundle;1")
MOZ_SERVICE(XPConnect, nsIXPConnect,
@@ -51,4 +48,3 @@ MOZ_SERVICE(HistoryService, IHistory,
#ifdef MOZ_USE_NAMESPACE
} // namespace mozilla
#endif
#endif // !defined(MOZILLA_XPCOMRT_API)
-2
View File
@@ -11,7 +11,6 @@
#include "nsNetCID.h"
#include "nsObserverService.h"
#include "nsXPCOMPrivate.h"
#if !defined(MOZILLA_XPCOMRT_API)
#include "nsIIOService.h"
#include "nsIDirectoryService.h"
#ifdef ACCESSIBILITY
@@ -29,7 +28,6 @@
#include "nsIAsyncShutdown.h"
#include "nsIUUIDGenerator.h"
#include "nsIGfxInfo.h"
#endif // !defined(MOZILLA_XPCOMRT_API)
using namespace mozilla;
using namespace mozilla::services;
-2
View File
@@ -8,9 +8,7 @@
#define ManifestParser_h
#include "nsComponentManager.h"
#if !defined(MOZILLA_XPCOMRT_API)
#include "nsChromeRegistry.h"
#endif // !defined(MOZILLA_XPCOMRT_API)
#include "mozilla/FileLocation.h"
void ParseManifest(NSLocationType aType, mozilla::FileLocation& aFile,
-6
View File
@@ -418,9 +418,7 @@ nsCategoryManager::nsCategoryManager()
void
nsCategoryManager::InitMemoryReporter()
{
#if !defined(MOZILLA_XPCOMRT_API)
RegisterStrongMemoryReporter(this);
#endif // !defined(MOZILLA_XPCOMRT_API)
}
nsCategoryManager::~nsCategoryManager()
@@ -805,10 +803,8 @@ NS_CreateServicesFromCategory(const char* aCategory,
nsCOMPtr<nsISupports> instance = do_GetService(contractID);
if (!instance) {
#if !defined(MOZILLA_XPCOMRT_API)
LogMessage("While creating services from category '%s', could not create service for entry '%s', contract ID '%s'",
aCategory, entryString.get(), contractID.get());
#endif // !defined(MOZILLA_XPCOMRT_API)
continue;
}
@@ -818,10 +814,8 @@ NS_CreateServicesFromCategory(const char* aCategory,
if (observer) {
observer->Observe(aOrigin, aObserverTopic, EmptyString().get());
} else {
#if !defined(MOZILLA_XPCOMRT_API)
LogMessage("While creating services from category '%s', service for entry '%s', contract ID '%s' does not implement nsIObserver.",
aCategory, entryString.get(), contractID.get());
#endif // !defined(MOZILLA_XPCOMRT_API)
}
}
}
-52
View File
@@ -58,10 +58,8 @@
#include "private/pprthred.h"
#include "nsTArray.h"
#include "prio.h"
#if !defined(MOZILLA_XPCOMRT_API)
#include "ManifestParser.h"
#include "nsNetUtil.h"
#endif // !defined(MOZILLA_XPCOMRT_API)
#include "mozilla/Services.h"
#include "mozilla/GenericFactory.h"
@@ -254,7 +252,6 @@ private:
} // namespace
#if !defined(MOZILLA_XPCOMRT_API)
// this is safe to call during InitXPCOM
static already_AddRefed<nsIFile>
GetLocationFromDirectoryService(const char* aProp)
@@ -291,7 +288,6 @@ CloneAndAppend(nsIFile* aBase, const nsACString& aAppend)
f->AppendNative(aAppend);
return f.forget();
}
#endif // !defined(MOZILLA_XPCOMRT_API)
////////////////////////////////////////////////////////////////////////////////
// nsComponentManagerImpl
@@ -324,10 +320,8 @@ nsComponentManagerImpl::nsComponentManagerImpl()
nsTArray<const mozilla::Module*>* nsComponentManagerImpl::sStaticModules;
#if !defined(MOZILLA_XPCOMRT_API)
NSMODULE_DEFN(start_kPStaticModules);
NSMODULE_DEFN(end_kPStaticModules);
#endif // !defined(MOZILLA_XPCOMRT_API)
/* The content between start_kPStaticModules and end_kPStaticModules is gathered
* by the linker from various objects containing symbols in a specific section.
@@ -343,14 +337,12 @@ nsComponentManagerImpl::InitializeStaticModules()
}
sStaticModules = new nsTArray<const mozilla::Module*>;
#if !defined(MOZILLA_XPCOMRT_API)
for (const mozilla::Module * const* staticModules =
&NSMODULE_NAME(start_kPStaticModules) + 1;
staticModules < &NSMODULE_NAME(end_kPStaticModules); ++staticModules)
if (*staticModules) { // ASAN adds padding
sStaticModules->AppendElement(*staticModules);
}
#endif // !defined(MOZILLA_XPCOMRT_API)
}
nsTArray<nsComponentManagerImpl::ComponentLocation>*
@@ -374,37 +366,26 @@ nsComponentManagerImpl::Init()
// Initialize our arena
PL_INIT_ARENA_POOL(&mArena, "ComponentManagerArena", NS_CM_BLOCK_SIZE);
#if !defined(MOZILLA_XPCOMRT_API)
nsCOMPtr<nsIFile> greDir =
GetLocationFromDirectoryService(NS_GRE_DIR);
nsCOMPtr<nsIFile> appDir =
GetLocationFromDirectoryService(NS_XPCOM_CURRENT_PROCESS_DIR);
#endif
InitializeStaticModules();
#if !defined(MOZILLA_XPCOMRT_API)
nsresult rv = mNativeModuleLoader.Init();
if (NS_FAILED(rv)) {
return rv;
}
nsCategoryManager::GetSingleton()->SuppressNotifications(true);
#endif
#if defined(MOZILLA_XPCOMRT_API)
RegisterModule(&kXPCOMRTModule, nullptr);
RegisterModule(&kNeckoStandaloneModule, nullptr);
RegisterModule(&kStunUDPSocketFilterHandlerModule, nullptr);
#else
RegisterModule(&kXPCOMModule, nullptr);
#endif // defined(MOZILLA_XPCOMRT_API)
for (uint32_t i = 0; i < sStaticModules->Length(); ++i) {
RegisterModule((*sStaticModules)[i], nullptr);
}
#if !defined(MOZILLA_XPCOMRT_API)
// The overall order in which chrome.manifests are expected to be treated
// is the following:
// - greDir
@@ -463,7 +444,6 @@ nsComponentManagerImpl::Init()
// component manager initialization we know it is setup and we can register
// for notifications.
LogModulePrefWatcher::RegisterPrefWatcher();
#endif
// Unfortunately, we can't register the nsCategoryManager memory reporter
// in its constructor (which is triggered by the GetSingleton() call
@@ -572,12 +552,10 @@ nsComponentManagerImpl::RegisterCIDEntryLocked(
existing = "<unknown module>";
}
SafeMutexAutoUnlock unlock(mLock);
#if !defined(MOZILLA_XPCOMRT_API)
LogMessage("While registering XPCOM module %s, trying to re-register CID '%s' already registered by %s.",
aModule->Description().get(),
idstr,
existing.get());
#endif // !defined(MOZILLA_XPCOMRT_API)
return;
}
@@ -603,11 +581,9 @@ nsComponentManagerImpl::RegisterContractIDLocked(
aEntry->cid->ToProvidedString(idstr);
SafeMutexAutoUnlock unlock(mLock);
#if !defined(MOZILLA_XPCOMRT_API)
LogMessage("Could not map contract ID '%s' to CID %s because no implementation of the CID is registered.",
aEntry->contractid,
idstr);
#endif // !defined(MOZILLA_XPCOMRT_API)
return;
}
@@ -615,7 +591,6 @@ nsComponentManagerImpl::RegisterContractIDLocked(
mContractIDs.Put(nsDependentCString(aEntry->contractid), f);
}
#if !defined(MOZILLA_XPCOMRT_API)
static void
CutExtension(nsCString& aPath)
{
@@ -858,12 +833,10 @@ nsComponentManagerImpl::RereadChromeManifests(bool aChromeOnly)
RegisterManifest(l.type, l.location, aChromeOnly);
}
}
#endif // !defined(MOZILLA_XPCOMRT_API)
bool
nsComponentManagerImpl::KnownModule::EnsureLoader()
{
#if !defined(MOZILLA_XPCOMRT_API)
if (!mLoader) {
nsCString extension;
mFile.GetURIString(extension);
@@ -871,7 +844,6 @@ nsComponentManagerImpl::KnownModule::EnsureLoader()
mLoader =
nsComponentManagerImpl::gComponentManager->LoaderForExtension(extension);
}
#endif // !defined(MOZILLA_XPCOMRT_API)
return !!mLoader;
}
@@ -928,9 +900,7 @@ nsresult nsComponentManagerImpl::Shutdown(void)
MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
("nsComponentManager: Beginning Shutdown."));
#if !defined(MOZILLA_XPCOMRT_API)
UnregisterWeakMemoryReporter(this);
#endif
// Release all cached factories
mContractIDs.Clear();
@@ -946,10 +916,8 @@ nsresult nsComponentManagerImpl::Shutdown(void)
sXPTIInfosBook = nullptr;
#endif
#if !defined(MOZILLA_XPCOMRT_API)
// Unload libraries
mNativeModuleLoader.UnloadLibraries();
#endif // !defined(MOZILLA_XPCOMRT_API)
// delete arena for strings and small objects
PL_FinishArenaPool(&mArena);
@@ -1620,7 +1588,6 @@ nsComponentManagerImpl::GetServiceByContractID(const char* aContractID,
return NS_OK;
}
#if !defined(MOZILLA_XPCOMRT_API)
already_AddRefed<mozilla::ModuleLoader>
nsComponentManagerImpl::LoaderForExtension(const nsACString& aExt)
{
@@ -1637,7 +1604,6 @@ nsComponentManagerImpl::LoaderForExtension(const nsACString& aExt)
return loader.forget();
}
#endif
NS_IMETHODIMP
nsComponentManagerImpl::RegisterFactory(const nsCID& aClass,
@@ -1710,12 +1676,8 @@ nsComponentManagerImpl::UnregisterFactory(const nsCID& aClass,
NS_IMETHODIMP
nsComponentManagerImpl::AutoRegister(nsIFile* aLocation)
{
#if !defined(MOZILLA_XPCOMRT_API)
XRE_AddManifestLocation(NS_COMPONENT_LOCATION, aLocation);
return NS_OK;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif // !defined(MOZILLA_XPCOMRT_API)
}
NS_IMETHODIMP
@@ -2033,7 +1995,6 @@ XRE_AddStaticComponent(const mozilla::Module* aComponent)
NS_IMETHODIMP
nsComponentManagerImpl::AddBootstrappedManifestLocation(nsIFile* aLocation)
{
#if !defined(MOZILLA_XPCOMRT_API)
nsString path;
nsresult rv = aLocation->GetPath(path);
if (NS_FAILED(rv)) {
@@ -2047,15 +2008,11 @@ nsComponentManagerImpl::AddBootstrappedManifestLocation(nsIFile* aLocation)
nsCOMPtr<nsIFile> manifest =
CloneAndAppend(aLocation, NS_LITERAL_CSTRING("chrome.manifest"));
return XRE_AddManifestLocation(NS_BOOTSTRAPPED_LOCATION, manifest);
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif // !defined(MOZILLA_XPCOMRT_API)
}
NS_IMETHODIMP
nsComponentManagerImpl::RemoveBootstrappedManifestLocation(nsIFile* aLocation)
{
#if !defined(MOZILLA_XPCOMRT_API)
nsCOMPtr<nsIChromeRegistry> cr = mozilla::services::GetChromeRegistryService();
if (!cr) {
return NS_ERROR_FAILURE;
@@ -2085,15 +2042,11 @@ nsComponentManagerImpl::RemoveBootstrappedManifestLocation(nsIFile* aLocation)
rv = cr->CheckForNewChrome();
return rv;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif // !defined(MOZILLA_XPCOMRT_API)
}
NS_IMETHODIMP
nsComponentManagerImpl::GetManifestLocations(nsIArray** aLocations)
{
#if !defined(MOZILLA_XPCOMRT_API)
NS_ENSURE_ARG_POINTER(aLocations);
*aLocations = nullptr;
@@ -2117,9 +2070,6 @@ nsComponentManagerImpl::GetManifestLocations(nsIArray** aLocations)
locations.forget(aLocations);
return NS_OK;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif // !defined(MOZILLA_XPCOMRT_API)
}
#ifdef MOZ_B2G_LOADER
@@ -2168,7 +2118,6 @@ PreloadXPT(nsIFile* aOmnijarFile)
#endif /* MOZ_B2G_LOADER */
#if !defined(MOZILLA_XPCOMRT_API)
EXPORT_XPCOM_API(nsresult)
XRE_AddManifestLocation(NSLocationType aType, nsIFile* aLocation)
{
@@ -2209,5 +2158,4 @@ XRE_AddJarManifestLocation(NSLocationType aType, nsIFile* aLocation)
return NS_OK;
}
#endif // !defined(MOZILLA_XPCOMRT_API)
-6
View File
@@ -69,13 +69,7 @@ extern const char staticComponentType[];
#endif
////////////////////////////////////////////////////////////////////////////////
#if defined(MOZILLA_XPCOMRT_API)
extern const mozilla::Module kXPCOMRTModule;
extern const mozilla::Module kNeckoStandaloneModule;
extern const mozilla::Module kStunUDPSocketFilterHandlerModule;
#else
extern const mozilla::Module kXPCOMModule;
#endif
/**
* This is a wrapper around mozilla::Mutex which provides runtime
-6
View File
@@ -172,17 +172,13 @@ nsObserverService::~nsObserverService(void)
void
nsObserverService::RegisterReporter()
{
#if !defined(MOZILLA_XPCOMRT_API)
RegisterWeakMemoryReporter(this);
#endif // !defined(MOZILLA_XPCOMRT_API)
}
void
nsObserverService::Shutdown()
{
#if !defined(MOZILLA_XPCOMRT_API)
UnregisterWeakMemoryReporter(this);
#endif // !defined(MOZILLA_XPCOMRT_API)
mShuttingDown = true;
@@ -322,7 +318,6 @@ nsObserverService::UnmarkGrayStrongObservers()
{
NS_ENSURE_VALIDCALL
#if !defined(MOZILLA_XPCOMRT_API)
nsCOMArray<nsIObserver> strongObservers;
for (auto iter = mObserverTopicTable.Iter(); !iter.Done(); iter.Next()) {
nsObserverList* aObserverList = iter.Get();
@@ -334,7 +329,6 @@ nsObserverService::UnmarkGrayStrongObservers()
for (uint32_t i = 0; i < strongObservers.Length(); ++i) {
xpc_TryUnmarkWrappedGrayObject(strongObservers[i]);
}
#endif // !defined(MOZILLA_XPCOMRT_API)
return NS_OK;
}

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