mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 05:37:11 +00:00
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:
@@ -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",
|
||||
|
||||
@@ -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")))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -1681,4 +1681,4 @@ ApplyGdkScreenFontOptions(FcPattern *aPattern)
|
||||
cairo_ft_font_options_substitute(options, aPattern);
|
||||
}
|
||||
|
||||
#endif // MOZ_WIDGET_GTK2
|
||||
#endif // MOZ_WIDGET_GTK
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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(§ionStart))
|
||||
return Fail(cx, d, "expected signature section byte size");
|
||||
if (!d.startSection(SigLabel, §ionStart))
|
||||
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(§ionStart))
|
||||
return Fail(cx, d, "expected decl section byte size");
|
||||
if (!d.startSection(DeclLabel, §ionStart))
|
||||
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(§ionStart))
|
||||
return Fail(cx, d, "expected table section byte size");
|
||||
if (!d.startSection(TableLabel, §ionStart))
|
||||
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(§ionStart))
|
||||
return Fail(cx, d, "expected import section byte size");
|
||||
if (!d.startSection(ImportLabel, §ionStart))
|
||||
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, §ionStart))
|
||||
return Fail(cx, d, "failed to start section");
|
||||
if (sectionStart == Decoder::NotStarted)
|
||||
return true;
|
||||
|
||||
uint32_t sectionStart;
|
||||
if (!d.startSection(§ionStart))
|
||||
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(§ionStart))
|
||||
return Fail(cx, d, "expected export section byte size");
|
||||
if (!d.startSection(ExportLabel, §ionStart))
|
||||
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(§ionStart))
|
||||
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, §ionStart))
|
||||
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, §ionStart))
|
||||
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(§ionStart))
|
||||
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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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."),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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])])));
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -118,6 +118,9 @@ namespace jit {
|
||||
_(ToFloat32) \
|
||||
_(ToInt32) \
|
||||
_(TruncateToInt32) \
|
||||
_(TruncateToInt64) \
|
||||
_(WrapInt64ToInt32) \
|
||||
_(ExtendInt32ToInt64) \
|
||||
_(ToString) \
|
||||
_(ToObjectOrNull) \
|
||||
_(NewArray) \
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -335,3 +335,9 @@ LIRGeneratorARM64::visitRandom(MRandom* ins)
|
||||
temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorARM64::visitTruncateToInt64(MTruncateToInt64* ins)
|
||||
{
|
||||
MOZ_CRASH("NY");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
{
|
||||
|
||||
@@ -189,6 +189,8 @@
|
||||
_(Float32ToInt32) \
|
||||
_(TruncateDToInt32) \
|
||||
_(TruncateFToInt32) \
|
||||
_(WrapInt64ToInt32) \
|
||||
_(ExtendInt32ToInt64) \
|
||||
_(BooleanToString) \
|
||||
_(IntToString) \
|
||||
_(DoubleToString) \
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
_(DivOrModConstantI) \
|
||||
_(DivOrModI64) \
|
||||
_(UDivOrMod64) \
|
||||
_(TruncateToInt64) \
|
||||
_(SimdValueInt32x4) \
|
||||
_(SimdValueFloat32x4) \
|
||||
_(UDivOrMod) \
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -939,7 +939,8 @@ ProcessArgsForCompartment(JSContext* cx, char** argv, int argc)
|
||||
break;
|
||||
case 'I':
|
||||
RuntimeOptionsRef(cx).toggleIon()
|
||||
.toggleAsmJS();
|
||||
.toggleAsmJS()
|
||||
.toggleWasm();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
神神神神
|
||||
</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>
|
||||
神神󠄀神󠄃神︀
|
||||
</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
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user