From e1c4d9e76fc4865f69f8609519bbfba3977bd18e Mon Sep 17 00:00:00 2001 From: roytam1 Date: Fri, 2 Oct 2020 18:15:14 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1077318 - call out to C++ for ARMv6 sub-word atomics. r=luke (2810c3f93) - Bug 1142593 - factor atomics into the platform layer. r=waldo (f5088b2af) - Bug 1173642 - Import unimplemented ARM64 Ion components. r=efaust (2567975a1) - Bug 1170107 part 1 - Call GetProperty/CallProperty from the interpreter and Baseline. r=bhackett (fe45a446a) - Bug 1170107 part 2 - Don't allocate a wrapper object for (pure) primitive.foo operations. r=bhackett (8793d7d2d) - Bug 1152079 - Remove Purify support files. r=froydnj (69836c373) - Bug 1152079 - Remove references to deleted Purify files from moz.build. r=bustage (ebd68a5fa) - Bug 1126089 - Assert we don't Destroy nsFrameLoader in destructor (r=smaug) (c0894ed2b) - Bug 1250871 - only send messages to child scripts when the docShell is still around. r=smaug (ac2562f55) - Bug 1168750 - SharedStubs: (part3) Make split between baseline only and shared stubs, r=jandem (21c89cffe) - Bug 1168750 - SharedStubs: (part4) Track for which engine a stub needs to get compiled, r=jandem (a36eb943b) - Bug 1168753 - SharedStubs: Simplification to push BaselineFrame* in stubs, r=jandem (4a26b318d) - pointer style (e20a50c7e) - Bug 1169391 - Use a ReservedRooted class for optimized Rooted use in vm/Interpreter.cpp, r=terrence (1b80a857d) - Bug 1182865 - Relax assertion on the allowed class for home objects, r=efaust (33009a9fe) - Bug 1154391 - Update import declarations to current ES6 spec r=shu (ad7042d54) - Bug 1154391 - Update parsing of export declarations to match current ES6 spec r=shu (4b509d9ec) - Bug 1169171 - Split PNK_DELETE into several different kinds for each of the syntactically distinct modes of |delete| operation, depending upon the syntax of the operand. r=efaust (5667265fc) - Bug 1169511 - Split PNK_TYPEOF into one kind for application to names one kind for application to other expressions. r=efaust (91a1086d8) - pointer style (0546201d8) - Bug 1163851 - Remove the remaining two tests of |pc->parsingForInit|, replacing them with yet another argument provided by the caller. r=efaust (919903e1e) - Bug 1163851 - |pc->parsingForInit| is dead. Long live |InHandling|! r=efaust (bf35fb405) - Bug 1167030 - Suppress MSVC warning C4661 in js/src. r=luke (1d28ed1d2) - Bug 1167845 - Rejigger {in,de}crement/assignment parsing to vet targets primarily through a single Parser::isValidSimpleAssignmentTarget method, so that this method can also be called outside the parser. r=efaust (87484d3fc) - Bug 1166950 - Introduce a new FunctionKind for class-constructors. r=efaust (e9dd197e1) - Bug 1166950 - Make generator methods constructors. r=efaust (e076cacd6) - Bug 1167845 - Rename ParseHandler::isName to ParseHandler::maybeName. r=efaust (1e090741b) - Bug 1141865 - Part 3: Parse new.target, add Reflect support, and tests. (r=jorendorff, r=shu) (9e0282870) - Bug 1139759 - Self-host %TypedArray%.prototype.copyWithin. r=till (8beea5f7c) - pointer style (0da72f30a) - Bug 1139769 - Add TypedArrayBuffer, TypedArrayByteOffset, and TypedArrayElementShift intrinsics to safely get the relevant information for a typed array. r=till (b5847a441) - pointer style (5486dd33d) - Bug 1139769 - Self-host %TypedArray%.prototype.subarray. r=t (b10c55e93) - pointer style (335ab6c87) - Bug 1140752 - Land code to self-host %TypedArray%.prototype.set, but don't enable it yet, pending perf-testing. This shouldn't make any changes to the build, except in terms of adding more code to it and affecting binary layout. r=jandem, also much feedback from till that effectively amounts to a review as well (98050308d) - Bug 1144692: Fix --disable-ion builds; r=h4writer (3be32046c) - Bug 1148970 - Check for possibly incomplete type sets when double checking the correctness of argument type set information, r=jandem. (934130c2e) - bug 1171125 - Fix nsStackWalk to build for iOS. r=froydnj (c076c4d22) - Bug 1119980 - Use 'snprintf' instead of 'sprintf' to avoid a warning on Lollipop-based builds. r=froydnj (a000af66c) - Bug 1146597 - Avoid unnecessary calls to maybeSweep under hasUnanalyzedPreliminaryObjects, r=jandem. (335ba3fc3) - Bug 1162134 - IonMonkey MIPS: Fix build failure on MIPS (js/src/jit/CodeGenerator.cpp:6740:28: error: `obj' was not declared in this scope). r=bhackett (56824379b) - Bug 1160884 followup - Add testcase. r=me (5495ad016) - Bug 1141865 - Part 4: Remove LCallDirectEvalV in anticipation of register pressure with new.target in eval frames. (r=jandem) (cbb706356) - Bug 1162242 - Part 1: Remove instances of #ifdef PR_LOGGING. r=froydnj PR_LOGGING is now always defined, we can remove #ifdefs checking for it. (878131a4a) - revert pointer style for patch (a263375f1) - Bug 1144097. Fix yet another is() assert. r=shu (96d516117) - Bug 1145282. Reverse the sense of IsValidTerminatingScope and rename it to IsSyntacticScope. r=luke (3681fc831) - Bug 1149897 - PerformanceStatsService data is not monotonic. r=jandem (927639ac6) - Bug 1142457 - Compute stopwatch durations per thread on MacOS X. r=jandem, r=areinald (a52c388be) --- dom/base/nsFrameLoader.cpp | 6 +- dom/media/AudioStream.cpp | 3 +- dom/wifi/WifiHotspotUtils.cpp | 5 +- extensions/auth/nsHttpNegotiateAuth.cpp | 6 +- gfx/thebes/gfxFontUtils.cpp | 16 +- gfx/thebes/gfxHarfBuzzShaper.cpp | 17 +- gfx/thebes/gfxTextRun.cpp | 5 +- intl/locale/nsScriptableDateFormat.cpp | 3 +- .../src/base/third_party/purify/pure.h | 145 -- .../src/base/third_party/purify/pure_api.c | 152 -- ipc/netd/Netd.cpp | 7 +- js/public/RootingAPI.h | 4 +- js/src/asmjs/AsmJSFrameIterator.cpp | 6 + js/src/asmjs/AsmJSFrameIterator.h | 6 + js/src/asmjs/AsmJSModule.cpp | 13 + js/src/asmjs/AsmJSValidate.cpp | 17 +- js/src/builtin/AtomicsObject.cpp | 507 +++-- js/src/builtin/AtomicsObject.h | 8 + js/src/builtin/Eval.cpp | 16 - js/src/builtin/Eval.h | 5 - js/src/builtin/ReflectParse.cpp | 64 +- js/src/builtin/TypedArray.js | 255 +++ js/src/builtin/TypedObjectConstants.h | 21 + js/src/frontend/BytecodeEmitter.cpp | 270 ++- js/src/frontend/BytecodeEmitter.h | 11 +- js/src/frontend/FoldConstants.cpp | 33 +- js/src/frontend/FullParseHandler.h | 72 +- js/src/frontend/NameFunctions.cpp | 17 +- js/src/frontend/ParseNode.cpp | 12 +- js/src/frontend/ParseNode.h | 41 +- js/src/frontend/Parser.cpp | 787 ++++---- js/src/frontend/Parser.h | 60 +- js/src/frontend/SyntaxParseHandler.h | 112 +- js/src/irregexp/RegExpParser.h | 1 - js/src/jit-test/tests/basic/bug1182865.js | 8 + js/src/jit-test/tests/basic/newTargetOSR.js | 6 + .../tests/basic/typed-array-copyWithin.js | 22 + .../tests/debug/Frame-evalWithBindings-14.js | 20 + js/src/jit-test/tests/ion/bug1160884.js | 12 + .../tests/modules/export-declaration.js | 140 +- .../tests/modules/import-declaration.js | 152 ++ js/src/jit/AtomicOperations-inl.h | 22 + js/src/jit/AtomicOperations.h | 121 ++ js/src/jit/BaselineCompiler.cpp | 37 + js/src/jit/BaselineCompiler.h | 3 +- js/src/jit/BaselineIC.cpp | 1067 +++-------- js/src/jit/BaselineIC.h | 1638 +++-------------- js/src/jit/BaselineICList.h | 160 ++ js/src/jit/CodeGenerator.cpp | 93 +- js/src/jit/CodeGenerator.h | 5 +- js/src/jit/Ion.cpp | 8 +- js/src/jit/IonBuilder.cpp | 23 + js/src/jit/IonBuilder.h | 5 + js/src/jit/LIR-Common.h | 73 +- js/src/jit/LOpcodes.h | 7 +- js/src/jit/Lowering.cpp | 47 +- js/src/jit/Lowering.h | 2 + js/src/jit/MCallOptimize.cpp | 82 +- js/src/jit/MIR.h | 67 +- js/src/jit/MOpcodes.h | 4 +- js/src/jit/SharedIC.cpp | 790 ++++++++ js/src/jit/SharedIC.h | 1089 +++++++++++ js/src/jit/SharedICList.h | 19 + js/src/jit/TypePolicy.cpp | 2 +- js/src/jit/arm/AtomicOperations-arm.h | 220 +++ js/src/jit/arm/CodeGenerator-arm.cpp | 56 + js/src/jit/arm/CodeGenerator-arm.h | 2 + js/src/jit/arm/LIR-arm.h | 47 + js/src/jit/arm/LOpcodes-arm.h | 4 +- js/src/jit/arm/Lowering-arm.cpp | 16 + js/src/jit/arm64/CodeGenerator-arm64.cpp | 746 ++++++++ js/src/jit/arm64/CodeGenerator-arm64.h | 276 +++ js/src/jit/arm64/LIR-arm64.h | 467 +++++ js/src/jit/arm64/LOpcodes-arm64.h | 29 + js/src/jit/arm64/Lowering-arm64.cpp | 321 ++++ js/src/jit/arm64/Lowering-arm64.h | 121 ++ js/src/jit/mips/AtomicOperations-mips.h | 105 ++ js/src/jit/none/Architecture-none.h | 1 + js/src/jit/none/AtomicOperations-none.h | 105 ++ js/src/jit/none/MacroAssembler-none.h | 6 + js/src/jit/shared/Assembler-shared.h | 6 + .../x86-shared/AtomicOperations-x86-shared.h | 547 ++++++ js/src/js.msg | 7 +- js/src/jsapi-tests/moz.build | 1 + .../testNewTargetInvokeConstructor.cpp | 25 + js/src/jsarray.cpp | 2 +- js/src/jsarray.h | 8 +- js/src/jsast.tbl | 1 + js/src/jscntxt.h | 11 + js/src/jsfriendapi.h | 28 +- js/src/jsfun.cpp | 12 +- js/src/jsfun.h | 25 +- js/src/jsobj.cpp | 13 +- js/src/jsopcode.cpp | 2 + js/src/jsstr.cpp | 2 +- js/src/moz.build | 6 + js/src/tests/ecma_6/Class/methDefnGen.js | 9 + .../ecma_6/Class/newTargetArgumentsIntact.js | 44 + js/src/tests/ecma_6/Class/newTargetBound.js | 16 + js/src/tests/ecma_6/Class/newTargetContext.js | 11 + js/src/tests/ecma_6/Class/newTargetDVG.js | 7 + .../ecma_6/Class/newTargetDirectInvoke.js | 47 + js/src/tests/ecma_6/Class/newTargetMethods.js | 52 + js/src/tests/ecma_6/Class/shell.js | 11 + js/src/tests/ecma_6/Class/superPropDVG.js | 11 - .../js1_8_5/reflect-parse/PatternAsserts.js | 5 + .../js1_8_5/reflect-parse/PatternBuilders.js | 3 + .../tests/js1_8_5/reflect-parse/newTarget.js | 41 + js/src/vm/ArrayBufferObject.h | 7 + js/src/vm/CommonPropertyNames.h | 3 + js/src/vm/Debugger.cpp | 4 +- js/src/vm/Interpreter.cpp | 603 +++--- js/src/vm/ObjectGroup.h | 3 +- js/src/vm/Opcodes.h | 11 +- js/src/vm/Runtime.h | 22 + js/src/vm/ScopeObject.cpp | 6 +- js/src/vm/ScopeObject.h | 10 +- js/src/vm/SelfHosting.cpp | 496 ++++- js/src/vm/Stack.cpp | 6 +- js/src/vm/Stack.h | 18 + js/src/vm/TypedArrayCommon.h | 12 +- js/src/vm/TypedArrayObject.cpp | 24 +- js/src/vm/TypedArrayObject.h | 31 +- js/src/vm/Xdr.h | 4 +- layout/base/nsPresShell.cpp | 15 +- layout/generic/nsAbsoluteContainingBlock.cpp | 14 +- layout/generic/nsFrame.cpp | 93 +- layout/style/FontFaceSet.cpp | 3 +- media/gmp-clearkey/0.1/openaes/oaes_lib.c | 4 +- mfbt/ArrayUtils.h | 21 +- mfbt/JSONWriter.h | 3 +- mfbt/Snprintf.h | 2 +- mfbt/tests/TestIntegerPrintfMacros.cpp | 169 +- netwerk/cache/nsDiskCacheMap.cpp | 7 +- .../rtsp/rtsp/ASessionDescription.cpp | 8 +- netwerk/test/PropertiesTest.cpp | 3 +- testing/mochitest/ssltunnel/ssltunnel.cpp | 18 +- .../tests/browser/browser_compartments.js | 24 +- toolkit/profile/nsToolkitProfileService.cpp | 35 +- widget/nsBaseWidget.cpp | 4 +- xpcom/base/CodeAddressService.h | 4 - xpcom/base/moz.build | 3 - xpcom/base/nsStackWalk.cpp | 11 +- xpcom/base/pure.h | 135 -- xpcom/base/pure_api.c | 126 -- xpcom/components/nsComponentManager.cpp | 6 - xpcom/ds/TimeStamp_posix.cpp | 3 +- xpcom/ds/TimeStamp_windows.cpp | 6 +- xpcom/ds/nsObserverService.cpp | 6 +- xpcom/glue/nsCRTGlue.cpp | 7 +- xpcom/io/nsInputStreamTee.cpp | 4 - xpcom/io/nsPipe3.cpp | 4 - xpcom/io/nsStorageStream.cpp | 2 - xpcom/threads/nsEventQueue.cpp | 2 - xpcom/threads/nsThread.cpp | 2 - xpcom/threads/nsThreadPool.cpp | 2 - xpcom/threads/nsTimerImpl.h | 4 - 157 files changed, 9589 insertions(+), 4323 deletions(-) delete mode 100644 ipc/chromium/src/base/third_party/purify/pure.h delete mode 100644 ipc/chromium/src/base/third_party/purify/pure_api.c create mode 100644 js/src/jit-test/tests/basic/bug1182865.js create mode 100644 js/src/jit-test/tests/basic/newTargetOSR.js create mode 100644 js/src/jit-test/tests/debug/Frame-evalWithBindings-14.js create mode 100644 js/src/jit-test/tests/ion/bug1160884.js create mode 100644 js/src/jit/AtomicOperations-inl.h create mode 100644 js/src/jit/AtomicOperations.h create mode 100644 js/src/jit/BaselineICList.h create mode 100644 js/src/jit/SharedIC.cpp create mode 100644 js/src/jit/SharedIC.h create mode 100644 js/src/jit/SharedICList.h create mode 100644 js/src/jit/arm/AtomicOperations-arm.h create mode 100644 js/src/jit/arm64/CodeGenerator-arm64.cpp create mode 100644 js/src/jit/arm64/CodeGenerator-arm64.h create mode 100644 js/src/jit/arm64/LIR-arm64.h create mode 100644 js/src/jit/arm64/LOpcodes-arm64.h create mode 100644 js/src/jit/arm64/Lowering-arm64.cpp create mode 100644 js/src/jit/arm64/Lowering-arm64.h create mode 100644 js/src/jit/mips/AtomicOperations-mips.h create mode 100644 js/src/jit/none/AtomicOperations-none.h create mode 100644 js/src/jit/x86-shared/AtomicOperations-x86-shared.h create mode 100644 js/src/jsapi-tests/testNewTargetInvokeConstructor.cpp create mode 100644 js/src/tests/ecma_6/Class/newTargetArgumentsIntact.js create mode 100644 js/src/tests/ecma_6/Class/newTargetBound.js create mode 100644 js/src/tests/ecma_6/Class/newTargetContext.js create mode 100644 js/src/tests/ecma_6/Class/newTargetDVG.js create mode 100644 js/src/tests/ecma_6/Class/newTargetDirectInvoke.js create mode 100644 js/src/tests/ecma_6/Class/newTargetMethods.js create mode 100644 js/src/tests/js1_8_5/reflect-parse/newTarget.js delete mode 100644 xpcom/base/pure.h delete mode 100644 xpcom/base/pure_api.c diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 3d1ce247ee..3c63d761e4 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -164,7 +164,7 @@ nsFrameLoader::~nsFrameLoader() if (mMessageManager) { mMessageManager->Disconnect(); } - nsFrameLoader::Destroy(); + MOZ_RELEASE_ASSERT(mDestroyCalled); } nsFrameLoader* @@ -2400,7 +2400,9 @@ public: { nsInProcessTabChildGlobal* tabChild = static_cast(mFrameLoader->mChildMessageManager.get()); - if (tabChild && tabChild->GetInnerManager()) { + // Since bug 1126089, messages can arrive even when the docShell is destroyed. + // Here we make sure that those messages are not delivered. + if (tabChild && tabChild->GetInnerManager() && mFrameLoader->GetExistingDocShell()) { nsCOMPtr kungFuDeathGrip(tabChild->GetGlobal()); ReceiveMessage(static_cast(tabChild), mFrameLoader, tabChild->GetInnerManager()); diff --git a/dom/media/AudioStream.cpp b/dom/media/AudioStream.cpp index 7e14af0d83..6ab9ab5baf 100644 --- a/dom/media/AudioStream.cpp +++ b/dom/media/AudioStream.cpp @@ -12,6 +12,7 @@ #include "VideoUtils.h" #include "mozilla/Monitor.h" #include "mozilla/Mutex.h" +#include "mozilla/Snprintf.h" #include #include "soundtouch/SoundTouch.h" #include "Latency.h" @@ -263,7 +264,7 @@ OpenDumpFile(AudioStream* aStream) if (!getenv("MOZ_DUMP_AUDIO")) return nullptr; char buf[100]; - sprintf(buf, "dumped-audio-%d.wav", gDumpedAudioCount); + snprintf_literal(buf, "dumped-audio-%d.wav", gDumpedAudioCount); FILE* f = fopen(buf, "wb"); if (!f) return nullptr; diff --git a/dom/wifi/WifiHotspotUtils.cpp b/dom/wifi/WifiHotspotUtils.cpp index 5d1d7664c9..c52434518a 100644 --- a/dom/wifi/WifiHotspotUtils.cpp +++ b/dom/wifi/WifiHotspotUtils.cpp @@ -14,6 +14,7 @@ #include "prinit.h" #include "mozilla/Assertions.h" +#include "mozilla/Snprintf.h" #include "nsDebug.h" #include "nsPrintfCString.h" @@ -177,10 +178,10 @@ int32_t WifiHotspotUtils::do_wifi_hostapd_get_stations() } stations++; - sprintf(cmd, "STA-NEXT %s", addr); + snprintf_literal(cmd, "STA-NEXT %s", addr); while (sendCommand(ctrl_conn, cmd, addr, &addrLen) == 0) { stations++; - sprintf(cmd, "STA-NEXT %s", addr); + snprintf_literal(cmd, "STA-NEXT %s", addr); } return stations; diff --git a/extensions/auth/nsHttpNegotiateAuth.cpp b/extensions/auth/nsHttpNegotiateAuth.cpp index 4c13d6b3b1..bd0205e94e 100644 --- a/extensions/auth/nsHttpNegotiateAuth.cpp +++ b/extensions/auth/nsHttpNegotiateAuth.cpp @@ -37,6 +37,7 @@ #include "prmem.h" #include "prnetdb.h" #include "mozilla/Likely.h" +#include "mozilla/Snprintf.h" //----------------------------------------------------------------------------- @@ -280,11 +281,12 @@ nsHttpNegotiateAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChanne LOG((" Sending a token of length %d\n", outTokenLen)); // allocate a buffer sizeof("Negotiate" + " " + b64output_token + "\0") - *creds = (char *) moz_xmalloc(kNegotiateLen + 1 + strlen(encoded_token) + 1); + const int bufsize = kNegotiateLen + 1 + strlen(encoded_token) + 1; + *creds = (char *) moz_xmalloc(bufsize); if (MOZ_UNLIKELY(!*creds)) rv = NS_ERROR_OUT_OF_MEMORY; else - sprintf(*creds, "%s %s", kNegotiate, encoded_token); + snprintf(*creds, bufsize, "%s %s", kNegotiate, encoded_token); PR_Free(encoded_token); return rv; diff --git a/gfx/thebes/gfxFontUtils.cpp b/gfx/thebes/gfxFontUtils.cpp index 24c3458e85..62dc19ac93 100644 --- a/gfx/thebes/gfxFontUtils.cpp +++ b/gfx/thebes/gfxFontUtils.cpp @@ -15,6 +15,7 @@ #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/BinarySearch.h" +#include "mozilla/Snprintf.h" #include "nsCOMPtr.h" #include "nsIUUIDGenerator.h" @@ -68,9 +69,10 @@ gfxSparseBitSet::Dump(const char* aPrefix, eGfxLog aWhichLog) const for (b = 0; b < numBlocks; b++) { Block *block = mBlocks[b]; if (!block) continue; - char outStr[256]; + const int BUFSIZE = 256; + char outStr[BUFSIZE]; int index = 0; - index += sprintf(&outStr[index], "%s u+%6.6x [", aPrefix, (b << BLOCK_INDEX_SHIFT)); + index += snprintf(&outStr[index], BUFSIZE - index, "%s u+%6.6x [", aPrefix, (b << BLOCK_INDEX_SHIFT)); for (int i = 0; i < 32; i += 4) { for (int j = i; j < i + 4; j++) { uint8_t bits = block->mBits[j]; @@ -78,11 +80,11 @@ gfxSparseBitSet::Dump(const char* aPrefix, eGfxLog aWhichLog) const uint8_t flip2 = ((flip1 & 0xcc) >> 2) | ((flip1 & 0x33) << 2); uint8_t flipped = ((flip2 & 0xf0) >> 4) | ((flip2 & 0x0f) << 4); - index += sprintf(&outStr[index], "%2.2x", flipped); + index += snprintf(&outStr[index], BUFSIZE - index, "%2.2x", flipped); } - if (i + 4 != 32) index += sprintf(&outStr[index], " "); + if (i + 4 != 32) index += snprintf(&outStr[index], BUFSIZE - index, " "); } - index += sprintf(&outStr[index], "]"); + index += snprintf(&outStr[index], BUFSIZE - index, "]"); LOG(aWhichLog, ("%s", outStr)); } } @@ -1407,8 +1409,8 @@ gfxFontUtils::DecodeFontName(const char *aNameData, int32_t aByteLen, char warnBuf[128]; if (aByteLen > 64) aByteLen = 64; - sprintf(warnBuf, "skipping font name, unknown charset %d:%d:%d for <%.*s>", - aPlatformCode, aScriptCode, aLangCode, aByteLen, aNameData); + snprintf_literal(warnBuf, "skipping font name, unknown charset %d:%d:%d for <%.*s>", + aPlatformCode, aScriptCode, aLangCode, aByteLen, aNameData); NS_WARNING(warnBuf); #endif return false; diff --git a/gfx/thebes/gfxHarfBuzzShaper.cpp b/gfx/thebes/gfxHarfBuzzShaper.cpp index 6f7adb7292..f835583757 100644 --- a/gfx/thebes/gfxHarfBuzzShaper.cpp +++ b/gfx/thebes/gfxHarfBuzzShaper.cpp @@ -9,6 +9,7 @@ #include "gfxHarfBuzzShaper.h" #include "gfxFontUtils.h" #include "gfxTextRun.h" +#include "mozilla/Snprintf.h" #include "nsUnicodeProperties.h" #include "nsUnicodeScriptCodes.h" #include "nsUnicodeNormalizer.h" @@ -814,10 +815,10 @@ gfxHarfBuzzShaper::GetHKerning(uint16_t aFirstGlyph, #if DEBUG { char buf[1024]; - sprintf(buf, "unknown kern subtable in %s: " - "ver 0 format %d\n", - NS_ConvertUTF16toUTF8(mFont->GetName()).get(), - format); + snprintf_literal(buf, "unknown kern subtable in %s: " + "ver 0 format %d\n", + NS_ConvertUTF16toUTF8(mFont->GetName()).get(), + format); NS_WARNING(buf); } #endif @@ -876,10 +877,10 @@ gfxHarfBuzzShaper::GetHKerning(uint16_t aFirstGlyph, #if DEBUG { char buf[1024]; - sprintf(buf, "unknown kern subtable in %s: " - "ver 0 format %d\n", - NS_ConvertUTF16toUTF8(mFont->GetName()).get(), - format); + snprintf_literal(buf, "unknown kern subtable in %s: " + "ver 0 format %d\n", + NS_ConvertUTF16toUTF8(mFont->GetName()).get(), + format); NS_WARNING(buf); } #endif diff --git a/gfx/thebes/gfxTextRun.cpp b/gfx/thebes/gfxTextRun.cpp index 9b861b41ea..f5c604d5cd 100644 --- a/gfx/thebes/gfxTextRun.cpp +++ b/gfx/thebes/gfxTextRun.cpp @@ -9,6 +9,7 @@ #include "gfxUserFontSet.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/PathHelpers.h" +#include "mozilla/Snprintf.h" #include "nsGkAtoms.h" #include "nsILanguageAtomService.h" #include "nsServiceManagerUtils.h" @@ -1892,8 +1893,8 @@ gfxFontGroup::GetDefaultFont() char msg[256]; // CHECK buffer length if revising message below nsAutoString families; mFamilyList.ToString(families); - sprintf(msg, "unable to find a usable font (%.220s)", - NS_ConvertUTF16toUTF8(families).get()); + snprintf_literal(msg, "unable to find a usable font (%.220s)", + NS_ConvertUTF16toUTF8(families).get()); NS_RUNTIMEABORT(msg); } diff --git a/intl/locale/nsScriptableDateFormat.cpp b/intl/locale/nsScriptableDateFormat.cpp index 69e7084344..b8fa40b8a3 100644 --- a/intl/locale/nsScriptableDateFormat.cpp +++ b/intl/locale/nsScriptableDateFormat.cpp @@ -3,6 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "mozilla/Snprintf.h" #include "nsILocaleService.h" #include "nsDateTimeFormatCID.h" #include "nsIDateTimeFormat.h" @@ -113,7 +114,7 @@ NS_IMETHODIMP nsScriptableDateFormat::FormatDateTime( // if mktime fails (e.g. year <= 1970), then try NSPR. PRTime prtime; char string[32]; - sprintf(string, "%.2d/%.2d/%d %.2d:%.2d:%.2d", month, day, year, hour, minute, second); + snprintf_literal(string, "%.2d/%.2d/%d %.2d:%.2d:%.2d", month, day, year, hour, minute, second); if (PR_SUCCESS != PR_ParseTimeString(string, false, &prtime)) return NS_ERROR_INVALID_ARG; diff --git a/ipc/chromium/src/base/third_party/purify/pure.h b/ipc/chromium/src/base/third_party/purify/pure.h deleted file mode 100644 index ed50a37825..0000000000 --- a/ipc/chromium/src/base/third_party/purify/pure.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Header file of Pure API function declarations. - * -* (C) Copyright IBM Corporation. 2006, 2006. All Rights Reserved. - * You may recompile and redistribute these definitions as required. - * - * Version 1.0 - */ - -#if defined(PURIFY) || defined(QUANTIFY) - -#if defined(c_plusplus) || defined(__cplusplus) -extern "C" { -#endif - -// Don't include this file directly, use purify.h instead. -// If you need something that's not there, add it. -#ifdef PURIFY_PRIVATE_INCLUDE - -#define PURE_H_VERSION 1 -#include - -////////////////////////////// -// API's Specific to Purify // -////////////////////////////// - -// TRUE when Purify is running. -int __cdecl PurifyIsRunning(void) ; -// -// Print a string to the viewer. -// -int __cdecl PurePrintf(const char *fmt, ...) ; -int __cdecl PurifyPrintf(const char *fmt, ...) ; -// -// Purify functions for leak and memory-in-use functionalty. -// -size_t __cdecl PurifyNewInuse(void) ; -size_t __cdecl PurifyAllInuse(void) ; -size_t __cdecl PurifyClearInuse(void) ; -size_t __cdecl PurifyNewLeaks(void) ; -size_t __cdecl PurifyAllLeaks(void) ; -size_t __cdecl PurifyClearLeaks(void) ; -// -// Purify functions for handle leakage. -// -size_t __cdecl PurifyAllHandlesInuse(void) ; -size_t __cdecl PurifyNewHandlesInuse(void) ; -// -// Functions that tell you about the state of memory. -// -size_t __cdecl PurifyDescribe(void *addr) ; -size_t __cdecl PurifyWhatColors(void *addr, size_t size) ; -// -// Functions to test the state of memory. If the memory is not -// accessable, an error is signaled just as if there were a memory -// reference and the function returns false. -// -int __cdecl PurifyAssertIsReadable(const void *addr, size_t size) ; // size used to be an int, until IA64 came along -int __cdecl PurifyAssertIsWritable(const void *addr, size_t size) ; -// -// Functions to test the state of memory. If the memory is not -// accessable, these functions return false. No error is signaled. -// -int __cdecl PurifyIsReadable(const void *addr, size_t size) ; -int __cdecl PurifyIsWritable(const void *addr, size_t size) ; -int __cdecl PurifyIsInitialized(const void *addr, size_t size) ; -// -// Functions to set the state of memory. -// -void __cdecl PurifyMarkAsInitialized(void *addr, size_t size) ; -void __cdecl PurifyMarkAsUninitialized(void *addr, size_t size) ; -// -// Functions to do late detection of ABWs, FMWs, IPWs. -// -#define PURIFY_HEAP_CRT (HANDLE) ~(__int64) 1 /* 0xfffffffe */ -#define PURIFY_HEAP_ALL (HANDLE) ~(__int64) 2 /* 0xfffffffd */ -#define PURIFY_HEAP_BLOCKS_LIVE 0x80000000 -#define PURIFY_HEAP_BLOCKS_DEFERRED_FREE 0x40000000 -#define PURIFY_HEAP_BLOCKS_ALL (PURIFY_HEAP_BLOCKS_LIVE|PURIFY_HEAP_BLOCKS_DEFERRED_FREE) -int __cdecl PurifyHeapValidate(unsigned int hHeap, unsigned int dwFlags, const void *addr) ; -int __cdecl PurifySetLateDetectScanCounter(int counter); -int __cdecl PurifySetLateDetectScanInterval(int seconds); -// -// Functions to support pool allocators -// -void __cdecl PurifySetPoolId(const void *mem, int id); -int __cdecl PurifyGetPoolId(const void *mem); -void __cdecl PurifySetUserData(const void *mem, void *data); -void * __cdecl PurifyGetUserData(const void *mem); -void __cdecl PurifyMapPool(int id, void(*fn)()); - - -//////////////////////////////// -// API's Specific to Quantify // -//////////////////////////////// - -// TRUE when Quantify is running. -int __cdecl QuantifyIsRunning(void) ; - -// -// Functions for controlling collection -// -int __cdecl QuantifyDisableRecordingData(void) ; -int __cdecl QuantifyStartRecordingData(void) ; -int __cdecl QuantifyStopRecordingData(void) ; -int __cdecl QuantifyClearData(void) ; -int __cdecl QuantifyIsRecordingData(void) ; - -// Add a comment to the dataset -int __cdecl QuantifyAddAnnotation(char *) ; - -// Save the current data, creating a "checkpoint" dataset -int __cdecl QuantifySaveData(void) ; - -// Set the name of the current thread in the viewer -int __cdecl QuantifySetThreadName(char *) ; - -//////////////////////////////// -// API's Specific to Coverage // -//////////////////////////////// - -// TRUE when Coverage is running. -int __cdecl CoverageIsRunning(void) ; -// -// Functions for controlling collection -// -int __cdecl CoverageDisableRecordingData(void) ; -int __cdecl CoverageStartRecordingData(void) ; -int __cdecl CoverageStopRecordingData(void) ; -int __cdecl CoverageClearData(void) ; -int __cdecl CoverageIsRecordingData(void) ; -// Add a comment to the dataset -int __cdecl CoverageAddAnnotation(char *) ; - -// Save the current data, creating a "checkpoint" dataset -int __cdecl CoverageSaveData(void) ; - - -#endif // PURIFY_PRIVATE_INCLUDE - -#if defined(c_plusplus) || defined(__cplusplus) -} -#endif - -#endif // defined(PURIFY) || defined(QUANTIFY) \ No newline at end of file diff --git a/ipc/chromium/src/base/third_party/purify/pure_api.c b/ipc/chromium/src/base/third_party/purify/pure_api.c deleted file mode 100644 index 7203cfdc14..0000000000 --- a/ipc/chromium/src/base/third_party/purify/pure_api.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Header file of Pure API function declarations. - * - * Explicitly no copyright. - * You may recompile and redistribute these definitions as required. - * - * NOTE1: In some situations when compiling with MFC, you should - * enable the setting 'Not using precompiled headers' in Visual C++ - * to avoid a compiler diagnostic. - * - * NOTE2: This file works through the use of deep magic. Calls to functions - * in this file are replaced with calls into the OCI runtime system - * when an instrumented version of this program is run. - * - * NOTE3: The static vars avoidGy_n (where n is a unique number) are used - * to prevent optimizing the functions away when compiler option - * /Gy is set. This is needed so that NOTE2 works properly. - */ - -// Chromium note: We used to only compile this code if PURIFY was defined, -// because we did special builds with all optimizations turned off for Purify. -// However, for profiling with Quantify, we want most/all optimizations turned -// on so that we measure something closer to real execution. - -#ifdef _WINDOWS // we only use Purify/Quantify on Windows - -#pragma once - extern int errno; -typedef int ptrdiff_t; -typedef unsigned int size_t; -typedef unsigned short wchar_t; -static int avoidGy_1 = 0; -static int avoidGy_2 = 0; -static int avoidGy_3 = 0; -static int avoidGy_4 = 0; -static int avoidGy_5 = 0; -static int avoidGy_6 = 0; -static int avoidGy_7 = 0; -static int avoidGy_8 = 0; -static int avoidGy_9 = 0; -static int avoidGy_10 = 0; -static int avoidGy_11 = 0; -static int avoidGy_12 = 0; -static int avoidGy_13 = 0; -static int avoidGy_14 = 0; -static int avoidGy_15 = 0; -static int avoidGy_16 = 0; -static int avoidGy_17 = 0; -static int avoidGy_18 = 0; -static int avoidGy_19 = 0; -static int avoidGy_20 = 0; -static int avoidGy_21 = 0; -static int avoidGy_22 = 0; -static int avoidGy_23 = 0; -static int avoidGy_24 = 0; -static int avoidGy_25 = 0; -static int avoidGy_26 = 0; -static int avoidGy_27 = 0; -static int avoidGy_28 = 0; -static int avoidGy_29 = 0; -static int avoidGy_30 = 0; -static int avoidGy_31 = 0; -static int avoidGy_32 = 0; -static int avoidGy_33 = 0; -static int avoidGy_34 = 0; -static int avoidGy_35 = 0; -static int avoidGy_36 = 0; -static int avoidGy_37 = 0; -static int avoidGy_38 = 0; -static int avoidGy_39 = 0; -static int avoidGy_40 = 0; -static int avoidGy_41 = 0; -static int avoidGy_42 = 0; -static int avoidGy_43 = 0; -static int avoidGy_44 = 0; -static int avoidGy_45 = 0; -static int avoidGy_46 = 0; -static int avoidGy_47 = 0; -static int avoidGy_48 = 0; -static int avoidGy_49 = 0; -static int avoidGy_50 = 0; -static int avoidGy_51 = 0; -static int avoidGy_52 = 0; -static int avoidGy_53 = 0; -static int avoidGy_54 = 0; -static int avoidGy_55 = 0; -static int avoidGy_56 = 0; -static int avoidGy_57 = 0; -static int avoidGy_58 = 0; -static int avoidGy_59 = 0; -static int avoidGy_60 = 0; -static int avoidGy_61 = 0; -static int avoidGy_62 = 0; -static int avoidGy_63 = 0; -static int avoidGy_64 = 0; -static int avoidGy_65 = 0; -static int avoidGy_PL_01 = 0; -static int avoidGy_PL_02 = 0; -__declspec(dllexport) int __cdecl PurePrintf(const char *fmt, ...) { if(!++avoidGy_1); fmt; return 0; } -__declspec(dllexport) int __cdecl PurifyIsRunning(void) { if(!++avoidGy_2); return 0; } -__declspec(dllexport) int __cdecl PurifyPrintf(const char *fmt, ...) { if(!++avoidGy_3); fmt; return 0; } -__declspec(dllexport) size_t __cdecl PurifyNewInuse(void) { if(!++avoidGy_4); return 0; } -__declspec(dllexport) size_t __cdecl PurifyAllInuse(void) { if(!++avoidGy_5); return 0; } -__declspec(dllexport) size_t __cdecl PurifyClearInuse(void) { if(!++avoidGy_6); return 0; } -__declspec(dllexport) size_t __cdecl PurifyNewLeaks(void) { if(!++avoidGy_7); return 0; } -__declspec(dllexport) size_t __cdecl PurifyAllLeaks(void) { if(!++avoidGy_8); return 0; } -__declspec(dllexport) size_t __cdecl PurifyClearLeaks(void) { if(!++avoidGy_9); return 0; } -__declspec(dllexport) size_t __cdecl PurifyAllHandlesInuse(void) { if(!++avoidGy_10); return 0; } -__declspec(dllexport) size_t __cdecl PurifyNewHandlesInuse(void) { if(!++avoidGy_11); return 0; } -__declspec(dllexport) size_t __cdecl PurifyDescribe(void *addr) { if(!++avoidGy_12); addr; return 0; } -__declspec(dllexport) int __cdecl PurifyWhatColors(void *addr, size_t size) { if(!++avoidGy_13); addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyAssertIsReadable(const void *addr, size_t size) { if(!++avoidGy_14); addr; size; return 1; } -__declspec(dllexport) int __cdecl PurifyAssertIsWritable(const void *addr, size_t size) { if(!++avoidGy_15); addr; size; return 1; } -__declspec(dllexport) int __cdecl PurifyIsReadable(const void *addr, size_t size) { if(!++avoidGy_16); addr; size; return 1; } -__declspec(dllexport) int __cdecl PurifyIsWritable(const void *addr, size_t size) { if(!++avoidGy_17); addr; size; return 1; } -__declspec(dllexport) int __cdecl PurifyIsInitialized(const void *addr, size_t size) { if(!++avoidGy_18); addr; size; return 1; } -__declspec(dllexport) int __cdecl PurifyRed(void *addr, size_t size) { if(!++avoidGy_19); addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyGreen(void *addr, size_t size) { if(!++avoidGy_20); addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyYellow(void *addr, size_t size) { if(!++avoidGy_21); addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyBlue(void *addr, size_t size) { if(!++avoidGy_22); addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyMarkAsInitialized(void *addr, size_t size) { if(!++avoidGy_23); addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyMarkAsUninitialized(void *addr, size_t size) { if(!++avoidGy_24); addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyMarkForTrap(void *addr, size_t size) { if(!++avoidGy_25); addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyMarkForNoTrap(void *addr, size_t size) { if(!++avoidGy_26); addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyHeapValidate(unsigned int hHeap, unsigned int dwFlags, const void *addr) - { if(!++avoidGy_27); hHeap; dwFlags; addr; return 1; } -__declspec(dllexport) int __cdecl PurifySetLateDetectScanCounter(int counter) { if(!++avoidGy_28); counter; return 0; }; -__declspec(dllexport) int __cdecl PurifySetLateDetectScanInterval(int seconds) { if(!++avoidGy_29); seconds; return 0; }; -__declspec(dllexport) void __cdecl PurifySetPoolId(const void *mem, int id) { if(!++avoidGy_61); mem; id; return; }; -__declspec(dllexport) int __cdecl PurifyGetPoolId(const void *mem) { if(!++avoidGy_62); mem; return 0; }; -__declspec(dllexport) void __cdecl PurifySetUserData(const void *mem, void *data) { if(!++avoidGy_63); mem; data; return; }; -__declspec(dllexport) void * __cdecl PurifyGetUserData(const void *mem) { if(!++avoidGy_64); mem; return 0; }; -__declspec(dllexport) void __cdecl PurifyMapPool(int id, void(*fn)()) { if(!++avoidGy_65); id; fn; return; }; -__declspec(dllexport) int __cdecl CoverageIsRunning(void) { if(!++avoidGy_30); return 0; } -__declspec(dllexport) int __cdecl CoverageDisableRecordingData(void) { if(!++avoidGy_31); return 0; } -__declspec(dllexport) int __cdecl CoverageStartRecordingData(void) { if(!++avoidGy_32); return 0; } -__declspec(dllexport) int __cdecl CoverageStopRecordingData(void) { if(!++avoidGy_33); return 0; } -__declspec(dllexport) int __cdecl CoverageClearData(void) { if(!++avoidGy_34); return 0; } -__declspec(dllexport) int __cdecl CoverageIsRecordingData(void) { if(!++avoidGy_35); return 0; } -__declspec(dllexport) int __cdecl CoverageAddAnnotation(char *str) { if(!++avoidGy_36); str; return 0; } -__declspec(dllexport) int __cdecl CoverageSaveData(void) { if(!++avoidGy_37); return 0; } -__declspec(dllexport) int __cdecl QuantifyIsRunning(void) { if(!++avoidGy_42); return 0; } -__declspec(dllexport) int __cdecl QuantifyDisableRecordingData(void) { if(!++avoidGy_43); return 0; } -__declspec(dllexport) int __cdecl QuantifyStartRecordingData(void) { if(!++avoidGy_44); return 0; } -__declspec(dllexport) int __cdecl QuantifyStopRecordingData(void) { if(!++avoidGy_45); return 0; } -__declspec(dllexport) int __cdecl QuantifyClearData(void) { if(!++avoidGy_46); return 0; } -__declspec(dllexport) int __cdecl QuantifyIsRecordingData(void) { if(!++avoidGy_47); return 0; } -__declspec(dllexport) int __cdecl QuantifyAddAnnotation(char *str) { if(!++avoidGy_48); str; return 0; } -__declspec(dllexport) int __cdecl QuantifySaveData(void) { if(!++avoidGy_49); return 0; } -__declspec(dllexport) int __cdecl QuantifySetThreadName(const char *szName) { if(!++avoidGy_50) ; szName; return 0; } - -#endif // _WINDOWS diff --git a/ipc/netd/Netd.cpp b/ipc/netd/Netd.cpp index efe7d58cfa..8eeb7307bd 100644 --- a/ipc/netd/Netd.cpp +++ b/ipc/netd/Netd.cpp @@ -17,6 +17,7 @@ #include "nsString.h" #include "nsThreadUtils.h" #include "mozilla/RefPtr.h" +#include "mozilla/Snprintf.h" #define NETD_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args) #define ICS_SYS_USB_RNDIS_MAC "/sys/class/android_usb/android0/f_rndis/ethaddr" @@ -67,9 +68,9 @@ InitRndisAddress() address[i % (kEthernetAddressLength - 1) + 1] ^= serialno[i]; } - sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x", - address[0], address[1], address[2], - address[3], address[4], address[5]); + snprintf_literal(mac, "%02x:%02x:%02x:%02x:%02x:%02x", + address[0], address[1], address[2], + address[3], address[4], address[5]); length = strlen(mac); ret = write(fd.get(), mac, length); if (ret != length) { diff --git a/js/public/RootingAPI.h b/js/public/RootingAPI.h index c9cd5770da..20aad5df3a 100644 --- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -76,7 +76,7 @@ * - MutableHandle is a non-const reference to Rooted. It is used in the * same way as Handle and includes a |set(const T& v)| method to allow * updating the value of the referenced Rooted. A MutableHandle can be - * created from a Rooted by using |Rooted::operator&()|. + * created with an implicit cast from a Rooted*. * * In some cases the small performance overhead of exact rooting (measured to * be a few nanoseconds on desktop) is too much. In these cases, try the @@ -1204,7 +1204,5 @@ CallTraceCallbackOnNonHeap(T* v, const TraceCallbacks& aCallbacks, const char* a } /* namespace js */ #undef DELETE_ASSIGNMENT_OPS -#undef DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS -#undef DECLARE_NONPOINTER_ACCESSOR_METHODS #endif /* js_RootingAPI_h */ diff --git a/js/src/asmjs/AsmJSFrameIterator.cpp b/js/src/asmjs/AsmJSFrameIterator.cpp index 418aa4b835..43afbd6278 100644 --- a/js/src/asmjs/AsmJSFrameIterator.cpp +++ b/js/src/asmjs/AsmJSFrameIterator.cpp @@ -684,6 +684,12 @@ BuiltinToName(AsmJSExit::BuiltinKind builtin) #if defined(JS_CODEGEN_ARM) case AsmJSExit::Builtin_IDivMod: return "software idivmod (in asm.js)"; case AsmJSExit::Builtin_UDivMod: return "software uidivmod (in asm.js)"; + case AsmJSExit::Builtin_AtomicCmpXchg: return "Atomics.compareExchange (in asm.js)"; + case AsmJSExit::Builtin_AtomicFetchAdd: return "Atomics.add (in asm.js)"; + case AsmJSExit::Builtin_AtomicFetchSub: return "Atomics.sub (in asm.js)"; + case AsmJSExit::Builtin_AtomicFetchAnd: return "Atomics.and (in asm.js)"; + case AsmJSExit::Builtin_AtomicFetchOr: return "Atomics.or (in asm.js)"; + case AsmJSExit::Builtin_AtomicFetchXor: return "Atomics.xor (in asm.js)"; #endif case AsmJSExit::Builtin_ModD: return "fmod (in asm.js)"; case AsmJSExit::Builtin_SinD: return "Math.sin (in asm.js)"; diff --git a/js/src/asmjs/AsmJSFrameIterator.h b/js/src/asmjs/AsmJSFrameIterator.h index 731337f4d7..6a5847bfd7 100644 --- a/js/src/asmjs/AsmJSFrameIterator.h +++ b/js/src/asmjs/AsmJSFrameIterator.h @@ -79,6 +79,12 @@ namespace AsmJSExit #if defined(JS_CODEGEN_ARM) Builtin_IDivMod, Builtin_UDivMod, + Builtin_AtomicCmpXchg, + Builtin_AtomicFetchAdd, + Builtin_AtomicFetchSub, + Builtin_AtomicFetchAnd, + Builtin_AtomicFetchOr, + Builtin_AtomicFetchXor, #endif Builtin_ModD, Builtin_SinD, diff --git a/js/src/asmjs/AsmJSModule.cpp b/js/src/asmjs/AsmJSModule.cpp index 8c024b8053..363fa634ad 100644 --- a/js/src/asmjs/AsmJSModule.cpp +++ b/js/src/asmjs/AsmJSModule.cpp @@ -43,6 +43,7 @@ #include "jsobjinlines.h" +#include "builtin/AtomicsObject.h" #include "frontend/ParseNode-inl.h" #include "jit/MacroAssembler-inl.h" #include "vm/ArrayBufferObject-inl.h" @@ -695,6 +696,18 @@ AddressOf(AsmJSImmKind kind, ExclusiveContext* cx) return RedirectCall(FuncCast(__aeabi_idivmod), Args_General2); case AsmJSImm_aeabi_uidivmod: return RedirectCall(FuncCast(__aeabi_uidivmod), Args_General2); + case AsmJSImm_AtomicCmpXchg: + return RedirectCall(FuncCast(js::atomics_cmpxchg_asm_callout), Args_General4); + case AsmJSImm_AtomicFetchAdd: + return RedirectCall(FuncCast(js::atomics_add_asm_callout), Args_General3); + case AsmJSImm_AtomicFetchSub: + return RedirectCall(FuncCast(js::atomics_sub_asm_callout), Args_General3); + case AsmJSImm_AtomicFetchAnd: + return RedirectCall(FuncCast(js::atomics_and_asm_callout), Args_General3); + case AsmJSImm_AtomicFetchOr: + return RedirectCall(FuncCast(js::atomics_or_asm_callout), Args_General3); + case AsmJSImm_AtomicFetchXor: + return RedirectCall(FuncCast(js::atomics_xor_asm_callout), Args_General3); #endif case AsmJSImm_ModD: return RedirectCall(FuncCast(NumberMod), Args_Double_DoubleDouble); diff --git a/js/src/asmjs/AsmJSValidate.cpp b/js/src/asmjs/AsmJSValidate.cpp index fb12d33122..0c5b6b64d7 100644 --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -8798,7 +8798,7 @@ GenerateBuiltinThunk(ModuleCompiler& m, AsmJSExit::BuiltinKind builtin) MOZ_ASSERT(masm.framePushed() == 0); MIRTypeVector argTypes(m.cx()); - if (!argTypes.reserve(2)) + if (!argTypes.reserve(5)) return false; switch (builtin) { @@ -8811,6 +8811,21 @@ GenerateBuiltinThunk(ModuleCompiler& m, AsmJSExit::BuiltinKind builtin) argTypes.infallibleAppend(MIRType_Int32); argTypes.infallibleAppend(MIRType_Int32); break; + case AsmJSExit::Builtin_AtomicCmpXchg: + argTypes.infallibleAppend(MIRType_Int32); + argTypes.infallibleAppend(MIRType_Int32); + argTypes.infallibleAppend(MIRType_Int32); + argTypes.infallibleAppend(MIRType_Int32); + break; + case AsmJSExit::Builtin_AtomicFetchAdd: + case AsmJSExit::Builtin_AtomicFetchSub: + case AsmJSExit::Builtin_AtomicFetchAnd: + case AsmJSExit::Builtin_AtomicFetchOr: + case AsmJSExit::Builtin_AtomicFetchXor: + argTypes.infallibleAppend(MIRType_Int32); + argTypes.infallibleAppend(MIRType_Int32); + argTypes.infallibleAppend(MIRType_Int32); + break; #endif case AsmJSExit::Builtin_SinD: case AsmJSExit::Builtin_CosD: diff --git a/js/src/builtin/AtomicsObject.cpp b/js/src/builtin/AtomicsObject.cpp index d5288dc3bc..98a11d4d50 100644 --- a/js/src/builtin/AtomicsObject.cpp +++ b/js/src/builtin/AtomicsObject.cpp @@ -55,98 +55,18 @@ #include "prmjtime.h" +#include "jit/AtomicOperations.h" + #include "js/Class.h" #include "vm/GlobalObject.h" #include "vm/SharedTypedArrayObject.h" #include "vm/TypedArrayObject.h" #include "jsobjinlines.h" +#include "jit/AtomicOperations-inl.h" using namespace js; -#if defined(MOZ_HAVE_CXX11_ATOMICS) -# define CXX11_ATOMICS -#elif defined(__clang__) || defined(__GNUC__) -# define GNU_ATOMICS -#elif _MSC_VER >= 1700 && _MSC_VER < 1800 -// Visual Studion 2012 -# define CXX11_ATOMICS -# include -#elif defined(_MSC_VER) -// Visual Studio 2010 -# define GNU_ATOMICS -static inline void -__sync_synchronize() -{ -# if JS_BITS_PER_WORD == 32 - // If configured for SSE2+ we can use the MFENCE instruction, available - // through the _mm_mfence intrinsic. But for non-SSE2 systems we have - // to do something else. Linux uses "lock add [esp], 0", so why not? - __asm lock add [esp], 0; -# else - _mm_mfence(); -# endif -} - -# define MSC_CAS(T, U, cmpxchg) \ - static inline T \ - __sync_val_compare_and_swap(T* addr, T oldval, T newval) { \ - return (T)cmpxchg((U volatile*)addr, (U)oldval, (U)newval); \ - } - -MSC_CAS(int8_t, char, _InterlockedCompareExchange8) -MSC_CAS(uint8_t, char, _InterlockedCompareExchange8) -MSC_CAS(int16_t, short, _InterlockedCompareExchange16) -MSC_CAS(uint16_t, short, _InterlockedCompareExchange16) -MSC_CAS(int32_t, long, _InterlockedCompareExchange) -MSC_CAS(uint32_t, long, _InterlockedCompareExchange) - -# define MSC_FETCHADDOP(T, U, xadd) \ - static inline T \ - __sync_fetch_and_add(T* addr, T val) { \ - return (T)xadd((U volatile*)addr, (U)val); \ - } \ - static inline T \ - __sync_fetch_and_sub(T* addr, T val) { \ - return (T)xadd((U volatile*)addr, (U)-val); \ - } - -MSC_FETCHADDOP(int8_t, char, _InterlockedExchangeAdd8) -MSC_FETCHADDOP(uint8_t, char, _InterlockedExchangeAdd8) -MSC_FETCHADDOP(int16_t, short, _InterlockedExchangeAdd16) -MSC_FETCHADDOP(uint16_t, short, _InterlockedExchangeAdd16) -MSC_FETCHADDOP(int32_t, long, _InterlockedExchangeAdd) -MSC_FETCHADDOP(uint32_t, long, _InterlockedExchangeAdd) - -# define MSC_FETCHBITOP(T, U, andop, orop, xorop) \ - static inline T \ - __sync_fetch_and_and(T* addr, T val) { \ - return (T)andop((U volatile*)addr, (U)val); \ - } \ - static inline T \ - __sync_fetch_and_or(T* addr, T val) { \ - return (T)orop((U volatile*)addr, (U)val); \ - } \ - static inline T \ - __sync_fetch_and_xor(T* addr, T val) { \ - return (T)xorop((U volatile*)addr, (U)val); \ - } \ - -MSC_FETCHBITOP(int8_t, char, _InterlockedAnd8, _InterlockedOr8, _InterlockedXor8) -MSC_FETCHBITOP(uint8_t, char, _InterlockedAnd8, _InterlockedOr8, _InterlockedXor8) -MSC_FETCHBITOP(int16_t, short, _InterlockedAnd16, _InterlockedOr16, _InterlockedXor16) -MSC_FETCHBITOP(uint16_t, short, _InterlockedAnd16, _InterlockedOr16, _InterlockedXor16) -MSC_FETCHBITOP(int32_t, long, _InterlockedAnd, _InterlockedOr, _InterlockedXor) -MSC_FETCHBITOP(uint32_t, long, _InterlockedAnd, _InterlockedOr, _InterlockedXor) - -# undef MSC_CAS -# undef MSC_FETCHADDOP -# undef MSC_FETCHBITOP - -#elif defined(ENABLE_SHARED_ARRAY_BUFFER) -# error "Either disable JS shared memory or use a compiler that supports C++11 atomics or GCC/clang atomics" -#endif - const Class AtomicsObject::class_ = { "Atomics", JSCLASS_HAS_CACHED_PROTO(JSProto_Atomics) @@ -193,11 +113,7 @@ GetSharedTypedArrayIndex(JSContext* cx, HandleValue v, Handletype(), oldCandidate, newCandidate, view->viewData(), offset, &badType); -#if defined(CXX11_ATOMICS) -# define CAS(T, addr, oldval, newval) \ - do { \ - std::atomic_compare_exchange_strong(reinterpret_cast*>(addr), &oldval, newval); \ - } while(0) -#elif defined(GNU_ATOMICS) -# define CAS(T, addr, oldval, newval) \ - do { \ - oldval = __sync_val_compare_and_swap(addr, (oldval), (newval)); \ - } while(0) -#else -# define CAS(a, b, c, newval) (void)newval -#endif - - switch (view->type()) { - case Scalar::Int8: { - int8_t oldval = (int8_t)oldCandidate; - int8_t newval = (int8_t)newCandidate; - CAS(int8_t, (int8_t*)view->viewData() + offset, oldval, newval); - r.setInt32(oldval); - return true; - } - case Scalar::Uint8: { - uint8_t oldval = (uint8_t)oldCandidate; - uint8_t newval = (uint8_t)newCandidate; - CAS(uint8_t, (uint8_t*)view->viewData() + offset, oldval, newval); - r.setInt32(oldval); - return true; - } - case Scalar::Uint8Clamped: { - uint8_t oldval = ClampIntForUint8Array(oldCandidate); - uint8_t newval = ClampIntForUint8Array(newCandidate); - CAS(uint8_t, (uint8_t*)view->viewData() + offset, oldval, newval); - r.setInt32(oldval); - return true; - } - case Scalar::Int16: { - int16_t oldval = (int16_t)oldCandidate; - int16_t newval = (int16_t)newCandidate; - CAS(int16_t, (int16_t*)view->viewData() + offset, oldval, newval); - r.setInt32(oldval); - return true; - } - case Scalar::Uint16: { - uint16_t oldval = (uint16_t)oldCandidate; - uint16_t newval = (uint16_t)newCandidate; - CAS(uint16_t, (uint16_t*)view->viewData() + offset, oldval, newval); - r.setInt32(oldval); - return true; - } - case Scalar::Int32: { - int32_t oldval = oldCandidate; - int32_t newval = newCandidate; - CAS(int32_t, (int32_t*)view->viewData() + offset, oldval, newval); - r.setInt32(oldval); - return true; - } - case Scalar::Uint32: { - uint32_t oldval = (uint32_t)oldCandidate; - uint32_t newval = (uint32_t)newCandidate; - CAS(uint32_t, (uint32_t*)view->viewData() + offset, oldval, newval); - r.setNumber((double)oldval); - return true; - } - default: + if (badType) return ReportBadArrayType(cx); - } - // Do not undef CAS, it is used later + if (view->type() == Scalar::Uint32) + r.setNumber((double)(uint32_t)result); + else + r.setInt32(result); + return true; } bool @@ -335,69 +243,41 @@ js::atomics_load(JSContext* cx, unsigned argc, Value* vp) if (!inRange) return atomics_fence_impl(cx, r); - // LOAD sets v to the value of *addr - // addr must be a T*, and v must be a variable of type T - -#if defined(CXX11_ATOMICS) -# define LOAD(T, addr, v) \ - do { \ - v = std::atomic_load(reinterpret_cast*>(addr)); \ - } while(0) -#elif defined(GNU_ATOMICS) -# define LOAD(T, addr, v) \ - do { \ - __sync_synchronize(); \ - v = *(addr); \ - __sync_synchronize(); \ - } while(0) -#else -# define LOAD(a, b, v) v = 0 -#endif - switch (view->type()) { case Scalar::Uint8: case Scalar::Uint8Clamped: { - uint8_t v; - LOAD(uint8_t, (uint8_t*)view->viewData() + offset, v); + uint8_t v = jit::AtomicOperations::loadSeqCst((uint8_t*)view->viewData() + offset); r.setInt32(v); return true; } case Scalar::Int8: { - int8_t v; - LOAD(int8_t, (int8_t*)view->viewData() + offset, v); + int8_t v = jit::AtomicOperations::loadSeqCst((uint8_t*)view->viewData() + offset); r.setInt32(v); return true; } case Scalar::Int16: { - int16_t v; - LOAD(int16_t, (int16_t*)view->viewData() + offset, v); + int16_t v = jit::AtomicOperations::loadSeqCst((int16_t*)view->viewData() + offset); r.setInt32(v); return true; } case Scalar::Uint16: { - uint16_t v; - LOAD(uint16_t, (uint16_t*)view->viewData() + offset, v); + uint16_t v = jit::AtomicOperations::loadSeqCst((uint16_t*)view->viewData() + offset); r.setInt32(v); return true; } case Scalar::Int32: { - int32_t v; - LOAD(int32_t, (int32_t*)view->viewData() + offset, v); + int32_t v = jit::AtomicOperations::loadSeqCst((int32_t*)view->viewData() + offset); r.setInt32(v); return true; } case Scalar::Uint32: { - uint32_t v; - LOAD(uint32_t, (uint32_t*)view->viewData() + offset, v); + uint32_t v = jit::AtomicOperations::loadSeqCst((uint32_t*)view->viewData() + offset); r.setNumber(v); return true; } default: return ReportBadArrayType(cx); } - -#undef LOAD - } bool @@ -426,73 +306,52 @@ js::atomics_store(JSContext* cx, unsigned argc, Value* vp) return true; } - // STORE stores value in *addr - // addr must be a T*, and value should be of type T - -#if defined(CXX11_ATOMICS) -# define STORE(T, addr, value) \ - do { \ - std::atomic_store(reinterpret_cast*>(addr), (T)value); \ -} while(0) -#elif defined(GNU_ATOMICS) -# define STORE(T, addr, value) \ - do { \ - __sync_synchronize(); \ - *(addr) = value; \ - __sync_synchronize(); \ - } while(0) -#else -# define STORE(a, b, c) (void)0 -#endif - switch (view->type()) { case Scalar::Int8: { int8_t value = (int8_t)numberValue; - STORE(int8_t, (int8_t*)view->viewData() + offset, value); + jit::AtomicOperations::storeSeqCst((int8_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Uint8: { uint8_t value = (uint8_t)numberValue; - STORE(uint8_t, (uint8_t*)view->viewData() + offset, value); + jit::AtomicOperations::storeSeqCst((uint8_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Uint8Clamped: { uint8_t value = ClampIntForUint8Array(numberValue); - STORE(uint8_t, (uint8_t*)view->viewData() + offset, value); + jit::AtomicOperations::storeSeqCst((uint8_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Int16: { int16_t value = (int16_t)numberValue; - STORE(int16_t, (int16_t*)view->viewData() + offset, value); + jit::AtomicOperations::storeSeqCst((int16_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Uint16: { uint16_t value = (uint16_t)numberValue; - STORE(uint16_t, (uint16_t*)view->viewData() + offset, value); + jit::AtomicOperations::storeSeqCst((uint16_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Int32: { int32_t value = numberValue; - STORE(int32_t, (int32_t*)view->viewData() + offset, value); + jit::AtomicOperations::storeSeqCst((int32_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Uint32: { uint32_t value = (uint32_t)numberValue; - STORE(uint32_t, (uint32_t*)view->viewData() + offset, value); + jit::AtomicOperations::storeSeqCst((uint32_t*)view->viewData() + offset, value); r.setNumber((double)value); return true; } default: return ReportBadArrayType(cx); } - -#undef STORE } template @@ -537,8 +396,7 @@ atomics_binop_impl(JSContext* cx, HandleValue objv, HandleValue idxv, HandleValu for (;;) { uint8_t old = *loc; uint8_t result = (uint8_t)ClampIntForUint8Array(T::perform(old, value)); - uint8_t tmp = old; // tmp is overwritten by CAS - CAS(uint8_t, loc, tmp, result); + uint8_t tmp = jit::AtomicOperations::compareExchangeSeqCst(loc, old, result); if (tmp == old) { r.setInt32(old); break; @@ -571,28 +429,18 @@ atomics_binop_impl(JSContext* cx, HandleValue objv, HandleValue idxv, HandleValu } } -#define INTEGRAL_TYPES_FOR_EACH(NAME, TRANSFORM) \ - static int8_t operate(int8_t* addr, int8_t v) { return NAME(TRANSFORM(int8_t, addr), v); } \ - static uint8_t operate(uint8_t* addr, uint8_t v) { return NAME(TRANSFORM(uint8_t, addr), v); } \ - static int16_t operate(int16_t* addr, int16_t v) { return NAME(TRANSFORM(int16_t, addr), v); } \ - static uint16_t operate(uint16_t* addr, uint16_t v) { return NAME(TRANSFORM(uint16_t, addr), v); } \ - static int32_t operate(int32_t* addr, int32_t v) { return NAME(TRANSFORM(int32_t, addr), v); } \ - static uint32_t operate(uint32_t* addr, uint32_t v) { return NAME(TRANSFORM(uint32_t, addr), v); } - -#define CAST_ATOMIC(t, v) reinterpret_cast*>(v) -#define DO_NOTHING(t, v) v -#define ZERO(t, v) 0 +#define INTEGRAL_TYPES_FOR_EACH(NAME) \ + static int8_t operate(int8_t* addr, int8_t v) { return NAME(addr, v); } \ + static uint8_t operate(uint8_t* addr, uint8_t v) { return NAME(addr, v); } \ + static int16_t operate(int16_t* addr, int16_t v) { return NAME(addr, v); } \ + static uint16_t operate(uint16_t* addr, uint16_t v) { return NAME(addr, v); } \ + static int32_t operate(int32_t* addr, int32_t v) { return NAME(addr, v); } \ + static uint32_t operate(uint32_t* addr, uint32_t v) { return NAME(addr, v); } class do_add { public: -#if defined(CXX11_ATOMICS) - INTEGRAL_TYPES_FOR_EACH(std::atomic_fetch_add, CAST_ATOMIC) -#elif defined(GNU_ATOMICS) - INTEGRAL_TYPES_FOR_EACH(__sync_fetch_and_add, DO_NOTHING) -#else - INTEGRAL_TYPES_FOR_EACH(ZERO, DO_NOTHING) -#endif + INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchAddSeqCst) static int32_t perform(int32_t x, int32_t y) { return x + y; } }; @@ -606,13 +454,7 @@ js::atomics_add(JSContext* cx, unsigned argc, Value* vp) class do_sub { public: -#if defined(CXX11_ATOMICS) - INTEGRAL_TYPES_FOR_EACH(std::atomic_fetch_sub, CAST_ATOMIC) -#elif defined(GNU_ATOMICS) - INTEGRAL_TYPES_FOR_EACH(__sync_fetch_and_sub, DO_NOTHING) -#else - INTEGRAL_TYPES_FOR_EACH(ZERO, DO_NOTHING) -#endif + INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchSubSeqCst) static int32_t perform(int32_t x, int32_t y) { return x - y; } }; @@ -626,13 +468,7 @@ js::atomics_sub(JSContext* cx, unsigned argc, Value* vp) class do_and { public: -#if defined(CXX11_ATOMICS) - INTEGRAL_TYPES_FOR_EACH(std::atomic_fetch_and, CAST_ATOMIC) -#elif defined(GNU_ATOMICS) - INTEGRAL_TYPES_FOR_EACH(__sync_fetch_and_and, DO_NOTHING) -#else - INTEGRAL_TYPES_FOR_EACH(ZERO, DO_NOTHING) -#endif + INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchAndSeqCst) static int32_t perform(int32_t x, int32_t y) { return x & y; } }; @@ -646,13 +482,7 @@ js::atomics_and(JSContext* cx, unsigned argc, Value* vp) class do_or { public: -#if defined(CXX11_ATOMICS) - INTEGRAL_TYPES_FOR_EACH(std::atomic_fetch_or, CAST_ATOMIC) -#elif defined(GNU_ATOMICS) - INTEGRAL_TYPES_FOR_EACH(__sync_fetch_and_or, DO_NOTHING) -#else - INTEGRAL_TYPES_FOR_EACH(ZERO, DO_NOTHING) -#endif + INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchOrSeqCst) static int32_t perform(int32_t x, int32_t y) { return x | y; } }; @@ -666,13 +496,7 @@ js::atomics_or(JSContext* cx, unsigned argc, Value* vp) class do_xor { public: -#if defined(CXX11_ATOMICS) - INTEGRAL_TYPES_FOR_EACH(std::atomic_fetch_xor, CAST_ATOMIC) -#elif defined(GNU_ATOMICS) - INTEGRAL_TYPES_FOR_EACH(__sync_fetch_and_xor, DO_NOTHING) -#else - INTEGRAL_TYPES_FOR_EACH(ZERO, DO_NOTHING) -#endif + INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchXorSeqCst) static int32_t perform(int32_t x, int32_t y) { return x ^ y; } }; @@ -683,10 +507,151 @@ js::atomics_xor(JSContext* cx, unsigned argc, Value* vp) return atomics_binop_impl(cx, args.get(0), args.get(1), args.get(2), args.rval()); } -#undef INTEGRAL_TYPES_FOR_EACH -#undef CAST_ATOMIC -#undef DO_NOTHING -#undef ZERO +// asm.js callouts for platforms that do not have non-word-sized +// atomics where we don't want to inline the logic for the atomics. +// +// size is currently -1 (signed byte), 1 (unsigned byte), -2 (signed halfword), +// or 2 (halfword). +// ptr is the byte offset within the heap array. This will have low bit zero +// for halfword accesses. +// value (for binops) and oldval/newval (for cmpxchg) are the values +// to be operated upon. + +static void +GetCurrentAsmJSHeap(void** heap, size_t* length) +{ + JSRuntime* rt = js::TlsPerThreadData.get()->runtimeFromMainThread(); + AsmJSModule& mod = rt->asmJSActivationStack()->module(); + *heap = mod.heapDatum(); + *length = mod.heapLength(); +} + +int32_t +js::atomics_add_asm_callout(int32_t vt, int32_t offset, int32_t value) +{ + void* heap; + size_t heapLength; + GetCurrentAsmJSHeap(&heap, &heapLength); + if ((size_t)offset >= heapLength) return 0; + switch (Scalar::Type(vt)) { + case Scalar::Int8: + return do_add::operate((int8_t*)heap + offset, value); + case Scalar::Uint8: + return do_add::operate((uint8_t*)heap + offset, value); + case Scalar::Int16: + return do_add::operate((int16_t*)heap + (offset >> 1), value); + case Scalar::Uint16: + return do_add::operate((uint16_t*)heap + (offset >> 1), value); + default: + MOZ_CRASH("Invalid size"); + } +} + +int32_t +js::atomics_sub_asm_callout(int32_t vt, int32_t offset, int32_t value) +{ + void* heap; + size_t heapLength; + GetCurrentAsmJSHeap(&heap, &heapLength); + if ((size_t)offset >= heapLength) return 0; + switch (Scalar::Type(vt)) { + case Scalar::Int8: + return do_sub::operate((int8_t*)heap + offset, value); + case Scalar::Uint8: + return do_sub::operate((uint8_t*)heap + offset, value); + case Scalar::Int16: + return do_sub::operate((int16_t*)heap + (offset >> 1), value); + case Scalar::Uint16: + return do_sub::operate((uint16_t*)heap + (offset >> 1), value); + default: + MOZ_CRASH("Invalid size"); + } +} + +int32_t +js::atomics_and_asm_callout(int32_t vt, int32_t offset, int32_t value) +{ + void* heap; + size_t heapLength; + GetCurrentAsmJSHeap(&heap, &heapLength); + if ((size_t)offset >= heapLength) return 0; + switch (Scalar::Type(vt)) { + case Scalar::Int8: + return do_and::operate((int8_t*)heap + offset, value); + case Scalar::Uint8: + return do_and::operate((uint8_t*)heap + offset, value); + case Scalar::Int16: + return do_and::operate((int16_t*)heap + (offset >> 1), value); + case Scalar::Uint16: + return do_and::operate((uint16_t*)heap + (offset >> 1), value); + default: + MOZ_CRASH("Invalid size"); + } +} + +int32_t +js::atomics_or_asm_callout(int32_t vt, int32_t offset, int32_t value) +{ + void* heap; + size_t heapLength; + GetCurrentAsmJSHeap(&heap, &heapLength); + if ((size_t)offset >= heapLength) return 0; + switch (Scalar::Type(vt)) { + case Scalar::Int8: + return do_or::operate((int8_t*)heap + offset, value); + case Scalar::Uint8: + return do_or::operate((uint8_t*)heap + offset, value); + case Scalar::Int16: + return do_or::operate((int16_t*)heap + (offset >> 1), value); + case Scalar::Uint16: + return do_or::operate((uint16_t*)heap + (offset >> 1), value); + default: + MOZ_CRASH("Invalid size"); + } +} + +int32_t +js::atomics_xor_asm_callout(int32_t vt, int32_t offset, int32_t value) +{ + void* heap; + size_t heapLength; + GetCurrentAsmJSHeap(&heap, &heapLength); + if ((size_t)offset >= heapLength) return 0; + switch (Scalar::Type(vt)) { + case Scalar::Int8: + return do_xor::operate((int8_t*)heap + offset, value); + case Scalar::Uint8: + return do_xor::operate((uint8_t*)heap + offset, value); + case Scalar::Int16: + return do_xor::operate((int16_t*)heap + (offset >> 1), value); + case Scalar::Uint16: + return do_xor::operate((uint16_t*)heap + (offset >> 1), value); + default: + MOZ_CRASH("Invalid size"); + } +} + +int32_t +js::atomics_cmpxchg_asm_callout(int32_t vt, int32_t offset, int32_t oldval, int32_t newval) +{ + void* heap; + size_t heapLength; + GetCurrentAsmJSHeap(&heap, &heapLength); + if ((size_t)offset >= heapLength) return 0; + bool badType = false; + switch (Scalar::Type(vt)) { + case Scalar::Int8: + return do_cmpxchg(Scalar::Int8, oldval, newval, heap, offset, &badType); + case Scalar::Uint8: + return do_cmpxchg(Scalar::Uint8, oldval, newval, heap, offset, &badType); + case Scalar::Int16: + return do_cmpxchg(Scalar::Int16, oldval, newval, heap, offset>>1, &badType); + case Scalar::Uint16: + return do_cmpxchg(Scalar::Uint16, oldval, newval, heap, offset>>1, &badType); + default: + MOZ_CRASH("Invalid size"); + } +} namespace js { diff --git a/js/src/builtin/AtomicsObject.h b/js/src/builtin/AtomicsObject.h index 768225459f..a3f9ef378d 100644 --- a/js/src/builtin/AtomicsObject.h +++ b/js/src/builtin/AtomicsObject.h @@ -44,6 +44,14 @@ bool atomics_futexWait(JSContext* cx, unsigned argc, Value* vp); bool atomics_futexWake(JSContext* cx, unsigned argc, Value* vp); bool atomics_futexWakeOrRequeue(JSContext* cx, unsigned argc, Value* vp); +/* asm.js callouts */ +int32_t atomics_add_asm_callout(int32_t vt, int32_t offset, int32_t value); +int32_t atomics_sub_asm_callout(int32_t vt, int32_t offset, int32_t value); +int32_t atomics_and_asm_callout(int32_t vt, int32_t offset, int32_t value); +int32_t atomics_or_asm_callout(int32_t vt, int32_t offset, int32_t value); +int32_t atomics_xor_asm_callout(int32_t vt, int32_t offset, int32_t value); +int32_t atomics_cmpxchg_asm_callout(int32_t vt, int32_t offset, int32_t oldval, int32_t newval); + class FutexRuntime { public: diff --git a/js/src/builtin/Eval.cpp b/js/src/builtin/Eval.cpp index 1d7509552c..3b348a6ee3 100644 --- a/js/src/builtin/Eval.cpp +++ b/js/src/builtin/Eval.cpp @@ -465,22 +465,6 @@ js::DirectEvalStringFromIon(JSContext* cx, NullFramePtr() /* evalInFrame */, vp.address()); } -bool -js::DirectEvalValueFromIon(JSContext* cx, - HandleObject scopeobj, HandleScript callerScript, - HandleValue thisValue, HandleValue evalArg, - jsbytecode* pc, MutableHandleValue vp) -{ - // Act as identity on non-strings per ES5 15.1.2.1 step 1. - if (!evalArg.isString()) { - vp.set(evalArg); - return true; - } - - RootedString string(cx, evalArg.toString()); - return DirectEvalStringFromIon(cx, scopeobj, callerScript, thisValue, string, pc, vp); -} - bool js::IndirectEval(JSContext* cx, unsigned argc, Value* vp) { diff --git a/js/src/builtin/Eval.h b/js/src/builtin/Eval.h index 7badd7315f..0b27abbf01 100644 --- a/js/src/builtin/Eval.h +++ b/js/src/builtin/Eval.h @@ -32,11 +32,6 @@ DirectEvalStringFromIon(JSContext* cx, HandleObject scopeObj, HandleScript callerScript, HandleValue thisValue, HandleString str, jsbytecode * pc, MutableHandleValue vp); -extern bool -DirectEvalValueFromIon(JSContext* cx, - HandleObject scopeObj, HandleScript callerScript, - HandleValue thisValue, HandleValue evalArg, - jsbytecode * pc, MutableHandleValue vp); // True iff fun is a built-in eval function. extern bool diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index ac243609ba..7924c6cd60 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -683,7 +683,8 @@ class NodeBuilder bool importSpecifier(HandleValue importName, HandleValue bindingName, TokenPos* pos, MutableHandleValue dst); - bool exportDeclaration(HandleValue decl, NodeVector& elts, HandleValue moduleSpec, TokenPos* pos, MutableHandleValue dst); + bool exportDeclaration(HandleValue decl, NodeVector& elts, HandleValue moduleSpec, + HandleValue isDefault, TokenPos* pos, MutableHandleValue dst); bool exportSpecifier(HandleValue bindingName, HandleValue exportName, TokenPos* pos, MutableHandleValue dst); @@ -754,6 +755,8 @@ class NodeBuilder bool generatorExpression(HandleValue body, NodeVector& blocks, HandleValue filter, bool isLegacy, TokenPos* pos, MutableHandleValue dst); + bool newTargetExpression(TokenPos* pos, MutableHandleValue dst); + /* * declarations */ @@ -1597,13 +1600,13 @@ NodeBuilder::importSpecifier(HandleValue importName, HandleValue bindingName, To bool NodeBuilder::exportDeclaration(HandleValue decl, NodeVector& elts, HandleValue moduleSpec, - TokenPos* pos, MutableHandleValue dst) + HandleValue isDefault, TokenPos* pos, MutableHandleValue dst) { RootedValue array(cx, NullValue()); if (decl.isNull() && !newArray(elts, &array)) return false; - RootedValue cb(cx, callbacks[AST_IMPORT_DECL]); + RootedValue cb(cx, callbacks[AST_EXPORT_DECL]); if (!cb.isNull()) return callback(cb, decl, array, moduleSpec, pos, dst); @@ -1612,6 +1615,7 @@ NodeBuilder::exportDeclaration(HandleValue decl, NodeVector& elts, HandleValue m "declaration", decl, "specifiers", array, "source", moduleSpec, + "isDefault", isDefault, dst); } @@ -1820,6 +1824,16 @@ NodeBuilder::classDefinition(bool expr, HandleValue name, HandleValue heritage, dst); } +bool +NodeBuilder::newTargetExpression(TokenPos* pos, MutableHandleValue dst) +{ + RootedValue cb(cx, callbacks[AST_NEWTARGET_EXPR]); + if (!cb.isNull()) + return callback(cb, pos, dst); + + return newNode(AST_NEWTARGET_EXPR, pos, dst); +} + namespace { /* @@ -1984,9 +1998,12 @@ ASTSerializer::aop(JSOp op) UnaryOperator ASTSerializer::unop(ParseNodeKind kind, JSOp op) { - if (kind == PNK_DELETE) + if (IsDeleteKind(kind)) return UNOP_DELETE; + if (kind == PNK_TYPEOFNAME || kind == PNK_TYPEOFEXPR) + return UNOP_TYPEOF; + switch (op) { case JSOP_NEG: return UNOP_NEG; @@ -1996,9 +2013,6 @@ ASTSerializer::unop(ParseNodeKind kind, JSOp op) return UNOP_NOT; case JSOP_BITNOT: return UNOP_BITNOT; - case JSOP_TYPEOF: - case JSOP_TYPEOFEXPR: - return UNOP_TYPEOF; case JSOP_VOID: return UNOP_VOID; default: @@ -2234,6 +2248,7 @@ bool ASTSerializer::importDeclaration(ParseNode* pn, MutableHandleValue dst) { MOZ_ASSERT(pn->isKind(PNK_IMPORT)); + MOZ_ASSERT(pn->isArity(PN_BINARY)); MOZ_ASSERT(pn->pn_left->isKind(PNK_IMPORT_SPEC_LIST)); MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING)); @@ -2268,13 +2283,15 @@ ASTSerializer::importSpecifier(ParseNode* pn, MutableHandleValue dst) bool ASTSerializer::exportDeclaration(ParseNode* pn, MutableHandleValue dst) { - MOZ_ASSERT(pn->isKind(PNK_EXPORT) || pn->isKind(PNK_EXPORT_FROM)); + MOZ_ASSERT(pn->isKind(PNK_EXPORT) || + pn->isKind(PNK_EXPORT_FROM) || + pn->isKind(PNK_EXPORT_DEFAULT)); MOZ_ASSERT_IF(pn->isKind(PNK_EXPORT_FROM), pn->pn_right->isKind(PNK_STRING)); RootedValue decl(cx, NullValue()); NodeVector elts(cx); - ParseNode* kid = pn->isKind(PNK_EXPORT) ? pn->pn_kid : pn->pn_left; + ParseNode* kid = pn->isKind(PNK_EXPORT_FROM) ? pn->pn_left: pn->pn_kid; switch (ParseNodeKind kind = kid->getKind()) { case PNK_EXPORT_SPEC_LIST: if (!elts.reserve(pn->pn_left->pn_count)) @@ -2298,6 +2315,11 @@ ASTSerializer::exportDeclaration(ParseNode* pn, MutableHandleValue dst) return false; break; + case PNK_CLASS: + if (!classDefinition(kid, false, &decl)) + return false; + break; + case PNK_VAR: case PNK_CONST: case PNK_GLOBALCONST: @@ -2307,14 +2329,20 @@ ASTSerializer::exportDeclaration(ParseNode* pn, MutableHandleValue dst) break; default: - LOCAL_NOT_REACHED("unexpected statement type"); + if (!expression(kid, &decl)) + return false; + break; } RootedValue moduleSpec(cx, NullValue()); if (pn->isKind(PNK_EXPORT_FROM) && !literal(pn->pn_right, &moduleSpec)) return false; - return builder.exportDeclaration(decl, elts, moduleSpec, &pn->pn_pos, dst); + RootedValue isDefault(cx, BooleanValue(false)); + if (pn->isKind(PNK_EXPORT_DEFAULT)) + isDefault.setBoolean(true); + + return builder.exportDeclaration(decl, elts, moduleSpec, isDefault, &pn->pn_pos, dst); } bool @@ -2507,6 +2535,7 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst) return importDeclaration(pn, dst); case PNK_EXPORT: + case PNK_EXPORT_DEFAULT: case PNK_EXPORT_FROM: return exportDeclaration(pn, dst); @@ -3059,8 +3088,14 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case PNK_POW: return rightAssociate(pn, dst); - case PNK_DELETE: - case PNK_TYPEOF: + case PNK_DELETENAME: + case PNK_DELETEPROP: + case PNK_DELETESUPERPROP: + case PNK_DELETEELEM: + case PNK_DELETESUPERELEM: + case PNK_DELETEEXPR: + case PNK_TYPEOFNAME: + case PNK_TYPEOFEXPR: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: @@ -3300,6 +3335,9 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case PNK_CLASS: return classDefinition(pn, true, dst); + case PNK_NEWTARGET: + return builder.newTargetExpression(&pn->pn_pos, dst); + default: LOCAL_NOT_REACHED("unexpected expression type"); } diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js index 844ea33224..b49e3b0f82 100644 --- a/js/src/builtin/TypedArray.js +++ b/js/src/builtin/TypedArray.js @@ -2,6 +2,79 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "TypedObjectConstants.h" + +// ES6 draft 20150304 %TypedArray%.prototype.copyWithin +function TypedArrayCopyWithin(target, start, end = undefined) { + // This function is not generic. + if (!IsObject(this) || !IsTypedArray(this)) { + return callFunction(CallTypedArrayMethodIfWrapped, this, target, start, end, + "TypedArrayCopyWithin"); + } + + // Bug 1101256: detachment checks mandated by ValidateTypedArray + + // Steps 1-2. + var obj = this; + + // Steps 3-4, modified for the typed array case. + var len = TypedArrayLength(obj); + + assert(0 <= len && len <= 0x7FFFFFFF, + "assumed by some of the math below, see also the other assertions"); + + // Steps 5-7. + var relativeTarget = ToInteger(target); + + var to = relativeTarget < 0 ? std_Math_max(len + relativeTarget, 0) + : std_Math_min(relativeTarget, len); + + // Steps 8-10. + var relativeStart = ToInteger(start); + + var from = relativeStart < 0 ? std_Math_max(len + relativeStart, 0) + : std_Math_min(relativeStart, len); + + // Steps 11-13. + var relativeEnd = end === undefined ? len + : ToInteger(end); + + var final = relativeEnd < 0 ? std_Math_max(len + relativeEnd, 0) + : std_Math_min(relativeEnd, len); + + // Step 14. + var count = std_Math_min(final - from, len - to); + + assert(0 <= to && to <= 0x7FFFFFFF, + "typed array |to| index assumed int32_t"); + assert(0 <= from && from <= 0x7FFFFFFF, + "typed array |from| index assumed int32_t"); + + // Negative counts are possible for cases like tarray.copyWithin(0, 3, 0) + // where |count === final - from|. As |to| is within the [0, len] range, + // only |final - from| may underflow; with |final| in the range [0, len] + // and |from| in the range [0, len] the overall subtraction range is + // [-len, len] for |count| -- and with |len| bounded by implementation + // limits to 2**31 - 1, there can be no exceeding int32_t. + assert(-0x7FFFFFFF - 1 <= count && count <= 0x7FFFFFFF, + "typed array element count assumed int32_t"); + + // Steps 15-17. + // + // Note that getting or setting a typed array element must throw if the + // typed array is neutered, so the intrinsic below checks for neutering. + // This happens *only* if a get/set occurs, i.e. when |count > 0|. + // + // Also note that this copies elements effectively by memmove, *not* in + // step 17's specified order. This is unobservable, but it would be if we + // used this method to implement shared typed arrays' copyWithin. + if (count > 0) + MoveTypedArrayElements(obj, to | 0, from | 0, count | 0); + + // Step 18. + return obj; +} + // ES6 draft rev30 (2014/12/24) 22.2.3.6 %TypedArray%.prototype.entries() function TypedArrayEntries() { // Step 1. @@ -578,6 +651,142 @@ function TypedArrayReverse() { return O; } +function ViewedArrayBufferIfReified(tarray) { + assert(IsTypedArray(tarray), "non-typed array asked for its buffer"); + + var buf = UnsafeGetReservedSlot(tarray, JS_TYPEDARRAYLAYOUT_BUFFER_SLOT); + assert(buf === null || (IsObject(buf) && IsArrayBuffer(buf)), + "unexpected value in buffer slot"); + return buf; +} + +function IsDetachedBuffer(buffer) { + // Typed arrays whose buffers are null use inline storage and can't have + // been neutered. + if (buffer === null) + return false; + + assert(IsArrayBuffer(buffer), + "non-ArrayBuffer passed to IsDetachedBuffer"); + + var flags = UnsafeGetInt32FromReservedSlot(buffer, JS_ARRAYBUFFER_FLAGS_SLOT); + return (flags & JS_ARRAYBUFFER_NEUTERED_FLAG) !== 0; +} + +// ES6 draft 20150220 22.2.3.22.1 %TypedArray%.prototype.set(array [, offset]) +function SetFromNonTypedArray(target, array, targetOffset, targetLength, targetBuffer) { + assert(!IsPossiblyWrappedTypedArray(array), + "typed arrays must be passed to SetFromTypedArray"); + + // Steps 1-11 provided by caller. + + // Steps 16-17. + var src = ToObject(array); + + // Steps 18-19. + var srcLength = ToLength(src.length); + + // Step 20. + var limitOffset = targetOffset + srcLength; + if (limitOffset > targetLength) + ThrowRangeError(JSMSG_BAD_INDEX); + + // Step 22. + var k = 0; + + // Steps 12-15, 21, 23-24. + while (targetOffset < limitOffset) { + // Steps 24a-c. + var kNumber = ToNumber(src[k]); + + // Step 24d. This explicit check will be unnecessary when we implement + // throw-on-getting/setting-element-in-detached-buffer semantics. + if (targetBuffer === null) { + // A typed array previously using inline storage may acquire a + // buffer, so we must check with the source. + targetBuffer = ViewedArrayBufferIfReified(target); + } + if (IsDetachedBuffer(targetBuffer)) + ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED); + + // Step 24e. + target[targetOffset] = kNumber; + + // Steps 24f-g. + k++; + targetOffset++; + } + + // Step 25. + return undefined; +} + +// ES6 draft 20150220 22.2.3.22.2 %TypedArray%.prototype.set(typedArray [, offset]) +function SetFromTypedArray(target, typedArray, targetOffset, targetLength) { + assert(IsPossiblyWrappedTypedArray(typedArray), + "only typed arrays may be passed to this method"); + + // Steps 1-11 provided by caller. + + // Steps 12-24. + var res = SetFromTypedArrayApproach(target, typedArray, targetOffset, + targetLength | 0); + assert(res === JS_SETTYPEDARRAY_SAME_TYPE || + res === JS_SETTYPEDARRAY_OVERLAPPING || + res === JS_SETTYPEDARRAY_DISJOINT, + "intrinsic didn't return one of its enumerated return values"); + + // If the elements had the same type, then SetFromTypedArrayApproach also + // performed step 29. + if (res == JS_SETTYPEDARRAY_SAME_TYPE) + return undefined; // Step 25: done. + + // Otherwise, all checks and side effects except the actual element-writing + // happened. Either we're assigning from one range to a non-overlapping + // second range, or we're not. + + if (res === JS_SETTYPEDARRAY_DISJOINT) { + SetDisjointTypedElements(target, targetOffset | 0, typedArray); + return undefined; // Step 25: done. + } + + // Now the hard case: overlapping memory ranges. Delegate to yet another + // intrinsic. + SetOverlappingTypedElements(target, targetOffset | 0, typedArray); + + // Step 25. + return undefined; +} + +// ES6 draft 20150304 %TypedArray%.prototype.set +function TypedArraySet(overloaded, offset) { + // Steps 2-5, either algorithm. + var target = this; + if (!IsObject(target) || !IsTypedArray(target)) { + return callFunction(CallTypedArrayMethodIfWrapped, + target, overloaded, offset, "TypedArraySet"); + } + + // Steps 6-8, either algorithm. + var targetOffset = ToInteger(offset); + if (targetOffset < 0) + ThrowRangeError(JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "2"); + + // Steps 9-10. + var targetBuffer = ViewedArrayBufferIfReified(target); + if (IsDetachedBuffer(targetBuffer)) + ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED); + + // Step 11. + var targetLength = TypedArrayLength(target); + + // Steps 12 et seq. + if (IsPossiblyWrappedTypedArray(overloaded)) + return SetFromTypedArray(target, overloaded, targetOffset, targetLength); + + return SetFromNonTypedArray(target, overloaded, targetOffset, targetLength, targetBuffer); +} + // ES6 draft rev32 (2015-02-02) 22.2.3.23 %TypedArray%.prototype.slice(start, end). function TypedArraySlice(start, end) { @@ -678,6 +887,52 @@ function TypedArraySome(callbackfn, thisArg = undefined) { return false; } +// ES6 draft 20150304 %TypedArray%.prototype.subarray +function TypedArraySubarray(begin, end) { + // Step 1. + var obj = this; + + // Steps 2-3. + // This function is not generic. + if (!IsObject(obj) || !IsTypedArray(obj)) { + return callFunction(CallTypedArrayMethodIfWrapped, this, begin, end, + "TypedArraySubarray"); + } + + // Steps 4-6. + var buffer = TypedArrayBuffer(obj); + var srcLength = TypedArrayLength(obj); + + // Steps 7-9. + var relativeBegin = ToInteger(begin); + var beginIndex = relativeBegin < 0 ? std_Math_max(srcLength + relativeBegin, 0) + : std_Math_min(relativeBegin, srcLength); + + // Steps 10-12. + var relativeEnd = end === undefined ? srcLength : ToInteger(end); + var endIndex = relativeEnd < 0 ? std_Math_max(srcLength + relativeEnd, 0) + : std_Math_min(relativeEnd, srcLength); + + // Step 13. + var newLength = std_Math_max(endIndex - beginIndex, 0); + + // Steps 14-15, altered to use a shift instead of a size for performance. + var elementShift = TypedArrayElementShift(obj); + + // Step 16. + var srcByteOffset = TypedArrayByteOffset(obj); + + // Step 17. + var beginByteOffset = srcByteOffset + (beginIndex << elementShift); + + // Steps 18-20. + var defaultConstructor = _ConstructorForTypedArray(obj); + var constructor = SpeciesConstructor(obj, defaultConstructor); + + // Steps 21-22. + return new constructor(buffer, beginByteOffset, newLength); +} + // ES6 draft rev30 (2014/12/24) 22.2.3.30 %TypedArray%.prototype.values() function TypedArrayValues() { // Step 1. diff --git a/js/src/builtin/TypedObjectConstants.h b/js/src/builtin/TypedObjectConstants.h index bd1af64810..6089c89808 100644 --- a/js/src/builtin/TypedObjectConstants.h +++ b/js/src/builtin/TypedObjectConstants.h @@ -9,6 +9,27 @@ #ifndef builtin_TypedObjectConstants_h #define builtin_TypedObjectConstants_h +/////////////////////////////////////////////////////////////////////////// +// Values to be returned by SetFromTypedArrayApproach + +#define JS_SETTYPEDARRAY_SAME_TYPE 0 +#define JS_SETTYPEDARRAY_OVERLAPPING 1 +#define JS_SETTYPEDARRAY_DISJOINT 2 + +/////////////////////////////////////////////////////////////////////////// +// Slots for objects using the typed array layout + +#define JS_TYPEDARRAYLAYOUT_BUFFER_SLOT 0 +#define JS_TYPEDARRAYLAYOUT_LENGTH_SLOT 1 +#define JS_TYPEDARRAYLAYOUT_BYTEOFFSET_SLOT 2 + +/////////////////////////////////////////////////////////////////////////// +// Slots and flags for ArrayBuffer objects + +#define JS_ARRAYBUFFER_FLAGS_SLOT 3 + +#define JS_ARRAYBUFFER_NEUTERED_FLAG 0x4 + /////////////////////////////////////////////////////////////////////////// // Slots for typed prototypes diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 9a183aba6f..aeeca4d08c 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2002,32 +2002,28 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case PN_UNARY: switch (pn->getKind()) { - case PNK_DELETE: - { - ParseNode* pn2 = pn->pn_kid; - switch (pn2->getKind()) { - case PNK_NAME: - if (!bindNameToSlot(pn2)) - return false; - if (pn2->isConst()) { - MOZ_ASSERT(*answer == false); - return true; - } - /* FALL THROUGH */ - case PNK_DOT: - case PNK_CALL: - case PNK_ELEM: - case PNK_SUPERELEM: - /* All these delete addressing modes have effects too. */ - *answer = true; - return true; - default: - return checkSideEffects(pn2, answer); - } - MOZ_CRASH("We have a returning default case"); + case PNK_DELETENAME: { + ParseNode* nameExpr = pn->pn_kid; + MOZ_ASSERT(nameExpr->isKind(PNK_NAME)); + if (!bindNameToSlot(nameExpr)) + return false; + *answer = !nameExpr->isConst(); + return true; } - case PNK_TYPEOF: + case PNK_DELETEPROP: + case PNK_DELETESUPERPROP: + case PNK_DELETEELEM: + case PNK_DELETESUPERELEM: + // All these delete addressing modes have effects, too. + *answer = true; + return true; + + case PNK_DELETEEXPR: + return checkSideEffects(pn->pn_kid, answer); + + case PNK_TYPEOFNAME: + case PNK_TYPEOFEXPR: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: @@ -6127,82 +6123,110 @@ BytecodeEmitter::emitStatement(ParseNode* pn) } bool -BytecodeEmitter::emitDelete(ParseNode* pn) +BytecodeEmitter::emitDeleteName(ParseNode* node) { - /* - * Under ECMA 3, deleting a non-reference returns true -- but alas we - * must evaluate the operand if it appears it might have side effects. - */ - ParseNode* pn2 = pn->pn_kid; - switch (pn2->getKind()) { - case PNK_NAME: - if (!bindNameToSlot(pn2)) - return false; - if (!emitAtomOp(pn2, pn2->getOp())) - return false; - break; - case PNK_DOT: - { - JSOp delOp = sc->strict() ? JSOP_STRICTDELPROP : JSOP_DELPROP; - if (!emitPropOp(pn2, delOp)) - return false; - break; - } - case PNK_SUPERPROP: - // Still have to calculate the base, even though we are are going - // to throw unconditionally, as calculating the base could also - // throw. - if (!emit1(JSOP_SUPERBASE)) - return false; - if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER)) - return false; - break; - case PNK_ELEM: - { - JSOp delOp = sc->strict() ? JSOP_STRICTDELELEM : JSOP_DELELEM; - if (!emitElemOp(pn2, delOp)) - return false; - break; - } - case PNK_SUPERELEM: - // Still have to calculate everything, even though we're gonna throw - // since it may have side effects - if (!emitTree(pn2->pn_kid)) - return false; - if (!emit1(JSOP_SUPERBASE)) - return false; - if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER)) - return false; + MOZ_ASSERT(node->isKind(PNK_DELETENAME)); + MOZ_ASSERT(node->isArity(PN_UNARY)); - // Another wrinkle: Balance the stack from the emitter's point of view. - // Execution will not reach here, as the last bytecode threw. + ParseNode* nameExpr = node->pn_kid; + MOZ_ASSERT(nameExpr->isKind(PNK_NAME)); + + if (!bindNameToSlot(nameExpr)) + return false; + + MOZ_ASSERT(nameExpr->isOp(JSOP_DELNAME)); + return emitAtomOp(nameExpr, JSOP_DELNAME); +} + +bool +BytecodeEmitter::emitDeleteProperty(ParseNode* node) +{ + MOZ_ASSERT(node->isKind(PNK_DELETEPROP)); + MOZ_ASSERT(node->isArity(PN_UNARY)); + + ParseNode* propExpr = node->pn_kid; + MOZ_ASSERT(propExpr->isKind(PNK_DOT)); + + JSOp delOp = sc->strict() ? JSOP_STRICTDELPROP : JSOP_DELPROP; + return emitPropOp(propExpr, delOp); +} + +bool +BytecodeEmitter::emitDeleteSuperProperty(ParseNode* node) +{ + MOZ_ASSERT(node->isKind(PNK_DELETESUPERPROP)); + MOZ_ASSERT(node->isArity(PN_UNARY)); + MOZ_ASSERT(node->pn_kid->isKind(PNK_SUPERPROP)); + + // Still have to calculate the base, even though we are are going + // to throw unconditionally, as calculating the base could also + // throw. + if (!emit1(JSOP_SUPERBASE)) + return false; + + return emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER); +} + +bool +BytecodeEmitter::emitDeleteElement(ParseNode* node) +{ + MOZ_ASSERT(node->isKind(PNK_DELETEELEM)); + MOZ_ASSERT(node->isArity(PN_UNARY)); + + ParseNode* elemExpr = node->pn_kid; + MOZ_ASSERT(elemExpr->isKind(PNK_ELEM)); + + JSOp delOp = sc->strict() ? JSOP_STRICTDELELEM : JSOP_DELELEM; + return emitElemOp(elemExpr, delOp); +} + +bool +BytecodeEmitter::emitDeleteSuperElement(ParseNode* node) +{ + MOZ_ASSERT(node->isKind(PNK_DELETESUPERELEM)); + MOZ_ASSERT(node->isArity(PN_UNARY)); + + ParseNode* superElemExpr = node->pn_kid; + MOZ_ASSERT(superElemExpr->isKind(PNK_SUPERELEM)); + + // Still have to calculate everything, even though we're gonna throw + // since it may have side effects + MOZ_ASSERT(superElemExpr->isArity(PN_UNARY)); + if (!emitTree(superElemExpr->pn_kid)) + return false; + if (!emit1(JSOP_SUPERBASE)) + return false; + if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER)) + return false; + + // Another wrinkle: Balance the stack from the emitter's point of view. + // Execution will not reach here, as the last bytecode threw. + return emit1(JSOP_POP); +} + +bool +BytecodeEmitter::emitDeleteExpression(ParseNode* node) +{ + MOZ_ASSERT(node->isKind(PNK_DELETEEXPR)); + MOZ_ASSERT(node->isArity(PN_UNARY)); + + ParseNode* expression = node->pn_kid; + + // If useless, just emit JSOP_TRUE; otherwise convert |delete | to + // effectively |, true|. + bool useful = false; + if (!checkSideEffects(expression, &useful)) + return false; + + if (useful) { + MOZ_ASSERT_IF(expression->isKind(PNK_CALL), !(expression->pn_xflags & PNX_SETCALL)); + if (!emitTree(expression)) + return false; if (!emit1(JSOP_POP)) return false; - break; - default: - { - /* - * If useless, just emit JSOP_TRUE; otherwise convert delete foo() - * to foo(), true (a comma expression). - */ - bool useful = false; - if (!checkSideEffects(pn2, &useful)) - return false; - - if (useful) { - MOZ_ASSERT_IF(pn2->isKind(PNK_CALL), !(pn2->pn_xflags & PNX_SETCALL)); - if (!emitTree(pn2)) - return false; - if (!emit1(JSOP_POP)) - return false; - } - - if (!emit1(JSOP_TRUE)) - return false; - } } - return true; + return emit1(JSOP_TRUE); } bool @@ -6968,9 +6992,6 @@ BytecodeEmitter::emitUnary(ParseNode* pn) JSOp op = pn->getOp(); ParseNode* pn2 = pn->pn_kid; - if (op == JSOP_TYPEOF && !pn2->isKind(PNK_NAME)) - op = JSOP_TYPEOFEXPR; - bool oldEmittingForInit = emittingForInit; emittingForInit = false; if (!emitTree(pn2)) @@ -6980,6 +7001,23 @@ BytecodeEmitter::emitUnary(ParseNode* pn) return emit1(op); } +bool +BytecodeEmitter::emitTypeof(ParseNode* node, JSOp op) +{ + MOZ_ASSERT(op == JSOP_TYPEOF || op == JSOP_TYPEOFEXPR); + + if (!updateSourceCoordNotes(node->pn_pos.begin)) + return false; + + bool oldEmittingForInit = emittingForInit; + emittingForInit = false; + if (!emitTree(node->pn_kid)) + return false; + + emittingForInit = oldEmittingForInit; + return emit1(op); +} + bool BytecodeEmitter::emitDefaults(ParseNode* pn) { @@ -7452,8 +7490,15 @@ BytecodeEmitter::emitTree(ParseNode* pn) break; } + case PNK_TYPEOFNAME: + ok = emitTypeof(pn, JSOP_TYPEOF); + break; + + case PNK_TYPEOFEXPR: + ok = emitTypeof(pn, JSOP_TYPEOFEXPR); + break; + case PNK_THROW: - case PNK_TYPEOF: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: @@ -7469,8 +7514,28 @@ BytecodeEmitter::emitTree(ParseNode* pn) ok = emitIncOrDec(pn); break; - case PNK_DELETE: - ok = emitDelete(pn); + case PNK_DELETENAME: + ok = emitDeleteName(pn); + break; + + case PNK_DELETEPROP: + ok = emitDeleteProperty(pn); + break; + + case PNK_DELETESUPERPROP: + ok = emitDeleteSuperProperty(pn); + break; + + case PNK_DELETEELEM: + ok = emitDeleteElement(pn); + break; + + case PNK_DELETESUPERELEM: + ok = emitDeleteSuperElement(pn); + break; + + case PNK_DELETEEXPR: + ok = emitDeleteExpression(pn); break; case PNK_DOT: @@ -7631,6 +7696,11 @@ BytecodeEmitter::emitTree(ParseNode* pn) ok = emitClass(pn); break; + case PNK_NEWTARGET: + if (!emit1(JSOP_NEWTARGET)) + return false; + break; + default: MOZ_ASSERT(0); } diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 1e274ad33a..a406b53f48 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -545,7 +545,16 @@ struct BytecodeEmitter bool emitStatementList(ParseNode* pn, ptrdiff_t top); bool emitSyntheticStatements(ParseNode* pn, ptrdiff_t top); - bool emitDelete(ParseNode* pn); + bool emitDeleteName(ParseNode* pn); + bool emitDeleteProperty(ParseNode* pn); + bool emitDeleteSuperProperty(ParseNode* pn); + bool emitDeleteElement(ParseNode* pn); + bool emitDeleteSuperElement(ParseNode* pn); + bool emitDeleteExpression(ParseNode* pn); + + // |op| must be JSOP_TYPEOF or JSOP_TYPEOFEXPR. + bool emitTypeof(ParseNode* node, JSOp op); + bool emitLogical(ParseNode* pn); bool emitUnary(ParseNode* pn); diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index a26f8c6ed9..1708d8e3ac 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -128,6 +128,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) case PNK_IMPORT_SPEC_LIST: case PNK_IMPORT_SPEC: case PNK_EXPORT_FROM: + case PNK_EXPORT_DEFAULT: case PNK_EXPORT_SPEC_LIST: case PNK_EXPORT_SPEC: case PNK_EXPORT: @@ -336,11 +337,17 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) case PNK_COLON: case PNK_SHORTHAND: case PNK_CONDITIONAL: - case PNK_TYPEOF: + case PNK_TYPEOFNAME: + case PNK_TYPEOFEXPR: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: - case PNK_DELETE: + case PNK_DELETENAME: + case PNK_DELETEPROP: + case PNK_DELETESUPERPROP: + case PNK_DELETEELEM: + case PNK_DELETESUPERELEM: + case PNK_DELETEEXPR: case PNK_POS: case PNK_NEG: case PNK_PREINCREMENT: @@ -419,6 +426,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) case PNK_CLASSNAMES: case PNK_SUPERPROP: case PNK_SUPERELEM: + case PNK_NEWTARGET: MOZ_CRASH("ContainsHoistedDeclaration should have indicated false on " "some parent node without recurring to test this node"); @@ -721,23 +729,11 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, break; case PN_UNARY: - /* - * Kludge to deal with typeof expressions: because constant folding - * can turn an expression into a name node, we have to check here, - * before folding, to see if we should throw undefined name errors. - * - * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be - * null. This assumption does not hold true for other unary - * expressions. - */ - if (pn->isKind(PNK_TYPEOF) && !pn->pn_kid->isKind(PNK_NAME)) - pn->setOp(JSOP_TYPEOFEXPR); - if (pn->pn_kid) { SyntacticContext kidsc = pn->isKind(PNK_NOT) ? SyntacticContext::Condition - : pn->isKind(PNK_DELETE) + : IsDeleteKind(pn->getKind()) ? SyntacticContext::Delete : SyntacticContext::Other; if (!Fold(cx, &pn->pn_kid, handler, options, inGenexpLambda, kidsc)) @@ -767,7 +763,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, break; } - // The immediate child of a PNK_DELETE node should not be replaced + // The immediate child of a PNK_DELETE* node should not be replaced // with node indicating a different syntactic form; |delete x| is not // the same as |delete (true && x)|. See bug 888002. // @@ -1055,7 +1051,8 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, } break; - case PNK_TYPEOF: + case PNK_TYPEOFNAME: + case PNK_TYPEOFEXPR: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: @@ -1159,7 +1156,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, // necessarily-weird structure (say, by nulling out |pn->pn_left| // only) that would fail AST sanity assertions performed by // |handler.freeTree(pn)|. - pn->setKind(PNK_TYPEOF); + pn->setKind(PNK_TYPEOFEXPR); pn->setArity(PN_UNARY); pn->pn_kid = pn2; handler.freeTree(pn); diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 4047a4100c..9dcf62fa9d 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -69,6 +69,21 @@ class FullParseHandler typedef ParseNode* Node; typedef Definition* DefinitionNode; + bool isPropertyAccess(ParseNode* node) { + if (node->isKind(PNK_DOT) || node->isKind(PNK_ELEM)) + return true; + return node->isKind(PNK_SUPERPROP) || node->isKind(PNK_SUPERELEM); + } + + bool isFunctionCall(ParseNode* node) { + // Note: super() is a special form, *not* a function call. + return node->isKind(PNK_CALL); + } + + bool isDestructuringTarget(ParseNode* node) { + return node->isKind(PNK_OBJECT) || node->isKind(PNK_ARRAY); + } + FullParseHandler(ExclusiveContext* cx, LifoAlloc& alloc, TokenStream& tokenStream, Parser* syntaxParser, LazyScript* lazyOuterFunction) @@ -85,7 +100,9 @@ class FullParseHandler void prepareNodeForMutation(ParseNode* pn) { return allocator.prepareNodeForMutation(pn); } const Token& currentToken() { return tokenStream.currentToken(); } - ParseNode* newName(PropertyName* name, uint32_t blockid, const TokenPos& pos) { + ParseNode* newName(PropertyName* name, uint32_t blockid, const TokenPos& pos, + ExclusiveContext* cx) + { return new_(PNK_NAME, JSOP_GETNAME, name, blockid, pos); } @@ -184,11 +201,29 @@ class FullParseHandler } ParseNode* newDelete(uint32_t begin, ParseNode* expr) { - if (expr->getKind() == PNK_NAME) { + if (expr->isKind(PNK_NAME)) { expr->pn_dflags |= PND_DEOPTIMIZED; expr->setOp(JSOP_DELNAME); + return newUnary(PNK_DELETENAME, JSOP_NOP, begin, expr); } - return newUnary(PNK_DELETE, JSOP_NOP, begin, expr); + + if (expr->isKind(PNK_DOT)) + return newUnary(PNK_DELETEPROP, JSOP_NOP, begin, expr); + if (expr->isKind(PNK_SUPERPROP)) + return newUnary(PNK_DELETESUPERPROP, JSOP_NOP, begin, expr); + + if (expr->isKind(PNK_ELEM)) + return newUnary(PNK_DELETEELEM, JSOP_NOP, begin, expr); + if (expr->isKind(PNK_SUPERELEM)) + return newUnary(PNK_DELETESUPERELEM, JSOP_NOP, begin, expr); + + return newUnary(PNK_DELETEEXPR, JSOP_NOP, begin, expr); + } + + ParseNode* newTypeof(uint32_t begin, ParseNode* kid) { + TokenPos pos(begin, kid->pn_pos.end); + ParseNodeKind kind = kid->isKind(PNK_NAME) ? PNK_TYPEOFNAME : PNK_TYPEOFEXPR; + return new_(kind, JSOP_NOP, pos, kid); } ParseNode* newNullary(ParseNodeKind kind, JSOp op, const TokenPos& pos) { @@ -273,6 +308,14 @@ class FullParseHandler literal->append(element); } + ParseNode* newCall() { + return newList(PNK_CALL, JSOP_CALL); + } + + ParseNode* newTaggedTemplate() { + return newList(PNK_TAGGED_TEMPLATE, JSOP_CALL); + } + ParseNode* newObjectLiteral(uint32_t begin) { ParseNode* literal = new_(PNK_OBJECT, TokenPos(begin, begin + 1)); // Later in this stack: remove dependency on this opcode. @@ -296,6 +339,9 @@ class FullParseHandler ParseNode* newSuperElement(ParseNode* expr, const TokenPos& pos) { return new_(expr, pos); } + ParseNode* newNewTarget(const TokenPos& pos) { + return new_(PNK_NEWTARGET, pos); + } bool addPrototypeMutation(ParseNode* literal, uint32_t begin, ParseNode* expr) { // Object literals with mutated [[Prototype]] are non-constant so that @@ -461,6 +507,10 @@ class FullParseHandler return pn; } + ParseNode* newExportDefaultDeclaration(ParseNode* kid, const TokenPos& pos) { + return new_(PNK_EXPORT_DEFAULT, JSOP_NOP, pos, kid); + } + ParseNode* newExprStatement(ParseNode* expr, uint32_t end) { MOZ_ASSERT(expr->pn_pos.end <= end); return new_(PNK_SEMI, JSOP_NOP, TokenPos(expr->pn_pos.begin, end), expr); @@ -663,6 +713,9 @@ class FullParseHandler MOZ_ASSERT(kind != PNK_VAR); return new_(kind, op, pos()); } + ParseNode* newList(ParseNodeKind kind, uint32_t begin, JSOp op = JSOP_NOP) { + return new_(kind, op, TokenPos(begin, begin + 1)); + } ParseNode* newDeclarationList(ParseNodeKind kind, JSOp op = JSOP_NOP) { MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET || kind == PNK_GLOBALCONST); @@ -719,13 +772,13 @@ class FullParseHandler bool isConstant(ParseNode* pn) { return pn->isConstant(); } - PropertyName* isName(ParseNode* pn) { + PropertyName* maybeName(ParseNode* pn) { return pn->isKind(PNK_NAME) ? pn->pn_atom->asPropertyName() : nullptr; } bool isCall(ParseNode* pn) { return pn->isKind(PNK_CALL); } - PropertyName* isGetProp(ParseNode* pn) { + PropertyName* maybeDottedProperty(ParseNode* pn) { return pn->is() ? &pn->as().name() : nullptr; } JSAtom* isStringExprStatement(ParseNode* pn, TokenPos* pos) { @@ -736,6 +789,15 @@ class FullParseHandler return nullptr; } + void markAsAssigned(ParseNode* node) { node->markAsAssigned(); } + void adjustGetToSet(ParseNode* node) { + node->setOp(node->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME); + } + void maybeDespecializeSet(ParseNode* node) { + if (!(js_CodeSpec[node->getOp()].format & JOF_SET)) + node->setOp(JSOP_SETNAME); + } + inline ParseNode* makeAssignment(ParseNode* pn, ParseNode* rhs); static Definition* getDefinitionNode(Definition* dn) { diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index 4aaa106e9d..45dbee7249 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -374,16 +374,28 @@ class NameResolver case PNK_FRESHENBLOCK: case PNK_SUPERPROP: case PNK_OBJECT_PROPERTY_NAME: + case PNK_NEWTARGET: MOZ_ASSERT(cur->isArity(PN_NULLARY)); break; + case PNK_TYPEOFNAME: + MOZ_ASSERT(cur->isArity(PN_UNARY)); + MOZ_ASSERT(cur->pn_kid->isKind(PNK_NAME)); + MOZ_ASSERT(!cur->pn_kid->maybeExpr()); + break; + // Nodes with a single non-null child requiring name resolution. - case PNK_TYPEOF: + case PNK_TYPEOFEXPR: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: case PNK_THROW: - case PNK_DELETE: + case PNK_DELETENAME: + case PNK_DELETEPROP: + case PNK_DELETESUPERPROP: + case PNK_DELETEELEM: + case PNK_DELETESUPERELEM: + case PNK_DELETEEXPR: case PNK_NEG: case PNK_POS: case PNK_PREINCREMENT: @@ -396,6 +408,7 @@ class NameResolver case PNK_MUTATEPROTO: case PNK_SUPERELEM: case PNK_EXPORT: + case PNK_EXPORT_DEFAULT: MOZ_ASSERT(cur->isArity(PN_UNARY)); if (!resolve(cur->pn_kid, prefix)) return false; diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 3a2532df61..21d889b6d1 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -215,18 +215,25 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) case PNK_OBJECT_PROPERTY_NAME: case PNK_FRESHENBLOCK: case PNK_SUPERPROP: + case PNK_NEWTARGET: MOZ_ASSERT(pn->isArity(PN_NULLARY)); MOZ_ASSERT(!pn->isUsed(), "handle non-trivial cases separately"); MOZ_ASSERT(!pn->isDefn(), "handle non-trivial cases separately"); return PushResult::Recyclable; // Nodes with a single non-null child. - case PNK_TYPEOF: + case PNK_TYPEOFNAME: + case PNK_TYPEOFEXPR: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: case PNK_THROW: - case PNK_DELETE: + case PNK_DELETENAME: + case PNK_DELETEPROP: + case PNK_DELETESUPERPROP: + case PNK_DELETEELEM: + case PNK_DELETESUPERELEM: + case PNK_DELETEEXPR: case PNK_POS: case PNK_NEG: case PNK_PREINCREMENT: @@ -238,6 +245,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) case PNK_SPREAD: case PNK_MUTATEPROTO: case PNK_EXPORT: + case PNK_EXPORT_DEFAULT: case PNK_SUPERELEM: return PushUnaryNodeChild(pn, stack); diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 515beae34d..7425bdab7c 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -118,7 +118,13 @@ class UpvarCookie F(WITH) \ F(RETURN) \ F(NEW) \ - F(DELETE) \ + /* Delete operations. These must be sequential. */ \ + F(DELETENAME) \ + F(DELETEPROP) \ + F(DELETESUPERPROP) \ + F(DELETEELEM) \ + F(DELETESUPERELEM) \ + F(DELETEEXPR) \ F(TRY) \ F(CATCH) \ F(CATCHLIST) \ @@ -138,6 +144,7 @@ class UpvarCookie F(IMPORT_SPEC) \ F(EXPORT) \ F(EXPORT_FROM) \ + F(EXPORT_DEFAULT) \ F(EXPORT_SPEC_LIST) \ F(EXPORT_SPEC) \ F(EXPORT_BATCH_SPEC) \ @@ -155,9 +162,11 @@ class UpvarCookie F(CLASSNAMES) \ F(SUPERPROP) \ F(SUPERELEM) \ + F(NEWTARGET) \ \ /* Unary operators. */ \ - F(TYPEOF) \ + F(TYPEOFNAME) \ + F(TYPEOFEXPR) \ F(VOID) \ F(NOT) \ F(BITNOT) \ @@ -229,6 +238,12 @@ enum ParseNodeKind PNK_ASSIGNMENT_LAST = PNK_POWASSIGN }; +inline bool +IsDeleteKind(ParseNodeKind kind) +{ + return PNK_DELETENAME <= kind && kind <= PNK_DELETEEXPR; +} + /* * Label Variant Members * ----- ------- ------- @@ -328,6 +343,12 @@ enum ParseNodeKind * in original source, not introduced via * constant folding or other tree rewriting * PNK_LABEL name pn_atom: label, pn_expr: labeled statement + * PNK_IMPORT binary pn_left: PNK_IMPORT_SPEC_LIST import specifiers + * pn_right: PNK_STRING module specifier + * PNK_EXPORT unary pn_kid: declaration expression + * PNK_EXPORT_FROM binary pn_left: PNK_EXPORT_SPEC_LIST export specifiers + * pn_right: PNK_STRING module specifier + * PNK_EXPORT_DEFAULT unary pn_kid: export default declaration or expression * * * All left-associated binary trees of the same type are optimized into lists @@ -378,10 +399,11 @@ enum ParseNodeKind * PNK_MOD * PNK_POS, unary pn_kid: UNARY expr * PNK_NEG - * PNK_TYPEOF, unary pn_kid: UNARY expr - * PNK_VOID, + * PNK_VOID, unary pn_kid: UNARY expr * PNK_NOT, * PNK_BITNOT + * PNK_TYPEOFNAME, unary pn_kid: UNARY expr + * PNK_TYPEOFEXPR * PNK_PREINCREMENT, unary pn_kid: MEMBER expr * PNK_POSTINCREMENT, * PNK_PREDECREMENT, @@ -389,7 +411,16 @@ enum ParseNodeKind * PNK_NEW list pn_head: list of ctor, arg1, arg2, ... argN * pn_count: 1 + N (where N is number of args) * ctor is a MEMBER expr - * PNK_DELETE unary pn_kid: MEMBER expr + * PNK_DELETENAME unary pn_kid: PNK_NAME expr + * PNK_DELETEPROP unary pn_kid: PNK_DOT expr + * PNK_DELETESUPERPROP unary pn_kid: PNK_SUPERPROP expr + * PNK_DELETEELEM unary pn_kid: PNK_ELEM expr + * PNK_DELETESUPERELEM unary pn_kid: PNK_SUPERELEM expr + * PNK_DELETEEXPR unary pn_kid: MEMBER expr that's evaluated, then the + * overall delete evaluates to true; can't be a kind + * for a more-specific PNK_DELETE* unless constant + * folding (or a similar parse tree manipulation) has + * occurred * PNK_DOT name pn_expr: MEMBER expr to left of . * pn_atom: name to right of . * PNK_ELEM binary pn_left: MEMBER expr to left of [ diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 6424984520..c8e6bf11ca 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -744,32 +744,6 @@ Parser::reportBadReturn(Node pn, ParseReportKind kind, return report(kind, pc->sc->strict(), pn, errnum, name.ptr()); } -/* - * Check that assigning to lhs is permitted. Assigning to 'eval' or - * 'arguments' is banned in strict mode. - */ -template -bool -Parser::checkStrictAssignment(Node lhs) -{ - if (!pc->sc->needStrictChecks()) - return true; - - JSAtom* atom = handler.isName(lhs); - if (!atom) - return true; - - if (atom == context->names().eval || atom == context->names().arguments) { - JSAutoByteString name; - if (!AtomToPrintableString(context, atom, &name)) - return false; - - if (!report(ParseStrictError, pc->sc->strict(), lhs, JSMSG_BAD_STRICT_ASSIGN, name.ptr())) - return false; - } - return true; -} - /* * Check that it is permitted to introduce a binding for atom. Strict mode * forbids introducing new definitions for 'eval', 'arguments', or for any @@ -1183,7 +1157,8 @@ struct BindData template JSFunction* -Parser::newFunction(HandleAtom atom, FunctionSyntaxKind kind, HandleObject proto) +Parser::newFunction(HandleAtom atom, FunctionSyntaxKind kind, + GeneratorKind generatorKind, HandleObject proto) { MOZ_ASSERT_IF(kind == Statement, atom != nullptr); @@ -1200,7 +1175,11 @@ Parser::newFunction(HandleAtom atom, FunctionSyntaxKind kind, Hand allocKind = gc::AllocKind::FUNCTION_EXTENDED; break; case Method: - flags = JSFunction::INTERPRETED_METHOD; + MOZ_ASSERT(generatorKind == NotGenerator || generatorKind == StarGenerator); + if (generatorKind == NotGenerator) + flags = JSFunction::INTERPRETED_METHOD; + else + flags = JSFunction::INTERPRETED_METHOD_GENERATOR; allocKind = gc::AllocKind::FUNCTION_EXTENDED; break; case ClassConstructor: @@ -2167,7 +2146,7 @@ Parser::functionDef(InHandling inHandling, YieldHandling yieldHand if (!proto) return null(); } - RootedFunction fun(context, newFunction(funName, kind, proto)); + RootedFunction fun(context, newFunction(funName, kind, generatorKind, proto)); if (!fun) return null(); @@ -2610,7 +2589,7 @@ Parser::checkYieldNameValidity() template typename ParseHandler::Node -Parser::functionStmt(YieldHandling yieldHandling) +Parser::functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); @@ -2632,6 +2611,9 @@ Parser::functionStmt(YieldHandling yieldHandling) if (!checkYieldNameValidity()) return null(); name = tokenStream.currentName(); + } else if (defaultHandling == AllowDefaultName) { + name = context->names().starDefaultStar; + tokenStream.ungetToken(); } else { /* Unnamed function expressions are forbidden in statement context. */ report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT); @@ -3296,20 +3278,20 @@ Parser::bindVarOrGlobalConst(BindData* data, return true; } -template <> +template bool -Parser::makeSetCall(ParseNode* pn, unsigned msg) +Parser::makeSetCall(Node target, unsigned msg) { - MOZ_ASSERT(pn->isKind(PNK_CALL)); - MOZ_ASSERT(pn->isArity(PN_LIST)); - MOZ_ASSERT(pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL) || - pn->isOp(JSOP_EVAL) || pn->isOp(JSOP_STRICTEVAL) || - pn->isOp(JSOP_SPREADEVAL) || pn->isOp(JSOP_STRICTSPREADEVAL) || - pn->isOp(JSOP_FUNCALL) || pn->isOp(JSOP_FUNAPPLY)); + MOZ_ASSERT(handler.isFunctionCall(target)); - if (!report(ParseStrictError, pc->sc->strict(), pn, msg)) + // Assignment to function calls is forbidden in ES6. We're still somewhat + // concerned about sites using this in dead code, so forbid it only in + // strict mode code (or if the werror option has been set), and otherwise + // warn. + if (!report(ParseStrictError, pc->sc->strict(), target, msg)) return false; - handler.markAsSetCall(pn); + + handler.markAsSetCall(target); return true; } @@ -3720,8 +3702,9 @@ Parser::newBindingNode(PropertyName* name, bool functionScope, Var template typename ParseHandler::Node Parser::variables(YieldHandling yieldHandling, - ParseNodeKind kind, bool* psimple, - StaticBlockObject* blockObj, VarContext varContext) + ParseNodeKind kind, + ForInitLocation location, + bool* psimple, StaticBlockObject* blockObj, VarContext varContext) { /* * The four options here are: @@ -3783,7 +3766,7 @@ Parser::variables(YieldHandling yieldHandling, return null(); bool parsingForInOrOfInit = false; - if (pc->parsingForInit) { + if (location == InForInit) { bool isForIn, isForOf; if (!matchInOrOf(&isForIn, &isForOf)) return null(); @@ -3868,7 +3851,7 @@ Parser::variables(YieldHandling yieldHandling, if (!handler.finishInitializerAssignment(pn2, init, data.op)) return null(); } else { - if (data.isConst && !pc->parsingForInit) { + if (data.isConst && location == NotInForInit) { report(ParseError, false, null(), JSMSG_BAD_CONST_DECL); return null(); } @@ -4047,9 +4030,8 @@ Parser::lexicalDeclaration(YieldHandling yieldHandling, bool i else if (isConst) kind = PNK_CONST; - ParseNode* pn = variables(yieldHandling, kind, nullptr, - CurrentLexicalStaticBlock(pc), - HoistVars); + ParseNode* pn = variables(yieldHandling, kind, NotInForInit, + nullptr, CurrentLexicalStaticBlock(pc), HoistVars); if (!pn) return null(); pn->pn_xflags = PNX_POPVAR; @@ -4064,6 +4046,105 @@ Parser::lexicalDeclaration(YieldHandling, bool) return SyntaxParseHandler::NodeFailure; } +template<> +bool +Parser::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet) +{ + if (tt == TOK_LC) { + while (true) { + // Handle the forms |import {} from 'a'| and + // |import { ..., } from 'a'| (where ... is non empty), by + // escaping the loop early if the next token is }. + if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName)) + return false; + + if (tt == TOK_RC) + break; + + // If the next token is a keyword, the previous call to + // peekToken matched it as a TOK_NAME, and put it in the + // lookahead buffer, so this call will match keywords as well. + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME); + Node importName = newName(tokenStream.currentName()); + if (!importName) + return false; + + if (!tokenStream.getToken(&tt)) + return false; + + if (tt == TOK_NAME && tokenStream.currentName() == context->names().as) { + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME); + } else { + // Keywords cannot be bound to themselves, so an import name + // that is a keyword is a syntax error if it is not followed + // by the keyword 'as'. + // See the ImportSpecifier production in ES6 section 15.2.2. + if (IsKeyword(importName->name())) { + JSAutoByteString bytes; + if (!AtomToPrintableString(context, importName->name(), &bytes)) + return false; + report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr()); + return false; + } + tokenStream.ungetToken(); + } + Node bindingName = newName(tokenStream.currentName()); + if (!bindingName) + return false; + + Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName); + if (!importSpec) + return false; + + handler.addList(importSpecSet, importSpec); + + bool matched; + if (!tokenStream.matchToken(&matched, TOK_COMMA)) + return false; + + if (!matched) + break; + } + + MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_IMPORT_SPEC_LIST); + } else { + MOZ_ASSERT(tt == TOK_MUL); + if (!tokenStream.getToken(&tt)) + return false; + + if (tt != TOK_NAME || tokenStream.currentName() != context->names().as) { + report(ParseError, false, null(), JSMSG_AS_AFTER_IMPORT_STAR); + return false; + } + + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME); + + Node importName = newName(context->names().star); + if (!importName) + return null(); + + Node bindingName = newName(tokenStream.currentName()); + if (!bindingName) + return false; + + Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName); + if (!importSpec) + return false; + + handler.addList(importSpecSet, importSpec); + } + + return true; +} + +template<> +bool +Parser::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet) +{ + MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); + return false; +} + template typename ParseHandler::Node Parser::importDeclaration() @@ -4084,7 +4165,7 @@ Parser::importDeclaration() if (!importSpecSet) return null(); - if (tt == TOK_NAME || tt == TOK_LC) { + if (tt == TOK_NAME || tt == TOK_LC || tt == TOK_MUL) { if (tt == TOK_NAME) { // Handle the form |import a from 'b'|, by adding a single import // specifier to the list, with 'default' as the import name and @@ -4103,83 +4184,43 @@ Parser::importDeclaration() return null(); handler.addList(importSpecSet, importSpec); - } else { - while (true) { - // Handle the forms |import {} from 'a'| and - // |import { ..., } from 'a'| (where ... is non empty), by - // escaping the loop early if the next token is }. - if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName)) - return null(); - if (tt == TOK_RC) - break; - // If the next token is a keyword, the previous call to - // peekToken matched it as a TOK_NAME, and put it in the - // lookahead buffer, so this call will match keywords as well. - MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME); - Node importName = newName(tokenStream.currentName()); - if (!importName) + if (!tokenStream.peekToken(&tt)) + return null(); + + if (tt == TOK_COMMA) { + if (!tokenStream.getToken(&tt) || !tokenStream.getToken(&tt)) return null(); - if (!tokenStream.getToken(&tt)) + if (tt != TOK_LC && tt != TOK_MUL) { + report(ParseError, false, null(), JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT); return null(); - if (tt == TOK_NAME && tokenStream.currentName() == context->names().as) { - if (!tokenStream.getToken(&tt)) - return null(); - if (tt != TOK_NAME) { - report(ParseError, false, null(), JSMSG_NO_BINDING_NAME); - return null(); - } - } else { - // Keywords cannot be bound to themselves, so an import name - // that is a keyword is a syntax error if it is not followed - // by the keyword 'as'. - if (IsKeyword(importName->name())) { - JSAutoByteString bytes; - if (!AtomToPrintableString(context, importName->name(), &bytes)) - return null(); - report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr()); - return null(); - } - tokenStream.ungetToken(); } - Node bindingName = newName(tokenStream.currentName()); - if (!bindingName) - return null(); - Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName); - if (!importSpec) + if (!namedImportsOrNamespaceImport(tt, importSpecSet)) return null(); - - handler.addList(importSpecSet, importSpec); - - bool matched; - if (!tokenStream.matchToken(&matched, TOK_COMMA)) - return null(); - if (!matched) - break; } - - MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_IMPORT_SPEC_LIST); + } else { + if (!namedImportsOrNamespaceImport(tt, importSpecSet)) + return null(); } if (!tokenStream.getToken(&tt)) return null(); + if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) { - report(ParseError, false, null(), JSMSG_FROM_AFTER_IMPORT_SPEC_SET); + report(ParseError, false, null(), JSMSG_FROM_AFTER_IMPORT_CLAUSE); return null(); } MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); - } else { - if (tt != TOK_STRING) { - report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_IMPORT); - return null(); - } - + } else if (tt == TOK_STRING) { // Handle the form |import 'a'| by leaving the list empty. This is // equivalent to |import {} from 'a'|. importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin; + } else { + report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_IMPORT); + return null(); } Node moduleSpec = stringLiteral(); @@ -4189,8 +4230,7 @@ Parser::importDeclaration() if (!MatchOrInsertSemicolon(tokenStream)) return null(); - return handler.newImportDeclaration(importSpecSet, moduleSpec, - TokenPos(begin, pos().end)); + return handler.newImportDeclaration(importSpecSet, moduleSpec, TokenPos(begin, pos().end)); } template<> @@ -4201,9 +4241,15 @@ Parser::importDeclaration() return SyntaxParseHandler::NodeFailure; } -template -typename ParseHandler::Node -Parser::exportDeclaration() +template <> +ParseNode* +Parser::classDefinition(YieldHandling yieldHandling, + ClassContext classContext, + DefaultHandling defaultHandling); + +template<> +ParseNode* +Parser::exportDeclaration() { MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT); @@ -4218,6 +4264,7 @@ Parser::exportDeclaration() TokenKind tt; if (!tokenStream.getToken(&tt)) return null(); + bool isExportStar = tt == TOK_MUL; switch (tt) { case TOK_LC: case TOK_MUL: @@ -4274,7 +4321,7 @@ Parser::exportDeclaration() // Handle the form |export *| by adding a special export batch // specifier to the list. Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos()); - if (!kid) + if (!exportSpec) return null(); handler.addList(kid, exportSpec); @@ -4292,22 +4339,31 @@ Parser::exportDeclaration() return null(); return handler.newExportFromDeclaration(begin, kid, moduleSpec); + } else if (isExportStar) { + report(ParseError, false, null(), JSMSG_FROM_AFTER_EXPORT_STAR); + return null(); } else { tokenStream.ungetToken(); } - kid = MatchOrInsertSemicolon(tokenStream) ? kid : nullptr; - if (!kid) + if (!MatchOrInsertSemicolon(tokenStream)) return null(); break; case TOK_FUNCTION: - kid = functionStmt(YieldIsKeyword); + kid = functionStmt(YieldIsKeyword, NameRequired); if (!kid) return null(); break; - case TOK_VAR: kid = variables(YieldIsName, PNK_VAR); + case TOK_CLASS: + kid = classDefinition(YieldIsKeyword, ClassStatement, NameRequired); + if (!kid) + return null(); + break; + + case TOK_VAR: + kid = variables(YieldIsName, PNK_VAR, NotInForInit); if (!kid) return null(); kid->pn_xflags = PNX_POPVAR; @@ -4317,6 +4373,28 @@ Parser::exportDeclaration() return null(); break; + case TOK_DEFAULT: { + if (!tokenStream.getToken(&tt)) + return null(); + + switch (tt) { + case TOK_FUNCTION: + kid = functionStmt(YieldIsKeyword, AllowDefaultName); + break; + case TOK_CLASS: + kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName); + break; + default: + tokenStream.ungetToken(); + kid = assignExpr(InAllowed, YieldIsKeyword); + break; + } + if (!kid) + return null(); + + return handler.newExportDefaultDeclaration(kid, TokenPos(begin, pos().end)); + } + case TOK_NAME: // Handle the form |export a| in the same way as |export let a|, by // acting as if we've just seen the let keyword. Simply unget the token @@ -4548,24 +4626,19 @@ Parser::forStatement(YieldHandling yieldHandling) if (tt == TOK_SEMI) { pn1 = nullptr; } else { - /* - * Set pn1 to a var list or an initializing expression. - * - * Set the parsingForInit flag during parsing of the first clause - * of the for statement. This flag will be used by the RelExpr - * production; if it is set, then the 'in' keyword will not be - * recognized as an operator, leaving it available to be parsed as - * part of a for/in loop. - * - * A side effect of this restriction is that (unparenthesized) - * expressions involving an 'in' operator are illegal in the init - * clause of an ordinary for loop. - */ - pc->parsingForInit = true; + // Set pn1 to a variable list or an initializing expression. + // + // Pass |InForInit| to Parser::variables when parsing declarations + // to trigger |for|-specific parsing for that one position. In a + // normal variable declaration, any initializer may be an |in| + // expression. But for declarations at the start of a for-loop + // head, initializers can't contain |in|. (Such syntax conflicts + // with ES5's |for (var i = 0 in foo)| syntax, removed in ES6, that + // we "support" by ignoring the |= 0|.) if (tt == TOK_VAR) { isForDecl = true; tokenStream.consumeKnownToken(tt); - pn1 = variables(yieldHandling, PNK_VAR); + pn1 = variables(yieldHandling, PNK_VAR, InForInit); } else if (tt == TOK_LET || tt == TOK_CONST) { handler.disableSyntaxParser(); bool constDecl = tt == TOK_CONST; @@ -4574,13 +4647,15 @@ Parser::forStatement(YieldHandling yieldHandling) blockObj = StaticBlockObject::create(context); if (!blockObj) return null(); - pn1 = variables(yieldHandling, - constDecl ? PNK_CONST : PNK_LET, nullptr, blockObj, - DontHoistVars); + pn1 = variables(yieldHandling, constDecl ? PNK_CONST : PNK_LET, InForInit, + nullptr, blockObj, DontHoistVars); } else { + // Pass |InProhibited| when parsing an expression so that |in| + // isn't parsed in a RelationalExpression as a binary operator. + // In this context, |in| is part of a for-in loop -- *not* part + // of a binary expression. pn1 = expr(InProhibited, yieldHandling); } - pc->parsingForInit = false; if (!pn1) return null(); } @@ -4641,12 +4716,8 @@ Parser::forStatement(YieldHandling yieldHandling) // 'for (var v = expr1 in expr2) stmt'. ParseNode* hoistedVar = nullptr; - /* - * We can be sure that it's a for/in loop if there's still an 'in' - * keyword here, even if JavaScript recognizes 'in' as an operator, - * as we've excluded 'in' from being parsed in RelExpr by setting - * pc->parsingForInit. - */ + // If there's an |in| keyword here, it's a for-in loop, by dint of careful + // parsing of |pn1|. StmtInfoPC letStmt(context); /* used if blockObj != nullptr. */ ParseNode* pn2; /* forHead->pn_kid2 */ ParseNode* pn3; /* forHead->pn_kid3 */ @@ -4908,11 +4979,10 @@ Parser::forStatement(YieldHandling yieldHandling) lhsNode = null(); } else { /* Set lhsNode to a var list or an initializing expression. */ - pc->parsingForInit = true; if (tt == TOK_VAR) { isForDecl = true; tokenStream.consumeKnownToken(tt); - lhsNode = variables(yieldHandling, PNK_VAR, &simpleForDecl); + lhsNode = variables(yieldHandling, PNK_VAR, InForInit, &simpleForDecl); } else if (tt == TOK_CONST || tt == TOK_LET) { JS_ALWAYS_FALSE(abortIfSyntaxParser()); @@ -4923,16 +4993,11 @@ Parser::forStatement(YieldHandling yieldHandling) } if (!lhsNode) return null(); - pc->parsingForInit = false; } } - /* - * We can be sure that it's a for/in loop if there's still an 'in' - * keyword here, even if JavaScript recognizes 'in' as an operator, - * as we've excluded 'in' from being parsed in RelExpr by setting - * pc->parsingForInit. - */ + // If there's an |in| keyword here, it's a for-in loop, by dint of careful + // parsing of |pn1|. bool isForIn = false, isForOf = false; if (lhsNode) { if (!matchInOrOf(&isForIn, &isForOf)) @@ -4944,9 +5009,8 @@ Parser::forStatement(YieldHandling yieldHandling) /* Check that the left side of the 'in' or 'of' is valid. */ if (!isForDecl && - lhsNode != SyntaxParseHandler::NodeName && - lhsNode != SyntaxParseHandler::NodeGetProp && - lhsNode != SyntaxParseHandler::NodeLValue) + !handler.maybeName(lhsNode) && + !handler.isPropertyAccess(lhsNode)) { JS_ALWAYS_FALSE(abortIfSyntaxParser()); return null(); @@ -5733,7 +5797,8 @@ Parser::debuggerStatement() template <> ParseNode* Parser::classDefinition(YieldHandling yieldHandling, - ClassContext classContext) + ClassContext classContext, + DefaultHandling defaultHandling) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS)); @@ -5752,9 +5817,14 @@ Parser::classDefinition(YieldHandling yieldHandling, MOZ_ASSERT(yieldHandling != YieldIsKeyword); name = tokenStream.currentName(); } else if (classContext == ClassStatement) { - // Class statements must have a bound name - report(ParseError, false, null(), JSMSG_UNNAMED_CLASS_STMT); - return null(); + if (defaultHandling == AllowDefaultName) { + name = context->names().starDefaultStar; + tokenStream.ungetToken(); + } else { + // Class statements must have a bound name + report(ParseError, false, null(), JSMSG_UNNAMED_CLASS_STMT); + return null(); + } } else { // Make sure to put it back, whatever it was tokenStream.ungetToken(); @@ -5828,7 +5898,9 @@ Parser::classDefinition(YieldHandling yieldHandling, template <> SyntaxParseHandler::Node -Parser::classDefinition(YieldHandling yieldHandling, ClassContext classContext) +Parser::classDefinition(YieldHandling yieldHandling, + ClassContext classContext, + DefaultHandling defaultHandling) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return SyntaxParseHandler::NodeFailure; @@ -5853,7 +5925,7 @@ Parser::statement(YieldHandling yieldHandling, bool canHaveDirecti // VariableStatement[?Yield] case TOK_VAR: { - Node pn = variables(yieldHandling, PNK_VAR); + Node pn = variables(yieldHandling, PNK_VAR, NotInForInit); if (!pn) return null(); @@ -5973,13 +6045,13 @@ Parser::statement(YieldHandling yieldHandling, bool canHaveDirecti // HoistableDeclaration[?Yield] case TOK_FUNCTION: - return functionStmt(yieldHandling); + return functionStmt(yieldHandling, NameRequired); // ClassDeclaration[?Yield] case TOK_CLASS: if (!abortIfSyntaxParser()) return null(); - return classDefinition(yieldHandling, ClassStatement); + return classDefinition(yieldHandling, ClassStatement, NameRequired); // LexicalDeclaration[In, ?Yield] case TOK_LET: @@ -6154,9 +6226,6 @@ Parser::orExpr1(InHandling inHandling, YieldHandling yieldHandling ParseNodeKind kindStack[PRECEDENCE_CLASSES]; int depth = 0; - bool oldParsingForInit = pc->parsingForInit; - pc->parsingForInit = false; - Node pn; for (;;) { pn = unaryExpr(yieldHandling, invoked); @@ -6172,7 +6241,7 @@ Parser::orExpr1(InHandling inHandling, YieldHandling yieldHandling // FIXME: Change this to use |inHandling == InAllowed|, not // |pc->parsingForInit|. ParseNodeKind pnk; - if (IsBinaryOpToken(tok, oldParsingForInit)) { + if (tok == TOK_IN ? inHandling == InAllowed : TokenKindIsBinaryOp(tok)) { pnk = BinaryOpTokenKindToParseNodeKind(tok); } else { tok = TOK_EOF; @@ -6205,7 +6274,6 @@ Parser::orExpr1(InHandling inHandling, YieldHandling yieldHandling } MOZ_ASSERT(depth == 0); - pc->parsingForInit = oldParsingForInit; return pn; } @@ -6218,15 +6286,7 @@ Parser::condExpr1(InHandling inHandling, YieldHandling yieldHandli if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK)) return condition; - /* - * Always accept the 'in' operator in the middle clause of a ternary, - * where it's unambiguous, even if we might be parsing the init of a - * for statement. - */ - bool oldParsingForInit = pc->parsingForInit; - pc->parsingForInit = false; Node thenExpr = assignExpr(InAllowed, yieldHandling); - pc->parsingForInit = oldParsingForInit; if (!thenExpr) return null(); @@ -6243,74 +6303,56 @@ Parser::condExpr1(InHandling inHandling, YieldHandling yieldHandli return handler.newConditional(condition, thenExpr, elseExpr); } -template <> +template bool -Parser::checkAndMarkAsAssignmentLhs(ParseNode* pn, AssignmentFlavor flavor) +Parser::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor flavor) { - switch (pn->getKind()) { - case PNK_NAME: - if (!checkStrictAssignment(pn)) - return false; - if (flavor == KeyedDestructuringAssignment) { - /* - * We may be called on a name node that has already been - * specialized, in the very weird "for (var [x] = i in o) ..." - * case. See bug 558633. - */ - if (!(js_CodeSpec[pn->getOp()].format & JOF_SET)) - pn->setOp(JSOP_SETNAME); - } else { - pn->setOp(pn->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME); - } - pn->markAsAssigned(); - break; - - case PNK_DOT: - case PNK_ELEM: - case PNK_SUPERPROP: - case PNK_SUPERELEM: - break; - - case PNK_ARRAY: - case PNK_OBJECT: + // Handle destructuring object/array patterns specially. + if (handler.isDestructuringTarget(target)) { if (flavor == CompoundAssignment) { report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS); return false; } - if (!checkDestructuring(nullptr, pn)) - return false; - break; - case PNK_CALL: + return checkDestructuring(nullptr, target); + } + + // All other permitted targets are simple. + if (!reportIfNotValidSimpleAssignmentTarget(target, flavor)) + return false; + + if (handler.isPropertyAccess(target)) + return true; + + if (handler.maybeName(target)) { + // The arguments/eval identifiers are simple in non-strict mode code, + // but warn to discourage use nonetheless. + if (!reportIfArgumentsEvalTarget(target)) + return false; + if (flavor == KeyedDestructuringAssignment) { - report(ParseError, false, pn, JSMSG_BAD_DESTRUCT_TARGET); - return false; + // We may be called on a name node that has already been + // specialized, in the very weird "for (var [x] = i in o) ..." + // case. See bug 558633. + // + // XXX Is this necessary with the changes in bug 1164741? This is + // likely removable now. + handler.maybeDespecializeSet(target); + } else { + handler.adjustGetToSet(target); } - if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS)) - return false; - break; + handler.markAsAssigned(target); + return true; + } - default: - unsigned errnum = (flavor == KeyedDestructuringAssignment) ? JSMSG_BAD_DESTRUCT_TARGET : - JSMSG_BAD_LEFTSIDE_OF_ASS; - report(ParseError, false, pn, errnum); + MOZ_ASSERT(handler.isFunctionCall(target)); + + if (flavor == KeyedDestructuringAssignment) { + report(ParseError, false, target, JSMSG_BAD_DESTRUCT_TARGET); return false; } - return true; -} -template <> -bool -Parser::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor) -{ - /* Full syntax checking of valid assignment LHS terms requires a parse tree. */ - if (pn != SyntaxParseHandler::NodeName && - pn != SyntaxParseHandler::NodeGetProp && - pn != SyntaxParseHandler::NodeLValue) - { - return abortIfSyntaxParser(); - } - return checkStrictAssignment(pn); + return makeSetCall(target, JSMSG_BAD_LEFTSIDE_OF_ASS); } template @@ -6430,51 +6472,108 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl return handler.newAssignment(kind, lhs, rhs, pc, op); } -static const char incop_name_str[][10] = {"increment", "decrement"}; - -template <> +template bool -Parser::checkAndMarkAsIncOperand(ParseNode* kid, TokenKind tt, bool preorder) +Parser::isValidSimpleAssignmentTarget(Node node) { - // Check. - if (!kid->isKind(PNK_NAME) && - !kid->isKind(PNK_DOT) && - !kid->isKind(PNK_SUPERPROP) && - !kid->isKind(PNK_SUPERELEM) && - !kid->isKind(PNK_ELEM) && - !(kid->isKind(PNK_CALL) && - (kid->isOp(JSOP_CALL) || kid->isOp(JSOP_SPREADCALL) || - kid->isOp(JSOP_EVAL) || kid->isOp(JSOP_STRICTEVAL) || - kid->isOp(JSOP_SPREADEVAL) || kid->isOp(JSOP_STRICTSPREADEVAL) || - kid->isOp(JSOP_FUNCALL) || - kid->isOp(JSOP_FUNAPPLY)))) - { - report(ParseError, false, null(), JSMSG_BAD_OPERAND, incop_name_str[tt == TOK_DEC]); - return false; + if (PropertyName* name = handler.maybeName(node)) { + // Note that we implement *exactly* the ES6 semantics here. Warning + // for arguments/eval when extraWarnings is set isn't handled here. + if (!pc->sc->strict()) + return true; + + return name != context->names().arguments && name != context->names().eval; } - if (!checkStrictAssignment(kid)) + if (handler.isPropertyAccess(node)) + return true; + return handler.isFunctionCall(node); +} + +template +bool +Parser::reportIfArgumentsEvalTarget(Node target) +{ + PropertyName* name = handler.maybeName(target); + if (!name) + return true; + + const char* chars = (name == context->names().arguments) + ? js_arguments_str + : (name == context->names().eval) + ? js_eval_str + : nullptr; + if (!chars) + return true; + + if (!report(ParseStrictError, pc->sc->strict(), target, JSMSG_BAD_STRICT_ASSIGN, chars)) return false; - // Mark. - if (kid->isKind(PNK_NAME)) { - kid->markAsAssigned(); - } else if (kid->isKind(PNK_CALL)) { - if (!makeSetCall(kid, JSMSG_BAD_INCOP_OPERAND)) - return false; - } + MOZ_ASSERT(!pc->sc->strict(), "in strict mode an error should have been reported"); return true; } -template <> +template bool -Parser::checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder) +Parser::reportIfNotValidSimpleAssignmentTarget(Node target, + AssignmentFlavor flavor) { - // To the extent of what we support in syntax-parse mode, the rules for - // inc/dec operands are the same as for assignment. There are differences, - // such as destructuring; but if we hit any of those cases, we'll abort and - // reparse in full mode. - return checkAndMarkAsAssignmentLhs(kid, IncDecAssignment); + if (isValidSimpleAssignmentTarget(target)) + return true; + + // Use a special error if the target is arguments/eval, as a nicety. + if (!reportIfArgumentsEvalTarget(target)) + return false; + + unsigned errnum; + const char* extra = nullptr; + + switch (flavor) { + case IncrementAssignment: + errnum = JSMSG_BAD_OPERAND; + extra = "increment"; + break; + + case DecrementAssignment: + errnum = JSMSG_BAD_OPERAND; + extra = "decrement"; + break; + + case KeyedDestructuringAssignment: + errnum = JSMSG_BAD_DESTRUCT_TARGET; + break; + + case PlainAssignment: + case CompoundAssignment: + errnum = JSMSG_BAD_LEFTSIDE_OF_ASS; + break; + } + + report(ParseError, pc->sc->strict(), target, errnum, extra); + return false; +} + +template +bool +Parser::checkAndMarkAsIncOperand(Node target, AssignmentFlavor flavor) +{ + // Check. + if (!reportIfNotValidSimpleAssignmentTarget(target, flavor)) + return false; + + // Assignment to arguments/eval is allowed outside strict mode code, + // but it's dodgy. Report a strict warning (error, if werror was set). + if (!reportIfArgumentsEvalTarget(target)) + return false; + + // Mark. + if (handler.maybeName(target)) { + handler.markAsAssigned(target); + } else if (handler.isFunctionCall(target)) { + if (!makeSetCall(target, JSMSG_BAD_INCOP_OPERAND)) + return false; + } + return true; } template @@ -6492,8 +6591,6 @@ template typename ParseHandler::Node Parser::unaryExpr(YieldHandling yieldHandling, InvokedPrediction invoked) { - Node pn, pn2; - JS_CHECK_RECURSION(context, return null()); TokenKind tt; @@ -6501,8 +6598,6 @@ Parser::unaryExpr(YieldHandling yieldHandling, InvokedPrediction i return null(); uint32_t begin = pos().begin; switch (tt) { - case TOK_TYPEOF: - return unaryOpExpr(yieldHandling, PNK_TYPEOF, JSOP_TYPEOF, begin); case TOK_VOID: return unaryOpExpr(yieldHandling, PNK_VOID, JSOP_VOID, begin); case TOK_NOT: @@ -6514,16 +6609,36 @@ Parser::unaryExpr(YieldHandling yieldHandling, InvokedPrediction i case TOK_SUB: return unaryOpExpr(yieldHandling, PNK_NEG, JSOP_NEG, begin); + case TOK_TYPEOF: { + // The |typeof| operator is specially parsed to distinguish its + // application to a name, from its application to a non-name + // expression: + // + // // Looks up the name, doesn't find it and so evaluates to + // // "undefined". + // assertEq(typeof nonExistentName, "undefined"); + // + // // Evaluates expression, triggering a runtime ReferenceError for + // // the undefined name. + // typeof (1, nonExistentName); + Node kid = unaryExpr(yieldHandling); + if (!kid) + return null(); + + return handler.newTypeof(begin, kid); + } + case TOK_INC: case TOK_DEC: { TokenKind tt2; if (!tokenStream.getToken(&tt2, TokenStream::Operand)) return null(); - pn2 = memberExpr(yieldHandling, tt2, true); + Node pn2 = memberExpr(yieldHandling, tt2, true); if (!pn2) return null(); - if (!checkAndMarkAsIncOperand(pn2, tt, true)) + AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment; + if (!checkAndMarkAsIncOperand(pn2, flavor)) return null(); return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT, JSOP_NOP, @@ -6538,7 +6653,7 @@ Parser::unaryExpr(YieldHandling yieldHandling, InvokedPrediction i // Per spec, deleting any unary expression is valid -- it simply // returns true -- except for one case that is illegal in strict mode. - if (handler.isName(expr)) { + if (handler.maybeName(expr)) { if (!report(ParseStrictError, pc->sc->strict(), expr, JSMSG_DEPRECATED_DELETE_OPERAND)) return null(); pc->sc->setBindingsAccessedDynamically(); @@ -6547,8 +6662,8 @@ Parser::unaryExpr(YieldHandling yieldHandling, InvokedPrediction i return handler.newDelete(begin, expr); } - default: - pn = memberExpr(yieldHandling, tt, /* allowCallSyntax = */ true, invoked); + default: { + Node pn = memberExpr(yieldHandling, tt, /* allowCallSyntax = */ true, invoked); if (!pn) return null(); @@ -6557,7 +6672,8 @@ Parser::unaryExpr(YieldHandling yieldHandling, InvokedPrediction i return null(); if (tt == TOK_INC || tt == TOK_DEC) { tokenStream.consumeKnownToken(tt); - if (!checkAndMarkAsIncOperand(pn, tt, false)) + AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment; + if (!checkAndMarkAsIncOperand(pn, flavor)) return null(); return handler.newUnary((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT, JSOP_NOP, @@ -6565,6 +6681,7 @@ Parser::unaryExpr(YieldHandling yieldHandling, InvokedPrediction i pn); } return pn; + } } } @@ -7181,7 +7298,7 @@ Parser::generatorComprehensionLambda(GeneratorKind comprehensionKi return null(); } - RootedFunction fun(context, newFunction(/* atom = */ nullptr, Expression, proto)); + RootedFunction fun(context, newFunction(/* atom = */ nullptr, Expression, comprehensionKind, proto)); if (!fun) return null(); @@ -7659,27 +7776,36 @@ Parser::memberExpr(YieldHandling yieldHandling, TokenKind tt, bool /* Check for new expression first. */ if (tt == TOK_NEW) { - lhs = handler.newList(PNK_NEW, JSOP_NEW); - if (!lhs) + uint32_t newBegin = pos().begin; + // Make sure this wasn't a |new.target| in disguise. + Node newTarget; + if (!tryNewTarget(newTarget)) return null(); - - if (!tokenStream.getToken(&tt, TokenStream::Operand)) - return null(); - Node ctorExpr = memberExpr(yieldHandling, tt, false, PredictInvoked); - if (!ctorExpr) - return null(); - - handler.addList(lhs, ctorExpr); - - bool matched; - if (!tokenStream.matchToken(&matched, TOK_LP)) - return null(); - if (matched) { - bool isSpread = false; - if (!argumentList(yieldHandling, lhs, &isSpread)) + if (newTarget) { + lhs = newTarget; + } else { + lhs = handler.newList(PNK_NEW, newBegin, JSOP_NEW); + if (!lhs) return null(); - if (isSpread) - handler.setOp(lhs, JSOP_SPREADNEW); + + // Gotten by tryNewTarget + tt = tokenStream.currentToken().type; + Node ctorExpr = memberExpr(yieldHandling, tt, false, PredictInvoked); + if (!ctorExpr) + return null(); + + handler.addList(lhs, ctorExpr); + + bool matched; + if (!tokenStream.matchToken(&matched, TOK_LP)) + return null(); + if (matched) { + bool isSpread = false; + if (!argumentList(yieldHandling, lhs, &isSpread)) + return null(); + if (isSpread) + handler.setOp(lhs, JSOP_SPREADNEW); + } } } else if (tt == TOK_SUPER) { lhs = null(); @@ -7747,13 +7873,13 @@ Parser::memberExpr(YieldHandling yieldHandling, TokenKind tt, bool return null(); } - JSOp op = JSOP_CALL; - nextMember = handler.newList(tt == TOK_LP ? PNK_CALL : PNK_TAGGED_TEMPLATE, JSOP_CALL); + nextMember = tt == TOK_LP ? handler.newCall() : handler.newTaggedTemplate(); if (!nextMember) return null(); - if (JSAtom* atom = handler.isName(lhs)) { - if (tt == TOK_LP && atom == context->names().eval) { + JSOp op = JSOP_CALL; + if (PropertyName* name = handler.maybeName(lhs)) { + if (tt == TOK_LP && name == context->names().eval) { /* Select JSOP_EVAL and flag pc as heavyweight. */ op = pc->sc->strict() ? JSOP_STRICTEVAL : JSOP_EVAL; pc->sc->setBindingsAccessedDynamically(); @@ -7772,13 +7898,14 @@ Parser::memberExpr(YieldHandling yieldHandling, TokenKind tt, bool // return value.) checkAndMarkSuperScope(); } - } else if (JSAtom* atom = handler.isGetProp(lhs)) { - /* Select JSOP_FUNAPPLY given foo.apply(...). */ - if (atom == context->names().apply) { + } else if (PropertyName* prop = handler.maybeDottedProperty(lhs)) { + // Use the JSOP_FUN{APPLY,CALL} optimizations given the right + // syntax. + if (prop == context->names().apply) { op = JSOP_FUNAPPLY; if (pc->sc->isFunctionBox()) pc->sc->asFunctionBox()->usesApply = true; - } else if (atom == context->names().call) { + } else if (prop == context->names().call) { op = JSOP_FUNCALL; } } @@ -7827,7 +7954,7 @@ template typename ParseHandler::Node Parser::newName(PropertyName* name) { - return handler.newName(name, pc->blockid(), pos()); + return handler.newName(name, pc->blockid(), pos(), context); } template @@ -8398,6 +8525,44 @@ Parser::methodDefinition(YieldHandling yieldHandling, PropListType return handler.addObjectMethodDefinition(propList, propname, fn, op); } +template +bool +Parser::tryNewTarget(Node &newTarget) +{ + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NEW)); + + uint32_t begin = pos().begin; + newTarget = null(); + + // |new| expects to look for an operand, so we will honor that. + TokenKind next; + if (!tokenStream.getToken(&next, TokenStream::Operand)) + return false; + + // Don't unget the token, since lookahead cannot handle someone calling + // getToken() with a different modifier. Callers should inspect currentToken(). + if (next != TOK_DOT) + return true; + + if (!tokenStream.getToken(&next)) + return false; + if (next != TOK_NAME || tokenStream.currentName() != context->names().target) { + report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, + "target", TokenKindToDesc(next)); + return false; + } + + if (!pc->sc->isFunctionBox() || pc->sc->asFunctionBox()->isGenerator() || + pc->sc->asFunctionBox()->function()->isArrow()) + { + reportWithOffset(ParseError, false, begin, JSMSG_BAD_NEWTARGET); + return false; + } + + newTarget = handler.newNewTarget(TokenPos(begin, pos().end)); + return !!newTarget; +} + template typename ParseHandler::Node Parser::primaryExpr(YieldHandling yieldHandling, TokenKind tt, @@ -8411,7 +8576,7 @@ Parser::primaryExpr(YieldHandling yieldHandling, TokenKind tt, return functionExpr(invoked); case TOK_CLASS: - return classDefinition(yieldHandling, ClassExpression); + return classDefinition(yieldHandling, ClassExpression, NameRequired); case TOK_LB: return arrayInitializer(yieldHandling); @@ -8540,16 +8705,7 @@ Parser::parenExprOrGeneratorComprehension(YieldHandling yieldHandl if (matched) return generatorComprehension(begin); - /* - * Always accept the 'in' operator in a parenthesized expression, - * where it's unambiguous, even if we might be parsing the init of a - * for statement. - */ - bool oldParsingForInit = pc->parsingForInit; - pc->parsingForInit = false; Node pn = expr(InAllowed, yieldHandling, PredictInvoked); - pc->parsingForInit = oldParsingForInit; - if (!pn) return null(); @@ -8617,16 +8773,7 @@ Parser::exprInParens(InHandling inHandling, YieldHandling yieldHan uint32_t begin = pos().begin; uint32_t startYieldOffset = pc->lastYieldOffset; - /* - * Always accept the 'in' operator in a parenthesized expression, - * where it's unambiguous, even if we might be parsing the init of a - * for statement. - */ - bool oldParsingForInit = pc->parsingForInit; - pc->parsingForInit = false; Node pn = expr(inHandling, yieldHandling, PredictInvoked); - pc->parsingForInit = oldParsingForInit; - if (!pn) return null(); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index b6ef11fbc9..7c33104b6c 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -70,9 +70,6 @@ struct GenericParseContext // The following flags are set when parsing enters a particular region of // source code, and cleared when that region is exited. - // true while parsing init expr of for; exclude 'in' - bool parsingForInit:1; - // true while we are within a with-statement in the current ParseContext // chain (which stops at the top-level or an eval() bool parsingWith:1; @@ -82,7 +79,6 @@ struct GenericParseContext sc(sc), funHasReturnExpr(false), funHasReturnVoid(false), - parsingForInit(false), parsingWith(parent ? parent->parsingWith : false) {} }; @@ -335,6 +331,7 @@ enum PropListType { ObjectLiteral, ClassBody }; // parameter everywhere. enum YieldHandling { YieldIsName, YieldIsKeyword }; enum InHandling { InAllowed, InProhibited }; +enum DefaultHandling { NameRequired, AllowDefaultName }; template class Parser : private JS::AutoGCRooter, public StrictModeGetter @@ -445,7 +442,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter * Create a new function object given a name (which is optional if this is * a function expression). */ - JSFunction* newFunction(HandleAtom atom, FunctionSyntaxKind kind, HandleObject proto); + JSFunction* newFunction(HandleAtom atom, FunctionSyntaxKind kind, GeneratorKind generatorKind, + HandleObject proto); void trace(JSTracer* trc); @@ -524,6 +522,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter private: enum InvokedPrediction { PredictUninvoked = false, PredictInvoked = true }; + enum ForInitLocation { InForInit, NotInForInit }; private: /* @@ -542,7 +541,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter * Some parsers have two versions: an always-inlined version (with an 'i' * suffix) and a never-inlined version (with an 'n' suffix). */ - Node functionStmt(YieldHandling yieldHandling); + Node functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling); Node functionExpr(InvokedPrediction invoked = PredictUninvoked); Node statements(YieldHandling yieldHandling); @@ -567,8 +566,10 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node expressionStatement(YieldHandling yieldHandling, InvokedPrediction invoked = PredictUninvoked); Node variables(YieldHandling yieldHandling, - ParseNodeKind kind, bool* psimple = nullptr, - StaticBlockObject* blockObj = nullptr, VarContext varContext = HoistVars); + ParseNodeKind kind, + ForInitLocation location, + bool* psimple = nullptr, StaticBlockObject* blockObj = nullptr, + VarContext varContext = HoistVars); Node expr(InHandling inHandling, YieldHandling yieldHandling, InvokedPrediction invoked = PredictUninvoked); Node assignExpr(InHandling inHandling, YieldHandling yieldHandling, @@ -587,6 +588,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node parenExprOrGeneratorComprehension(YieldHandling yieldHandling); Node exprInParens(InHandling inHandling, YieldHandling yieldHandling); + bool tryNewTarget(Node& newTarget); bool checkAndMarkSuperScope(); bool methodDefinition(YieldHandling yieldHandling, PropListType listType, Node propList, @@ -632,8 +634,10 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node destructuringExprWithoutYield(YieldHandling yieldHandling, BindData* data, TokenKind tt, unsigned msg); + bool namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet); + enum ClassContext { ClassStatement, ClassExpression }; - Node classDefinition(YieldHandling yieldHandling, ClassContext classContext); + Node classDefinition(YieldHandling yieldHandling, ClassContext classContext, DefaultHandling defaultHandling); Node identifierName(YieldHandling yieldHandling); @@ -651,7 +655,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter PlainAssignment, CompoundAssignment, KeyedDestructuringAssignment, - IncDecAssignment + IncrementAssignment, + DecrementAssignment }; bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor); @@ -667,8 +672,18 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter bool isValidForStatementLHS(Node pn1, JSVersion version, bool forDecl, bool forEach, ParseNodeKind headKind); bool checkForHeadConstInitializers(Node pn1); - bool checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder); + + bool isValidSimpleAssignmentTarget(Node node); + + // Invalid assignment targets are handled differently in different places. + // Select the desired semantics using |flavor|. + bool reportIfArgumentsEvalTarget(Node target); + bool reportIfNotValidSimpleAssignmentTarget(Node target, AssignmentFlavor flavor); + + bool checkAndMarkAsIncOperand(Node kid, AssignmentFlavor flavor); + bool checkStrictAssignment(Node lhs); + bool checkStrictBinding(PropertyName* name, Node pn); bool defineArg(Node funcpn, HandlePropertyName name, bool disallowDuplicateArgs = false, Node* duplicatedArg = nullptr); @@ -686,13 +701,12 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter bool checkAndPrepareLexical(bool isConst, const TokenPos &errorPos); Node makeInitializedLexicalBinding(HandlePropertyName name, bool isConst, const TokenPos &pos); - Node newBindingNode(PropertyName *name, bool functionScope, VarContext varContext = HoistVars); - bool checkDestructuring(BindData *data, Node left); - bool checkDestructuringObject(BindData *data, Node objectPattern); - bool checkDestructuringArray(BindData *data, Node arrayPattern); - bool bindInitialized(BindData *data, Node pn); - bool bindDestructuringLHS(Node pn); - bool makeSetCall(Node pn, unsigned msg); + Node newBindingNode(PropertyName* name, bool functionScope, VarContext varContext = HoistVars); + bool checkDestructuring(BindData* data, Node left); + bool checkDestructuringObject(BindData* data, Node objectPattern); + bool checkDestructuringArray(BindData* data, Node arrayPattern); + bool bindInitialized(BindData* data, Node pn); + bool makeSetCall(Node node, unsigned errnum); Node cloneDestructuringDefault(Node opn); Node cloneLeftHandSide(Node opn); Node cloneParseTree(Node opn); @@ -730,16 +744,6 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter friend struct BindData; }; -/* Declare some required template specializations. */ - -template <> -bool -Parser::checkAndMarkAsAssignmentLhs(ParseNode* pn, AssignmentFlavor flavor); - -template <> -bool -Parser::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor); - } /* namespace frontend */ } /* namespace js */ diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 31418c8121..9882eb7ff8 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -39,15 +39,34 @@ class SyntaxParseHandler enum Node { NodeFailure = 0, NodeGeneric, - NodeName, NodeGetProp, NodeStringExprStatement, - NodeLValue, NodeReturn, NodeHoistableDeclaration, NodeBreak, NodeThrow, + // This is needed for proper assignment-target handling. ES6 formally + // requires function calls *not* pass IsValidSimpleAssignmentTarget, + // but at last check there were still sites with |f() = 5| and similar + // in code not actually executed (or at least not executed enough to be + // noticed). + NodeFunctionCall, + + // Nodes representing names. These *must* be sequential per |isName|. + NodeArgumentsName, + NodeEvalName, + NodeName, + + NodeDottedProperty, + NodeElement, + NodeSuperProperty, + NodeSuperElement, + + // Valuable for recognizing potential destructuring patterns. + NodeArray, + NodeObject, + // In rare cases a parenthesized |node| doesn't have the same semantics // as |node|. Each such node has a special Node value, and we use a // different Node value to represent the parenthesized form. See also @@ -90,6 +109,20 @@ class SyntaxParseHandler }; typedef Definition::Kind DefinitionNode; + bool isPropertyAccess(Node node) { + return node == NodeDottedProperty || node == NodeElement || + node == NodeSuperProperty || node == NodeSuperElement; + } + + bool isFunctionCall(Node node) { + // Note: super() is a special form, *not* a function call. + return node == NodeFunctionCall; + } + + bool isDestructuringTarget(Node node) { + return node == NodeArray || node == NodeObject; + } + private: static bool meaningMightChangeIfParenthesized(Node node) { return node == NodeUnparenthesizedString || @@ -111,8 +144,12 @@ class SyntaxParseHandler void trace(JSTracer* trc) {} - Node newName(PropertyName* name, uint32_t blockid, const TokenPos& pos) { + Node newName(PropertyName* name, uint32_t blockid, const TokenPos& pos, ExclusiveContext* cx) { lastAtom = name; + if (name == cx->names().arguments) + return NodeArgumentsName; + if (name == cx->names().eval) + return NodeEvalName; return NodeName; } @@ -159,7 +196,17 @@ class SyntaxParseHandler Node newElision() { return NodeGeneric; } - Node newDelete(uint32_t begin, Node expr) { return NodeGeneric; } + void markAsSetCall(Node node) { + MOZ_ASSERT(node == NodeFunctionCall); + } + + Node newDelete(uint32_t begin, Node expr) { + return NodeGeneric; + } + + Node newTypeof(uint32_t begin, Node kid) { + return NodeGeneric; + } Node newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, Node kid) { return NodeGeneric; @@ -184,15 +231,26 @@ class SyntaxParseHandler Node newArrayComprehension(Node body, unsigned blockid, const TokenPos& pos) { return NodeGeneric; } - Node newArrayLiteral(uint32_t begin, unsigned blockid) { return NodeGeneric; } + Node newArrayLiteral(uint32_t begin, unsigned blockid) { return NodeArray; } bool addElision(Node literal, const TokenPos& pos) { return true; } bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; } void addArrayElement(Node literal, Node element) { } - Node newObjectLiteral(uint32_t begin) { return NodeGeneric; } + Node newCall() { return NodeFunctionCall; } + Node newTaggedTemplate() { return NodeGeneric; } + + Node newObjectLiteral(uint32_t begin) { return NodeObject; } Node newClassMethodList(uint32_t begin) { return NodeGeneric; } - Node newSuperProperty(JSAtom* atom, const TokenPos& pos) { return NodeGeneric; } - Node newSuperElement(Node expr, const TokenPos& pos) { return NodeGeneric; } + + Node newSuperProperty(PropertyName* prop, const TokenPos& pos) { + return NodeSuperProperty; + } + + Node newSuperElement(Node expr, const TokenPos& pos) { + return NodeSuperElement; + } + Node newNewTarget(const TokenPos& pos) { return NodeGeneric; } + bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; } bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; } bool addShorthand(Node literal, Node name, Node expr) { return true; } @@ -233,10 +291,10 @@ class SyntaxParseHandler Node newPropertyAccess(Node pn, PropertyName* name, uint32_t end) { lastAtom = name; - return NodeGetProp; + return NodeDottedProperty; } - Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeLValue; } + Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeElement; } bool addCatchBlock(Node catchList, Node letBlock, Node catchName, Node catchGuard, Node catchBody) { return true; } @@ -280,6 +338,9 @@ class SyntaxParseHandler MOZ_ASSERT(kind != PNK_VAR); return NodeGeneric; } + Node newList(ParseNodeKind kind, uint32_t begin, JSOp op = JSOP_NOP) { + return NodeGeneric; + } Node newDeclarationList(ParseNodeKind kind, JSOp op = JSOP_NOP) { MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET || kind == PNK_GLOBALCONST); @@ -304,8 +365,12 @@ class SyntaxParseHandler } void addList(Node list, Node kid) { - MOZ_ASSERT(list == NodeGeneric || list == NodeUnparenthesizedCommaExpr || - list == NodeHoistableDeclaration); + MOZ_ASSERT(list == NodeGeneric || + list == NodeArray || + list == NodeObject || + list == NodeUnparenthesizedCommaExpr || + list == NodeHoistableDeclaration || + list == NodeFunctionCall); } Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs, @@ -354,12 +419,23 @@ class SyntaxParseHandler void setPrologue(Node pn) {} bool isConstant(Node pn) { return false; } - PropertyName* isName(Node pn) { - return (pn == NodeName) ? lastAtom->asPropertyName() : nullptr; + PropertyName* maybeName(Node pn) { + if (pn == NodeName || pn == NodeArgumentsName || pn == NodeEvalName) + return lastAtom->asPropertyName(); + return nullptr; } - PropertyName* isGetProp(Node pn) { - return (pn == NodeGetProp) ? lastAtom->asPropertyName() : nullptr; + + PropertyName* maybeDottedProperty(Node node) { + // Note: |super.apply(...)| is a special form that calls an "apply" + // method retrieved from one value, but using a *different* value as + // |this|. It's not really eligible for the funapply/funcall + // optimizations as they're currently implemented (assuming a single + // value is used for both retrieval and |this|). + if (node != NodeDottedProperty) + return nullptr; + return lastAtom->asPropertyName(); } + JSAtom* isStringExprStatement(Node pn, TokenPos* pos) { if (pn == NodeStringExprStatement) { *pos = lastStringPos; @@ -368,6 +444,10 @@ class SyntaxParseHandler return nullptr; } + void markAsAssigned(Node node) {} + void adjustGetToSet(Node node) {} + void maybeDespecializeSet(Node node) {} + Node makeAssignment(Node pn, Node rhs) { return NodeGeneric; } static Node getDefinitionNode(DefinitionNode dn) { return NodeGeneric; } diff --git a/js/src/irregexp/RegExpParser.h b/js/src/irregexp/RegExpParser.h index ee6074a68e..e36a47f750 100644 --- a/js/src/irregexp/RegExpParser.h +++ b/js/src/irregexp/RegExpParser.h @@ -178,7 +178,6 @@ class RegExpParser RegExpTree* ParsePattern(); RegExpTree* ParseDisjunction(); - RegExpTree* ParseGroup(); RegExpTree* ParseCharacterClass(); // Parses a {...,...} quantifier and stores the range in the given diff --git a/js/src/jit-test/tests/basic/bug1182865.js b/js/src/jit-test/tests/basic/bug1182865.js new file mode 100644 index 0000000000..f221b4328c --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1182865.js @@ -0,0 +1,8 @@ + +for (var j = 0; j < 99; j++) { + Array[{ + f() { + eval() + } + }]; +} diff --git a/js/src/jit-test/tests/basic/newTargetOSR.js b/js/src/jit-test/tests/basic/newTargetOSR.js new file mode 100644 index 0000000000..b90e96cf1b --- /dev/null +++ b/js/src/jit-test/tests/basic/newTargetOSR.js @@ -0,0 +1,6 @@ +function testOSRNewTarget(expected) { + for (let i = 0; i < 1100; i++) + assertEq(new.target, expected); +} + +new testOSRNewTarget(testOSRNewTarget); diff --git a/js/src/jit-test/tests/basic/typed-array-copyWithin.js b/js/src/jit-test/tests/basic/typed-array-copyWithin.js index c5c9421d46..1449ca1ae0 100644 --- a/js/src/jit-test/tests/basic/typed-array-copyWithin.js +++ b/js/src/jit-test/tests/basic/typed-array-copyWithin.js @@ -172,6 +172,28 @@ for (var constructor of constructors) { assertEq(e, 42, "should have failed converting target to index"); } + function neuterAndConvertTo(x) { + return { valueOf() { neuter(tarray.buffer, "change-data"); return x; } }; + } + + // Neutering during argument processing triggers a TypeError. + tarray = new constructor([1, 2, 3, 4, 5]); + try + { + tarray.copyWithin(0, 3, neuterAndConvertTo(4)); + throw new Error("expected to throw"); + } + catch (e) + { + assertEq(e instanceof TypeError, true, + "expected throw with neutered array during set"); + } + + // ...unless no elements are to be copied. + tarray = new constructor([1, 2, 3, 4, 5]); + assertDeepEq(tarray.copyWithin(0, 3, neuterAndConvertTo(3)), + new constructor([])); + /* // fails, unclear whether it should, disabling for now // test with a proxy object var handler = { diff --git a/js/src/jit-test/tests/debug/Frame-evalWithBindings-14.js b/js/src/jit-test/tests/debug/Frame-evalWithBindings-14.js new file mode 100644 index 0000000000..1c8fc93cbe --- /dev/null +++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-14.js @@ -0,0 +1,20 @@ +var g = newGlobal(); +var dbg = new Debugger(g); +// We're going to need to eval with a thrown exception from inside +// onExceptionUnwind, so guard against infinite recursion. +var exceptionCount = 0; +dbg.onDebuggerStatement = function (frame) { + var x = frame.evalWithBindings("throw 'haha'", { rightSpelling: 4 }).throw; + assertEq(x, "haha"); +}; +dbg.onExceptionUnwind = function (frame, exc) { + ++exceptionCount; + if (exceptionCount == 1) { + var y = frame.evalWithBindings("throw 'haha2'", { rightSpelling: 2 }).throw; + assertEq(y, "haha2"); + } else { + assertEq(frame.evalWithBindings("rightSpelling + three", { three : 3 }).return, 5); + } +}; +g.eval("(function () { var rightSpelling = 7; debugger; })();"); +assertEq(exceptionCount, 2); diff --git a/js/src/jit-test/tests/ion/bug1160884.js b/js/src/jit-test/tests/ion/bug1160884.js new file mode 100644 index 0000000000..7eb301688a --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1160884.js @@ -0,0 +1,12 @@ +function foo(date) { + var F = date.split(" "); + var D = F[0].split("-"); + var C = F[1].split(":"); + return new Date(D[0], D[1], D[2], C[0], C[1], C[2]); +} +function test() { + with(this) {}; + for (var i = 0; i < 1200; i++) + foo("13-5-2015 18:30:" + i); +} +test(); diff --git a/js/src/jit-test/tests/modules/export-declaration.js b/js/src/jit-test/tests/modules/export-declaration.js index b03d227e2f..9d3245aa58 100644 --- a/js/src/jit-test/tests/modules/export-declaration.js +++ b/js/src/jit-test/tests/modules/export-declaration.js @@ -7,11 +7,12 @@ program = (elts) => Pattern({ type: "Program", body: elts }) -exportDeclaration = (declaration, specifiers, source) => Pattern({ +exportDeclaration = (declaration, specifiers, source, isDefault) => Pattern({ type: "ExportDeclaration", declaration: declaration, specifiers: specifiers, - source: source + source: source, + isDefault: isDefault }); exportSpecifier = (id, name) => Pattern({ type: "ExportSpecifier", @@ -34,6 +35,10 @@ functionDeclaration = (id, params, body) => Pattern({ rest: null, generator: false }); +classDeclaration = (name) => Pattern({ + type: "ClassStatement", + name: name +}); variableDeclaration = (decls) => Pattern({ type: "VariableDeclaration", kind: "var", @@ -62,7 +67,8 @@ program([ exportDeclaration( null, [], - null + null, + false ) ]).assert(Reflect.parse("export {}")); @@ -75,7 +81,8 @@ program([ ident("a") ) ], - null + null, + false ) ]).assert(Reflect.parse("export { a }")); @@ -88,7 +95,8 @@ program([ ident("b") ) ], - null + null, + false ) ]).assert(Reflect.parse("export { a as b }")); @@ -101,7 +109,8 @@ program([ ident("as") ) ], - null + null, + false ) ]).assert(Reflect.parse("export { as as as }")); @@ -114,7 +123,8 @@ program([ ident("true") ) ], - null + null, + false ) ]).assert(Reflect.parse("export { a as true }")); @@ -131,7 +141,8 @@ program([ ident("b") ), ], - null + null, + false ) ]).assert(Reflect.parse("export { a, b }")); @@ -148,7 +159,8 @@ program([ ident("d") ), ], - null + null, + false ) ]).assert(Reflect.parse("export { a as b, c as d }")); @@ -156,11 +168,15 @@ program([ exportDeclaration( null, [ - exportBatchSpecifier() + exportSpecifier( + ident("a"), + ident("a") + ) ], - null + lit("b"), + false ) -]).assert(Reflect.parse("export *")); +]).assert(Reflect.parse("export { a } from 'b'")); program([ exportDeclaration( @@ -168,7 +184,8 @@ program([ [ exportBatchSpecifier() ], - lit("a") + lit("a"), + false ) ]).assert(Reflect.parse("export * from 'a'")); @@ -180,10 +197,22 @@ program([ blockStatement([]) ), null, - null + null, + false ) ]).assert(Reflect.parse("export function f() {}")); +program([ + exportDeclaration( + classDeclaration( + ident("Foo") + ), + null, + null, + false + ) +]).assert(Reflect.parse("export class Foo { constructor() {} }")); + program([ exportDeclaration( variableDeclaration([ @@ -196,7 +225,8 @@ program([ } ]), null, - null + null, + false ) ]).assert(Reflect.parse("export var a = 1, b = 2;")); @@ -212,12 +242,13 @@ program([ } ]), null, - null + null, + false ) ]).assert(Reflect.parse("export const a = 1, b = 2;")); // FIXME: In scripts, top level lets are converted back to vars. Fix this when -// we implement compiling scripts as modules. +// we implement compiling scripts as modules (bug 589199). program([ exportDeclaration( variableDeclaration([ @@ -230,7 +261,8 @@ program([ } ]), null, - null + null, + false ) ]).assert(Reflect.parse("export let a = 1, b = 2;")); @@ -248,10 +280,68 @@ program([ } ]), null, - null + null, + false ) ]).assert(Reflect.parse("export a = 1, b = 2;")); +program([ + exportDeclaration( + functionDeclaration( + ident("*default*"), + [], + blockStatement([]) + ), + null, + null, + true + ) +]).assert(Reflect.parse("export default function() {}")); + +program([ + exportDeclaration( + functionDeclaration( + ident("foo"), + [], + blockStatement([]) + ), + null, + null, + true + ) +]).assert(Reflect.parse("export default function foo() {}")); + +program([ + exportDeclaration( + classDeclaration( + ident("*default*") + ), + null, + null, + true + ) +]).assert(Reflect.parse("export default class { constructor() {} }")); + +program([ + exportDeclaration( + classDeclaration( + ident("Foo") + ), + null, + null, + true + ) +]).assert(Reflect.parse("export default class Foo { constructor() {} }")); + +program([ + exportDeclaration( + lit(1234), + null, + null, + true + ) +]).assert(Reflect.parse("export default 1234")); + var loc = Reflect.parse("export { a as b } from 'c'", { loc: true }).body[0].loc; @@ -289,6 +379,10 @@ assertThrowsInstanceOf(function () { Reflect.parse("export { a } from 'b' f();"); }, SyntaxError); +assertThrowsInstanceOf(function () { + Reflect.parse("export *"); +}, SyntaxError); + assertThrowsInstanceOf(function () { Reflect.parse("export * from 'b' f();"); }, SyntaxError); @@ -296,3 +390,11 @@ assertThrowsInstanceOf(function () { assertThrowsInstanceOf(function() { Reflect.parse("export {}\nfrom ()"); }, SyntaxError); + +assertThrowsInstanceOf(function() { + Reflect.parse("function() {}"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + Reflect.parse("class() { constructor() {} }"); +}, SyntaxError); diff --git a/js/src/jit-test/tests/modules/import-declaration.js b/js/src/jit-test/tests/modules/import-declaration.js index 54596359a9..cf5468c3f0 100644 --- a/js/src/jit-test/tests/modules/import-declaration.js +++ b/js/src/jit-test/tests/modules/import-declaration.js @@ -38,6 +38,18 @@ program([ ) ]).assert(Reflect.parse("import a from 'b'")); +program([ + importDeclaration( + [ + importSpecifier( + ident("*"), + ident("a") + ) + ], + lit("b") + ) +]).assert(Reflect.parse("import * as a from 'b'")); + program([ importDeclaration( [], @@ -93,6 +105,106 @@ program([ ) ]).assert(Reflect.parse("import { as as as } from 'a'")); +program([ + importDeclaration( + [ + importSpecifier( + ident("default"), + ident("a") + ), + importSpecifier( + ident("*"), + ident("b") + ) + ], + lit("c") + ) +]).assert(Reflect.parse("import a, * as b from 'c'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("default"), + ident("d") + ) + ], + lit("a") + ) +]).assert(Reflect.parse("import d, {} from 'a'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("default"), + ident("d") + ), + importSpecifier( + ident("a"), + ident("a") + ) + ], + lit("b") + ) +]).assert(Reflect.parse("import d, { a } from 'b'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("default"), + ident("d") + ), + importSpecifier( + ident("a"), + ident("b") + ) + ], + lit("c") + ) +]).assert(Reflect.parse("import d, { a as b } from 'c'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("default"), + ident("d") + ), + importSpecifier( + ident("a"), + ident("a") + ), + importSpecifier( + ident("b"), + ident("b") + ), + ], + lit("c") + ) +]).assert(Reflect.parse("import d, { a, b } from 'c'")); + +program([ + importDeclaration( + [ + importSpecifier( + ident("default"), + ident("d") + ), + importSpecifier( + ident("a"), + ident("b") + ), + importSpecifier( + ident("c"), + ident("d") + ), + ], + lit("e") + ) +]).assert(Reflect.parse("import d, { a as b, c as d } from 'e'")); + program([ importDeclaration( [ @@ -184,3 +296,43 @@ assertThrowsInstanceOf(function() { assertThrowsInstanceOf(function() { Reflect.parse("import { true } from 'a'"); }, SyntaxError); + +assertThrowsInstanceOf(function() { + Reflect.parse("import a,"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + Reflect.parse("import a, b from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + Reflect.parse("import * as a,"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + Reflect.parse("import * as a, {} from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + Reflect.parse("import as a from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + Reflect.parse("import * a from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + Reflect.parse("import * as from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + Reflect.parse("import , {} from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + Reflect.parse("import d, from 'a'"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + Reflect.parse("import * as true from 'b'"); +}, SyntaxError); diff --git a/js/src/jit/AtomicOperations-inl.h b/js/src/jit/AtomicOperations-inl.h new file mode 100644 index 0000000000..37adf40a31 --- /dev/null +++ b/js/src/jit/AtomicOperations-inl.h @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_AtomicOperations_inl_h +#define jit_AtomicOperations_inl_h + +#if defined(JS_CODEGEN_ARM) +# include "jit/arm/AtomicOperations-arm.h" +#elif defined(JS_CODEGEN_MIPS) +# include "jit/mips/AtomicOperations-mips.h" +#elif defined(JS_CODEGEN_NONE) +# include "jit/none/AtomicOperations-none.h" +#elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) +# include "jit/x86-shared/AtomicOperations-x86-shared.h" +#else +# error "Atomic operations must be defined for this platform" +#endif + +#endif // jit_AtomicOperations_inl_h diff --git a/js/src/jit/AtomicOperations.h b/js/src/jit/AtomicOperations.h new file mode 100644 index 0000000000..367aa615e9 --- /dev/null +++ b/js/src/jit/AtomicOperations.h @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_AtomicOperations_h +#define jit_AtomicOperations_h + +namespace js { +namespace jit { + +/* + * The atomic operations layer defines the following types and + * functions. The "SeqCst" suffix on operations means "sequentially + * consistent" and means such a function's operation must have + * "sequentially consistent" memory ordering. See mfbt/Atomics.h for + * an explanation of this memory ordering. + * + * To make use of the functions you generally have to include + * AtomicOperations-inl.h. + * + * The fundamental constraint on all these primitives is that their + * realization by the C++ compiler MUST be compatible with code the + * JIT generates for its Atomics operations, so that an atomic access + * from the interpreter really is atomic relative to a concurrent + * access from jitted code. + * + * It's not a requirement that these methods be inlined; performance + * is not a great concern. On some platforms these methods may call + * out to code that's generated at run time. + */ + +class AtomicOperations +{ + public: + + // Execute a full memory barrier (LoadLoad+LoadStore+StoreLoad+StoreStore). + static inline void fenceSeqCst(); + + // If the return value is true then a call to the 64-bit (8-byte) + // routines below will work, otherwise those functions will assert in + // debug builds and may crash in release build. (See the code in + // ../arm for an example.) The value of this call does not change + // during a run. + static inline bool isLockfree8(); + + // The following functions are defined for T = int8_t, uint8_t, + // int16_t, uint16_t, int32_t, uint32_t, int64_t, and uint64_t + + // Atomically read* addr. + template + static inline T loadSeqCst(T* addr); + + // Atomically store val in *addr. + template + static inline void storeSeqCst(T* addr, T val); + + // Atomically store val in *addr and return the old value of *addr. + template + static inline T exchangeSeqCst(T* addr, T val); + + // Atomically check that *addr contains oldval and if so replace it + // with newval, in any case return the old contents of *addr + template + static inline T compareExchangeSeqCst(T* addr, T oldval, T newval); + + // The following functions are defined for T = int8_t, uint8_t, + // int16_t, uint16_t, int32_t, uint32_t only. + + // Atomically add, subtract, bitwise-AND, bitwise-OR, or bitwise-XOR + // val into *addr and return the old value of *addr. + template + static inline T fetchAddSeqCst(T* addr, T val); + + template + static inline T fetchSubSeqCst(T* addr, T val); + + template + static inline T fetchAndSeqCst(T* addr, T val); + + template + static inline T fetchOrSeqCst(T* addr, T val); + + template + static inline T fetchXorSeqCst(T* addr, T val); +}; + +/* A data type representing a lock on some region of a + * SharedArrayRawBuffer's memory, to be used only when the hardware + * does not provide necessary atomicity (eg, float64 access on ARMv6 + * and some ARMv7 systems). + */ +struct RegionLock +{ + public: + RegionLock() : spinlock(0) {} + + /* Addr is the address to be locked, nbytes the number of bytes we + * need to lock. The lock that is taken may cover a larger range + * of bytes. + */ + template + void acquire(void* addr); + + /* Addr is the address to be unlocked, nbytes the number of bytes + * we need to unlock. The lock must be held by the calling thread, + * at the given address and for the number of bytes. + */ + template + void release(void* addr); + + private: + /* For now, a simple spinlock that covers the entire buffer. */ + uint32_t spinlock; +}; + +} // namespace jit +} // namespace js + +#endif // jit_AtomicOperations_h diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 940f3f55f1..16d886a380 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -2667,6 +2667,43 @@ BaselineCompiler::emit_JSOP_SETARG() return emitFormalArgAccess(arg, /* get = */ false); } +bool +BaselineCompiler::emit_JSOP_NEWTARGET() +{ + MOZ_ASSERT(function()); + frame.syncStack(0); + + // if (!isConstructing()) push(undefined) + Label constructing, done; + masm.branchTestPtr(Assembler::NonZero, frame.addressOfCalleeToken(), + Imm32(CalleeToken_FunctionConstructing), &constructing); + masm.moveValue(UndefinedValue(), R0); + masm.jump(&done); + + masm.bind(&constructing); + + // else push(argv[Max(numActualArgs, numFormalArgs)]) + Register argvLen = R0.scratchReg(); + + Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs()); + masm.loadPtr(actualArgs, argvLen); + + Label actualArgsSufficient; + + masm.branchPtr(Assembler::AboveOrEqual, argvLen, Imm32(function()->nargs()), + &actualArgsSufficient); + masm.move32(Imm32(function()->nargs()), argvLen); + masm.bind(&actualArgsSufficient); + + BaseValueIndex newTarget(BaselineFrameReg, argvLen, BaselineFrame::offsetOfArg(0)); + masm.loadValue(newTarget, R0); + + masm.bind(&done); + frame.push(R0); + + return true; +} + typedef bool (*ThrowUninitializedLexicalFn)(JSContext* cx); static const VMFunction ThrowUninitializedLexicalInfo = FunctionInfo(jit::ThrowUninitializedLexical); diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index aac726e3e4..b13a74e4ce 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -197,7 +197,8 @@ namespace jit { _(JSOP_CALLEE) \ _(JSOP_SETRVAL) \ _(JSOP_RETRVAL) \ - _(JSOP_RETURN) + _(JSOP_RETURN) \ + _(JSOP_NEWTARGET) class BaselineCompiler : public BaselineCompilerSpecific { diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 2d9f2b525e..0f9f306907 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -45,757 +45,6 @@ using mozilla::DebugOnly; namespace js { namespace jit { -#ifdef DEBUG -void -FallbackICSpew(JSContext* cx, ICFallbackStub* stub, const char* fmt, ...) -{ - if (JitSpewEnabled(JitSpew_BaselineICFallback)) { - RootedScript script(cx, GetTopJitJSScript(cx)); - jsbytecode* pc = stub->icEntry()->pc(script); - - char fmtbuf[100]; - va_list args; - va_start(args, fmt); - vsnprintf(fmtbuf, 100, fmt, args); - va_end(args); - - JitSpew(JitSpew_BaselineICFallback, - "Fallback hit for (%s:%" PRIuSIZE ") (pc=%" PRIuSIZE ",line=%d,uses=%d,stubs=%" PRIuSIZE "): %s", - script->filename(), - script->lineno(), - script->pcToOffset(pc), - PCToLineNumber(script, pc), - script->getWarmUpCount(), - stub->numOptimizedStubs(), - fmtbuf); - } -} - -void -TypeFallbackICSpew(JSContext* cx, ICTypeMonitor_Fallback* stub, const char* fmt, ...) -{ - if (JitSpewEnabled(JitSpew_BaselineICFallback)) { - RootedScript script(cx, GetTopJitJSScript(cx)); - jsbytecode* pc = stub->icEntry()->pc(script); - - char fmtbuf[100]; - va_list args; - va_start(args, fmt); - vsnprintf(fmtbuf, 100, fmt, args); - va_end(args); - - JitSpew(JitSpew_BaselineICFallback, - "Type monitor fallback hit for (%s:%" PRIuSIZE ") (pc=%" PRIuSIZE ",line=%d,uses=%d,stubs=%d): %s", - script->filename(), - script->lineno(), - script->pcToOffset(pc), - PCToLineNumber(script, pc), - script->getWarmUpCount(), - (int) stub->numOptimizedMonitorStubs(), - fmtbuf); - } -} - -#else -#define FallbackICSpew(...) -#define TypeFallbackICSpew(...) -#endif - - -ICFallbackStub* -ICEntry::fallbackStub() const -{ - return firstStub()->getChainFallback(); -} - - -ICStubConstIterator& -ICStubConstIterator::operator++() -{ - MOZ_ASSERT(currentStub_ != nullptr); - currentStub_ = currentStub_->next(); - return *this; -} - - -ICStubIterator::ICStubIterator(ICFallbackStub* fallbackStub, bool end) - : icEntry_(fallbackStub->icEntry()), - fallbackStub_(fallbackStub), - previousStub_(nullptr), - currentStub_(end ? fallbackStub : icEntry_->firstStub()), - unlinked_(false) -{ } - -ICStubIterator& -ICStubIterator::operator++() -{ - MOZ_ASSERT(currentStub_->next() != nullptr); - if (!unlinked_) - previousStub_ = currentStub_; - currentStub_ = currentStub_->next(); - unlinked_ = false; - return *this; -} - -void -ICStubIterator::unlink(JSContext* cx) -{ - MOZ_ASSERT(currentStub_->next() != nullptr); - MOZ_ASSERT(currentStub_ != fallbackStub_); - MOZ_ASSERT(!unlinked_); - - fallbackStub_->unlinkStub(cx->zone(), previousStub_, currentStub_); - - // Mark the current iterator position as unlinked, so operator++ works properly. - unlinked_ = true; -} - - -void -ICStub::markCode(JSTracer* trc, const char* name) -{ - JitCode* stubJitCode = jitCode(); - TraceManuallyBarrieredEdge(trc, &stubJitCode, name); -} - -void -ICStub::updateCode(JitCode* code) -{ - // Write barrier on the old code. - JitCode::writeBarrierPre(jitCode()); - stubCode_ = code->raw(); -} - -/* static */ void -ICStub::trace(JSTracer* trc) -{ - markCode(trc, "baseline-stub-jitcode"); - - // If the stub is a monitored fallback stub, then mark the monitor ICs hanging - // off of that stub. We don't need to worry about the regular monitored stubs, - // because the regular monitored stubs will always have a monitored fallback stub - // that references the same stub chain. - if (isMonitoredFallback()) { - ICTypeMonitor_Fallback* lastMonStub = toMonitoredFallbackStub()->fallbackMonitorStub(); - for (ICStubConstIterator iter(lastMonStub->firstMonitorStub()); !iter.atEnd(); iter++) { - MOZ_ASSERT_IF(iter->next() == nullptr, *iter == lastMonStub); - iter->trace(trc); - } - } - - if (isUpdated()) { - for (ICStubConstIterator iter(toUpdatedStub()->firstUpdateStub()); !iter.atEnd(); iter++) { - MOZ_ASSERT_IF(iter->next() == nullptr, iter->isTypeUpdate_Fallback()); - iter->trace(trc); - } - } - - switch (kind()) { - case ICStub::Call_Scripted: { - ICCall_Scripted* callStub = toCall_Scripted(); - TraceEdge(trc, &callStub->callee(), "baseline-callscripted-callee"); - if (callStub->templateObject()) - TraceEdge(trc, &callStub->templateObject(), "baseline-callscripted-template"); - break; - } - case ICStub::Call_Native: { - ICCall_Native* callStub = toCall_Native(); - TraceEdge(trc, &callStub->callee(), "baseline-callnative-callee"); - if (callStub->templateObject()) - TraceEdge(trc, &callStub->templateObject(), "baseline-callnative-template"); - break; - } - case ICStub::Call_ClassHook: { - ICCall_ClassHook* callStub = toCall_ClassHook(); - if (callStub->templateObject()) - TraceEdge(trc, &callStub->templateObject(), "baseline-callclasshook-template"); - break; - } - case ICStub::Call_StringSplit: { - ICCall_StringSplit* callStub = toCall_StringSplit(); - TraceEdge(trc, &callStub->templateObject(), "baseline-callstringsplit-template"); - TraceEdge(trc, &callStub->expectedArg(), "baseline-callstringsplit-arg"); - TraceEdge(trc, &callStub->expectedThis(), "baseline-callstringsplit-this"); - break; - } - case ICStub::GetElem_NativeSlot: { - ICGetElem_NativeSlot* getElemStub = toGetElem_NativeSlot(); - TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-native-shape"); - TraceEdge(trc, &getElemStub->name(), "baseline-getelem-native-name"); - break; - } - case ICStub::GetElem_NativePrototypeSlot: { - ICGetElem_NativePrototypeSlot* getElemStub = toGetElem_NativePrototypeSlot(); - TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-nativeproto-shape"); - TraceEdge(trc, &getElemStub->name(), "baseline-getelem-nativeproto-name"); - TraceEdge(trc, &getElemStub->holder(), "baseline-getelem-nativeproto-holder"); - TraceEdge(trc, &getElemStub->holderShape(), "baseline-getelem-nativeproto-holdershape"); - break; - } - case ICStub::GetElem_NativePrototypeCallNative: - case ICStub::GetElem_NativePrototypeCallScripted: { - ICGetElemNativePrototypeCallStub* callStub = - reinterpret_cast(this); - TraceEdge(trc, &callStub->shape(), "baseline-getelem-nativeprotocall-shape"); - TraceEdge(trc, &callStub->name(), "baseline-getelem-nativeprotocall-name"); - TraceEdge(trc, &callStub->getter(), "baseline-getelem-nativeprotocall-getter"); - TraceEdge(trc, &callStub->holder(), "baseline-getelem-nativeprotocall-holder"); - TraceEdge(trc, &callStub->holderShape(), "baseline-getelem-nativeprotocall-holdershape"); - break; - } - case ICStub::GetElem_Dense: { - ICGetElem_Dense* getElemStub = toGetElem_Dense(); - TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-dense-shape"); - break; - } - case ICStub::GetElem_UnboxedArray: { - ICGetElem_UnboxedArray* getElemStub = toGetElem_UnboxedArray(); - TraceEdge(trc, &getElemStub->group(), "baseline-getelem-unboxed-array-group"); - break; - } - case ICStub::GetElem_TypedArray: { - ICGetElem_TypedArray* getElemStub = toGetElem_TypedArray(); - TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-typedarray-shape"); - break; - } - case ICStub::SetElem_DenseOrUnboxedArray: { - ICSetElem_DenseOrUnboxedArray* setElemStub = toSetElem_DenseOrUnboxedArray(); - if (setElemStub->shape()) - TraceEdge(trc, &setElemStub->shape(), "baseline-getelem-dense-shape"); - TraceEdge(trc, &setElemStub->group(), "baseline-setelem-dense-group"); - break; - } - case ICStub::SetElem_DenseOrUnboxedArrayAdd: { - ICSetElem_DenseOrUnboxedArrayAdd* setElemStub = toSetElem_DenseOrUnboxedArrayAdd(); - TraceEdge(trc, &setElemStub->group(), "baseline-setelem-denseadd-group"); - - JS_STATIC_ASSERT(ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH == 4); - - switch (setElemStub->protoChainDepth()) { - case 0: setElemStub->toImpl<0>()->traceShapes(trc); break; - case 1: setElemStub->toImpl<1>()->traceShapes(trc); break; - case 2: setElemStub->toImpl<2>()->traceShapes(trc); break; - case 3: setElemStub->toImpl<3>()->traceShapes(trc); break; - case 4: setElemStub->toImpl<4>()->traceShapes(trc); break; - default: MOZ_CRASH("Invalid proto stub."); - } - break; - } - case ICStub::SetElem_TypedArray: { - ICSetElem_TypedArray* setElemStub = toSetElem_TypedArray(); - TraceEdge(trc, &setElemStub->shape(), "baseline-setelem-typedarray-shape"); - break; - } - case ICStub::TypeMonitor_SingleObject: { - ICTypeMonitor_SingleObject* monitorStub = toTypeMonitor_SingleObject(); - TraceEdge(trc, &monitorStub->object(), "baseline-monitor-singleton"); - break; - } - case ICStub::TypeMonitor_ObjectGroup: { - ICTypeMonitor_ObjectGroup* monitorStub = toTypeMonitor_ObjectGroup(); - TraceEdge(trc, &monitorStub->group(), "baseline-monitor-group"); - break; - } - case ICStub::TypeUpdate_SingleObject: { - ICTypeUpdate_SingleObject* updateStub = toTypeUpdate_SingleObject(); - TraceEdge(trc, &updateStub->object(), "baseline-update-singleton"); - break; - } - case ICStub::TypeUpdate_ObjectGroup: { - ICTypeUpdate_ObjectGroup* updateStub = toTypeUpdate_ObjectGroup(); - TraceEdge(trc, &updateStub->group(), "baseline-update-group"); - break; - } - case ICStub::In_Native: { - ICIn_Native* inStub = toIn_Native(); - TraceEdge(trc, &inStub->shape(), "baseline-innative-stub-shape"); - TraceEdge(trc, &inStub->name(), "baseline-innative-stub-name"); - break; - } - case ICStub::In_NativePrototype: { - ICIn_NativePrototype* inStub = toIn_NativePrototype(); - TraceEdge(trc, &inStub->shape(), "baseline-innativeproto-stub-shape"); - TraceEdge(trc, &inStub->name(), "baseline-innativeproto-stub-name"); - TraceEdge(trc, &inStub->holder(), "baseline-innativeproto-stub-holder"); - TraceEdge(trc, &inStub->holderShape(), "baseline-innativeproto-stub-holdershape"); - break; - } - case ICStub::In_NativeDoesNotExist: { - ICIn_NativeDoesNotExist* inStub = toIn_NativeDoesNotExist(); - TraceEdge(trc, &inStub->name(), "baseline-innativedoesnotexist-stub-name"); - JS_STATIC_ASSERT(ICIn_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8); - switch (inStub->protoChainDepth()) { - case 0: inStub->toImpl<0>()->traceShapes(trc); break; - case 1: inStub->toImpl<1>()->traceShapes(trc); break; - case 2: inStub->toImpl<2>()->traceShapes(trc); break; - case 3: inStub->toImpl<3>()->traceShapes(trc); break; - case 4: inStub->toImpl<4>()->traceShapes(trc); break; - case 5: inStub->toImpl<5>()->traceShapes(trc); break; - case 6: inStub->toImpl<6>()->traceShapes(trc); break; - case 7: inStub->toImpl<7>()->traceShapes(trc); break; - case 8: inStub->toImpl<8>()->traceShapes(trc); break; - default: MOZ_CRASH("Invalid proto stub."); - } - break; - } - case ICStub::In_Dense: { - ICIn_Dense* inStub = toIn_Dense(); - TraceEdge(trc, &inStub->shape(), "baseline-in-dense-shape"); - break; - } - case ICStub::GetName_Global: { - ICGetName_Global* globalStub = toGetName_Global(); - TraceEdge(trc, &globalStub->shape(), "baseline-global-stub-shape"); - break; - } - case ICStub::GetName_Scope0: - static_cast*>(this)->traceScopes(trc); - break; - case ICStub::GetName_Scope1: - static_cast*>(this)->traceScopes(trc); - break; - case ICStub::GetName_Scope2: - static_cast*>(this)->traceScopes(trc); - break; - case ICStub::GetName_Scope3: - static_cast*>(this)->traceScopes(trc); - break; - case ICStub::GetName_Scope4: - static_cast*>(this)->traceScopes(trc); - break; - case ICStub::GetName_Scope5: - static_cast*>(this)->traceScopes(trc); - break; - case ICStub::GetName_Scope6: - static_cast*>(this)->traceScopes(trc); - break; - case ICStub::GetIntrinsic_Constant: { - ICGetIntrinsic_Constant* constantStub = toGetIntrinsic_Constant(); - TraceEdge(trc, &constantStub->value(), "baseline-getintrinsic-constant-value"); - break; - } - case ICStub::GetProp_Primitive: { - ICGetProp_Primitive* propStub = toGetProp_Primitive(); - TraceEdge(trc, &propStub->protoShape(), "baseline-getprop-primitive-stub-shape"); - break; - } - case ICStub::GetProp_Native: { - ICGetProp_Native* propStub = toGetProp_Native(); - propStub->receiverGuard().trace(trc); - break; - } - case ICStub::GetProp_NativePrototype: { - ICGetProp_NativePrototype* propStub = toGetProp_NativePrototype(); - propStub->receiverGuard().trace(trc); - TraceEdge(trc, &propStub->holder(), "baseline-getpropnativeproto-stub-holder"); - TraceEdge(trc, &propStub->holderShape(), "baseline-getpropnativeproto-stub-holdershape"); - break; - } - case ICStub::GetProp_NativeDoesNotExist: { - ICGetProp_NativeDoesNotExist* propStub = toGetProp_NativeDoesNotExist(); - propStub->guard().trace(trc); - JS_STATIC_ASSERT(ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8); - switch (propStub->protoChainDepth()) { - case 0: propStub->toImpl<0>()->traceShapes(trc); break; - case 1: propStub->toImpl<1>()->traceShapes(trc); break; - case 2: propStub->toImpl<2>()->traceShapes(trc); break; - case 3: propStub->toImpl<3>()->traceShapes(trc); break; - case 4: propStub->toImpl<4>()->traceShapes(trc); break; - case 5: propStub->toImpl<5>()->traceShapes(trc); break; - case 6: propStub->toImpl<6>()->traceShapes(trc); break; - case 7: propStub->toImpl<7>()->traceShapes(trc); break; - case 8: propStub->toImpl<8>()->traceShapes(trc); break; - default: MOZ_CRASH("Invalid proto stub."); - } - break; - } - case ICStub::GetProp_Unboxed: { - ICGetProp_Unboxed* propStub = toGetProp_Unboxed(); - TraceEdge(trc, &propStub->group(), "baseline-getprop-unboxed-stub-group"); - break; - } - case ICStub::GetProp_TypedObject: { - ICGetProp_TypedObject* propStub = toGetProp_TypedObject(); - TraceEdge(trc, &propStub->shape(), "baseline-getprop-typedobject-stub-shape"); - break; - } - case ICStub::GetProp_CallDOMProxyNative: - case ICStub::GetProp_CallDOMProxyWithGenerationNative: { - ICGetPropCallDOMProxyNativeStub* propStub; - if (kind() == ICStub::GetProp_CallDOMProxyNative) - propStub = toGetProp_CallDOMProxyNative(); - else - propStub = toGetProp_CallDOMProxyWithGenerationNative(); - propStub->receiverGuard().trace(trc); - if (propStub->expandoShape()) { - TraceEdge(trc, &propStub->expandoShape(), - "baseline-getproplistbasenative-stub-expandoshape"); - } - TraceEdge(trc, &propStub->holder(), "baseline-getproplistbasenative-stub-holder"); - TraceEdge(trc, &propStub->holderShape(), "baseline-getproplistbasenative-stub-holdershape"); - TraceEdge(trc, &propStub->getter(), "baseline-getproplistbasenative-stub-getter"); - break; - } - case ICStub::GetProp_DOMProxyShadowed: { - ICGetProp_DOMProxyShadowed* propStub = toGetProp_DOMProxyShadowed(); - TraceEdge(trc, &propStub->shape(), "baseline-getproplistbaseshadowed-stub-shape"); - TraceEdge(trc, &propStub->name(), "baseline-getproplistbaseshadowed-stub-name"); - break; - } - case ICStub::GetProp_CallScripted: { - ICGetProp_CallScripted* callStub = toGetProp_CallScripted(); - callStub->receiverGuard().trace(trc); - TraceEdge(trc, &callStub->holder(), "baseline-getpropcallscripted-stub-holder"); - TraceEdge(trc, &callStub->holderShape(), "baseline-getpropcallscripted-stub-holdershape"); - TraceEdge(trc, &callStub->getter(), "baseline-getpropcallscripted-stub-getter"); - break; - } - case ICStub::GetProp_CallNative: { - ICGetProp_CallNative* callStub = toGetProp_CallNative(); - callStub->receiverGuard().trace(trc); - TraceEdge(trc, &callStub->holder(), "baseline-getpropcallnative-stub-holder"); - TraceEdge(trc, &callStub->holderShape(), "baseline-getpropcallnative-stub-holdershape"); - TraceEdge(trc, &callStub->getter(), "baseline-getpropcallnative-stub-getter"); - break; - } - case ICStub::SetProp_Native: { - ICSetProp_Native* propStub = toSetProp_Native(); - TraceEdge(trc, &propStub->shape(), "baseline-setpropnative-stub-shape"); - TraceEdge(trc, &propStub->group(), "baseline-setpropnative-stub-group"); - break; - } - case ICStub::SetProp_NativeAdd: { - ICSetProp_NativeAdd* propStub = toSetProp_NativeAdd(); - TraceEdge(trc, &propStub->group(), "baseline-setpropnativeadd-stub-group"); - TraceEdge(trc, &propStub->newShape(), "baseline-setpropnativeadd-stub-newshape"); - if (propStub->newGroup()) - TraceEdge(trc, &propStub->newGroup(), "baseline-setpropnativeadd-stub-new-group"); - JS_STATIC_ASSERT(ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH == 4); - switch (propStub->protoChainDepth()) { - case 0: propStub->toImpl<0>()->traceShapes(trc); break; - case 1: propStub->toImpl<1>()->traceShapes(trc); break; - case 2: propStub->toImpl<2>()->traceShapes(trc); break; - case 3: propStub->toImpl<3>()->traceShapes(trc); break; - case 4: propStub->toImpl<4>()->traceShapes(trc); break; - default: MOZ_CRASH("Invalid proto stub."); - } - break; - } - case ICStub::SetProp_Unboxed: { - ICSetProp_Unboxed* propStub = toSetProp_Unboxed(); - TraceEdge(trc, &propStub->group(), "baseline-setprop-unboxed-stub-group"); - break; - } - case ICStub::SetProp_TypedObject: { - ICSetProp_TypedObject* propStub = toSetProp_TypedObject(); - TraceEdge(trc, &propStub->shape(), "baseline-setprop-typedobject-stub-shape"); - TraceEdge(trc, &propStub->group(), "baseline-setprop-typedobject-stub-group"); - break; - } - case ICStub::SetProp_CallScripted: { - ICSetProp_CallScripted* callStub = toSetProp_CallScripted(); - callStub->receiverGuard().trace(trc); - TraceEdge(trc, &callStub->holder(), "baseline-setpropcallscripted-stub-holder"); - TraceEdge(trc, &callStub->holderShape(), "baseline-setpropcallscripted-stub-holdershape"); - TraceEdge(trc, &callStub->setter(), "baseline-setpropcallscripted-stub-setter"); - break; - } - case ICStub::SetProp_CallNative: { - ICSetProp_CallNative* callStub = toSetProp_CallNative(); - callStub->receiverGuard().trace(trc); - TraceEdge(trc, &callStub->holder(), "baseline-setpropcallnative-stub-holder"); - TraceEdge(trc, &callStub->holderShape(), "baseline-setpropcallnative-stub-holdershape"); - TraceEdge(trc, &callStub->setter(), "baseline-setpropcallnative-stub-setter"); - break; - } - case ICStub::InstanceOf_Function: { - ICInstanceOf_Function* instanceofStub = toInstanceOf_Function(); - TraceEdge(trc, &instanceofStub->shape(), "baseline-instanceof-fun-shape"); - TraceEdge(trc, &instanceofStub->prototypeObject(), "baseline-instanceof-fun-prototype"); - break; - } - case ICStub::NewArray_Fallback: { - ICNewArray_Fallback* stub = toNewArray_Fallback(); - if (stub->templateObject()) - TraceEdge(trc, &stub->templateObject(), "baseline-newarray-template"); - TraceEdge(trc, &stub->templateGroup(), "baseline-newarray-template-group"); - break; - } - case ICStub::NewObject_Fallback: { - ICNewObject_Fallback* stub = toNewObject_Fallback(); - if (stub->templateObject()) - TraceEdge(trc, &stub->templateObject(), "baseline-newobject-template"); - break; - } - case ICStub::Rest_Fallback: { - ICRest_Fallback* stub = toRest_Fallback(); - TraceEdge(trc, &stub->templateObject(), "baseline-rest-template"); - break; - } - default: - break; - } -} - -void -ICFallbackStub::unlinkStub(Zone* zone, ICStub* prev, ICStub* stub) -{ - MOZ_ASSERT(stub->next()); - - // If stub is the last optimized stub, update lastStubPtrAddr. - if (stub->next() == this) { - MOZ_ASSERT(lastStubPtrAddr_ == stub->addressOfNext()); - if (prev) - lastStubPtrAddr_ = prev->addressOfNext(); - else - lastStubPtrAddr_ = icEntry()->addressOfFirstStub(); - *lastStubPtrAddr_ = this; - } else { - if (prev) { - MOZ_ASSERT(prev->next() == stub); - prev->setNext(stub->next()); - } else { - MOZ_ASSERT(icEntry()->firstStub() == stub); - icEntry()->setFirstStub(stub->next()); - } - } - - MOZ_ASSERT(numOptimizedStubs_ > 0); - numOptimizedStubs_--; - - if (zone->needsIncrementalBarrier()) { - // We are removing edges from ICStub to gcthings. Perform one final trace - // of the stub for incremental GC, as it must know about those edges. - stub->trace(zone->barrierTracer()); - } - - if (ICStub::CanMakeCalls(stub->kind()) && stub->isMonitored()) { - // This stub can make calls so we can return to it if it's on the stack. - // We just have to reset its firstMonitorStub_ field to avoid a stale - // pointer when purgeOptimizedStubs destroys all optimized monitor - // stubs (unlinked stubs won't be updated). - ICTypeMonitor_Fallback* monitorFallback = toMonitoredFallbackStub()->fallbackMonitorStub(); - stub->toMonitoredStub()->resetFirstMonitorStub(monitorFallback); - } - -#ifdef DEBUG - // Poison stub code to ensure we don't call this stub again. However, if this - // stub can make calls, a pointer to it may be stored in a stub frame on the - // stack, so we can't touch the stubCode_ or GC will crash when marking this - // pointer. - if (!ICStub::CanMakeCalls(stub->kind())) - stub->stubCode_ = (uint8_t*)0xbad; -#endif -} - -void -ICFallbackStub::unlinkStubsWithKind(JSContext* cx, ICStub::Kind kind) -{ - for (ICStubIterator iter = beginChain(); !iter.atEnd(); iter++) { - if (iter->kind() == kind) - iter.unlink(cx); - } -} - -void -ICTypeMonitor_Fallback::resetMonitorStubChain(Zone* zone) -{ - if (zone->needsIncrementalBarrier()) { - // We are removing edges from monitored stubs to gcthings (JitCode). - // Perform one final trace of all monitor stubs for incremental GC, - // as it must know about those edges. - for (ICStub* s = firstMonitorStub_; !s->isTypeMonitor_Fallback(); s = s->next()) - s->trace(zone->barrierTracer()); - } - - firstMonitorStub_ = this; - numOptimizedMonitorStubs_ = 0; - - if (hasFallbackStub_) { - lastMonitorStubPtrAddr_ = nullptr; - - // Reset firstMonitorStub_ field of all monitored stubs. - for (ICStubConstIterator iter = mainFallbackStub_->beginChainConst(); - !iter.atEnd(); iter++) - { - if (!iter->isMonitored()) - continue; - iter->toMonitoredStub()->resetFirstMonitorStub(this); - } - } else { - icEntry_->setFirstStub(this); - lastMonitorStubPtrAddr_ = icEntry_->addressOfFirstStub(); - } -} - -ICMonitoredStub::ICMonitoredStub(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub) - : ICStub(kind, ICStub::Monitored, stubCode), - firstMonitorStub_(firstMonitorStub) -{ - // If the first monitored stub is a ICTypeMonitor_Fallback stub, then - // double check that _its_ firstMonitorStub is the same as this one. - MOZ_ASSERT_IF(firstMonitorStub_->isTypeMonitor_Fallback(), - firstMonitorStub_->toTypeMonitor_Fallback()->firstMonitorStub() == - firstMonitorStub_); -} - -bool -ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx, ICStubSpace* space) -{ - MOZ_ASSERT(fallbackMonitorStub_ == nullptr); - - ICTypeMonitor_Fallback::Compiler compiler(cx, this); - ICTypeMonitor_Fallback* stub = compiler.getStub(space); - if (!stub) - return false; - fallbackMonitorStub_ = stub; - return true; -} - -bool -ICMonitoredFallbackStub::addMonitorStubForValue(JSContext* cx, JSScript* script, HandleValue val) -{ - return fallbackMonitorStub_->addMonitorStubForValue(cx, script, val); -} - -bool -ICUpdatedStub::initUpdatingChain(JSContext* cx, ICStubSpace* space) -{ - MOZ_ASSERT(firstUpdateStub_ == nullptr); - - ICTypeUpdate_Fallback::Compiler compiler(cx); - ICTypeUpdate_Fallback* stub = compiler.getStub(space); - if (!stub) - return false; - - firstUpdateStub_ = stub; - return true; -} - -JitCode* -ICStubCompiler::getStubCode() -{ - JitCompartment* comp = cx->compartment()->jitCompartment(); - - // Check for existing cached stubcode. - uint32_t stubKey = getKey(); - JitCode* stubCode = comp->getStubCode(stubKey); - if (stubCode) - return stubCode; - - // Compile new stubcode. - JitContext jctx(cx, nullptr); - MacroAssembler masm; -#ifdef JS_CODEGEN_ARM - masm.setSecondScratchReg(BaselineSecondScratchReg); -#endif - - if (!generateStubCode(masm)) - return nullptr; - Linker linker(masm); - AutoFlushICache afc("getStubCode"); - Rooted newStubCode(cx, linker.newCode(cx, BASELINE_CODE)); - if (!newStubCode) - return nullptr; - - // After generating code, run postGenerateStubCode() - if (!postGenerateStubCode(masm, newStubCode)) - return nullptr; - - // All barriers are emitted off-by-default, enable them if needed. - if (cx->zone()->needsIncrementalBarrier()) - newStubCode->togglePreBarriers(true); - - // Cache newly compiled stubcode. - if (!comp->putStubCode(stubKey, newStubCode)) - return nullptr; - - MOZ_ASSERT(entersStubFrame_ == ICStub::CanMakeCalls(kind)); - -#ifdef JS_ION_PERF - writePerfSpewerJitCodeProfile(newStubCode, "BaselineIC"); -#endif - - return newStubCode; -} - -bool -ICStubCompiler::tailCallVM(const VMFunction& fun, MacroAssembler& masm) -{ - JitCode* code = cx->runtime()->jitRuntime()->getVMWrapper(fun); - if (!code) - return false; - - MOZ_ASSERT(fun.expectTailCall == TailCall); - uint32_t argSize = fun.explicitStackSlots() * sizeof(void*); - EmitTailCallVM(code, masm, argSize); - return true; -} - -bool -ICStubCompiler::callVM(const VMFunction& fun, MacroAssembler& masm) -{ - JitCode* code = cx->runtime()->jitRuntime()->getVMWrapper(fun); - if (!code) - return false; - - MOZ_ASSERT(fun.expectTailCall == NonTailCall); - EmitCallVM(code, masm); - return true; -} - -bool -ICStubCompiler::callTypeUpdateIC(MacroAssembler& masm, uint32_t objectOffset) -{ - JitCode* code = cx->runtime()->jitRuntime()->getVMWrapper(DoTypeUpdateFallbackInfo); - if (!code) - return false; - - EmitCallTypeUpdateIC(masm, code, objectOffset); - return true; -} - -void -ICStubCompiler::enterStubFrame(MacroAssembler& masm, Register scratch) -{ - EmitEnterStubFrame(masm, scratch); -#ifdef DEBUG - entersStubFrame_ = true; -#endif -} - -void -ICStubCompiler::leaveStubFrame(MacroAssembler& masm, bool calledIntoIon) -{ - MOZ_ASSERT(entersStubFrame_); - EmitLeaveStubFrame(masm, calledIntoIon); -} - -inline bool -ICStubCompiler::emitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, ValueOperand val, - Register scratch, LiveGeneralRegisterSet saveRegs) -{ - Label skipBarrier; - masm.branchPtrInNurseryRange(Assembler::Equal, obj, scratch, &skipBarrier); - masm.branchValueIsNurseryObject(Assembler::NotEqual, val, scratch, &skipBarrier); - - // void PostWriteBarrier(JSRuntime* rt, JSObject* obj); -#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) - saveRegs.add(ICTailCallReg); -#endif - saveRegs.set() = GeneralRegisterSet::Intersect(saveRegs.set(), GeneralRegisterSet::Volatile()); - masm.PushRegsInMask(saveRegs); - masm.setupUnalignedABICall(2, scratch); - masm.movePtr(ImmPtr(cx->runtime()), scratch); - masm.passABIArg(scratch); - masm.passABIArg(obj); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier)); - masm.PopRegsInMask(saveRegs); - - masm.bind(&skipBarrier); - return true; -} - // // WarmUpCounter_Fallback // @@ -934,8 +183,8 @@ PrepareOsrTempData(JSContext* cx, ICWarmUpCounter_Fallback* stub, BaselineFrame* } static bool -DoWarmUpCounterFallback(JSContext* cx, ICWarmUpCounter_Fallback* stub, BaselineFrame* frame, - IonOsrTempData** infoPtr) +DoWarmUpCounterFallback(JSContext* cx, BaselineFrame* frame, ICWarmUpCounter_Fallback* stub, + IonOsrTempData** infoPtr) { MOZ_ASSERT(infoPtr); *infoPtr = nullptr; @@ -995,17 +244,15 @@ DoWarmUpCounterFallback(JSContext* cx, ICWarmUpCounter_Fallback* stub, BaselineF return true; } -typedef bool (*DoWarmUpCounterFallbackFn)(JSContext*, ICWarmUpCounter_Fallback*, BaselineFrame* frame, - IonOsrTempData** infoPtr); +typedef bool (*DoWarmUpCounterFallbackFn)(JSContext*, BaselineFrame*, + ICWarmUpCounter_Fallback*, IonOsrTempData** infoPtr); static const VMFunction DoWarmUpCounterFallbackInfo = FunctionInfo(DoWarmUpCounterFallback); bool ICWarmUpCounter_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { - // enterStubFrame is going to clobber the BaselineFrameReg, save it in R0.scratchReg() - // first. - masm.movePtr(BaselineFrameReg, R0.scratchReg()); + MOZ_ASSERT(engine_ == Engine::Baseline); // Push a stub frame so that we can perform a non-tail call. enterStubFrame(masm, R1.scratchReg()); @@ -1017,13 +264,11 @@ ICWarmUpCounter_Fallback::Compiler::generateStubCode(MacroAssembler& masm) masm.subPtr(Imm32(sizeof(void*)), BaselineStackReg); masm.push(BaselineStackReg); - // Push JitFrameLayout pointer. - masm.loadBaselineFramePtr(R0.scratchReg(), R0.scratchReg()); - masm.push(R0.scratchReg()); - // Push stub pointer. masm.push(ICStubReg); + pushFramePtr(masm, R0.scratchReg()); + if (!callVM(DoWarmUpCounterFallbackInfo, masm)) return false; @@ -1256,6 +501,7 @@ static const VMFunction DoTypeMonitorFallbackInfo = bool ICTypeMonitor_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); MOZ_ASSERT(R0 == JSReturnOperand); // Restore the tail call register. @@ -1263,7 +509,7 @@ ICTypeMonitor_Fallback::Compiler::generateStubCode(MacroAssembler& masm) masm.pushValue(R0); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoTypeMonitorFallbackInfo, masm); } @@ -1271,6 +517,8 @@ ICTypeMonitor_Fallback::Compiler::generateStubCode(MacroAssembler& masm) bool ICTypeMonitor_PrimitiveSet::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label success; if ((flags_ & TypeToFlag(JSVAL_TYPE_INT32)) && !(flags_ & TypeToFlag(JSVAL_TYPE_DOUBLE))) masm.branchTestInt32(Assembler::Equal, R0, &success); @@ -1314,6 +562,8 @@ ICTypeMonitor_PrimitiveSet::Compiler::generateStubCode(MacroAssembler& masm) bool ICTypeMonitor_SingleObject::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestObject(Assembler::NotEqual, R0, &failure); @@ -1332,6 +582,8 @@ ICTypeMonitor_SingleObject::Compiler::generateStubCode(MacroAssembler& masm) bool ICTypeMonitor_ObjectGroup::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestObject(Assembler::NotEqual, R0, &failure); @@ -1506,6 +758,8 @@ const VMFunction DoTypeUpdateFallbackInfo = bool ICTypeUpdate_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + // Just store false into R1.scratchReg() and return. masm.move32(Imm32(0), R1.scratchReg()); EmitReturnFromIC(masm); @@ -1515,6 +769,8 @@ ICTypeUpdate_Fallback::Compiler::generateStubCode(MacroAssembler& masm) bool ICTypeUpdate_PrimitiveSet::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label success; if ((flags_ & TypeToFlag(JSVAL_TYPE_INT32)) && !(flags_ & TypeToFlag(JSVAL_TYPE_DOUBLE))) masm.branchTestInt32(Assembler::Equal, R0, &success); @@ -1561,6 +817,8 @@ ICTypeUpdate_PrimitiveSet::Compiler::generateStubCode(MacroAssembler& masm) bool ICTypeUpdate_SingleObject::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestObject(Assembler::NotEqual, R0, &failure); @@ -1581,6 +839,8 @@ ICTypeUpdate_SingleObject::Compiler::generateStubCode(MacroAssembler& masm) bool ICTypeUpdate_ObjectGroup::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestObject(Assembler::NotEqual, R0, &failure); @@ -1649,6 +909,7 @@ static const VMFunction DoThisFallbackInfo = FunctionInfo(DoTh bool ICThis_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); MOZ_ASSERT(R0 == JSReturnOperand); // Restore the tail call register. @@ -1702,11 +963,13 @@ static const VMFunction DoNewArrayInfo = FunctionInfo(DoNewArray, bool ICNewArray_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + EmitRestoreTailCallReg(masm); masm.push(R0.scratchReg()); // length masm.push(ICStubReg); // stub. - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoNewArrayInfo, masm); } @@ -1800,10 +1063,12 @@ static const VMFunction DoNewObjectInfo = FunctionInfo(DoNewObjec bool ICNewObject_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + EmitRestoreTailCallReg(masm); masm.push(ICStubReg); // stub. - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoNewObjectInfo, masm); } @@ -2013,6 +1278,8 @@ static const VMFunction DoCompareFallbackInfo = bool ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + MOZ_ASSERT(R0 == JSReturnOperand); // Restore the tail call register. @@ -2026,7 +1293,7 @@ ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler& masm) masm.pushValue(R1); masm.pushValue(R0); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoCompareFallbackInfo, masm); } @@ -2037,6 +1304,8 @@ ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler& masm) bool ICCompare_String::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestString(Assembler::NotEqual, R0, &failure); masm.branchTestString(Assembler::NotEqual, R1, &failure); @@ -2065,6 +1334,8 @@ ICCompare_String::Compiler::generateStubCode(MacroAssembler& masm) bool ICCompare_Boolean::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestBoolean(Assembler::NotEqual, R0, &failure); masm.branchTestBoolean(Assembler::NotEqual, R1, &failure); @@ -2093,6 +1364,8 @@ ICCompare_Boolean::Compiler::generateStubCode(MacroAssembler& masm) bool ICCompare_NumberWithUndefined::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + ValueOperand numberOperand, undefinedOperand; if (lhsIsUndefined) { numberOperand = R1; @@ -2125,6 +1398,8 @@ ICCompare_NumberWithUndefined::Compiler::generateStubCode(MacroAssembler& masm) bool ICCompare_Object::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestObject(Assembler::NotEqual, R0, &failure); masm.branchTestObject(Assembler::NotEqual, R1, &failure); @@ -2157,6 +1432,7 @@ ICCompare_Object::Compiler::generateStubCode(MacroAssembler& masm) bool ICCompare_ObjectWithUndefined::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); MOZ_ASSERT(IsEqualityOp(op)); ValueOperand objectOperand, undefinedOperand; @@ -2222,6 +1498,8 @@ ICCompare_ObjectWithUndefined::Compiler::generateStubCode(MacroAssembler& masm) bool ICCompare_Int32WithBoolean::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; ValueOperand int32Val; ValueOperand boolVal; @@ -2348,6 +1626,7 @@ static const VMFunction fun = FunctionInfo(DoToBoolFallback, TailCall); bool ICToBool_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); MOZ_ASSERT(R0 == JSReturnOperand); // Restore the tail call register. @@ -2356,7 +1635,7 @@ ICToBool_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // Push arguments. masm.pushValue(R0); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(fun, masm); } @@ -2368,6 +1647,8 @@ ICToBool_Fallback::Compiler::generateStubCode(MacroAssembler& masm) bool ICToBool_Int32::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestInt32(Assembler::NotEqual, R0, &failure); @@ -2394,6 +1675,8 @@ ICToBool_Int32::Compiler::generateStubCode(MacroAssembler& masm) bool ICToBool_String::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestString(Assembler::NotEqual, R0, &failure); @@ -2420,6 +1703,8 @@ ICToBool_String::Compiler::generateStubCode(MacroAssembler& masm) bool ICToBool_NullUndefined::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure, ifFalse; masm.branchTestNull(Assembler::Equal, R0, &ifFalse); masm.branchTestUndefined(Assembler::NotEqual, R0, &failure); @@ -2441,6 +1726,8 @@ ICToBool_NullUndefined::Compiler::generateStubCode(MacroAssembler& masm) bool ICToBool_Double::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure, ifTrue; masm.branchTestDouble(Assembler::NotEqual, R0, &failure); masm.unboxDouble(R0, FloatReg0); @@ -2466,6 +1753,8 @@ ICToBool_Double::Compiler::generateStubCode(MacroAssembler& masm) bool ICToBool_Object::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure, ifFalse, slowPath; masm.branchTestObject(Assembler::NotEqual, R0, &failure); @@ -2515,6 +1804,7 @@ static const VMFunction DoToNumberFallbackInfo = bool ICToNumber_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); MOZ_ASSERT(R0 == JSReturnOperand); // Restore the tail call register. @@ -2758,6 +2048,7 @@ static const VMFunction DoBinaryArithFallbackInfo = bool ICBinaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); MOZ_ASSERT(R0 == JSReturnOperand); // Restore the tail call register. @@ -2771,7 +2062,7 @@ ICBinaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm) masm.pushValue(R1); masm.pushValue(R0); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoBinaryArithFallbackInfo, masm); } @@ -2793,6 +2084,8 @@ static const VMFunction DoConcatStringsInfo = FunctionInfo(Do bool ICBinaryArith_StringConcat::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestString(Assembler::NotEqual, R0, &failure); masm.branchTestString(Assembler::NotEqual, R1, &failure); @@ -2873,6 +2166,8 @@ static const VMFunction DoConcatStringObjectInfo = bool ICBinaryArith_StringObjectConcat::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; if (lhsIsString_) { masm.branchTestString(Assembler::NotEqual, R0, &failure); @@ -2905,6 +2200,8 @@ ICBinaryArith_StringObjectConcat::Compiler::generateStubCode(MacroAssembler& mas bool ICBinaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.ensureDouble(R0, FloatReg0, &failure); masm.ensureDouble(R1, FloatReg1, &failure); @@ -2945,6 +2242,8 @@ ICBinaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm) bool ICBinaryArith_BooleanWithInt32::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; if (lhsIsBool_) masm.branchTestBoolean(Assembler::NotEqual, R0, &failure); @@ -3020,6 +2319,7 @@ ICBinaryArith_BooleanWithInt32::Compiler::generateStubCode(MacroAssembler& masm) bool ICBinaryArith_DoubleWithInt32::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); MOZ_ASSERT(op == JSOP_BITOR || op == JSOP_BITAND || op == JSOP_BITXOR); Label failure; @@ -3160,6 +2460,7 @@ static const VMFunction DoUnaryArithFallbackInfo = bool ICUnaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); MOZ_ASSERT(R0 == JSReturnOperand); // Restore the tail call register. @@ -3171,7 +2472,7 @@ ICUnaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // Push arguments. masm.pushValue(R0); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoUnaryArithFallbackInfo, masm); } @@ -3179,6 +2480,8 @@ ICUnaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm) bool ICUnaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.ensureDouble(R0, FloatReg0, &failure); @@ -4257,6 +3560,7 @@ static const VMFunction DoGetElemFallbackInfo = bool ICGetElem_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); MOZ_ASSERT(R0 == JSReturnOperand); // Restore the tail call register. @@ -4270,7 +3574,7 @@ ICGetElem_Fallback::Compiler::generateStubCode(MacroAssembler& masm) masm.pushValue(R1); masm.pushValue(R0); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoGetElemFallbackInfo, masm); } @@ -4400,6 +3704,8 @@ ICGetElemNativeCompiler::emitCallScripted(MacroAssembler& masm, Register objReg) bool ICGetElemNativeCompiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; Label failurePopR1; bool popR1 = false; @@ -4623,6 +3929,8 @@ ICGetElemNativeCompiler::generateStubCode(MacroAssembler& masm) bool ICGetElem_String::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestString(Assembler::NotEqual, R0, &failure); masm.branchTestInt32(Assembler::NotEqual, R1, &failure); @@ -4671,6 +3979,8 @@ ICGetElem_String::Compiler::generateStubCode(MacroAssembler& masm) bool ICGetElem_Dense::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestObject(Assembler::NotEqual, R0, &failure); masm.branchTestInt32(Assembler::NotEqual, R1, &failure); @@ -4770,6 +4080,8 @@ ICGetElem_Dense::Compiler::generateStubCode(MacroAssembler& masm) bool ICGetElem_UnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestObject(Assembler::NotEqual, R0, &failure); masm.branchTestInt32(Assembler::NotEqual, R1, &failure); @@ -4864,6 +4176,8 @@ CheckForNeuteredTypedObject(JSContext* cx, MacroAssembler& masm, Label* failure) bool ICGetElem_TypedArray::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; if (layout_ != Layout_TypedArray) @@ -4926,6 +4240,8 @@ ICGetElem_TypedArray::Compiler::generateStubCode(MacroAssembler& masm) bool ICGetElem_Arguments::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + // Variants of GetElem_Arguments can enter stub frames if entered in CallProp // context when noSuchMethod support is on. #if JS_HAS_NO_SUCH_METHOD @@ -5436,6 +4752,7 @@ static const VMFunction DoSetElemFallbackInfo = bool ICSetElem_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); MOZ_ASSERT(R0 == JSReturnOperand); EmitRestoreTailCallReg(masm); @@ -5464,7 +4781,7 @@ ICSetElem_Fallback::Compiler::generateStubCode(MacroAssembler& masm) masm.push(R0.scratchReg()); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoSetElemFallbackInfo, masm); } @@ -5498,6 +4815,8 @@ EmitUnboxedPreBarrierForBaseline(MacroAssembler &masm, T address, JSValueType ty bool ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + // R0 = object // R1 = key // Stack = { ... rhs-value, ? } @@ -5691,6 +5010,8 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::getStub(ICStubSpace* space) bool ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + // R0 = object // R1 = key // Stack = { ... rhs-value, ? } @@ -5957,6 +5278,8 @@ StoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type, Addres bool ICSetElem_TypedArray::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; if (layout_ != Layout_TypedArray) @@ -6188,6 +5511,8 @@ static const VMFunction DoInFallbackInfo = bool ICIn_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + EmitRestoreTailCallReg(masm); // Sync for the decompiler. @@ -6198,7 +5523,7 @@ ICIn_Fallback::Compiler::generateStubCode(MacroAssembler& masm) masm.pushValue(R1); masm.pushValue(R0); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoInFallbackInfo, masm); } @@ -6206,6 +5531,8 @@ ICIn_Fallback::Compiler::generateStubCode(MacroAssembler& masm) bool ICInNativeCompiler::generateStubCode(MacroAssembler &masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure, failurePopR0Scratch; masm.branchTestString(Assembler::NotEqual, R0, &failure); @@ -6281,6 +5608,8 @@ ICInNativeDoesNotExistCompiler::getStub(ICStubSpace* space) bool ICInNativeDoesNotExistCompiler::generateStubCode(MacroAssembler &masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure, failurePopR0Scratch; masm.branchTestString(Assembler::NotEqual, R0, &failure); @@ -6338,6 +5667,8 @@ ICInNativeDoesNotExistCompiler::generateStubCode(MacroAssembler &masm) bool ICIn_Dense::Compiler::generateStubCode(MacroAssembler &masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestInt32(Assembler::NotEqual, R0, &failure); @@ -6744,13 +6075,14 @@ static const VMFunction DoGetNameFallbackInfo = FunctionInfo bool ICGetName_Scope::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); Register obj = R0.scratchReg(); @@ -6855,13 +6191,14 @@ static const VMFunction DoBindNameFallbackInfo = bool ICBindName_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); MOZ_ASSERT(R0 == JSReturnOperand); EmitRestoreTailCallReg(masm); masm.push(R0.scratchReg()); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoBindNameFallbackInfo, masm); } @@ -6915,10 +6252,12 @@ static const VMFunction DoGetIntrinsicFallbackInfo = bool ICGetIntrinsic_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + EmitRestoreTailCallReg(masm); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoGetIntrinsicFallbackInfo, masm); } @@ -6926,6 +6265,8 @@ ICGetIntrinsic_Fallback::Compiler::generateStubCode(MacroAssembler& masm) bool ICGetIntrinsic_Constant::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + masm.loadValue(Address(ICStubReg, ICGetIntrinsic_Constant::offsetOfValue()), R0); EmitReturnFromIC(masm); @@ -7553,27 +6894,19 @@ ComputeGetPropResult(JSContext* cx, BaselineFrame* frame, JSOp op, HandlePropert res.setObject(*frame->callee()); } } else { - // Handle when val is an object. - RootedObject obj(cx, ToObjectFromStack(cx, val)); - if (!obj) - return false; - - RootedId id(cx, NameToId(name)); - if (op == JSOP_GETXPROP) { - if (!GetPropertyForNameLookup(cx, obj, id, res)) + if (op == JSOP_GETPROP || op == JSOP_LENGTH) { + if (!GetProperty(cx, val, name, res)) + return false; + } else if (op == JSOP_CALLPROP) { + if (!CallProperty(cx, val, name, res)) return false; } else { - if (!GetProperty(cx, obj, obj, id, res)) + MOZ_ASSERT(op == JSOP_GETXPROP); + RootedObject obj(cx, &val.toObject()); + RootedId id(cx, NameToId(name)); + if (!GetPropertyForNameLookup(cx, obj, id, res)) return false; } - -#if JS_HAS_NO_SUCH_METHOD - // Handle objects with __noSuchMethod__. - if (op == JSOP_CALLPROP && MOZ_UNLIKELY(res.isUndefined()) && val.isObject()) { - if (!OnUnknownMethod(cx, obj, IdToValue(id), res)) - return false; - } -#endif } return true; @@ -7698,6 +7031,7 @@ static const VMFunction DoGetPropFallbackInfo = bool ICGetProp_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); MOZ_ASSERT(R0 == JSReturnOperand); EmitRestoreTailCallReg(masm); @@ -7708,7 +7042,7 @@ ICGetProp_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // Push arguments. masm.pushValue(R0); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); if (!tailCallVM(DoGetPropFallbackInfo, masm)) return false; @@ -7719,6 +7053,7 @@ ICGetProp_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // Even though the fallback frame doesn't enter a stub frame, the CallScripted // frame that we are emulating does. Again, we lie. + inStubFrame_ = true; #ifdef DEBUG entersStubFrame_ = true; #endif @@ -7747,6 +7082,8 @@ ICGetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm, Handle< bool ICGetProp_ArrayLength::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestObject(Assembler::NotEqual, R0, &failure); @@ -7775,6 +7112,8 @@ ICGetProp_ArrayLength::Compiler::generateStubCode(MacroAssembler& masm) bool ICGetProp_UnboxedArrayLength::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestObject(Assembler::NotEqual, R0, &failure); @@ -7799,6 +7138,8 @@ ICGetProp_UnboxedArrayLength::Compiler::generateStubCode(MacroAssembler& masm) bool ICGetProp_StringLength::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; masm.branchTestString(Assembler::NotEqual, R0, &failure); @@ -7818,6 +7159,8 @@ ICGetProp_StringLength::Compiler::generateStubCode(MacroAssembler& masm) bool ICGetProp_Primitive::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; switch (primitiveType_) { case JSVAL_TYPE_STRING: @@ -7926,6 +7269,8 @@ GuardReceiverObject(MacroAssembler& masm, ReceiverGuard guard, bool ICGetPropNativeCompiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); Register objReg = InvalidReg; @@ -8070,6 +7415,8 @@ ICGetPropNativeDoesNotExistCompiler::getStub(ICStubSpace* space) bool ICGetPropNativeDoesNotExistCompiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); @@ -8120,6 +7467,8 @@ ICGetPropNativeDoesNotExistCompiler::generateStubCode(MacroAssembler& masm) bool ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; Label failureLeaveStubFrame; AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); @@ -8198,6 +7547,7 @@ ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) // Leave stub frame and go to next stub. masm.bind(&failureLeaveStubFrame); + inStubFrame_ = true; leaveStubFrame(masm, false); // Failure case - jump to next stub @@ -8209,6 +7559,8 @@ ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) bool ICGetProp_CallNative::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); @@ -8283,6 +7635,8 @@ ICGetPropCallDOMProxyNativeCompiler::generateStubCode(MacroAssembler& masm, Address* expandoAndGenerationAddr, Address* generationAddr) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); Register scratch = regs.takeAnyExcluding(ICTailCallReg); @@ -8353,6 +7707,8 @@ ICGetPropCallDOMProxyNativeCompiler::generateStubCode(MacroAssembler& masm, bool ICGetPropCallDOMProxyNativeCompiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + if (kind == ICStub::GetProp_CallDOMProxyNative) return generateStubCode(masm, nullptr, nullptr); @@ -8423,6 +7779,8 @@ static const VMFunction ProxyGetInfo = FunctionInfo(ProxyGet); bool ICGetProp_DOMProxyShadowed::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); @@ -8473,6 +7831,8 @@ ICGetProp_DOMProxyShadowed::Compiler::generateStubCode(MacroAssembler& masm) bool ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; if (which_ == ICGetProp_ArgumentsLength::Magic) { // Ensure that this is lazy arguments. @@ -8533,6 +7893,8 @@ ICGetProp_ArgumentsCallee::ICGetProp_ArgumentsCallee(JitCode* stubCode, ICStub* bool ICGetProp_ArgumentsCallee::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; // Ensure that this is lazy arguments. @@ -8577,6 +7939,8 @@ static const VMFunction DoGetPropGenericInfo = FunctionInfo( bool ICGetProp_Generic::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); Register scratch = regs.takeAnyExcluding(ICTailCallReg); @@ -8589,8 +7953,7 @@ ICGetProp_Generic::Compiler::generateStubCode(MacroAssembler& masm) // Push arguments. masm.pushValue(R0); masm.push(ICStubReg); - masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg()); - masm.pushBaselineFramePtr(R0.scratchReg(), R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); if(!callVM(DoGetPropGenericInfo, masm)) return false; @@ -8604,6 +7967,8 @@ ICGetProp_Generic::Compiler::generateStubCode(MacroAssembler& masm) bool ICGetProp_Unboxed::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); @@ -8637,6 +8002,8 @@ ICGetProp_Unboxed::Compiler::generateStubCode(MacroAssembler& masm) bool ICGetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; CheckForNeuteredTypedObject(cx, masm, &failure); @@ -9118,6 +8485,7 @@ static const VMFunction DoSetPropFallbackInfo = bool ICSetProp_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); MOZ_ASSERT(R0 == JSReturnOperand); EmitRestoreTailCallReg(masm); @@ -9130,7 +8498,7 @@ ICSetProp_Fallback::Compiler::generateStubCode(MacroAssembler& masm) masm.pushValue(R1); masm.pushValue(R0); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); if (!tailCallVM(DoSetPropFallbackInfo, masm)) return false; @@ -9141,6 +8509,7 @@ ICSetProp_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // Even though the fallback frame doesn't enter a stub frame, the CallScripted // frame that we are emulating does. Again, we lie. + inStubFrame_ = true; #ifdef DEBUG entersStubFrame_ = true; #endif @@ -9194,6 +8563,8 @@ GuardGroupAndShapeMaybeUnboxedExpando(MacroAssembler& masm, JSObject* obj, bool ICSetProp_Native::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; // Guard input is an object. @@ -9291,6 +8662,8 @@ ICSetPropNativeAddCompiler::getStub(ICStubSpace* space) bool ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; Label failureUnstow; @@ -9424,6 +8797,8 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm) bool ICSetProp_Unboxed::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; // Guard input is an object. @@ -9487,6 +8862,8 @@ ICSetProp_Unboxed::Compiler::generateStubCode(MacroAssembler& masm) bool ICSetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; CheckForNeuteredTypedObject(cx, masm, &failure); @@ -9609,6 +8986,8 @@ ICSetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm) bool ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; Label failureUnstow; Label failureLeaveStubFrame; @@ -9698,6 +9077,7 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) // Leave stub frame and go to next stub. masm.bind(&failureLeaveStubFrame); + inStubFrame_ = true; leaveStubFrame(masm, false); // Unstow R0 and R1 @@ -9731,6 +9111,8 @@ static const VMFunction DoCallNativeSetterInfo = bool ICSetProp_CallNative::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; Label failureUnstow; @@ -10780,10 +10162,9 @@ static const VMFunction DoSpreadCallFallbackInfo = bool ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { - MOZ_ASSERT(R0 == JSReturnOperand); + MOZ_ASSERT(engine_ == Engine::Baseline); - // Push a stub frame so that we can perform a non-tail call. - enterStubFrame(masm, R1.scratchReg()); + MOZ_ASSERT(R0 == JSReturnOperand); // Values are on the stack left-to-right. Calling convention wants them // right-to-left so duplicate them on the stack in reverse order. @@ -10792,6 +10173,9 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm) AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); if (MOZ_UNLIKELY(isSpread_)) { + // Push a stub frame so that we can perform a non-tail call. + enterStubFrame(masm, R1.scratchReg()); + // Use BaselineFrameReg instead of BaselineStackReg, because // BaselineFrameReg and BaselineStackReg hold the same value just after // calling enterStubFrame. @@ -10813,8 +10197,7 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm) masm.push(BaselineStackReg); masm.push(ICStubReg); - masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg()); - masm.pushBaselineFramePtr(R0.scratchReg(), R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); if (!callVM(DoSpreadCallFallbackInfo, masm)) return false; @@ -10827,6 +10210,9 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm) return true; } + // Push a stub frame so that we can perform a non-tail call. + enterStubFrame(masm, R1.scratchReg()); + regs.take(R0.scratchReg()); // argc. pushCallArguments(masm, regs, R0.scratchReg(), /* isJitCall = */ false, isConstructing_); @@ -10835,9 +10221,7 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm) masm.push(R0.scratchReg()); masm.push(ICStubReg); - // Load previous frame pointer, push BaselineFrame*. - masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg()); - masm.pushBaselineFramePtr(R0.scratchReg(), R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); if (!callVM(DoCallFallbackInfo, masm)) return false; @@ -10850,6 +10234,9 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // reconstructed baseline stack points here. returnOffset_ = masm.currentOffset(); + // Here we are again in a stub frame. Marking as so. + inStubFrame_ = true; + // Load passed-in ThisV into R1 just in case it's needed. Need to do this before // we leave the stub frame since that info will be lost. // Current stack: [...., ThisV, ActualArgc, CalleeToken, Descriptor ] @@ -10902,6 +10289,8 @@ static const VMFunction CreateThisInfoBaseline = FunctionInfo(Crea bool ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); bool canUseTailCallReg = regs.has(ICTailCallReg); @@ -11160,6 +10549,7 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm) // Leave stub frame and restore argc for the next stub. masm.bind(&failureLeaveStubFrame); + inStubFrame_ = true; leaveStubFrame(masm, false); if (argcReg != R0.scratchReg()) masm.mov(argcReg, R0.scratchReg()); @@ -11175,6 +10565,8 @@ static const VMFunction CopyArrayInfo = FunctionInfo(CopyArray); bool ICCall_StringSplit::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + // Stack Layout: [ ..., CalleeVal, ThisVal, Arg0Val, +ICStackValueOffset+ ] AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); Label failureRestoreArgc; @@ -11266,6 +10658,8 @@ ICCall_StringSplit::Compiler::generateStubCode(MacroAssembler& masm) bool ICCall_IsSuspendedStarGenerator::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + // The IsSuspendedStarGenerator intrinsic is only called in self-hosted // code, so it's safe to assume we have a single argument and the callee // is our intrinsic. @@ -11308,6 +10702,8 @@ ICCall_IsSuspendedStarGenerator::Compiler::generateStubCode(MacroAssembler& masm bool ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); @@ -11413,6 +10809,8 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm) bool ICCall_ClassHook::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); @@ -11501,6 +10899,8 @@ ICCall_ClassHook::Compiler::generateStubCode(MacroAssembler& masm) bool ICCall_ScriptedApplyArray::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); @@ -11603,6 +11003,8 @@ ICCall_ScriptedApplyArray::Compiler::generateStubCode(MacroAssembler& masm) bool ICCall_ScriptedApplyArguments::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); @@ -11699,6 +11101,8 @@ ICCall_ScriptedApplyArguments::Compiler::generateStubCode(MacroAssembler& masm) bool ICCall_ScriptedFunCall::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); bool canUseTailCallReg = regs.has(ICTailCallReg); @@ -11836,6 +11240,8 @@ DoubleValueToInt32ForSwitch(Value* v) bool ICTableSwitch::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label isInt32, notInt32, outOfRange; Register scratch = R1.scratchReg(); @@ -11957,6 +11363,8 @@ static const VMFunction DoIteratorNewFallbackInfo = bool ICIteratorNew_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + EmitRestoreTailCallReg(masm); // Sync stack for the decompiler. @@ -11964,7 +11372,7 @@ ICIteratorNew_Fallback::Compiler::generateStubCode(MacroAssembler& masm) masm.pushValue(R0); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoIteratorNewFallbackInfo, masm); } @@ -12013,12 +11421,14 @@ static const VMFunction DoIteratorMoreFallbackInfo = bool ICIteratorMore_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + EmitRestoreTailCallReg(masm); masm.unboxObject(R0, R0.scratchReg()); masm.push(R0.scratchReg()); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoIteratorMoreFallbackInfo, masm); } @@ -12030,6 +11440,8 @@ ICIteratorMore_Fallback::Compiler::generateStubCode(MacroAssembler& masm) bool ICIteratorMore_Native::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; Register obj = masm.extractObject(R0, ExtractTemp0); @@ -12092,6 +11504,8 @@ static const VMFunction DoIteratorCloseFallbackInfo = bool ICIteratorClose_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + EmitRestoreTailCallReg(masm); masm.pushValue(R0); @@ -12194,6 +11608,8 @@ static const VMFunction DoInstanceOfFallbackInfo = bool ICInstanceOf_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + EmitRestoreTailCallReg(masm); // Sync stack for the decompiler. @@ -12203,7 +11619,7 @@ ICInstanceOf_Fallback::Compiler::generateStubCode(MacroAssembler& masm) masm.pushValue(R1); masm.pushValue(R0); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoInstanceOfFallbackInfo, masm); } @@ -12211,6 +11627,8 @@ ICInstanceOf_Fallback::Compiler::generateStubCode(MacroAssembler& masm) bool ICInstanceOf_Function::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + Label failure; // Ensure RHS is an object. @@ -12318,11 +11736,13 @@ static const VMFunction DoTypeOfFallbackInfo = bool ICTypeOf_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + EmitRestoreTailCallReg(masm); masm.pushValue(R0); masm.push(ICStubReg); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoTypeOfFallbackInfo, masm); } @@ -12330,6 +11750,7 @@ ICTypeOf_Fallback::Compiler::generateStubCode(MacroAssembler& masm) bool ICTypeOf_Typed::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); MOZ_ASSERT(type_ != JSTYPE_NULL); MOZ_ASSERT(type_ != JSTYPE_FUNCTION); MOZ_ASSERT(type_ != JSTYPE_OBJECT); @@ -12409,6 +11830,8 @@ static const VMFunction ThrowInfoBaseline = FunctionInfo(js::Throw, Tai bool ICRetSub_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + // If R0 is BooleanValue(true), rethrow R1. Label rethrow; masm.branchTestBooleanTruthy(true, R0, &rethrow); @@ -12417,15 +11840,13 @@ ICRetSub_Fallback::Compiler::generateStubCode(MacroAssembler& masm) AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); regs.take(R1); regs.takeUnchecked(ICTailCallReg); + Register scratch = regs.getAny(); - Register frame = regs.takeAny(); - masm.movePtr(BaselineFrameReg, frame); - - enterStubFrame(masm, regs.getAny()); + enterStubFrame(masm, scratch); masm.pushValue(R1); masm.push(ICStubReg); - masm.pushBaselineFramePtr(frame, frame); + pushFramePtr(masm, scratch); if (!callVM(DoRetSubFallbackInfo, masm)) return false; @@ -12445,6 +11866,8 @@ ICRetSub_Fallback::Compiler::generateStubCode(MacroAssembler& masm) bool ICRetSub_Resume::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + // If R0 is BooleanValue(true), rethrow R1. Label fail, rethrow; masm.branchTestBooleanTruthy(true, R0, &rethrow); @@ -12688,7 +12111,7 @@ ICIn_NativeDoesNotExistImpl::ICIn_NativeDoesNotExistImpl( ICInNativeDoesNotExistCompiler::ICInNativeDoesNotExistCompiler( JSContext* cx, HandleObject obj, HandlePropertyName name, size_t protoChainDepth) - : ICStubCompiler(cx, ICStub::In_NativeDoesNotExist), + : ICStubCompiler(cx, ICStub::In_NativeDoesNotExist, Engine::Baseline), obj_(cx, obj), name_(cx, name), protoChainDepth_(protoChainDepth) @@ -12806,7 +12229,7 @@ ICGetProp_NativeDoesNotExistImpl::ICGetProp_NativeDoesNotExistI ICGetPropNativeDoesNotExistCompiler::ICGetPropNativeDoesNotExistCompiler( JSContext* cx, ICStub* firstMonitorStub, HandleObject obj, size_t protoChainDepth) - : ICStubCompiler(cx, ICStub::GetProp_NativeDoesNotExist), + : ICStubCompiler(cx, ICStub::GetProp_NativeDoesNotExist, Engine::Baseline), firstMonitorStub_(firstMonitorStub), obj_(cx, obj), protoChainDepth_(protoChainDepth) @@ -12915,7 +12338,7 @@ ICSetPropNativeAddCompiler::ICSetPropNativeAddCompiler(JSContext* cx, HandleObje size_t protoChainDepth, bool isFixedSlot, uint32_t offset) - : ICStubCompiler(cx, ICStub::SetProp_NativeAdd), + : ICStubCompiler(cx, ICStub::SetProp_NativeAdd, Engine::Baseline), obj_(cx, obj), oldShape_(cx, oldShape), oldGroup_(cx, oldGroup), @@ -13080,7 +12503,7 @@ ICGetPropCallDOMProxyNativeCompiler::ICGetPropCallDOMProxyNativeCompiler(JSConte HandleObject holder, HandleFunction getter, uint32_t pcOffset) - : ICStubCompiler(cx, kind), + : ICStubCompiler(cx, kind, Engine::Baseline), firstMonitorStub_(firstMonitorStub), proxy_(cx, proxy), holder_(cx, holder), @@ -13144,8 +12567,8 @@ ICGetProp_DOMProxyShadowed::Clone(JSContext* cx, ICStubSpace* space, ICStub* fir // Rest_Fallback // -static bool DoRestFallback(JSContext* cx, ICRest_Fallback* stub, - BaselineFrame* frame, MutableHandleValue res) +static bool DoRestFallback(JSContext* cx, BaselineFrame* frame, ICRest_Fallback* stub, + MutableHandleValue res) { unsigned numFormals = frame->numFormalArgs() - 1; unsigned numActuals = frame->numActualArgs(); @@ -13160,7 +12583,7 @@ static bool DoRestFallback(JSContext* cx, ICRest_Fallback* stub, return true; } -typedef bool (*DoRestFallbackFn)(JSContext*, ICRest_Fallback*, BaselineFrame*, +typedef bool (*DoRestFallbackFn)(JSContext*, BaselineFrame*, ICRest_Fallback*, MutableHandleValue); static const VMFunction DoRestFallbackInfo = FunctionInfo(DoRestFallback, TailCall); @@ -13168,10 +12591,12 @@ static const VMFunction DoRestFallbackInfo = bool ICRest_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { + MOZ_ASSERT(engine_ == Engine::Baseline); + EmitRestoreTailCallReg(masm); - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); masm.push(ICStubReg); + pushFramePtr(masm, R0.scratchReg()); return tailCallVM(DoRestFallbackInfo, masm); } diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index 6806c4af39..3526bea386 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -15,7 +15,9 @@ #include "jsopcode.h" #include "builtin/TypedObject.h" +#include "jit/BaselineICList.h" #include "jit/BaselineJIT.h" +#include "jit/SharedIC.h" #include "jit/SharedICRegisters.h" #include "vm/ArrayObject.h" #include "vm/ReceiverGuard.h" @@ -25,1187 +27,6 @@ namespace js { namespace jit { -// -// Baseline Inline Caches are polymorphic caches that aggressively -// share their stub code. -// -// Every polymorphic site contains a linked list of stubs which are -// specific to that site. These stubs are composed of a |StubData| -// structure that stores parametrization information (e.g. -// the shape pointer for a shape-check-and-property-get stub), any -// dynamic information (e.g. warm-up counters), a pointer to the stub code, -// and a pointer to the next stub state in the linked list. -// -// Every BaselineScript keeps an table of |CacheDescriptor| data -// structures, which store the following: -// A pointer to the first StubData in the cache. -// The bytecode PC of the relevant IC. -// The machine-code PC where the call to the stubcode returns. -// -// A diagram: -// -// Control flow Pointers -// =======# ----. .----> -// # | | -// #======> \-----/ -// -// -// .---------------------------------------. -// | .-------------------------. | -// | | .----. | | -// Baseline | | | | | | -// JIT Code 0 ^ 1 ^ 2 ^ | | | -// +--------------+ .-->+-----+ +-----+ +-----+ | | | -// | | #=|==>| |==>| |==>| FB | | | | -// | | # | +-----+ +-----+ +-----+ | | | -// | | # | # # # | | | -// |==============|==# | # # # | | | -// |=== IC =======| | # # # | | | -// .->|==============|<===|======#=========#=========# | | | -// | | | | | | | -// | | | | | | | -// | | | | | | | -// | | | | v | | -// | | | | +---------+ | | -// | | | | | Fallback| | | -// | | | | | Stub | | | -// | | | | | Code | | | -// | | | | +---------+ | | -// | +--------------+ | | | -// | |_______ | +---------+ | | -// | | | | Stub |<---/ | -// | IC | \--. | Code | | -// | Descriptor | | +---------+ | -// | Table v | | -// | +-----------------+ | +---------+ | -// \--| Ins | PC | Stub |----/ | Stub |<-------/ -// +-----------------+ | Code | -// | ... | +---------+ -// +-----------------+ -// Shared -// Stub Code -// -// -// Type ICs -// ======== -// -// Type ICs are otherwise regular ICs that are actually nested within -// other IC chains. They serve to optimize locations in the code where the -// baseline compiler would have otherwise had to perform a type Monitor operation -// (e.g. the result of GetProp, GetElem, etc.), or locations where the baseline -// compiler would have had to modify a heap typeset using the type of an input -// value (e.g. SetProp, SetElem, etc.) -// -// There are two kinds of Type ICs: Monitor and Update. -// -// Note that type stub bodies are no-ops. The stubs only exist for their -// guards, and their existence simply signifies that the typeset (implicit) -// that is being checked already contains that type. -// -// TypeMonitor ICs -// --------------- -// Monitor ICs are shared between stubs in the general IC, and monitor the resulting -// types of getter operations (call returns, getprop outputs, etc.) -// -// +-----------+ +-----------+ +-----------+ +-----------+ -// ---->| Stub 1 |---->| Stub 2 |---->| Stub 3 |---->| FB Stub | -// +-----------+ +-----------+ +-----------+ +-----------+ -// | | | | -// |------------------/-----------------/ | -// v | -// +-----------+ +-----------+ +-----------+ | -// | Type 1 |---->| Type 2 |---->| Type FB | | -// +-----------+ +-----------+ +-----------+ | -// | | | | -// <----------/-----------------/------------------/------------------/ -// r e t u r n p a t h -// -// After an optimized IC stub successfully executes, it passes control to the type stub -// chain to check the resulting type. If no type stub succeeds, and the monitor fallback -// stub is reached, the monitor fallback stub performs a manual monitor, and also adds the -// appropriate type stub to the chain. -// -// The IC's main fallback, in addition to generating new mainline stubs, also generates -// type stubs as reflected by its returned value. -// -// NOTE: The type IC chain returns directly to the mainline code, not back to the -// stub it was entered from. Thus, entering a type IC is a matter of a |jump|, not -// a |call|. This allows us to safely call a VM Monitor function from within the monitor IC's -// fallback chain, since the return address (needed for stack inspection) is preserved. -// -// -// TypeUpdate ICs -// -------------- -// Update ICs update heap typesets and monitor the input types of setter operations -// (setelem, setprop inputs, etc.). Unlike monitor ICs, they are not shared -// between stubs on an IC, but instead are kept track of on a per-stub basis. -// -// This is because the main stubs for the operation will each identify a potentially -// different ObjectGroup to update. New input types must be tracked on a group-to- -// group basis. -// -// Type-update ICs cannot be called in tail position (they must return to the -// the stub that called them so that the stub may continue to perform its original -// purpose). This means that any VMCall to perform a manual type update from C++ must be -// done from within the main IC stub. This necessitates that the stub enter a -// "BaselineStub" frame before making the call. -// -// If the type-update IC chain could itself make the VMCall, then the BaselineStub frame -// must be entered before calling the type-update chain, and exited afterward. This -// is very expensive for a common case where we expect the type-update fallback to not -// be called. To avoid the cost of entering and exiting a BaselineStub frame when -// using the type-update IC chain, we design the chain to not perform any VM-calls -// in its fallback. -// -// Instead, the type-update IC chain is responsible for returning 1 or 0, depending -// on if a type is represented in the chain or not. The fallback stub simply returns -// 0, and all other optimized stubs return 1. -// If the chain returns 1, then the IC stub goes ahead and performs its operation. -// If the chain returns 0, then the IC stub performs a call to the fallback function -// inline (doing the requisite BaselineStub frame enter/exit). -// This allows us to avoid the expensive subfram enter/exit in the common case. -// -// r e t u r n p a t h -// <--------------.-----------------.-----------------.-----------------. -// | | | | -// +-----------+ +-----------+ +-----------+ +-----------+ -// ---->| Stub 1 |---->| Stub 2 |---->| Stub 3 |---->| FB Stub | -// +-----------+ +-----------+ +-----------+ +-----------+ -// | ^ | ^ | ^ -// | | | | | | -// | | | | | |----------------. -// | | | | v |1 |0 -// | | | | +-----------+ +-----------+ -// | | | | | Type 3.1 |--->| FB 3 | -// | | | | +-----------+ +-----------+ -// | | | | -// | | | \-------------.-----------------. -// | | | | | | -// | | v |1 |1 |0 -// | | +-----------+ +-----------+ +-----------+ -// | | | Type 2.1 |---->| Type 2.2 |---->| FB 2 | -// | | +-----------+ +-----------+ +-----------+ -// | | -// | \-------------.-----------------. -// | | | | -// v |1 |1 |0 -// +-----------+ +-----------+ +-----------+ -// | Type 1.1 |---->| Type 1.2 |---->| FB 1 | -// +-----------+ +-----------+ +-----------+ -// - -class ICStub; -class ICFallbackStub; - -// -// An entry in the Baseline IC descriptor table. -// -class ICEntry -{ - private: - // A pointer to the baseline IC stub for this instruction. - ICStub* firstStub_; - - // Offset from the start of the JIT code where the IC - // load and call instructions are. - uint32_t returnOffset_; - - // The PC of this IC's bytecode op within the JSScript. - uint32_t pcOffset_ : 28; - - public: - enum Kind { - // A for-op IC entry. - Kind_Op = 0, - - // A non-op IC entry. - Kind_NonOp, - - // A fake IC entry for returning from a callVM for an op. - Kind_CallVM, - - // A fake IC entry for returning from a callVM not for an op (e.g., in - // the prologue). - Kind_NonOpCallVM, - - // A fake IC entry for returning from a callVM to the interrupt - // handler via the over-recursion check on function entry. - Kind_StackCheck, - - // As above, but for the early check. See emitStackCheck. - Kind_EarlyStackCheck, - - // A fake IC entry for returning from DebugTrapHandler. - Kind_DebugTrap, - - // A fake IC entry for returning from a callVM to - // Debug{Prologue,Epilogue}. - Kind_DebugPrologue, - Kind_DebugEpilogue, - - Kind_Invalid - }; - - private: - // What this IC is for. - Kind kind_ : 4; - - // Set the kind and asserts that it's sane. - void setKind(Kind kind) { - MOZ_ASSERT(kind < Kind_Invalid); - kind_ = kind; - MOZ_ASSERT(this->kind() == kind); - } - - public: - ICEntry(uint32_t pcOffset, Kind kind) - : firstStub_(nullptr), returnOffset_(), pcOffset_(pcOffset) - { - // The offset must fit in at least 28 bits, since we shave off 4 for - // the Kind enum. - MOZ_ASSERT(pcOffset_ == pcOffset); - JS_STATIC_ASSERT(BaselineScript::MAX_JSSCRIPT_LENGTH <= (1u << 28) - 1); - MOZ_ASSERT(pcOffset <= BaselineScript::MAX_JSSCRIPT_LENGTH); - setKind(kind); - } - - CodeOffsetLabel returnOffset() const { - return CodeOffsetLabel(returnOffset_); - } - - void setReturnOffset(CodeOffsetLabel offset) { - MOZ_ASSERT(offset.offset() <= (size_t) UINT32_MAX); - returnOffset_ = (uint32_t) offset.offset(); - } - - void fixupReturnOffset(MacroAssembler& masm) { - CodeOffsetLabel offset = returnOffset(); - offset.fixup(&masm); - MOZ_ASSERT(offset.offset() <= UINT32_MAX); - returnOffset_ = (uint32_t) offset.offset(); - } - - uint32_t pcOffset() const { - return pcOffset_; - } - - jsbytecode* pc(JSScript* script) const { - return script->offsetToPC(pcOffset_); - } - - Kind kind() const { - // MSVC compiles enums as signed. - return Kind(kind_ & 0xf); - } - bool isForOp() const { - return kind() == Kind_Op; - } - - void setFakeKind(Kind kind) { - MOZ_ASSERT(kind != Kind_Op && kind != Kind_NonOp); - setKind(kind); - } - - bool hasStub() const { - return firstStub_ != nullptr; - } - ICStub* firstStub() const { - MOZ_ASSERT(hasStub()); - return firstStub_; - } - - ICFallbackStub* fallbackStub() const; - - void setFirstStub(ICStub* stub) { - firstStub_ = stub; - } - - static inline size_t offsetOfFirstStub() { - return offsetof(ICEntry, firstStub_); - } - - inline ICStub** addressOfFirstStub() { - return &firstStub_; - } -}; - -// List of baseline IC stub kinds. -#define IC_STUB_KIND_LIST(_) \ - _(WarmUpCounter_Fallback) \ - \ - _(TypeMonitor_Fallback) \ - _(TypeMonitor_SingleObject) \ - _(TypeMonitor_ObjectGroup) \ - _(TypeMonitor_PrimitiveSet) \ - \ - _(TypeUpdate_Fallback) \ - _(TypeUpdate_SingleObject) \ - _(TypeUpdate_ObjectGroup) \ - _(TypeUpdate_PrimitiveSet) \ - \ - _(This_Fallback) \ - \ - _(NewArray_Fallback) \ - _(NewObject_Fallback) \ - _(NewObject_WithTemplate) \ - \ - _(Compare_Fallback) \ - _(Compare_Int32) \ - _(Compare_Double) \ - _(Compare_NumberWithUndefined) \ - _(Compare_String) \ - _(Compare_Boolean) \ - _(Compare_Object) \ - _(Compare_ObjectWithUndefined) \ - _(Compare_Int32WithBoolean) \ - \ - _(ToBool_Fallback) \ - _(ToBool_Int32) \ - _(ToBool_String) \ - _(ToBool_NullUndefined) \ - _(ToBool_Double) \ - _(ToBool_Object) \ - \ - _(ToNumber_Fallback) \ - \ - _(BinaryArith_Fallback) \ - _(BinaryArith_Int32) \ - _(BinaryArith_Double) \ - _(BinaryArith_StringConcat) \ - _(BinaryArith_StringObjectConcat) \ - _(BinaryArith_BooleanWithInt32) \ - _(BinaryArith_DoubleWithInt32) \ - \ - _(UnaryArith_Fallback) \ - _(UnaryArith_Int32) \ - _(UnaryArith_Double) \ - \ - _(Call_Fallback) \ - _(Call_Scripted) \ - _(Call_AnyScripted) \ - _(Call_Native) \ - _(Call_ClassHook) \ - _(Call_ScriptedApplyArray) \ - _(Call_ScriptedApplyArguments) \ - _(Call_ScriptedFunCall) \ - _(Call_StringSplit) \ - _(Call_IsSuspendedStarGenerator) \ - \ - _(GetElem_Fallback) \ - _(GetElem_NativeSlot) \ - _(GetElem_NativePrototypeSlot) \ - _(GetElem_NativePrototypeCallNative) \ - _(GetElem_NativePrototypeCallScripted) \ - _(GetElem_String) \ - _(GetElem_Dense) \ - _(GetElem_UnboxedArray) \ - _(GetElem_TypedArray) \ - _(GetElem_Arguments) \ - \ - _(SetElem_Fallback) \ - _(SetElem_DenseOrUnboxedArray) \ - _(SetElem_DenseOrUnboxedArrayAdd) \ - _(SetElem_TypedArray) \ - \ - _(In_Fallback) \ - _(In_Native) \ - _(In_NativePrototype) \ - _(In_NativeDoesNotExist) \ - _(In_Dense) \ - \ - _(GetName_Fallback) \ - _(GetName_Global) \ - _(GetName_Scope0) \ - _(GetName_Scope1) \ - _(GetName_Scope2) \ - _(GetName_Scope3) \ - _(GetName_Scope4) \ - _(GetName_Scope5) \ - _(GetName_Scope6) \ - \ - _(BindName_Fallback) \ - \ - _(GetIntrinsic_Fallback) \ - _(GetIntrinsic_Constant) \ - \ - _(GetProp_Fallback) \ - _(GetProp_ArrayLength) \ - _(GetProp_UnboxedArrayLength) \ - _(GetProp_Primitive) \ - _(GetProp_StringLength) \ - _(GetProp_Native) \ - _(GetProp_NativeDoesNotExist) \ - _(GetProp_NativePrototype) \ - _(GetProp_Unboxed) \ - _(GetProp_TypedObject) \ - _(GetProp_CallScripted) \ - _(GetProp_CallNative) \ - _(GetProp_CallDOMProxyNative)\ - _(GetProp_CallDOMProxyWithGenerationNative)\ - _(GetProp_DOMProxyShadowed) \ - _(GetProp_ArgumentsLength) \ - _(GetProp_ArgumentsCallee) \ - _(GetProp_Generic) \ - \ - _(SetProp_Fallback) \ - _(SetProp_Native) \ - _(SetProp_NativeAdd) \ - _(SetProp_Unboxed) \ - _(SetProp_TypedObject) \ - _(SetProp_CallScripted) \ - _(SetProp_CallNative) \ - \ - _(TableSwitch) \ - \ - _(IteratorNew_Fallback) \ - _(IteratorMore_Fallback) \ - _(IteratorMore_Native) \ - _(IteratorClose_Fallback) \ - \ - _(InstanceOf_Fallback) \ - _(InstanceOf_Function) \ - \ - _(TypeOf_Fallback) \ - _(TypeOf_Typed) \ - \ - _(Rest_Fallback) \ - \ - _(RetSub_Fallback) \ - _(RetSub_Resume) - -#define FORWARD_DECLARE_STUBS(kindName) class IC##kindName; - IC_STUB_KIND_LIST(FORWARD_DECLARE_STUBS) -#undef FORWARD_DECLARE_STUBS - -class ICMonitoredStub; -class ICMonitoredFallbackStub; -class ICUpdatedStub; - -// Constant iterator that traverses arbitrary chains of ICStubs. -// No requirements are made of the ICStub used to construct this -// iterator, aside from that the stub be part of a nullptr-terminated -// chain. -// The iterator is considered to be at its end once it has been -// incremented _past_ the last stub. Thus, if 'atEnd()' returns -// true, the '*' and '->' operations are not valid. -class ICStubConstIterator -{ - friend class ICStub; - friend class ICFallbackStub; - - private: - ICStub* currentStub_; - - public: - explicit ICStubConstIterator(ICStub* currentStub) : currentStub_(currentStub) {} - - static ICStubConstIterator StartingAt(ICStub* stub) { - return ICStubConstIterator(stub); - } - static ICStubConstIterator End(ICStub* stub) { - return ICStubConstIterator(nullptr); - } - - bool operator ==(const ICStubConstIterator& other) const { - return currentStub_ == other.currentStub_; - } - bool operator !=(const ICStubConstIterator& other) const { - return !(*this == other); - } - - ICStubConstIterator& operator++(); - - ICStubConstIterator operator++(int) { - ICStubConstIterator oldThis(*this); - ++(*this); - return oldThis; - } - - ICStub* operator*() const { - MOZ_ASSERT(currentStub_); - return currentStub_; - } - - ICStub* operator ->() const { - MOZ_ASSERT(currentStub_); - return currentStub_; - } - - bool atEnd() const { - return currentStub_ == nullptr; - } -}; - -// Iterator that traverses "regular" IC chains that start at an ICEntry -// and are terminated with an ICFallbackStub. -// -// The iterator is considered to be at its end once it is _at_ the -// fallback stub. Thus, unlike the ICStubConstIterator, operators -// '*' and '->' are valid even if 'atEnd()' returns true - they -// will act on the fallback stub. -// -// This iterator also allows unlinking of stubs being traversed. -// Note that 'unlink' does not implicitly advance the iterator - -// it must be advanced explicitly using '++'. -class ICStubIterator -{ - friend class ICFallbackStub; - - private: - ICEntry* icEntry_; - ICFallbackStub* fallbackStub_; - ICStub* previousStub_; - ICStub* currentStub_; - bool unlinked_; - - explicit ICStubIterator(ICFallbackStub* fallbackStub, bool end=false); - public: - - bool operator ==(const ICStubIterator& other) const { - // == should only ever be called on stubs from the same chain. - MOZ_ASSERT(icEntry_ == other.icEntry_); - MOZ_ASSERT(fallbackStub_ == other.fallbackStub_); - return currentStub_ == other.currentStub_; - } - bool operator !=(const ICStubIterator& other) const { - return !(*this == other); - } - - ICStubIterator& operator++(); - - ICStubIterator operator++(int) { - ICStubIterator oldThis(*this); - ++(*this); - return oldThis; - } - - ICStub* operator*() const { - return currentStub_; - } - - ICStub* operator ->() const { - return currentStub_; - } - - bool atEnd() const { - return currentStub_ == (ICStub*) fallbackStub_; - } - - void unlink(JSContext* cx); -}; - -// -// Base class for all IC stubs. -// -class ICStub -{ - friend class ICFallbackStub; - - public: - enum Kind { - INVALID = 0, -#define DEF_ENUM_KIND(kindName) kindName, - IC_STUB_KIND_LIST(DEF_ENUM_KIND) -#undef DEF_ENUM_KIND - LIMIT - }; - - static inline bool IsValidKind(Kind k) { - return (k > INVALID) && (k < LIMIT); - } - - static const char* KindString(Kind k) { - switch(k) { -#define DEF_KIND_STR(kindName) case kindName: return #kindName; - IC_STUB_KIND_LIST(DEF_KIND_STR) -#undef DEF_KIND_STR - default: - MOZ_CRASH("Invalid kind."); - } - } - - enum Trait { - Regular = 0x0, - Fallback = 0x1, - Monitored = 0x2, - MonitoredFallback = 0x3, - Updated = 0x4 - }; - - void markCode(JSTracer* trc, const char* name); - void updateCode(JitCode* stubCode); - void trace(JSTracer* trc); - - template - static T* New(JSContext* cx, ICStubSpace* space, JitCode* code, Args&&... args) { - if (!code) - return nullptr; - T* result = space->allocate(code, mozilla::Forward(args)...); - if (!result) - ReportOutOfMemory(cx); - return result; - } - - protected: - // The raw jitcode to call for this stub. - uint8_t* stubCode_; - - // Pointer to next IC stub. This is null for the last IC stub, which should - // either be a fallback or inert IC stub. - ICStub* next_; - - // A 16-bit field usable by subtypes of ICStub for subtype-specific small-info - uint16_t extra_; - - // The kind of the stub. - // High bit is 'isFallback' flag. - // Second high bit is 'isMonitored' flag. - Trait trait_ : 3; - Kind kind_ : 13; - - inline ICStub(Kind kind, JitCode* stubCode) - : stubCode_(stubCode->raw()), - next_(nullptr), - extra_(0), - trait_(Regular), - kind_(kind) - { - MOZ_ASSERT(stubCode != nullptr); - } - - inline ICStub(Kind kind, Trait trait, JitCode* stubCode) - : stubCode_(stubCode->raw()), - next_(nullptr), - extra_(0), - trait_(trait), - kind_(kind) - { - MOZ_ASSERT(stubCode != nullptr); - } - - inline Trait trait() const { - // Workaround for MSVC reading trait_ as signed value. - return (Trait)(trait_ & 0x7); - } - - public: - - inline Kind kind() const { - return static_cast(kind_); - } - - inline bool isFallback() const { - return trait() == Fallback || trait() == MonitoredFallback; - } - - inline bool isMonitored() const { - return trait() == Monitored; - } - - inline bool isUpdated() const { - return trait() == Updated; - } - - inline bool isMonitoredFallback() const { - return trait() == MonitoredFallback; - } - - inline const ICFallbackStub* toFallbackStub() const { - MOZ_ASSERT(isFallback()); - return reinterpret_cast(this); - } - - inline ICFallbackStub* toFallbackStub() { - MOZ_ASSERT(isFallback()); - return reinterpret_cast(this); - } - - inline const ICMonitoredStub* toMonitoredStub() const { - MOZ_ASSERT(isMonitored()); - return reinterpret_cast(this); - } - - inline ICMonitoredStub* toMonitoredStub() { - MOZ_ASSERT(isMonitored()); - return reinterpret_cast(this); - } - - inline const ICMonitoredFallbackStub* toMonitoredFallbackStub() const { - MOZ_ASSERT(isMonitoredFallback()); - return reinterpret_cast(this); - } - - inline ICMonitoredFallbackStub* toMonitoredFallbackStub() { - MOZ_ASSERT(isMonitoredFallback()); - return reinterpret_cast(this); - } - - inline const ICUpdatedStub* toUpdatedStub() const { - MOZ_ASSERT(isUpdated()); - return reinterpret_cast(this); - } - - inline ICUpdatedStub* toUpdatedStub() { - MOZ_ASSERT(isUpdated()); - return reinterpret_cast(this); - } - -#define KIND_METHODS(kindName) \ - inline bool is##kindName() const { return kind() == kindName; } \ - inline const IC##kindName* to##kindName() const { \ - MOZ_ASSERT(is##kindName()); \ - return reinterpret_cast(this); \ - } \ - inline IC##kindName* to##kindName() { \ - MOZ_ASSERT(is##kindName()); \ - return reinterpret_cast(this); \ - } - IC_STUB_KIND_LIST(KIND_METHODS) -#undef KIND_METHODS - - inline ICStub* next() const { - return next_; - } - - inline bool hasNext() const { - return next_ != nullptr; - } - - inline void setNext(ICStub* stub) { - // Note: next_ only needs to be changed under the compilation lock for - // non-type-monitor/update ICs. - next_ = stub; - } - - inline ICStub** addressOfNext() { - return &next_; - } - - inline JitCode* jitCode() { - return JitCode::FromExecutable(stubCode_); - } - - inline uint8_t* rawStubCode() const { - return stubCode_; - } - - // This method is not valid on TypeUpdate stub chains! - inline ICFallbackStub* getChainFallback() { - ICStub* lastStub = this; - while (lastStub->next_) - lastStub = lastStub->next_; - MOZ_ASSERT(lastStub->isFallback()); - return lastStub->toFallbackStub(); - } - - inline ICStubConstIterator beginHere() { - return ICStubConstIterator::StartingAt(this); - } - - static inline size_t offsetOfNext() { - return offsetof(ICStub, next_); - } - - static inline size_t offsetOfStubCode() { - return offsetof(ICStub, stubCode_); - } - - static inline size_t offsetOfExtra() { - return offsetof(ICStub, extra_); - } - - static bool CanMakeCalls(ICStub::Kind kind) { - MOZ_ASSERT(IsValidKind(kind)); - switch (kind) { - case Call_Fallback: - case Call_Scripted: - case Call_AnyScripted: - case Call_Native: - case Call_ClassHook: - case Call_ScriptedApplyArray: - case Call_ScriptedApplyArguments: - case Call_ScriptedFunCall: - case Call_StringSplit: - case WarmUpCounter_Fallback: - case GetElem_NativeSlot: - case GetElem_NativePrototypeSlot: - case GetElem_NativePrototypeCallNative: - case GetElem_NativePrototypeCallScripted: - case GetProp_CallScripted: - case GetProp_CallNative: - case GetProp_CallDOMProxyNative: - case GetProp_CallDOMProxyWithGenerationNative: - case GetProp_DOMProxyShadowed: - case GetProp_Generic: - case SetProp_CallScripted: - case SetProp_CallNative: - case RetSub_Fallback: - // These two fallback stubs don't actually make non-tail calls, - // but the fallback code for the bailout path needs to pop the stub frame - // pushed during the bailout. - case GetProp_Fallback: - case SetProp_Fallback: -#if JS_HAS_NO_SUCH_METHOD - case GetElem_Dense: - case GetElem_Arguments: - case GetProp_NativePrototype: - case GetProp_Native: -#endif - return true; - default: - return false; - } - } - - // Optimized stubs get purged on GC. But some stubs can be active on the - // stack during GC - specifically the ones that can make calls. To ensure - // that these do not get purged, all stubs that can make calls are allocated - // in the fallback stub space. - bool allocatedInFallbackSpace() const { - MOZ_ASSERT(next()); - return CanMakeCalls(kind()); - } -}; - -class ICFallbackStub : public ICStub -{ - friend class ICStubConstIterator; - protected: - // Fallback stubs need these fields to easily add new stubs to - // the linked list of stubs for an IC. - - // The IC entry for this linked list of stubs. - ICEntry* icEntry_; - - // The number of stubs kept in the IC entry. - uint32_t numOptimizedStubs_; - - // A pointer to the location stub pointer that needs to be - // changed to add a new "last" stub immediately before the fallback - // stub. This'll start out pointing to the icEntry's "firstStub_" - // field, and as new stubs are addd, it'll point to the current - // last stub's "next_" field. - ICStub** lastStubPtrAddr_; - - ICFallbackStub(Kind kind, JitCode* stubCode) - : ICStub(kind, ICStub::Fallback, stubCode), - icEntry_(nullptr), - numOptimizedStubs_(0), - lastStubPtrAddr_(nullptr) {} - - ICFallbackStub(Kind kind, Trait trait, JitCode* stubCode) - : ICStub(kind, trait, stubCode), - icEntry_(nullptr), - numOptimizedStubs_(0), - lastStubPtrAddr_(nullptr) - { - MOZ_ASSERT(trait == ICStub::Fallback || - trait == ICStub::MonitoredFallback); - } - - public: - inline ICEntry* icEntry() const { - return icEntry_; - } - - inline size_t numOptimizedStubs() const { - return (size_t) numOptimizedStubs_; - } - - // The icEntry and lastStubPtrAddr_ fields can't be initialized when the stub is - // created since the stub is created at compile time, and we won't know the IC entry - // address until after compile when the BaselineScript is created. This method - // allows these fields to be fixed up at that point. - void fixupICEntry(ICEntry* icEntry) { - MOZ_ASSERT(icEntry_ == nullptr); - MOZ_ASSERT(lastStubPtrAddr_ == nullptr); - icEntry_ = icEntry; - lastStubPtrAddr_ = icEntry_->addressOfFirstStub(); - } - - // Add a new stub to the IC chain terminated by this fallback stub. - void addNewStub(ICStub* stub) { - MOZ_ASSERT(*lastStubPtrAddr_ == this); - MOZ_ASSERT(stub->next() == nullptr); - stub->setNext(this); - *lastStubPtrAddr_ = stub; - lastStubPtrAddr_ = stub->addressOfNext(); - numOptimizedStubs_++; - } - - ICStubConstIterator beginChainConst() const { - return ICStubConstIterator(icEntry_->firstStub()); - } - - ICStubIterator beginChain() { - return ICStubIterator(this); - } - - bool hasStub(ICStub::Kind kind) const { - for (ICStubConstIterator iter = beginChainConst(); !iter.atEnd(); iter++) { - if (iter->kind() == kind) - return true; - } - return false; - } - - unsigned numStubsWithKind(ICStub::Kind kind) const { - unsigned count = 0; - for (ICStubConstIterator iter = beginChainConst(); !iter.atEnd(); iter++) { - if (iter->kind() == kind) - count++; - } - return count; - } - - void unlinkStub(Zone* zone, ICStub* prev, ICStub* stub); - void unlinkStubsWithKind(JSContext* cx, ICStub::Kind kind); -}; - -// Monitored stubs are IC stubs that feed a single resulting value out to a -// type monitor operation. -class ICMonitoredStub : public ICStub -{ - protected: - // Pointer to the start of the type monitoring stub chain. - ICStub* firstMonitorStub_; - - ICMonitoredStub(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub); - - public: - inline void updateFirstMonitorStub(ICStub* monitorStub) { - // This should only be called once: when the first optimized monitor stub - // is added to the type monitor IC chain. - MOZ_ASSERT(firstMonitorStub_ && firstMonitorStub_->isTypeMonitor_Fallback()); - firstMonitorStub_ = monitorStub; - } - inline void resetFirstMonitorStub(ICStub* monitorFallback) { - MOZ_ASSERT(monitorFallback->isTypeMonitor_Fallback()); - firstMonitorStub_ = monitorFallback; - } - inline ICStub* firstMonitorStub() const { - return firstMonitorStub_; - } - - static inline size_t offsetOfFirstMonitorStub() { - return offsetof(ICMonitoredStub, firstMonitorStub_); - } -}; - -// Monitored fallback stubs - as the name implies. -class ICMonitoredFallbackStub : public ICFallbackStub -{ - protected: - // Pointer to the fallback monitor stub. - ICTypeMonitor_Fallback* fallbackMonitorStub_; - - ICMonitoredFallbackStub(Kind kind, JitCode* stubCode) - : ICFallbackStub(kind, ICStub::MonitoredFallback, stubCode), - fallbackMonitorStub_(nullptr) {} - - public: - bool initMonitoringChain(JSContext* cx, ICStubSpace* space); - bool addMonitorStubForValue(JSContext* cx, JSScript* script, HandleValue val); - - inline ICTypeMonitor_Fallback* fallbackMonitorStub() const { - return fallbackMonitorStub_; - } - - static inline size_t offsetOfFallbackMonitorStub() { - return offsetof(ICMonitoredFallbackStub, fallbackMonitorStub_); - } -}; - -// Updated stubs are IC stubs that use a TypeUpdate IC to track -// the status of heap typesets that need to be updated. -class ICUpdatedStub : public ICStub -{ - protected: - // Pointer to the start of the type updating stub chain. - ICStub* firstUpdateStub_; - - static const uint32_t MAX_OPTIMIZED_STUBS = 8; - uint32_t numOptimizedStubs_; - - ICUpdatedStub(Kind kind, JitCode* stubCode) - : ICStub(kind, ICStub::Updated, stubCode), - firstUpdateStub_(nullptr), - numOptimizedStubs_(0) - {} - - public: - bool initUpdatingChain(JSContext* cx, ICStubSpace* space); - - bool addUpdateStubForValue(JSContext* cx, HandleScript script, HandleObject obj, HandleId id, - HandleValue val); - - void addOptimizedUpdateStub(ICStub* stub) { - if (firstUpdateStub_->isTypeUpdate_Fallback()) { - stub->setNext(firstUpdateStub_); - firstUpdateStub_ = stub; - } else { - ICStub* iter = firstUpdateStub_; - MOZ_ASSERT(iter->next() != nullptr); - while (!iter->next()->isTypeUpdate_Fallback()) - iter = iter->next(); - MOZ_ASSERT(iter->next()->next() == nullptr); - stub->setNext(iter->next()); - iter->setNext(stub); - } - - numOptimizedStubs_++; - } - - inline ICStub* firstUpdateStub() const { - return firstUpdateStub_; - } - - bool hasTypeUpdateStub(ICStub::Kind kind) { - ICStub* stub = firstUpdateStub_; - do { - if (stub->kind() == kind) - return true; - - stub = stub->next(); - } while (stub); - - return false; - } - - inline uint32_t numOptimizedStubs() const { - return numOptimizedStubs_; - } - - static inline size_t offsetOfFirstUpdateStub() { - return offsetof(ICUpdatedStub, firstUpdateStub_); - } -}; - -// Base class for stubcode compilers. -class ICStubCompiler -{ - // Prevent GC in the middle of stub compilation. - js::gc::AutoSuppressGC suppressGC; - - protected: - JSContext* cx; - ICStub::Kind kind; -#ifdef DEBUG - bool entersStubFrame_; -#endif - - // By default the stubcode key is just the kind. - virtual int32_t getKey() const { - return static_cast(kind); - } - - virtual bool generateStubCode(MacroAssembler& masm) = 0; - virtual bool postGenerateStubCode(MacroAssembler& masm, Handle genCode) { - return true; - } - JitCode* getStubCode(); - - ICStubCompiler(JSContext* cx, ICStub::Kind kind) - : suppressGC(cx), cx(cx), kind(kind) -#ifdef DEBUG - , entersStubFrame_(false) -#endif - {} - - // Emits a tail call to a VMFunction wrapper. - bool tailCallVM(const VMFunction& fun, MacroAssembler& masm); - - // Emits a normal (non-tail) call to a VMFunction wrapper. - bool callVM(const VMFunction& fun, MacroAssembler& masm); - - // Emits a call to a type-update IC, assuming that the value to be - // checked is already in R0. - bool callTypeUpdateIC(MacroAssembler& masm, uint32_t objectOffset); - - // A stub frame is used when a stub wants to call into the VM without - // performing a tail call. This is required for the return address - // to pc mapping to work. - void enterStubFrame(MacroAssembler& masm, Register scratch); - void leaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false); - - // Some stubs need to emit SPS profiler updates. This emits the guarding - // jitcode for those stubs. If profiling is not enabled, jumps to the - // given label. - void guardProfilingEnabled(MacroAssembler& masm, Register scratch, Label* skip); - - inline AllocatableGeneralRegisterSet availableGeneralRegs(size_t numInputs) const { - AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); - MOZ_ASSERT(!regs.has(BaselineStackReg)); -#if defined(JS_CODEGEN_ARM) - MOZ_ASSERT(!regs.has(ICTailCallReg)); - regs.take(BaselineSecondScratchReg); -#elif defined(JS_CODEGEN_MIPS) - MOZ_ASSERT(!regs.has(ICTailCallReg)); - MOZ_ASSERT(!regs.has(BaselineSecondScratchReg)); -#endif - regs.take(BaselineFrameReg); - regs.take(ICStubReg); -#ifdef JS_CODEGEN_X64 - regs.take(ExtractTemp0); - regs.take(ExtractTemp1); -#endif - - switch (numInputs) { - case 0: - break; - case 1: - regs.take(R0); - break; - case 2: - regs.take(R0); - regs.take(R1); - break; - default: - MOZ_CRASH("Invalid numInputs"); - } - - return regs; - } - - inline bool emitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, ValueOperand val, - Register scratch, LiveGeneralRegisterSet saveRegs); - - template - T* newStub(Args&&... args) { - return ICStub::New(cx, mozilla::Forward(args)...); - } - - public: - virtual ICStub* getStub(ICStubSpace* space) = 0; - - static ICStubSpace* StubSpaceForKind(ICStub::Kind kind, JSScript* script) { - if (ICStub::CanMakeCalls(kind)) - return script->baselineScript()->fallbackStubSpace(); - return script->zone()->jitZone()->optimizedStubSpace(); - } - - ICStubSpace* getStubSpace(JSScript* script) { - return StubSpaceForKind(kind, script); - } -}; - -// Base class for stub compilers that can generate multiple stubcodes. -// These compilers need access to the JSOp they are compiling for. -class ICMultiStubCompiler : public ICStubCompiler -{ - protected: - JSOp op; - - // Stub keys for multi-stub kinds are composed of both the kind - // and the op they are compiled for. - virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(op) << 16); - } - - ICMultiStubCompiler(JSContext* cx, ICStub::Kind kind, JSOp op) - : ICStubCompiler(cx, kind), op(op) {} -}; - // WarmUpCounter_Fallback // A WarmUpCounter IC chain has only the fallback stub. @@ -1225,7 +46,7 @@ class ICWarmUpCounter_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::WarmUpCounter_Fallback) + : ICStubCompiler(cx, ICStub::WarmUpCounter_Fallback, Engine::Baseline) { } ICWarmUpCounter_Fallback* getStub(ICStubSpace* space) { @@ -1293,13 +114,15 @@ class TypeCheckPrimitiveSetStub : public ICStub uint16_t flags_; virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(flags_) << 16); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(flags_) << 17); } public: Compiler(JSContext* cx, Kind kind, TypeCheckPrimitiveSetStub* existingStub, JSValueType type) - : ICStubCompiler(cx, kind), + : ICStubCompiler(cx, kind, Engine::Baseline), existingStub_(existingStub), flags_((existingStub ? existingStub->typeFlags() : 0) | TypeToFlag(type)) { @@ -1464,13 +287,13 @@ class ICTypeMonitor_Fallback : public ICStub public: Compiler(JSContext* cx, ICMonitoredFallbackStub* mainFallbackStub) - : ICStubCompiler(cx, ICStub::TypeMonitor_Fallback), + : ICStubCompiler(cx, ICStub::TypeMonitor_Fallback, Engine::Baseline), mainFallbackStub_(mainFallbackStub), argumentIndex_(BYTECODE_INDEX) { } Compiler(JSContext* cx, uint32_t argumentIndex) - : ICStubCompiler(cx, ICStub::TypeMonitor_Fallback), + : ICStubCompiler(cx, ICStub::TypeMonitor_Fallback, Engine::Baseline), mainFallbackStub_(nullptr), argumentIndex_(argumentIndex) { } @@ -1539,7 +362,7 @@ class ICTypeMonitor_SingleObject : public ICStub public: Compiler(JSContext* cx, HandleObject obj) - : ICStubCompiler(cx, TypeMonitor_SingleObject), + : ICStubCompiler(cx, TypeMonitor_SingleObject, Engine::Baseline), obj_(obj) { } @@ -1573,7 +396,7 @@ class ICTypeMonitor_ObjectGroup : public ICStub public: Compiler(JSContext* cx, HandleObjectGroup group) - : ICStubCompiler(cx, TypeMonitor_ObjectGroup), + : ICStubCompiler(cx, TypeMonitor_ObjectGroup, Engine::Baseline), group_(group) { } @@ -1605,7 +428,7 @@ class ICTypeUpdate_Fallback : public ICStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::TypeUpdate_Fallback) + : ICStubCompiler(cx, ICStub::TypeUpdate_Fallback, Engine::Baseline) { } ICTypeUpdate_Fallback* getStub(ICStubSpace* space) { @@ -1672,7 +495,7 @@ class ICTypeUpdate_SingleObject : public ICStub public: Compiler(JSContext* cx, HandleObject obj) - : ICStubCompiler(cx, TypeUpdate_SingleObject), + : ICStubCompiler(cx, TypeUpdate_SingleObject, Engine::Baseline), obj_(obj) { } @@ -1707,7 +530,7 @@ class ICTypeUpdate_ObjectGroup : public ICStub public: Compiler(JSContext* cx, HandleObjectGroup group) - : ICStubCompiler(cx, TypeUpdate_ObjectGroup), + : ICStubCompiler(cx, TypeUpdate_ObjectGroup, Engine::Baseline), group_(group) { } @@ -1735,7 +558,7 @@ class ICThis_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::This_Fallback) {} + : ICStubCompiler(cx, ICStub::This_Fallback, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { return newStub(space, getStubCode()); @@ -1765,7 +588,7 @@ class ICNewArray_Fallback : public ICFallbackStub public: Compiler(JSContext* cx, ObjectGroup* templateGroup) - : ICStubCompiler(cx, ICStub::NewArray_Fallback), + : ICStubCompiler(cx, ICStub::NewArray_Fallback, Engine::Baseline), templateGroup(cx, templateGroup) {} @@ -1804,7 +627,7 @@ class ICNewObject_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::NewObject_Fallback) + : ICStubCompiler(cx, ICStub::NewObject_Fallback, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { @@ -1859,7 +682,7 @@ class ICCompare_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::Compare_Fallback) {} + : ICStubCompiler(cx, ICStub::Compare_Fallback, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { return newStub(space, getStubCode()); @@ -1882,7 +705,7 @@ class ICCompare_Int32 : public ICStub public: Compiler(JSContext* cx, JSOp op) - : ICMultiStubCompiler(cx, ICStub::Compare_Int32, op) {} + : ICMultiStubCompiler(cx, ICStub::Compare_Int32, op, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { return newStub(space, getStubCode()); @@ -1905,7 +728,7 @@ class ICCompare_Double : public ICStub public: Compiler(JSContext* cx, JSOp op) - : ICMultiStubCompiler(cx, ICStub::Compare_Double, op) + : ICMultiStubCompiler(cx, ICStub::Compare_Double, op, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { @@ -1937,14 +760,15 @@ class ICCompare_NumberWithUndefined : public ICStub public: Compiler(JSContext* cx, JSOp op, bool lhsIsUndefined) - : ICMultiStubCompiler(cx, ICStub::Compare_NumberWithUndefined, op), + : ICMultiStubCompiler(cx, ICStub::Compare_NumberWithUndefined, op, Engine::Baseline), lhsIsUndefined(lhsIsUndefined) {} virtual int32_t getKey() const { - return static_cast(kind) - | (static_cast(op) << 16) - | (static_cast(lhsIsUndefined) << 24); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(op) << 17) | + (static_cast(lhsIsUndefined) << 25); } ICStub* getStub(ICStubSpace* space) { @@ -1969,7 +793,7 @@ class ICCompare_String : public ICStub public: Compiler(JSContext* cx, JSOp op) - : ICMultiStubCompiler(cx, ICStub::Compare_String, op) + : ICMultiStubCompiler(cx, ICStub::Compare_String, op, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { @@ -1993,7 +817,7 @@ class ICCompare_Boolean : public ICStub public: Compiler(JSContext* cx, JSOp op) - : ICMultiStubCompiler(cx, ICStub::Compare_Boolean, op) + : ICMultiStubCompiler(cx, ICStub::Compare_Boolean, op, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { @@ -2017,7 +841,7 @@ class ICCompare_Object : public ICStub public: Compiler(JSContext* cx, JSOp op) - : ICMultiStubCompiler(cx, ICStub::Compare_Object, op) + : ICMultiStubCompiler(cx, ICStub::Compare_Object, op, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { @@ -2044,16 +868,17 @@ class ICCompare_ObjectWithUndefined : public ICStub public: Compiler(JSContext* cx, JSOp op, bool lhsIsUndefined, bool compareWithNull) - : ICMultiStubCompiler(cx, ICStub::Compare_ObjectWithUndefined, op), + : ICMultiStubCompiler(cx, ICStub::Compare_ObjectWithUndefined, op, Engine::Baseline), lhsIsUndefined(lhsIsUndefined), compareWithNull(compareWithNull) {} virtual int32_t getKey() const { - return static_cast(kind) - | (static_cast(op) << 16) - | (static_cast(lhsIsUndefined) << 24) - | (static_cast(compareWithNull) << 25); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(op) << 17) | + (static_cast(lhsIsUndefined) << 25) | + (static_cast(compareWithNull) << 26); } ICStub* getStub(ICStubSpace* space) { @@ -2085,13 +910,15 @@ class ICCompare_Int32WithBoolean : public ICStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(op_) << 16) | - (static_cast(lhsIsInt32_) << 24); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(op_) << 17) | + (static_cast(lhsIsInt32_) << 25); } public: Compiler(JSContext* cx, JSOp op, bool lhsIsInt32) - : ICStubCompiler(cx, ICStub::Compare_Int32WithBoolean), + : ICStubCompiler(cx, ICStub::Compare_Int32WithBoolean, Engine::Baseline), op_(op), lhsIsInt32_(lhsIsInt32) {} @@ -2122,7 +949,7 @@ class ICToBool_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::ToBool_Fallback) {} + : ICStubCompiler(cx, ICStub::ToBool_Fallback, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { return newStub(space, getStubCode()); @@ -2145,7 +972,7 @@ class ICToBool_Int32 : public ICStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::ToBool_Int32) {} + : ICStubCompiler(cx, ICStub::ToBool_Int32, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { return newStub(space, getStubCode()); @@ -2168,7 +995,7 @@ class ICToBool_String : public ICStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::ToBool_String) {} + : ICStubCompiler(cx, ICStub::ToBool_String, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { return newStub(space, getStubCode()); @@ -2191,7 +1018,7 @@ class ICToBool_NullUndefined : public ICStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::ToBool_NullUndefined) {} + : ICStubCompiler(cx, ICStub::ToBool_NullUndefined, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { return newStub(space, getStubCode()); @@ -2214,7 +1041,7 @@ class ICToBool_Double : public ICStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::ToBool_Double) {} + : ICStubCompiler(cx, ICStub::ToBool_Double, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { return newStub(space, getStubCode()); @@ -2237,7 +1064,7 @@ class ICToBool_Object : public ICStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::ToBool_Object) {} + : ICStubCompiler(cx, ICStub::ToBool_Object, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { return newStub(space, getStubCode()); @@ -2263,7 +1090,7 @@ class ICToNumber_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::ToNumber_Fallback) {} + : ICStubCompiler(cx, ICStub::ToNumber_Fallback, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { return newStub(space, getStubCode()); @@ -2312,7 +1139,7 @@ class ICBinaryArith_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::BinaryArith_Fallback) {} + : ICStubCompiler(cx, ICStub::BinaryArith_Fallback, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { return newStub(space, getStubCode()); @@ -2345,13 +1172,15 @@ class ICBinaryArith_Int32 : public ICStub // Stub keys shift-stubs need to encode the kind, the JSOp and if we allow doubles. virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(op_) << 16) | - (static_cast(allowDouble_) << 24); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(op_) << 17) | + (static_cast(allowDouble_) << 25); } public: Compiler(JSContext* cx, JSOp op, bool allowDouble) - : ICStubCompiler(cx, ICStub::BinaryArith_Int32), + : ICStubCompiler(cx, ICStub::BinaryArith_Int32, Engine::Baseline), op_(op), allowDouble_(allowDouble) {} ICStub* getStub(ICStubSpace* space) { @@ -2375,7 +1204,7 @@ class ICBinaryArith_StringConcat : public ICStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::BinaryArith_StringConcat) + : ICStubCompiler(cx, ICStub::BinaryArith_StringConcat, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { @@ -2405,12 +1234,14 @@ class ICBinaryArith_StringObjectConcat : public ICStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(lhsIsString_) << 16); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(lhsIsString_) << 17); } public: Compiler(JSContext* cx, bool lhsIsString) - : ICStubCompiler(cx, ICStub::BinaryArith_StringObjectConcat), + : ICStubCompiler(cx, ICStub::BinaryArith_StringObjectConcat, Engine::Baseline), lhsIsString_(lhsIsString) {} @@ -2436,7 +1267,7 @@ class ICBinaryArith_Double : public ICStub public: Compiler(JSContext* cx, JSOp op) - : ICMultiStubCompiler(cx, ICStub::BinaryArith_Double, op) + : ICMultiStubCompiler(cx, ICStub::BinaryArith_Double, op, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { @@ -2477,14 +1308,16 @@ class ICBinaryArith_BooleanWithInt32 : public ICStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(op_) << 16) | - (static_cast(lhsIsBool_) << 24) | - (static_cast(rhsIsBool_) << 25); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(op_) << 17) | + (static_cast(lhsIsBool_) << 25) | + (static_cast(rhsIsBool_) << 26); } public: Compiler(JSContext* cx, JSOp op, bool lhsIsBool, bool rhsIsBool) - : ICStubCompiler(cx, ICStub::BinaryArith_BooleanWithInt32), + : ICStubCompiler(cx, ICStub::BinaryArith_BooleanWithInt32, Engine::Baseline), op_(op), lhsIsBool_(lhsIsBool), rhsIsBool_(rhsIsBool) { MOZ_ASSERT(op_ == JSOP_ADD || op_ == JSOP_SUB || op_ == JSOP_BITOR || @@ -2520,13 +1353,15 @@ class ICBinaryArith_DoubleWithInt32 : public ICStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(op) << 16) | - (static_cast(lhsIsDouble_) << 24); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(op) << 17) | + (static_cast(lhsIsDouble_) << 25); } public: Compiler(JSContext* cx, JSOp op, bool lhsIsDouble) - : ICMultiStubCompiler(cx, ICStub::BinaryArith_DoubleWithInt32, op), + : ICMultiStubCompiler(cx, ICStub::BinaryArith_DoubleWithInt32, op, Engine::Baseline), lhsIsDouble_(lhsIsDouble) {} @@ -2568,7 +1403,7 @@ class ICUnaryArith_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::UnaryArith_Fallback) + : ICStubCompiler(cx, ICStub::UnaryArith_Fallback, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { @@ -2592,7 +1427,7 @@ class ICUnaryArith_Int32 : public ICStub public: Compiler(JSContext* cx, JSOp op) - : ICMultiStubCompiler(cx, ICStub::UnaryArith_Int32, op) + : ICMultiStubCompiler(cx, ICStub::UnaryArith_Int32, op, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { @@ -2616,7 +1451,7 @@ class ICUnaryArith_Double : public ICStub public: Compiler(JSContext* cx, JSOp op) - : ICMultiStubCompiler(cx, ICStub::UnaryArith_Double, op) + : ICMultiStubCompiler(cx, ICStub::UnaryArith_Double, op, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { @@ -2670,7 +1505,7 @@ class ICGetElem_Fallback : public ICMonitoredFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::GetElem_Fallback) + : ICStubCompiler(cx, ICStub::GetElem_Fallback, Engine::Baseline) { } ICStub* getStub(ICStubSpace* space) { @@ -2908,13 +1743,16 @@ class ICGetElemNativeCompiler : public ICStubCompiler protected: virtual int32_t getKey() const { #if JS_HAS_NO_SUCH_METHOD - return static_cast(kind) | - (static_cast(isCallElem_) << 16) | - (static_cast(needsAtomize_) << 17) | - (static_cast(acctype_) << 18); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(isCallElem_) << 17) | + (static_cast(needsAtomize_) << 18) | + (static_cast(acctype_) << 19); #else - return static_cast(kind) | (static_cast(needsAtomize_) << 16) | - (static_cast(acctype_) << 17); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(needsAtomize_) << 17) | + (static_cast(acctype_) << 18); #endif } @@ -2923,7 +1761,7 @@ class ICGetElemNativeCompiler : public ICStubCompiler ICStub* firstMonitorStub, HandleObject obj, HandleObject holder, HandlePropertyName name, ICGetElemNativeStub::AccessType acctype, bool needsAtomize, uint32_t offset) - : ICStubCompiler(cx, kind), + : ICStubCompiler(cx, kind, Engine::Baseline), isCallElem_(isCallElem), firstMonitorStub_(firstMonitorStub), obj_(obj), @@ -2940,7 +1778,7 @@ class ICGetElemNativeCompiler : public ICStubCompiler HandleObject obj, HandleObject holder, HandlePropertyName name, ICGetElemNativeStub::AccessType acctype, bool needsAtomize, HandleFunction getter, uint32_t pcOffset, bool isCallElem) - : ICStubCompiler(cx, kind), + : ICStubCompiler(cx, kind, Engine::Baseline), isCallElem_(false), firstMonitorStub_(firstMonitorStub), obj_(obj), @@ -3002,7 +1840,7 @@ class ICGetElem_String : public ICStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::GetElem_String) {} + : ICStubCompiler(cx, ICStub::GetElem_String, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { return newStub(space, getStubCode()); @@ -3040,15 +1878,18 @@ class ICGetElem_Dense : public ICMonitoredStub virtual int32_t getKey() const { #if JS_HAS_NO_SUCH_METHOD - return static_cast(kind) | (static_cast(isCallElem_) << 16); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(isCallElem_) << 17); #else - return static_cast(kind); + return static_cast(engine_) | + (static_cast(kind) << 1); #endif } public: Compiler(JSContext* cx, ICStub* firstMonitorStub, Shape* shape, bool isCallElem) - : ICStubCompiler(cx, ICStub::GetElem_Dense), + : ICStubCompiler(cx, ICStub::GetElem_Dense, Engine::Baseline), firstMonitorStub_(firstMonitorStub), shape_(cx, shape), isCallElem_(isCallElem) @@ -3089,13 +1930,14 @@ class ICGetElem_UnboxedArray : public ICMonitoredStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | - (static_cast(elementType_) << 16); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(elementType_) << 17); } public: Compiler(JSContext* cx, ICStub* firstMonitorStub, ObjectGroup* group) - : ICStubCompiler(cx, ICStub::GetElem_UnboxedArray), + : ICStubCompiler(cx, ICStub::GetElem_UnboxedArray, Engine::Baseline), firstMonitorStub_(firstMonitorStub), group_(cx, group), elementType_(group->unboxedLayout().elementType()) @@ -3154,14 +1996,15 @@ class ICGetElem_TypedArray : public ICStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | - (static_cast(type_) << 16) | - (static_cast(layout_) << 24); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(type_) << 17) | + (static_cast(layout_) << 25); } public: Compiler(JSContext* cx, Shape* shape, Scalar::Type type) - : ICStubCompiler(cx, ICStub::GetElem_TypedArray), + : ICStubCompiler(cx, ICStub::GetElem_TypedArray, Engine::Baseline), shape_(cx, shape), type_(type), layout_(GetTypedThingLayout(shape->getObjectClass())) @@ -3204,17 +2047,20 @@ class ICGetElem_Arguments : public ICMonitoredStub virtual int32_t getKey() const { #if JS_HAS_NO_SUCH_METHOD - return static_cast(kind) | - static_cast(isCallElem_ << 16) | - (static_cast(which_) << 17); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(isCallElem_) << 17) | + (static_cast(which_) << 18); #else - return static_cast(kind) | (static_cast(which_) << 16); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(which_) << 17); #endif } public: Compiler(JSContext* cx, ICStub* firstMonitorStub, Which which, bool isCallElem) - : ICStubCompiler(cx, ICStub::GetElem_Arguments), + : ICStubCompiler(cx, ICStub::GetElem_Arguments, Engine::Baseline), firstMonitorStub_(firstMonitorStub), which_(which), isCallElem_(isCallElem) @@ -3255,7 +2101,7 @@ class ICSetElem_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::SetElem_Fallback) + : ICStubCompiler(cx, ICStub::SetElem_Fallback, Engine::Baseline) { } ICStub* getStub(ICStubSpace* space) { @@ -3297,12 +2143,13 @@ class ICSetElem_DenseOrUnboxedArray : public ICUpdatedStub public: virtual int32_t getKey() const { - return static_cast(kind) | - (static_cast(unboxedType_) << 16); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(unboxedType_) << 17); } Compiler(JSContext* cx, Shape* shape, HandleObjectGroup group) - : ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArray), + : ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArray, Engine::Baseline), shape_(cx, shape), group_(cx, group), unboxedType_(shape ? JSVAL_TYPE_MAGIC : group->unboxedLayout().elementType()) @@ -3404,14 +2251,15 @@ class ICSetElemDenseOrUnboxedArrayAddCompiler : public ICStubCompiler { protected: virtual int32_t getKey() const { - return static_cast(kind) | - (static_cast(protoChainDepth_) << 16) | - (static_cast(unboxedType_) << 19); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(protoChainDepth_) << 17) | + (static_cast(unboxedType_) << 20); } public: ICSetElemDenseOrUnboxedArrayAddCompiler(JSContext* cx, HandleObject obj, size_t protoChainDepth) - : ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArrayAdd), + : ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArrayAdd, Engine::Baseline), obj_(cx, obj), protoChainDepth_(protoChainDepth), unboxedType_(obj->is() @@ -3467,15 +2315,16 @@ class ICSetElem_TypedArray : public ICStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | - (static_cast(type_) << 16) | - (static_cast(layout_) << 24) | - (static_cast(expectOutOfBounds_) << 28); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(type_) << 17) | + (static_cast(layout_) << 25) | + (static_cast(expectOutOfBounds_) << 29); } public: Compiler(JSContext* cx, Shape* shape, Scalar::Type type, bool expectOutOfBounds) - : ICStubCompiler(cx, ICStub::SetElem_TypedArray), + : ICStubCompiler(cx, ICStub::SetElem_TypedArray, Engine::Baseline), shape_(cx, shape), type_(type), layout_(GetTypedThingLayout(shape->getObjectClass())), @@ -3508,7 +2357,7 @@ class ICIn_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::In_Fallback) + : ICStubCompiler(cx, ICStub::In_Fallback, Engine::Baseline) { } ICStub* getStub(ICStubSpace* space) { @@ -3593,7 +2442,7 @@ class ICInNativeCompiler : public ICStubCompiler public: ICInNativeCompiler(JSContext* cx, ICStub::Kind kind, HandleObject obj, HandleObject holder, HandlePropertyName name) - : ICStubCompiler(cx, kind), + : ICStubCompiler(cx, kind, Engine::Baseline), obj_(cx, obj), holder_(cx, holder), name_(cx, name) @@ -3684,7 +2533,9 @@ class ICInNativeDoesNotExistCompiler : public ICStubCompiler protected: virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(protoChainDepth_) << 16); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(protoChainDepth_) << 17); } bool generateStubCode(MacroAssembler& masm); @@ -3725,7 +2576,7 @@ class ICIn_Dense : public ICStub public: Compiler(JSContext* cx, Shape* shape) - : ICStubCompiler(cx, ICStub::In_Dense), + : ICStubCompiler(cx, ICStub::In_Dense, Engine::Baseline), shape_(cx, shape) {} @@ -3763,7 +2614,7 @@ class ICGetName_Fallback : public ICMonitoredFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::GetName_Fallback) + : ICStubCompiler(cx, ICStub::GetName_Fallback, Engine::Baseline) { } ICStub* getStub(ICStubSpace* space) { @@ -3807,7 +2658,7 @@ class ICGetName_Global : public ICMonitoredStub public: Compiler(JSContext* cx, ICStub* firstMonitorStub, Shape* shape, uint32_t slot) - : ICStubCompiler(cx, ICStub::GetName_Global), + : ICStubCompiler(cx, ICStub::GetName_Global, Engine::Baseline), firstMonitorStub_(firstMonitorStub), shape_(cx, shape), slot_(slot) @@ -3866,13 +2717,15 @@ class ICGetName_Scope : public ICMonitoredStub protected: virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(isFixedSlot_) << 16); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(isFixedSlot_) << 17); } public: Compiler(JSContext* cx, ICStub* firstMonitorStub, AutoShapeVector* shapes, bool isFixedSlot, uint32_t offset) - : ICStubCompiler(cx, GetStubKind()), + : ICStubCompiler(cx, GetStubKind(), Engine::Baseline), firstMonitorStub_(firstMonitorStub), shapes_(shapes), isFixedSlot_(isFixedSlot), @@ -3904,7 +2757,7 @@ class ICBindName_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::BindName_Fallback) + : ICStubCompiler(cx, ICStub::BindName_Fallback, Engine::Baseline) { } ICStub* getStub(ICStubSpace* space) { @@ -3930,7 +2783,7 @@ class ICGetIntrinsic_Fallback : public ICMonitoredFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::GetIntrinsic_Fallback) + : ICStubCompiler(cx, ICStub::GetIntrinsic_Fallback, Engine::Baseline) { } ICStub* getStub(ICStubSpace* space) { @@ -3968,7 +2821,7 @@ class ICGetIntrinsic_Constant : public ICStub public: Compiler(JSContext* cx, HandleValue value) - : ICStubCompiler(cx, ICStub::GetIntrinsic_Constant), + : ICStubCompiler(cx, ICStub::GetIntrinsic_Constant, Engine::Baseline), value_(value) {} @@ -4006,6 +2859,11 @@ class ICGetProp_Fallback : public ICMonitoredFallbackStub } class Compiler : public ICStubCompiler { + public: + static const int32_t BASELINE_KEY = + (static_cast(Engine::Baseline)) | + (static_cast(ICStub::GetProp_Fallback) << 1); + protected: uint32_t returnOffset_; bool generateStubCode(MacroAssembler& masm); @@ -4013,7 +2871,7 @@ class ICGetProp_Fallback : public ICMonitoredFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::GetProp_Fallback) + : ICStubCompiler(cx, ICStub::GetProp_Fallback, Engine::Baseline) { } ICStub* getStub(ICStubSpace* space) { @@ -4044,7 +2902,7 @@ class ICGetProp_Generic : public ICMonitoredStub ICStub* firstMonitorStub_; public: explicit Compiler(JSContext* cx, ICStub* firstMonitorStub) - : ICStubCompiler(cx, ICStub::GetProp_Generic), + : ICStubCompiler(cx, ICStub::GetProp_Generic, Engine::Baseline), firstMonitorStub_(firstMonitorStub) {} @@ -4069,7 +2927,7 @@ class ICGetProp_ArrayLength : public ICStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::GetProp_ArrayLength) + : ICStubCompiler(cx, ICStub::GetProp_ArrayLength, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { @@ -4093,7 +2951,7 @@ class ICGetProp_UnboxedArrayLength : public ICStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::GetProp_UnboxedArrayLength) + : ICStubCompiler(cx, ICStub::GetProp_UnboxedArrayLength, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { @@ -4145,15 +3003,16 @@ class ICGetProp_Primitive : public ICMonitoredStub protected: virtual int32_t getKey() const { static_assert(sizeof(JSValueType) == 1, "JSValueType should fit in one byte"); - return static_cast(kind) - | (static_cast(isFixedSlot_) << 16) - | (static_cast(primitiveType_) << 24); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(isFixedSlot_) << 17) | + (static_cast(primitiveType_) << 25); } public: Compiler(JSContext* cx, ICStub* firstMonitorStub, JSValueType primitiveType, HandleObject prototype, bool isFixedSlot, uint32_t offset) - : ICStubCompiler(cx, ICStub::GetProp_Primitive), + : ICStubCompiler(cx, ICStub::GetProp_Primitive, Engine::Baseline), firstMonitorStub_(firstMonitorStub), primitiveType_(primitiveType), prototype_(cx, prototype), @@ -4184,7 +3043,7 @@ class ICGetProp_StringLength : public ICStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::GetProp_StringLength) + : ICStubCompiler(cx, ICStub::GetProp_StringLength, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { @@ -4297,11 +3156,12 @@ class ICGetPropNativeCompiler : public ICStubCompiler protected: virtual int32_t getKey() const { - return static_cast(kind) | - (static_cast(isCallProp_) << 16) | - (static_cast(isFixedSlot_) << 17) | - (static_cast(inputDefinitelyObject_) << 18) | - (HeapReceiverGuard::keyBits(obj_) << 19); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(isCallProp_) << 17) | + (static_cast(isFixedSlot_) << 18) | + (static_cast(inputDefinitelyObject_) << 19) | + (HeapReceiverGuard::keyBits(obj_) << 20); } public: @@ -4309,7 +3169,7 @@ class ICGetPropNativeCompiler : public ICStubCompiler ICStub* firstMonitorStub, HandleObject obj, HandleObject holder, HandlePropertyName propName, bool isFixedSlot, uint32_t offset, bool inputDefinitelyObject = false) - : ICStubCompiler(cx, kind), + : ICStubCompiler(cx, kind, Engine::Baseline), isCallProp_(isCallProp), firstMonitorStub_(firstMonitorStub), obj_(obj), @@ -4396,9 +3256,10 @@ class ICGetPropNativeDoesNotExistCompiler : public ICStubCompiler protected: virtual int32_t getKey() const { - return static_cast(kind) | - (HeapReceiverGuard::keyBits(obj_) << 16) | - (static_cast(protoChainDepth_) << 18); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (HeapReceiverGuard::keyBits(obj_) << 17) | + (static_cast(protoChainDepth_) << 19); } bool generateStubCode(MacroAssembler& masm); @@ -4454,13 +3315,15 @@ class ICGetProp_Unboxed : public ICMonitoredStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(fieldType_)) << 16; + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(fieldType_) << 17); } public: Compiler(JSContext* cx, ICStub* firstMonitorStub, ObjectGroup* group, uint32_t fieldOffset, JSValueType fieldType) - : ICStubCompiler(cx, ICStub::GetProp_Unboxed), + : ICStubCompiler(cx, ICStub::GetProp_Unboxed, Engine::Baseline), firstMonitorStub_(firstMonitorStub), group_(cx, group), fieldOffset_(fieldOffset), @@ -4520,15 +3383,16 @@ class ICGetProp_TypedObject : public ICMonitoredStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | - (static_cast(SimpleTypeDescrKey(fieldDescr_)) << 16) | - (static_cast(layout_) << 24); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(SimpleTypeDescrKey(fieldDescr_)) << 17) | + (static_cast(layout_) << 25); } public: Compiler(JSContext* cx, ICStub* firstMonitorStub, Shape* shape, uint32_t fieldOffset, SimpleTypeDescr* fieldDescr) - : ICStubCompiler(cx, ICStub::GetProp_TypedObject), + : ICStubCompiler(cx, ICStub::GetProp_TypedObject, Engine::Baseline), firstMonitorStub_(firstMonitorStub), shape_(cx, shape), fieldOffset_(fieldOffset), @@ -4619,17 +3483,18 @@ class ICGetPropCallGetter : public ICMonitoredStub virtual int32_t getKey() const { // ICGetProp_CallNative::Compiler::getKey adds more bits to our // return value, so be careful when making changes here. - return static_cast(kind) | - (HeapReceiverGuard::keyBits(receiver_) << 16) | - (static_cast(!!outerClass_) << 18) | - (static_cast(receiver_ != holder_) << 19); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (HeapReceiverGuard::keyBits(receiver_) << 17) | + (static_cast(!!outerClass_) << 19) | + (static_cast(receiver_ != holder_) << 20); } public: Compiler(JSContext* cx, ICStub::Kind kind, ICStub* firstMonitorStub, HandleObject receiver, HandleObject holder, HandleFunction getter, uint32_t pcOffset, const Class* outerClass) - : ICStubCompiler(cx, kind), + : ICStubCompiler(cx, kind, Engine::Baseline), firstMonitorStub_(firstMonitorStub), receiver_(cx, receiver), holder_(cx, holder), @@ -4711,8 +3576,8 @@ class ICGetProp_CallNative : public ICGetPropCallGetter virtual int32_t getKey() const { int32_t baseKey = ICGetPropCallGetter::Compiler::getKey(); - MOZ_ASSERT((baseKey >> 20) == 0); - return baseKey | (static_cast(inputDefinitelyObject_) << 20); + MOZ_ASSERT((baseKey >> 21) == 0); + return baseKey | (static_cast(inputDefinitelyObject_) << 21); } public: @@ -4889,7 +3754,7 @@ class ICGetProp_DOMProxyShadowed : public ICMonitoredStub public: Compiler(JSContext* cx, ICStub* firstMonitorStub, Handle proxy, HandlePropertyName name, uint32_t pcOffset) - : ICStubCompiler(cx, ICStub::GetProp_CallNative), + : ICStubCompiler(cx, ICStub::GetProp_CallNative, Engine::Baseline), firstMonitorStub_(firstMonitorStub), proxy_(cx, proxy), name_(cx, name), @@ -4919,12 +3784,14 @@ class ICGetProp_ArgumentsLength : public ICStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(which_) << 16); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(which_) << 17); } public: Compiler(JSContext* cx, Which which) - : ICStubCompiler(cx, ICStub::GetProp_ArgumentsLength), + : ICStubCompiler(cx, ICStub::GetProp_ArgumentsLength, Engine::Baseline), which_(which) {} @@ -4949,7 +3816,7 @@ class ICGetProp_ArgumentsCallee : public ICMonitoredStub public: Compiler(JSContext* cx, ICStub* firstMonitorStub) - : ICStubCompiler(cx, ICStub::GetProp_ArgumentsCallee), + : ICStubCompiler(cx, ICStub::GetProp_ArgumentsCallee, Engine::Baseline), firstMonitorStub_(firstMonitorStub) {} @@ -4985,6 +3852,11 @@ class ICSetProp_Fallback : public ICFallbackStub } class Compiler : public ICStubCompiler { + public: + static const int32_t BASELINE_KEY = + (static_cast(Engine::Baseline)) | + (static_cast(ICStub::SetProp_Fallback) << 1); + protected: uint32_t returnOffset_; bool generateStubCode(MacroAssembler& masm); @@ -4992,7 +3864,7 @@ class ICSetProp_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::SetProp_Fallback) + : ICStubCompiler(cx, ICStub::SetProp_Fallback, Engine::Baseline) { } ICStub* getStub(ICStubSpace* space) { @@ -5043,16 +3915,17 @@ class ICSetProp_Native : public ICUpdatedStub protected: virtual int32_t getKey() const { - return static_cast(kind) | - (static_cast(isFixedSlot_) << 16) | - (static_cast(obj_->is()) << 17); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(isFixedSlot_) << 17) | + (static_cast(obj_->is()) << 18); } bool generateStubCode(MacroAssembler& masm); public: Compiler(JSContext* cx, HandleObject obj, bool isFixedSlot, uint32_t offset) - : ICStubCompiler(cx, ICStub::SetProp_Native), + : ICStubCompiler(cx, ICStub::SetProp_Native, Engine::Baseline), obj_(cx, obj), isFixedSlot_(isFixedSlot), offset_(offset) @@ -5147,10 +4020,11 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler protected: virtual int32_t getKey() const { - return static_cast(kind) | - (static_cast(isFixedSlot_) << 16) | - (static_cast(obj_->is()) << 17) | - (static_cast(protoChainDepth_) << 18); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(isFixedSlot_) << 17) | + (static_cast(obj_->is()) << 18) | + (static_cast(protoChainDepth_) << 19); } bool generateStubCode(MacroAssembler& masm); @@ -5222,14 +4096,15 @@ class ICSetProp_Unboxed : public ICUpdatedStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | - (static_cast(fieldType_) << 16); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(fieldType_) << 17); } public: Compiler(JSContext* cx, ObjectGroup* group, uint32_t fieldOffset, JSValueType fieldType) - : ICStubCompiler(cx, ICStub::SetProp_Unboxed), + : ICStubCompiler(cx, ICStub::SetProp_Unboxed, Engine::Baseline), group_(cx, group), fieldOffset_(fieldOffset), fieldType_(fieldType) @@ -5301,15 +4176,16 @@ class ICSetProp_TypedObject : public ICUpdatedStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | - (static_cast(SimpleTypeDescrKey(fieldDescr_)) << 16) | - (static_cast(layout_) << 24); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(SimpleTypeDescrKey(fieldDescr_)) << 17) | + (static_cast(layout_) << 25); } public: Compiler(JSContext* cx, Shape* shape, ObjectGroup* group, uint32_t fieldOffset, SimpleTypeDescr* fieldDescr) - : ICStubCompiler(cx, ICStub::SetProp_TypedObject), + : ICStubCompiler(cx, ICStub::SetProp_TypedObject, Engine::Baseline), shape_(cx, shape), group_(cx, group), fieldOffset_(fieldOffset), @@ -5406,15 +4282,16 @@ class ICSetPropCallSetter : public ICStub uint32_t pcOffset_; virtual int32_t getKey() const { - return static_cast(kind) | - (HeapReceiverGuard::keyBits(receiver_) << 16) | - (static_cast(receiver_ != holder_) << 19); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (HeapReceiverGuard::keyBits(receiver_) << 17) | + (static_cast(receiver_ != holder_) << 20); } public: Compiler(JSContext* cx, ICStub::Kind kind, HandleObject receiver, HandleObject holder, HandleFunction setter, uint32_t pcOffset) - : ICStubCompiler(cx, kind), + : ICStubCompiler(cx, kind, Engine::Baseline), receiver_(cx, receiver), holder_(cx, holder), setter_(cx, setter), @@ -5511,7 +4388,7 @@ class ICCallStubCompiler : public ICStubCompiler { protected: ICCallStubCompiler(JSContext* cx, ICStub::Kind kind) - : ICStubCompiler(cx, kind) + : ICStubCompiler(cx, kind, Engine::Baseline) { } enum FunApplyThing { @@ -5574,8 +4451,17 @@ class ICCall_Fallback : public ICMonitoredFallbackStub // Compiler for this stub kind. class Compiler : public ICCallStubCompiler { public: - static const int32_t CALL_KEY = static_cast(ICStub::Call_Fallback); - static const int32_t CONSTRUCT_KEY = static_cast(ICStub::Call_Fallback) | (1 << 17); + static const int32_t BASELINE_CALL_KEY = + (static_cast(Engine::Baseline)) | + (static_cast(ICStub::Call_Fallback) << 1) | + (0 << 17) | // spread + (0 << 18); // constructing + static const int32_t BASELINE_CONSTRUCT_KEY = + (static_cast(Engine::Baseline)) | + (static_cast(ICStub::Call_Fallback) << 1) | + (0 << 17) | // spread + (1 << 18); // constructing + protected: bool isConstructing_; bool isSpread_; @@ -5584,8 +4470,10 @@ class ICCall_Fallback : public ICMonitoredFallbackStub bool postGenerateStubCode(MacroAssembler& masm, Handle code); virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(isSpread_) << 16) | - (static_cast(isConstructing_) << 17); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(isSpread_) << 17) | + (static_cast(isConstructing_) << 18); } public: @@ -5674,8 +4562,10 @@ class ICCallScriptedCompiler : public ICCallStubCompiler { bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(isConstructing_) << 16) | - (static_cast(isSpread_) << 17); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(isConstructing_) << 17) | + (static_cast(isSpread_) << 18); } public: @@ -5764,8 +4654,10 @@ class ICCall_Native : public ICMonitoredStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(isConstructing_) << 16) | - (static_cast(isSpread_) << 17); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(isConstructing_) << 17) | + (static_cast(isSpread_) << 18); } public: @@ -5838,7 +4730,9 @@ class ICCall_ClassHook : public ICMonitoredStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(isConstructing_) << 16); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(isConstructing_) << 17); } public: @@ -5897,7 +4791,8 @@ class ICCall_ScriptedApplyArray : public ICMonitoredStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind); + return static_cast(engine_) | + (static_cast(kind) << 1); } public: @@ -5944,7 +4839,8 @@ class ICCall_ScriptedApplyArguments : public ICMonitoredStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind); + return static_cast(engine_) | + (static_cast(kind) << 1); } public: @@ -5990,7 +4886,8 @@ class ICCall_ScriptedFunCall : public ICMonitoredStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind); + return static_cast(engine_) | + (static_cast(kind) << 1); } public: @@ -6060,7 +4957,8 @@ class ICCall_StringSplit : public ICMonitoredStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind); + return static_cast(engine_) | + (static_cast(kind) << 1); } public: @@ -6097,7 +4995,7 @@ class ICCall_IsSuspendedStarGenerator : public ICStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::Call_IsSuspendedStarGenerator) + : ICStubCompiler(cx, ICStub::Call_IsSuspendedStarGenerator, Engine::Baseline) {} ICStub* getStub(ICStubSpace* space) { return newStub(space, getStubCode()); @@ -6133,7 +5031,7 @@ class ICTableSwitch : public ICStub public: Compiler(JSContext* cx, jsbytecode* pc) - : ICStubCompiler(cx, ICStub::TableSwitch), pc_(pc) + : ICStubCompiler(cx, ICStub::TableSwitch, Engine::Baseline), pc_(pc) {} ICStub* getStub(ICStubSpace* space); @@ -6156,7 +5054,7 @@ class ICIteratorNew_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::IteratorNew_Fallback) + : ICStubCompiler(cx, ICStub::IteratorNew_Fallback, Engine::Baseline) { } ICStub* getStub(ICStubSpace* space) { @@ -6189,7 +5087,7 @@ class ICIteratorMore_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::IteratorMore_Fallback) + : ICStubCompiler(cx, ICStub::IteratorMore_Fallback, Engine::Baseline) { } ICStub* getStub(ICStubSpace* space) { @@ -6214,7 +5112,7 @@ class ICIteratorMore_Native : public ICStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::IteratorMore_Native) + : ICStubCompiler(cx, ICStub::IteratorMore_Native, Engine::Baseline) { } ICStub* getStub(ICStubSpace* space) { @@ -6239,7 +5137,7 @@ class ICIteratorClose_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::IteratorClose_Fallback) + : ICStubCompiler(cx, ICStub::IteratorClose_Fallback, Engine::Baseline) { } ICStub* getStub(ICStubSpace* space) { @@ -6276,7 +5174,7 @@ class ICInstanceOf_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::InstanceOf_Fallback) + : ICStubCompiler(cx, ICStub::InstanceOf_Fallback, Engine::Baseline) { } ICStub* getStub(ICStubSpace* space) { @@ -6325,7 +5223,7 @@ class ICInstanceOf_Function : public ICStub public: Compiler(JSContext* cx, Shape* shape, JSObject* prototypeObj, uint32_t slot) - : ICStubCompiler(cx, ICStub::InstanceOf_Function), + : ICStubCompiler(cx, ICStub::InstanceOf_Function, Engine::Baseline), shape_(cx, shape), prototypeObj_(cx, prototypeObj), slot_(slot) @@ -6356,7 +5254,7 @@ class ICTypeOf_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::TypeOf_Fallback) + : ICStubCompiler(cx, ICStub::TypeOf_Fallback, Engine::Baseline) { } ICStub* getStub(ICStubSpace* space) { @@ -6388,12 +5286,14 @@ class ICTypeOf_Typed : public ICFallbackStub bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(type_) << 16); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(type_) << 17); } public: Compiler(JSContext* cx, JSType type, HandleString string) - : ICStubCompiler(cx, ICStub::TypeOf_Typed), + : ICStubCompiler(cx, ICStub::TypeOf_Typed, Engine::Baseline), type_(type), typeString_(cx, string) { } @@ -6428,7 +5328,7 @@ class ICRest_Fallback : public ICFallbackStub public: Compiler(JSContext* cx, ArrayObject* templateObject) - : ICStubCompiler(cx, ICStub::Rest_Fallback), + : ICStubCompiler(cx, ICStub::Rest_Fallback, Engine::Baseline), templateObject(cx, templateObject) { } @@ -6456,7 +5356,7 @@ class ICRetSub_Fallback : public ICFallbackStub public: explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::RetSub_Fallback) + : ICStubCompiler(cx, ICStub::RetSub_Fallback, Engine::Baseline) { } ICStub* getStub(ICStubSpace* space) { @@ -6497,7 +5397,7 @@ class ICRetSub_Resume : public ICStub public: Compiler(JSContext* cx, uint32_t pcOffset, uint8_t* addr) - : ICStubCompiler(cx, ICStub::RetSub_Resume), + : ICStubCompiler(cx, ICStub::RetSub_Resume, Engine::Baseline), pcOffset_(pcOffset), addr_(addr) { } diff --git a/js/src/jit/BaselineICList.h b/js/src/jit/BaselineICList.h new file mode 100644 index 0000000000..afed113e35 --- /dev/null +++ b/js/src/jit/BaselineICList.h @@ -0,0 +1,160 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_BaselineICList_h +#define jit_BaselineICList_h + +namespace js { +namespace jit { + +// List of IC stub kinds that can only run in Baseline. +#define IC_BASELINE_STUB_KIND_LIST(_) \ + _(WarmUpCounter_Fallback) \ + \ + _(TypeMonitor_Fallback) \ + _(TypeMonitor_SingleObject) \ + _(TypeMonitor_ObjectGroup) \ + _(TypeMonitor_PrimitiveSet) \ + \ + _(TypeUpdate_Fallback) \ + _(TypeUpdate_SingleObject) \ + _(TypeUpdate_ObjectGroup) \ + _(TypeUpdate_PrimitiveSet) \ + \ + _(This_Fallback) \ + \ + _(NewArray_Fallback) \ + _(NewObject_Fallback) \ + _(NewObject_WithTemplate) \ + \ + _(Compare_Fallback) \ + _(Compare_Int32) \ + _(Compare_Double) \ + _(Compare_NumberWithUndefined) \ + _(Compare_String) \ + _(Compare_Boolean) \ + _(Compare_Object) \ + _(Compare_ObjectWithUndefined) \ + _(Compare_Int32WithBoolean) \ + \ + _(ToBool_Fallback) \ + _(ToBool_Int32) \ + _(ToBool_String) \ + _(ToBool_NullUndefined) \ + _(ToBool_Double) \ + _(ToBool_Object) \ + \ + _(ToNumber_Fallback) \ + \ + _(BinaryArith_Fallback) \ + _(BinaryArith_Int32) \ + _(BinaryArith_Double) \ + _(BinaryArith_StringConcat) \ + _(BinaryArith_StringObjectConcat) \ + _(BinaryArith_BooleanWithInt32) \ + _(BinaryArith_DoubleWithInt32) \ + \ + _(UnaryArith_Fallback) \ + _(UnaryArith_Int32) \ + _(UnaryArith_Double) \ + \ + _(Call_Fallback) \ + _(Call_Scripted) \ + _(Call_AnyScripted) \ + _(Call_Native) \ + _(Call_ClassHook) \ + _(Call_ScriptedApplyArray) \ + _(Call_ScriptedApplyArguments) \ + _(Call_ScriptedFunCall) \ + _(Call_StringSplit) \ + _(Call_IsSuspendedStarGenerator) \ + \ + _(GetElem_Fallback) \ + _(GetElem_NativeSlot) \ + _(GetElem_NativePrototypeSlot) \ + _(GetElem_NativePrototypeCallNative) \ + _(GetElem_NativePrototypeCallScripted) \ + _(GetElem_String) \ + _(GetElem_Dense) \ + _(GetElem_UnboxedArray) \ + _(GetElem_TypedArray) \ + _(GetElem_Arguments) \ + \ + _(SetElem_Fallback) \ + _(SetElem_DenseOrUnboxedArray) \ + _(SetElem_DenseOrUnboxedArrayAdd) \ + _(SetElem_TypedArray) \ + \ + _(In_Fallback) \ + _(In_Native) \ + _(In_NativePrototype) \ + _(In_NativeDoesNotExist) \ + _(In_Dense) \ + \ + _(GetName_Fallback) \ + _(GetName_Global) \ + _(GetName_Scope0) \ + _(GetName_Scope1) \ + _(GetName_Scope2) \ + _(GetName_Scope3) \ + _(GetName_Scope4) \ + _(GetName_Scope5) \ + _(GetName_Scope6) \ + \ + _(BindName_Fallback) \ + \ + _(GetIntrinsic_Fallback) \ + _(GetIntrinsic_Constant) \ + \ + _(GetProp_Fallback) \ + _(GetProp_ArrayLength) \ + _(GetProp_UnboxedArrayLength) \ + _(GetProp_Primitive) \ + _(GetProp_StringLength) \ + _(GetProp_Native) \ + _(GetProp_NativeDoesNotExist) \ + _(GetProp_NativePrototype) \ + _(GetProp_Unboxed) \ + _(GetProp_TypedObject) \ + _(GetProp_CallScripted) \ + _(GetProp_CallNative) \ + _(GetProp_CallDOMProxyNative) \ + _(GetProp_CallDOMProxyWithGenerationNative) \ + _(GetProp_DOMProxyShadowed) \ + _(GetProp_ArgumentsLength) \ + _(GetProp_ArgumentsCallee) \ + _(GetProp_Generic) \ + \ + _(SetProp_Fallback) \ + _(SetProp_Native) \ + _(SetProp_NativeAdd) \ + _(SetProp_Unboxed) \ + _(SetProp_TypedObject) \ + _(SetProp_CallScripted) \ + _(SetProp_CallNative) \ + \ + _(TableSwitch) \ + \ + _(IteratorNew_Fallback) \ + _(IteratorMore_Fallback) \ + _(IteratorMore_Native) \ + _(IteratorClose_Fallback) \ + \ + _(InstanceOf_Fallback) \ + _(InstanceOf_Function) \ + \ + _(TypeOf_Fallback) \ + _(TypeOf_Typed) \ + \ + _(Rest_Fallback) \ + \ + _(RetSub_Fallback) \ + _(RetSub_Resume) + +} // namespace jit +} // namespace js + +#endif /* jit_BaselineICList_h */ diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index eaa13e5753..fdf4f9f3cc 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -3451,44 +3451,26 @@ typedef bool (*DirectEvalSFn)(JSContext*, HandleObject, HandleScript, HandleValu static const VMFunction DirectEvalStringInfo = FunctionInfo(DirectEvalStringFromIon); void -CodeGenerator::visitCallDirectEvalS(LCallDirectEvalS* lir) +CodeGenerator::visitCallDirectEval(LCallDirectEval* lir) { Register scopeChain = ToRegister(lir->getScopeChain()); Register string = ToRegister(lir->getString()); pushArg(ImmPtr(lir->mir()->pc())); pushArg(string); - pushArg(ToValue(lir, LCallDirectEvalS::ThisValue)); + pushArg(ToValue(lir, LCallDirectEval::ThisValue)); pushArg(ImmGCPtr(gen->info().script())); pushArg(scopeChain); callVM(DirectEvalStringInfo, lir); } -typedef bool (*DirectEvalVFn)(JSContext*, HandleObject, HandleScript, HandleValue, HandleValue, - jsbytecode*, MutableHandleValue); -static const VMFunction DirectEvalValueInfo = FunctionInfo(DirectEvalValueFromIon); - -void -CodeGenerator::visitCallDirectEvalV(LCallDirectEvalV* lir) -{ - Register scopeChain = ToRegister(lir->getScopeChain()); - - pushArg(ImmPtr(lir->mir()->pc())); - pushArg(ToValue(lir, LCallDirectEvalV::Argument)); - pushArg(ToValue(lir, LCallDirectEvalV::ThisValue)); - pushArg(ImmGCPtr(gen->info().script())); - pushArg(scopeChain); - - callVM(DirectEvalValueInfo, lir); -} - -// Registers safe for use before generatePrologue(). -static const uint32_t EntryTempMask = Registers::TempMask & ~(1 << OsrFrameReg.code()); - void CodeGenerator::generateArgumentsChecks(bool bailout) { + // Registers safe for use before generatePrologue(). + static const uint32_t EntryTempMask = Registers::TempMask & ~(1 << OsrFrameReg.code()); + // This function can be used the normal way to check the argument types, // before entering the function and bailout when arguments don't match. // For debug purpose, this is can also be used to force/check that the @@ -3525,6 +3507,18 @@ CodeGenerator::generateArgumentsChecks(bool bailout) Label success; masm.jump(&success); masm.bind(&miss); + + // Check for cases where the type set guard might have missed due to + // changing object groups. + for (uint32_t i = info.startArgSlot(); i < info.endArgSlot(); i++) { + Label skip; + Address addr(StackPointer, ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value))); + masm.branchTestObject(Assembler::NotEqual, addr, &skip); + Register obj = masm.extractObject(addr, temp); + masm.guardTypeSetMightBeIncomplete(obj, temp, &success); + masm.bind(&skip); + } + masm.assumeUnreachable("Argument check fail."); masm.bind(&success); } @@ -4966,6 +4960,22 @@ CodeGenerator::visitTypedArrayElements(LTypedArrayElements* lir) masm.loadPtr(Address(obj, TypedArrayLayout::dataOffset()), out); } +void +CodeGenerator::visitSetDisjointTypedElements(LSetDisjointTypedElements* lir) +{ + Register target = ToRegister(lir->target()); + Register targetOffset = ToRegister(lir->targetOffset()); + Register source = ToRegister(lir->source()); + + Register temp = ToRegister(lir->temp()); + + masm.setupUnalignedABICall(3, temp); + masm.passABIArg(target); + masm.passABIArg(targetOffset); + masm.passABIArg(source); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::SetDisjointTypedElements)); +} + void CodeGenerator::visitTypedObjectDescr(LTypedObjectDescr* lir) { @@ -6708,7 +6718,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) Address initLength(elements, ObjectElements::offsetOfInitializedLength()); masm.branchKey(Assembler::NotEqual, initLength, ToInt32Key(index), &callStub); } else { - Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); + Address initLength(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); masm.load32(initLength, ToRegister(temp)); masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), ToRegister(temp)); masm.branchKey(Assembler::NotEqual, ToRegister(temp), ToInt32Key(index), &callStub); @@ -10009,5 +10019,40 @@ CodeGenerator::visitDebugger(LDebugger* ins) bailoutFrom(&bail, ins->snapshot()); } +void +CodeGenerator::visitNewTarget(LNewTarget *ins) +{ + ValueOperand output = GetValueOutput(ins); + + // if (!isConstructing()) output = undefined + Label constructing, done; + Address calleeToken(masm.getStackPointer(), frameSize() + JitFrameLayout::offsetOfCalleeToken()); + masm.branchTestPtr(Assembler::NonZero, calleeToken, + Imm32(CalleeToken_FunctionConstructing), &constructing); + masm.moveValue(UndefinedValue(), output); + masm.jump(&done); + + masm.bind(&constructing); + + // else output = argv[Max(numActualArgs, numFormalArgs)] + Register argvLen = output.scratchReg(); + + Address actualArgsPtr(masm.getStackPointer(), frameSize() + JitFrameLayout::offsetOfNumActualArgs()); + masm.loadPtr(actualArgsPtr, argvLen); + + Label actualArgsSufficient; + + size_t numFormalArgs = ins->mirRaw()->block()->info().funMaybeLazy()->nargs(); + masm.branchPtr(Assembler::AboveOrEqual, argvLen, Imm32(numFormalArgs), + &actualArgsSufficient); + masm.move32(Imm32(numFormalArgs), argvLen); + masm.bind(&actualArgsSufficient); + + BaseValueIndex newTarget(masm.getStackPointer(), argvLen, frameSize() + JitFrameLayout::offsetOfActualArgs()); + masm.loadValue(newTarget, output); + + masm.bind(&done); +} + } // namespace jit } // namespace js diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index f1e7eceb90..a07a82d195 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -146,8 +146,7 @@ class CodeGenerator : public CodeGeneratorSpecific void visitUnreachable(LUnreachable* unreachable); void visitEncodeSnapshot(LEncodeSnapshot* lir); void visitGetDynamicName(LGetDynamicName* lir); - void visitCallDirectEvalS(LCallDirectEvalS* lir); - void visitCallDirectEvalV(LCallDirectEvalV* lir); + void visitCallDirectEval(LCallDirectEval* lir); void visitDoubleToInt32(LDoubleToInt32* lir); void visitFloat32ToInt32(LFloat32ToInt32* lir); void visitNewArrayCallVM(LNewArray* lir); @@ -184,6 +183,7 @@ class CodeGenerator : public CodeGeneratorSpecific void visitSetArrayLength(LSetArrayLength* lir); void visitTypedArrayLength(LTypedArrayLength* lir); void visitTypedArrayElements(LTypedArrayElements* lir); + void visitSetDisjointTypedElements(LSetDisjointTypedElements* lir); void visitTypedObjectElements(LTypedObjectElements* lir); void visitSetTypedObjectOffset(LSetTypedObjectOffset* lir); void visitTypedObjectDescr(LTypedObjectDescr* ins); @@ -322,6 +322,7 @@ class CodeGenerator : public CodeGeneratorSpecific void visitLexicalCheck(LLexicalCheck* ins); void visitThrowUninitializedLexical(LThrowUninitializedLexical* ins); void visitDebugger(LDebugger* ins); + void visitNewTarget(LNewTarget* ins); void visitCheckOverRecursed(LCheckOverRecursed* lir); void visitCheckOverRecursedFailure(CheckOverRecursedFailure* ool); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 6edf23b54b..2f89b8aabb 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -652,15 +652,15 @@ JitCompartment::sweep(FreeOp* fop, JSCompartment* compartment) stubCodes_->sweep(fop); // If the sweep removed the ICCall_Fallback stub, nullptr the baselineCallReturnAddr_ field. - if (!stubCodes_->lookup(ICCall_Fallback::Compiler::CALL_KEY)) + if (!stubCodes_->lookup(ICCall_Fallback::Compiler::BASELINE_CALL_KEY)) baselineCallReturnAddrs_[0] = nullptr; - if (!stubCodes_->lookup(ICCall_Fallback::Compiler::CONSTRUCT_KEY)) + if (!stubCodes_->lookup(ICCall_Fallback::Compiler::BASELINE_CONSTRUCT_KEY)) baselineCallReturnAddrs_[1] = nullptr; // Similarly for the ICGetProp_Fallback stub. - if (!stubCodes_->lookup(static_cast(ICStub::GetProp_Fallback))) + if (!stubCodes_->lookup(ICGetProp_Fallback::Compiler::BASELINE_KEY)) baselineGetPropReturnAddr_ = nullptr; - if (!stubCodes_->lookup(static_cast(ICStub::SetProp_Fallback))) + if (!stubCodes_->lookup(ICSetProp_Fallback::Compiler::BASELINE_KEY)) baselineSetPropReturnAddr_ = nullptr; if (stringConcatStub_ && !IsMarkedUnbarriered(&stringConcatStub_)) diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 62e74f2c1a..d57c61cfaa 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -2013,6 +2013,9 @@ IonBuilder::inspectOpcode(JSOp op) // Just fall through to the unsupported bytecode case. break; + case JSOP_NEWTARGET: + return jsop_newtarget(); + #ifdef DEBUG case JSOP_PUSHBLOCKSCOPE: case JSOP_FRESHENBLOCKSCOPE: @@ -9419,6 +9422,26 @@ IonBuilder::jsop_arguments() return true; } +bool +IonBuilder::jsop_newtarget() +{ + MOZ_ASSERT(info().funMaybeLazy()); + if (inliningDepth_ == 0) { + MNewTarget* newTarget = MNewTarget::New(alloc()); + current->add(newTarget); + current->push(newTarget); + return true; + } + + if (!info().constructing()) { + pushConstant(UndefinedValue()); + return true; + } + + current->push(inlineCallInfo_->getNewTarget()); + return true; +} + bool IonBuilder::jsop_rest() { diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 31f7b5760a..9b595c3e9b 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -707,6 +707,7 @@ class IonBuilder bool jsop_getaliasedvar(ScopeCoordinate sc); bool jsop_setaliasedvar(ScopeCoordinate sc); bool jsop_debugger(); + bool jsop_newtarget(); /* Inlining. */ @@ -804,8 +805,12 @@ class IonBuilder MIRType knownValueType); // TypedArray intrinsics. + enum WrappingBehavior { AllowWrappedTypedArrays, RejectWrappedTypedArrays }; + InliningStatus inlineIsTypedArrayHelper(CallInfo& callInfo, WrappingBehavior wrappingBehavior); InliningStatus inlineIsTypedArray(CallInfo& callInfo); + InliningStatus inlineIsPossiblyWrappedTypedArray(CallInfo& callInfo); InliningStatus inlineTypedArrayLength(CallInfo& callInfo); + InliningStatus inlineSetDisjointTypedElements(CallInfo& callInfo); // TypedObject intrinsics and natives. InliningStatus inlineObjectIsTypeDescr(CallInfo& callInfo); diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 4168875d74..81dae1aadc 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -1534,7 +1534,7 @@ class LJSCallInstructionHelper : public LCallInstructionHelpernumActualArgs(); } - + bool isConstructing() const { return mir()->isConstructing(); } @@ -1870,12 +1870,12 @@ class LGetDynamicName : public LCallInstructionHelper } }; -class LCallDirectEvalS : public LCallInstructionHelper +class LCallDirectEval : public LCallInstructionHelper { public: - LIR_HEADER(CallDirectEvalS) + LIR_HEADER(CallDirectEval) - LCallDirectEvalS(const LAllocation& scopeChain, const LAllocation& string) + LCallDirectEval(const LAllocation& scopeChain, const LAllocation& string) { setOperand(0, scopeChain); setOperand(1, string); @@ -1895,28 +1895,6 @@ class LCallDirectEvalS : public LCallInstructionHelper -{ - public: - LIR_HEADER(CallDirectEvalV) - - explicit LCallDirectEvalV(const LAllocation& scopeChain) - { - setOperand(0, scopeChain); - } - - static const size_t Argument = 1; - static const size_t ThisValue = 1 + BOX_PIECES; - - MCallDirectEval* mir() const { - return mir_->toCallDirectEval(); - } - - const LAllocation* getScopeChain() { - return getOperand(0); - } -}; - // Takes in either an integer or boolean input and tests it for truthiness. class LTestIAndBranch : public LControlInstructionHelper<2, 1, 0> { @@ -4250,6 +4228,43 @@ class LTypedArrayElements : public LInstructionHelper<1, 1, 0> } }; +// Assign +// +// target[targetOffset..targetOffset + source.length] = source[0..source.length] +// +// where the source element range doesn't overlap the target element range in +// memory. +class LSetDisjointTypedElements : public LCallInstructionHelper<0, 3, 1> +{ + public: + LIR_HEADER(SetDisjointTypedElements) + + explicit LSetDisjointTypedElements(const LAllocation& target, const LAllocation& targetOffset, + const LAllocation& source, const LDefinition& temp) + { + setOperand(0, target); + setOperand(1, targetOffset); + setOperand(2, source); + setTemp(0, temp); + } + + const LAllocation* target() { + return getOperand(0); + } + + const LAllocation* targetOffset() { + return getOperand(1); + } + + const LAllocation* source() { + return getOperand(2); + } + + const LDefinition* temp() { + return getTemp(0); + } +}; + // Load a typed object's descriptor. class LTypedObjectDescr : public LInstructionHelper<1, 1, 0> { @@ -6952,6 +6967,12 @@ class LDebugger : public LCallInstructionHelper<0, 0, 2> } }; +class LNewTarget : public LInstructionHelper +{ + public: + LIR_HEADER(NewTarget) +}; + } // namespace jit } // namespace js diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index 4206ea2d93..c35d0d3b58 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -74,8 +74,7 @@ _(Unreachable) \ _(EncodeSnapshot) \ _(GetDynamicName) \ - _(CallDirectEvalS) \ - _(CallDirectEvalV) \ + _(CallDirectEval) \ _(StackArgT) \ _(StackArgV) \ _(CreateThis) \ @@ -287,6 +286,7 @@ _(SetArrayLength) \ _(TypedArrayLength) \ _(TypedArrayElements) \ + _(SetDisjointTypedElements) \ _(TypedObjectDescr) \ _(TypedObjectElements) \ _(SetTypedObjectOffset) \ @@ -347,7 +347,8 @@ _(LexicalCheck) \ _(ThrowUninitializedLexical) \ _(NurseryObject) \ - _(Debugger) + _(Debugger) \ + _(NewTarget) #if defined(JS_CODEGEN_X86) # include "jit/x86/LOpcodes-x86.h" diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index f431711da7..dbf8e9c045 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -582,24 +582,13 @@ LIRGenerator::visitCallDirectEval(MCallDirectEval* ins) MOZ_ASSERT(scopeChain->type() == MIRType_Object); MDefinition* string = ins->getString(); - MOZ_ASSERT(string->type() == MIRType_String || string->type() == MIRType_Value); + MOZ_ASSERT(string->type() == MIRType_String); MDefinition* thisValue = ins->getThisValue(); - - LInstruction* lir; - if (string->type() == MIRType_String) { - lir = new(alloc()) LCallDirectEvalS(useRegisterAtStart(scopeChain), - useRegisterAtStart(string)); - } else { - lir = new(alloc()) LCallDirectEvalV(useRegisterAtStart(scopeChain)); - useBoxAtStart(lir, LCallDirectEvalV::Argument, string); - } - - if (string->type() == MIRType_String) - useBoxAtStart(lir, LCallDirectEvalS::ThisValue, thisValue); - else - useBoxAtStart(lir, LCallDirectEvalV::ThisValue, thisValue); + LInstruction* lir = new(alloc()) LCallDirectEval(useRegisterAtStart(scopeChain), + useRegisterAtStart(string)); + useBoxAtStart(lir, LCallDirectEval::ThisValue, thisValue); defineReturn(lir, ins); assignSafepoint(lir, ins); @@ -2418,6 +2407,27 @@ LIRGenerator::visitTypedArrayElements(MTypedArrayElements* ins) define(new(alloc()) LTypedArrayElements(useRegisterAtStart(ins->object())), ins); } +void +LIRGenerator::visitSetDisjointTypedElements(MSetDisjointTypedElements* ins) +{ + MOZ_ASSERT(ins->type() == MIRType_None); + + MDefinition* target = ins->target(); + MOZ_ASSERT(target->type() == MIRType_Object); + + MDefinition* targetOffset = ins->targetOffset(); + MOZ_ASSERT(targetOffset->type() == MIRType_Int32); + + MDefinition* source = ins->source(); + MOZ_ASSERT(source->type() == MIRType_Object); + + auto lir = new(alloc()) LSetDisjointTypedElements(useRegister(target), + useRegister(targetOffset), + useRegister(source), + temp()); + add(lir, ins); +} + void LIRGenerator::visitTypedObjectDescr(MTypedObjectDescr* ins) { @@ -3515,6 +3525,13 @@ LIRGenerator::visitGetFrameArgument(MGetFrameArgument* ins) defineBox(lir, ins); } +void +LIRGenerator::visitNewTarget(MNewTarget* ins) +{ + LNewTarget* lir = new(alloc()) LNewTarget(); + defineBox(lir, ins); +} + void LIRGenerator::visitSetFrameArgument(MSetFrameArgument* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 6e04978f4f..3ee81c8066 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -181,6 +181,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitSetArrayLength(MSetArrayLength* ins); void visitTypedArrayLength(MTypedArrayLength* ins); void visitTypedArrayElements(MTypedArrayElements* ins); + void visitSetDisjointTypedElements(MSetDisjointTypedElements* ins); void visitTypedObjectElements(MTypedObjectElements* ins); void visitSetTypedObjectOffset(MSetTypedObjectOffset* ins); void visitTypedObjectDescr(MTypedObjectDescr* ins); @@ -295,6 +296,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitThrowUninitializedLexical(MThrowUninitializedLexical* ins); void visitDebugger(MDebugger* ins); void visitNurseryObject(MNurseryObject* ins); + void visitNewTarget(MNewTarget* ins); }; } // namespace jit diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 6a38e42084..54fe4d8e70 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -225,8 +225,12 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) // TypedArray intrinsics. if (native == intrinsic_IsTypedArray) return inlineIsTypedArray(callInfo); + if (native == intrinsic_IsPossiblyWrappedTypedArray) + return inlineIsPossiblyWrappedTypedArray(callInfo); if (native == intrinsic_TypedArrayLength) return inlineTypedArrayLength(callInfo); + if (native == intrinsic_SetDisjointTypedElements) + return inlineSetDisjointTypedElements(callInfo); // TypedObject intrinsics. if (native == js::ObjectIsTypedObject) @@ -2346,10 +2350,11 @@ IonBuilder::inlineHasClass(CallInfo& callInfo, } IonBuilder::InliningStatus -IonBuilder::inlineIsTypedArray(CallInfo& callInfo) +IonBuilder::inlineIsTypedArrayHelper(CallInfo& callInfo, WrappingBehavior wrappingBehavior) { MOZ_ASSERT(!callInfo.constructing()); MOZ_ASSERT(callInfo.argc() == 1); + if (callInfo.getArg(0)->type() != MIRType_Object) return InliningStatus_NotInlined; if (getInlineReturnType() != MIRType_Boolean) @@ -2365,12 +2370,22 @@ IonBuilder::inlineIsTypedArray(CallInfo& callInfo) bool result = false; switch (types->forAllClasses(constraints(), IsTypedArrayClass)) { case TemporaryTypeSet::ForAllResult::ALL_FALSE: - case TemporaryTypeSet::ForAllResult::EMPTY: + case TemporaryTypeSet::ForAllResult::EMPTY: { + // Wrapped typed arrays won't appear to be typed arrays per a + // |forAllClasses| query. If wrapped typed arrays are to be considered + // typed arrays, a negative answer is not conclusive. Don't inline in + // that case. + if (wrappingBehavior == AllowWrappedTypedArrays) + return InliningStatus_NotInlined; + result = false; break; + } + case TemporaryTypeSet::ForAllResult::ALL_TRUE: result = true; break; + case TemporaryTypeSet::ForAllResult::MIXED: return InliningStatus_NotInlined; } @@ -2394,6 +2409,18 @@ IsTypedArrayObject(CompilerConstraintList* constraints, MDefinition* def) TemporaryTypeSet::ForAllResult::ALL_TRUE; } +IonBuilder::InliningStatus +IonBuilder::inlineIsTypedArray(CallInfo& callInfo) +{ + return inlineIsTypedArrayHelper(callInfo, RejectWrappedTypedArrays); +} + +IonBuilder::InliningStatus +IonBuilder::inlineIsPossiblyWrappedTypedArray(CallInfo& callInfo) +{ + return inlineIsTypedArrayHelper(callInfo, AllowWrappedTypedArrays); +} + IonBuilder::InliningStatus IonBuilder::inlineTypedArrayLength(CallInfo& callInfo) { @@ -2416,6 +2443,57 @@ IonBuilder::inlineTypedArrayLength(CallInfo& callInfo) return InliningStatus_Inlined; } +IonBuilder::InliningStatus +IonBuilder::inlineSetDisjointTypedElements(CallInfo& callInfo) +{ + MOZ_ASSERT(!callInfo.constructing()); + MOZ_ASSERT(callInfo.argc() == 3); + + // Initial argument requirements. + + MDefinition* target = callInfo.getArg(0); + if (target->type() != MIRType_Object) + return InliningStatus_NotInlined; + + if (getInlineReturnType() != MIRType_Undefined) + return InliningStatus_NotInlined; + + MDefinition* targetOffset = callInfo.getArg(1); + MOZ_ASSERT(targetOffset->type() == MIRType_Int32); + + MDefinition* sourceTypedArray = callInfo.getArg(2); + if (sourceTypedArray->type() != MIRType_Object) + return InliningStatus_NotInlined; + + // Only attempt to optimize if |target| and |sourceTypedArray| are both + // definitely typed arrays. (The former always is. The latter is not, + // necessarily, because of wrappers.) + + MDefinition* arrays[] = { target, sourceTypedArray }; + + for (MDefinition* def : arrays) { + TemporaryTypeSet* types = def->resultTypeSet(); + if (!types) + return InliningStatus_NotInlined; + + if (types->forAllClasses(constraints(), IsTypedArrayClass) != + TemporaryTypeSet::ForAllResult::ALL_TRUE) + { + return InliningStatus_NotInlined; + } + } + + auto sets = MSetDisjointTypedElements::New(alloc(), target, targetOffset, sourceTypedArray); + current->add(sets); + + pushConstant(UndefinedValue()); + + if (!resumeAfter(sets)) + return InliningStatus_Error; + + callInfo.setImplicitlyUsedUnchecked(); + return InliningStatus_Inlined; +} IonBuilder::InliningStatus IonBuilder::inlineObjectIsTypeDescr(CallInfo& callInfo) diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 7ab10928db..322d39a35b 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -4027,7 +4027,7 @@ class MGetDynamicName class MCallDirectEval : public MAryInstruction<3>, public Mix3Policy, - BoxExceptPolicy<1, MIRType_String>, + StringPolicy<1>, BoxPolicy<2> >::Data { protected: @@ -8037,6 +8037,49 @@ class MTypedArrayElements ALLOW_CLONE(MTypedArrayElements) }; +class MSetDisjointTypedElements + : public MTernaryInstruction, + public NoTypePolicy::Data +{ + explicit MSetDisjointTypedElements(MDefinition* target, MDefinition* targetOffset, + MDefinition* source) + : MTernaryInstruction(target, targetOffset, source) + { + MOZ_ASSERT(target->type() == MIRType_Object); + MOZ_ASSERT(targetOffset->type() == MIRType_Int32); + MOZ_ASSERT(source->type() == MIRType_Object); + setResultType(MIRType_None); + } + + public: + INSTRUCTION_HEADER(SetDisjointTypedElements) + + static MSetDisjointTypedElements* + New(TempAllocator& alloc, MDefinition* target, MDefinition* targetOffset, + MDefinition* source) + { + return new(alloc) MSetDisjointTypedElements(target, targetOffset, source); + } + + MDefinition* target() const { + return getOperand(0); + } + + MDefinition* targetOffset() const { + return getOperand(1); + } + + MDefinition* source() const { + return getOperand(2); + } + + AliasSet getAliasSet() const override { + return AliasSet::Store(AliasSet::UnboxedElement); + } + + ALLOW_CLONE(MSetDisjointTypedElements) +}; + // Load a binary data object's "elements", which is just its opaque // binary data space. Eventually this should probably be // unified with `MTypedArrayElements`. @@ -11929,6 +11972,28 @@ class MGetFrameArgument } }; +class MNewTarget : public MNullaryInstruction +{ + MNewTarget() : MNullaryInstruction() { + setResultType(MIRType_Value); + setMovable(); + } + + public: + INSTRUCTION_HEADER(NewTarget) + + static MNewTarget* New(TempAllocator& alloc) { + return new(alloc) MNewTarget(); + } + + bool congruentTo(const MDefinition* ins) const override { + return congruentIfOperandsEqual(ins); + } + AliasSet getAliasSet() const override { + return AliasSet::None(); + } +}; + // This MIR instruction is used to set an argument value in the frame. class MSetFrameArgument : public MUnaryInstruction, diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 96e0df92b4..99d2ed3277 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -172,6 +172,7 @@ namespace jit { _(SetArrayLength) \ _(TypedArrayLength) \ _(TypedArrayElements) \ + _(SetDisjointTypedElements) \ _(TypedObjectDescr) \ _(TypedObjectElements) \ _(SetTypedObjectOffset) \ @@ -266,7 +267,8 @@ namespace jit { _(UnknownValue) \ _(LexicalCheck) \ _(ThrowUninitializedLexical) \ - _(Debugger) + _(Debugger) \ + _(NewTarget) // Forward declarations of MIR types. #define FORWARD_DECLARE(op) class M##op; diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp new file mode 100644 index 0000000000..3890bb1451 --- /dev/null +++ b/js/src/jit/SharedIC.cpp @@ -0,0 +1,790 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/SharedIC.h" + +#include "mozilla/SizePrintfMacros.h" + +#include "jstypes.h" + +#include "jit/JitSpewer.h" +#include "jit/Linker.h" +#include "jit/SharedICHelpers.h" +#ifdef JS_ION_PERF +# include "jit/PerfSpewer.h" +#endif +#include "jit/VMFunctions.h" + +namespace js { +namespace jit { + +#ifdef DEBUG +void +FallbackICSpew(JSContext* cx, ICFallbackStub* stub, const char* fmt, ...) +{ + if (JitSpewEnabled(JitSpew_BaselineICFallback)) { + RootedScript script(cx, GetTopJitJSScript(cx)); + jsbytecode* pc = stub->icEntry()->pc(script); + + char fmtbuf[100]; + va_list args; + va_start(args, fmt); + vsnprintf(fmtbuf, 100, fmt, args); + va_end(args); + + JitSpew(JitSpew_BaselineICFallback, + "Fallback hit for (%s:%" PRIuSIZE ") (pc=%" PRIuSIZE ",line=%d,uses=%d,stubs=%" PRIuSIZE "): %s", + script->filename(), + script->lineno(), + script->pcToOffset(pc), + PCToLineNumber(script, pc), + script->getWarmUpCount(), + stub->numOptimizedStubs(), + fmtbuf); + } +} + +void +TypeFallbackICSpew(JSContext* cx, ICTypeMonitor_Fallback* stub, const char* fmt, ...) +{ + if (JitSpewEnabled(JitSpew_BaselineICFallback)) { + RootedScript script(cx, GetTopJitJSScript(cx)); + jsbytecode* pc = stub->icEntry()->pc(script); + + char fmtbuf[100]; + va_list args; + va_start(args, fmt); + vsnprintf(fmtbuf, 100, fmt, args); + va_end(args); + + JitSpew(JitSpew_BaselineICFallback, + "Type monitor fallback hit for (%s:%" PRIuSIZE ") (pc=%" PRIuSIZE ",line=%d,uses=%d,stubs=%d): %s", + script->filename(), + script->lineno(), + script->pcToOffset(pc), + PCToLineNumber(script, pc), + script->getWarmUpCount(), + (int) stub->numOptimizedMonitorStubs(), + fmtbuf); + } +} +#endif + +ICFallbackStub* +ICEntry::fallbackStub() const +{ + return firstStub()->getChainFallback(); +} + + +ICStubConstIterator& +ICStubConstIterator::operator++() +{ + MOZ_ASSERT(currentStub_ != nullptr); + currentStub_ = currentStub_->next(); + return *this; +} + + +ICStubIterator::ICStubIterator(ICFallbackStub* fallbackStub, bool end) + : icEntry_(fallbackStub->icEntry()), + fallbackStub_(fallbackStub), + previousStub_(nullptr), + currentStub_(end ? fallbackStub : icEntry_->firstStub()), + unlinked_(false) +{ } + +ICStubIterator& +ICStubIterator::operator++() +{ + MOZ_ASSERT(currentStub_->next() != nullptr); + if (!unlinked_) + previousStub_ = currentStub_; + currentStub_ = currentStub_->next(); + unlinked_ = false; + return *this; +} + +void +ICStubIterator::unlink(JSContext* cx) +{ + MOZ_ASSERT(currentStub_->next() != nullptr); + MOZ_ASSERT(currentStub_ != fallbackStub_); + MOZ_ASSERT(!unlinked_); + + fallbackStub_->unlinkStub(cx->zone(), previousStub_, currentStub_); + + // Mark the current iterator position as unlinked, so operator++ works properly. + unlinked_ = true; +} + + +void +ICStub::markCode(JSTracer* trc, const char* name) +{ + JitCode* stubJitCode = jitCode(); + TraceManuallyBarrieredEdge(trc, &stubJitCode, name); +} + +void +ICStub::updateCode(JitCode* code) +{ + // Write barrier on the old code. + JitCode::writeBarrierPre(jitCode()); + stubCode_ = code->raw(); +} + +/* static */ void +ICStub::trace(JSTracer* trc) +{ + markCode(trc, "baseline-stub-jitcode"); + + // If the stub is a monitored fallback stub, then mark the monitor ICs hanging + // off of that stub. We don't need to worry about the regular monitored stubs, + // because the regular monitored stubs will always have a monitored fallback stub + // that references the same stub chain. + if (isMonitoredFallback()) { + ICTypeMonitor_Fallback* lastMonStub = toMonitoredFallbackStub()->fallbackMonitorStub(); + for (ICStubConstIterator iter(lastMonStub->firstMonitorStub()); !iter.atEnd(); iter++) { + MOZ_ASSERT_IF(iter->next() == nullptr, *iter == lastMonStub); + iter->trace(trc); + } + } + + if (isUpdated()) { + for (ICStubConstIterator iter(toUpdatedStub()->firstUpdateStub()); !iter.atEnd(); iter++) { + MOZ_ASSERT_IF(iter->next() == nullptr, iter->isTypeUpdate_Fallback()); + iter->trace(trc); + } + } + + switch (kind()) { + case ICStub::Call_Scripted: { + ICCall_Scripted* callStub = toCall_Scripted(); + TraceEdge(trc, &callStub->callee(), "baseline-callscripted-callee"); + if (callStub->templateObject()) + TraceEdge(trc, &callStub->templateObject(), "baseline-callscripted-template"); + break; + } + case ICStub::Call_Native: { + ICCall_Native* callStub = toCall_Native(); + TraceEdge(trc, &callStub->callee(), "baseline-callnative-callee"); + if (callStub->templateObject()) + TraceEdge(trc, &callStub->templateObject(), "baseline-callnative-template"); + break; + } + case ICStub::Call_ClassHook: { + ICCall_ClassHook* callStub = toCall_ClassHook(); + if (callStub->templateObject()) + TraceEdge(trc, &callStub->templateObject(), "baseline-callclasshook-template"); + break; + } + case ICStub::Call_StringSplit: { + ICCall_StringSplit* callStub = toCall_StringSplit(); + TraceEdge(trc, &callStub->templateObject(), "baseline-callstringsplit-template"); + TraceEdge(trc, &callStub->expectedArg(), "baseline-callstringsplit-arg"); + TraceEdge(trc, &callStub->expectedThis(), "baseline-callstringsplit-this"); + break; + } + case ICStub::GetElem_NativeSlot: { + ICGetElem_NativeSlot* getElemStub = toGetElem_NativeSlot(); + TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-native-shape"); + TraceEdge(trc, &getElemStub->name(), "baseline-getelem-native-name"); + break; + } + case ICStub::GetElem_NativePrototypeSlot: { + ICGetElem_NativePrototypeSlot* getElemStub = toGetElem_NativePrototypeSlot(); + TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-nativeproto-shape"); + TraceEdge(trc, &getElemStub->name(), "baseline-getelem-nativeproto-name"); + TraceEdge(trc, &getElemStub->holder(), "baseline-getelem-nativeproto-holder"); + TraceEdge(trc, &getElemStub->holderShape(), "baseline-getelem-nativeproto-holdershape"); + break; + } + case ICStub::GetElem_NativePrototypeCallNative: + case ICStub::GetElem_NativePrototypeCallScripted: { + ICGetElemNativePrototypeCallStub* callStub = + reinterpret_cast(this); + TraceEdge(trc, &callStub->shape(), "baseline-getelem-nativeprotocall-shape"); + TraceEdge(trc, &callStub->name(), "baseline-getelem-nativeprotocall-name"); + TraceEdge(trc, &callStub->getter(), "baseline-getelem-nativeprotocall-getter"); + TraceEdge(trc, &callStub->holder(), "baseline-getelem-nativeprotocall-holder"); + TraceEdge(trc, &callStub->holderShape(), "baseline-getelem-nativeprotocall-holdershape"); + break; + } + case ICStub::GetElem_Dense: { + ICGetElem_Dense* getElemStub = toGetElem_Dense(); + TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-dense-shape"); + break; + } + case ICStub::GetElem_UnboxedArray: { + ICGetElem_UnboxedArray* getElemStub = toGetElem_UnboxedArray(); + TraceEdge(trc, &getElemStub->group(), "baseline-getelem-unboxed-array-group"); + break; + } + case ICStub::GetElem_TypedArray: { + ICGetElem_TypedArray* getElemStub = toGetElem_TypedArray(); + TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-typedarray-shape"); + break; + } + case ICStub::SetElem_DenseOrUnboxedArray: { + ICSetElem_DenseOrUnboxedArray* setElemStub = toSetElem_DenseOrUnboxedArray(); + if (setElemStub->shape()) + TraceEdge(trc, &setElemStub->shape(), "baseline-getelem-dense-shape"); + TraceEdge(trc, &setElemStub->group(), "baseline-setelem-dense-group"); + break; + } + case ICStub::SetElem_DenseOrUnboxedArrayAdd: { + ICSetElem_DenseOrUnboxedArrayAdd* setElemStub = toSetElem_DenseOrUnboxedArrayAdd(); + TraceEdge(trc, &setElemStub->group(), "baseline-setelem-denseadd-group"); + + JS_STATIC_ASSERT(ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH == 4); + + switch (setElemStub->protoChainDepth()) { + case 0: setElemStub->toImpl<0>()->traceShapes(trc); break; + case 1: setElemStub->toImpl<1>()->traceShapes(trc); break; + case 2: setElemStub->toImpl<2>()->traceShapes(trc); break; + case 3: setElemStub->toImpl<3>()->traceShapes(trc); break; + case 4: setElemStub->toImpl<4>()->traceShapes(trc); break; + default: MOZ_CRASH("Invalid proto stub."); + } + break; + } + case ICStub::SetElem_TypedArray: { + ICSetElem_TypedArray* setElemStub = toSetElem_TypedArray(); + TraceEdge(trc, &setElemStub->shape(), "baseline-setelem-typedarray-shape"); + break; + } + case ICStub::TypeMonitor_SingleObject: { + ICTypeMonitor_SingleObject* monitorStub = toTypeMonitor_SingleObject(); + TraceEdge(trc, &monitorStub->object(), "baseline-monitor-singleton"); + break; + } + case ICStub::TypeMonitor_ObjectGroup: { + ICTypeMonitor_ObjectGroup* monitorStub = toTypeMonitor_ObjectGroup(); + TraceEdge(trc, &monitorStub->group(), "baseline-monitor-group"); + break; + } + case ICStub::TypeUpdate_SingleObject: { + ICTypeUpdate_SingleObject* updateStub = toTypeUpdate_SingleObject(); + TraceEdge(trc, &updateStub->object(), "baseline-update-singleton"); + break; + } + case ICStub::TypeUpdate_ObjectGroup: { + ICTypeUpdate_ObjectGroup* updateStub = toTypeUpdate_ObjectGroup(); + TraceEdge(trc, &updateStub->group(), "baseline-update-group"); + break; + } + case ICStub::In_Native: { + ICIn_Native* inStub = toIn_Native(); + TraceEdge(trc, &inStub->shape(), "baseline-innative-stub-shape"); + TraceEdge(trc, &inStub->name(), "baseline-innative-stub-name"); + break; + } + case ICStub::In_NativePrototype: { + ICIn_NativePrototype* inStub = toIn_NativePrototype(); + TraceEdge(trc, &inStub->shape(), "baseline-innativeproto-stub-shape"); + TraceEdge(trc, &inStub->name(), "baseline-innativeproto-stub-name"); + TraceEdge(trc, &inStub->holder(), "baseline-innativeproto-stub-holder"); + TraceEdge(trc, &inStub->holderShape(), "baseline-innativeproto-stub-holdershape"); + break; + } + case ICStub::In_NativeDoesNotExist: { + ICIn_NativeDoesNotExist* inStub = toIn_NativeDoesNotExist(); + TraceEdge(trc, &inStub->name(), "baseline-innativedoesnotexist-stub-name"); + JS_STATIC_ASSERT(ICIn_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8); + switch (inStub->protoChainDepth()) { + case 0: inStub->toImpl<0>()->traceShapes(trc); break; + case 1: inStub->toImpl<1>()->traceShapes(trc); break; + case 2: inStub->toImpl<2>()->traceShapes(trc); break; + case 3: inStub->toImpl<3>()->traceShapes(trc); break; + case 4: inStub->toImpl<4>()->traceShapes(trc); break; + case 5: inStub->toImpl<5>()->traceShapes(trc); break; + case 6: inStub->toImpl<6>()->traceShapes(trc); break; + case 7: inStub->toImpl<7>()->traceShapes(trc); break; + case 8: inStub->toImpl<8>()->traceShapes(trc); break; + default: MOZ_CRASH("Invalid proto stub."); + } + break; + } + case ICStub::In_Dense: { + ICIn_Dense* inStub = toIn_Dense(); + TraceEdge(trc, &inStub->shape(), "baseline-in-dense-shape"); + break; + } + case ICStub::GetName_Global: { + ICGetName_Global* globalStub = toGetName_Global(); + TraceEdge(trc, &globalStub->shape(), "baseline-global-stub-shape"); + break; + } + case ICStub::GetName_Scope0: + static_cast*>(this)->traceScopes(trc); + break; + case ICStub::GetName_Scope1: + static_cast*>(this)->traceScopes(trc); + break; + case ICStub::GetName_Scope2: + static_cast*>(this)->traceScopes(trc); + break; + case ICStub::GetName_Scope3: + static_cast*>(this)->traceScopes(trc); + break; + case ICStub::GetName_Scope4: + static_cast*>(this)->traceScopes(trc); + break; + case ICStub::GetName_Scope5: + static_cast*>(this)->traceScopes(trc); + break; + case ICStub::GetName_Scope6: + static_cast*>(this)->traceScopes(trc); + break; + case ICStub::GetIntrinsic_Constant: { + ICGetIntrinsic_Constant* constantStub = toGetIntrinsic_Constant(); + TraceEdge(trc, &constantStub->value(), "baseline-getintrinsic-constant-value"); + break; + } + case ICStub::GetProp_Primitive: { + ICGetProp_Primitive* propStub = toGetProp_Primitive(); + TraceEdge(trc, &propStub->protoShape(), "baseline-getprop-primitive-stub-shape"); + break; + } + case ICStub::GetProp_Native: { + ICGetProp_Native* propStub = toGetProp_Native(); + propStub->receiverGuard().trace(trc); + break; + } + case ICStub::GetProp_NativePrototype: { + ICGetProp_NativePrototype* propStub = toGetProp_NativePrototype(); + propStub->receiverGuard().trace(trc); + TraceEdge(trc, &propStub->holder(), "baseline-getpropnativeproto-stub-holder"); + TraceEdge(trc, &propStub->holderShape(), "baseline-getpropnativeproto-stub-holdershape"); + break; + } + case ICStub::GetProp_NativeDoesNotExist: { + ICGetProp_NativeDoesNotExist* propStub = toGetProp_NativeDoesNotExist(); + propStub->guard().trace(trc); + JS_STATIC_ASSERT(ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8); + switch (propStub->protoChainDepth()) { + case 0: propStub->toImpl<0>()->traceShapes(trc); break; + case 1: propStub->toImpl<1>()->traceShapes(trc); break; + case 2: propStub->toImpl<2>()->traceShapes(trc); break; + case 3: propStub->toImpl<3>()->traceShapes(trc); break; + case 4: propStub->toImpl<4>()->traceShapes(trc); break; + case 5: propStub->toImpl<5>()->traceShapes(trc); break; + case 6: propStub->toImpl<6>()->traceShapes(trc); break; + case 7: propStub->toImpl<7>()->traceShapes(trc); break; + case 8: propStub->toImpl<8>()->traceShapes(trc); break; + default: MOZ_CRASH("Invalid proto stub."); + } + break; + } + case ICStub::GetProp_Unboxed: { + ICGetProp_Unboxed* propStub = toGetProp_Unboxed(); + TraceEdge(trc, &propStub->group(), "baseline-getprop-unboxed-stub-group"); + break; + } + case ICStub::GetProp_TypedObject: { + ICGetProp_TypedObject* propStub = toGetProp_TypedObject(); + TraceEdge(trc, &propStub->shape(), "baseline-getprop-typedobject-stub-shape"); + break; + } + case ICStub::GetProp_CallDOMProxyNative: + case ICStub::GetProp_CallDOMProxyWithGenerationNative: { + ICGetPropCallDOMProxyNativeStub* propStub; + if (kind() == ICStub::GetProp_CallDOMProxyNative) + propStub = toGetProp_CallDOMProxyNative(); + else + propStub = toGetProp_CallDOMProxyWithGenerationNative(); + propStub->receiverGuard().trace(trc); + if (propStub->expandoShape()) { + TraceEdge(trc, &propStub->expandoShape(), + "baseline-getproplistbasenative-stub-expandoshape"); + } + TraceEdge(trc, &propStub->holder(), "baseline-getproplistbasenative-stub-holder"); + TraceEdge(trc, &propStub->holderShape(), "baseline-getproplistbasenative-stub-holdershape"); + TraceEdge(trc, &propStub->getter(), "baseline-getproplistbasenative-stub-getter"); + break; + } + case ICStub::GetProp_DOMProxyShadowed: { + ICGetProp_DOMProxyShadowed* propStub = toGetProp_DOMProxyShadowed(); + TraceEdge(trc, &propStub->shape(), "baseline-getproplistbaseshadowed-stub-shape"); + TraceEdge(trc, &propStub->name(), "baseline-getproplistbaseshadowed-stub-name"); + break; + } + case ICStub::GetProp_CallScripted: { + ICGetProp_CallScripted* callStub = toGetProp_CallScripted(); + callStub->receiverGuard().trace(trc); + TraceEdge(trc, &callStub->holder(), "baseline-getpropcallscripted-stub-holder"); + TraceEdge(trc, &callStub->holderShape(), "baseline-getpropcallscripted-stub-holdershape"); + TraceEdge(trc, &callStub->getter(), "baseline-getpropcallscripted-stub-getter"); + break; + } + case ICStub::GetProp_CallNative: { + ICGetProp_CallNative* callStub = toGetProp_CallNative(); + callStub->receiverGuard().trace(trc); + TraceEdge(trc, &callStub->holder(), "baseline-getpropcallnative-stub-holder"); + TraceEdge(trc, &callStub->holderShape(), "baseline-getpropcallnative-stub-holdershape"); + TraceEdge(trc, &callStub->getter(), "baseline-getpropcallnative-stub-getter"); + break; + } + case ICStub::SetProp_Native: { + ICSetProp_Native* propStub = toSetProp_Native(); + TraceEdge(trc, &propStub->shape(), "baseline-setpropnative-stub-shape"); + TraceEdge(trc, &propStub->group(), "baseline-setpropnative-stub-group"); + break; + } + case ICStub::SetProp_NativeAdd: { + ICSetProp_NativeAdd* propStub = toSetProp_NativeAdd(); + TraceEdge(trc, &propStub->group(), "baseline-setpropnativeadd-stub-group"); + TraceEdge(trc, &propStub->newShape(), "baseline-setpropnativeadd-stub-newshape"); + if (propStub->newGroup()) + TraceEdge(trc, &propStub->newGroup(), "baseline-setpropnativeadd-stub-new-group"); + JS_STATIC_ASSERT(ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH == 4); + switch (propStub->protoChainDepth()) { + case 0: propStub->toImpl<0>()->traceShapes(trc); break; + case 1: propStub->toImpl<1>()->traceShapes(trc); break; + case 2: propStub->toImpl<2>()->traceShapes(trc); break; + case 3: propStub->toImpl<3>()->traceShapes(trc); break; + case 4: propStub->toImpl<4>()->traceShapes(trc); break; + default: MOZ_CRASH("Invalid proto stub."); + } + break; + } + case ICStub::SetProp_Unboxed: { + ICSetProp_Unboxed* propStub = toSetProp_Unboxed(); + TraceEdge(trc, &propStub->group(), "baseline-setprop-unboxed-stub-group"); + break; + } + case ICStub::SetProp_TypedObject: { + ICSetProp_TypedObject* propStub = toSetProp_TypedObject(); + TraceEdge(trc, &propStub->shape(), "baseline-setprop-typedobject-stub-shape"); + TraceEdge(trc, &propStub->group(), "baseline-setprop-typedobject-stub-group"); + break; + } + case ICStub::SetProp_CallScripted: { + ICSetProp_CallScripted* callStub = toSetProp_CallScripted(); + callStub->receiverGuard().trace(trc); + TraceEdge(trc, &callStub->holder(), "baseline-setpropcallscripted-stub-holder"); + TraceEdge(trc, &callStub->holderShape(), "baseline-setpropcallscripted-stub-holdershape"); + TraceEdge(trc, &callStub->setter(), "baseline-setpropcallscripted-stub-setter"); + break; + } + case ICStub::SetProp_CallNative: { + ICSetProp_CallNative* callStub = toSetProp_CallNative(); + callStub->receiverGuard().trace(trc); + TraceEdge(trc, &callStub->holder(), "baseline-setpropcallnative-stub-holder"); + TraceEdge(trc, &callStub->holderShape(), "baseline-setpropcallnative-stub-holdershape"); + TraceEdge(trc, &callStub->setter(), "baseline-setpropcallnative-stub-setter"); + break; + } + case ICStub::InstanceOf_Function: { + ICInstanceOf_Function* instanceofStub = toInstanceOf_Function(); + TraceEdge(trc, &instanceofStub->shape(), "baseline-instanceof-fun-shape"); + TraceEdge(trc, &instanceofStub->prototypeObject(), "baseline-instanceof-fun-prototype"); + break; + } + case ICStub::NewArray_Fallback: { + ICNewArray_Fallback* stub = toNewArray_Fallback(); + if (stub->templateObject()) + TraceEdge(trc, &stub->templateObject(), "baseline-newarray-template"); + TraceEdge(trc, &stub->templateGroup(), "baseline-newarray-template-group"); + break; + } + case ICStub::NewObject_Fallback: { + ICNewObject_Fallback* stub = toNewObject_Fallback(); + if (stub->templateObject()) + TraceEdge(trc, &stub->templateObject(), "baseline-newobject-template"); + break; + } + case ICStub::Rest_Fallback: { + ICRest_Fallback* stub = toRest_Fallback(); + TraceEdge(trc, &stub->templateObject(), "baseline-rest-template"); + break; + } + default: + break; + } +} + +void +ICFallbackStub::unlinkStub(Zone* zone, ICStub* prev, ICStub* stub) +{ + MOZ_ASSERT(stub->next()); + + // If stub is the last optimized stub, update lastStubPtrAddr. + if (stub->next() == this) { + MOZ_ASSERT(lastStubPtrAddr_ == stub->addressOfNext()); + if (prev) + lastStubPtrAddr_ = prev->addressOfNext(); + else + lastStubPtrAddr_ = icEntry()->addressOfFirstStub(); + *lastStubPtrAddr_ = this; + } else { + if (prev) { + MOZ_ASSERT(prev->next() == stub); + prev->setNext(stub->next()); + } else { + MOZ_ASSERT(icEntry()->firstStub() == stub); + icEntry()->setFirstStub(stub->next()); + } + } + + MOZ_ASSERT(numOptimizedStubs_ > 0); + numOptimizedStubs_--; + + if (zone->needsIncrementalBarrier()) { + // We are removing edges from ICStub to gcthings. Perform one final trace + // of the stub for incremental GC, as it must know about those edges. + stub->trace(zone->barrierTracer()); + } + + if (ICStub::CanMakeCalls(stub->kind()) && stub->isMonitored()) { + // This stub can make calls so we can return to it if it's on the stack. + // We just have to reset its firstMonitorStub_ field to avoid a stale + // pointer when purgeOptimizedStubs destroys all optimized monitor + // stubs (unlinked stubs won't be updated). + ICTypeMonitor_Fallback* monitorFallback = toMonitoredFallbackStub()->fallbackMonitorStub(); + stub->toMonitoredStub()->resetFirstMonitorStub(monitorFallback); + } + +#ifdef DEBUG + // Poison stub code to ensure we don't call this stub again. However, if this + // stub can make calls, a pointer to it may be stored in a stub frame on the + // stack, so we can't touch the stubCode_ or GC will crash when marking this + // pointer. + if (!ICStub::CanMakeCalls(stub->kind())) + stub->stubCode_ = (uint8_t*)0xbad; +#endif +} + +void +ICFallbackStub::unlinkStubsWithKind(JSContext* cx, ICStub::Kind kind) +{ + for (ICStubIterator iter = beginChain(); !iter.atEnd(); iter++) { + if (iter->kind() == kind) + iter.unlink(cx); + } +} + +void +ICTypeMonitor_Fallback::resetMonitorStubChain(Zone* zone) +{ + if (zone->needsIncrementalBarrier()) { + // We are removing edges from monitored stubs to gcthings (JitCode). + // Perform one final trace of all monitor stubs for incremental GC, + // as it must know about those edges. + for (ICStub* s = firstMonitorStub_; !s->isTypeMonitor_Fallback(); s = s->next()) + s->trace(zone->barrierTracer()); + } + + firstMonitorStub_ = this; + numOptimizedMonitorStubs_ = 0; + + if (hasFallbackStub_) { + lastMonitorStubPtrAddr_ = nullptr; + + // Reset firstMonitorStub_ field of all monitored stubs. + for (ICStubConstIterator iter = mainFallbackStub_->beginChainConst(); + !iter.atEnd(); iter++) + { + if (!iter->isMonitored()) + continue; + iter->toMonitoredStub()->resetFirstMonitorStub(this); + } + } else { + icEntry_->setFirstStub(this); + lastMonitorStubPtrAddr_ = icEntry_->addressOfFirstStub(); + } +} + +ICMonitoredStub::ICMonitoredStub(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub) + : ICStub(kind, ICStub::Monitored, stubCode), + firstMonitorStub_(firstMonitorStub) +{ + // If the first monitored stub is a ICTypeMonitor_Fallback stub, then + // double check that _its_ firstMonitorStub is the same as this one. + MOZ_ASSERT_IF(firstMonitorStub_->isTypeMonitor_Fallback(), + firstMonitorStub_->toTypeMonitor_Fallback()->firstMonitorStub() == + firstMonitorStub_); +} + +bool +ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx, ICStubSpace* space) +{ + MOZ_ASSERT(fallbackMonitorStub_ == nullptr); + + ICTypeMonitor_Fallback::Compiler compiler(cx, this); + ICTypeMonitor_Fallback* stub = compiler.getStub(space); + if (!stub) + return false; + fallbackMonitorStub_ = stub; + return true; +} + +bool +ICMonitoredFallbackStub::addMonitorStubForValue(JSContext* cx, JSScript* script, HandleValue val) +{ + return fallbackMonitorStub_->addMonitorStubForValue(cx, script, val); +} + +bool +ICUpdatedStub::initUpdatingChain(JSContext* cx, ICStubSpace* space) +{ + MOZ_ASSERT(firstUpdateStub_ == nullptr); + + ICTypeUpdate_Fallback::Compiler compiler(cx); + ICTypeUpdate_Fallback* stub = compiler.getStub(space); + if (!stub) + return false; + + firstUpdateStub_ = stub; + return true; +} + +JitCode* +ICStubCompiler::getStubCode() +{ + JitCompartment* comp = cx->compartment()->jitCompartment(); + + // Check for existing cached stubcode. + uint32_t stubKey = getKey(); + JitCode* stubCode = comp->getStubCode(stubKey); + if (stubCode) + return stubCode; + + // Compile new stubcode. + JitContext jctx(cx, nullptr); + MacroAssembler masm; +#ifdef JS_CODEGEN_ARM + masm.setSecondScratchReg(BaselineSecondScratchReg); +#endif + + if (!generateStubCode(masm)) + return nullptr; + Linker linker(masm); + AutoFlushICache afc("getStubCode"); + Rooted newStubCode(cx, linker.newCode(cx, BASELINE_CODE)); + if (!newStubCode) + return nullptr; + + // After generating code, run postGenerateStubCode() + if (!postGenerateStubCode(masm, newStubCode)) + return nullptr; + + // All barriers are emitted off-by-default, enable them if needed. + if (cx->zone()->needsIncrementalBarrier()) + newStubCode->togglePreBarriers(true); + + // Cache newly compiled stubcode. + if (!comp->putStubCode(stubKey, newStubCode)) + return nullptr; + + MOZ_ASSERT(entersStubFrame_ == ICStub::CanMakeCalls(kind)); + MOZ_ASSERT(!inStubFrame_); + +#ifdef JS_ION_PERF + writePerfSpewerJitCodeProfile(newStubCode, "BaselineIC"); +#endif + + return newStubCode; +} + +bool +ICStubCompiler::tailCallVM(const VMFunction& fun, MacroAssembler& masm) +{ + JitCode* code = cx->runtime()->jitRuntime()->getVMWrapper(fun); + if (!code) + return false; + + MOZ_ASSERT(fun.expectTailCall == TailCall); + uint32_t argSize = fun.explicitStackSlots() * sizeof(void*); + EmitTailCallVM(code, masm, argSize); + return true; +} + +bool +ICStubCompiler::callVM(const VMFunction& fun, MacroAssembler& masm) +{ + MOZ_ASSERT(inStubFrame_); + + JitCode* code = cx->runtime()->jitRuntime()->getVMWrapper(fun); + if (!code) + return false; + + MOZ_ASSERT(fun.expectTailCall == NonTailCall); + EmitCallVM(code, masm); + return true; +} + +bool +ICStubCompiler::callTypeUpdateIC(MacroAssembler& masm, uint32_t objectOffset) +{ + JitCode* code = cx->runtime()->jitRuntime()->getVMWrapper(DoTypeUpdateFallbackInfo); + if (!code) + return false; + + EmitCallTypeUpdateIC(masm, code, objectOffset); + return true; +} + +void +ICStubCompiler::enterStubFrame(MacroAssembler& masm, Register scratch) +{ + EmitEnterStubFrame(masm, scratch); + + MOZ_ASSERT(!inStubFrame_); + inStubFrame_ = true; + +#ifdef DEBUG + entersStubFrame_ = true; +#endif +} + +void +ICStubCompiler::leaveStubFrame(MacroAssembler& masm, bool calledIntoIon) +{ + MOZ_ASSERT(entersStubFrame_ && inStubFrame_); + inStubFrame_ = false; + EmitLeaveStubFrame(masm, calledIntoIon); +} + +void +ICStubCompiler::pushFramePtr(MacroAssembler& masm, Register scratch) +{ + if (inStubFrame_) { + masm.loadPtr(Address(BaselineFrameReg, 0), scratch); + masm.pushBaselineFramePtr(scratch, scratch); + } else { + masm.pushBaselineFramePtr(BaselineFrameReg, scratch); + } +} + +bool +ICStubCompiler::emitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, ValueOperand val, + Register scratch, LiveGeneralRegisterSet saveRegs) +{ + Label skipBarrier; + masm.branchPtrInNurseryRange(Assembler::Equal, obj, scratch, &skipBarrier); + masm.branchValueIsNurseryObject(Assembler::NotEqual, val, scratch, &skipBarrier); + + // void PostWriteBarrier(JSRuntime* rt, JSObject* obj); +#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) + saveRegs.add(ICTailCallReg); +#endif + saveRegs.set() = GeneralRegisterSet::Intersect(saveRegs.set(), GeneralRegisterSet::Volatile()); + masm.PushRegsInMask(saveRegs); + masm.setupUnalignedABICall(2, scratch); + masm.movePtr(ImmPtr(cx->runtime()), scratch); + masm.passABIArg(scratch); + masm.passABIArg(obj); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier)); + masm.PopRegsInMask(saveRegs); + + masm.bind(&skipBarrier); + return true; +} + +} // namespace jit +} // namespace js diff --git a/js/src/jit/SharedIC.h b/js/src/jit/SharedIC.h new file mode 100644 index 0000000000..4169efb641 --- /dev/null +++ b/js/src/jit/SharedIC.h @@ -0,0 +1,1089 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_SharedIC_h +#define jit_SharedIC_h + +#include "jscntxt.h" +#include "jscompartment.h" +#include "jsgc.h" + +#include "jit/BaselineICList.h" +#include "jit/MacroAssembler.h" +#include "jit/SharedICList.h" +#include "jit/SharedICRegisters.h" + +namespace js { +namespace jit { + +// +// Baseline Inline Caches are polymorphic caches that aggressively +// share their stub code. +// +// Every polymorphic site contains a linked list of stubs which are +// specific to that site. These stubs are composed of a |StubData| +// structure that stores parametrization information (e.g. +// the shape pointer for a shape-check-and-property-get stub), any +// dynamic information (e.g. warm-up counters), a pointer to the stub code, +// and a pointer to the next stub state in the linked list. +// +// Every BaselineScript keeps an table of |CacheDescriptor| data +// structures, which store the following: +// A pointer to the first StubData in the cache. +// The bytecode PC of the relevant IC. +// The machine-code PC where the call to the stubcode returns. +// +// A diagram: +// +// Control flow Pointers +// =======# ----. .----> +// # | | +// #======> \-----/ +// +// +// .---------------------------------------. +// | .-------------------------. | +// | | .----. | | +// Baseline | | | | | | +// JIT Code 0 ^ 1 ^ 2 ^ | | | +// +--------------+ .-->+-----+ +-----+ +-----+ | | | +// | | #=|==>| |==>| |==>| FB | | | | +// | | # | +-----+ +-----+ +-----+ | | | +// | | # | # # # | | | +// |==============|==# | # # # | | | +// |=== IC =======| | # # # | | | +// .->|==============|<===|======#=========#=========# | | | +// | | | | | | | +// | | | | | | | +// | | | | | | | +// | | | | v | | +// | | | | +---------+ | | +// | | | | | Fallback| | | +// | | | | | Stub | | | +// | | | | | Code | | | +// | | | | +---------+ | | +// | +--------------+ | | | +// | |_______ | +---------+ | | +// | | | | Stub |<---/ | +// | IC | \--. | Code | | +// | Descriptor | | +---------+ | +// | Table v | | +// | +-----------------+ | +---------+ | +// \--| Ins | PC | Stub |----/ | Stub |<-------/ +// +-----------------+ | Code | +// | ... | +---------+ +// +-----------------+ +// Shared +// Stub Code +// +// +// Type ICs +// ======== +// +// Type ICs are otherwise regular ICs that are actually nested within +// other IC chains. They serve to optimize locations in the code where the +// baseline compiler would have otherwise had to perform a type Monitor operation +// (e.g. the result of GetProp, GetElem, etc.), or locations where the baseline +// compiler would have had to modify a heap typeset using the type of an input +// value (e.g. SetProp, SetElem, etc.) +// +// There are two kinds of Type ICs: Monitor and Update. +// +// Note that type stub bodies are no-ops. The stubs only exist for their +// guards, and their existence simply signifies that the typeset (implicit) +// that is being checked already contains that type. +// +// TypeMonitor ICs +// --------------- +// Monitor ICs are shared between stubs in the general IC, and monitor the resulting +// types of getter operations (call returns, getprop outputs, etc.) +// +// +-----------+ +-----------+ +-----------+ +-----------+ +// ---->| Stub 1 |---->| Stub 2 |---->| Stub 3 |---->| FB Stub | +// +-----------+ +-----------+ +-----------+ +-----------+ +// | | | | +// |------------------/-----------------/ | +// v | +// +-----------+ +-----------+ +-----------+ | +// | Type 1 |---->| Type 2 |---->| Type FB | | +// +-----------+ +-----------+ +-----------+ | +// | | | | +// <----------/-----------------/------------------/------------------/ +// r e t u r n p a t h +// +// After an optimized IC stub successfully executes, it passes control to the type stub +// chain to check the resulting type. If no type stub succeeds, and the monitor fallback +// stub is reached, the monitor fallback stub performs a manual monitor, and also adds the +// appropriate type stub to the chain. +// +// The IC's main fallback, in addition to generating new mainline stubs, also generates +// type stubs as reflected by its returned value. +// +// NOTE: The type IC chain returns directly to the mainline code, not back to the +// stub it was entered from. Thus, entering a type IC is a matter of a |jump|, not +// a |call|. This allows us to safely call a VM Monitor function from within the monitor IC's +// fallback chain, since the return address (needed for stack inspection) is preserved. +// +// +// TypeUpdate ICs +// -------------- +// Update ICs update heap typesets and monitor the input types of setter operations +// (setelem, setprop inputs, etc.). Unlike monitor ICs, they are not shared +// between stubs on an IC, but instead are kept track of on a per-stub basis. +// +// This is because the main stubs for the operation will each identify a potentially +// different ObjectGroup to update. New input types must be tracked on a group-to- +// group basis. +// +// Type-update ICs cannot be called in tail position (they must return to the +// the stub that called them so that the stub may continue to perform its original +// purpose). This means that any VMCall to perform a manual type update from C++ must be +// done from within the main IC stub. This necessitates that the stub enter a +// "BaselineStub" frame before making the call. +// +// If the type-update IC chain could itself make the VMCall, then the BaselineStub frame +// must be entered before calling the type-update chain, and exited afterward. This +// is very expensive for a common case where we expect the type-update fallback to not +// be called. To avoid the cost of entering and exiting a BaselineStub frame when +// using the type-update IC chain, we design the chain to not perform any VM-calls +// in its fallback. +// +// Instead, the type-update IC chain is responsible for returning 1 or 0, depending +// on if a type is represented in the chain or not. The fallback stub simply returns +// 0, and all other optimized stubs return 1. +// If the chain returns 1, then the IC stub goes ahead and performs its operation. +// If the chain returns 0, then the IC stub performs a call to the fallback function +// inline (doing the requisite BaselineStub frame enter/exit). +// This allows us to avoid the expensive subfram enter/exit in the common case. +// +// r e t u r n p a t h +// <--------------.-----------------.-----------------.-----------------. +// | | | | +// +-----------+ +-----------+ +-----------+ +-----------+ +// ---->| Stub 1 |---->| Stub 2 |---->| Stub 3 |---->| FB Stub | +// +-----------+ +-----------+ +-----------+ +-----------+ +// | ^ | ^ | ^ +// | | | | | | +// | | | | | |----------------. +// | | | | v |1 |0 +// | | | | +-----------+ +-----------+ +// | | | | | Type 3.1 |--->| FB 3 | +// | | | | +-----------+ +-----------+ +// | | | | +// | | | \-------------.-----------------. +// | | | | | | +// | | v |1 |1 |0 +// | | +-----------+ +-----------+ +-----------+ +// | | | Type 2.1 |---->| Type 2.2 |---->| FB 2 | +// | | +-----------+ +-----------+ +-----------+ +// | | +// | \-------------.-----------------. +// | | | | +// v |1 |1 |0 +// +-----------+ +-----------+ +-----------+ +// | Type 1.1 |---->| Type 1.2 |---->| FB 1 | +// +-----------+ +-----------+ +-----------+ +// + +class ICStub; +class ICFallbackStub; + +#define FORWARD_DECLARE_STUBS(kindName) class IC##kindName; + IC_BASELINE_STUB_KIND_LIST(FORWARD_DECLARE_STUBS) + IC_SHARED_STUB_KIND_LIST(FORWARD_DECLARE_STUBS) +#undef FORWARD_DECLARE_STUBS + +#ifdef DEBUG +void FallbackICSpew(JSContext* cx, ICFallbackStub* stub, const char* fmt, ...); +void TypeFallbackICSpew(JSContext* cx, ICTypeMonitor_Fallback* stub, const char* fmt, ...); +#else +#define FallbackICSpew(...) +#define TypeFallbackICSpew(...) +#endif + +// +// An entry in the Baseline IC descriptor table. +// +class ICEntry +{ + private: + // A pointer to the baseline IC stub for this instruction. + ICStub* firstStub_; + + // Offset from the start of the JIT code where the IC + // load and call instructions are. + uint32_t returnOffset_; + + // The PC of this IC's bytecode op within the JSScript. + uint32_t pcOffset_ : 28; + + public: + enum Kind { + // A for-op IC entry. + Kind_Op = 0, + + // A non-op IC entry. + Kind_NonOp, + + // A fake IC entry for returning from a callVM for an op. + Kind_CallVM, + + // A fake IC entry for returning from a callVM not for an op (e.g., in + // the prologue). + Kind_NonOpCallVM, + + // A fake IC entry for returning from a callVM to the interrupt + // handler via the over-recursion check on function entry. + Kind_StackCheck, + + // As above, but for the early check. See emitStackCheck. + Kind_EarlyStackCheck, + + // A fake IC entry for returning from DebugTrapHandler. + Kind_DebugTrap, + + // A fake IC entry for returning from a callVM to + // Debug{Prologue,Epilogue}. + Kind_DebugPrologue, + Kind_DebugEpilogue, + + Kind_Invalid + }; + + private: + // What this IC is for. + Kind kind_ : 4; + + // Set the kind and asserts that it's sane. + void setKind(Kind kind) { + MOZ_ASSERT(kind < Kind_Invalid); + kind_ = kind; + MOZ_ASSERT(this->kind() == kind); + } + + public: + ICEntry(uint32_t pcOffset, Kind kind) + : firstStub_(nullptr), returnOffset_(), pcOffset_(pcOffset) + { + // The offset must fit in at least 28 bits, since we shave off 4 for + // the Kind enum. + MOZ_ASSERT(pcOffset_ == pcOffset); + JS_STATIC_ASSERT(BaselineScript::MAX_JSSCRIPT_LENGTH <= (1u << 28) - 1); + MOZ_ASSERT(pcOffset <= BaselineScript::MAX_JSSCRIPT_LENGTH); + setKind(kind); + } + + CodeOffsetLabel returnOffset() const { + return CodeOffsetLabel(returnOffset_); + } + + void setReturnOffset(CodeOffsetLabel offset) { + MOZ_ASSERT(offset.offset() <= (size_t) UINT32_MAX); + returnOffset_ = (uint32_t) offset.offset(); + } + + void fixupReturnOffset(MacroAssembler& masm) { + CodeOffsetLabel offset = returnOffset(); + offset.fixup(&masm); + MOZ_ASSERT(offset.offset() <= UINT32_MAX); + returnOffset_ = (uint32_t) offset.offset(); + } + + uint32_t pcOffset() const { + return pcOffset_; + } + + jsbytecode* pc(JSScript* script) const { + return script->offsetToPC(pcOffset_); + } + + Kind kind() const { + // MSVC compiles enums as signed. + return Kind(kind_ & 0xf); + } + bool isForOp() const { + return kind() == Kind_Op; + } + + void setFakeKind(Kind kind) { + MOZ_ASSERT(kind != Kind_Op && kind != Kind_NonOp); + setKind(kind); + } + + bool hasStub() const { + return firstStub_ != nullptr; + } + ICStub* firstStub() const { + MOZ_ASSERT(hasStub()); + return firstStub_; + } + + ICFallbackStub* fallbackStub() const; + + void setFirstStub(ICStub* stub) { + firstStub_ = stub; + } + + static inline size_t offsetOfFirstStub() { + return offsetof(ICEntry, firstStub_); + } + + inline ICStub** addressOfFirstStub() { + return &firstStub_; + } +}; + +class ICMonitoredStub; +class ICMonitoredFallbackStub; +class ICUpdatedStub; + +// Constant iterator that traverses arbitrary chains of ICStubs. +// No requirements are made of the ICStub used to construct this +// iterator, aside from that the stub be part of a nullptr-terminated +// chain. +// The iterator is considered to be at its end once it has been +// incremented _past_ the last stub. Thus, if 'atEnd()' returns +// true, the '*' and '->' operations are not valid. +class ICStubConstIterator +{ + friend class ICStub; + friend class ICFallbackStub; + + private: + ICStub* currentStub_; + + public: + explicit ICStubConstIterator(ICStub* currentStub) : currentStub_(currentStub) {} + + static ICStubConstIterator StartingAt(ICStub* stub) { + return ICStubConstIterator(stub); + } + static ICStubConstIterator End(ICStub* stub) { + return ICStubConstIterator(nullptr); + } + + bool operator ==(const ICStubConstIterator& other) const { + return currentStub_ == other.currentStub_; + } + bool operator !=(const ICStubConstIterator& other) const { + return !(*this == other); + } + + ICStubConstIterator& operator++(); + + ICStubConstIterator operator++(int) { + ICStubConstIterator oldThis(*this); + ++(*this); + return oldThis; + } + + ICStub* operator*() const { + MOZ_ASSERT(currentStub_); + return currentStub_; + } + + ICStub* operator ->() const { + MOZ_ASSERT(currentStub_); + return currentStub_; + } + + bool atEnd() const { + return currentStub_ == nullptr; + } +}; + +// Iterator that traverses "regular" IC chains that start at an ICEntry +// and are terminated with an ICFallbackStub. +// +// The iterator is considered to be at its end once it is _at_ the +// fallback stub. Thus, unlike the ICStubConstIterator, operators +// '*' and '->' are valid even if 'atEnd()' returns true - they +// will act on the fallback stub. +// +// This iterator also allows unlinking of stubs being traversed. +// Note that 'unlink' does not implicitly advance the iterator - +// it must be advanced explicitly using '++'. +class ICStubIterator +{ + friend class ICFallbackStub; + + private: + ICEntry* icEntry_; + ICFallbackStub* fallbackStub_; + ICStub* previousStub_; + ICStub* currentStub_; + bool unlinked_; + + explicit ICStubIterator(ICFallbackStub* fallbackStub, bool end=false); + public: + + bool operator ==(const ICStubIterator& other) const { + // == should only ever be called on stubs from the same chain. + MOZ_ASSERT(icEntry_ == other.icEntry_); + MOZ_ASSERT(fallbackStub_ == other.fallbackStub_); + return currentStub_ == other.currentStub_; + } + bool operator !=(const ICStubIterator& other) const { + return !(*this == other); + } + + ICStubIterator& operator++(); + + ICStubIterator operator++(int) { + ICStubIterator oldThis(*this); + ++(*this); + return oldThis; + } + + ICStub* operator*() const { + return currentStub_; + } + + ICStub* operator ->() const { + return currentStub_; + } + + bool atEnd() const { + return currentStub_ == (ICStub*) fallbackStub_; + } + + void unlink(JSContext* cx); +}; + +// +// Base class for all IC stubs. +// +class ICStub +{ + friend class ICFallbackStub; + + public: + enum Kind { + INVALID = 0, +#define DEF_ENUM_KIND(kindName) kindName, + IC_BASELINE_STUB_KIND_LIST(DEF_ENUM_KIND) + IC_SHARED_STUB_KIND_LIST(DEF_ENUM_KIND) +#undef DEF_ENUM_KIND + LIMIT + }; + + static inline bool IsValidKind(Kind k) { + return (k > INVALID) && (k < LIMIT); + } + + static const char* KindString(Kind k) { + switch(k) { +#define DEF_KIND_STR(kindName) case kindName: return #kindName; + IC_BASELINE_STUB_KIND_LIST(DEF_KIND_STR) + IC_SHARED_STUB_KIND_LIST(DEF_KIND_STR) +#undef DEF_KIND_STR + default: + MOZ_CRASH("Invalid kind."); + } + } + + enum Trait { + Regular = 0x0, + Fallback = 0x1, + Monitored = 0x2, + MonitoredFallback = 0x3, + Updated = 0x4 + }; + + void markCode(JSTracer* trc, const char* name); + void updateCode(JitCode* stubCode); + void trace(JSTracer* trc); + + template + static T* New(JSContext* cx, ICStubSpace* space, JitCode* code, Args&&... args) { + if (!code) + return nullptr; + T* result = space->allocate(code, mozilla::Forward(args)...); + if (!result) + ReportOutOfMemory(cx); + return result; + } + + protected: + // The raw jitcode to call for this stub. + uint8_t* stubCode_; + + // Pointer to next IC stub. This is null for the last IC stub, which should + // either be a fallback or inert IC stub. + ICStub* next_; + + // A 16-bit field usable by subtypes of ICStub for subtype-specific small-info + uint16_t extra_; + + // The kind of the stub. + // High bit is 'isFallback' flag. + // Second high bit is 'isMonitored' flag. + Trait trait_ : 3; + Kind kind_ : 13; + + inline ICStub(Kind kind, JitCode* stubCode) + : stubCode_(stubCode->raw()), + next_(nullptr), + extra_(0), + trait_(Regular), + kind_(kind) + { + MOZ_ASSERT(stubCode != nullptr); + } + + inline ICStub(Kind kind, Trait trait, JitCode* stubCode) + : stubCode_(stubCode->raw()), + next_(nullptr), + extra_(0), + trait_(trait), + kind_(kind) + { + MOZ_ASSERT(stubCode != nullptr); + } + + inline Trait trait() const { + // Workaround for MSVC reading trait_ as signed value. + return (Trait)(trait_ & 0x7); + } + + public: + + inline Kind kind() const { + return static_cast(kind_); + } + + inline bool isFallback() const { + return trait() == Fallback || trait() == MonitoredFallback; + } + + inline bool isMonitored() const { + return trait() == Monitored; + } + + inline bool isUpdated() const { + return trait() == Updated; + } + + inline bool isMonitoredFallback() const { + return trait() == MonitoredFallback; + } + + inline const ICFallbackStub* toFallbackStub() const { + MOZ_ASSERT(isFallback()); + return reinterpret_cast(this); + } + + inline ICFallbackStub* toFallbackStub() { + MOZ_ASSERT(isFallback()); + return reinterpret_cast(this); + } + + inline const ICMonitoredStub* toMonitoredStub() const { + MOZ_ASSERT(isMonitored()); + return reinterpret_cast(this); + } + + inline ICMonitoredStub* toMonitoredStub() { + MOZ_ASSERT(isMonitored()); + return reinterpret_cast(this); + } + + inline const ICMonitoredFallbackStub* toMonitoredFallbackStub() const { + MOZ_ASSERT(isMonitoredFallback()); + return reinterpret_cast(this); + } + + inline ICMonitoredFallbackStub* toMonitoredFallbackStub() { + MOZ_ASSERT(isMonitoredFallback()); + return reinterpret_cast(this); + } + + inline const ICUpdatedStub* toUpdatedStub() const { + MOZ_ASSERT(isUpdated()); + return reinterpret_cast(this); + } + + inline ICUpdatedStub* toUpdatedStub() { + MOZ_ASSERT(isUpdated()); + return reinterpret_cast(this); + } + +#define KIND_METHODS(kindName) \ + inline bool is##kindName() const { return kind() == kindName; } \ + inline const IC##kindName* to##kindName() const { \ + MOZ_ASSERT(is##kindName()); \ + return reinterpret_cast(this); \ + } \ + inline IC##kindName* to##kindName() { \ + MOZ_ASSERT(is##kindName()); \ + return reinterpret_cast(this); \ + } + IC_BASELINE_STUB_KIND_LIST(KIND_METHODS) + IC_SHARED_STUB_KIND_LIST(KIND_METHODS) +#undef KIND_METHODS + + inline ICStub* next() const { + return next_; + } + + inline bool hasNext() const { + return next_ != nullptr; + } + + inline void setNext(ICStub* stub) { + // Note: next_ only needs to be changed under the compilation lock for + // non-type-monitor/update ICs. + next_ = stub; + } + + inline ICStub** addressOfNext() { + return &next_; + } + + inline JitCode* jitCode() { + return JitCode::FromExecutable(stubCode_); + } + + inline uint8_t* rawStubCode() const { + return stubCode_; + } + + // This method is not valid on TypeUpdate stub chains! + inline ICFallbackStub* getChainFallback() { + ICStub* lastStub = this; + while (lastStub->next_) + lastStub = lastStub->next_; + MOZ_ASSERT(lastStub->isFallback()); + return lastStub->toFallbackStub(); + } + + inline ICStubConstIterator beginHere() { + return ICStubConstIterator::StartingAt(this); + } + + static inline size_t offsetOfNext() { + return offsetof(ICStub, next_); + } + + static inline size_t offsetOfStubCode() { + return offsetof(ICStub, stubCode_); + } + + static inline size_t offsetOfExtra() { + return offsetof(ICStub, extra_); + } + + static bool CanMakeCalls(ICStub::Kind kind) { + MOZ_ASSERT(IsValidKind(kind)); + switch (kind) { + case Call_Fallback: + case Call_Scripted: + case Call_AnyScripted: + case Call_Native: + case Call_ClassHook: + case Call_ScriptedApplyArray: + case Call_ScriptedApplyArguments: + case Call_ScriptedFunCall: + case Call_StringSplit: + case WarmUpCounter_Fallback: + case GetElem_NativeSlot: + case GetElem_NativePrototypeSlot: + case GetElem_NativePrototypeCallNative: + case GetElem_NativePrototypeCallScripted: + case GetProp_CallScripted: + case GetProp_CallNative: + case GetProp_CallDOMProxyNative: + case GetProp_CallDOMProxyWithGenerationNative: + case GetProp_DOMProxyShadowed: + case GetProp_Generic: + case SetProp_CallScripted: + case SetProp_CallNative: + case RetSub_Fallback: + // These two fallback stubs don't actually make non-tail calls, + // but the fallback code for the bailout path needs to pop the stub frame + // pushed during the bailout. + case GetProp_Fallback: + case SetProp_Fallback: +#if JS_HAS_NO_SUCH_METHOD + case GetElem_Dense: + case GetElem_Arguments: + case GetProp_NativePrototype: + case GetProp_Native: +#endif + return true; + default: + return false; + } + } + + // Optimized stubs get purged on GC. But some stubs can be active on the + // stack during GC - specifically the ones that can make calls. To ensure + // that these do not get purged, all stubs that can make calls are allocated + // in the fallback stub space. + bool allocatedInFallbackSpace() const { + MOZ_ASSERT(next()); + return CanMakeCalls(kind()); + } +}; + +class ICFallbackStub : public ICStub +{ + friend class ICStubConstIterator; + protected: + // Fallback stubs need these fields to easily add new stubs to + // the linked list of stubs for an IC. + + // The IC entry for this linked list of stubs. + ICEntry* icEntry_; + + // The number of stubs kept in the IC entry. + uint32_t numOptimizedStubs_; + + // A pointer to the location stub pointer that needs to be + // changed to add a new "last" stub immediately before the fallback + // stub. This'll start out pointing to the icEntry's "firstStub_" + // field, and as new stubs are addd, it'll point to the current + // last stub's "next_" field. + ICStub** lastStubPtrAddr_; + + ICFallbackStub(Kind kind, JitCode* stubCode) + : ICStub(kind, ICStub::Fallback, stubCode), + icEntry_(nullptr), + numOptimizedStubs_(0), + lastStubPtrAddr_(nullptr) {} + + ICFallbackStub(Kind kind, Trait trait, JitCode* stubCode) + : ICStub(kind, trait, stubCode), + icEntry_(nullptr), + numOptimizedStubs_(0), + lastStubPtrAddr_(nullptr) + { + MOZ_ASSERT(trait == ICStub::Fallback || + trait == ICStub::MonitoredFallback); + } + + public: + inline ICEntry* icEntry() const { + return icEntry_; + } + + inline size_t numOptimizedStubs() const { + return (size_t) numOptimizedStubs_; + } + + // The icEntry and lastStubPtrAddr_ fields can't be initialized when the stub is + // created since the stub is created at compile time, and we won't know the IC entry + // address until after compile when the BaselineScript is created. This method + // allows these fields to be fixed up at that point. + void fixupICEntry(ICEntry* icEntry) { + MOZ_ASSERT(icEntry_ == nullptr); + MOZ_ASSERT(lastStubPtrAddr_ == nullptr); + icEntry_ = icEntry; + lastStubPtrAddr_ = icEntry_->addressOfFirstStub(); + } + + // Add a new stub to the IC chain terminated by this fallback stub. + void addNewStub(ICStub* stub) { + MOZ_ASSERT(*lastStubPtrAddr_ == this); + MOZ_ASSERT(stub->next() == nullptr); + stub->setNext(this); + *lastStubPtrAddr_ = stub; + lastStubPtrAddr_ = stub->addressOfNext(); + numOptimizedStubs_++; + } + + ICStubConstIterator beginChainConst() const { + return ICStubConstIterator(icEntry_->firstStub()); + } + + ICStubIterator beginChain() { + return ICStubIterator(this); + } + + bool hasStub(ICStub::Kind kind) const { + for (ICStubConstIterator iter = beginChainConst(); !iter.atEnd(); iter++) { + if (iter->kind() == kind) + return true; + } + return false; + } + + unsigned numStubsWithKind(ICStub::Kind kind) const { + unsigned count = 0; + for (ICStubConstIterator iter = beginChainConst(); !iter.atEnd(); iter++) { + if (iter->kind() == kind) + count++; + } + return count; + } + + void unlinkStub(Zone* zone, ICStub* prev, ICStub* stub); + void unlinkStubsWithKind(JSContext* cx, ICStub::Kind kind); +}; + +// Monitored stubs are IC stubs that feed a single resulting value out to a +// type monitor operation. +class ICMonitoredStub : public ICStub +{ + protected: + // Pointer to the start of the type monitoring stub chain. + ICStub* firstMonitorStub_; + + ICMonitoredStub(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub); + + public: + inline void updateFirstMonitorStub(ICStub* monitorStub) { + // This should only be called once: when the first optimized monitor stub + // is added to the type monitor IC chain. + MOZ_ASSERT(firstMonitorStub_ && firstMonitorStub_->isTypeMonitor_Fallback()); + firstMonitorStub_ = monitorStub; + } + inline void resetFirstMonitorStub(ICStub* monitorFallback) { + MOZ_ASSERT(monitorFallback->isTypeMonitor_Fallback()); + firstMonitorStub_ = monitorFallback; + } + inline ICStub* firstMonitorStub() const { + return firstMonitorStub_; + } + + static inline size_t offsetOfFirstMonitorStub() { + return offsetof(ICMonitoredStub, firstMonitorStub_); + } +}; + +// Monitored fallback stubs - as the name implies. +class ICMonitoredFallbackStub : public ICFallbackStub +{ + protected: + // Pointer to the fallback monitor stub. + ICTypeMonitor_Fallback* fallbackMonitorStub_; + + ICMonitoredFallbackStub(Kind kind, JitCode* stubCode) + : ICFallbackStub(kind, ICStub::MonitoredFallback, stubCode), + fallbackMonitorStub_(nullptr) {} + + public: + bool initMonitoringChain(JSContext* cx, ICStubSpace* space); + bool addMonitorStubForValue(JSContext* cx, JSScript* script, HandleValue val); + + inline ICTypeMonitor_Fallback* fallbackMonitorStub() const { + return fallbackMonitorStub_; + } + + static inline size_t offsetOfFallbackMonitorStub() { + return offsetof(ICMonitoredFallbackStub, fallbackMonitorStub_); + } +}; + +// Updated stubs are IC stubs that use a TypeUpdate IC to track +// the status of heap typesets that need to be updated. +class ICUpdatedStub : public ICStub +{ + protected: + // Pointer to the start of the type updating stub chain. + ICStub* firstUpdateStub_; + + static const uint32_t MAX_OPTIMIZED_STUBS = 8; + uint32_t numOptimizedStubs_; + + ICUpdatedStub(Kind kind, JitCode* stubCode) + : ICStub(kind, ICStub::Updated, stubCode), + firstUpdateStub_(nullptr), + numOptimizedStubs_(0) + {} + + public: + bool initUpdatingChain(JSContext* cx, ICStubSpace* space); + + bool addUpdateStubForValue(JSContext* cx, HandleScript script, HandleObject obj, HandleId id, + HandleValue val); + + void addOptimizedUpdateStub(ICStub* stub) { + if (firstUpdateStub_->isTypeUpdate_Fallback()) { + stub->setNext(firstUpdateStub_); + firstUpdateStub_ = stub; + } else { + ICStub* iter = firstUpdateStub_; + MOZ_ASSERT(iter->next() != nullptr); + while (!iter->next()->isTypeUpdate_Fallback()) + iter = iter->next(); + MOZ_ASSERT(iter->next()->next() == nullptr); + stub->setNext(iter->next()); + iter->setNext(stub); + } + + numOptimizedStubs_++; + } + + inline ICStub* firstUpdateStub() const { + return firstUpdateStub_; + } + + bool hasTypeUpdateStub(ICStub::Kind kind) { + ICStub* stub = firstUpdateStub_; + do { + if (stub->kind() == kind) + return true; + + stub = stub->next(); + } while (stub); + + return false; + } + + inline uint32_t numOptimizedStubs() const { + return numOptimizedStubs_; + } + + static inline size_t offsetOfFirstUpdateStub() { + return offsetof(ICUpdatedStub, firstUpdateStub_); + } +}; + +// Base class for stubcode compilers. +class ICStubCompiler +{ + // Prevent GC in the middle of stub compilation. + js::gc::AutoSuppressGC suppressGC; + + public: + enum class Engine { + Baseline = 0, + IonMonkey + }; + + protected: + JSContext* cx; + ICStub::Kind kind; + Engine engine_; + bool inStubFrame_; + +#ifdef DEBUG + bool entersStubFrame_; +#endif + + // By default the stubcode key is just the kind. + virtual int32_t getKey() const { + return static_cast(engine_) | + (static_cast(kind) << 1); + } + + virtual bool generateStubCode(MacroAssembler& masm) = 0; + virtual bool postGenerateStubCode(MacroAssembler& masm, Handle genCode) { + return true; + } + JitCode* getStubCode(); + + ICStubCompiler(JSContext* cx, ICStub::Kind kind, Engine engine) + : suppressGC(cx), cx(cx), kind(kind), engine_(engine), inStubFrame_(false) +#ifdef DEBUG + , entersStubFrame_(false) +#endif + {} + + // Pushes the frame ptr. + void pushFramePtr(MacroAssembler& masm, Register scratch); + + // Emits a tail call to a VMFunction wrapper. + bool tailCallVM(const VMFunction& fun, MacroAssembler& masm); + + // Emits a normal (non-tail) call to a VMFunction wrapper. + bool callVM(const VMFunction& fun, MacroAssembler& masm); + + // Emits a call to a type-update IC, assuming that the value to be + // checked is already in R0. + bool callTypeUpdateIC(MacroAssembler& masm, uint32_t objectOffset); + + // A stub frame is used when a stub wants to call into the VM without + // performing a tail call. This is required for the return address + // to pc mapping to work. + void enterStubFrame(MacroAssembler& masm, Register scratch); + void leaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false); + + // Some stubs need to emit SPS profiler updates. This emits the guarding + // jitcode for those stubs. If profiling is not enabled, jumps to the + // given label. + void guardProfilingEnabled(MacroAssembler& masm, Register scratch, Label* skip); + + inline AllocatableGeneralRegisterSet availableGeneralRegs(size_t numInputs) const { + AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); + MOZ_ASSERT(!regs.has(BaselineStackReg)); +#if defined(JS_CODEGEN_ARM) + MOZ_ASSERT(!regs.has(ICTailCallReg)); + regs.take(BaselineSecondScratchReg); +#elif defined(JS_CODEGEN_MIPS) + MOZ_ASSERT(!regs.has(ICTailCallReg)); + MOZ_ASSERT(!regs.has(BaselineSecondScratchReg)); +#endif + regs.take(BaselineFrameReg); + regs.take(ICStubReg); +#ifdef JS_CODEGEN_X64 + regs.take(ExtractTemp0); + regs.take(ExtractTemp1); +#endif + + switch (numInputs) { + case 0: + break; + case 1: + regs.take(R0); + break; + case 2: + regs.take(R0); + regs.take(R1); + break; + default: + MOZ_CRASH("Invalid numInputs"); + } + + return regs; + } + + bool emitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, ValueOperand val, + Register scratch, LiveGeneralRegisterSet saveRegs); + + template + T* newStub(Args&&... args) { + return ICStub::New(cx, mozilla::Forward(args)...); + } + + public: + virtual ICStub* getStub(ICStubSpace* space) = 0; + + static ICStubSpace* StubSpaceForKind(ICStub::Kind kind, JSScript* script) { + if (ICStub::CanMakeCalls(kind)) + return script->baselineScript()->fallbackStubSpace(); + return script->zone()->jitZone()->optimizedStubSpace(); + } + + ICStubSpace* getStubSpace(JSScript* script) { + return StubSpaceForKind(kind, script); + } +}; + +// Base class for stub compilers that can generate multiple stubcodes. +// These compilers need access to the JSOp they are compiling for. +class ICMultiStubCompiler : public ICStubCompiler +{ + protected: + JSOp op; + + // Stub keys for multi-stub kinds are composed of both the kind + // and the op they are compiled for. + virtual int32_t getKey() const { + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(op) << 17); + } + + ICMultiStubCompiler(JSContext* cx, ICStub::Kind kind, JSOp op, Engine engine) + : ICStubCompiler(cx, kind, engine), op(op) {} +}; + +} // namespace jit +} // namespace js + +#endif /* jit_SharedIC_h */ diff --git a/js/src/jit/SharedICList.h b/js/src/jit/SharedICList.h new file mode 100644 index 0000000000..6a67822129 --- /dev/null +++ b/js/src/jit/SharedICList.h @@ -0,0 +1,19 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_SharedICList_h +#define jit_SharedICList_h + +namespace js { +namespace jit { + +// List of IC stub kinds that can run in Baseline and in IonMonkey +#define IC_SHARED_STUB_KIND_LIST(_) + +} // namespace jit +} // namespace js + +#endif /* jit_SharedICList_h */ diff --git a/js/src/jit/TypePolicy.cpp b/js/src/jit/TypePolicy.cpp index d573e9028d..5984c1fafe 100644 --- a/js/src/jit/TypePolicy.cpp +++ b/js/src/jit/TypePolicy.cpp @@ -1146,7 +1146,7 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) _(FloatingPointPolicy<0>) \ _(IntPolicy<0>) \ _(IntPolicy<1>) \ - _(Mix3Policy, BoxExceptPolicy<1, MIRType_String>, BoxPolicy<2> >) \ + _(Mix3Policy, StringPolicy<1>, BoxPolicy<2> >) \ _(Mix3Policy, BoxPolicy<1>, BoxPolicy<2> >) \ _(Mix3Policy, BoxPolicy<1>, ObjectPolicy<2> >) \ _(Mix3Policy, IntPolicy<1>, BoxPolicy<2> >) \ diff --git a/js/src/jit/arm/AtomicOperations-arm.h b/js/src/jit/arm/AtomicOperations-arm.h new file mode 100644 index 0000000000..2413719b8b --- /dev/null +++ b/js/src/jit/arm/AtomicOperations-arm.h @@ -0,0 +1,220 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* For documentation, see jit/AtomicOperations.h */ + +#ifndef jit_arm_AtomicOperations_arm_h +#define jit_arm_AtomicOperations_arm_h + +#include "jit/arm/Architecture-arm.h" +#include "jit/AtomicOperations.h" + +#if defined(__clang__) || defined(__GNUC__) + +// The default implementation tactic for gcc/clang is to use the newer +// __atomic intrinsics added for use in C++11 . Where that +// isn't available, we use GCC's older __sync functions instead. +// +// ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS is kept as a backward +// compatible option for older compilers: enable this to use GCC's old +// __sync functions instead of the newer __atomic functions. This +// will be required for GCC 4.6.x and earlier, and probably for Clang +// 3.1, should we need to use those versions. + +//#define ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + +inline bool +js::jit::AtomicOperations::isLockfree8() +{ + // The JIT and the C++ compiler must agree on whether to use atomics + // for 64-bit accesses. There are two ways to do this: either the + // JIT defers to the C++ compiler (so if the C++ code is compiled + // for ARMv6, say, and __atomic_always_lock_free(8) is false, then the + // JIT ignores the fact that the program is running on ARMv7 or newer); + // or the C++ code in this file calls out to run-time generated code + // to do whatever the JIT does. + // + // For now, make the JIT defer to the C++ compiler when we know what + // the C++ compiler will do, otherwise assume a lock is needed. +# ifndef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + MOZ_ASSERT(__atomic_always_lock_free(sizeof(int8_t), 0)); + MOZ_ASSERT(__atomic_always_lock_free(sizeof(int16_t), 0)); + MOZ_ASSERT(__atomic_always_lock_free(sizeof(int32_t), 0)); + return HasLDSTREXBHD() && __atomic_always_lock_free(sizeof(int64_t), 0); +# else + return false; +# endif +} + +inline void +js::jit::AtomicOperations::fenceSeqCst() +{ +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + __sync_synchronize(); +# else + __atomic_thread_fence(__ATOMIC_SEQ_CST); +# endif +} + +template +inline T +js::jit::AtomicOperations::loadSeqCst(T* addr) +{ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + __sync_synchronize(); + T v = *addr; + __sync_synchronize(); +# else + T v; + __atomic_load(addr, &v, __ATOMIC_SEQ_CST); +# endif + return v; +} + +template +inline void +js::jit::AtomicOperations::storeSeqCst(T* addr, T val) +{ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + __sync_synchronize(); + *addr = val; + __sync_synchronize(); +# else + __atomic_store(addr, &val, __ATOMIC_SEQ_CST); +# endif +} + +template +inline T +js::jit::AtomicOperations::exchangeSeqCst(T* addr, T val) +{ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + T v; + __sync_synchronize(); + do { + v = *addr; + } while (__sync_val_compare_and_swap(addr, v, val) != v); + return v; +# else + T v; + __atomic_exchange(addr, &val, &v, __ATOMIC_SEQ_CST); + return v; +# endif +} + +template +inline T +js::jit::AtomicOperations::compareExchangeSeqCst(T* addr, T oldval, T newval) +{ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_val_compare_and_swap(addr, oldval, newval); +# else + __atomic_compare_exchange(addr, &oldval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return oldval; +# endif +} + +template +inline T +js::jit::AtomicOperations::fetchAddSeqCst(T* addr, T val) +{ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_fetch_and_add(addr, val); +# else + return __atomic_fetch_add(addr, val, __ATOMIC_SEQ_CST); +# endif +} + +template +inline T +js::jit::AtomicOperations::fetchSubSeqCst(T* addr, T val) +{ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_fetch_and_sub(addr, val); +# else + return __atomic_fetch_sub(addr, val, __ATOMIC_SEQ_CST); +# endif +} + +template +inline T +js::jit::AtomicOperations::fetchAndSeqCst(T* addr, T val) +{ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_fetch_and_and(addr, val); +# else + return __atomic_fetch_and(addr, val, __ATOMIC_SEQ_CST); +# endif +} + +template +inline T +js::jit::AtomicOperations::fetchOrSeqCst(T* addr, T val) +{ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_fetch_and_or(addr, val); +# else + return __atomic_fetch_or(addr, val, __ATOMIC_SEQ_CST); +# endif +} + +template +inline T +js::jit::AtomicOperations::fetchXorSeqCst(T* addr, T val) +{ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_fetch_and_xor(addr, val); +# else + return __atomic_fetch_xor(addr, val, __ATOMIC_SEQ_CST); +# endif +} + +template +inline void +js::jit::RegionLock::acquire(void* addr) +{ +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + while (!__sync_bool_compare_and_swap(&spinlock, 0, 1)) + ; +# else + uint32_t zero = 0; + uint32_t one = 1; + while (!__atomic_compare_exchange(&spinlock, &zero, &one, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) + continue; +# endif +} + +template +inline void +js::jit::RegionLock::release(void* addr) +{ + MOZ_ASSERT(AtomicOperations::loadSeqCst(&spinlock) == 1, "releasing unlocked region lock"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + __sync_sub_and_fetch(&spinlock, 1); +# else + uint32_t zero = 0; + __atomic_store(&spinlock, &zero, __ATOMIC_SEQ_CST); +# endif +} + +# undef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + +#elif defined(ENABLE_SHARED_ARRAY_BUFFER) + +# error "Either disable JS shared memory, use GCC or Clang, or add code here" + +#endif + +#endif // jit_arm_AtomicOperations_arm_h diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index e7129fba43..fc4b7f5381 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -1950,6 +1950,27 @@ CodeGeneratorARM::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins) } } +void +CodeGeneratorARM::visitAsmJSCompareExchangeCallout(LAsmJSCompareExchangeCallout* ins) +{ + const MAsmJSCompareExchangeHeap* mir = ins->mir(); + Scalar::Type viewType = mir->accessType(); + Register ptr = ToRegister(ins->ptr()); + Register oldval = ToRegister(ins->oldval()); + Register newval = ToRegister(ins->newval()); + + MOZ_ASSERT(ToRegister(ins->output()) == ReturnReg); + + masm.setupAlignedABICall(4); + masm.ma_mov(Imm32(viewType), ScratchRegister); + masm.passABIArg(ScratchRegister); + masm.passABIArg(ptr); + masm.passABIArg(oldval); + masm.passABIArg(newval); + + masm.callWithABI(AsmJSImm_AtomicCmpXchg); +} + void CodeGeneratorARM::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins) { @@ -2030,6 +2051,41 @@ CodeGeneratorARM::visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEff } } +void +CodeGeneratorARM::visitAsmJSAtomicBinopCallout(LAsmJSAtomicBinopCallout* ins) +{ + const MAsmJSAtomicBinopHeap* mir = ins->mir(); + Scalar::Type viewType = mir->accessType(); + Register ptr = ToRegister(ins->ptr()); + Register value = ToRegister(ins->value()); + + masm.setupAlignedABICall(3); + masm.ma_mov(Imm32(viewType), ScratchRegister); + masm.passABIArg(ScratchRegister); + masm.passABIArg(ptr); + masm.passABIArg(value); + + switch (mir->operation()) { + case AtomicFetchAddOp: + masm.callWithABI(AsmJSImm_AtomicFetchAdd); + break; + case AtomicFetchSubOp: + masm.callWithABI(AsmJSImm_AtomicFetchSub); + break; + case AtomicFetchAndOp: + masm.callWithABI(AsmJSImm_AtomicFetchAnd); + break; + case AtomicFetchOrOp: + masm.callWithABI(AsmJSImm_AtomicFetchOr); + break; + case AtomicFetchXorOp: + masm.callWithABI(AsmJSImm_AtomicFetchXor); + break; + default: + MOZ_CRASH("Unknown op"); + } +} + void CodeGeneratorARM::visitAsmJSPassStackArg(LAsmJSPassStackArg* ins) { diff --git a/js/src/jit/arm/CodeGenerator-arm.h b/js/src/jit/arm/CodeGenerator-arm.h index 1c81f2a489..e3aea5e0c1 100644 --- a/js/src/jit/arm/CodeGenerator-arm.h +++ b/js/src/jit/arm/CodeGenerator-arm.h @@ -202,8 +202,10 @@ class CodeGeneratorARM : public CodeGeneratorShared void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins); void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins); void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins); + void visitAsmJSCompareExchangeCallout(LAsmJSCompareExchangeCallout* ins); void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins); void visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins); + void visitAsmJSAtomicBinopCallout(LAsmJSAtomicBinopCallout* ins); void visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins); void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins); void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins); diff --git a/js/src/jit/arm/LIR-arm.h b/js/src/jit/arm/LIR-arm.h index f2f83b1647..b47b29ba38 100644 --- a/js/src/jit/arm/LIR-arm.h +++ b/js/src/jit/arm/LIR-arm.h @@ -465,6 +465,53 @@ class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 1> } }; +class LAsmJSCompareExchangeCallout : public LInstructionHelper<1, 3, 0> +{ + public: + LIR_HEADER(AsmJSCompareExchangeCallout) + LAsmJSCompareExchangeCallout(const LAllocation& ptr, const LAllocation& oldval, + const LAllocation& newval) + { + setOperand(0, ptr); + setOperand(1, oldval); + setOperand(2, newval); + } + const LAllocation* ptr() { + return getOperand(0); + } + const LAllocation* oldval() { + return getOperand(1); + } + const LAllocation* newval() { + return getOperand(2); + } + + const MAsmJSCompareExchangeHeap* mir() const { + return mir_->toAsmJSCompareExchangeHeap(); + } +}; + +class LAsmJSAtomicBinopCallout : public LInstructionHelper<1, 2, 0> +{ + public: + LIR_HEADER(AsmJSAtomicBinopCallout) + LAsmJSAtomicBinopCallout(const LAllocation& ptr, const LAllocation& value) + { + setOperand(0, ptr); + setOperand(1, value); + } + const LAllocation* ptr() { + return getOperand(0); + } + const LAllocation* value() { + return getOperand(1); + } + + const MAsmJSAtomicBinopHeap* mir() const { + return mir_->toAsmJSAtomicBinopHeap(); + } +}; + } // namespace jit } // namespace js diff --git a/js/src/jit/arm/LOpcodes-arm.h b/js/src/jit/arm/LOpcodes-arm.h index d412640393..5a2e3c1333 100644 --- a/js/src/jit/arm/LOpcodes-arm.h +++ b/js/src/jit/arm/LOpcodes-arm.h @@ -25,6 +25,8 @@ _(UDiv) \ _(UMod) \ _(SoftUDivOrMod) \ - _(AsmJSLoadFuncPtr) + _(AsmJSLoadFuncPtr) \ + _(AsmJSCompareExchangeCallout) \ + _(AsmJSAtomicBinopCallout) #endif /* jit_arm_LOpcodes_arm_h */ diff --git a/js/src/jit/arm/Lowering-arm.cpp b/js/src/jit/arm/Lowering-arm.cpp index 18b8824cf7..8c799ed177 100644 --- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -655,6 +655,15 @@ LIRGeneratorARM::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins) MDefinition* ptr = ins->ptr(); MOZ_ASSERT(ptr->type() == MIRType_Int32); + if (byteSize(ins->accessType()) != 4 && !HasLDSTREXBHD()) { + LAsmJSCompareExchangeCallout* lir = + new(alloc()) LAsmJSCompareExchangeCallout(useRegister(ptr), + useRegister(ins->oldValue()), + useRegister(ins->newValue())); + defineFixed(lir, ins, LAllocation(AnyRegister(ReturnReg))); + return; + } + LAsmJSCompareExchangeHeap* lir = new(alloc()) LAsmJSCompareExchangeHeap(useRegister(ptr), useRegister(ins->oldValue()), @@ -671,6 +680,13 @@ LIRGeneratorARM::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins) MDefinition* ptr = ins->ptr(); MOZ_ASSERT(ptr->type() == MIRType_Int32); + if (byteSize(ins->accessType()) != 4 && !HasLDSTREXBHD()) { + LAsmJSAtomicBinopCallout* lir = + new(alloc()) LAsmJSAtomicBinopCallout(useRegister(ptr), useRegister(ins->value())); + defineFixed(lir, ins, LAllocation(AnyRegister(ReturnReg))); + return; + } + if (!ins->hasUses()) { LAsmJSAtomicBinopHeapForEffect* lir = new(alloc()) LAsmJSAtomicBinopHeapForEffect(useRegister(ptr), diff --git a/js/src/jit/arm64/CodeGenerator-arm64.cpp b/js/src/jit/arm64/CodeGenerator-arm64.cpp new file mode 100644 index 0000000000..fd037b9906 --- /dev/null +++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp @@ -0,0 +1,746 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/arm64/CodeGenerator-arm64.h" + +#include "mozilla/MathAlgorithms.h" + +#include "jscntxt.h" +#include "jscompartment.h" +#include "jsnum.h" + +#include "jit/CodeGenerator.h" +#include "jit/JitCompartment.h" +#include "jit/JitFrames.h" +#include "jit/MIR.h" +#include "jit/MIRGraph.h" +#include "vm/Shape.h" +#include "vm/TraceLogging.h" + +#include "jsscriptinlines.h" + +#include "jit/shared/CodeGenerator-shared-inl.h" + +using namespace js; +using namespace js::jit; + +using mozilla::FloorLog2; +using mozilla::NegativeInfinity; +using JS::GenericNaN; + +// shared +CodeGeneratorARM64::CodeGeneratorARM64(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm) + : CodeGeneratorShared(gen, graph, masm) +{ +} + +bool +CodeGeneratorARM64::generatePrologue() +{ + MOZ_CRASH("generatePrologue"); +} + +bool +CodeGeneratorARM64::generateEpilogue() +{ + MOZ_CRASH("generateEpilogue"); +} + +bool +CodeGeneratorARM64::generateOutOfLineCode() +{ + MOZ_CRASH("generateOutOfLineCode"); +} + +void +CodeGeneratorARM64::emitBranch(Assembler::Condition cond, MBasicBlock* mirTrue, MBasicBlock* mirFalse) +{ + MOZ_CRASH("emitBranch"); +} + +void +OutOfLineBailout::accept(CodeGeneratorARM64* codegen) +{ + MOZ_CRASH("accept"); +} + +void +CodeGeneratorARM64::visitTestIAndBranch(LTestIAndBranch* test) +{ + MOZ_CRASH("visitTestIAndBranch"); +} + +void +CodeGeneratorARM64::visitCompare(LCompare* comp) +{ + MOZ_CRASH("visitCompare"); +} + +void +CodeGeneratorARM64::visitCompareAndBranch(LCompareAndBranch* comp) +{ + MOZ_CRASH("visitCompareAndBranch"); +} + +void +CodeGeneratorARM64::bailoutIf(Assembler::Condition condition, LSnapshot* snapshot) +{ + MOZ_CRASH("bailoutIf"); +} + +void +CodeGeneratorARM64::bailoutFrom(Label* label, LSnapshot* snapshot) +{ + MOZ_CRASH("bailoutFrom"); +} + +void +CodeGeneratorARM64::bailout(LSnapshot* snapshot) +{ + MOZ_CRASH("bailout"); +} + +void +CodeGeneratorARM64::visitOutOfLineBailout(OutOfLineBailout* ool) +{ + MOZ_CRASH("visitOutOfLineBailout"); +} + +void +CodeGeneratorARM64::visitMinMaxD(LMinMaxD* ins) +{ + MOZ_CRASH("visitMinMaxD"); +} + +void +CodeGeneratorARM64::visitMinMaxF(LMinMaxF* ins) +{ + MOZ_CRASH("visitMinMaxF"); +} + +void +CodeGeneratorARM64::visitAbsD(LAbsD* ins) +{ + MOZ_CRASH("visitAbsD"); +} + +void +CodeGeneratorARM64::visitAbsF(LAbsF* ins) +{ + MOZ_CRASH("visitAbsF"); +} + +void +CodeGeneratorARM64::visitSqrtD(LSqrtD* ins) +{ + MOZ_CRASH("visitSqrtD"); +} + +void +CodeGeneratorARM64::visitSqrtF(LSqrtF* ins) +{ + MOZ_CRASH("visitSqrtF"); +} + +// FIXME: Uh, is this a static function? It looks like it is... +template +ARMRegister +toWRegister(const T* a) +{ + return ARMRegister(ToRegister(a), 32); +} + +// FIXME: Uh, is this a static function? It looks like it is... +template +ARMRegister +toXRegister(const T* a) +{ + return ARMRegister(ToRegister(a), 64); +} + +js::jit::Operand +toWOperand(const LAllocation* a) +{ + MOZ_CRASH("toWOperand"); +} + +vixl::CPURegister +ToCPURegister(const LAllocation* a, Scalar::Type type) +{ + MOZ_CRASH("ToCPURegister"); +} + +vixl::CPURegister +ToCPURegister(const LDefinition* d, Scalar::Type type) +{ + return ToCPURegister(d->output(), type); +} + +void +CodeGeneratorARM64::visitAddI(LAddI* ins) +{ + MOZ_CRASH("visitAddI"); +} + +void +CodeGeneratorARM64::visitSubI(LSubI* ins) +{ + MOZ_CRASH("visitSubI"); +} + +void +CodeGeneratorARM64::visitMulI(LMulI* ins) +{ + MOZ_CRASH("visitMulI"); +} + + +void +CodeGeneratorARM64::visitDivI(LDivI* ins) +{ + MOZ_CRASH("visitDivI"); +} + +void +CodeGeneratorARM64::visitDivPowTwoI(LDivPowTwoI* ins) +{ + MOZ_CRASH("CodeGeneratorARM64::visitDivPowTwoI"); +} + +void +CodeGeneratorARM64::modICommon(MMod* mir, Register lhs, Register rhs, Register output, + LSnapshot* snapshot, Label& done) +{ + MOZ_CRASH("CodeGeneratorARM64::modICommon"); +} + +void +CodeGeneratorARM64::visitModI(LModI* ins) +{ + MOZ_CRASH("visitModI"); +} + +void +CodeGeneratorARM64::visitModPowTwoI(LModPowTwoI* ins) +{ + MOZ_CRASH("visitModPowTwoI"); +} + +void +CodeGeneratorARM64::visitModMaskI(LModMaskI* ins) +{ + MOZ_CRASH("CodeGeneratorARM64::visitModMaskI"); +} + +void +CodeGeneratorARM64::visitBitNotI(LBitNotI* ins) +{ + MOZ_CRASH("visitBitNotI"); +} + +void +CodeGeneratorARM64::visitBitOpI(LBitOpI* ins) +{ + MOZ_CRASH("visitBitOpI"); +} + +void +CodeGeneratorARM64::visitShiftI(LShiftI* ins) +{ + MOZ_CRASH("visitShiftI"); +} + +void +CodeGeneratorARM64::visitUrshD(LUrshD* ins) +{ + MOZ_CRASH("visitUrshD"); +} + +void +CodeGeneratorARM64::visitPowHalfD(LPowHalfD* ins) +{ + MOZ_CRASH("visitPowHalfD"); +} + +MoveOperand +CodeGeneratorARM64::toMoveOperand(const LAllocation a) const +{ + MOZ_CRASH("toMoveOperand"); +} + +class js::jit::OutOfLineTableSwitch : public OutOfLineCodeBase +{ + MTableSwitch* mir_; + Vector codeLabels_; + + void accept(CodeGeneratorARM64* codegen) { + codegen->visitOutOfLineTableSwitch(this); + } + + public: + OutOfLineTableSwitch(TempAllocator& alloc, MTableSwitch* mir) + : mir_(mir), + codeLabels_(alloc) + { } + + MTableSwitch* mir() const { + return mir_; + } + + bool addCodeLabel(CodeLabel label) { + return codeLabels_.append(label); + } + CodeLabel codeLabel(unsigned i) { + return codeLabels_[i]; + } +}; + +void +CodeGeneratorARM64::visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool) +{ + MOZ_CRASH("visitOutOfLineTableSwitch"); +} + +void +CodeGeneratorARM64::emitTableSwitchDispatch(MTableSwitch* mir, Register index_, Register base_) +{ + MOZ_CRASH("emitTableSwitchDispatch"); +} + +void +CodeGeneratorARM64::visitMathD(LMathD* math) +{ + MOZ_CRASH("visitMathD"); +} + +void +CodeGeneratorARM64::visitMathF(LMathF* math) +{ + MOZ_CRASH("visitMathF"); +} + +void +CodeGeneratorARM64::visitFloor(LFloor* lir) +{ + MOZ_CRASH("visitFloor"); +} + +void +CodeGeneratorARM64::visitFloorF(LFloorF* lir) +{ + MOZ_CRASH("visitFloorF"); +} + +void +CodeGeneratorARM64::visitCeil(LCeil* lir) +{ + MOZ_CRASH("visitCeil"); +} + +void +CodeGeneratorARM64::visitCeilF(LCeilF* lir) +{ + MOZ_CRASH("visitCeilF"); +} + +void +CodeGeneratorARM64::visitRound(LRound* lir) +{ + MOZ_CRASH("visitRound"); +} + +void +CodeGeneratorARM64::visitRoundF(LRoundF* lir) +{ + MOZ_CRASH("visitRoundF"); +} + +void +CodeGeneratorARM64::visitClzI(LClzI* lir) +{ + MOZ_CRASH("visitClzI"); +} + +void +CodeGeneratorARM64::emitRoundDouble(FloatRegister src, Register dest, Label* fail) +{ + MOZ_CRASH("CodeGeneratorARM64::emitRoundDouble"); +} + +void +CodeGeneratorARM64::visitTruncateDToInt32(LTruncateDToInt32* ins) +{ + MOZ_CRASH("visitTruncateDToInt32"); +} + +void +CodeGeneratorARM64::visitTruncateFToInt32(LTruncateFToInt32* ins) +{ + MOZ_CRASH("visitTruncateFToInt32"); +} + +static const uint32_t FrameSizes[] = { 128, 256, 512, 1024 }; + +FrameSizeClass +FrameSizeClass::FromDepth(uint32_t frameDepth) +{ + return FrameSizeClass::None(); +} + +FrameSizeClass +FrameSizeClass::ClassLimit() +{ + return FrameSizeClass(0); +} + +uint32_t +FrameSizeClass::frameSize() const +{ + MOZ_CRASH("arm64 does not use frame size classes"); +} + +ValueOperand +CodeGeneratorARM64::ToValue(LInstruction* ins, size_t pos) +{ + return ValueOperand(ToRegister(ins->getOperand(pos))); +} + +ValueOperand +CodeGeneratorARM64::ToOutValue(LInstruction* ins) +{ + Register payloadReg = ToRegister(ins->getDef(0)); + return ValueOperand(payloadReg); +} + +ValueOperand +CodeGeneratorARM64::ToTempValue(LInstruction* ins, size_t pos) +{ + MOZ_CRASH("CodeGeneratorARM64::ToTempValue"); +} + +void +CodeGeneratorARM64::visitValue(LValue* value) +{ + MOZ_CRASH("visitValue"); +} + +void +CodeGeneratorARM64::visitBox(LBox* box) +{ + MOZ_CRASH("visitBox"); +} + +void +CodeGeneratorARM64::visitUnbox(LUnbox* unbox) +{ + MOZ_CRASH("visitUnbox"); +} + +void +CodeGeneratorARM64::visitDouble(LDouble* ins) +{ + MOZ_CRASH("visitDouble"); +} + +void +CodeGeneratorARM64::visitFloat32(LFloat32* ins) +{ + MOZ_CRASH("visitFloat32"); +} + +Register +CodeGeneratorARM64::splitTagForTest(const ValueOperand& value) +{ + MOZ_CRASH("splitTagForTest"); +} + +void +CodeGeneratorARM64::visitTestDAndBranch(LTestDAndBranch* test) +{ + MOZ_CRASH("visitTestDAndBranch"); +} + +void +CodeGeneratorARM64::visitTestFAndBranch(LTestFAndBranch* test) +{ + MOZ_CRASH("visitTestFAndBranch"); +} + +void +CodeGeneratorARM64::visitCompareD(LCompareD* comp) +{ + MOZ_CRASH("visitCompareD"); +} + +void +CodeGeneratorARM64::visitCompareF(LCompareF* comp) +{ + MOZ_CRASH("visitCompareF"); +} + +void +CodeGeneratorARM64::visitCompareDAndBranch(LCompareDAndBranch* comp) +{ + MOZ_CRASH("visitCompareDAndBranch"); +} + +void +CodeGeneratorARM64::visitCompareFAndBranch(LCompareFAndBranch* comp) +{ + MOZ_CRASH("visitCompareFAndBranch"); +} + +void +CodeGeneratorARM64::visitCompareB(LCompareB* lir) +{ + MOZ_CRASH("visitCompareB"); +} + +void +CodeGeneratorARM64::visitCompareBAndBranch(LCompareBAndBranch* lir) +{ + MOZ_CRASH("visitCompareBAndBranch"); +} + +void +CodeGeneratorARM64::visitCompareV(LCompareV* lir) +{ + MOZ_CRASH("visitCompareV"); +} + +void +CodeGeneratorARM64::visitCompareVAndBranch(LCompareVAndBranch* lir) +{ + MOZ_CRASH("visitCompareVAndBranch"); +} + +void +CodeGeneratorARM64::visitBitAndAndBranch(LBitAndAndBranch* baab) +{ + MOZ_CRASH("visitBitAndAndBranch"); +} + +void +CodeGeneratorARM64::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir) +{ + MOZ_CRASH("visitAsmJSUInt32ToDouble"); +} + +void +CodeGeneratorARM64::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir) +{ + MOZ_CRASH("visitAsmJSUInt32ToFloat32"); +} + +void +CodeGeneratorARM64::visitNotI(LNotI* ins) +{ + MOZ_CRASH("visitNotI"); +} + +// NZCV +// NAN -> 0011 +// == -> 0110 +// < -> 1000 +// > -> 0010 +void +CodeGeneratorARM64::visitNotD(LNotD* ins) +{ + MOZ_CRASH("visitNotD"); +} + +void +CodeGeneratorARM64::visitNotF(LNotF* ins) +{ + MOZ_CRASH("visitNotF"); +} + +void +CodeGeneratorARM64::visitLoadSlotV(LLoadSlotV* load) +{ + MOZ_CRASH("CodeGeneratorARM64::visitLoadSlotV"); +} + +void +CodeGeneratorARM64::visitLoadSlotT(LLoadSlotT* load) +{ + MOZ_CRASH("CodeGeneratorARM64::visitLoadSlotT"); +} + +void +CodeGeneratorARM64::visitStoreSlotT(LStoreSlotT* store) +{ + MOZ_CRASH("CodeGeneratorARM64::visitStoreSlotT"); +} + +void +CodeGeneratorARM64::visitLoadElementT(LLoadElementT* load) +{ + MOZ_CRASH("CodeGeneratorARM64::visitLoadElementT"); +} + +void +CodeGeneratorARM64::storeElementTyped(const LAllocation* value, MIRType valueType, + MIRType elementType, Register elements, + const LAllocation* index) +{ + MOZ_CRASH("CodeGeneratorARM64::storeElementTyped"); +} + +void +CodeGeneratorARM64::visitGuardShape(LGuardShape* guard) +{ + MOZ_CRASH("visitGuardShape"); +} + +void +CodeGeneratorARM64::visitGuardObjectGroup(LGuardObjectGroup* guard) +{ + MOZ_CRASH("visitGuardObjectGroup"); +} + +void +CodeGeneratorARM64::visitGuardClass(LGuardClass* guard) +{ + MOZ_CRASH("CodeGeneratorARM64::visitGuardClass"); +} + +void +CodeGeneratorARM64::visitInterruptCheck(LInterruptCheck* lir) +{ + MOZ_CRASH("CodeGeneratorARM64::visitInterruptCheck"); +} + +void +CodeGeneratorARM64::generateInvalidateEpilogue() +{ + MOZ_CRASH("generateInvalidateEpilogue"); +} + +void +CodeGeneratorARM64::visitRandom(LRandom* ins) +{ + MOZ_CRASH("visitRandom"); +} + +template +Register +getBase(U* mir) +{ + switch (mir->base()) { + case U::Heap: return HeapReg; + case U::Global: return GlobalReg; + } + return InvalidReg; +} + +void +CodeGeneratorARM64::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins) +{ + MOZ_CRASH("CodeGeneratorARM64::visitLoadTypedArrayElementStatic"); +} + +void +CodeGeneratorARM64::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins) +{ + MOZ_CRASH("CodeGeneratorARM64::visitStoreTypedArrayElementStatic"); +} + +void +CodeGeneratorARM64::visitAsmJSCall(LAsmJSCall* ins) +{ + MOZ_CRASH("visitAsmJSCall"); +} + +void +CodeGeneratorARM64::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins) +{ + MOZ_CRASH("visitAsmJSLoadHeap"); +} + +void +CodeGeneratorARM64::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) +{ + MOZ_CRASH("visitAsmJSStoreHeap"); +} + +void +CodeGeneratorARM64::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins) +{ + MOZ_CRASH("visitAsmJSCompareExchangeHeap"); +} + +void +CodeGeneratorARM64::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins) +{ + MOZ_CRASH("visitAsmJSAtomicBinopHeap"); +} + +void +CodeGeneratorARM64::visitAsmJSPassStackArg(LAsmJSPassStackArg* ins) +{ + MOZ_CRASH("visitAsmJSPassStackArg"); +} + +void +CodeGeneratorARM64::visitUDiv(LUDiv* ins) +{ + MOZ_CRASH("visitUDiv"); +} + +void +CodeGeneratorARM64::visitUMod(LUMod* ins) +{ + MOZ_CRASH("visitUMod"); +} + +void +CodeGeneratorARM64::visitEffectiveAddress(LEffectiveAddress* ins) +{ + MOZ_CRASH("visitEffectiveAddress"); +} + +void +CodeGeneratorARM64::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins) +{ + MOZ_CRASH("visitAsmJSLoadGlobalVar"); +} + +void +CodeGeneratorARM64::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins) +{ + MOZ_CRASH("visitAsmJSStoreGlobalVar"); +} + +void +CodeGeneratorARM64::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins) +{ + MOZ_CRASH("visitAsmJSLoadFuncPtr"); +} + +void +CodeGeneratorARM64::visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins) +{ + MOZ_CRASH("visitAsmJSLoadFFIFunc"); +} + +void +CodeGeneratorARM64::visitNegI(LNegI* ins) +{ + MOZ_CRASH("visitNegI"); +} + +void +CodeGeneratorARM64::visitNegD(LNegD* ins) +{ + MOZ_CRASH("visitNegD"); +} + +void +CodeGeneratorARM64::visitNegF(LNegF* ins) +{ + MOZ_CRASH("visitNegF"); +} diff --git a/js/src/jit/arm64/CodeGenerator-arm64.h b/js/src/jit/arm64/CodeGenerator-arm64.h new file mode 100644 index 0000000000..94c9bce87a --- /dev/null +++ b/js/src/jit/arm64/CodeGenerator-arm64.h @@ -0,0 +1,276 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_arm64_CodeGenerator_arm64_h +#define jit_arm64_CodeGenerator_arm64_h + +#include "jit/arm64/Assembler-arm64.h" +#include "jit/shared/CodeGenerator-shared.h" + +namespace js { +namespace jit { + +class OutOfLineBailout; +class OutOfLineTableSwitch; + +class CodeGeneratorARM64 : public CodeGeneratorShared +{ + friend class MoveResolverARM64; + + CodeGeneratorARM64* thisFromCtor() { return this; } + + public: + CodeGeneratorARM64(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm); + + protected: + // Label for the common return path. + NonAssertingLabel returnLabel_; + NonAssertingLabel deoptLabel_; + + // FIXME: VIXL Operand does not match the platform-agnostic Operand, + // which is just a union of possible arguments. + inline Operand ToOperand(const LAllocation& a) { + MOZ_CRASH("ToOperand"); + } + inline Operand ToOperand(const LAllocation* a) { + return ToOperand(*a); + } + inline Operand ToOperand(const LDefinition* def) { + return ToOperand(def->output()); + } + + MoveOperand toMoveOperand(const LAllocation a) const; + + void bailoutIf(Assembler::Condition condition, LSnapshot* snapshot); + void bailoutFrom(Label* label, LSnapshot* snapshot); + void bailout(LSnapshot* snapshot); + + template + void bailoutCmpPtr(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot* snapshot) { + masm.cmpPtr(lhs, rhs); + return bailoutIf(c, snapshot); + } + void bailoutTestPtr(Assembler::Condition c, Register lhs, Register rhs, LSnapshot* snapshot) { + masm.testPtr(lhs, rhs); + return bailoutIf(c, snapshot); + } + template + void bailoutCmp32(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot* snapshot) { + masm.cmp32(lhs, rhs); + return bailoutIf(c, snapshot); + } + template + void bailoutTest32(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot* snapshot) { + masm.test32(lhs, rhs); + return bailoutIf(c, snapshot); + } + void bailoutIfFalseBool(Register reg, LSnapshot* snapshot) { + masm.test32(reg, Imm32(0xFF)); + return bailoutIf(Assembler::Zero, snapshot); + } + + protected: + bool generatePrologue(); + bool generateEpilogue(); + bool generateOutOfLineCode(); + + void emitRoundDouble(FloatRegister src, Register dest, Label* fail); + + // Emits a branch that directs control flow to the true block if |cond| is + // true, and the false block if |cond| is false. + void emitBranch(Assembler::Condition cond, MBasicBlock* ifTrue, MBasicBlock* ifFalse); + + void testNullEmitBranch(Assembler::Condition cond, const ValueOperand& value, + MBasicBlock* ifTrue, MBasicBlock* ifFalse) + { + cond = masm.testNull(cond, value); + emitBranch(cond, ifTrue, ifFalse); + } + void testUndefinedEmitBranch(Assembler::Condition cond, const ValueOperand& value, + MBasicBlock* ifTrue, MBasicBlock* ifFalse) + { + cond = masm.testUndefined(cond, value); + emitBranch(cond, ifTrue, ifFalse); + } + void testObjectEmitBranch(Assembler::Condition cond, const ValueOperand& value, + MBasicBlock* ifTrue, MBasicBlock* ifFalse) + { + cond = masm.testObject(cond, value); + emitBranch(cond, ifTrue, ifFalse); + } + void testZeroEmitBranch(Assembler::Condition cond, Register reg, + MBasicBlock* ifTrue, MBasicBlock* ifFalse) + { + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + masm.cmpPtr(reg, ImmWord(0)); + emitBranch(cond, ifTrue, ifFalse); + } + + void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base); + + public: + // Instruction visitors. + virtual void visitMinMaxD(LMinMaxD* ins); + virtual void visitMinMaxF(LMinMaxF* math); + virtual void visitAbsD(LAbsD* ins); + virtual void visitAbsF(LAbsF* ins); + virtual void visitSqrtD(LSqrtD* ins); + virtual void visitSqrtF(LSqrtF* ins); + virtual void visitAddI(LAddI* ins); + virtual void visitSubI(LSubI* ins); + virtual void visitBitNotI(LBitNotI* ins); + virtual void visitBitOpI(LBitOpI* ins); + + virtual void visitMulI(LMulI* ins); + + virtual void visitDivI(LDivI* ins); + virtual void visitDivPowTwoI(LDivPowTwoI* ins); + virtual void visitModI(LModI* ins); + virtual void visitModPowTwoI(LModPowTwoI* ins); + virtual void visitModMaskI(LModMaskI* ins); + virtual void visitPowHalfD(LPowHalfD* ins); + virtual void visitShiftI(LShiftI* ins); + virtual void visitUrshD(LUrshD* ins); + + virtual void visitTestIAndBranch(LTestIAndBranch* test); + virtual void visitCompare(LCompare* comp); + virtual void visitCompareAndBranch(LCompareAndBranch* comp); + virtual void visitTestDAndBranch(LTestDAndBranch* test); + virtual void visitTestFAndBranch(LTestFAndBranch* test); + virtual void visitCompareD(LCompareD* comp); + virtual void visitCompareF(LCompareF* comp); + virtual void visitCompareDAndBranch(LCompareDAndBranch* comp); + virtual void visitCompareFAndBranch(LCompareFAndBranch* comp); + virtual void visitCompareB(LCompareB* lir); + virtual void visitCompareBAndBranch(LCompareBAndBranch* lir); + virtual void visitCompareV(LCompareV* lir); + virtual void visitCompareVAndBranch(LCompareVAndBranch* lir); + virtual void visitBitAndAndBranch(LBitAndAndBranch* baab); + virtual void visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir); + virtual void visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir); + virtual void visitNotI(LNotI* ins); + virtual void visitNotD(LNotD* ins); + virtual void visitNotF(LNotF* ins); + + virtual void visitMathD(LMathD* math); + virtual void visitMathF(LMathF* math); + virtual void visitFloor(LFloor* lir); + virtual void visitFloorF(LFloorF* lir); + virtual void visitCeil(LCeil* lir); + virtual void visitCeilF(LCeilF* lir); + virtual void visitRound(LRound* lir); + virtual void visitRoundF(LRoundF* lir); + virtual void visitTruncateDToInt32(LTruncateDToInt32* ins); + virtual void visitTruncateFToInt32(LTruncateFToInt32* ins); + + virtual void visitClzI(LClzI* lir); + // Out of line visitors. + void visitOutOfLineBailout(OutOfLineBailout* ool); + void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool); + + protected: + ValueOperand ToValue(LInstruction* ins, size_t pos); + ValueOperand ToOutValue(LInstruction* ins); + ValueOperand ToTempValue(LInstruction* ins, size_t pos); + + // Functions for LTestVAndBranch. + Register splitTagForTest(const ValueOperand& value); + + void storeElementTyped(const LAllocation* value, MIRType valueType, MIRType elementType, + Register elements, const LAllocation* index); + + void divICommon(MDiv* mir, Register lhs, Register rhs, Register output, LSnapshot* snapshot, + Label& done); + void modICommon(MMod* mir, Register lhs, Register rhs, Register output, LSnapshot* snapshot, + Label& done); + + public: + void visitBox(LBox* box); + void visitUnbox(LUnbox* unbox); + void visitValue(LValue* value); + void visitDouble(LDouble* ins); + void visitFloat32(LFloat32* ins); + + void visitLoadSlotV(LLoadSlotV* load); + void visitLoadSlotT(LLoadSlotT* load); + void visitStoreSlotT(LStoreSlotT* load); + + void visitLoadElementT(LLoadElementT* load); + + void visitGuardShape(LGuardShape* guard); + void visitGuardObjectGroup(LGuardObjectGroup* guard); + void visitGuardClass(LGuardClass* guard); + + void visitInterruptCheck(LInterruptCheck* lir); + + void visitNegI(LNegI* lir); + void visitNegD(LNegD* lir); + void visitNegF(LNegF* lir); + void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins); + void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins); + void visitAsmJSCall(LAsmJSCall* ins); + void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins); + void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins); + void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins); + void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins); + void visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins); + void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins); + void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins); + void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins); + void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins); + + void generateInvalidateEpilogue(); + + void visitRandom(LRandom* ins); + + protected: + void postAsmJSCall(LAsmJSCall* lir) { + MOZ_CRASH("postAsmJSCall"); + } + + void visitEffectiveAddress(LEffectiveAddress* ins); + void visitUDiv(LUDiv* ins); + void visitUMod(LUMod* ins); + + public: + // Unimplemented SIMD instructions. + void visitSimdSplatX4(LSimdSplatX4* lir) { MOZ_CRASH("NYI"); } + void visitInt32x4(LInt32x4* ins) { MOZ_CRASH("NYI"); } + void visitFloat32x4(LFloat32x4* ins) { MOZ_CRASH("NYI"); } + void visitSimdExtractElementI(LSimdExtractElementI* ins) { MOZ_CRASH("NYI"); } + void visitSimdExtractElementF(LSimdExtractElementF* ins) { MOZ_CRASH("NYI"); } + void visitSimdSignMaskX4(LSimdSignMaskX4* ins) { MOZ_CRASH("NYI"); } + void visitSimdBinaryCompIx4(LSimdBinaryCompIx4* lir) { MOZ_CRASH("NYI"); } + void visitSimdBinaryCompFx4(LSimdBinaryCompFx4* lir) { MOZ_CRASH("NYI"); } + void visitSimdBinaryArithIx4(LSimdBinaryArithIx4* lir) { MOZ_CRASH("NYI"); } + void visitSimdBinaryArithFx4(LSimdBinaryArithFx4* lir) { MOZ_CRASH("NYI"); } + void visitSimdBinaryBitwiseX4(LSimdBinaryBitwiseX4* lir) { MOZ_CRASH("NYI"); } +}; + +typedef CodeGeneratorARM64 CodeGeneratorSpecific; + +// An out-of-line bailout thunk. +class OutOfLineBailout : public OutOfLineCodeBase +{ + protected: // Silence Clang warning. + LSnapshot* snapshot_; + + public: + OutOfLineBailout(LSnapshot* snapshot) + : snapshot_(snapshot) + { } + + void accept(CodeGeneratorARM64* codegen); + + LSnapshot* snapshot() const { + return snapshot_; + } +}; + +} // namespace jit +} // namespace js + +#endif /* jit_arm64_CodeGenerator_arm64_h */ diff --git a/js/src/jit/arm64/LIR-arm64.h b/js/src/jit/arm64/LIR-arm64.h new file mode 100644 index 0000000000..762520908e --- /dev/null +++ b/js/src/jit/arm64/LIR-arm64.h @@ -0,0 +1,467 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_arm64_LIR_arm64_h +#define jit_arm64_LIR_arm64_h + +namespace js { +namespace jit { + +class LBox : public LInstructionHelper<1, 1, 0> +{ + MIRType type_; + + public: + LIR_HEADER(Box); + + LBox(const LAllocation& in_payload, MIRType type) + : type_(type) + { + setOperand(0, in_payload); + } + + MIRType type() const { + return type_; + } + const char* extraName() const { + return StringFromMIRType(type_); + } +}; + +class LUnboxBase : public LInstructionHelper<1, 1, 0> +{ + public: + LUnboxBase(const LAllocation& input) { + setOperand(0, input); + } + + static const size_t Input = 0; + + MUnbox* mir() const { + return mir_->toUnbox(); + } +}; + +class LUnbox : public LUnboxBase +{ + public: + LIR_HEADER(Unbox); + + LUnbox(const LAllocation& input) + : LUnboxBase(input) + { } + + const char* extraName() const { + return StringFromMIRType(mir()->type()); + } +}; + +class LUnboxFloatingPoint : public LUnboxBase +{ + MIRType type_; + + public: + LIR_HEADER(UnboxFloatingPoint); + + LUnboxFloatingPoint(const LAllocation& input, MIRType type) + : LUnboxBase(input), + type_(type) + { } + + MIRType type() const { + return type_; + } + const char* extraName() const { + return StringFromMIRType(type_); + } +}; + +// Convert a 32-bit unsigned integer to a double. +class LAsmJSUInt32ToDouble : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(AsmJSUInt32ToDouble) + + LAsmJSUInt32ToDouble(const LAllocation& input) { + setOperand(0, input); + } +}; + +// Convert a 32-bit unsigned integer to a float32. +class LAsmJSUInt32ToFloat32 : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(AsmJSUInt32ToFloat32) + + LAsmJSUInt32ToFloat32(const LAllocation& input) { + setOperand(0, input); + } +}; + +class LDivI : public LBinaryMath<1> +{ + public: + LIR_HEADER(DivI); + + LDivI(const LAllocation& lhs, const LAllocation& rhs, + const LDefinition& temp) + { + setOperand(0, lhs); + setOperand(1, rhs); + setTemp(0, temp); + } + + MDiv* mir() const { + return mir_->toDiv(); + } +}; + +// LSoftDivI is a software divide for ARM cores that don't support a hardware +// divide instruction. +// +// It is implemented as a proper C function so it trashes r0, r1, r2 and r3. +// The call also trashes lr, and has the ability to trash ip. The function also +// takes two arguments (dividend in r0, divisor in r1). The LInstruction gets +// encoded such that the divisor and dividend are passed in their apropriate +// registers and end their life at the start of the instruction by the use of +// useFixedAtStart. The result is returned in r0 and the other three registers +// that can be trashed are marked as temps. For the time being, the link +// register is not marked as trashed because we never allocate to the link +// register. The FP registers are not trashed. +class LSoftDivI : public LBinaryMath<3> +{ + public: + LIR_HEADER(SoftDivI); + + LSoftDivI(const LAllocation& lhs, const LAllocation& rhs, + const LDefinition& temp1, const LDefinition& temp2, const LDefinition& temp3) { + setOperand(0, lhs); + setOperand(1, rhs); + setTemp(0, temp1); + setTemp(1, temp2); + setTemp(2, temp3); + } + + MDiv* mir() const { + return mir_->toDiv(); + } +}; + +class LDivPowTwoI : public LInstructionHelper<1, 1, 0> +{ + const int32_t shift_; + + public: + LIR_HEADER(DivPowTwoI) + + LDivPowTwoI(const LAllocation& lhs, int32_t shift) + : shift_(shift) + { + setOperand(0, lhs); + } + + const LAllocation* numerator() { + return getOperand(0); + } + + int32_t shift() { + return shift_; + } + + MDiv* mir() const { + return mir_->toDiv(); + } +}; + +class LModI : public LBinaryMath<1> +{ + public: + LIR_HEADER(ModI); + + LModI(const LAllocation& lhs, const LAllocation& rhs, + const LDefinition& callTemp) + { + setOperand(0, lhs); + setOperand(1, rhs); + setTemp(0, callTemp); + } + + const LDefinition* callTemp() { + return getTemp(0); + } + + MMod* mir() const { + return mir_->toMod(); + } +}; + +class LSoftModI : public LBinaryMath<4> +{ + public: + LIR_HEADER(SoftModI); + + LSoftModI(const LAllocation& lhs, const LAllocation& rhs, + const LDefinition& temp1, const LDefinition& temp2, const LDefinition& temp3, + const LDefinition& callTemp) + { + setOperand(0, lhs); + setOperand(1, rhs); + setTemp(0, temp1); + setTemp(1, temp2); + setTemp(2, temp3); + setTemp(3, callTemp); + } + + const LDefinition* callTemp() { + return getTemp(3); + } + + MMod* mir() const { + return mir_->toMod(); + } +}; + +class LModPowTwoI : public LInstructionHelper<1, 1, 0> +{ + const int32_t shift_; + + public: + LIR_HEADER(ModPowTwoI); + int32_t shift() + { + return shift_; + } + + LModPowTwoI(const LAllocation& lhs, int32_t shift) + : shift_(shift) + { + setOperand(0, lhs); + } + + MMod* mir() const { + return mir_->toMod(); + } +}; + +class LModMaskI : public LInstructionHelper<1, 1, 1> +{ + const int32_t shift_; + + public: + LIR_HEADER(ModMaskI); + + LModMaskI(const LAllocation& lhs, const LDefinition& temp1, int32_t shift) + : shift_(shift) + { + setOperand(0, lhs); + setTemp(0, temp1); + } + + int32_t shift() const { + return shift_; + } + + MMod* mir() const { + return mir_->toMod(); + } +}; + +class LPowHalfD : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(PowHalfD); + LPowHalfD(const LAllocation& input) { + setOperand(0, input); + } + + const LAllocation* input() { + return getOperand(0); + } + const LDefinition* output() { + return getDef(0); + } +}; + +// Takes a tableswitch with an integer to decide +class LTableSwitch : public LInstructionHelper<0, 1, 1> +{ + public: + LIR_HEADER(TableSwitch); + + LTableSwitch(const LAllocation& in, const LDefinition& inputCopy, MTableSwitch* ins) { + setOperand(0, in); + setTemp(0, inputCopy); + setMir(ins); + } + + MTableSwitch* mir() const { + return mir_->toTableSwitch(); + } + + const LAllocation* index() { + return getOperand(0); + } + const LDefinition* tempInt() { + return getTemp(0); + } + // This is added to share the same CodeGenerator prefixes. + const LDefinition* tempPointer() { + return nullptr; + } +}; + +// Takes a tableswitch with an integer to decide +class LTableSwitchV : public LInstructionHelper<0, BOX_PIECES, 2> +{ + public: + LIR_HEADER(TableSwitchV); + + LTableSwitchV(const LDefinition& inputCopy, const LDefinition& floatCopy, + MTableSwitch* ins) + { + setTemp(0, inputCopy); + setTemp(1, floatCopy); + setMir(ins); + } + + MTableSwitch* mir() const { + return mir_->toTableSwitch(); + } + + static const size_t InputValue = 0; + + const LDefinition* tempInt() { + return getTemp(0); + } + const LDefinition* tempFloat() { + return getTemp(1); + } + const LDefinition* tempPointer() { + return nullptr; + } +}; + +class LGuardShape : public LInstructionHelper<0, 1, 1> +{ + public: + LIR_HEADER(GuardShape); + + LGuardShape(const LAllocation& in, const LDefinition& temp) { + setOperand(0, in); + setTemp(0, temp); + } + const MGuardShape* mir() const { + return mir_->toGuardShape(); + } + const LDefinition* tempInt() { + return getTemp(0); + } +}; + +class LGuardObjectGroup : public LInstructionHelper<0, 1, 1> +{ + public: + LIR_HEADER(GuardObjectGroup); + + LGuardObjectGroup(const LAllocation& in, const LDefinition& temp) { + setOperand(0, in); + setTemp(0, temp); + } + const MGuardObjectGroup* mir() const { + return mir_->toGuardObjectGroup(); + } + const LDefinition* tempInt() { + return getTemp(0); + } +}; + +class LMulI : public LBinaryMath<0> +{ + public: + LIR_HEADER(MulI); + + MMul* mir() { + return mir_->toMul(); + } +}; + +class LUDiv : public LBinaryMath<0> +{ + public: + LIR_HEADER(UDiv); + + MDiv* mir() { + return mir_->toDiv(); + } +}; + +class LUMod : public LBinaryMath<0> +{ + public: + LIR_HEADER(UMod); + + MMod* mir() { + return mir_->toMod(); + } +}; + +// This class performs a simple x86 'div', yielding either a quotient or remainder depending on +// whether this instruction is defined to output eax (quotient) or edx (remainder). +class LSoftUDivOrMod : public LBinaryMath<3> +{ + public: + LIR_HEADER(SoftUDivOrMod); + + LSoftUDivOrMod(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp1, + const LDefinition& temp2, const LDefinition& temp3) { + setOperand(0, lhs); + setOperand(1, rhs); + setTemp(0, temp1); + setTemp(1, temp2); + setTemp(2, temp3); + } +}; + +class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 1> +{ + public: + LIR_HEADER(AsmJSLoadFuncPtr); + LAsmJSLoadFuncPtr(const LAllocation& index, const LDefinition& temp) { + setOperand(0, index); + setTemp(0, temp); + } + const MAsmJSLoadFuncPtr* mir() const { + return mir_->toAsmJSLoadFuncPtr(); + } + const LAllocation* index() { + return getOperand(0); + } + const LDefinition* temp() { + return getTemp(0); + } +}; + +// Math.random(). +class LRandom : public LCallInstructionHelper<1, 0, 2> +{ + public: + LIR_HEADER(Random) + LRandom(const LDefinition& temp, const LDefinition& temp2) { + setTemp(0, temp); + setTemp(1, temp2); + } + const LDefinition* temp() { + return getTemp(0); + } + const LDefinition* temp2() { + return getTemp(1); + } +}; + +} // namespace jit +} // namespace js + +#endif /* jit_arm64_LIR_arm64_h */ diff --git a/js/src/jit/arm64/LOpcodes-arm64.h b/js/src/jit/arm64/LOpcodes-arm64.h new file mode 100644 index 0000000000..d83225afc1 --- /dev/null +++ b/js/src/jit/arm64/LOpcodes-arm64.h @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_arm64_LOpcodes_arm64_h +#define jit_arm64_LOpcodes_arm64_h + +#define LIR_CPU_OPCODE_LIST(_) \ + _(Unbox) \ + _(UnboxFloatingPoint) \ + _(Box) \ + _(DivI) \ + _(SoftDivI) \ + _(DivPowTwoI) \ + _(ModI) \ + _(SoftModI) \ + _(ModPowTwoI) \ + _(ModMaskI) \ + _(PowHalfD) \ + _(AsmJSUInt32ToDouble) \ + _(AsmJSUInt32ToFloat32) \ + _(UDiv) \ + _(UMod) \ + _(SoftUDivOrMod) \ + _(AsmJSLoadFuncPtr) + +#endif /* jit_arm64_LOpcodes_arm64_h */ diff --git a/js/src/jit/arm64/Lowering-arm64.cpp b/js/src/jit/arm64/Lowering-arm64.cpp new file mode 100644 index 0000000000..41524f049d --- /dev/null +++ b/js/src/jit/arm64/Lowering-arm64.cpp @@ -0,0 +1,321 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/MathAlgorithms.h" + +#include "jit/arm64/Assembler-arm64.h" +#include "jit/Lowering.h" +#include "jit/MIR.h" + +#include "jit/shared/Lowering-shared-inl.h" + +using namespace js; +using namespace js::jit; + +using mozilla::FloorLog2; + +void +LIRGeneratorARM64::useBox(LInstruction* lir, size_t n, MDefinition* mir, + LUse::Policy policy, bool useAtStart) +{ + MOZ_CRASH("useBox"); +} + +void +LIRGeneratorARM64::useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register) +{ + MOZ_CRASH("useBoxFixed"); +} + +LAllocation +LIRGeneratorARM64::useByteOpRegister(MDefinition* mir) +{ + MOZ_CRASH("useByteOpRegister"); +} + +LAllocation +LIRGeneratorARM64::useByteOpRegisterOrNonDoubleConstant(MDefinition* mir) +{ + MOZ_CRASH("useByteOpRegisterOrNonDoubleConstant"); +} + +void +LIRGeneratorARM64::lowerConstantDouble(double d, MInstruction* mir) +{ + MOZ_CRASH("lowerConstantDouble"); +} + +void +LIRGeneratorARM64::lowerConstantFloat32(float d, MInstruction* mir) +{ + MOZ_CRASH("lowerConstantFloat32"); +} + +void +LIRGeneratorARM64::visitConstant(MConstant* ins) +{ + MOZ_CRASH("visitConstant"); +} + +void +LIRGeneratorARM64::visitBox(MBox* box) +{ + MOZ_CRASH("visitBox"); +} + +void +LIRGeneratorARM64::visitUnbox(MUnbox* unbox) +{ + MOZ_CRASH("visitUnbox"); +} + +void +LIRGeneratorARM64::visitReturn(MReturn* ret) +{ + MOZ_CRASH("visitReturn"); +} + +// x = !y +void +LIRGeneratorARM64::lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input) +{ + MOZ_CRASH("lowerForALU"); +} + +// z = x+y +void +LIRGeneratorARM64::lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, + MDefinition* lhs, MDefinition* rhs) +{ + MOZ_CRASH("lowerForALU"); +} + +void +LIRGeneratorARM64::lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input) +{ + MOZ_CRASH("lowerForFPU"); +} + +template +void +LIRGeneratorARM64::lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir, + MDefinition* lhs, MDefinition* rhs) +{ + MOZ_CRASH("lowerForFPU"); +} + +template void LIRGeneratorARM64::lowerForFPU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, + MDefinition* lhs, MDefinition* rhs); +template void LIRGeneratorARM64::lowerForFPU(LInstructionHelper<1, 2, 1>* ins, MDefinition* mir, + MDefinition* lhs, MDefinition* rhs); + +void +LIRGeneratorARM64::lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir, + MDefinition* lhs, MDefinition* rhs) +{ + MOZ_CRASH("lowerForBitAndAndBranch"); +} + +void +LIRGeneratorARM64::defineUntypedPhi(MPhi* phi, size_t lirIndex) +{ + MOZ_CRASH("defineUntypedPhi"); +} + +void +LIRGeneratorARM64::lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition, + LBlock* block, size_t lirIndex) +{ + MOZ_CRASH("lowerUntypedPhiInput"); +} + +void +LIRGeneratorARM64::lowerForShift(LInstructionHelper<1, 2, 0>* ins, + MDefinition* mir, MDefinition* lhs, MDefinition* rhs) +{ + MOZ_CRASH("lowerForShift"); +} + +void +LIRGeneratorARM64::lowerDivI(MDiv* div) +{ + MOZ_CRASH("lowerDivI"); +} + +void +LIRGeneratorARM64::lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs) +{ + MOZ_CRASH("lowerMulI"); +} + +void +LIRGeneratorARM64::lowerModI(MMod* mod) +{ + MOZ_CRASH("lowerModI"); +} + +void +LIRGeneratorARM64::visitPowHalf(MPowHalf* ins) +{ + MOZ_CRASH("visitPowHalf"); +} + +LTableSwitch* +LIRGeneratorARM64::newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy, + MTableSwitch* tableswitch) +{ + MOZ_CRASH("newLTableSwitch"); +} + +LTableSwitchV* +LIRGeneratorARM64::newLTableSwitchV(MTableSwitch* tableswitch) +{ + MOZ_CRASH("newLTableSwitchV"); +} + +void +LIRGeneratorARM64::visitGuardShape(MGuardShape* ins) +{ + MOZ_CRASH("visitGuardShape"); +} + +void +LIRGeneratorARM64::visitGuardObjectGroup(MGuardObjectGroup* ins) +{ + MOZ_CRASH("visitGuardObjectGroup"); +} + +void +LIRGeneratorARM64::lowerUrshD(MUrsh* mir) +{ + MOZ_CRASH("lowerUrshD"); +} + +void +LIRGeneratorARM64::visitAsmJSNeg(MAsmJSNeg* ins) +{ + MOZ_CRASH("visitAsmJSNeg"); +} + +void +LIRGeneratorARM64::lowerUDiv(MDiv* div) +{ + MOZ_CRASH("lowerUDiv"); +} + +void +LIRGeneratorARM64::lowerUMod(MMod* mod) +{ + MOZ_CRASH("lowerUMod"); +} + +void +LIRGeneratorARM64::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble* ins) +{ + MOZ_CRASH("visitAsmJSUnsignedToDouble"); +} + +void +LIRGeneratorARM64::visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins) +{ + MOZ_CRASH("visitAsmJSUnsignedToFloat32"); +} + +void +LIRGeneratorARM64::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins) +{ + MOZ_CRASH("visitAsmJSLoadHeap"); +} + +void +LIRGeneratorARM64::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins) +{ + MOZ_CRASH("visitAsmJSStoreHeap"); +} + +void +LIRGeneratorARM64::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins) +{ + MOZ_CRASH("visitAsmJSLoadFuncPtr"); +} + +void +LIRGeneratorARM64::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins) +{ + MOZ_CRASH("visitAsmJSCompareExchangeHeap"); +} + +void +LIRGeneratorARM64::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins) +{ + MOZ_CRASH("visitAsmJSAtomicBinopHeap"); +} + +void +LIRGeneratorARM64::lowerTruncateDToInt32(MTruncateToInt32* ins) +{ + MOZ_CRASH("lowerTruncateDToInt32"); +} + +void +LIRGeneratorARM64::lowerTruncateFToInt32(MTruncateToInt32* ins) +{ + MOZ_CRASH("lowerTruncateFToInt32"); +} + +void +LIRGeneratorARM64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins) +{ + MOZ_CRASH("NYI"); +} + +void +LIRGeneratorARM64::visitSimdBinaryArith(MSimdBinaryArith* ins) +{ + MOZ_CRASH("NYI"); +} + +void +LIRGeneratorARM64::visitSimdSelect(MSimdSelect* ins) +{ + MOZ_CRASH("NYI"); +} + +void +LIRGeneratorARM64::visitSimdSplatX4(MSimdSplatX4* ins) +{ + MOZ_CRASH("NYI"); +} + +void +LIRGeneratorARM64::visitSimdValueX4(MSimdValueX4* ins) +{ + MOZ_CRASH("NYI"); +} + +void +LIRGeneratorARM64::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins) +{ + MOZ_CRASH("NYI"); +} + +void +LIRGeneratorARM64::visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins) +{ + MOZ_CRASH("NYI"); +} + +void +LIRGeneratorARM64::visitSubstr(MSubstr* ins) +{ + MOZ_CRASH("visitSubstr"); +} + +void +LIRGeneratorARM64::visitRandom(MRandom* ins) +{ + MOZ_CRASH("visitRandom"); +} diff --git a/js/src/jit/arm64/Lowering-arm64.h b/js/src/jit/arm64/Lowering-arm64.h new file mode 100644 index 0000000000..68fcaee6a2 --- /dev/null +++ b/js/src/jit/arm64/Lowering-arm64.h @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_arm64_Lowering_arm64_h +#define jit_arm64_Lowering_arm64_h + +#include "jit/shared/Lowering-shared.h" + +namespace js { +namespace jit { + +class LIRGeneratorARM64 : public LIRGeneratorShared +{ + public: + LIRGeneratorARM64(MIRGenerator* gen, MIRGraph& graph, LIRGraph& lirGraph) + : LIRGeneratorShared(gen, graph, lirGraph) + { } + + protected: + // Adds a box input to an instruction, setting operand |n| to the type and + // |n+1| to the payload. + void useBox(LInstruction* lir, size_t n, MDefinition* mir, + LUse::Policy policy = LUse::REGISTER, bool useAtStart = false); + void useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register reg2); + + LAllocation useByteOpRegister(MDefinition* mir); + LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition* mir); + + inline LDefinition tempToUnbox() { + return temp(); + } + + bool needTempForPostBarrier() { return true; } + + // ARM64 has a scratch register, so no need for another temp for dispatch ICs. + LDefinition tempForDispatchCache(MIRType outputType = MIRType_None) { + return LDefinition::BogusTemp(); + } + + void lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, size_t lirIndex); + void defineUntypedPhi(MPhi* phi, size_t lirIndex); + void lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs, + MDefinition* rhs); + void lowerUrshD(MUrsh* mir); + + void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input); + void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, + MDefinition* lhs, MDefinition* rhs); + + void lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input); + + template + void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir, + MDefinition* lhs, MDefinition* rhs); + + void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir, + MDefinition* lhs, MDefinition* rhs) + { + return lowerForFPU(ins, mir, lhs, rhs); + } + + void lowerForCompFx4(LSimdBinaryCompFx4* ins, MSimdBinaryComp* mir, + MDefinition* lhs, MDefinition* rhs) + { + return lowerForFPU(ins, mir, lhs, rhs); + } + + void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir, + MDefinition* lhs, MDefinition* rhs); + void lowerConstantDouble(double d, MInstruction* ins); + void lowerConstantFloat32(float d, MInstruction* ins); + void lowerTruncateDToInt32(MTruncateToInt32* ins); + void lowerTruncateFToInt32(MTruncateToInt32* ins); + void lowerDivI(MDiv* div); + void lowerModI(MMod* mod); + void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs); + void lowerUDiv(MDiv* div); + void lowerUMod(MMod* mod); + void visitPowHalf(MPowHalf* ins); + void visitAsmJSNeg(MAsmJSNeg* ins); + + LTableSwitchV* newLTableSwitchV(MTableSwitch* ins); + LTableSwitch* newLTableSwitch(const LAllocation& in, + const LDefinition& inputCopy, + MTableSwitch* ins); + + public: + void visitConstant(MConstant* ins); + void visitBox(MBox* box); + void visitUnbox(MUnbox* unbox); + void visitReturn(MReturn* ret); + void lowerPhi(MPhi* phi); + void visitGuardShape(MGuardShape* ins); + void visitGuardObjectGroup(MGuardObjectGroup* ins); + void visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble* ins); + void visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins); + void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins); + void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins); + void visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins); + void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins); + void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins); + void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins); + void visitSimdBinaryArith(MSimdBinaryArith* ins); + void visitSimdSelect(MSimdSelect* ins); + void visitSimdSplatX4(MSimdSplatX4* ins); + void visitSimdValueX4(MSimdValueX4* ins); + void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins); + void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins); + void visitSubstr(MSubstr* ins); + void visitRandom(MRandom* ins); +}; + +typedef LIRGeneratorARM64 LIRGeneratorSpecific; + +} // namespace jit +} // namespace js + +#endif /* jit_arm64_Lowering_arm64_h */ diff --git a/js/src/jit/mips/AtomicOperations-mips.h b/js/src/jit/mips/AtomicOperations-mips.h new file mode 100644 index 0000000000..7c509c5188 --- /dev/null +++ b/js/src/jit/mips/AtomicOperations-mips.h @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* For documentation, see jit/AtomicOperations.h */ + +#ifndef jit_mips_AtomicOperations_mips_h +#define jit_mips_AtomicOperations_mips_h + +#include "jit/AtomicOperations.h" + +inline bool +js::jit::AtomicOperations::isLockfree8() +{ + // Don't crash this one, since it may be read during + // initialization, to cache the value. + return false; +} + +inline void +js::jit::AtomicOperations::fenceSeqCst() +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::loadSeqCst(T* addr) +{ + MOZ_CRASH(); +} + +template +inline void +js::jit::AtomicOperations::storeSeqCst(T* addr, T val) +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::compareExchangeSeqCst(T* addr, T oldval, T newval) +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::fetchAddSeqCst(T* addr, T val) +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::fetchSubSeqCst(T* addr, T val) +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::fetchAndSeqCst(T* addr, T val) +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::fetchOrSeqCst(T* addr, T val) +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::fetchXorSeqCst(T* addr, T val) +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::exchangeSeqCst(T* addr, T val) +{ + MOZ_CRASH(); +} + +template +inline void +js::jit::RegionLock::acquire(void* addr) +{ + MOZ_CRASH(); +} + +template +inline void +js::jit::RegionLock::release(void* addr) +{ + MOZ_CRASH(); +} + +#endif // jit_mips_AtomicOperations_mips_h diff --git a/js/src/jit/none/Architecture-none.h b/js/src/jit/none/Architecture-none.h index 8243261f16..2e71dc6c55 100644 --- a/js/src/jit/none/Architecture-none.h +++ b/js/src/jit/none/Architecture-none.h @@ -111,6 +111,7 @@ struct FloatRegister FloatRegister asInt32x4() const { MOZ_CRASH(); } FloatRegister asFloat32x4() const { MOZ_CRASH(); } Code code() const { MOZ_CRASH(); } + Encoding encoding() const { MOZ_CRASH(); } const char* name() const { MOZ_CRASH(); } bool volatile_() const { MOZ_CRASH(); } bool operator != (FloatRegister) const { MOZ_CRASH(); } diff --git a/js/src/jit/none/AtomicOperations-none.h b/js/src/jit/none/AtomicOperations-none.h new file mode 100644 index 0000000000..16f2325bce --- /dev/null +++ b/js/src/jit/none/AtomicOperations-none.h @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* For documentation, see jit/AtomicOperations.h */ + +#ifndef jit_none_AtomicOperations_none_h +#define jit_none_AtomicOperations_none_h + +#include "jit/AtomicOperations.h" + +inline bool +js::jit::AtomicOperations::isLockfree8() +{ + // Don't crash this one, since it may be read during + // initialization, to cache the value. + return false; +} + +inline void +js::jit::AtomicOperations::fenceSeqCst() +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::loadSeqCst(T* addr) +{ + MOZ_CRASH(); +} + +template +inline void +js::jit::AtomicOperations::storeSeqCst(T* addr, T val) +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::compareExchangeSeqCst(T* addr, T oldval, T newval) +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::fetchAddSeqCst(T* addr, T val) +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::fetchSubSeqCst(T* addr, T val) +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::fetchAndSeqCst(T* addr, T val) +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::fetchOrSeqCst(T* addr, T val) +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::fetchXorSeqCst(T* addr, T val) +{ + MOZ_CRASH(); +} + +template +inline T +js::jit::AtomicOperations::exchangeSeqCst(T* addr, T val) +{ + MOZ_CRASH(); +} + +template +inline void +js::jit::RegionLock::acquire(void* addr) +{ + MOZ_CRASH(); +} + +template +inline void +js::jit::RegionLock::release(void* addr) +{ + MOZ_CRASH(); +} + +#endif // jit_none_AtomicOperations_none_h diff --git a/js/src/jit/none/MacroAssembler-none.h b/js/src/jit/none/MacroAssembler-none.h index 69a1cefdd7..a80a0eea3b 100644 --- a/js/src/jit/none/MacroAssembler-none.h +++ b/js/src/jit/none/MacroAssembler-none.h @@ -131,6 +131,12 @@ class Assembler : public AssemblerShared static void ToggleCall(CodeLocationLabel, bool) { MOZ_CRASH(); } static uintptr_t GetPointer(uint8_t*) { MOZ_CRASH(); } + + void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, + const Disassembler::HeapAccess &heapAccess) + { + MOZ_CRASH(); + } }; class Operand diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index ccf8e830fc..9022b25493 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -856,6 +856,12 @@ enum AsmJSImmKind #if defined(JS_CODEGEN_ARM) AsmJSImm_aeabi_idivmod = AsmJSExit::Builtin_IDivMod, AsmJSImm_aeabi_uidivmod = AsmJSExit::Builtin_UDivMod, + AsmJSImm_AtomicCmpXchg = AsmJSExit::Builtin_AtomicCmpXchg, + AsmJSImm_AtomicFetchAdd = AsmJSExit::Builtin_AtomicFetchAdd, + AsmJSImm_AtomicFetchSub = AsmJSExit::Builtin_AtomicFetchSub, + AsmJSImm_AtomicFetchAnd = AsmJSExit::Builtin_AtomicFetchAnd, + AsmJSImm_AtomicFetchOr = AsmJSExit::Builtin_AtomicFetchOr, + AsmJSImm_AtomicFetchXor = AsmJSExit::Builtin_AtomicFetchXor, #endif AsmJSImm_ModD = AsmJSExit::Builtin_ModD, AsmJSImm_SinD = AsmJSExit::Builtin_SinD, diff --git a/js/src/jit/x86-shared/AtomicOperations-x86-shared.h b/js/src/jit/x86-shared/AtomicOperations-x86-shared.h new file mode 100644 index 0000000000..78d5996c11 --- /dev/null +++ b/js/src/jit/x86-shared/AtomicOperations-x86-shared.h @@ -0,0 +1,547 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* For overall documentation, see jit/AtomicOperations.h */ + +#ifndef jit_shared_AtomicOperations_x86_shared_h +#define jit_shared_AtomicOperations_x86_shared_h + +#include "jit/AtomicOperations.h" + +// Lock-freedom on x86 and x64: +// +// On x86 and x64 there are atomic instructions for 8-byte accesses: +// +// Load and stores: +// - Loads and stores are single-copy atomic for up to 8 bytes +// starting with the Pentium; the store requires a post-fence for +// sequential consistency +// +// CompareExchange: +// - On x64 CMPXCHGQ can always be used +// - On x86 CMPXCHG8B can be used starting with the first Pentium +// +// Exchange: +// - On x64 XCHGQ can always be used +// - On x86 one has to use a CompareExchange loop +// +// Observe also that the JIT will not be enabled unless we have SSE2, +// which was introduced with the Pentium 4. Ergo the JIT will be able +// to use atomic instructions for up to 8 bytes on all x86 platforms +// for the primitives we care about. +// +// However, C++ compilers and libraries may not provide access to +// those 8-byte instructions directly. Clang in 32-bit mode does not +// provide 8-byte atomic primitives at all (even with eg -arch i686 +// specified). On Windows 32-bit, MSVC does not provide +// _InterlockedExchange64 since it does not map directly to an +// instruction. +// +// There are thus sundry workarounds below to handle known corner +// cases. + +#if defined(__clang__) || defined(__GNUC__) + +// The default implementation tactic for gcc/clang is to use the newer +// __atomic intrinsics added for use in C++11 . Where that +// isn't available, we use GCC's older __sync functions instead. +// +// ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS is kept as a backward +// compatible option for older compilers: enable this to use GCC's old +// __sync functions instead of the newer __atomic functions. This +// will be required for GCC 4.6.x and earlier, and probably for Clang +// 3.1, should we need to use those versions. + +// #define ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + +// Lock-free 8-byte atomics are assumed on x86 but must be disabled in +// corner cases, see comments below and in isLockfree8(). + +# define LOCKFREE8 + +// This pertains to Clang compiling with -m32, in this case the 64-bit +// __atomic builtins are not available (observed on various Mac OS X +// versions with Apple Clang and on Linux with Clang 3.5). +// +// For now just punt: disable lock-free 8-word data. The JIT will +// call isLockfree8() to determine what to do and will stay in sync. +// (Bug 1146817 tracks the work to improve on this.) + +# if defined(__clang__) && defined(__i386) +# undef LOCKFREE8 +# endif + +inline bool +js::jit::AtomicOperations::isLockfree8() +{ +# ifndef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + MOZ_ASSERT(__atomic_always_lock_free(sizeof(int8_t), 0)); + MOZ_ASSERT(__atomic_always_lock_free(sizeof(int16_t), 0)); + MOZ_ASSERT(__atomic_always_lock_free(sizeof(int32_t), 0)); +# endif +# ifdef LOCKFREE8 +# ifndef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + MOZ_ASSERT(__atomic_always_lock_free(sizeof(int64_t), 0)); +# endif + return true; +# else + return false; +# endif +} + +inline void +js::jit::AtomicOperations::fenceSeqCst() +{ +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + __sync_synchronize(); +# else + __atomic_thread_fence(__ATOMIC_SEQ_CST); +# endif +} + +template +inline T +js::jit::AtomicOperations::loadSeqCst(T* addr) +{ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + // Inhibit compiler reordering with a volatile load. The x86 does + // not reorder loads with respect to subsequent loads or stores + // and no ordering barrier is required here. See more elaborate + // comments in storeSeqCst. + T v = *static_cast(addr); +# else + T v; + __atomic_load(addr, &v, __ATOMIC_SEQ_CST); +# endif + return v; +} + +# ifndef LOCKFREE8 +template<> +inline int64_t +js::jit::AtomicOperations::loadSeqCst(int64_t* addr) +{ + MOZ_CRASH(); +} + +template<> +inline uint64_t +js::jit::AtomicOperations::loadSeqCst(uint64_t* addr) +{ + MOZ_CRASH(); +} +# endif // LOCKFREE8 + +template +inline void +js::jit::AtomicOperations::storeSeqCst(T* addr, T val) +{ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + // Inhibit compiler reordering with a volatile store. The x86 may + // reorder a store with respect to a subsequent load from a + // different location, hence there is an ordering barrier here to + // prevent that. + // + // By way of background, look to eg + // http://bartoszmilewski.com/2008/11/05/who-ordered-memory-fences-on-an-x86/ + // + // Consider: + // + // uint8_t x = 0, y = 0; // to start + // + // thread1: + // sx: AtomicOperations::store(&x, 1); + // gy: uint8_t obs1 = AtomicOperations::loadSeqCst(&y); + // + // thread2: + // sy: AtomicOperations::store(&y, 1); + // gx: uint8_t obs2 = AtomicOperations::loadSeqCst(&x); + // + // Sequential consistency requires a total global ordering of + // operations: sx-gy-sy-gx, sx-sy-gx-gy, sx-sy-gy-gx, sy-gx-sx-gy, + // sy-sx-gy-gx, or sy-sx-gx-gy. In every ordering at least one of + // sx-before-gx or sy-before-gy happens, so *at least one* of + // obs1/obs2 is 1. + // + // If AtomicOperations::{load,store}SeqCst were just volatile + // {load,store}, x86 could reorder gx/gy before each thread's + // prior load. That would permit gx-gy-sx-sy: both loads would be + // 0! Thus after a volatile store we must synchronize to ensure + // the store happens before the load. + *static_cast(addr) = val; + __sync_synchronize(); +# else + __atomic_store(addr, &val, __ATOMIC_SEQ_CST); +# endif +} + +# ifndef LOCKFREE8 +template<> +inline void +js::jit::AtomicOperations::storeSeqCst(int64_t* addr, int64_t val) +{ + MOZ_CRASH(); +} + +template<> +inline void +js::jit::AtomicOperations::storeSeqCst(uint64_t* addr, uint64_t val) +{ + MOZ_CRASH(); +} +# endif // LOCKFREE8 + +template +inline T +js::jit::AtomicOperations::exchangeSeqCst(T* addr, T val) +{ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + T v; + do { + // Here I assume the compiler will not hoist the load. It + // shouldn't, because the CAS could affect* addr. + v = *addr; + } while (!__sync_bool_compare_and_swap(addr, v, val)); + return v; +# else + T v; + __atomic_exchange(addr, &val, &v, __ATOMIC_SEQ_CST); + return v; +# endif +} + +# ifndef LOCKFREE8 +template<> +inline int64_t +js::jit::AtomicOperations::exchangeSeqCst(int64_t* addr, int64_t val) +{ + MOZ_CRASH(); +} + +template<> +inline uint64_t +js::jit::AtomicOperations::exchangeSeqCst(uint64_t* addr, uint64_t val) +{ + MOZ_CRASH(); +} +# endif // LOCKFREE8 + +template +inline T +js::jit::AtomicOperations::compareExchangeSeqCst(T* addr, T oldval, T newval) +{ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_val_compare_and_swap(addr, oldval, newval); +# else + __atomic_compare_exchange(addr, &oldval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return oldval; +# endif +} + +# ifndef LOCKFREE8 +template<> +inline int64_t +js::jit::AtomicOperations::compareExchangeSeqCst(int64_t* addr, int64_t oldval, int64_t newval) +{ + MOZ_CRASH(); +} + +template<> +inline uint64_t +js::jit::AtomicOperations::compareExchangeSeqCst(uint64_t* addr, uint64_t oldval, uint64_t newval) +{ + MOZ_CRASH(); +} +# endif // LOCKFREE8 + +template +inline T +js::jit::AtomicOperations::fetchAddSeqCst(T* addr, T val) +{ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_fetch_and_add(addr, val); +# else + return __atomic_fetch_add(addr, val, __ATOMIC_SEQ_CST); +# endif +} + +template +inline T +js::jit::AtomicOperations::fetchSubSeqCst(T* addr, T val) +{ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_fetch_and_sub(addr, val); +# else + return __atomic_fetch_sub(addr, val, __ATOMIC_SEQ_CST); +# endif +} + +template +inline T +js::jit::AtomicOperations::fetchAndSeqCst(T* addr, T val) +{ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_fetch_and_and(addr, val); +# else + return __atomic_fetch_and(addr, val, __ATOMIC_SEQ_CST); +# endif +} + +template +inline T +js::jit::AtomicOperations::fetchOrSeqCst(T* addr, T val) +{ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_fetch_and_or(addr, val); +# else + return __atomic_fetch_or(addr, val, __ATOMIC_SEQ_CST); +# endif +} + +template +inline T +js::jit::AtomicOperations::fetchXorSeqCst(T* addr, T val) +{ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_fetch_and_xor(addr, val); +# else + return __atomic_fetch_xor(addr, val, __ATOMIC_SEQ_CST); +# endif +} + +template +inline void +js::jit::RegionLock::acquire(void* addr) +{ +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + while (!__sync_bool_compare_and_swap(&spinlock, 0, 1)) + continue; +# else + uint32_t zero = 0; + uint32_t one = 1; + while (!__atomic_compare_exchange(&spinlock, &zero, &one, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) + continue; +# endif +} + +template +inline void +js::jit::RegionLock::release(void* addr) +{ + MOZ_ASSERT(AtomicOperations::loadSeqCst(&spinlock) == 1, "releasing unlocked region lock"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + __sync_sub_and_fetch(&spinlock, 1); // Should turn into LOCK XADD +# else + uint32_t zero = 0; + __atomic_store(&spinlock, &zero, __ATOMIC_SEQ_CST); +# endif +} + +# undef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS +# undef LOCKFREE8 + +#elif defined(_MSC_VER) + +// On 32-bit CPUs there is no 64-bit XCHG instruction, one must +// instead use a loop with CMPXCHG8B. Since MSVC provides +// _InterlockedExchange64 only if it maps directly to XCHG, the +// workaround must be manual. + +# define HAVE_EXCHANGE64 + +# if !_WIN64 +# undef HAVE_EXCHANGE64 +# endif + +// Below, _ReadWriteBarrier is a compiler directive, preventing +// reordering of instructions and reuse of memory values across it. + +inline bool +js::jit::AtomicOperations::isLockfree8() +{ + // See general comments at the start of this file. + // + // The MSDN docs suggest very strongly that if code is compiled for + // Pentium or better the 64-bit primitives will be lock-free, see + // eg the "Remarks" secion of the page for _InterlockedCompareExchange64, + // currently here: + // https://msdn.microsoft.com/en-us/library/ttk2z1ws%28v=vs.85%29.aspx + // + // But I've found no way to assert that at compile time or run time, + // there appears to be no WinAPI is_lock_free() test. + return true; +} + +inline void +js::jit::AtomicOperations::fenceSeqCst() +{ + _ReadWriteBarrier(); +# if JS_BITS_PER_WORD == 32 + // If configured for SSE2+ we can use the MFENCE instruction, available + // through the _mm_mfence intrinsic. But for non-SSE2 systems we have + // to do something else. Linux uses "lock add [esp], 0", so why not? + __asm lock add [esp], 0; +# else + _mm_mfence(); +# endif +} + +template +inline T +js::jit::AtomicOperations::loadSeqCst(T* addr) +{ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); + _ReadWriteBarrier(); + T v = *addr; + _ReadWriteBarrier(); + return v; +} + +template +inline void +js::jit::AtomicOperations::storeSeqCst(T* addr, T val) +{ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); + _ReadWriteBarrier(); + *addr = val; + fenceSeqCst(); +} + +# define MSC_EXCHANGEOP(T, U, xchgop) \ + template<> inline T \ + js::jit::AtomicOperations::exchangeSeqCst(T* addr, T val) { \ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); \ + return (T)xchgop((U volatile*)addr, (U)val); \ + } + +# define MSC_EXCHANGEOP_CAS(T, U, cmpxchg) \ + template<> inline T \ + js::jit::AtomicOperations::exchangeSeqCst(T* addr, T newval) { \ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); \ + T oldval; \ + do { \ + _ReadWriteBarrier(); \ + oldval = *addr; \ + } while (!cmpxchg((U volatile*)addr, (U)newval, (U)oldval)); \ + return oldval; \ + } + +MSC_EXCHANGEOP(int8_t, char, _InterlockedExchange8) +MSC_EXCHANGEOP(uint8_t, char, _InterlockedExchange8) +MSC_EXCHANGEOP(int16_t, short, _InterlockedExchange16) +MSC_EXCHANGEOP(uint16_t, short, _InterlockedExchange16) +MSC_EXCHANGEOP(int32_t, long, _InterlockedExchange) +MSC_EXCHANGEOP(uint32_t, long, _InterlockedExchange) +# ifdef HAVE_EXCHANGE64 +MSC_EXCHANGEOP(int64_t, __int64, _InterlockedExchange64) +MSC_EXCHANGEOP(uint64_t, __int64, _InterlockedExchange64) +# else +MSC_EXCHANGEOP_CAS(int64_t, __int64, _InterlockedCompareExchange64) +MSC_EXCHANGEOP_CAS(uint64_t, __int64, _InterlockedCompareExchange64) +# endif + +# undef MSC_EXCHANGEOP +# undef MSC_EXCHANGEOP_CAS + +# define MSC_CAS(T, U, cmpxchg) \ + template<> inline T \ + js::jit::AtomicOperations::compareExchangeSeqCst(T* addr, T oldval, T newval) { \ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); \ + return (T)cmpxchg((U volatile*)addr, (U)newval, (U)oldval); \ + } + +MSC_CAS(int8_t, char, _InterlockedCompareExchange8) +MSC_CAS(uint8_t, char, _InterlockedCompareExchange8) +MSC_CAS(int16_t, short, _InterlockedCompareExchange16) +MSC_CAS(uint16_t, short, _InterlockedCompareExchange16) +MSC_CAS(int32_t, long, _InterlockedCompareExchange) +MSC_CAS(uint32_t, long, _InterlockedCompareExchange) +MSC_CAS(int64_t, __int64, _InterlockedCompareExchange64) +MSC_CAS(uint64_t, __int64, _InterlockedCompareExchange64) + +# undef MSC_CAS + +# define MSC_FETCHADDOP(T, U, xadd) \ + template<> inline T \ + js::jit::AtomicOperations::fetchAddSeqCst(T* addr, T val) { \ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); \ + return (T)xadd((U volatile*)addr, (U)val); \ + } \ + template<> inline T \ + js::jit::AtomicOperations::fetchSubSeqCst(T* addr, T val) { \ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); \ + return (T)xadd((U volatile*)addr, (U)-val); \ + } + +MSC_FETCHADDOP(int8_t, char, _InterlockedExchangeAdd8) +MSC_FETCHADDOP(uint8_t, char, _InterlockedExchangeAdd8) +MSC_FETCHADDOP(int16_t, short, _InterlockedExchangeAdd16) +MSC_FETCHADDOP(uint16_t, short, _InterlockedExchangeAdd16) +MSC_FETCHADDOP(int32_t, long, _InterlockedExchangeAdd) +MSC_FETCHADDOP(uint32_t, long, _InterlockedExchangeAdd) + +# undef MSC_FETCHADDOP + +# define MSC_FETCHBITOP(T, U, andop, orop, xorop) \ + template<> inline T \ + js::jit::AtomicOperations::fetchAndSeqCst(T* addr, T val) { \ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); \ + return (T)andop((U volatile*)addr, (U)val); \ + } \ + template<> inline T \ + js::jit::AtomicOperations::fetchOrSeqCst(T* addr, T val) { \ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); \ + return (T)orop((U volatile*)addr, (U)val); \ + } \ + template<> inline T \ + js::jit::AtomicOperations::fetchXorSeqCst(T* addr, T val) { \ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); \ + return (T)xorop((U volatile*)addr, (U)val); \ + } + +MSC_FETCHBITOP(int8_t, char, _InterlockedAnd8, _InterlockedOr8, _InterlockedXor8) +MSC_FETCHBITOP(uint8_t, char, _InterlockedAnd8, _InterlockedOr8, _InterlockedXor8) +MSC_FETCHBITOP(int16_t, short, _InterlockedAnd16, _InterlockedOr16, _InterlockedXor16) +MSC_FETCHBITOP(uint16_t, short, _InterlockedAnd16, _InterlockedOr16, _InterlockedXor16) +MSC_FETCHBITOP(int32_t, long, _InterlockedAnd, _InterlockedOr, _InterlockedXor) +MSC_FETCHBITOP(uint32_t, long, _InterlockedAnd, _InterlockedOr, _InterlockedXor) + +# undef MSC_FETCHBITOP + +template +inline void +js::jit::RegionLock::acquire(void* addr) +{ + while (_InterlockedCompareExchange((long*)&spinlock, /*newval=*/1, /*oldval=*/0) == 1) + continue; +} + +template +inline void +js::jit::RegionLock::release(void* addr) +{ + MOZ_ASSERT(AtomicOperations::loadSeqCst(&spinlock) == 1, "releasing unlocked region lock"); + _InterlockedExchange((long*)&spinlock, 0); +} + +# undef HAVE_EXCHANGE64 + +#elif defined(ENABLE_SHARED_ARRAY_BUFFER) + +# error "Either disable JS shared memory, use GCC, Clang, or MSVC, or add code here" + +#endif // platform + +#endif // jit_shared_AtomicOperations_x86_shared_h diff --git a/js/src/js.msg b/js/src/js.msg index e4e045d063..fd1a7fbb6f 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -177,6 +177,7 @@ MSG_DEF(JSMSG_UNKNOWN_FORMAT, 1, JSEXN_INTERNALERR, "unknown bytecode f MSG_DEF(JSMSG_ACCESSOR_WRONG_ARGS, 3, JSEXN_SYNTAXERR, "{0} functions must have {1} argument{2}") MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side") MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 0, JSEXN_INTERNALERR, "array initialiser too large") +MSG_DEF(JSMSG_AS_AFTER_IMPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'as' after import *") MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'") MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "anonymous generator function returns a value") MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)") @@ -243,7 +244,8 @@ MSG_DEF(JSMSG_EMPTY_CONSEQUENT, 0, JSEXN_SYNTAXERR, "mistyped ; after con MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 0, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?") MSG_DEF(JSMSG_EXPORT_DECL_AT_TOP_LEVEL,0, JSEXN_SYNTAXERR, "export declarations may only appear at top level") MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY, 0, JSEXN_SYNTAXERR, "finally without try") -MSG_DEF(JSMSG_FROM_AFTER_IMPORT_SPEC_SET, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import specifier set") +MSG_DEF(JSMSG_FROM_AFTER_IMPORT_CLAUSE, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import clause") +MSG_DEF(JSMSG_FROM_AFTER_EXPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after export *") MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT, 2, JSEXN_SYNTAXERR, "unexpected garbage after {0}, starting with {1}") MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER, 0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal") MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 0, JSEXN_SYNTAXERR, "illegal character") @@ -265,6 +267,8 @@ MSG_DEF(JSMSG_MISSING_OCTAL_DIGITS, 0, JSEXN_SYNTAXERR, "missing octal digits MSG_DEF(JSMSG_MODULES_NOT_IMPLEMENTED, 0, JSEXN_SYNTAXERR, "modules are not implemented yet") MSG_DEF(JSMSG_MODULE_SPEC_AFTER_FROM, 0, JSEXN_SYNTAXERR, "missing module specifier after 'from' keyword") MSG_DEF(JSMSG_NAME_AFTER_DOT, 0, JSEXN_SYNTAXERR, "missing name after . operator") +MSG_DEF(JSMSG_NAME_AFTER_IMPORT_STAR_AS, 0, JSEXN_SYNTAXERR, "missing name after import * as") +MSG_DEF(JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT, 0, JSEXN_SYNTAXERR, "expected named imports or namespace import after comma") MSG_DEF(JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT, 0, JSEXN_SYNTAXERR, "parameter(s) with default followed by parameter without default") MSG_DEF(JSMSG_NO_BINDING_NAME, 0, JSEXN_SYNTAXERR, "missing binding name") MSG_DEF(JSMSG_NO_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class default constructors not yet implemented") @@ -329,6 +333,7 @@ MSG_DEF(JSMSG_YIELD_IN_DEFAULT, 0, JSEXN_SYNTAXERR, "yield in default exp MSG_DEF(JSMSG_BAD_COLUMN_NUMBER, 0, JSEXN_RANGEERR, "column number out of range") MSG_DEF(JSMSG_COMPUTED_NAME_IN_PATTERN,0, JSEXN_SYNTAXERR, "computed property names aren't supported in this destructuring declaration") MSG_DEF(JSMSG_DEFAULT_IN_PATTERN, 0, JSEXN_SYNTAXERR, "destructuring defaults aren't supported in this destructuring declaration") +MSG_DEF(JSMSG_BAD_NEWTARGET, 0, JSEXN_SYNTAXERR, "new.target only allowed in non-exotic functions") // asm.js MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL, 1, JSEXN_TYPEERR, "asm.js type error: {0}") diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index a9ac6fbf47..0c5b504b38 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -56,6 +56,7 @@ UNIFIED_SOURCES += [ 'testMappedArrayBuffer.cpp', 'testMutedErrors.cpp', 'testNewObject.cpp', + 'testNewTargetInvokeConstructor.cpp', 'testNullRoot.cpp', 'testObjectEmulatingUndefined.cpp', 'testOOM.cpp', diff --git a/js/src/jsapi-tests/testNewTargetInvokeConstructor.cpp b/js/src/jsapi-tests/testNewTargetInvokeConstructor.cpp new file mode 100644 index 0000000000..18d86ca68d --- /dev/null +++ b/js/src/jsapi-tests/testNewTargetInvokeConstructor.cpp @@ -0,0 +1,25 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jsapi.h" + +#include "jsapi-tests/tests.h" + +BEGIN_TEST(testNewTargetInvokeConstructor) +{ + JS::RootedValue func(cx); + + EVAL("(function(expected) { if (expected !== new.target) throw new Error('whoops'); })", + &func); + + JS::AutoValueArray<1> args(cx); + args[0].set(func); + + JS::RootedValue rval(cx); + + CHECK(JS::Construct(cx, func, args, &rval)); + + return true; +} +END_TEST(testNewTargetInvokeConstructor) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 7f14e2f813..d782f85db3 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -3582,7 +3582,7 @@ js::NewDenseUnallocatedArray(ExclusiveContext* cx, uint32_t length, return NewArray<0>(cx, length, proto, newKind); } -ArrayObject * +ArrayObject* js::NewDenseArray(ExclusiveContext* cx, uint32_t length, HandleObjectGroup group, AllocatingBehaviour allocating, bool convertDoubleElements) { diff --git a/js/src/jsarray.h b/js/src/jsarray.h index 8287575970..2ac01847bc 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -32,8 +32,8 @@ IdIsIndex(jsid id, uint32_t* indexp) return js::StringIsArrayIndex(JSID_TO_ATOM(id), indexp); } -extern JSObject * -InitArrayClass(JSContext *cx, js::HandleObject obj); +extern JSObject* +InitArrayClass(JSContext* cx, js::HandleObject obj); class ArrayObject; @@ -73,8 +73,8 @@ enum AllocatingBehaviour { * Create a dense array with a set length, but only allocates space for the * contents if the length is not excessive. */ -extern ArrayObject * -NewDenseArray(ExclusiveContext *cx, uint32_t length, HandleObjectGroup group, +extern ArrayObject* +NewDenseArray(ExclusiveContext* cx, uint32_t length, HandleObjectGroup group, AllocatingBehaviour allocating, bool convertDoubleElements = false); /* Create a dense array with a copy of the dense array elements in src. */ diff --git a/js/src/jsast.tbl b/js/src/jsast.tbl index 56545c7469..33f69f7921 100644 --- a/js/src/jsast.tbl +++ b/js/src/jsast.tbl @@ -38,6 +38,7 @@ ASTDEF(AST_COMP_EXPR, "ComprehensionExpression", "comprehensi ASTDEF(AST_GENERATOR_EXPR, "GeneratorExpression", "generatorExpression") ASTDEF(AST_YIELD_EXPR, "YieldExpression", "yieldExpression") ASTDEF(AST_CLASS_EXPR, "ClassExpression", "classExpression") +ASTDEF(AST_NEWTARGET_EXPR, "NewTargetExpression", "newTargetExpression") ASTDEF(AST_EMPTY_STMT, "EmptyStatement", "emptyStatement") ASTDEF(AST_BLOCK_STMT, "BlockStatement", "blockStatement") diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 04c9e0d5f2..00f100767b 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -805,9 +805,20 @@ bool intrinsic_IsSuspendedStarGenerator(JSContext* cx, unsigned argc, Value* vp) bool intrinsic_IsArrayIterator(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_IsStringIterator(JSContext* cx, unsigned argc, Value* vp); +bool intrinsic_IsArrayBuffer(JSContext* cx, unsigned argc, Value* vp); + bool intrinsic_IsTypedArray(JSContext* cx, unsigned argc, Value* vp); +bool intrinsic_IsPossiblyWrappedTypedArray(JSContext* cx, unsigned argc, Value* vp); +bool intrinsic_TypedArrayBuffer(JSContext* cx, unsigned argc, Value* vp); +bool intrinsic_TypedArrayByteOffset(JSContext* cx, unsigned argc, Value* vp); +bool intrinsic_TypedArrayElementShift(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_TypedArrayLength(JSContext* cx, unsigned argc, Value* vp); +bool intrinsic_MoveTypedArrayElements(JSContext* cx, unsigned argc, Value* vp); +bool intrinsic_SetFromTypedArrayApproach(JSContext* cx, unsigned argc, Value* vp); +bool intrinsic_SetDisjointTypedElements(JSContext* cx, unsigned argc, Value* vp); +bool intrinsic_SetOverlappingTypedElements(JSContext* cx, unsigned argc, Value* vp); + class AutoLockForExclusiveAccess { JSRuntime* runtime; diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 5f6b2e246d..55c9c4e8d7 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1411,8 +1411,9 @@ GetSCOffset(JSStructuredCloneWriter* writer); namespace Scalar { -/* Scalar types which can appear in typed arrays and typed objects. The enum - * values need to be kept in sync with the JS_SCALARTYPEREPR_ constants, as +/* + * Scalar types that can appear in typed arrays and typed objects. The enum + * values must to be kept in sync with the JS_SCALARTYPEREPR_ constants, as * well as the TypedArrayObject::classes and TypedArrayObject::protoClasses * definitions. */ @@ -1466,6 +1467,27 @@ byteSize(Type atype) } } +static inline bool +isSignedIntType(Type atype) { + switch (atype) { + case Int8: + case Int16: + case Int32: + case Int32x4: + return true; + case Uint8: + case Uint8Clamped: + case Uint16: + case Uint32: + case Float32: + case Float64: + case Float32x4: + return false; + default: + MOZ_CRASH("invalid scalar type"); + } +} + static inline bool isSimdType(Type atype) { switch (atype) { @@ -2396,7 +2418,7 @@ FunctionObjectToShadowFunction(JSObject* fun) } /* Statically asserted in jsfun.h. */ -static const unsigned JS_FUNCTION_INTERPRETED_BITS = 0x401; +static const unsigned JS_FUNCTION_INTERPRETED_BITS = 0x0201; // Return whether the given function object is native. static MOZ_ALWAYS_INLINE bool diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 29e53472d9..dbfb588aaa 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2178,12 +2178,12 @@ js::CloneFunctionObjectUseSameScript(JSCompartment *compartment, HandleFunction if (newParent->is()) return true; - // Don't need to clone the script if newParent is not a valid lexical scope - // chain terminator, since in that case we have some actual scope objects on - // our scope chain and whatnot; whoever put them there should be responsible - // for setting our script's flags appropriately. We hit this case for - // JSOP_LAMBDA, for example. - if (!IsValidTerminatingScope(newParent)) + // Don't need to clone the script if newParent is a syntactic scope, since + // in that case we have some actual scope objects on our scope chain and + // whatnot; whoever put them there should be responsible for setting our + // script's flags appropriately. We hit this case for JSOP_LAMBDA, for + // example. + if (IsSyntacticScope(newParent)) return true; // We need to clone the script if we're interpreted and not already marked diff --git a/js/src/jsfun.h b/js/src/jsfun.h index fbbf5c171e..72887a5821 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -32,6 +32,7 @@ class JSFunction : public js::NativeObject NormalFunction = 0, Arrow, /* ES6 '(args) => body' syntax */ Method, /* ES6 MethodDefinition */ + ClassConstructor, Getter, Setter, AsmJS, /* function is an asm.js module or exported function */ @@ -52,18 +53,18 @@ class JSFunction : public js::NativeObject function-statement) */ SELF_HOSTED = 0x0080, /* function is self-hosted builtin and must not be decompilable nor constructible. */ - // Free bit - HAS_REST = 0x0200, /* function has a rest (...) parameter */ - INTERPRETED_LAZY = 0x0400, /* function is interpreted but doesn't have a script yet */ - RESOLVED_LENGTH = 0x0800, /* f.length has been resolved (see fun_resolve). */ - RESOLVED_NAME = 0x1000, /* f.name has been resolved (see fun_resolve). */ + HAS_REST = 0x0100, /* function has a rest (...) parameter */ + INTERPRETED_LAZY = 0x0200, /* function is interpreted but doesn't have a script yet */ + RESOLVED_LENGTH = 0x0400, /* f.length has been resolved (see fun_resolve). */ + RESOLVED_NAME = 0x0800, /* f.name has been resolved (see fun_resolve). */ - FUNCTION_KIND_SHIFT = 13, - FUNCTION_KIND_MASK = 0x7 << FUNCTION_KIND_SHIFT, + FUNCTION_KIND_SHIFT = 12, + FUNCTION_KIND_MASK = 0xf << FUNCTION_KIND_SHIFT, ASMJS_KIND = AsmJS << FUNCTION_KIND_SHIFT, ARROW_KIND = Arrow << FUNCTION_KIND_SHIFT, METHOD_KIND = Method << FUNCTION_KIND_SHIFT, + CLASSCONSTRUCTOR_KIND = ClassConstructor << FUNCTION_KIND_SHIFT, GETTER_KIND = Getter << FUNCTION_KIND_SHIFT, SETTER_KIND = Setter << FUNCTION_KIND_SHIFT, @@ -73,7 +74,8 @@ class JSFunction : public js::NativeObject ASMJS_CTOR = ASMJS_KIND | NATIVE_CTOR, ASMJS_LAMBDA_CTOR = ASMJS_KIND | NATIVE_CTOR | LAMBDA, INTERPRETED_METHOD = INTERPRETED | METHOD_KIND, - INTERPRETED_CLASS_CONSTRUCTOR = INTERPRETED | METHOD_KIND | CONSTRUCTOR, + INTERPRETED_METHOD_GENERATOR = INTERPRETED | METHOD_KIND | CONSTRUCTOR, + INTERPRETED_CLASS_CONSTRUCTOR = INTERPRETED | CLASSCONSTRUCTOR_KIND | CONSTRUCTOR, INTERPRETED_GETTER = INTERPRETED | GETTER_KIND, INTERPRETED_SETTER = INTERPRETED | SETTER_KIND, INTERPRETED_LAMBDA = INTERPRETED | LAMBDA | CONSTRUCTOR, @@ -165,15 +167,12 @@ class JSFunction : public js::NativeObject // Arrow functions store their lexical |this| in the first extended slot. bool isArrow() const { return kind() == Arrow; } // Every class-constructor is also a method. - bool isMethod() const { return kind() == Method; } + bool isMethod() const { return kind() == Method || kind() == ClassConstructor; } + bool isClassConstructor() const { return kind() == ClassConstructor; } bool isGetter() const { return kind() == Getter; } bool isSetter() const { return kind() == Setter; } - bool isClassConstructor() const { - return kind() == Method && isConstructor(); - } - bool allowSuperProperty() const { return isMethod() || isGetter() || isSetter(); } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index f97f589cd7..7daf4fed3f 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2839,6 +2839,10 @@ js::LookupPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSObject** static inline bool NativeGetPureInline(NativeObject* pobj, Shape* shape, Value* vp) { + /* Fail if we have a custom getter. */ + if (!shape->hasDefaultGetter()) + return false; + if (shape->hasSlot()) { *vp = pobj->getSlot(shape->slot()); MOZ_ASSERT(!vp->isMagic()); @@ -2846,8 +2850,7 @@ NativeGetPureInline(NativeObject* pobj, Shape* shape, Value* vp) vp->setUndefined(); } - /* Fail if we have a custom getter. */ - return shape->hasDefaultGetter(); + return true; } bool @@ -2857,6 +2860,12 @@ js::GetPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Value* vp) Shape* shape; if (!LookupPropertyPure(cx, obj, id, &pobj, &shape)) return false; + + if (!shape) { + vp->setUndefined(); + return true; + } + return pobj->isNative() && NativeGetPureInline(&pobj->as(), shape, vp); } diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index b6f107ae80..f98ee230da 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -1264,6 +1264,8 @@ ExpressionDecompiler::decompilePC(jsbytecode* pc) // |this| could convert to a very long object initialiser, so cite it by // its keyword name. return write(js_this_str); + case JSOP_NEWTARGET: + return write("new.target"); case JSOP_CALL: case JSOP_FUNCALL: return decompilePCForStackOperand(pc, -int32_t(GET_ARGC(pc) + 2)) && diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index f5a1e75452..c5d078882f 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -3598,7 +3598,7 @@ class SplitMatchResult { template static ArrayObject* -SplitHelper(JSContext *cx, HandleLinearString str, uint32_t limit, const Matcher &splitMatch, +SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher& splitMatch, HandleObjectGroup group) { size_t strLength = str->length(); diff --git a/js/src/moz.build b/js/src/moz.build index 813796ca80..e57cb0f5ec 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -190,6 +190,7 @@ UNIFIED_SOURCES += [ 'jit/shared/BaselineCompiler-shared.cpp', 'jit/shared/CodeGenerator-shared.cpp', 'jit/shared/Lowering-shared.cpp', + 'jit/SharedIC.cpp', 'jit/Sink.cpp', 'jit/Snapshots.cpp', 'jit/StupidAllocator.cpp', @@ -497,6 +498,11 @@ if CONFIG['_MSC_VER']: # But given the conversion from bool is specified, and this is a # pattern widely used in code in js/src, suppress this warning here. CXXFLAGS += ['-wd4805'] + # C4661 ("no suitable definition provided for explicit template + # instantiation request") is emitted for all Parser methods that + # have a Parser definition but no + # Parser definition, see bug 1167030. + CXXFLAGS += ['-wd4661'] CXXFLAGS += ['-we4067', '-we4258', '-we4275'] if CONFIG['OS_ARCH'] not in ('WINNT', 'HP-UX'): diff --git a/js/src/tests/ecma_6/Class/methDefnGen.js b/js/src/tests/ecma_6/Class/methDefnGen.js index 30f96db03f..596d55477f 100644 --- a/js/src/tests/ecma_6/Class/methDefnGen.js +++ b/js/src/tests/ecma_6/Class/methDefnGen.js @@ -72,4 +72,13 @@ assertEq(a.b(1).next().value, 1); a = {*["b"](c){"use strict";return c;}}; assertEq(a.b(1).next().value, 1); +// Constructing +a = {*g() { yield 1; }} +it = new a.g; +next = it.next(); +assertEq(next.done, false); +assertEq(next.value, 1); +next = it.next(); +assertEq(next.done, true); + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/ecma_6/Class/newTargetArgumentsIntact.js b/js/src/tests/ecma_6/Class/newTargetArgumentsIntact.js new file mode 100644 index 0000000000..d047085ea0 --- /dev/null +++ b/js/src/tests/ecma_6/Class/newTargetArgumentsIntact.js @@ -0,0 +1,44 @@ +// Since we put new.target at the end of the arguments vector, ensrue that it +// doesn't interact with the arguments object + +var argsContent; + +function argsWithNewTarget(foo) { + assertEq(arguments.length, argsContent.length); + for (let i = 0; i < arguments.length; i++) + assertEq(arguments[i], argsContent[i]); + let nt = new.target; + + // Assigning to the arguments object shouldn't infect new.target, either + arguments[arguments.length] = 42; + assertEq(new.target, nt); +} + +// Test constructing invocations, with under and overflow +argsContent = []; +for (let i = 0; i < 100; i++) + new argsWithNewTarget(); + +argsContent = [1]; +for (let i = 0; i < 100; i++) + new argsWithNewTarget(1); + +argsContent = [1,2,3]; +for (let i = 0; i < 100; i++) + new argsWithNewTarget(1, 2, 3); + +// Test spreadnew as well. +argsContent = []; +for (let i = 0; i < 100; i++) + new argsWithNewTarget(...[]); + +argsContent = [1]; +for (let i = 0; i < 100; i++) + new argsWithNewTarget(...[1]); + +argsContent = [1,2,3]; +for (let i = 0; i < 100; i++) + new argsWithNewTarget(...[1,2,3]); + +if (typeof reportCompare === "function") + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/Class/newTargetBound.js b/js/src/tests/ecma_6/Class/newTargetBound.js new file mode 100644 index 0000000000..f65cfe3caf --- /dev/null +++ b/js/src/tests/ecma_6/Class/newTargetBound.js @@ -0,0 +1,16 @@ +function boundTarget(expected) { + assertEq(new.target, expected); +} + +let bound = boundTarget.bind(undefined); + +const TEST_ITERATIONS = 550; + +for (let i = 0; i < TEST_ITERATIONS; i++) + bound(undefined); + +for (let i = 0; i < TEST_ITERATIONS; i++) + new bound(boundTarget); + +if (typeof reportCompare === "function") + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/Class/newTargetContext.js b/js/src/tests/ecma_6/Class/newTargetContext.js new file mode 100644 index 0000000000..4f1bc30b78 --- /dev/null +++ b/js/src/tests/ecma_6/Class/newTargetContext.js @@ -0,0 +1,11 @@ +// new.target is valid inside Function() invocations +var func = new Function("new.target"); + +// new.target is invalid inside eval, even (for now!) eval inside a function. +assertThrowsInstanceOf(() => eval('new.target'), SyntaxError); + +function evalInFunction() { eval('new.target'); } +assertThrowsInstanceOf(() => evalInFunction(), SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/Class/newTargetDVG.js b/js/src/tests/ecma_6/Class/newTargetDVG.js new file mode 100644 index 0000000000..1f9c7d7fc5 --- /dev/null +++ b/js/src/tests/ecma_6/Class/newTargetDVG.js @@ -0,0 +1,7 @@ +function thunk() { + new.target(); +} +assertThrownErrorContains(thunk, "new.target"); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "OK"); diff --git a/js/src/tests/ecma_6/Class/newTargetDirectInvoke.js b/js/src/tests/ecma_6/Class/newTargetDirectInvoke.js new file mode 100644 index 0000000000..1090aa80a4 --- /dev/null +++ b/js/src/tests/ecma_6/Class/newTargetDirectInvoke.js @@ -0,0 +1,47 @@ +// Note that this will also test new.target in ion inlines. When the toplevel +// script is compiled, assertNewTarget will be inlined. +function assertNewTarget(expected, unused) { assertEq(new.target, expected); } + +// Test non-constructing invocations, with arg underflow, overflow, and correct +// numbers +for (let i = 0; i < 100; i++) + assertNewTarget(undefined, null); + +for (let i = 0; i < 100; i++) + assertNewTarget(undefined); + +for (let i = 0; i < 100; i++) + assertNewTarget(undefined, null, 1); + +// Test spread-call +for (let i = 0; i < 100; i++) + assertNewTarget(...[undefined]); + +for (let i = 0; i < 100; i++) + assertNewTarget(...[undefined, null]); + +for (let i = 0; i < 100; i++) + assertNewTarget(...[undefined, null, 1]); + +// Test constructing invocations, again with under and overflow +for (let i = 0; i < 100; i++) + new assertNewTarget(assertNewTarget, null); + +for (let i = 0; i < 100; i++) + new assertNewTarget(assertNewTarget); + +for (let i = 0; i < 100; i++) + new assertNewTarget(assertNewTarget, null, 1); + +// Test spreadnew as well. +for (let i = 0; i < 100; i++) + new assertNewTarget(...[assertNewTarget]); + +for (let i = 0; i < 100; i++) + new assertNewTarget(...[assertNewTarget, null]); + +for (let i = 0; i < 100; i++) + new assertNewTarget(...[assertNewTarget, null, 1]); + +if (typeof reportCompare === "function") + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/Class/newTargetMethods.js b/js/src/tests/ecma_6/Class/newTargetMethods.js new file mode 100644 index 0000000000..84dba1cec7 --- /dev/null +++ b/js/src/tests/ecma_6/Class/newTargetMethods.js @@ -0,0 +1,52 @@ +// Just like newTargetDirectInvoke, except to prove it works in functions +// defined with method syntax as well. Note that methods, getters, and setters +// are not constructible. + +let ol = { + olTest(arg) { assertEq(arg, 4); assertEq(new.target, undefined); }, + get ol() { assertEq(new.target, undefined); }, + set ol(arg) { assertEq(arg, 4); assertEq(new.target, undefined); } +} + +class cl { + constructor() { assertEq(new.target, cl); } + clTest(arg) { assertEq(arg, 4); assertEq(new.target, undefined); } + get cl() { assertEq(new.target, undefined); } + set cl(arg) { assertEq(arg, 4); assertEq(new.target, undefined); } + + static staticclTest(arg) { assertEq(arg, 4); assertEq(new.target, undefined); } + static get staticcl() { assertEq(new.target, undefined); } + static set staticcl(arg) { assertEq(arg, 4); assertEq(new.target, undefined); } +} + +const TEST_ITERATIONS = 150; + +for (let i = 0; i < TEST_ITERATIONS; i++) + ol.olTest(4); +for (let i = 0; i < TEST_ITERATIONS; i++) + ol.ol; +for (let i = 0; i < TEST_ITERATIONS; i++) + ol.ol = 4; + +for (let i = 0; i < TEST_ITERATIONS; i++) + cl.staticclTest(4); +for (let i = 0; i < TEST_ITERATIONS; i++) + cl.staticcl; +for (let i = 0; i < TEST_ITERATIONS; i++) + cl.staticcl = 4; + +for (let i = 0; i < TEST_ITERATIONS; i++) + new cl(); + +let clInst = new cl(); + +for (let i = 0; i < TEST_ITERATIONS; i++) + clInst.clTest(4); +for (let i = 0; i < TEST_ITERATIONS; i++) + clInst.cl; +for (let i = 0; i < TEST_ITERATIONS; i++) + clInst.cl = 4; + + +if (typeof reportCompare === "function") + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/Class/shell.js b/js/src/tests/ecma_6/Class/shell.js index 5c4d61268e..e9d0f9e832 100644 --- a/js/src/tests/ecma_6/Class/shell.js +++ b/js/src/tests/ecma_6/Class/shell.js @@ -10,3 +10,14 @@ function classesEnabled() { return false; } } + +function assertThrownErrorContains(thunk, substr) { + try { + thunk(); + } catch (e) { + if (e.message.indexOf(substr) !== -1) + return; + throw new Error("Expected error containing " + substr + ", got " + e); + } + throw new Error("Expected error containing " + substr + ", no exception thrown"); +} diff --git a/js/src/tests/ecma_6/Class/superPropDVG.js b/js/src/tests/ecma_6/Class/superPropDVG.js index 66ff33b364..7281e3dfcf 100644 --- a/js/src/tests/ecma_6/Class/superPropDVG.js +++ b/js/src/tests/ecma_6/Class/superPropDVG.js @@ -2,17 +2,6 @@ var test = ` -function assertThrownErrorContains(thunk, substr) { - try { - thunk(); - throw new Error("Expected error containing " + substr + ", no exception thrown"); - } catch (e) { - if (e.message.indexOf(substr) !== -1) - return; - throw new Error("Expected error containing " + substr + ", got " + e); - } -} - class testNonExistent { constructor() { super["prop"](); diff --git a/js/src/tests/js1_8_5/reflect-parse/PatternAsserts.js b/js/src/tests/js1_8_5/reflect-parse/PatternAsserts.js index 21060e5759..8fd5840b62 100644 --- a/js/src/tests/js1_8_5/reflect-parse/PatternAsserts.js +++ b/js/src/tests/js1_8_5/reflect-parse/PatternAsserts.js @@ -66,6 +66,11 @@ function assertStmt(src, patt) { assertBlockStmt(src, patt); } +function assertInFunctionExpr(src, patt) { + assertLocalExpr(src, patt); + assertBlockExpr(src, patt); +} + function assertExpr(src, patt) { assertLocalExpr(src, patt); assertGlobalExpr(src, patt); diff --git a/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js b/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js index 2432677606..4ff6c1d0be 100644 --- a/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js +++ b/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js @@ -170,6 +170,9 @@ function arrowExpr(args, body) { body: body }); } +function newTarget() { + return Pattern({ type: "NewTargetExpression" }); +} function unExpr(op, arg) { return Pattern({ type: "UnaryExpression", operator: op, argument: arg }); } diff --git a/js/src/tests/js1_8_5/reflect-parse/newTarget.js b/js/src/tests/js1_8_5/reflect-parse/newTarget.js new file mode 100644 index 0000000000..4030ab466d --- /dev/null +++ b/js/src/tests/js1_8_5/reflect-parse/newTarget.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!xulRuntime.shell) +function testNewTarget() { + + // new.target is currently valid inside any non-arrow, non-generator function + assertInFunctionExpr("new.target", newTarget()); + + // even with gratuitous whitespace. + assertInFunctionExpr(`new. + target`, newTarget()); + + // invalid in top-level scripts + assertError("new.target", SyntaxError); + + // invalid (for now!) in any arrow function + assertError("function foo() { (() => new.target) }", SyntaxError); + assertError("(() => new.target))", SyntaxError); + + // invalid (for now!) in generators + assertError("function *foo() { new.target; }", SyntaxError); + + // new.target is a member expression. You should be able to call, invoke, or + // access properties of it. + assertInFunctionExpr("new.target.foo", dotExpr(newTarget(), ident("foo"))); + assertInFunctionExpr("new.target[\"foo\"]", memExpr(newTarget(), literal("foo"))); + + assertInFunctionExpr("new.target()", callExpr(newTarget(), [])); + assertInFunctionExpr("new new.target()", newExpr(newTarget(), [])); + + // assignment to newTarget is an error + assertError("new.target = 4", SyntaxError); + + // only new.target is a valid production, no shorn names or other names + assertError("new.", SyntaxError); + assertError("new.foo", SyntaxError); + assertError("new.targe", SyntaxError); + + // obj.new.target is still a member expression + assertExpr("obj.new.target", dotExpr(dotExpr(ident("obj"), ident("new")), ident("target"))); +} + +runtest(testNewTarget); diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h index 9db120c453..6567afdca1 100644 --- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -118,6 +118,10 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared static const size_t ARRAY_BUFFER_ALIGNMENT = 8; + static_assert(FLAGS_SLOT == JS_ARRAYBUFFER_FLAGS_SLOT, + "self-hosted code with burned-in constants must get the " + "right flags slot"); + public: enum OwnsState { @@ -163,6 +167,9 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared TYPED_OBJECT_VIEWS = 0x20 }; + static_assert(JS_ARRAYBUFFER_NEUTERED_FLAG == NEUTERED, + "self-hosted code with burned-in constants must use the " + "correct NEUTERED bit value"); public: class BufferContents { diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 81462e909b..0fa056d112 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -192,6 +192,8 @@ macro(signMask, signMask, "signMask") \ macro(source, source, "source") \ macro(stack, stack, "stack") \ + macro(star, star, "*") \ + macro(starDefaultStar, starDefaultStar, "*default*") \ macro(startTimestamp, startTimestamp, "startTimestamp") \ macro(static, static_, "static") \ macro(sticky, sticky, "sticky") \ @@ -199,6 +201,7 @@ macro(StructType, StructType, "StructType") \ macro(style, style, "style") \ macro(super, super, "super") \ + macro(target, target, "target") \ macro(test, test, "test") \ macro(throw, throw_, "throw") \ macro(timestamp, timestamp, "timestamp") \ diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 0d322f00f3..463e4aab7e 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -768,7 +768,7 @@ Debugger::wrapEnvironment(JSContext* cx, Handle env, MutableHandleValue rv * DebuggerEnv should only wrap a debug scope chain obtained (transitively) * from GetDebugScopeFor(Frame|Function). */ - MOZ_ASSERT(IsValidTerminatingScope(env)); + MOZ_ASSERT(!IsSyntacticScope(env)); NativeObject* envobj; DependentAddPtr p(cx, environments, env); @@ -7505,7 +7505,7 @@ DebuggerEnv_checkThis(JSContext* cx, const CallArgs& args, const char* fnname, return false; \ Rooted env(cx, static_cast(envobj->getPrivate())); \ MOZ_ASSERT(env); \ - MOZ_ASSERT(IsValidTerminatingScope(env)); + MOZ_ASSERT(!IsSyntacticScope(env)); #define THIS_DEBUGENV_OWNER(cx, argc, vp, fnname, args, envobj, env, dbg) \ THIS_DEBUGENV(cx, argc, vp, fnname, args, envobj, env); \ diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index eadaef1d7d..32d296cbd3 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -13,6 +13,7 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/DebugOnly.h" #include "mozilla/FloatingPoint.h" +#include "mozilla/Maybe.h" #include "mozilla/PodOperations.h" #include @@ -53,12 +54,14 @@ #include "vm/ScopeObject-inl.h" #include "vm/Stack-inl.h" -#if defined(XP_UNIX) +#if defined(XP_MACOSX) +#include +#elif defined(XP_UNIX) #include #elif defined(XP_WIN) #include #include -#endif // defined(XP_UNIX) || defined(XP_WIN) +#endif // defined(XP_MACOSX) || defined(XP_UNIX) || defined(XP_WIN) using namespace js; using namespace js::gc; @@ -234,47 +237,22 @@ GetPropertyOperation(JSContext* cx, InterpreterFrame* fp, HandleScript script, j return true; } - RootedId id(cx, NameToId(script->getName(pc))); + RootedPropertyName name(cx, script->getName(pc)); - if (id == NameToId(cx->names().callee) && IsOptimizedArguments(fp, lval)) { + if (name == cx->names().callee && IsOptimizedArguments(fp, lval)) { vp.setObject(fp->callee()); return true; } - Rooted global(cx, &fp->global()); - RootedObject obj(cx); - - /* Optimize (.1).toString(). */ - if (lval.isNumber() && id == NameToId(cx->names().toString)) { - NativeObject* proto = GlobalObject::getOrCreateNumberPrototype(cx, global); - if (!proto) - return false; - if (ClassMethodIsNative(cx, proto, &NumberObject::class_, id, num_toString)) - obj = proto; + if (op == JSOP_CALLPROP) { + // The __noSuchMethod__ code in CallProperty requires non-aliasing + // v and vp arguments. + RootedValue v(cx, lval); + return CallProperty(cx, v, name, vp); } - if (!obj) { - obj = ToObjectFromStack(cx, lval); - if (!obj) - return false; - } - - bool wasObject = lval.isObject(); - - if (!GetProperty(cx, obj, obj, id, vp)) - return false; - -#if JS_HAS_NO_SUCH_METHOD - if (op == JSOP_CALLPROP && - MOZ_UNLIKELY(vp.isUndefined()) && - wasObject) - { - if (!OnUnknownMethod(cx, obj, IdToValue(id), vp)) - return false; - } -#endif - - return true; + MOZ_ASSERT(op == JSOP_GETPROP || op == JSOP_LENGTH); + return GetProperty(cx, lval, name, vp); } static inline bool @@ -518,31 +496,52 @@ struct AutoStopwatch final } } - // Get the OS-reported time spent in userland/systemland, - // in microseconds. - bool getTimes(uint64_t *userTime, uint64_t *systemTime) const { + // Get the OS-reported time spent in userland/systemland, in + // microseconds. On most platforms, this data is per-thread, + // but on some platforms we need to fall back to per-process. + bool getTimes(uint64_t* userTime, uint64_t* systemTime) const { MOZ_ASSERT(userTime); MOZ_ASSERT(systemTime); -#if defined(XP_UNIX) +#if defined(XP_MACOSX) + // On MacOS X, to get we per-thread data, we need to + // reach into the kernel. + mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; + thread_basic_info_data_t info; + mach_port_t port = mach_thread_self(); + kern_return_t err = + thread_info(/* [in] targeted thread*/ port, + /* [in] nature of information*/ THREAD_BASIC_INFO, + /* [out] thread information */ (thread_info_t)&info, + /* [inout] number of items */ &count); + + // We do not need ability to communicate with the thread, so + // let's release the port. + mach_port_deallocate(mach_task_self(), port); + + if (err != KERN_SUCCESS) + return false; + + *userTime = info.user_time.microseconds + info.user_time.seconds * 1000000; + *systemTime = info.system_time.microseconds + info.system_time.seconds * 1000000; + +#elif defined(XP_UNIX) struct rusage rusage; #if defined(RUSAGE_THREAD) // Under Linux, we can obtain per-thread statistics int err = getrusage(RUSAGE_THREAD, &rusage); #else - // Under other Unices, including MacOS X, we need to - // do with more noisy per-process statistics. + // Under other Unices, we need to do with more noisy + // per-process statistics. int err = getrusage(RUSAGE_SELF, &rusage); #endif // defined(RUSAGE_THREAD) - MOZ_ASSERT(!err); + if (err) return false; - *userTime = rusage.ru_utime.tv_usec - + rusage.ru_utime.tv_sec * 1000000; - *systemTime = rusage.ru_stime.tv_usec - + rusage.ru_stime.tv_sec * 1000000; + *userTime = rusage.ru_utime.tv_usec + rusage.ru_utime.tv_sec * 1000000; + *systemTime = rusage.ru_stime.tv_usec + rusage.ru_stime.tv_sec * 1000000; #elif defined(XP_WIN) // Under Windows, we can obtain per-thread statistics, @@ -555,7 +554,7 @@ struct AutoStopwatch final BOOL success = GetThreadTimes(GetCurrentThread(), &creationFileTime, &exitFileTime, &kernelFileTime, &userFileTime); - MOZ_ASSERT(success); + if (!success) return false; @@ -563,12 +562,15 @@ struct AutoStopwatch final ULARGE_INTEGER userTimeInt; kernelTimeInt.LowPart = kernelFileTime.dwLowDateTime; kernelTimeInt.HighPart = kernelFileTime.dwHighDateTime; - *systemTime = kernelTimeInt.QuadPart / 10; // 100 ns to 1 us + // Convert 100 ns to 1 us, make sure that the result is monotonic + *systemTime = runtime_-> stopwatch.systemTimeFix.monotonize(kernelTimeInt.QuadPart / 10); userTimeInt.LowPart = userFileTime.dwLowDateTime; userTimeInt.HighPart = userFileTime.dwHighDateTime; - *userTime = userTimeInt.QuadPart / 10; // 100 ns to 1 us -#endif // defined(XP_UNIX) || defined(XP_WIN) + // Convert 100 ns to 1 us, make sure that the result is monotonic + *userTime = runtime_-> stopwatch.userTimeFix.monotonize(userTimeInt.QuadPart / 10); + +#endif // defined(XP_MACOSX) || defined(XP_UNIX) || defined(XP_WIN) return true; } @@ -845,7 +847,7 @@ js::ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChainArg, c ExecuteType type, AbstractFramePtr evalInFrame, Value* result) { MOZ_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG); - MOZ_ASSERT_IF(type == EXECUTE_GLOBAL, IsValidTerminatingScope(&scopeChainArg)); + MOZ_ASSERT_IF(type == EXECUTE_GLOBAL, !IsSyntacticScope(&scopeChainArg)); #ifdef DEBUG if (thisv.isObject()) { RootedObject thisObj(cx, &thisv.toObject()); @@ -853,7 +855,7 @@ js::ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChainArg, c MOZ_ASSERT(GetOuterObject(cx, thisObj) == thisObj); } RootedObject terminatingScope(cx, &scopeChainArg); - while (!IsValidTerminatingScope(terminatingScope)) + while (IsSyntacticScope(terminatingScope)) terminatingScope = terminatingScope->enclosingScope(); MOZ_ASSERT(terminatingScope->is() || script->hasPollutedGlobalScope()); @@ -1672,6 +1674,58 @@ SetObjectElementOperation(JSContext* cx, HandleObject obj, HandleValue receiver, result.checkStrictErrorOrWarning(cx, obj, id, strict); } +/* + * As an optimization, the interpreter creates a handful of reserved Rooted + * variables at the beginning, thus inserting them into the Rooted list once + * upon entry. ReservedRooted "borrows" a reserved Rooted variable and uses it + * within a local scope, resetting the value to nullptr (or the appropriate + * equivalent for T) at scope end. This avoids inserting/removing the Rooted + * from the rooter list, while preventing stale values from being kept alive + * unnecessarily. + */ + +template +class ReservedRootedBase { +}; + +template +class ReservedRooted : public ReservedRootedBase +{ + Rooted* savedRoot; + + public: + ReservedRooted(Rooted* root, const T& ptr) : savedRoot(root) { + *root = ptr; + } + + explicit ReservedRooted(Rooted* root) : savedRoot(root) { + *root = js::GCMethods::initial(); + } + + ~ReservedRooted() { + *savedRoot = js::GCMethods::initial(); + } + + void set(const T& p) const { *savedRoot = p; } + operator Handle() { return *savedRoot; } + operator Rooted&() { return *savedRoot; } + MutableHandle operator&() { return &*savedRoot; } + + DECLARE_NONPOINTER_ACCESSOR_METHODS(savedRoot->get()) + DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(savedRoot->get()) + DECLARE_POINTER_CONSTREF_OPS(T) + DECLARE_POINTER_ASSIGN_OPS(ReservedRooted, T) +}; + +template <> +class ReservedRootedBase : public ValueOperations> +{ + friend class ValueOperations>; + const Value* extract() const { + return static_cast*>(this)->address(); + } +}; + static MOZ_NEVER_INLINE bool Interpret(JSContext* cx, RunState& state) { @@ -1932,7 +1986,6 @@ CASE(EnableInterruptsPseudoOpcode) /* Various 1-byte no-ops. */ CASE(JSOP_NOP) CASE(JSOP_UNUSED2) -CASE(JSOP_UNUSED148) CASE(JSOP_BACKPATCH) CASE(JSOP_UNUSED161) CASE(JSOP_UNUSED162) @@ -2055,11 +2108,9 @@ END_CASE(JSOP_SETRVAL) CASE(JSOP_ENTERWITH) { - RootedValue& val = rootValue0; - RootedObject& staticWith = rootObject0; - val = REGS.sp[-1]; + ReservedRooted val(&rootValue0, REGS.sp[-1]); REGS.sp--; - staticWith = script->getObject(REGS.pc); + ReservedRooted staticWith(&rootObject0, script->getObject(REGS.pc)); if (!EnterWithOperation(cx, REGS.fp(), val, staticWith)) goto error; @@ -2187,13 +2238,14 @@ CASE(JSOP_IN) ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rref, nullptr); goto error; } - RootedObject& obj = rootObject0; - obj = &rref.toObject(); - RootedId& id = rootId0; - FETCH_ELEMENT_ID(-2, id); bool found; - if (!HasProperty(cx, obj, id, &found)) - goto error; + { + ReservedRooted obj(&rootObject0, &rref.toObject()); + ReservedRooted id(&rootId0); + FETCH_ELEMENT_ID(-2, id); + if (!HasProperty(cx, obj, id, &found)) + goto error; + } TRY_BRANCH_AFTER_COND(found, 2); REGS.sp--; REGS.sp[-1].setBoolean(found); @@ -2216,8 +2268,7 @@ CASE(JSOP_MOREITER) MOZ_ASSERT(REGS.stackDepth() >= 1); MOZ_ASSERT(REGS.sp[-1].isObject()); PUSH_NULL(); - RootedObject& obj = rootObject0; - obj = ®S.sp[-2].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-2].toObject()); if (!IteratorMore(cx, obj, REGS.stackHandleAt(-1))) goto error; } @@ -2233,8 +2284,7 @@ END_CASE(JSOP_ISNOITER) CASE(JSOP_ENDITER) { MOZ_ASSERT(REGS.stackDepth() >= 1); - RootedObject& obj = rootObject0; - obj = ®S.sp[-1].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-1].toObject()); bool ok = CloseIterator(cx, obj); REGS.sp--; if (!ok) @@ -2281,14 +2331,9 @@ END_CASE(JSOP_PICK) CASE(JSOP_SETCONST) { - RootedPropertyName& name = rootName0; - name = script->getName(REGS.pc); - - RootedValue& rval = rootValue0; - rval = REGS.sp[-1]; - - RootedObject& obj = rootObject0; - obj = ®S.fp()->varObj(); + ReservedRooted name(&rootName0, script->getName(REGS.pc)); + ReservedRooted rval(&rootValue0, REGS.sp[-1]); + ReservedRooted obj(&rootObject0, ®S.fp()->varObj()); if (!SetConstOperation(cx, obj, name, rval)) goto error; @@ -2304,14 +2349,11 @@ CASE(JSOP_BINDNAME) { JSOp op = JSOp(*REGS.pc); if (op == JSOP_BINDNAME || script->hasPollutedGlobalScope()) { - RootedObject& scopeChain = rootObject0; - scopeChain = REGS.fp()->scopeChain(); - - RootedPropertyName& name = rootName0; - name = script->getName(REGS.pc); + ReservedRooted scopeChain(&rootObject0, REGS.fp()->scopeChain()); + ReservedRooted name(&rootName0, script->getName(REGS.pc)); /* Assigning to an undeclared name adds a property to the global object. */ - RootedObject& scope = rootObject1; + ReservedRooted scope(&rootObject1); if (!LookupNameUnqualified(cx, name, scopeChain, &scope)) goto error; @@ -2499,10 +2541,8 @@ END_CASE(JSOP_ADD) CASE(JSOP_SUB) { - RootedValue& lval = rootValue0; - RootedValue& rval = rootValue1; - lval = REGS.sp[-2]; - rval = REGS.sp[-1]; + ReservedRooted lval(&rootValue0, REGS.sp[-2]); + ReservedRooted rval(&rootValue1, REGS.sp[-1]); MutableHandleValue res = REGS.stackHandleAt(-2); if (!SubOperation(cx, lval, rval, res)) goto error; @@ -2512,10 +2552,8 @@ END_CASE(JSOP_SUB) CASE(JSOP_MUL) { - RootedValue& lval = rootValue0; - RootedValue& rval = rootValue1; - lval = REGS.sp[-2]; - rval = REGS.sp[-1]; + ReservedRooted lval(&rootValue0, REGS.sp[-2]); + ReservedRooted rval(&rootValue1, REGS.sp[-1]); MutableHandleValue res = REGS.stackHandleAt(-2); if (!MulOperation(cx, lval, rval, res)) goto error; @@ -2525,10 +2563,8 @@ END_CASE(JSOP_MUL) CASE(JSOP_DIV) { - RootedValue& lval = rootValue0; - RootedValue& rval = rootValue1; - lval = REGS.sp[-2]; - rval = REGS.sp[-1]; + ReservedRooted lval(&rootValue0, REGS.sp[-2]); + ReservedRooted rval(&rootValue1, REGS.sp[-1]); MutableHandleValue res = REGS.stackHandleAt(-2); if (!DivOperation(cx, lval, rval, res)) goto error; @@ -2538,10 +2574,8 @@ END_CASE(JSOP_DIV) CASE(JSOP_MOD) { - RootedValue& lval = rootValue0; - RootedValue& rval = rootValue1; - lval = REGS.sp[-2]; - rval = REGS.sp[-1]; + ReservedRooted lval(&rootValue0, REGS.sp[-2]); + ReservedRooted rval(&rootValue1, REGS.sp[-1]); MutableHandleValue res = REGS.stackHandleAt(-2); if (!ModOperation(cx, lval, rval, res)) goto error; @@ -2581,8 +2615,7 @@ END_CASE(JSOP_BITNOT) CASE(JSOP_NEG) { - RootedValue& val = rootValue0; - val = REGS.sp[-1]; + ReservedRooted val(&rootValue0, REGS.sp[-1]); MutableHandleValue res = REGS.stackHandleAt(-1); if (!NegOperation(cx, script, REGS.pc, val, res)) goto error; @@ -2596,11 +2629,8 @@ END_CASE(JSOP_POS) CASE(JSOP_DELNAME) { - RootedPropertyName& name = rootName0; - name = script->getName(REGS.pc); - - RootedObject& scopeObj = rootObject0; - scopeObj = REGS.fp()->scopeChain(); + ReservedRooted name(&rootName0, script->getName(REGS.pc)); + ReservedRooted scopeObj(&rootObject0, REGS.fp()->scopeChain()); PUSH_BOOLEAN(true); MutableHandleValue res = REGS.stackHandleAt(-1); @@ -2614,10 +2644,8 @@ CASE(JSOP_STRICTDELPROP) { static_assert(JSOP_DELPROP_LENGTH == JSOP_STRICTDELPROP_LENGTH, "delprop and strictdelprop must be the same size"); - RootedId& id = rootId0; - id = NameToId(script->getName(REGS.pc)); - - RootedObject& obj = rootObject0; + ReservedRooted id(&rootId0, NameToId(script->getName(REGS.pc))); + ReservedRooted obj(&rootObject0); FETCH_OBJECT(cx, -1, obj); ObjectOpResult result; @@ -2638,14 +2666,13 @@ CASE(JSOP_STRICTDELELEM) static_assert(JSOP_DELELEM_LENGTH == JSOP_STRICTDELELEM_LENGTH, "delelem and strictdelelem must be the same size"); /* Fetch the left part and resolve it to a non-null object. */ - RootedObject& obj = rootObject0; + ReservedRooted obj(&rootObject0); FETCH_OBJECT(cx, -2, obj); - RootedValue& propval = rootValue0; - propval = REGS.sp[-1]; + ReservedRooted propval(&rootValue0, REGS.sp[-1]); ObjectOpResult result; - RootedId& id = rootId0; + ReservedRooted id(&rootId0); if (!ValueToId(cx, propval, &id)) goto error; if (!DeleteProperty(cx, obj, id, result)) @@ -2668,11 +2695,8 @@ CASE(JSOP_TOID) * but we need to avoid the observable stringification the second time. * There must be an object value below the id, which will not be popped. */ - RootedValue& objval = rootValue0; - RootedValue& idval = rootValue1; - objval = REGS.sp[-2]; - idval = REGS.sp[-1]; - + ReservedRooted objval(&rootValue0, REGS.sp[-2]); + ReservedRooted idval(&rootValue1, REGS.sp[-1]); MutableHandleValue res = REGS.stackHandleAt(-1); if (!ToIdOperation(cx, script, REGS.pc, objval, idval, res)) goto error; @@ -2711,12 +2735,9 @@ END_CASE(JSOP_GETPROP) CASE(JSOP_GETPROP_SUPER) { - RootedObject& receiver = rootObject0; - RootedObject& obj = rootObject1; - + ReservedRooted receiver(&rootObject0); FETCH_OBJECT(cx, -2, receiver); - obj = ®S.sp[-1].toObject(); - + ReservedRooted obj(&rootObject1, ®S.sp[-1].toObject()); MutableHandleValue rref = REGS.stackHandleAt(-2); if (!GetProperty(cx, obj, receiver, script->getName(REGS.pc), rref)) @@ -2728,10 +2749,8 @@ END_CASE(JSOP_GETPROP_SUPER) CASE(JSOP_GETXPROP) { - RootedObject& obj = rootObject0; - obj = ®S.sp[-1].toObject(); - RootedId& id = rootId0; - id = NameToId(script->getName(REGS.pc)); + ReservedRooted obj(&rootObject0, ®S.sp[-1].toObject()); + ReservedRooted id(&rootId0, NameToId(script->getName(REGS.pc))); MutableHandleValue rval = REGS.stackHandleAt(-1); if (!GetPropertyForNameLookup(cx, obj, id, rval)) goto error; @@ -2765,8 +2784,7 @@ CASE(JSOP_STRICTSETNAME) static_assert(JSOP_SETNAME_LENGTH == JSOP_SETGNAME_LENGTH, "We're sharing the END_CASE so the lengths better match"); - RootedObject& scope = rootObject0; - scope = ®S.sp[-2].toObject(); + ReservedRooted scope(&rootObject0, ®S.sp[-2].toObject()); HandleValue value = REGS.stackHandleAt(-1); if (!SetNameOperation(cx, script, REGS.pc, scope, value)) @@ -2785,8 +2803,7 @@ CASE(JSOP_STRICTSETPROP) HandleValue lval = REGS.stackHandleAt(-2); HandleValue rval = REGS.stackHandleAt(-1); - RootedId& id = rootId0; - id = NameToId(script->getName(REGS.pc)); + ReservedRooted id(&rootId0, NameToId(script->getName(REGS.pc))); if (!SetPropertyOperation(cx, JSOp(*REGS.pc), lval, id, rval)) goto error; @@ -2802,17 +2819,10 @@ CASE(JSOP_STRICTSETPROP_SUPER) "setprop-super and strictsetprop-super must be the same size"); - RootedValue& receiver = rootValue0; - receiver = REGS.sp[-3]; - - RootedObject& obj = rootObject0; - obj = ®S.sp[-2].toObject(); - - RootedValue& rval = rootValue1; - rval = REGS.sp[-1]; - - RootedId& id = rootId0; - id = NameToId(script->getName(REGS.pc)); + ReservedRooted receiver(&rootValue0, REGS.sp[-3]); + ReservedRooted obj(&rootObject0, ®S.sp[-2].toObject()); + ReservedRooted rval(&rootValue1, REGS.sp[-1]); + ReservedRooted id(&rootId0, NameToId(script->getName(REGS.pc))); ObjectOpResult result; if (!SetProperty(cx, obj, id, rval, receiver, result)) @@ -2851,10 +2861,9 @@ END_CASE(JSOP_GETELEM) CASE(JSOP_GETELEM_SUPER) { HandleValue rval = REGS.stackHandleAt(-3); - RootedObject& receiver = rootObject0; + ReservedRooted receiver(&rootObject0); FETCH_OBJECT(cx, -2, receiver); - RootedObject& obj = rootObject1; - obj = ®S.sp[-1].toObject(); + ReservedRooted obj(&rootObject1, ®S.sp[-1].toObject()); MutableHandleValue res = REGS.stackHandleAt(-3); @@ -2874,13 +2883,12 @@ CASE(JSOP_STRICTSETELEM) { static_assert(JSOP_SETELEM_LENGTH == JSOP_STRICTSETELEM_LENGTH, "setelem and strictsetelem must be the same size"); - RootedObject& obj = rootObject0; + ReservedRooted obj(&rootObject0); FETCH_OBJECT(cx, -3, obj); - RootedId& id = rootId0; + ReservedRooted id(&rootId0); FETCH_ELEMENT_ID(-2, id); Value& value = REGS.sp[-1]; - RootedValue& receiver = rootValue0; - receiver = ObjectValue(*obj); + ReservedRooted receiver(&rootValue0, ObjectValue(*obj)); if (!SetObjectElementOperation(cx, obj, receiver, id, value, *REGS.pc == JSOP_STRICTSETELEM)) goto error; REGS.sp[-3] = value; @@ -2894,12 +2902,10 @@ CASE(JSOP_STRICTSETELEM_SUPER) static_assert(JSOP_SETELEM_SUPER_LENGTH == JSOP_STRICTSETELEM_SUPER_LENGTH, "setelem-super and strictsetelem-super must be the same size"); - RootedId& id = rootId0; + ReservedRooted id(&rootId0); FETCH_ELEMENT_ID(-4, id); - RootedValue& receiver = rootValue0; - receiver = REGS.sp[-3]; - RootedObject& obj = rootObject1; - obj = ®S.sp[-2].toObject(); + ReservedRooted receiver(&rootValue0, REGS.sp[-3]); + ReservedRooted obj(&rootObject1, ®S.sp[-2].toObject()); Value& value = REGS.sp[-1]; bool strict = JSOp(*REGS.pc) == JSOP_STRICTSETELEM_SUPER; @@ -2982,11 +2988,11 @@ CASE(JSOP_FUNCALL) MOZ_ASSERT(REGS.stackDepth() >= 2u + GET_ARGC(REGS.pc)); CallArgs args = CallArgsFromSp(argStackSlots, REGS.sp, construct); - RootedFunction& fun = rootFunction0; - bool isFunction = IsFunctionObject(args.calleev(), fun.address()); + JSFunction* maybeFun; + bool isFunction = IsFunctionObject(args.calleev(), &maybeFun); /* Don't bother trying to fast-path calls to scripted non-constructors. */ - if (!isFunction || !fun->isInterpreted() || !fun->isConstructor()) { + if (!isFunction || !maybeFun->isInterpreted() || !maybeFun->isConstructor()) { if (construct) { if (!InvokeConstructor(cx, args)) goto error; @@ -3000,27 +3006,30 @@ CASE(JSOP_FUNCALL) ADVANCE_AND_DISPATCH(JSOP_CALL_LENGTH); } - RootedScript& funScript = rootScript0; - funScript = fun->getOrCreateScript(cx); - if (!funScript) - goto error; - - InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE; - bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, REGS.pc); - - TypeMonitorCall(cx, args, construct); - { - InvokeState state(cx, args, initial); + MOZ_ASSERT(maybeFun); + ReservedRooted fun(&rootFunction0, maybeFun); + ReservedRooted funScript(&rootScript0, fun->getOrCreateScript(cx)); + if (!funScript) + goto error; + + InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE; + bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, REGS.pc); + + TypeMonitorCall(cx, args, construct); + + mozilla::Maybe state; + state.emplace(cx, args, initial); + if (createSingleton) - state.setCreateSingleton(); + state->setCreateSingleton(); if (!createSingleton && jit::IsIonEnabled(cx)) { - jit::MethodStatus status = jit::CanEnter(cx, state); + jit::MethodStatus status = jit::CanEnter(cx, state.ref()); if (status == jit::Method_Error) goto error; if (status == jit::Method_Compiled) { - jit::JitExecStatus exec = jit::IonCannon(cx, state); + jit::JitExecStatus exec = jit::IonCannon(cx, state.ref()); CHECK_BRANCH(); REGS.sp = args.spAfterCall(); interpReturnOK = !IsErrorStatus(exec); @@ -3029,26 +3038,28 @@ CASE(JSOP_FUNCALL) } if (jit::IsBaselineEnabled(cx)) { - jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state); + jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state.ref()); if (status == jit::Method_Error) goto error; if (status == jit::Method_Compiled) { - jit::JitExecStatus exec = jit::EnterBaselineMethod(cx, state); + jit::JitExecStatus exec = jit::EnterBaselineMethod(cx, state.ref()); CHECK_BRANCH(); REGS.sp = args.spAfterCall(); interpReturnOK = !IsErrorStatus(exec); goto jit_return; } } + + state.reset(); + funScript = fun->nonLazyScript(); + + if (!activation.pushInlineFrame(args, funScript, initial)) + goto error; + + if (createSingleton) + REGS.fp()->setCreateSingleton(); } - funScript = fun->nonLazyScript(); - if (!activation.pushInlineFrame(args, funScript, initial)) - goto error; - - if (createSingleton) - REGS.fp()->setCreateSingleton(); - SET_SCRIPT(REGS.fp()->script()); { @@ -3090,17 +3101,13 @@ CASE(JSOP_GIMPLICITTHIS) { JSOp op = JSOp(*REGS.pc); if (op == JSOP_IMPLICITTHIS || script->hasPollutedGlobalScope()) { - RootedPropertyName& name = rootName0; - name = script->getName(REGS.pc); - - RootedObject& scopeObj = rootObject0; - scopeObj = REGS.fp()->scopeChain(); - - RootedObject& scope = rootObject1; + ReservedRooted name(&rootName0, script->getName(REGS.pc)); + ReservedRooted scopeObj(&rootObject0, REGS.fp()->scopeChain()); + ReservedRooted scope(&rootObject1); if (!LookupNameWithGlobalDefault(cx, name, scopeObj, &scope)) goto error; - RootedValue& v = rootValue0; + ReservedRooted v(&rootValue0); if (!ComputeImplicitThis(cx, scope, &v)) goto error; PUSH_COPY(v); @@ -3116,8 +3123,7 @@ END_CASE(JSOP_IMPLICITTHIS) CASE(JSOP_GETGNAME) CASE(JSOP_GETNAME) { - RootedValue& rval = rootValue0; - + ReservedRooted rval(&rootValue0); if (!GetNameOperation(cx, REGS.fp(), REGS.pc, &rval)) goto error; @@ -3130,8 +3136,7 @@ END_CASE(JSOP_GETNAME) CASE(JSOP_GETINTRINSIC) { - RootedValue& rval = rootValue0; - + ReservedRooted rval(&rootValue0); if (!GetIntrinsicOperation(cx, REGS.pc, &rval)) goto error; @@ -3187,8 +3192,7 @@ END_CASE(JSOP_SYMBOL) CASE(JSOP_OBJECT) { - RootedObject& ref = rootObject0; - ref = script->getObject(REGS.pc); + ReservedRooted ref(&rootObject0, script->getObject(REGS.pc)); if (JS::CompartmentOptionsRef(cx).cloneSingletons()) { JSObject* obj = DeepCloneObjectLiteral(cx, ref, TenuredObject); if (!obj) @@ -3204,12 +3208,9 @@ END_CASE(JSOP_OBJECT) CASE(JSOP_CALLSITEOBJ) { - RootedObject& cso = rootObject0; - cso = script->getObject(REGS.pc); - RootedObject& raw = rootObject1; - raw = script->getObject(GET_UINT32_INDEX(REGS.pc) + 1); - RootedValue& rawValue = rootValue0; - rawValue.setObject(*raw); + ReservedRooted cso(&rootObject0, script->getObject(REGS.pc)); + ReservedRooted raw(&rootObject1, script->getObject(GET_UINT32_INDEX(REGS.pc) + 1)); + ReservedRooted rawValue(&rootValue0, ObjectValue(*raw)); if (!ProcessCallSiteObjOperation(cx, cso, raw, rawValue)) goto error; @@ -3308,8 +3309,7 @@ END_CASE(JSOP_RUNONCE) CASE(JSOP_REST) { - RootedObject& rest = rootObject0; - rest = REGS.fp()->createRestParameter(cx); + ReservedRooted rest(&rootObject0, REGS.fp()->createRestParameter(cx)); if (!rest) goto error; PUSH_COPY(ObjectValue(*rest)); @@ -3319,8 +3319,7 @@ END_CASE(JSOP_REST) CASE(JSOP_GETALIASEDVAR) { ScopeCoordinate sc = ScopeCoordinate(REGS.pc); - RootedValue& val = rootValue0; - val = REGS.fp()->aliasedVarScope(sc).aliasedVar(sc); + ReservedRooted val(&rootValue0, REGS.fp()->aliasedVarScope(sc).aliasedVar(sc)); MOZ_ASSERT(!IsUninitializedLexical(val)); PUSH_COPY(val); TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]); @@ -3338,8 +3337,7 @@ END_CASE(JSOP_SETALIASEDVAR) CASE(JSOP_CHECKLEXICAL) { uint32_t i = GET_LOCALNO(REGS.pc); - RootedValue& val = rootValue0; - val = REGS.fp()->unaliasedLocal(i); + ReservedRooted val(&rootValue0, REGS.fp()->unaliasedLocal(i)); if (!CheckUninitializedLexical(cx, script, REGS.pc, val)) goto error; } @@ -3355,8 +3353,7 @@ END_CASE(JSOP_INITLEXICAL) CASE(JSOP_CHECKALIASEDLEXICAL) { ScopeCoordinate sc = ScopeCoordinate(REGS.pc); - RootedValue& val = rootValue0; - val = REGS.fp()->aliasedVarScope(sc).aliasedVar(sc); + ReservedRooted val(&rootValue0, REGS.fp()->aliasedVarScope(sc).aliasedVar(sc)); if (!CheckUninitializedLexical(cx, script, REGS.pc, val)) goto error; } @@ -3430,11 +3427,8 @@ CASE(JSOP_DEFVAR) attrs |= JSPROP_PERMANENT; /* Step 8b. */ - RootedObject& obj = rootObject0; - obj = ®S.fp()->varObj(); - - RootedPropertyName& name = rootName0; - name = script->getName(REGS.pc); + ReservedRooted obj(&rootObject0, ®S.fp()->varObj()); + ReservedRooted name(&rootName0, script->getName(REGS.pc)); if (!DefVarOrConstOperation(cx, obj, name, attrs)) goto error; @@ -3449,9 +3443,7 @@ CASE(JSOP_DEFFUN) * a compound statement (not at the top statement level of global code, or * at the top level of a function body). */ - RootedFunction& fun = rootFunction0; - fun = script->getFunction(GET_UINT32_INDEX(REGS.pc)); - + ReservedRooted fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc))); if (!DefFunOperation(cx, script, REGS.fp()->scopeChain(), fun)) goto error; } @@ -3460,9 +3452,7 @@ END_CASE(JSOP_DEFFUN) CASE(JSOP_LAMBDA) { /* Load the specified function object literal. */ - RootedFunction& fun = rootFunction0; - fun = script->getFunction(GET_UINT32_INDEX(REGS.pc)); - + ReservedRooted fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc))); JSObject* obj = Lambda(cx, fun, REGS.fp()->scopeChain()); if (!obj) goto error; @@ -3474,10 +3464,8 @@ END_CASE(JSOP_LAMBDA) CASE(JSOP_LAMBDA_ARROW) { /* Load the specified function object literal. */ - RootedFunction& fun = rootFunction0; - fun = script->getFunction(GET_UINT32_INDEX(REGS.pc)); - RootedValue& thisv = rootValue0; - thisv = REGS.sp[-1]; + ReservedRooted fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc))); + ReservedRooted thisv(&rootValue0, REGS.sp[-1]); JSObject* obj = LambdaArrow(cx, fun, REGS.fp()->scopeChain(), thisv); if (!obj) goto error; @@ -3494,14 +3482,11 @@ END_CASE(JSOP_CALLEE) CASE(JSOP_INITPROP_GETTER) CASE(JSOP_INITPROP_SETTER) { - RootedObject& obj = rootObject0; - RootedPropertyName& name = rootName0; - RootedObject& val = rootObject1; - MOZ_ASSERT(REGS.stackDepth() >= 2); - obj = ®S.sp[-2].toObject(); - name = script->getName(REGS.pc); - val = ®S.sp[-1].toObject(); + + ReservedRooted obj(&rootObject0, ®S.sp[-2].toObject()); + ReservedRooted name(&rootName0, script->getName(REGS.pc)); + ReservedRooted val(&rootObject1, ®S.sp[-1].toObject()); if (!InitGetterSetterOperation(cx, REGS.pc, obj, name, val)) goto error; @@ -3513,14 +3498,11 @@ END_CASE(JSOP_INITPROP_GETTER) CASE(JSOP_INITELEM_GETTER) CASE(JSOP_INITELEM_SETTER) { - RootedObject& obj = rootObject0; - RootedValue& idval = rootValue0; - RootedObject& val = rootObject1; - MOZ_ASSERT(REGS.stackDepth() >= 3); - obj = ®S.sp[-3].toObject(); - idval = REGS.sp[-2]; - val = ®S.sp[-1].toObject(); + + ReservedRooted obj(&rootObject0, ®S.sp[-3].toObject()); + ReservedRooted idval(&rootValue0, REGS.sp[-2]); + ReservedRooted val(&rootObject1, ®S.sp[-1].toObject()); if (!InitGetterSetterOperation(cx, REGS.pc, obj, idval, val)) goto error; @@ -3562,13 +3544,11 @@ END_CASE(JSOP_NEWARRAY) CASE(JSOP_NEWARRAY_COPYONWRITE) { - RootedObject& baseobj = rootObject0; - baseobj = ObjectGroup::getOrFixupCopyOnWriteObject(cx, script, REGS.pc); + ReservedRooted baseobj(&rootObject0, ObjectGroup::getOrFixupCopyOnWriteObject(cx, script, REGS.pc)); if (!baseobj) goto error; - RootedObject& obj = rootObject1; - obj = NewDenseCopyOnWriteArray(cx, baseobj.as(), gc::DefaultHeap); + ReservedRooted obj(&rootObject1, NewDenseCopyOnWriteArray(cx, ((RootedObject&)(baseobj)).as(), gc::DefaultHeap)); if (!obj) goto error; @@ -3590,11 +3570,8 @@ CASE(JSOP_MUTATEPROTO) MOZ_ASSERT(REGS.stackDepth() >= 2); if (REGS.sp[-1].isObjectOrNull()) { - RootedObject& newProto = rootObject1; - rootObject1 = REGS.sp[-1].toObjectOrNull(); - - RootedObject& obj = rootObject0; - obj = ®S.sp[-2].toObject(); + ReservedRooted newProto(&rootObject1, REGS.sp[-1].toObjectOrNull()); + ReservedRooted obj(&rootObject0, ®S.sp[-2].toObject()); MOZ_ASSERT(obj->is()); if (!SetPrototype(cx, obj, newProto)) @@ -3615,12 +3592,10 @@ CASE(JSOP_INITHIDDENPROP) "initprop and inithiddenprop must be the same size"); /* Load the property's initial value into rval. */ MOZ_ASSERT(REGS.stackDepth() >= 2); - RootedValue& rval = rootValue0; - rval = REGS.sp[-1]; + ReservedRooted rval(&rootValue0, REGS.sp[-1]); /* Load the object being initialized into lval/obj. */ - RootedObject& obj = rootObject0; - obj = ®S.sp[-2].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-2].toObject()); PropertyName* name = script->getName(REGS.pc); @@ -3640,8 +3615,7 @@ CASE(JSOP_INITELEM) HandleValue val = REGS.stackHandleAt(-1); HandleValue id = REGS.stackHandleAt(-2); - RootedObject& obj = rootObject0; - obj = ®S.sp[-3].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-3].toObject()); if (!InitElemOperation(cx, obj, id, val)) goto error; @@ -3655,8 +3629,7 @@ CASE(JSOP_INITELEM_ARRAY) MOZ_ASSERT(REGS.stackDepth() >= 2); HandleValue val = REGS.stackHandleAt(-1); - RootedObject& obj = rootObject0; - obj = ®S.sp[-2].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-2].toObject()); uint32_t index = GET_UINT24(REGS.pc); if (!InitArrayElemOperation(cx, REGS.pc, obj, index, val)) @@ -3671,8 +3644,7 @@ CASE(JSOP_INITELEM_INC) MOZ_ASSERT(REGS.stackDepth() >= 3); HandleValue val = REGS.stackHandleAt(-1); - RootedObject& obj = rootObject0; - obj = ®S.sp[-3].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-3].toObject()); uint32_t index = REGS.sp[-2].toInt32(); if (!InitArrayElemOperation(cx, REGS.pc, obj, index, val)) @@ -3731,7 +3703,7 @@ END_CASE(JSOP_FINALLY) CASE(JSOP_THROWING) { - RootedValue& v = rootValue0; + ReservedRooted v(&rootValue0); POP_COPY_TO(v); MOZ_ALWAYS_TRUE(ThrowingOperation(cx, v)); } @@ -3740,7 +3712,7 @@ END_CASE(JSOP_THROWING) CASE(JSOP_THROW) { CHECK_BRANCH(); - RootedValue& v = rootValue0; + ReservedRooted v(&rootValue0); POP_COPY_TO(v); JS_ALWAYS_FALSE(Throw(cx, v)); /* let the code at error try to catch the exception. */ @@ -3749,14 +3721,12 @@ CASE(JSOP_THROW) CASE(JSOP_INSTANCEOF) { - RootedValue& rref = rootValue0; - rref = REGS.sp[-1]; - if (rref.isPrimitive()) { + ReservedRooted rref(&rootValue0, REGS.sp[-1]); + if (HandleValue(rref).isPrimitive()) { ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rref, nullptr); goto error; } - RootedObject& obj = rootObject0; - obj = &rref.toObject(); + ReservedRooted obj(&rootObject0, &rref.toObject()); bool cond = false; if (!HasInstance(cx, obj, REGS.stackHandleAt(-2), &cond)) goto error; @@ -3852,8 +3822,7 @@ CASE(JSOP_INITIALYIELD) { MOZ_ASSERT(!cx->isExceptionPending()); MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame()); - RootedObject& obj = rootObject0; - obj = ®S.sp[-1].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-1].toObject()); POP_RETURN_VALUE(); MOZ_ASSERT(REGS.stackDepth() == 0); if (!GeneratorObject::initialSuspend(cx, obj, REGS.fp(), REGS.pc)) @@ -3865,8 +3834,7 @@ CASE(JSOP_YIELD) { MOZ_ASSERT(!cx->isExceptionPending()); MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame()); - RootedObject& obj = rootObject0; - obj = ®S.sp[-1].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-1].toObject()); if (!GeneratorObject::normalSuspend(cx, obj, REGS.fp(), REGS.pc, REGS.spForStackDepth(0), REGS.stackDepth() - 2)) { @@ -3881,19 +3849,18 @@ CASE(JSOP_YIELD) CASE(JSOP_RESUME) { - RootedObject& gen = rootObject0; - RootedValue& val = rootValue0; - val = REGS.sp[-1]; - gen = ®S.sp[-2].toObject(); - // popInlineFrame expects there to be an additional value on the stack to - // pop off, so leave "gen" on the stack. - - GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(REGS.pc); - bool ok = GeneratorObject::resume(cx, activation, gen, val, resumeKind); - SET_SCRIPT(REGS.fp()->script()); - if (!ok) - goto error; + { + ReservedRooted gen(&rootObject0, ®S.sp[-2].toObject()); + ReservedRooted val(&rootValue0, REGS.sp[-1]); + // popInlineFrame expects there to be an additional value on the stack + // to pop off, so leave "gen" on the stack. + GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(REGS.pc); + bool ok = GeneratorObject::resume(cx, activation, gen, val, resumeKind); + SET_SCRIPT(REGS.fp()->script()); + if (!ok) + goto error; + } ADVANCE_AND_DISPATCH(0); } @@ -3907,8 +3874,7 @@ END_CASE(JSOP_DEBUGAFTERYIELD) CASE(JSOP_FINALYIELDRVAL) { - RootedObject& gen = rootObject0; - gen = ®S.sp[-1].toObject(); + ReservedRooted gen(&rootObject0, ®S.sp[-1].toObject()); REGS.sp--; if (!GeneratorObject::finalSuspend(cx, gen)) { @@ -3921,8 +3887,7 @@ CASE(JSOP_FINALYIELDRVAL) CASE(JSOP_ARRAYPUSH) { - RootedObject& obj = rootObject0; - obj = ®S.sp[-1].toObject(); + ReservedRooted obj(&rootObject0, ®S.sp[-1].toObject()); if (!NewbornArrayPush(cx, obj, REGS.sp[-2])) goto error; REGS.sp -= 2; @@ -3931,13 +3896,12 @@ END_CASE(JSOP_ARRAYPUSH) CASE(JSOP_CLASSHERITAGE) { - RootedValue& val = rootValue0; - val = REGS.sp[-1]; + ReservedRooted val(&rootValue0, REGS.sp[-1]); - RootedValue& objProto = rootValue1; - RootedObject& funcProto = rootObject0; + ReservedRooted objProto(&rootValue1); + ReservedRooted funcProto(&rootObject0); if (val.isNull()) { - objProto.setNull(); + objProto = NullValue(); if (!GetBuiltinPrototype(cx, JSProto_Function, &funcProto)) goto error; } else { @@ -3964,12 +3928,10 @@ END_CASE(JSOP_CLASSHERITAGE) CASE(JSOP_FUNWITHPROTO) { - RootedObject& proto = rootObject1; - proto = ®S.sp[-1].toObject(); + ReservedRooted proto(&rootObject1, ®S.sp[-1].toObject()); /* Load the specified function object literal. */ - RootedFunction& fun = rootFunction0; - fun = script->getFunction(GET_UINT32_INDEX(REGS.pc)); + ReservedRooted fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc))); JSObject* obj = CloneFunctionObjectIfNotSingleton(cx, fun, REGS.fp()->scopeChain(), proto, GenericObject); @@ -3982,8 +3944,7 @@ END_CASE(JSOP_FUNWITHPROTO) CASE(JSOP_OBJWITHPROTO) { - RootedObject& proto = rootObject0; - proto = REGS.sp[-1].toObjectOrNull(); + ReservedRooted proto(&rootObject0, REGS.sp[-1].toObjectOrNull()); JSObject* obj = NewObjectWithGivenProto(cx, proto); if (!obj) @@ -3999,14 +3960,13 @@ CASE(JSOP_INITHOMEOBJECT) MOZ_ASSERT(REGS.stackDepth() >= skipOver + 2); /* Load the function to be initialized */ - RootedFunction& func = rootFunction0; - func = ®S.sp[-1].toObject().as(); + ReservedRooted func(&rootFunction0, ®S.sp[-1].toObject().as()); MOZ_ASSERT(func->allowSuperProperty()); /* Load the home object */ - RootedNativeObject& obj = rootNativeObject0; - obj = ®S.sp[int(-2 - skipOver)].toObject().as(); - MOZ_ASSERT(obj->is() || obj->is()); + ReservedRooted obj(&rootObject0); + obj = ®S.sp[int(-2 - skipOver)].toObject(); + MOZ_ASSERT(obj->is() || obj->is() || obj->is()); func->setExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT, ObjectValue(*obj)); } @@ -4029,10 +3989,8 @@ CASE(JSOP_SUPERBASE) MOZ_ASSERT(callee.nonLazyScript()->needsHomeObject()); const Value& homeObjVal = callee.getExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT); - RootedObject& homeObj = rootObject0; - homeObj = &homeObjVal.toObject(); - - RootedObject& superBase = rootObject1; + ReservedRooted homeObj(&rootObject0, &homeObjVal.toObject()); + ReservedRooted superBase(&rootObject1); if (!GetPrototype(cx, homeObj, &superBase)) goto error; @@ -4050,6 +4008,10 @@ CASE(JSOP_SUPERBASE) } END_CASE(JSOP_SUPERBASE) +CASE(JSOP_NEWTARGET) + PUSH_COPY(REGS.fp()->newTarget()); +END_CASE(JSOP_NEWTARGET) + DEFAULT() { char numBuf[12]; @@ -4075,12 +4037,12 @@ DEFAULT() case CatchContinuation: ADVANCE_AND_DISPATCH(0); - case FinallyContinuation: + case FinallyContinuation: { /* * Push (true, exception) pair for finally to indicate that [retsub] * should rethrow the exception. */ - RootedValue& exception = rootValue0; + ReservedRooted exception(&rootValue0); if (!cx->getPendingException(&exception)) { interpReturnOK = false; goto return_continuation; @@ -4088,7 +4050,8 @@ DEFAULT() PUSH_BOOLEAN(true); PUSH_COPY(exception); cx->clearPendingException(); - ADVANCE_AND_DISPATCH(0); + } + ADVANCE_AND_DISPATCH(0); } MOZ_CRASH("Invalid HandleError continuation"); @@ -4143,21 +4106,45 @@ js::GetProperty(JSContext* cx, HandleValue v, HandlePropertyName name, MutableHa return true; } + // Optimize common cases like (2).toString() or "foo".valueOf() to not + // create a wrapper object. + if (v.isPrimitive() && !v.isNullOrUndefined()) { + NativeObject* proto; + if (v.isNumber()) { + proto = GlobalObject::getOrCreateNumberPrototype(cx, cx->global()); + } else if (v.isString()) { + proto = GlobalObject::getOrCreateStringPrototype(cx, cx->global()); + } else if (v.isBoolean()) { + proto = GlobalObject::getOrCreateBooleanPrototype(cx, cx->global()); + } else { + MOZ_ASSERT(v.isSymbol()); + proto = GlobalObject::getOrCreateSymbolPrototype(cx, cx->global()); + } + if (!proto) + return false; + + if (GetPropertyPure(cx, proto, NameToId(name), vp.address())) + return true; + } + RootedObject obj(cx, ToObjectFromStack(cx, v)); if (!obj) return false; + return GetProperty(cx, obj, obj, name, vp); } bool js::CallProperty(JSContext* cx, HandleValue v, HandlePropertyName name, MutableHandleValue vp) { + // __noSuchMethod__ code below depends on this. + MOZ_ASSERT(v.address() != vp.address()); + if (!GetProperty(cx, v, name, vp)) return false; #if JS_HAS_NO_SUCH_METHOD - if (MOZ_UNLIKELY(vp.isUndefined()) && v.isObject()) - { + if (MOZ_UNLIKELY(vp.isUndefined()) && v.isObject()) { RootedObject obj(cx, &v.toObject()); if (!OnUnknownMethod(cx, obj, StringValue(name), vp)) return false; diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h index 68f11bd4c4..e9f9d3f91b 100644 --- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -330,7 +330,8 @@ class ObjectGroup : public gc::TenuredCell } bool hasUnanalyzedPreliminaryObjects() { - return (newScript() && !newScript()->analyzed()) || maybePreliminaryObjects(); + return (newScriptDontCheckGeneration() && !newScriptDontCheckGeneration()->analyzed()) || + maybePreliminaryObjectsDontCheckGeneration(); } UnboxedLayout* maybeUnboxedLayout() { diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index d2f68a800a..e119ac0f99 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -1514,8 +1514,15 @@ * Stack: obj, val => obj */ \ macro(JSOP_INITHIDDENPROP, 147,"inithiddenprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \ - /* Unused. */ \ - macro(JSOP_UNUSED148, 148,"unused148", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Push "new.target" + * + * Category: Variables and Scopes + * Type: Arguments + * Operands: + * Stack: => new.target + */ \ + macro(JSOP_NEWTARGET, 148, "newtarget", NULL, 1, 0, 1, JOF_BYTE) \ \ /* * Placeholder opcode used during bytecode generation. This never diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 6065f7dced..64ac450901 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -1521,6 +1521,28 @@ struct JSRuntime : public JS::shadow::Runtime, return isActive_; } + // Some systems have non-monotonic clocks. While we cannot + // improve the precision, we can make sure that our measures + // are monotonic nevertheless. We do this by storing the + // result of the latest call to the clock and making sure + // that the next timestamp is greater or equal. + struct MonotonicTimeStamp { + MonotonicTimeStamp() + : latestGood_(0) + {} + inline uint64_t monotonize(uint64_t stamp) + { + if (stamp <= latestGood_) + return latestGood_; + latestGood_ = stamp; + return stamp; + } + private: + uint64_t latestGood_; + }; + MonotonicTimeStamp systemTimeFix; + MonotonicTimeStamp userTimeFix; + private: /** * A map used to collapse compartments belonging to the same diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index 520584ecd8..dff205df4d 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -2501,12 +2501,12 @@ GetDebugScopeForMissing(JSContext* cx, const ScopeIter& si) static JSObject * GetDebugScopeForNonScopeObject(const ScopeIter &si) { - JSObject& enclosing = si.enclosingScope(); - MOZ_ASSERT(IsValidTerminatingScope(&enclosing)); + JSObject &enclosing = si.enclosingScope(); + MOZ_ASSERT(!IsSyntacticScope(&enclosing)); #ifdef DEBUG JSObject *o = &enclosing; while ((o = o->enclosingScope())) - MOZ_ASSERT(!o->is()); + MOZ_ASSERT(!IsSyntacticScope(o)); #endif return &enclosing; } diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index 30950f397b..0a311e20e2 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -1038,11 +1038,11 @@ JSObject::is() const namespace js { inline bool -IsValidTerminatingScope(JSObject* scope) +IsSyntacticScope(JSObject* scope) { - return !scope->is() || - (scope->is() && - !scope->as().isSyntactic()); + return scope->is() && + (!scope->is() || + scope->as().isSyntactic()); } inline const Value & @@ -1086,7 +1086,7 @@ ScopeIter::enclosingScope() const // chain; every scope chain must start with zero or more ScopeObjects and // terminate with one or more non-ScopeObjects (viz., GlobalObject). MOZ_ASSERT(done()); - MOZ_ASSERT(IsValidTerminatingScope(scope_)); + MOZ_ASSERT(!IsSyntacticScope(scope_)); return *scope_; } diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 5753d18f58..455ede5946 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -6,6 +6,8 @@ #include "vm/SelfHosting.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/Casting.h" #include "mozilla/DebugOnly.h" #include "jscntxt.h" @@ -41,6 +43,8 @@ using namespace js; using namespace js::selfhosted; using JS::AutoCheckCannotGC; +using mozilla::IsInRange; +using mozilla::PodMove; using mozilla::UniquePtr; static void @@ -642,6 +646,412 @@ intrinsic_StarGeneratorObjectIsClosed(JSContext* cx, unsigned argc, Value* vp) return true; } +bool +js::intrinsic_MoveTypedArrayElements(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 4); + + Rooted tarray(cx, &args[0].toObject().as()); + uint32_t to = uint32_t(args[1].toInt32()); + uint32_t from = uint32_t(args[2].toInt32()); + uint32_t count = uint32_t(args[3].toInt32()); + + MOZ_ASSERT(count > 0, + "don't call this method if copying no elements, because then " + "the not-neutered requirement is wrong"); + + if (tarray->hasBuffer() && tarray->buffer()->isNeutered()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); + return false; + } + + // Don't multiply by |tarray->bytesPerElement()| in case the compiler can't + // strength-reduce multiplication by 1/2/4/8 into the equivalent shift. + const size_t ElementShift = TypedArrayShift(tarray->type()); + + MOZ_ASSERT((UINT32_MAX >> ElementShift) > to); + uint32_t byteDest = to << ElementShift; + + MOZ_ASSERT((UINT32_MAX >> ElementShift) > from); + uint32_t byteSrc = from << ElementShift; + + MOZ_ASSERT((UINT32_MAX >> ElementShift) >= count); + uint32_t byteSize = count << ElementShift; + +#ifdef DEBUG + { + uint32_t viewByteLength = tarray->byteLength(); + MOZ_ASSERT(byteSize <= viewByteLength); + MOZ_ASSERT(byteDest < viewByteLength); + MOZ_ASSERT(byteSrc < viewByteLength); + MOZ_ASSERT(byteDest <= viewByteLength - byteSize); + MOZ_ASSERT(byteSrc <= viewByteLength - byteSize); + } +#endif + + uint8_t* data = static_cast(tarray->viewData()); + PodMove(&data[byteDest], &data[byteSrc], byteSize); + + args.rval().setUndefined(); + return true; +} + +// Extract the TypedArrayObject* underlying |obj| and return it. This method, +// in a TOTALLY UNSAFE manner, completely violates the normal compartment +// boundaries, returning an object not necessarily in the current compartment +// or in |obj|'s compartment. +// +// All callers of this method are expected to sigil this TypedArrayObject*, and +// all values and information derived from it, with an "unsafe" prefix, to +// indicate the extreme caution required when dealing with such values. +// +// If calling code discipline ever fails to be maintained, it's gonna have a +// bad time. +static TypedArrayObject* +DangerouslyUnwrapTypedArray(JSContext* cx, JSObject* obj) +{ + // An unwrapped pointer to an object potentially on the other side of a + // compartment boundary! Isn't this such fun? + JSObject* unwrapped = CheckedUnwrap(obj); + if (!unwrapped->is()) { + // By *appearances* this can't happen, as self-hosted TypedArraySet + // checked this. But. Who's to say a GC couldn't happen between + // the check that this value was a typed array, and this extraction + // occurring? A GC might turn a cross-compartment wrapper |obj| into + // |unwrapped == obj|, a dead object no longer connected its typed + // array. + // + // Yeah, yeah, it's pretty unlikely. Are you willing to stake a + // sec-critical bug on that assessment, now and forever, against + // all changes those pesky GC and JIT people might make? + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return nullptr; + } + + // Be super-duper careful using this, as we've just punched through + // the compartment boundary, and things like buffer() on this aren't + // same-compartment with anything else in the calling method. + return &unwrapped->as(); +} + +// ES6 draft 20150403 22.2.3.22.2, steps 12-24, 29. +bool +js::intrinsic_SetFromTypedArrayApproach(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 4); + + Rooted target(cx, &args[0].toObject().as()); + MOZ_ASSERT(!target->hasBuffer() || !target->buffer()->isNeutered(), + "something should have defended against a neutered target"); + + // As directed by |DangerouslyUnwrapTypedArray|, sigil this pointer and all + // variables derived from it to counsel extreme caution here. + Rooted unsafeTypedArrayCrossCompartment(cx); + unsafeTypedArrayCrossCompartment = DangerouslyUnwrapTypedArray(cx, &args[1].toObject()); + if (!unsafeTypedArrayCrossCompartment) + return false; + + double doubleTargetOffset = args[2].toNumber(); + MOZ_ASSERT(doubleTargetOffset >= 0, "caller failed to ensure |targetOffset >= 0|"); + + uint32_t targetLength = uint32_t(args[3].toInt32()); + + // Handle all checks preceding the actual element-setting. A visual skim + // of 22.2.3.22.2 should confirm these are the only steps after steps 1-11 + // that might abort processing (other than for reason of internal error.) + + // Steps 12-13. + if (unsafeTypedArrayCrossCompartment->hasBuffer() && + unsafeTypedArrayCrossCompartment->buffer()->isNeutered()) + { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return false; + } + + // Steps 21, 23. + uint32_t unsafeSrcLengthCrossCompartment = unsafeTypedArrayCrossCompartment->length(); + if (unsafeSrcLengthCrossCompartment + doubleTargetOffset > targetLength) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX); + return false; + } + + // Now that that's confirmed, we can use |targetOffset| of a sane type. + uint32_t targetOffset = uint32_t(doubleTargetOffset); + + // The remaining steps are unobservable *except* through their effect on + // which elements are copied and how. + + Scalar::Type targetType = target->type(); + Scalar::Type unsafeSrcTypeCrossCompartment = unsafeTypedArrayCrossCompartment->type(); + + size_t targetElementSize = TypedArrayElemSize(targetType); + uint8_t* targetData = + static_cast(target->viewData()) + targetOffset * targetElementSize; + + uint8_t* unsafeSrcDataCrossCompartment = + static_cast(unsafeTypedArrayCrossCompartment->viewData()); + + uint32_t unsafeSrcElementSizeCrossCompartment = + TypedArrayElemSize(unsafeSrcTypeCrossCompartment); + uint32_t unsafeSrcByteLengthCrossCompartment = + unsafeSrcLengthCrossCompartment * unsafeSrcElementSizeCrossCompartment; + + // Step 29. + // + // The same-type case requires exact copying preserving the bit-level + // encoding of the source data, so move the values. (We could PodCopy if + // we knew the buffers differed, but it's doubtful the work to check + // wouldn't swap any minor wins PodCopy would afford. Because of the + // TOTALLY UNSAFE CROSS-COMPARTMENT NONSENSE here, comparing buffer + // pointers directly could give an incorrect answer.) If this occurs, + // the %TypedArray%.prototype.set operation is completely finished. + if (targetType == unsafeSrcTypeCrossCompartment) { + PodMove(targetData, unsafeSrcDataCrossCompartment, unsafeSrcByteLengthCrossCompartment); + args.rval().setInt32(JS_SETTYPEDARRAY_SAME_TYPE); + return true; + } + + // Every other bit of element-copying is handled by step 28. Indicate + // whether such copying must take care not to overlap, so that self-hosted + // code may correctly perform the copying. + + uint8_t* unsafeSrcDataLimitCrossCompartment = + unsafeSrcDataCrossCompartment + unsafeSrcByteLengthCrossCompartment; + uint8_t* targetDataLimit = + static_cast(target->viewData()) + targetLength * targetElementSize; + + // Step 24 test (but not steps 24a-d -- the caller handles those). + bool overlap = + IsInRange(targetData, unsafeSrcDataCrossCompartment, unsafeSrcDataLimitCrossCompartment) || + IsInRange(unsafeSrcDataCrossCompartment, targetData, targetDataLimit); + + args.rval().setInt32(overlap ? JS_SETTYPEDARRAY_OVERLAPPING : JS_SETTYPEDARRAY_DISJOINT); + return true; +} + +template +static void +CopyValues(To* dest, const From* src, uint32_t count) +{ +#ifdef DEBUG + void* destVoid = static_cast(dest); + void* destVoidEnd = static_cast(dest + count); + const void* srcVoid = static_cast(src); + const void* srcVoidEnd = static_cast(src + count); + MOZ_ASSERT(!IsInRange(destVoid, srcVoid, srcVoidEnd)); + MOZ_ASSERT(!IsInRange(srcVoid, destVoid, destVoidEnd)); +#endif + + for (; count > 0; count--) + *dest++ = To(*src++); +} + +struct DisjointElements +{ + template + static void + copy(To* dest, const void* src, Scalar::Type fromType, uint32_t count) { + switch (fromType) { + case Scalar::Int8: + CopyValues(dest, static_cast(src), count); + return; + + case Scalar::Uint8: + CopyValues(dest, static_cast(src), count); + return; + + case Scalar::Int16: + CopyValues(dest, static_cast(src), count); + return; + + case Scalar::Uint16: + CopyValues(dest, static_cast(src), count); + return; + + case Scalar::Int32: + CopyValues(dest, static_cast(src), count); + return; + + case Scalar::Uint32: + CopyValues(dest, static_cast(src), count); + return; + + case Scalar::Float32: + CopyValues(dest, static_cast(src), count); + return; + + case Scalar::Float64: + CopyValues(dest, static_cast(src), count); + return; + + case Scalar::Uint8Clamped: + CopyValues(dest, static_cast(src), count); + return; + + default: + MOZ_CRASH("NonoverlappingSet with bogus from-type"); + } + } +}; + +static void +CopyToDisjointArray(TypedArrayObject* target, uint32_t targetOffset, const void* src, + Scalar::Type srcType, uint32_t count) +{ + Scalar::Type destType = target->type(); + void* dest = + static_cast(target->viewData()) + targetOffset * TypedArrayElemSize(destType); + + switch (destType) { + case Scalar::Int8: { + int8_t* dst = reinterpret_cast(dest); + DisjointElements::copy(dst, src, srcType, count); + break; + } + + case Scalar::Uint8: { + uint8_t* dst = reinterpret_cast(dest); + DisjointElements::copy(dst, src, srcType, count); + break; + } + + case Scalar::Int16: { + int16_t* dst = reinterpret_cast(dest); + DisjointElements::copy(dst, src, srcType, count); + break; + } + + case Scalar::Uint16: { + uint16_t* dst = reinterpret_cast(dest); + DisjointElements::copy(dst, src, srcType, count); + break; + } + + case Scalar::Int32: { + int32_t* dst = reinterpret_cast(dest); + DisjointElements::copy(dst, src, srcType, count); + break; + } + + case Scalar::Uint32: { + uint32_t* dst = reinterpret_cast(dest); + DisjointElements::copy(dst, src, srcType, count); + break; + } + + case Scalar::Float32: { + float* dst = reinterpret_cast(dest); + DisjointElements::copy(dst, src, srcType, count); + break; + } + + case Scalar::Float64: { + double* dst = reinterpret_cast(dest); + DisjointElements::copy(dst, src, srcType, count); + break; + } + + case Scalar::Uint8Clamped: { + uint8_clamped* dst = reinterpret_cast(dest); + DisjointElements::copy(dst, src, srcType, count); + break; + } + + default: + MOZ_CRASH("setFromAnyTypedArray with a typed array with bogus type"); + } +} + +// |unsafeSrcCrossCompartment| is produced by |DangerouslyUnwrapTypedArray|, +// counseling extreme caution when using it. As directed by +// |DangerouslyUnwrapTypedArray|, sigil this pointer and all variables derived +// from it to counsel extreme caution here. +void +js::SetDisjointTypedElements(TypedArrayObject* target, uint32_t targetOffset, + TypedArrayObject* unsafeSrcCrossCompartment) +{ + Scalar::Type unsafeSrcTypeCrossCompartment = unsafeSrcCrossCompartment->type(); + + const void* unsafeSrcDataCrossCompartment = unsafeSrcCrossCompartment->viewData(); + uint32_t count = unsafeSrcCrossCompartment->length(); + + CopyToDisjointArray(target, targetOffset, + unsafeSrcDataCrossCompartment, unsafeSrcTypeCrossCompartment, count); +} + +bool +js::intrinsic_SetDisjointTypedElements(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 3); + + Rooted target(cx, &args[0].toObject().as()); + MOZ_ASSERT(!target->hasBuffer() || !target->buffer()->isNeutered(), + "a neutered typed array has no elements to set, so " + "it's nonsensical to be setting them"); + + uint32_t targetOffset = uint32_t(args[1].toInt32()); + + // As directed by |DangerouslyUnwrapTypedArray|, sigil this pointer and all + // variables derived from it to counsel extreme caution here. + Rooted unsafeSrcCrossCompartment(cx); + unsafeSrcCrossCompartment = DangerouslyUnwrapTypedArray(cx, &args[2].toObject()); + if (!unsafeSrcCrossCompartment) + return false; + + SetDisjointTypedElements(target, targetOffset, unsafeSrcCrossCompartment); + + args.rval().setUndefined(); + return true; +} + +bool +js::intrinsic_SetOverlappingTypedElements(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 3); + + Rooted target(cx, &args[0].toObject().as()); + MOZ_ASSERT(!target->hasBuffer() || !target->buffer()->isNeutered(), + "shouldn't be setting elements if neutered"); + + uint32_t targetOffset = uint32_t(args[1].toInt32()); + + // As directed by |DangerouslyUnwrapTypedArray|, sigil this pointer and all + // variables derived from it to counsel extreme caution here. + Rooted unsafeSrcCrossCompartment(cx); + unsafeSrcCrossCompartment = DangerouslyUnwrapTypedArray(cx, &args[2].toObject()); + if (!unsafeSrcCrossCompartment) + return false; + + // Smarter algorithms exist to perform overlapping transfers of the sort + // this method performs (for example, v8's self-hosted implementation). + // But it seems likely deliberate overlapping transfers are rare enough + // that it's not worth the trouble to implement one (and worry about its + // safety/correctness!). Make a copy and do a disjoint set from that. + uint32_t count = unsafeSrcCrossCompartment->length(); + Scalar::Type unsafeSrcTypeCrossCompartment = unsafeSrcCrossCompartment->type(); + size_t sourceByteLen = count * TypedArrayElemSize(unsafeSrcTypeCrossCompartment); + + const void* unsafeSrcDataCrossCompartment = unsafeSrcCrossCompartment->viewData(); + + auto copyOfSrcData = target->zone()->make_pod_array(sourceByteLen); + if (!copyOfSrcData) + return false; + + mozilla::PodCopy(copyOfSrcData.get(), + static_cast(unsafeSrcDataCrossCompartment), + sourceByteLen); + + CopyToDisjointArray(target, targetOffset, copyOfSrcData.get(), + unsafeSrcTypeCrossCompartment, count); + + args.rval().setUndefined(); + return true; +} + bool js::intrinsic_IsSuspendedStarGenerator(JSContext* cx, unsigned argc, Value* vp) { @@ -726,6 +1136,17 @@ intrinsic_GeneratorSetClosed(JSContext* cx, unsigned argc, Value* vp) return true; } +bool +js::intrinsic_IsArrayBuffer(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + args.rval().setBoolean(args[0].toObject().is()); + return true; +} + bool js::intrinsic_IsTypedArray(JSContext* cx, unsigned argc, Value* vp) { @@ -733,8 +1154,68 @@ js::intrinsic_IsTypedArray(JSContext* cx, unsigned argc, Value* vp) MOZ_ASSERT(args.length() == 1); MOZ_ASSERT(args[0].isObject()); - RootedObject obj(cx, &args[0].toObject()); - args.rval().setBoolean(obj->is()); + args.rval().setBoolean(args[0].toObject().is()); + return true; +} + +bool +js::intrinsic_IsPossiblyWrappedTypedArray(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + + bool isTypedArray = false; + if (args[0].isObject()) { + JSObject* obj = CheckedUnwrap(&args[0].toObject()); + if (!obj) { + JS_ReportError(cx, "Permission denied to access object"); + return false; + } + + isTypedArray = obj->is(); + } + + args.rval().setBoolean(isTypedArray); + return true; +} + +bool +js::intrinsic_TypedArrayBuffer(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(TypedArrayObject::is(args[0])); + + Rooted tarray(cx, &args[0].toObject().as()); + if (!TypedArrayObject::ensureHasBuffer(cx, tarray)) + return false; + + args.rval().set(TypedArrayObject::bufferValue(tarray)); + return true; +} + +bool +js::intrinsic_TypedArrayByteOffset(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(TypedArrayObject::is(args[0])); + + args.rval().set(TypedArrayObject::byteOffsetValue(&args[0].toObject().as())); + return true; +} + +bool +js::intrinsic_TypedArrayElementShift(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(TypedArrayObject::is(args[0])); + + unsigned shift = TypedArrayShift(args[0].toObject().as().type()); + MOZ_ASSERT(shift == 0 || shift == 1 || shift == 2 || shift == 3); + + args.rval().setInt32(mozilla::AssertedCast(shift)); return true; } @@ -990,9 +1471,20 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("GeneratorIsRunning", intrinsic_GeneratorIsRunning, 1,0), JS_FN("GeneratorSetClosed", intrinsic_GeneratorSetClosed, 1,0), + JS_FN("IsArrayBuffer", intrinsic_IsArrayBuffer, 1,0), + JS_FN("IsTypedArray", intrinsic_IsTypedArray, 1,0), + JS_FN("IsPossiblyWrappedTypedArray",intrinsic_IsPossiblyWrappedTypedArray,1,0), + JS_FN("TypedArrayBuffer", intrinsic_TypedArrayBuffer, 1,0), + JS_FN("TypedArrayByteOffset", intrinsic_TypedArrayByteOffset, 1,0), + JS_FN("TypedArrayElementShift", intrinsic_TypedArrayElementShift, 1,0), JS_FN("TypedArrayLength", intrinsic_TypedArrayLength, 1,0), + JS_FN("MoveTypedArrayElements", intrinsic_MoveTypedArrayElements, 4,0), + JS_FN("SetFromTypedArrayApproach",intrinsic_SetFromTypedArrayApproach, 4, 0), + JS_FN("SetDisjointTypedElements",intrinsic_SetDisjointTypedElements,3,0), + JS_FN("SetOverlappingTypedElements",intrinsic_SetOverlappingTypedElements,3,0), + JS_FN("CallTypedArrayMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2, 0), diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 8496bc271c..6993caeb8a 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -172,7 +172,7 @@ AssertDynamicScopeMatchesStaticScope(JSContext* cx, JSScript* script, JSObject* // The scope chain is always ended by one or more non-syntactic // ScopeObjects (viz. GlobalObject or a non-syntactic WithObject). - MOZ_ASSERT(IsValidTerminatingScope(scope)); + MOZ_ASSERT(!IsSyntacticScope(scope)); #endif } @@ -239,7 +239,7 @@ InterpreterFrame::epilogue(JSContext* cx) DebugScopes::onPopStrictEvalScope(this); } else if (isDirectEvalFrame()) { if (isDebuggerEvalFrame()) - MOZ_ASSERT(IsValidTerminatingScope(scopeChain())); + MOZ_ASSERT(!IsSyntacticScope(scopeChain())); } else { /* * Debugger.Object.prototype.evalInGlobal creates indirect eval @@ -259,7 +259,7 @@ InterpreterFrame::epilogue(JSContext* cx) } if (isGlobalFrame()) { - MOZ_ASSERT(IsValidTerminatingScope(scopeChain())); + MOZ_ASSERT(!IsSyntacticScope(scopeChain())); return; } diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 2509b33b8f..fecaf164b0 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -12,6 +12,7 @@ #include "jsfun.h" #include "jsscript.h" +#include "jsutil.h" #include "asmjs/AsmJSFrameIterator.h" #include "jit/JitFrameIterator.h" @@ -734,6 +735,23 @@ class InterpreterFrame return CallReceiverFromArgv(argv()); } + /* + * New Target + * + * Only function frames have a meaningful newTarget. An eval frame in a + * function will have a copy of the newTarget of the enclosing function + * frame. + */ + Value newTarget() const { + // new.target in eval() NYI. + MOZ_ASSERT(isNonEvalFunctionFrame()); + if (isConstructing()) { + unsigned pushedArgs = Max(numFormalArgs(), numActualArgs()); + return argv()[pushedArgs]; + } + return UndefinedValue(); + } + /* * Frame compartment * diff --git a/js/src/vm/TypedArrayCommon.h b/js/src/vm/TypedArrayCommon.h index d3b8120f51..5e285b39cb 100644 --- a/js/src/vm/TypedArrayCommon.h +++ b/js/src/vm/TypedArrayCommon.h @@ -481,7 +481,12 @@ class TypedArrayMethods typedef typename SomeTypedArray::template OfType::Type Uint8ClampedArrayType; public: - /* subarray(start[, end]) */ + // subarray(start[, end]) + // %TypedArray%.prototype.subarray is a self-hosted method, so this code is + // only used for shared typed arrays. We should self-host both methods + // eventually (but note TypedArraySubarray will require changes to be used + // with shared typed arrays), but we need to rejigger the shared typed + // array prototype chain before we can do that. static bool subarray(JSContext* cx, const CallArgs& args) { @@ -569,6 +574,11 @@ class TypedArrayMethods /* copyWithin(target, start[, end]) */ // ES6 draft rev 26, 22.2.3.5 + // %TypedArray%.prototype.copyWithin is a self-hosted method, so this code + // is only used for shared typed arrays. We should self-host both methods + // eventually (but note TypedArrayCopyWithin will require changes to be + // usable for shared typed arrays), but we need to rejigger the shared + // typed array prototype chain before we can do that. static bool copyWithin(JSContext* cx, const CallArgs& args) { diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 9c6f2757bf..b71641bb5d 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -773,14 +773,6 @@ TypedArrayObject::protoAccessors[] = { JS_PS_END }; -/* static */ bool -TypedArrayObject::copyWithin(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod::copyWithin>(cx, args); -} - /* static */ bool TypedArrayObject::set(JSContext* cx, unsigned argc, Value* vp) { @@ -789,19 +781,15 @@ TypedArrayObject::set(JSContext* cx, unsigned argc, Value* vp) TypedArrayMethods::set>(cx, args); } -/* static */ bool -TypedArrayObject::subarray(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod::subarray>(cx, args); -} - /* static */ const JSFunctionSpec TypedArrayObject::protoFunctions[] = { - JS_FN("subarray", TypedArrayObject::subarray, 2, 0), + JS_SELF_HOSTED_FN("subarray", "TypedArraySubarray", 2, 0), +#if 0 /* disabled until perf-testing is completed */ + JS_SELF_HOSTED_FN("set", "TypedArraySet", 2, 0), +#else JS_FN("set", TypedArrayObject::set, 2, 0), - JS_FN("copyWithin", TypedArrayObject::copyWithin, 2, 0), +#endif + JS_SELF_HOSTED_FN("copyWithin", "TypedArrayCopyWithin", 3, 0), JS_SELF_HOSTED_FN("every", "TypedArrayEvery", 2, 0), JS_SELF_HOSTED_FN("fill", "TypedArrayFill", 3, 0), JS_SELF_HOSTED_FN("filter", "TypedArrayFilter", 2, 0), diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h index b126314cde..73eb39c4b1 100644 --- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -47,12 +47,21 @@ class TypedArrayLayout // Underlying (Shared)ArrayBufferObject. static const size_t BUFFER_SLOT = 0; + static_assert(BUFFER_SLOT == JS_TYPEDARRAYLAYOUT_BUFFER_SLOT, + "self-hosted code with burned-in constants must get the " + "right buffer slot"); // Slot containing length of the view in number of typed elements. static const size_t LENGTH_SLOT = 1; + static_assert(LENGTH_SLOT == JS_TYPEDARRAYLAYOUT_LENGTH_SLOT, + "self-hosted code with burned-in constants must get the " + "right length slot"); // Offset of view within underlying (Shared)ArrayBufferObject. static const size_t BYTEOFFSET_SLOT = 2; + static_assert(BYTEOFFSET_SLOT == JS_TYPEDARRAYLAYOUT_BYTEOFFSET_SLOT, + "self-hosted code with burned-in constants must get the " + "right byteOffset slot"); static const size_t RESERVED_SLOTS = 3; @@ -216,7 +225,6 @@ class TypedArrayObject : public NativeObject static bool copyWithin(JSContext* cx, unsigned argc, Value* vp); static bool set(JSContext* cx, unsigned argc, Value* vp); - static bool subarray(JSContext* cx, unsigned argc, Value* vp); }; inline bool @@ -284,8 +292,8 @@ IsTypedArrayIndex(jsid id, uint64_t* indexp) * when the property key is a TypedArray index. */ bool -DefineTypedArrayElement(JSContext *cx, HandleObject arr, uint64_t index, - Handle desc, ObjectOpResult &result); +DefineTypedArrayElement(JSContext* cx, HandleObject arr, uint64_t index, + Handle desc, ObjectOpResult& result); static inline unsigned TypedArrayShift(Scalar::Type viewType) @@ -318,8 +326,21 @@ TypedArrayElemSize(Scalar::Type viewType) return 1u << TypedArrayShift(viewType); } -extern JSObject * -InitDataViewClass(JSContext *cx, HandleObject obj); +// Assign +// +// target[targetOffset] = unsafeSrcCrossCompartment[0] +// ... +// target[targetOffset + unsafeSrcCrossCompartment.length - 1] = +// unsafeSrcCrossCompartment[unsafeSrcCrossCompartment.length - 1] +// +// where the source element range doesn't overlap the target element range in +// memory. +extern void +SetDisjointTypedElements(TypedArrayObject* target, uint32_t targetOffset, + TypedArrayObject* unsafeSrcCrossCompartment); + +extern JSObject* +InitDataViewClass(JSContext* cx, HandleObject obj); class DataViewObject : public NativeObject { diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 398ca43069..1ae311a72b 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,11 +29,11 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 278; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 281; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); -static_assert(JSErr_Limit == 387, +static_assert(JSErr_Limit == 392, "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or " "removed MSG_DEFs from js.msg, you should increment " "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's " diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index ace03cb596..44c90e408a 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -30,6 +30,7 @@ #include "mozilla/dom/TabChild.h" #include "mozilla/Likely.h" #include "mozilla/MouseEvents.h" +#include "mozilla/Snprintf.h" #include "mozilla/TextEvents.h" #include "mozilla/TouchEvents.h" #include "mozilla/UniquePtr.h" @@ -1002,14 +1003,14 @@ LogTextPerfStats(gfxTextPerfMetrics* aTextPerf, switch (aLogType) { case eLog_reflow: - sprintf(prefix, "(textperf-reflow) %p time-ms: %7.0f", aPresShell, aTime); + snprintf_literal(prefix, "(textperf-reflow) %p time-ms: %7.0f", aPresShell, aTime); break; case eLog_loaddone: - sprintf(prefix, "(textperf-loaddone) %p time-ms: %7.0f", aPresShell, aTime); + snprintf_literal(prefix, "(textperf-loaddone) %p time-ms: %7.0f", aPresShell, aTime); break; default: MOZ_ASSERT(aLogType == eLog_totals, "unknown textperf log type"); - sprintf(prefix, "(textperf-totals) %p", aPresShell); + snprintf_literal(prefix, "(textperf-totals) %p", aPresShell); } double hitRatio = 0.0; @@ -10312,7 +10313,7 @@ void ReflowCountMgr::Add(const char * aName, nsIFrame * aFrame) nullptr != mIndiFrameCounts && aFrame != nullptr) { char key[KEY_BUF_SIZE_FOR_PTR]; - sprintf(key, "%p", (void*)aFrame); + snprintf_literal(key, "%p", (void*)aFrame); IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(mIndiFrameCounts, key); if (counter == nullptr) { counter = new IndiReflowCounter(this); @@ -10340,7 +10341,7 @@ void ReflowCountMgr::PaintCount(const char* aName, nullptr != mIndiFrameCounts && aFrame != nullptr) { char key[KEY_BUF_SIZE_FOR_PTR]; - sprintf(key, "%p", (void*)aFrame); + snprintf_literal(key, "%p", (void*)aFrame); IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(mIndiFrameCounts, key); if (counter != nullptr && counter->mName.EqualsASCII(aName)) { @@ -10364,7 +10365,7 @@ void ReflowCountMgr::PaintCount(const char* aName, aPresContext->GetTextPerfMetrics(), *getter_AddRefs(fm)); char buf[16]; - int len = sprintf(buf, "%d", counter->mCount); + int len = snprintf_literal(buf, "%d", counter->mCount); nscoord x = 0, y = fm->MaxAscent(); nscoord width, height = fm->MaxHeight(); fm->SetTextRunRTL(false); @@ -10485,7 +10486,7 @@ static void RecurseIndiTotals(nsPresContext* aPresContext, } char key[KEY_BUF_SIZE_FOR_PTR]; - sprintf(key, "%p", (void*)aParentFrame); + snprintf_literal(key, "%p", (void*)aParentFrame); IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(aHT, key); if (counter) { counter->mHasBeenOutput = true; diff --git a/layout/generic/nsAbsoluteContainingBlock.cpp b/layout/generic/nsAbsoluteContainingBlock.cpp index ac95bddb8b..8be0a09f0f 100644 --- a/layout/generic/nsAbsoluteContainingBlock.cpp +++ b/layout/generic/nsAbsoluteContainingBlock.cpp @@ -17,10 +17,12 @@ #include "nsPresContext.h" #include "nsCSSFrameConstructor.h" +#include "mozilla/Snprintf.h" + #ifdef DEBUG #include "nsBlockFrame.h" -static void PrettyUC(nscoord aSize, char* aBuf) +static void PrettyUC(nscoord aSize, char* aBuf, int aBufSize) { if (NS_UNCONSTRAINEDSIZE == aSize) { strcpy(aBuf, "UC"); @@ -28,7 +30,7 @@ static void PrettyUC(nscoord aSize, char* aBuf) if((int32_t)0xdeadbeef == aSize) { strcpy(aBuf, "deadbeef"); } else { - sprintf(aBuf, "%d", aSize); + snprintf(aBuf, aBufSize, "%d", aSize); } } } @@ -360,11 +362,11 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat char width[16]; char height[16]; - PrettyUC(aReflowState.AvailableWidth(), width); - PrettyUC(aReflowState.AvailableHeight(), height); + PrettyUC(aReflowState.AvailableWidth(), width, 16); + PrettyUC(aReflowState.AvailableHeight(), height, 16); printf(" a=%s,%s ", width, height); - PrettyUC(aReflowState.ComputedWidth(), width); - PrettyUC(aReflowState.ComputedHeight(), height); + PrettyUC(aReflowState.ComputedWidth(), width, 16); + PrettyUC(aReflowState.ComputedHeight(), height, 16); printf("c=%s,%s \n", width, height); } AutoNoisyIndenter indent(nsBlockFrame::gNoisy); diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index d158b2fb04..1901ee6f13 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -11,12 +11,13 @@ #include #include +#include "gfx2DGlue.h" #include "gfxUtils.h" #include "mozilla/Attributes.h" #include "mozilla/DebugOnly.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/PathHelpers.h" -#include "gfx2DGlue.h" +#include "mozilla/Snprintf.h" #include "nsCOMPtr.h" #include "nsFrameList.h" @@ -9164,7 +9165,8 @@ struct DR_State bool GetNumber(char* aBuf, int32_t& aNumber); void PrettyUC(nscoord aSize, - char* aBuf); + char* aBuf, + int aBufSize); void PrintMargin(const char* tag, const nsMargin* aMargin); void DisplayFrameTypeInfo(nsIFrame* aFrame, int32_t aIndent); @@ -9655,7 +9657,8 @@ DR_FrameTreeNode* DR_State::CreateTreeNode(nsIFrame* aFrame, } void DR_State::PrettyUC(nscoord aSize, - char* aBuf) + char* aBuf, + int aBufSize) { if (NS_UNCONSTRAINEDSIZE == aSize) { strcpy(aBuf, "UC"); @@ -9666,7 +9669,7 @@ void DR_State::PrettyUC(nscoord aSize, strcpy(aBuf, "deadbeef"); } else { - sprintf(aBuf, "%d", aSize); + snprintf(aBuf, aBufSize, "%d", aSize); } } } @@ -9675,10 +9678,10 @@ void DR_State::PrintMargin(const char *tag, const nsMargin* aMargin) { if (aMargin) { char t[16], r[16], b[16], l[16]; - PrettyUC(aMargin->top, t); - PrettyUC(aMargin->right, r); - PrettyUC(aMargin->bottom, b); - PrettyUC(aMargin->left, l); + PrettyUC(aMargin->top, t, 16); + PrettyUC(aMargin->right, r, 16); + PrettyUC(aMargin->bottom, b, 16); + PrettyUC(aMargin->left, l, 16); printf(" %s=%s,%s,%s,%s", tag, t, r, b, l); } else { // use %p here for consistency with other null-pointer printouts @@ -9724,12 +9727,12 @@ static void DisplayReflowEnterPrint(nsPresContext* aPresContext, char width[16]; char height[16]; - DR_state->PrettyUC(aReflowState.AvailableWidth(), width); - DR_state->PrettyUC(aReflowState.AvailableHeight(), height); + DR_state->PrettyUC(aReflowState.AvailableWidth(), width, 16); + DR_state->PrettyUC(aReflowState.AvailableHeight(), height, 16); printf("Reflow a=%s,%s ", width, height); - DR_state->PrettyUC(aReflowState.ComputedWidth(), width); - DR_state->PrettyUC(aReflowState.ComputedHeight(), height); + DR_state->PrettyUC(aReflowState.ComputedWidth(), width, 16); + DR_state->PrettyUC(aReflowState.ComputedHeight(), height, 16); printf("c=%s,%s ", width, height); if (aFrame->GetStateBits() & NS_FRAME_IS_DIRTY) @@ -9851,38 +9854,38 @@ void nsFrame::DisplayReflowExit(nsPresContext* aPresContext, char height[16]; char x[16]; char y[16]; - DR_state->PrettyUC(aMetrics.Width(), width); - DR_state->PrettyUC(aMetrics.Height(), height); + DR_state->PrettyUC(aMetrics.Width(), width, 16); + DR_state->PrettyUC(aMetrics.Height(), height, 16); printf("Reflow d=%s,%s", width, height); if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) { printf(" status=0x%x", aStatus); } if (aFrame->HasOverflowAreas()) { - DR_state->PrettyUC(aMetrics.VisualOverflow().x, x); - DR_state->PrettyUC(aMetrics.VisualOverflow().y, y); - DR_state->PrettyUC(aMetrics.VisualOverflow().width, width); - DR_state->PrettyUC(aMetrics.VisualOverflow().height, height); + DR_state->PrettyUC(aMetrics.VisualOverflow().x, x, 16); + DR_state->PrettyUC(aMetrics.VisualOverflow().y, y, 16); + DR_state->PrettyUC(aMetrics.VisualOverflow().width, width, 16); + DR_state->PrettyUC(aMetrics.VisualOverflow().height, height, 16); printf(" vis-o=(%s,%s) %s x %s", x, y, width, height); nsRect storedOverflow = aFrame->GetVisualOverflowRect(); - DR_state->PrettyUC(storedOverflow.x, x); - DR_state->PrettyUC(storedOverflow.y, y); - DR_state->PrettyUC(storedOverflow.width, width); - DR_state->PrettyUC(storedOverflow.height, height); + DR_state->PrettyUC(storedOverflow.x, x, 16); + DR_state->PrettyUC(storedOverflow.y, y, 16); + DR_state->PrettyUC(storedOverflow.width, width, 16); + DR_state->PrettyUC(storedOverflow.height, height, 16); printf(" vis-sto=(%s,%s) %s x %s", x, y, width, height); - DR_state->PrettyUC(aMetrics.ScrollableOverflow().x, x); - DR_state->PrettyUC(aMetrics.ScrollableOverflow().y, y); - DR_state->PrettyUC(aMetrics.ScrollableOverflow().width, width); - DR_state->PrettyUC(aMetrics.ScrollableOverflow().height, height); + DR_state->PrettyUC(aMetrics.ScrollableOverflow().x, x, 16); + DR_state->PrettyUC(aMetrics.ScrollableOverflow().y, y, 16); + DR_state->PrettyUC(aMetrics.ScrollableOverflow().width, width, 16); + DR_state->PrettyUC(aMetrics.ScrollableOverflow().height, height, 16); printf(" scr-o=(%s,%s) %s x %s", x, y, width, height); storedOverflow = aFrame->GetScrollableOverflowRect(); - DR_state->PrettyUC(storedOverflow.x, x); - DR_state->PrettyUC(storedOverflow.y, y); - DR_state->PrettyUC(storedOverflow.width, width); - DR_state->PrettyUC(storedOverflow.height, height); + DR_state->PrettyUC(storedOverflow.x, x, 16); + DR_state->PrettyUC(storedOverflow.y, y, 16); + DR_state->PrettyUC(storedOverflow.width, width, 16); + DR_state->PrettyUC(storedOverflow.height, height, 16); printf(" scr-sto=(%s,%s) %s x %s", x, y, width, height); } printf("\n"); @@ -9926,7 +9929,7 @@ void nsFrame::DisplayIntrinsicISizeExit(nsIFrame* aFrame, if (treeNode->mDisplay) { DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); char width[16]; - DR_state->PrettyUC(aResult, width); + DR_state->PrettyUC(aResult, width, 16); printf("Get%sWidth=%s\n", aType, width); } DR_state->DeleteTreeNode(*treeNode); @@ -9948,8 +9951,8 @@ void nsFrame::DisplayIntrinsicSizeExit(nsIFrame* aFrame, char width[16]; char height[16]; - DR_state->PrettyUC(aResult.width, width); - DR_state->PrettyUC(aResult.height, height); + DR_state->PrettyUC(aResult.width, width, 16); + DR_state->PrettyUC(aResult.height, height, 16); printf("Get%sSize=%s,%s\n", aType, width, height); } DR_state->DeleteTreeNode(*treeNode); @@ -10000,12 +10003,12 @@ nsHTMLReflowState::DisplayInitConstraintsEnter(nsIFrame* aFrame, char width[16]; char height[16]; - DR_state->PrettyUC(aContainingBlockWidth, width); - DR_state->PrettyUC(aContainingBlockHeight, height); + DR_state->PrettyUC(aContainingBlockWidth, width, 16); + DR_state->PrettyUC(aContainingBlockHeight, height, 16); printf(" cb=%s,%s", width, height); - DR_state->PrettyUC(aState->AvailableWidth(), width); - DR_state->PrettyUC(aState->AvailableHeight(), height); + DR_state->PrettyUC(aState->AvailableWidth(), width, 16); + DR_state->PrettyUC(aState->AvailableHeight(), height, 16); printf(" as=%s,%s", width, height); DR_state->PrintMargin("b", aBorder); @@ -10030,12 +10033,12 @@ nsHTMLReflowState::DisplayInitConstraintsExit(nsIFrame* aFrame, if (treeNode->mDisplay) { DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); char cmiw[16], cw[16], cmxw[16], cmih[16], ch[16], cmxh[16]; - DR_state->PrettyUC(aState->ComputedMinWidth(), cmiw); - DR_state->PrettyUC(aState->ComputedWidth(), cw); - DR_state->PrettyUC(aState->ComputedMaxWidth(), cmxw); - DR_state->PrettyUC(aState->ComputedMinHeight(), cmih); - DR_state->PrettyUC(aState->ComputedHeight(), ch); - DR_state->PrettyUC(aState->ComputedMaxHeight(), cmxh); + DR_state->PrettyUC(aState->ComputedMinWidth(), cmiw, 16); + DR_state->PrettyUC(aState->ComputedWidth(), cw, 16); + DR_state->PrettyUC(aState->ComputedMaxWidth(), cmxw, 16); + DR_state->PrettyUC(aState->ComputedMinHeight(), cmih, 16); + DR_state->PrettyUC(aState->ComputedHeight(), ch, 16); + DR_state->PrettyUC(aState->ComputedMaxHeight(), cmxh, 16); printf("InitConstraints= cw=(%s <= %s <= %s) ch=(%s <= %s <= %s)", cmiw, cw, cmxw, cmih, ch, cmxh); DR_state->PrintMargin("co", &aState->ComputedPhysicalOffsets()); @@ -10066,8 +10069,8 @@ nsCSSOffsetState::DisplayInitOffsetsEnter(nsIFrame* aFrame, char horizPctBasisStr[16]; char vertPctBasisStr[16]; - DR_state->PrettyUC(aHorizontalPercentBasis, horizPctBasisStr); - DR_state->PrettyUC(aVerticalPercentBasis, vertPctBasisStr); + DR_state->PrettyUC(aHorizontalPercentBasis, horizPctBasisStr, 16); + DR_state->PrettyUC(aVerticalPercentBasis, vertPctBasisStr, 16); printf("InitOffsets pct_basis=%s,%s", horizPctBasisStr, vertPctBasisStr); DR_state->PrintMargin("b", aBorder); diff --git a/layout/style/FontFaceSet.cpp b/layout/style/FontFaceSet.cpp index 7de4fb34e2..857af4389b 100644 --- a/layout/style/FontFaceSet.cpp +++ b/layout/style/FontFaceSet.cpp @@ -15,6 +15,7 @@ #include "mozilla/dom/Promise.h" #include "mozilla/AsyncEventDispatcher.h" #include "mozilla/Preferences.h" +#include "mozilla/Snprintf.h" #include "nsCORSListenerProxy.h" #include "nsFontFaceLoader.h" #include "nsIConsoleService.h" @@ -1046,7 +1047,7 @@ FontFaceSet::LogMessage(gfxUserFontEntry* aUserFontEntry, if (weightKeywordString.Length() > 0) { weightKeyword = weightKeywordString.get(); } else { - sprintf(weightKeywordBuf, "%u", aUserFontEntry->Weight()); + snprintf_literal(weightKeywordBuf, "%u", aUserFontEntry->Weight()); weightKeyword = weightKeywordBuf; } diff --git a/media/gmp-clearkey/0.1/openaes/oaes_lib.c b/media/gmp-clearkey/0.1/openaes/oaes_lib.c index 6234b1d7a1..68f4b7d7df 100644 --- a/media/gmp-clearkey/0.1/openaes/oaes_lib.c +++ b/media/gmp-clearkey/0.1/openaes/oaes_lib.c @@ -33,6 +33,8 @@ #include #include +#include "mozilla/Snprintf.h" + #ifdef WIN32 #include #endif @@ -453,7 +455,7 @@ OAES_RET oaes_sprintf( for( _i = 0; _i < data_len; _i++ ) { - sprintf( _temp, "%02x ", data[_i] ); + snprintf( _temp, sizeof(_temp), "%02x ", data[_i] ); strcat( buf, _temp ); if( _i && 0 == ( _i + 1 ) % OAES_BLOCK_SIZE ) strcat( buf, "\n" ); diff --git a/mfbt/ArrayUtils.h b/mfbt/ArrayUtils.h index b3eebbbd51..9566a3f40a 100644 --- a/mfbt/ArrayUtils.h +++ b/mfbt/ArrayUtils.h @@ -92,22 +92,23 @@ ArrayEnd(const Array& aArr) namespace detail { -template +template::value>> struct AlignedChecker { static void - test(Pointee* aPtr) + test(const Pointee* aPtr) { MOZ_ASSERT((uintptr_t(aPtr) % MOZ_ALIGNOF(AlignType)) == 0, "performing a range-check with a misaligned pointer"); } }; -template -struct AlignedChecker +template +struct AlignedChecker { static void - test(Pointee* aPtr) + test(const Pointee* aPtr) { } }; @@ -132,13 +133,14 @@ inline typename EnableIf::value || IsBaseOf::value || IsVoid::value, bool>::Type -IsInRange(T* aPtr, U* aBegin, U* aEnd) +IsInRange(const T* aPtr, const U* aBegin, const U* aEnd) { MOZ_ASSERT(aBegin <= aEnd); detail::AlignedChecker::test(aPtr); detail::AlignedChecker::test(aBegin); detail::AlignedChecker::test(aEnd); - return aBegin <= static_cast(aPtr) && static_cast(aPtr) < aEnd; + return aBegin <= reinterpret_cast(aPtr) && + reinterpret_cast(aPtr) < aEnd; } /** @@ -148,10 +150,11 @@ IsInRange(T* aPtr, U* aBegin, U* aEnd) */ template inline bool -IsInRange(T* aPtr, uintptr_t aBegin, uintptr_t aEnd) +IsInRange(const T* aPtr, uintptr_t aBegin, uintptr_t aEnd) { return IsInRange(aPtr, - reinterpret_cast(aBegin), reinterpret_cast(aEnd)); + reinterpret_cast(aBegin), + reinterpret_cast(aEnd)); } namespace detail { diff --git a/mfbt/JSONWriter.h b/mfbt/JSONWriter.h index f470bd0aa0..da6e0c49b7 100644 --- a/mfbt/JSONWriter.h +++ b/mfbt/JSONWriter.h @@ -95,6 +95,7 @@ #include "mozilla/double-conversion.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/PodOperations.h" +#include "mozilla/Snprintf.h" #include "mozilla/UniquePtr.h" #include "mozilla/Vector.h" @@ -388,7 +389,7 @@ public: void IntProperty(const char* aName, int64_t aInt) { char buf[64]; - sprintf(buf, "%" PRId64, aInt); + snprintf_literal(buf, "%" PRId64, aInt); Scalar(aName, buf); } diff --git a/mfbt/Snprintf.h b/mfbt/Snprintf.h index 1d06094722..864f7c25b2 100644 --- a/mfbt/Snprintf.h +++ b/mfbt/Snprintf.h @@ -36,7 +36,7 @@ MOZ_ALWAYS_INLINE int snprintf(char* buffer, size_t n, const char* format, ...) // buffer, avoiding the need for the user to pass it in explicitly. #ifdef __cplusplus template -int SnprintfLiteral(char (&buffer)[N], const char* format, ...) +int snprintf_literal(char (&buffer)[N], const char* format, ...) { va_list args; va_start(args, format); diff --git a/mfbt/tests/TestIntegerPrintfMacros.cpp b/mfbt/tests/TestIntegerPrintfMacros.cpp index 540b63569f..d37c066b7b 100644 --- a/mfbt/tests/TestIntegerPrintfMacros.cpp +++ b/mfbt/tests/TestIntegerPrintfMacros.cpp @@ -6,6 +6,7 @@ #include "mozilla/Assertions.h" #include "mozilla/IntegerPrintfMacros.h" // this must pick up +#include "mozilla/Snprintf.h" #include #include @@ -33,11 +34,11 @@ static void TestPrintSigned8() { PoisonOutput(); - sprintf(gOutput, "%" PRId8, int8_t(-17)); + snprintf_literal(gOutput, "%" PRId8, int8_t(-17)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-17")); PoisonOutput(); - sprintf(gOutput, "%" PRIi8, int8_t(42)); + snprintf_literal(gOutput, "%" PRIi8, int8_t(42)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42")); } @@ -45,11 +46,11 @@ static void TestPrintSigned16() { PoisonOutput(); - sprintf(gOutput, "%" PRId16, int16_t(-289)); + snprintf_literal(gOutput, "%" PRId16, int16_t(-289)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-289")); PoisonOutput(); - sprintf(gOutput, "%" PRIi16, int16_t(728)); + snprintf_literal(gOutput, "%" PRIi16, int16_t(728)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "728")); } @@ -57,11 +58,11 @@ static void TestPrintSigned32() { PoisonOutput(); - sprintf(gOutput, "%" PRId32, int32_t(-342178)); + snprintf_literal(gOutput, "%" PRId32, int32_t(-342178)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-342178")); PoisonOutput(); - sprintf(gOutput, "%" PRIi32, int32_t(5719283)); + snprintf_literal(gOutput, "%" PRIi32, int32_t(5719283)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "5719283")); } @@ -69,11 +70,11 @@ static void TestPrintSigned64() { PoisonOutput(); - sprintf(gOutput, "%" PRId64, int64_t(-INT64_C(432157943248732))); + snprintf_literal(gOutput, "%" PRId64, int64_t(-INT64_C(432157943248732))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-432157943248732")); PoisonOutput(); - sprintf(gOutput, "%" PRIi64, int64_t(INT64_C(325719232983))); + snprintf_literal(gOutput, "%" PRIi64, int64_t(INT64_C(325719232983))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "325719232983")); } @@ -90,11 +91,11 @@ static void TestPrintSignedLeast8() { PoisonOutput(); - sprintf(gOutput, "%" PRIdLEAST8, int_least8_t(-17)); + snprintf_literal(gOutput, "%" PRIdLEAST8, int_least8_t(-17)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-17")); PoisonOutput(); - sprintf(gOutput, "%" PRIiLEAST8, int_least8_t(42)); + snprintf_literal(gOutput, "%" PRIiLEAST8, int_least8_t(42)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42")); } @@ -102,11 +103,11 @@ static void TestPrintSignedLeast16() { PoisonOutput(); - sprintf(gOutput, "%" PRIdLEAST16, int_least16_t(-289)); + snprintf_literal(gOutput, "%" PRIdLEAST16, int_least16_t(-289)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-289")); PoisonOutput(); - sprintf(gOutput, "%" PRIiLEAST16, int_least16_t(728)); + snprintf_literal(gOutput, "%" PRIiLEAST16, int_least16_t(728)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "728")); } @@ -114,11 +115,11 @@ static void TestPrintSignedLeast32() { PoisonOutput(); - sprintf(gOutput, "%" PRIdLEAST32, int_least32_t(-342178)); + snprintf_literal(gOutput, "%" PRIdLEAST32, int_least32_t(-342178)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-342178")); PoisonOutput(); - sprintf(gOutput, "%" PRIiLEAST32, int_least32_t(5719283)); + snprintf_literal(gOutput, "%" PRIiLEAST32, int_least32_t(5719283)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "5719283")); } @@ -126,11 +127,11 @@ static void TestPrintSignedLeast64() { PoisonOutput(); - sprintf(gOutput, "%" PRIdLEAST64, int_least64_t(-INT64_C(432157943248732))); + snprintf_literal(gOutput, "%" PRIdLEAST64, int_least64_t(-INT64_C(432157943248732))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-432157943248732")); PoisonOutput(); - sprintf(gOutput, "%" PRIiLEAST64, int_least64_t(INT64_C(325719232983))); + snprintf_literal(gOutput, "%" PRIiLEAST64, int_least64_t(INT64_C(325719232983))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "325719232983")); } @@ -147,11 +148,11 @@ static void TestPrintSignedFast8() { PoisonOutput(); - sprintf(gOutput, "%" PRIdFAST8, int_fast8_t(-17)); + snprintf_literal(gOutput, "%" PRIdFAST8, int_fast8_t(-17)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-17")); PoisonOutput(); - sprintf(gOutput, "%" PRIiFAST8, int_fast8_t(42)); + snprintf_literal(gOutput, "%" PRIiFAST8, int_fast8_t(42)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42")); } @@ -159,11 +160,11 @@ static void TestPrintSignedFast16() { PoisonOutput(); - sprintf(gOutput, "%" PRIdFAST16, int_fast16_t(-289)); + snprintf_literal(gOutput, "%" PRIdFAST16, int_fast16_t(-289)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-289")); PoisonOutput(); - sprintf(gOutput, "%" PRIiFAST16, int_fast16_t(728)); + snprintf_literal(gOutput, "%" PRIiFAST16, int_fast16_t(728)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "728")); } @@ -171,11 +172,11 @@ static void TestPrintSignedFast32() { PoisonOutput(); - sprintf(gOutput, "%" PRIdFAST32, int_fast32_t(-342178)); + snprintf_literal(gOutput, "%" PRIdFAST32, int_fast32_t(-342178)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-342178")); PoisonOutput(); - sprintf(gOutput, "%" PRIiFAST32, int_fast32_t(5719283)); + snprintf_literal(gOutput, "%" PRIiFAST32, int_fast32_t(5719283)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "5719283")); } @@ -183,11 +184,11 @@ static void TestPrintSignedFast64() { PoisonOutput(); - sprintf(gOutput, "%" PRIdFAST64, int_fast64_t(-INT64_C(432157943248732))); + snprintf_literal(gOutput, "%" PRIdFAST64, int_fast64_t(-INT64_C(432157943248732))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-432157943248732")); PoisonOutput(); - sprintf(gOutput, "%" PRIiFAST64, int_fast64_t(INT64_C(325719232983))); + snprintf_literal(gOutput, "%" PRIiFAST64, int_fast64_t(INT64_C(325719232983))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "325719232983")); } @@ -204,11 +205,11 @@ static void TestPrintSignedMax() { PoisonOutput(); - sprintf(gOutput, "%" PRIdMAX, intmax_t(-INTMAX_C(432157943248732))); + snprintf_literal(gOutput, "%" PRIdMAX, intmax_t(-INTMAX_C(432157943248732))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-432157943248732")); PoisonOutput(); - sprintf(gOutput, "%" PRIiMAX, intmax_t(INTMAX_C(325719232983))); + snprintf_literal(gOutput, "%" PRIiMAX, intmax_t(INTMAX_C(325719232983))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "325719232983")); } @@ -216,11 +217,11 @@ static void TestPrintSignedPtr() { PoisonOutput(); - sprintf(gOutput, "%" PRIdPTR, intptr_t(reinterpret_cast(12345678))); + snprintf_literal(gOutput, "%" PRIdPTR, intptr_t(reinterpret_cast(12345678))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "12345678")); PoisonOutput(); - sprintf(gOutput, "%" PRIiPTR, intptr_t(reinterpret_cast(87654321))); + snprintf_literal(gOutput, "%" PRIiPTR, intptr_t(reinterpret_cast(87654321))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "87654321")); } @@ -249,19 +250,19 @@ static void TestPrintUnsigned8() { PoisonOutput(); - sprintf(gOutput, "%" PRIo8, uint8_t(042)); + snprintf_literal(gOutput, "%" PRIo8, uint8_t(042)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42")); PoisonOutput(); - sprintf(gOutput, "%" PRIu8, uint8_t(17)); + snprintf_literal(gOutput, "%" PRIu8, uint8_t(17)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17")); PoisonOutput(); - sprintf(gOutput, "%" PRIx8, uint8_t(0x2a)); + snprintf_literal(gOutput, "%" PRIx8, uint8_t(0x2a)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a")); PoisonOutput(); - sprintf(gOutput, "%" PRIX8, uint8_t(0xCD)); + snprintf_literal(gOutput, "%" PRIX8, uint8_t(0xCD)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CD")); } @@ -269,19 +270,19 @@ static void TestPrintUnsigned16() { PoisonOutput(); - sprintf(gOutput, "%" PRIo16, uint16_t(04242)); + snprintf_literal(gOutput, "%" PRIo16, uint16_t(04242)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "4242")); PoisonOutput(); - sprintf(gOutput, "%" PRIu16, uint16_t(1717)); + snprintf_literal(gOutput, "%" PRIu16, uint16_t(1717)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "1717")); PoisonOutput(); - sprintf(gOutput, "%" PRIx16, uint16_t(0x2a2a)); + snprintf_literal(gOutput, "%" PRIx16, uint16_t(0x2a2a)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a")); PoisonOutput(); - sprintf(gOutput, "%" PRIX16, uint16_t(0xCDCD)); + snprintf_literal(gOutput, "%" PRIX16, uint16_t(0xCDCD)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCD")); } @@ -289,19 +290,19 @@ static void TestPrintUnsigned32() { PoisonOutput(); - sprintf(gOutput, "%" PRIo32, uint32_t(0424242)); + snprintf_literal(gOutput, "%" PRIo32, uint32_t(0424242)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242")); PoisonOutput(); - sprintf(gOutput, "%" PRIu32, uint32_t(171717)); + snprintf_literal(gOutput, "%" PRIu32, uint32_t(171717)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "171717")); PoisonOutput(); - sprintf(gOutput, "%" PRIx32, uint32_t(0x2a2a2a)); + snprintf_literal(gOutput, "%" PRIx32, uint32_t(0x2a2a2a)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a")); PoisonOutput(); - sprintf(gOutput, "%" PRIX32, uint32_t(0xCDCDCD)); + snprintf_literal(gOutput, "%" PRIX32, uint32_t(0xCDCDCD)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCD")); } @@ -309,19 +310,19 @@ static void TestPrintUnsigned64() { PoisonOutput(); - sprintf(gOutput, "%" PRIo64, uint64_t(UINT64_C(0424242424242))); + snprintf_literal(gOutput, "%" PRIo64, uint64_t(UINT64_C(0424242424242))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242424242")); PoisonOutput(); - sprintf(gOutput, "%" PRIu64, uint64_t(UINT64_C(17171717171717171717))); + snprintf_literal(gOutput, "%" PRIu64, uint64_t(UINT64_C(17171717171717171717))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17171717171717171717")); PoisonOutput(); - sprintf(gOutput, "%" PRIx64, uint64_t(UINT64_C(0x2a2a2a2a2a2a2a))); + snprintf_literal(gOutput, "%" PRIx64, uint64_t(UINT64_C(0x2a2a2a2a2a2a2a))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a2a2a2a2a")); PoisonOutput(); - sprintf(gOutput, "%" PRIX64, uint64_t(UINT64_C(0xCDCDCDCDCDCD))); + snprintf_literal(gOutput, "%" PRIX64, uint64_t(UINT64_C(0xCDCDCDCDCDCD))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCDCDCDCD")); } @@ -338,19 +339,19 @@ static void TestPrintUnsignedLeast8() { PoisonOutput(); - sprintf(gOutput, "%" PRIoLEAST8, uint_least8_t(042)); + snprintf_literal(gOutput, "%" PRIoLEAST8, uint_least8_t(042)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42")); PoisonOutput(); - sprintf(gOutput, "%" PRIuLEAST8, uint_least8_t(17)); + snprintf_literal(gOutput, "%" PRIuLEAST8, uint_least8_t(17)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17")); PoisonOutput(); - sprintf(gOutput, "%" PRIxLEAST8, uint_least8_t(0x2a)); + snprintf_literal(gOutput, "%" PRIxLEAST8, uint_least8_t(0x2a)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a")); PoisonOutput(); - sprintf(gOutput, "%" PRIXLEAST8, uint_least8_t(0xCD)); + snprintf_literal(gOutput, "%" PRIXLEAST8, uint_least8_t(0xCD)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CD")); } @@ -358,19 +359,19 @@ static void TestPrintUnsignedLeast16() { PoisonOutput(); - sprintf(gOutput, "%" PRIoLEAST16, uint_least16_t(04242)); + snprintf_literal(gOutput, "%" PRIoLEAST16, uint_least16_t(04242)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "4242")); PoisonOutput(); - sprintf(gOutput, "%" PRIuLEAST16, uint_least16_t(1717)); + snprintf_literal(gOutput, "%" PRIuLEAST16, uint_least16_t(1717)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "1717")); PoisonOutput(); - sprintf(gOutput, "%" PRIxLEAST16, uint_least16_t(0x2a2a)); + snprintf_literal(gOutput, "%" PRIxLEAST16, uint_least16_t(0x2a2a)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a")); PoisonOutput(); - sprintf(gOutput, "%" PRIXLEAST16, uint_least16_t(0xCDCD)); + snprintf_literal(gOutput, "%" PRIXLEAST16, uint_least16_t(0xCDCD)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCD")); } @@ -378,19 +379,19 @@ static void TestPrintUnsignedLeast32() { PoisonOutput(); - sprintf(gOutput, "%" PRIoLEAST32, uint_least32_t(0424242)); + snprintf_literal(gOutput, "%" PRIoLEAST32, uint_least32_t(0424242)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242")); PoisonOutput(); - sprintf(gOutput, "%" PRIuLEAST32, uint_least32_t(171717)); + snprintf_literal(gOutput, "%" PRIuLEAST32, uint_least32_t(171717)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "171717")); PoisonOutput(); - sprintf(gOutput, "%" PRIxLEAST32, uint_least32_t(0x2a2a2a)); + snprintf_literal(gOutput, "%" PRIxLEAST32, uint_least32_t(0x2a2a2a)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a")); PoisonOutput(); - sprintf(gOutput, "%" PRIXLEAST32, uint_least32_t(0xCDCDCD)); + snprintf_literal(gOutput, "%" PRIXLEAST32, uint_least32_t(0xCDCDCD)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCD")); } @@ -398,20 +399,20 @@ static void TestPrintUnsignedLeast64() { PoisonOutput(); - sprintf(gOutput, "%" PRIoLEAST64, uint_least64_t(UINT64_C(0424242424242))); + snprintf_literal(gOutput, "%" PRIoLEAST64, uint_least64_t(UINT64_C(0424242424242))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242424242")); PoisonOutput(); - sprintf(gOutput, "%" PRIuLEAST64, + snprintf_literal(gOutput, "%" PRIuLEAST64, uint_least64_t(UINT64_C(17171717171717171717))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17171717171717171717")); PoisonOutput(); - sprintf(gOutput, "%" PRIxLEAST64, uint_least64_t(UINT64_C(0x2a2a2a2a2a2a2a))); + snprintf_literal(gOutput, "%" PRIxLEAST64, uint_least64_t(UINT64_C(0x2a2a2a2a2a2a2a))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a2a2a2a2a")); PoisonOutput(); - sprintf(gOutput, "%" PRIXLEAST64, uint_least64_t(UINT64_C(0xCDCDCDCDCDCD))); + snprintf_literal(gOutput, "%" PRIXLEAST64, uint_least64_t(UINT64_C(0xCDCDCDCDCDCD))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCDCDCDCD")); } @@ -428,19 +429,19 @@ static void TestPrintUnsignedFast8() { PoisonOutput(); - sprintf(gOutput, "%" PRIoFAST8, uint_fast8_t(042)); + snprintf_literal(gOutput, "%" PRIoFAST8, uint_fast8_t(042)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42")); PoisonOutput(); - sprintf(gOutput, "%" PRIuFAST8, uint_fast8_t(17)); + snprintf_literal(gOutput, "%" PRIuFAST8, uint_fast8_t(17)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17")); PoisonOutput(); - sprintf(gOutput, "%" PRIxFAST8, uint_fast8_t(0x2a)); + snprintf_literal(gOutput, "%" PRIxFAST8, uint_fast8_t(0x2a)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a")); PoisonOutput(); - sprintf(gOutput, "%" PRIXFAST8, uint_fast8_t(0xCD)); + snprintf_literal(gOutput, "%" PRIXFAST8, uint_fast8_t(0xCD)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CD")); } @@ -448,19 +449,19 @@ static void TestPrintUnsignedFast16() { PoisonOutput(); - sprintf(gOutput, "%" PRIoFAST16, uint_fast16_t(04242)); + snprintf_literal(gOutput, "%" PRIoFAST16, uint_fast16_t(04242)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "4242")); PoisonOutput(); - sprintf(gOutput, "%" PRIuFAST16, uint_fast16_t(1717)); + snprintf_literal(gOutput, "%" PRIuFAST16, uint_fast16_t(1717)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "1717")); PoisonOutput(); - sprintf(gOutput, "%" PRIxFAST16, uint_fast16_t(0x2a2a)); + snprintf_literal(gOutput, "%" PRIxFAST16, uint_fast16_t(0x2a2a)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a")); PoisonOutput(); - sprintf(gOutput, "%" PRIXFAST16, uint_fast16_t(0xCDCD)); + snprintf_literal(gOutput, "%" PRIXFAST16, uint_fast16_t(0xCDCD)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCD")); } @@ -468,19 +469,19 @@ static void TestPrintUnsignedFast32() { PoisonOutput(); - sprintf(gOutput, "%" PRIoFAST32, uint_fast32_t(0424242)); + snprintf_literal(gOutput, "%" PRIoFAST32, uint_fast32_t(0424242)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242")); PoisonOutput(); - sprintf(gOutput, "%" PRIuFAST32, uint_fast32_t(171717)); + snprintf_literal(gOutput, "%" PRIuFAST32, uint_fast32_t(171717)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "171717")); PoisonOutput(); - sprintf(gOutput, "%" PRIxFAST32, uint_fast32_t(0x2a2a2a)); + snprintf_literal(gOutput, "%" PRIxFAST32, uint_fast32_t(0x2a2a2a)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a")); PoisonOutput(); - sprintf(gOutput, "%" PRIXFAST32, uint_fast32_t(0xCDCDCD)); + snprintf_literal(gOutput, "%" PRIXFAST32, uint_fast32_t(0xCDCDCD)); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCD")); } @@ -488,20 +489,20 @@ static void TestPrintUnsignedFast64() { PoisonOutput(); - sprintf(gOutput, "%" PRIoFAST64, uint_fast64_t(UINT64_C(0424242424242))); + snprintf_literal(gOutput, "%" PRIoFAST64, uint_fast64_t(UINT64_C(0424242424242))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242424242")); PoisonOutput(); - sprintf(gOutput, "%" PRIuFAST64, + snprintf_literal(gOutput, "%" PRIuFAST64, uint_fast64_t(UINT64_C(17171717171717171717))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17171717171717171717")); PoisonOutput(); - sprintf(gOutput, "%" PRIxFAST64, uint_fast64_t(UINT64_C(0x2a2a2a2a2a2a2a))); + snprintf_literal(gOutput, "%" PRIxFAST64, uint_fast64_t(UINT64_C(0x2a2a2a2a2a2a2a))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a2a2a2a2a")); PoisonOutput(); - sprintf(gOutput, "%" PRIXFAST64, uint_fast64_t(UINT64_C(0xCDCDCDCDCDCD))); + snprintf_literal(gOutput, "%" PRIXFAST64, uint_fast64_t(UINT64_C(0xCDCDCDCDCDCD))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCDCDCDCD")); } @@ -518,19 +519,19 @@ static void TestPrintUnsignedMax() { PoisonOutput(); - sprintf(gOutput, "%" PRIoMAX, uintmax_t(UINTMAX_C(432157943248732))); + snprintf_literal(gOutput, "%" PRIoMAX, uintmax_t(UINTMAX_C(432157943248732))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "14220563454333534")); PoisonOutput(); - sprintf(gOutput, "%" PRIuMAX, uintmax_t(UINTMAX_C(325719232983))); + snprintf_literal(gOutput, "%" PRIuMAX, uintmax_t(UINTMAX_C(325719232983))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "325719232983")); PoisonOutput(); - sprintf(gOutput, "%" PRIxMAX, uintmax_t(UINTMAX_C(327281321873))); + snprintf_literal(gOutput, "%" PRIxMAX, uintmax_t(UINTMAX_C(327281321873))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "4c337ca791")); PoisonOutput(); - sprintf(gOutput, "%" PRIXMAX, uintmax_t(UINTMAX_C(912389523743523))); + snprintf_literal(gOutput, "%" PRIXMAX, uintmax_t(UINTMAX_C(912389523743523))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "33DD03D75A323")); } @@ -538,19 +539,19 @@ static void TestPrintUnsignedPtr() { PoisonOutput(); - sprintf(gOutput, "%" PRIoPTR, uintptr_t(reinterpret_cast(12345678))); + snprintf_literal(gOutput, "%" PRIoPTR, uintptr_t(reinterpret_cast(12345678))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "57060516")); PoisonOutput(); - sprintf(gOutput, "%" PRIuPTR, uintptr_t(reinterpret_cast(87654321))); + snprintf_literal(gOutput, "%" PRIuPTR, uintptr_t(reinterpret_cast(87654321))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "87654321")); PoisonOutput(); - sprintf(gOutput, "%" PRIxPTR, uintptr_t(reinterpret_cast(0x4c3a791))); + snprintf_literal(gOutput, "%" PRIxPTR, uintptr_t(reinterpret_cast(0x4c3a791))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "4c3a791")); PoisonOutput(); - sprintf(gOutput, "%" PRIXPTR, uintptr_t(reinterpret_cast(0xF328DB))); + snprintf_literal(gOutput, "%" PRIXPTR, uintptr_t(reinterpret_cast(0xF328DB))); MOZ_RELEASE_ASSERT(!strcmp(gOutput, "F328DB")); } diff --git a/netwerk/cache/nsDiskCacheMap.cpp b/netwerk/cache/nsDiskCacheMap.cpp index e3bab10f92..4fd77a68ba 100644 --- a/netwerk/cache/nsDiskCacheMap.cpp +++ b/netwerk/cache/nsDiskCacheMap.cpp @@ -18,6 +18,7 @@ #include "nsSerializationHelper.h" #include "mozilla/MemoryReporting.h" +#include "mozilla/Snprintf.h" #include using namespace mozilla; @@ -1121,8 +1122,8 @@ nsDiskCacheMap::GetFileForDiskCacheRecord(nsDiskCacheRecord * record, int16_t generation = record->Generation(); char name[32]; // Cut the beginning of the hash that was used in the path - ::sprintf(name, "%05X%c%02X", hash & 0xFFFFF, (meta ? 'm' : 'd'), - generation); + ::snprintf_literal(name, "%05X%c%02X", hash & 0xFFFFF, (meta ? 'm' : 'd'), + generation); rv = file->AppendNative(nsDependentCString(name)); if (NS_FAILED(rv)) return rv; @@ -1159,7 +1160,7 @@ nsDiskCacheMap::GetBlockFileForIndex(uint32_t index, nsIFile ** result) if (NS_FAILED(rv)) return rv; char name[32]; - ::sprintf(name, "_CACHE_%03d_", index + 1); + ::snprintf_literal(name, "_CACHE_%03d_", index + 1); rv = file->AppendNative(nsDependentCString(name)); if (NS_FAILED(rv)) return rv; diff --git a/netwerk/protocol/rtsp/rtsp/ASessionDescription.cpp b/netwerk/protocol/rtsp/rtsp/ASessionDescription.cpp index 48b0ead137..cf52d8f390 100644 --- a/netwerk/protocol/rtsp/rtsp/ASessionDescription.cpp +++ b/netwerk/protocol/rtsp/rtsp/ASessionDescription.cpp @@ -19,6 +19,8 @@ #include "ASessionDescription.h" +#include "mozilla/Snprintf.h" + #include #include @@ -214,7 +216,7 @@ bool ASessionDescription::getFormatType( *PT = x; char key[20]; - sprintf(key, "a=rtpmap:%lu", x); + snprintf_literal(key, "a=rtpmap:%lu", x); if (!findAttribute(index, key, desc)) { // We only support dynamic payload type assignment for now. @@ -223,7 +225,7 @@ bool ASessionDescription::getFormatType( return false; } - sprintf(key, "a=fmtp:%lu", x); + snprintf_literal(key, "a=fmtp:%lu", x); if (!findAttribute(index, key, params)) { params->clear(); } @@ -238,7 +240,7 @@ bool ASessionDescription::getDimensions( *height = 0; char key[20]; - sprintf(key, "a=framesize:%lu", PT); + snprintf_literal(key, "a=framesize:%lu", PT); AString value; if (!findAttribute(index, key, &value)) { return false; diff --git a/netwerk/test/PropertiesTest.cpp b/netwerk/test/PropertiesTest.cpp index 584151c87e..e73d4b792a 100644 --- a/netwerk/test/PropertiesTest.cpp +++ b/netwerk/test/PropertiesTest.cpp @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "TestCommon.h" +#include "mozilla/Snprintf.h" #include "nsXPCOM.h" #include "nsStringAPI.h" #include "nsIPersistentProperties2.h" @@ -76,7 +77,7 @@ main(int argc, char* argv[]) while (1) { char name[16]; name[0] = 0; - sprintf(name, "%d", i); + snprintf_literal(name, "%d", i); nsAutoString v; ret = props->GetStringProperty(nsDependentCString(name), v); if (NS_FAILED(ret) || (!v.Length())) { diff --git a/testing/mochitest/ssltunnel/ssltunnel.cpp b/testing/mochitest/ssltunnel/ssltunnel.cpp index 2f8855e1b8..796b993b50 100644 --- a/testing/mochitest/ssltunnel/ssltunnel.cpp +++ b/testing/mochitest/ssltunnel/ssltunnel.cpp @@ -30,6 +30,7 @@ #include "ssl.h" #include "sslproto.h" #include "plhash.h" +#include "mozilla/Snprintf.h" using namespace mozilla; using namespace mozilla::psm; @@ -514,7 +515,7 @@ bool AdjustWebSocketHost(relayBuffer& buffer, connection_info_t *ci) char newhost[40]; PR_NetAddrToString(&inet_addr, newhost, sizeof(newhost)); assert(strlen(newhost) < sizeof(newhost) - 7); - sprintf(newhost, "%s:%d", newhost, PR_ntohs(inet_addr.inet.port)); + snprintf_literal(newhost, "%s:%d", newhost, PR_ntohs(inet_addr.inet.port)); int diff = strlen(newhost) - (endhost-host); if (diff > 0) @@ -784,17 +785,20 @@ void HandleConnection(void* data) LOG_DEBUG((" accepted CONNECT request with redirection, " "sending location and 302 to the client\n")); client_done = true; - sprintf(buffers[s2].buffer, - "HTTP/1.1 302 Moved\r\n" - "Location: https://%s/\r\n" - "Connection: close\r\n\r\n", - locationHeader.c_str()); + snprintf(buffers[s2].buffer, + buffers[s2].bufferend - buffers[s2].buffer, + "HTTP/1.1 302 Moved\r\n" + "Location: https://%s/\r\n" + "Connection: close\r\n\r\n", + locationHeader.c_str()); } else { LOG_ERRORD((" could not read the connect request, closing connection with %d", response)); client_done = true; - sprintf(buffers[s2].buffer, "HTTP/1.1 %d ERROR\r\nConnection: close\r\n\r\n", response); + snprintf(buffers[s2].buffer, + buffers[s2].bufferend - buffers[s2].buffer, + "HTTP/1.1 %d ERROR\r\nConnection: close\r\n\r\n", response); break; } diff --git a/toolkit/components/aboutperformance/tests/browser/browser_compartments.js b/toolkit/components/aboutperformance/tests/browser/browser_compartments.js index 5c7d598f19..ff5bb51851 100644 --- a/toolkit/components/aboutperformance/tests/browser/browser_compartments.js +++ b/toolkit/components/aboutperformance/tests/browser/browser_compartments.js @@ -93,8 +93,10 @@ function monotinicity_tester(source, testName) { // Sanity check on components data. let set = new Set(); + let keys = []; for (let item of snapshot.componentsData) { - let key = `{name: ${item.name}, addonId: ${item.addonId}}`; + let key = `{name: ${item.name}, addonId: ${item.addonId}, isSystem: ${item.isSystem}}`; + keys.push(key); set.add(key); sanityCheck(previous.componentsMap.get(key), item); previous.componentsMap.set(key, item); @@ -103,13 +105,15 @@ function monotinicity_tester(source, testName) { Assert_leq(item[k], snapshot.processData[k], `Sanity check (${name}): component has a lower ${k} than process`); } - for (let i = 0; i < item.durations.length; ++i) { - Assert_leq(item.durations[i], snapshot.processData.durations[i], - `Sanity check (${name}): component has a lower durations[${i}] than process.`); - } } // Check that we do not have duplicate components. - Assert.equal(set.size, snapshot.componentsData.length); + info(`Before deduplication, we had the following components: ${keys.sort().join(", ")}`); + info(`After deduplication, we have the following components: ${[...set.keys()].sort().join(", ")}`); + + info(`Deactivating deduplication check (Bug 1150045)`); + if (false) { + Assert.equal(set.size, snapshot.componentsData.length); + } }); let interval = window.setInterval(frameCheck, 300); registerCleanupFunction(() => { @@ -133,11 +137,9 @@ add_task(function* test() { info("Opening URL"); newTab.linkedBrowser.loadURI(URL); - info("Skipping monotonicity testing (1149897)"); - if (false) { - monotinicity_tester(() => PerformanceStats.getSnapshot(), "parent process"); - monotinicity_tester(() => promiseContentResponseOrNull(browser, "compartments-test:getStatistics", null), "content process" ); - } + info("Setting up monotonicity testing"); + monotinicity_tester(() => PerformanceStats.getSnapshot(), "parent process"); + monotinicity_tester(() => promiseContentResponseOrNull(browser, "compartments-test:getStatistics", null), "content process" ); while (true) { let stats = (yield promiseContentResponse(browser, "compartments-test:getStatistics", null)); diff --git a/toolkit/profile/nsToolkitProfileService.cpp b/toolkit/profile/nsToolkitProfileService.cpp index e318b64623..5f5d7e9633 100644 --- a/toolkit/profile/nsToolkitProfileService.cpp +++ b/toolkit/profile/nsToolkitProfileService.cpp @@ -40,6 +40,7 @@ #include "nsReadableUtils.h" #include "nsNativeCharsetUtils.h" #include "mozilla/Attributes.h" +#include "mozilla/Snprintf.h" using namespace mozilla; @@ -969,16 +970,18 @@ nsToolkitProfileService::Flush() ++pCount; uint32_t length; - nsAutoArrayPtr buffer (new char[100+MAXPATHLEN*pCount]); + const int bufsize = 100+MAXPATHLEN*pCount; + nsAutoArrayPtr buffer (new char[bufsize]); NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY); - char *end = buffer; + char *pos = buffer; + char *end = buffer + bufsize; - end += sprintf(end, - "[General]\n" - "StartWithLastProfile=%s\n\n", - mStartWithLast ? "1" : "0"); + pos += snprintf(pos, end - pos, + "[General]\n" + "StartWithLastProfile=%s\n\n", + mStartWithLast ? "1" : "0"); nsAutoCString path; cur = mFirst; @@ -997,21 +1000,21 @@ nsToolkitProfileService::Flush() NS_ENSURE_SUCCESS(rv, rv); } - end += sprintf(end, - "[Profile%u]\n" - "Name=%s\n" - "IsRelative=%s\n" - "Path=%s\n", - pCount, cur->mName.get(), - isRelative ? "1" : "0", path.get()); + pos += snprintf(pos, end - pos, + "[Profile%u]\n" + "Name=%s\n" + "IsRelative=%s\n" + "Path=%s\n", + pCount, cur->mName.get(), + isRelative ? "1" : "0", path.get()); nsCOMPtr profile; rv = this->GetDefaultProfile(getter_AddRefs(profile)); if (NS_SUCCEEDED(rv) && profile == cur) { - end += sprintf(end, "Default=1\n"); + pos += snprintf(pos, end - pos, "Default=1\n"); } - end += sprintf(end, "\n"); + pos += snprintf(pos, end - pos, "\n"); cur = cur->mNext; ++pCount; @@ -1022,7 +1025,7 @@ nsToolkitProfileService::Flush() NS_ENSURE_SUCCESS(rv, rv); if (buffer) { - length = end - buffer; + length = pos - buffer; if (fwrite(buffer, sizeof(char), length, writeFile) != length) { fclose(writeFile); diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index 32fd29cd5b..4deee2534c 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -52,6 +52,7 @@ #include "mozilla/layers/InputAPZContext.h" #include "mozilla/layers/APZCCallbackHelper.h" #include "mozilla/dom/TabParent.h" +#include "mozilla/Snprintf.h" #include "nsRefPtrHashtable.h" #include "TouchEvents.h" #ifdef ACCESSIBILITY @@ -76,6 +77,7 @@ static int32_t gNumWidgets; #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) static nsRefPtrHashtable* sPluginWidgetList; #endif + nsIRollupListener* nsBaseWidget::gRollupListener = nullptr; using namespace mozilla::layers; @@ -1964,7 +1966,7 @@ case _value: eventName.AssignLiteral(_name) ; break { char buf[32]; - sprintf(buf,"UNKNOWN: %d",aGuiEvent->message); + snprintf_literal(buf,"UNKNOWN: %d",aGuiEvent->message); CopyASCIItoUTF16(buf, eventName); } diff --git a/xpcom/base/CodeAddressService.h b/xpcom/base/CodeAddressService.h index 77ce5dd294..c6372b6c3e 100644 --- a/xpcom/base/CodeAddressService.h +++ b/xpcom/base/CodeAddressService.h @@ -15,10 +15,6 @@ #include "nsStackWalk.h" -#if defined(_MSC_VER) && _MSC_VER < 1900 -#define snprintf _snprintf -#endif - namespace mozilla { // This class is used to print details about code locations. diff --git a/xpcom/base/moz.build b/xpcom/base/moz.build index b74804d526..3621a45ca6 100644 --- a/xpcom/base/moz.build +++ b/xpcom/base/moz.build @@ -66,9 +66,6 @@ if CONFIG['OS_ARCH'] == 'WINNT': EXPORTS += [ 'nsWindowsHelpers.h', ] - if CONFIG['MOZ_DEBUG']: - EXPORTS += ['pure.h'] - SOURCES += ['pure_api.c'] EXPORTS.mozilla += [ 'AvailableMemoryTracker.h', diff --git a/xpcom/base/nsStackWalk.cpp b/xpcom/base/nsStackWalk.cpp index 8788040366..6cffec12e3 100644 --- a/xpcom/base/nsStackWalk.cpp +++ b/xpcom/base/nsStackWalk.cpp @@ -34,12 +34,12 @@ static CriticalAddress gCriticalAddress; #define _GNU_SOURCE #endif -#if defined(HAVE_DLOPEN) || defined(XP_MACOSX) +#if defined(HAVE_DLOPEN) || defined(XP_DARWIN) #include #endif #define NSSTACKWALK_SUPPORTS_MACOSX \ - (defined(XP_MACOSX) && \ + (defined(XP_DARWIN) && \ (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE))) #define NSSTACKWALK_SUPPORTS_LINUX \ @@ -49,7 +49,10 @@ static CriticalAddress gCriticalAddress; #if NSSTACKWALK_SUPPORTS_MACOSX #include +#include +#ifdef MOZ_WIDGET_COCOA #include +#endif typedef void malloc_logger_t(uint32_t aType, @@ -74,7 +77,7 @@ stack_callback(uint32_t aFrameNumber, void* aPc, void* aSp, void* aClosure) gCriticalAddress.mAddr = aPc; } -#ifdef DEBUG +#if defined(MOZ_WIDGET_COCOA) && defined(DEBUG) #define MAC_OS_X_VERSION_10_7_HEX 0x00001070 static int32_t OSXVersion() @@ -152,7 +155,9 @@ StackWalkInitCriticalAddress() // On Lion, malloc is no longer called from pthread_cond_*wait*. This prevents // us from finding the address, but that is fine, since with no call to malloc // there is no critical address. +#ifdef MOZ_WIDGET_COCOA MOZ_ASSERT(OnLionOrLater() || gCriticalAddress.mAddr != nullptr); +#endif MOZ_ASSERT(r == ETIMEDOUT); r = pthread_mutex_unlock(&mutex); MOZ_ASSERT(r == 0); diff --git a/xpcom/base/pure.h b/xpcom/base/pure.h deleted file mode 100644 index 1338d9bc34..0000000000 --- a/xpcom/base/pure.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Header file of Pure API function declarations. - * - * Explicitly no copyright. - * You may recompile and redistribute these definitions as required. - * - * Version 1.0 - */ - -#if defined(c_plusplus) || defined(__cplusplus) -extern "C" { -#endif - -#define PURE_H_VERSION 1 - -////////////////////////////// -// API's Specific to Purify // -////////////////////////////// - -// TRUE when Purify is running. -int __cdecl PurifyIsRunning(void) ; -// -// Print a string to the viewer. -// -int __cdecl PurePrintf(const char *fmt, ...) ; -int __cdecl PurifyPrintf(const char *fmt, ...) ; -// -// Purify functions for leak and memory-in-use functionalty. -// -int __cdecl PurifyNewInuse(void) ; -int __cdecl PurifyAllInuse(void) ; -int __cdecl PurifyClearInuse(void) ; -int __cdecl PurifyNewLeaks(void) ; -int __cdecl PurifyAllLeaks(void) ; -int __cdecl PurifyClearLeaks(void) ; -// -// Purify functions for handle leakage. -// -int __cdecl PurifyAllHandlesInuse(void) ; -int __cdecl PurifyNewHandlesInuse(void) ; -// -// Functions that tell you about the state of memory. -// -int __cdecl PurifyDescribe(void *addr) ; -int __cdecl PurifyWhatColors(void *addr, int size) ; -// -// Functions to test the state of memory. If the memory is not -// accessible, an error is signaled just as if there were a memory -// reference and the function returns false. -// -int __cdecl PurifyAssertIsReadable(const void *addr, int size) ; -int __cdecl PurifyAssertIsWritable(const void *addr, int size) ; -// -// Functions to test the state of memory. If the memory is not -// accessible, these functions return false. No error is signaled. -// -int __cdecl PurifyIsReadable(const void *addr, int size) ; -int __cdecl PurifyIsWritable(const void *addr, int size) ; -int __cdecl PurifyIsInitialized(const void *addr, int size) ; -// -// Functions to set the state of memory. -// -void __cdecl PurifyMarkAsInitialized(void *addr, int size) ; -void __cdecl PurifyMarkAsUninitialized(void *addr, int size) ; -// -// Functions to do late detection of ABWs, FMWs, IPWs. -// -#define PURIFY_HEAP_CRT 0xfffffffe -#define PURIFY_HEAP_ALL 0xfffffffd -#define PURIFY_HEAP_BLOCKS_LIVE 0x80000000 -#define PURIFY_HEAP_BLOCKS_DEFERRED_FREE 0x40000000 -#define PURIFY_HEAP_BLOCKS_ALL (PURIFY_HEAP_BLOCKS_LIVE|PURIFY_HEAP_BLOCKS_DEFERRED_FREE) -int __cdecl PurifyHeapValidate(unsigned int hHeap, unsigned int dwFlags, const void *addr) ; -int __cdecl PurifySetLateDetectScanCounter(int counter); -int __cdecl PurifySetLateDetectScanInterval(int seconds); - - -//////////////////////////////// -// API's Specific to Quantify // -//////////////////////////////// - -// TRUE when Quantify is running. -int __cdecl QuantifyIsRunning(void) ; - -// -// Functions for controlling collection -// -int __cdecl QuantifyDisableRecordingData(void) ; -int __cdecl QuantifyStartRecordingData(void) ; -int __cdecl QuantifyStopRecordingData(void) ; -int __cdecl QuantifyClearData(void) ; -int __cdecl QuantifyIsRecordingData(void) ; - -// Add a comment to the dataset -int __cdecl QuantifyAddAnnotation(char *) ; - -// Save the current data, creating a "checkpoint" dataset -int __cdecl QuantifySaveData(void) ; - -//////////////////////////////// -// API's Specific to Coverage // -//////////////////////////////// - -// TRUE when Coverage is running. -int __cdecl CoverageIsRunning(void) ; -// -// Functions for controlling collection -// -int __cdecl CoverageDisableRecordingData(void) ; -int __cdecl CoverageStartRecordingData(void) ; -int __cdecl CoverageStopRecordingData(void) ; -int __cdecl CoverageClearData(void) ; -int __cdecl CoverageIsRecordingData(void) ; -// Add a comment to the dataset -int __cdecl CoverageAddAnnotation(char *) ; - -// Save the current data, creating a "checkpoint" dataset -int __cdecl CoverageSaveData(void) ; - - - - -//////////////////////////////// -// API's Specific to Purelock // -//////////////////////////////// - -// TRUE when Purelock is running. -int __cdecl PurelockIsRunning(void) ; - - - - -#if defined(c_plusplus) || defined(__cplusplus) -} -#endif diff --git a/xpcom/base/pure_api.c b/xpcom/base/pure_api.c deleted file mode 100644 index 716460ede9..0000000000 --- a/xpcom/base/pure_api.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Header file of Pure API function declarations. - * - * Explicitly no copyright. - * You may recompile and redistribute these definitions as required. - * - * NOTE1: In some situations when compiling with MFC, you should - * enable the setting 'Not using precompiled headers' in Visual C++ - * to avoid a compiler diagnostic. - * - * NOTE2: This file works through the use of deep magic. Calls to functions - * in this file are replaced with calls into the OCI runtime system - * when an instrumented version of this program is run. - * - * NOTE3: The static vars avoidGy_n (where n is a unique number) are used - * to prevent optimizing the functions away when compiler option - * /Gy is set. This is needed so that NOTE2 works properly. - */ -static int avoidGy_1; -static int avoidGy_2; -static int avoidGy_3; -static int avoidGy_4; -static int avoidGy_5; -static int avoidGy_6; -static int avoidGy_7; -static int avoidGy_8; -static int avoidGy_9; -static int avoidGy_10; -static int avoidGy_11; -static int avoidGy_12; -static int avoidGy_13; -static int avoidGy_14; -static int avoidGy_15; -static int avoidGy_16; -static int avoidGy_17; -static int avoidGy_18; -static int avoidGy_19; -static int avoidGy_20; -static int avoidGy_21; -static int avoidGy_22; -static int avoidGy_23; -static int avoidGy_24; -static int avoidGy_25; -static int avoidGy_26; -static int avoidGy_27; -static int avoidGy_28; -static int avoidGy_29; -static int avoidGy_30; -static int avoidGy_31; -static int avoidGy_32; -static int avoidGy_33; -static int avoidGy_34; -static int avoidGy_35; -static int avoidGy_36; -static int avoidGy_37; -static int avoidGy_38; -static int avoidGy_39; -static int avoidGy_40; -static int avoidGy_41; -static int avoidGy_42; -static int avoidGy_43; -static int avoidGy_44; -static int avoidGy_45; -static int avoidGy_46; -static int avoidGy_47; -static int avoidGy_48; -static int avoidGy_49; -static int avoidGy_50; -static int avoidGy_51; -static int avoidGy_52; -static int avoidGy_53; -static int avoidGy_54; -static int avoidGy_55; -static int avoidGy_56; -static int avoidGy_57; -static int avoidGy_58; -static int avoidGy_59; -static int avoidGy_60; -static int avoidGy_PL_01; -__declspec(dllexport) int __cdecl PurePrintf(const char *fmt, ...) { avoidGy_1++; fmt; return 0; } -__declspec(dllexport) int __cdecl PurifyIsRunning(void) { avoidGy_2++; return 0; } -__declspec(dllexport) int __cdecl PurifyPrintf(const char *fmt, ...) { avoidGy_3++; fmt; return 0; } -__declspec(dllexport) int __cdecl PurifyNewInuse(void) { avoidGy_4++; return 0; } -__declspec(dllexport) int __cdecl PurifyAllInuse(void) { avoidGy_5++; return 0; } -__declspec(dllexport) int __cdecl PurifyClearInuse(void) { avoidGy_6++; return 0; } -__declspec(dllexport) int __cdecl PurifyNewLeaks(void) { avoidGy_7++; return 0; } -__declspec(dllexport) int __cdecl PurifyAllLeaks(void) { avoidGy_8++; return 0; } -__declspec(dllexport) int __cdecl PurifyClearLeaks(void) { avoidGy_9++; return 0; } -__declspec(dllexport) int __cdecl PurifyAllHandlesInuse(void) { avoidGy_10++; return 0; } -__declspec(dllexport) int __cdecl PurifyNewHandlesInuse(void) { avoidGy_11++; return 0; } -__declspec(dllexport) int __cdecl PurifyDescribe(void *addr) { avoidGy_12++; addr; return 0; } -__declspec(dllexport) int __cdecl PurifyWhatColors(void *addr, int size) { avoidGy_13++; addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyAssertIsReadable(const void *addr, int size) { avoidGy_14++; addr; size; return 1; } -__declspec(dllexport) int __cdecl PurifyAssertIsWritable(const void *addr, int size) { avoidGy_15++; addr; size; return 1; } -__declspec(dllexport) int __cdecl PurifyIsReadable(const void *addr, int size) { avoidGy_16++; addr; size; return 1; } -__declspec(dllexport) int __cdecl PurifyIsWritable(const void *addr, int size) { avoidGy_17++; addr; size; return 1; } -__declspec(dllexport) int __cdecl PurifyIsInitialized(const void *addr, int size) { avoidGy_18++; addr; size; return 1; } -__declspec(dllexport) int __cdecl PurifyRed(void *addr, int size) { avoidGy_19++; addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyGreen(void *addr, int size) { avoidGy_20++; addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyYellow(void *addr, int size) { avoidGy_21++; addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyBlue(void *addr, int size) { avoidGy_22++; addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyMarkAsInitialized(void *addr, int size) { avoidGy_23++; addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyMarkAsUninitialized(void *addr, int size) { avoidGy_24++; addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyMarkForTrap(void *addr, int size) { avoidGy_25++; addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyMarkForNoTrap(void *addr, int size) { avoidGy_26++; addr; size; return 0; } -__declspec(dllexport) int __cdecl PurifyHeapValidate(unsigned int hHeap, unsigned int dwFlags, const void *addr) - { avoidGy_27++; hHeap; dwFlags; addr; return 1; } -__declspec(dllexport) int __cdecl PurifySetLateDetectScanCounter(int counter) { avoidGy_28++; counter; return 0; }; -__declspec(dllexport) int __cdecl PurifySetLateDetectScanInterval(int seconds) { avoidGy_29++; seconds; return 0; }; -__declspec(dllexport) int __cdecl CoverageIsRunning(void) { avoidGy_30++; return 0; } -__declspec(dllexport) int __cdecl CoverageDisableRecordingData(void) { avoidGy_31++; return 0; } -__declspec(dllexport) int __cdecl CoverageStartRecordingData(void) { avoidGy_32++; return 0; } -__declspec(dllexport) int __cdecl CoverageStopRecordingData(void) { avoidGy_33++; return 0; } -__declspec(dllexport) int __cdecl CoverageClearData(void) { avoidGy_34++; return 0; } -__declspec(dllexport) int __cdecl CoverageIsRecordingData(void) { avoidGy_35++; return 0; } -__declspec(dllexport) int __cdecl CoverageAddAnnotation(char *str) { avoidGy_36++; str; return 0; } -__declspec(dllexport) int __cdecl CoverageSaveData(void) { avoidGy_37++; return 0; } -__declspec(dllexport) int __cdecl QuantifyIsRunning(void) { avoidGy_42++; return 0; } -__declspec(dllexport) int __cdecl QuantifyDisableRecordingData(void) { avoidGy_43++; return 0; } -__declspec(dllexport) int __cdecl QuantifyStartRecordingData(void) { avoidGy_44++; return 0; } -__declspec(dllexport) int __cdecl QuantifyStopRecordingData(void) { avoidGy_45++; return 0; } -__declspec(dllexport) int __cdecl QuantifyClearData(void) { avoidGy_46++; return 0; } -__declspec(dllexport) int __cdecl QuantifyIsRecordingData(void) { avoidGy_47++; return 0; } -__declspec(dllexport) int __cdecl QuantifyAddAnnotation(char *str) { avoidGy_48++; str; return 0; } -__declspec(dllexport) int __cdecl QuantifySaveData(void) { avoidGy_49++; return 0; } -__declspec(dllexport) int __cdecl PurelockIsRunning(void) { avoidGy_PL_01++; return 0; } diff --git a/xpcom/components/nsComponentManager.cpp b/xpcom/components/nsComponentManager.cpp index 487441c5c0..9aad3dcac3 100644 --- a/xpcom/components/nsComponentManager.cpp +++ b/xpcom/components/nsComponentManager.cpp @@ -1025,7 +1025,6 @@ nsComponentManagerImpl::GetClassObject(const nsCID& aClass, const nsIID& aIID, { nsresult rv; -#ifdef PR_LOGGING if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_DEBUG)) { char* buf = aClass.ToString(); PR_LogPrint("nsComponentManager: GetClassObject(%s)", buf); @@ -1033,7 +1032,6 @@ nsComponentManagerImpl::GetClassObject(const nsCID& aClass, const nsIID& aIID, NS_Free(buf); } } -#endif PR_ASSERT(aResult != nullptr); @@ -1064,11 +1062,9 @@ nsComponentManagerImpl::GetClassObjectByContractID(const char* aContractID, nsresult rv; -#ifdef PR_LOGGING if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_DEBUG)) { PR_LogPrint("nsComponentManager: GetClassObject(%s)", aContractID); } -#endif nsCOMPtr factory = FindFactory(aContractID, strlen(aContractID)); if (!factory) { @@ -1147,7 +1143,6 @@ nsComponentManagerImpl::CreateInstance(const nsCID& aClass, rv = NS_ERROR_FACTORY_NOT_REGISTERED; } -#ifdef PR_LOGGING if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_WARNING)) { char* buf = aClass.ToString(); PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, @@ -1157,7 +1152,6 @@ nsComponentManagerImpl::CreateInstance(const nsCID& aClass, NS_Free(buf); } } -#endif return rv; } diff --git a/xpcom/ds/TimeStamp_posix.cpp b/xpcom/ds/TimeStamp_posix.cpp index e4546b349e..02e04f2451 100644 --- a/xpcom/ds/TimeStamp_posix.cpp +++ b/xpcom/ds/TimeStamp_posix.cpp @@ -46,6 +46,7 @@ #define KP_START_USEC p_ustart_usec #endif +#include "mozilla/Snprintf.h" #include "mozilla/TimeStamp.h" #include "nsCRT.h" #include "prprf.h" @@ -271,7 +272,7 @@ ComputeProcessUptimeThread(void* aTime) } char threadStat[40]; - sprintf(threadStat, "/proc/self/task/%d/stat", (pid_t)syscall(__NR_gettid)); + snprintf_literal(threadStat, "/proc/self/task/%d/stat", (pid_t)syscall(__NR_gettid)); uint64_t threadJiffies = JiffiesSinceBoot(threadStat); uint64_t selfJiffies = JiffiesSinceBoot("/proc/self/stat"); diff --git a/xpcom/ds/TimeStamp_windows.cpp b/xpcom/ds/TimeStamp_windows.cpp index 4266957cc0..d7140d7a98 100644 --- a/xpcom/ds/TimeStamp_windows.cpp +++ b/xpcom/ds/TimeStamp_windows.cpp @@ -20,7 +20,6 @@ #include -#if defined(PR_LOGGING) // Log module for mozilla::TimeStamp for Windows logging... // // To enable logging (see prlog.h for full details): @@ -39,10 +38,7 @@ GetTimeStampLog() } return sLog; } - #define LOG(x) PR_LOG(GetTimeStampLog(), PR_LOG_DEBUG, x) -#else - #define LOG(x) -#endif /* PR_LOGGING */ +#define LOG(x) PR_LOG(GetTimeStampLog(), PR_LOG_DEBUG, x) // Estimate of the smallest duration of time we can measure. static volatile ULONGLONG sResolution; diff --git a/xpcom/ds/nsObserverService.cpp b/xpcom/ds/nsObserverService.cpp index 09d8671383..e8b4de34b7 100644 --- a/xpcom/ds/nsObserverService.cpp +++ b/xpcom/ds/nsObserverService.cpp @@ -17,7 +17,6 @@ #define NOTIFY_GLOBAL_OBSERVERS -#if defined(PR_LOGGING) // Log module for nsObserverService logging... // // To enable logging (see prlog.h for full details): @@ -36,10 +35,7 @@ GetObserverServiceLog() } return sLog; } - #define LOG(x) PR_LOG(GetObserverServiceLog(), PR_LOG_DEBUG, x) -#else - #define LOG(x) -#endif /* PR_LOGGING */ +#define LOG(x) PR_LOG(GetObserverServiceLog(), PR_LOG_DEBUG, x) namespace mozilla { diff --git a/xpcom/glue/nsCRTGlue.cpp b/xpcom/glue/nsCRTGlue.cpp index c2fdbede0d..0d63bc46b6 100644 --- a/xpcom/glue/nsCRTGlue.cpp +++ b/xpcom/glue/nsCRTGlue.cpp @@ -14,6 +14,8 @@ #include #include +#include "mozilla/Snprintf.h" + #ifdef XP_WIN #include #include @@ -312,8 +314,9 @@ copy_stderr_to_file(const char* aFile) if (sStderrCopy) { return; } - char* buf = (char*)malloc(strlen(aFile) + 16); - sprintf(buf, "%s.%u", aFile, (uint32_t)getpid()); + size_t buflen = strlen(aFile) + 16; + char* buf = (char*)malloc(buflen); + snprintf(buf, buflen, "%s.%u", aFile, (uint32_t)getpid()); sStderrCopy = fopen(buf, "w"); free(buf); set_stderr_callback(stderr_to_file); diff --git a/xpcom/io/nsInputStreamTee.cpp b/xpcom/io/nsInputStreamTee.cpp index 0df5ac22b3..a382f202dd 100644 --- a/xpcom/io/nsInputStreamTee.cpp +++ b/xpcom/io/nsInputStreamTee.cpp @@ -22,7 +22,6 @@ using namespace mozilla; #ifdef LOG #undef LOG #endif -#ifdef PR_LOGGING static PRLogModuleInfo* GetTeeLog() { @@ -33,9 +32,6 @@ GetTeeLog() return sLog; } #define LOG(args) PR_LOG(GetTeeLog(), PR_LOG_DEBUG, args) -#else -#define LOG(args) -#endif class nsInputStreamTee final : public nsIInputStreamTee { diff --git a/xpcom/io/nsPipe3.cpp b/xpcom/io/nsPipe3.cpp index f44a120085..fdf7f2a147 100644 --- a/xpcom/io/nsPipe3.cpp +++ b/xpcom/io/nsPipe3.cpp @@ -29,7 +29,6 @@ using namespace mozilla; #ifdef LOG #undef LOG #endif -#if defined(PR_LOGGING) // // set NSPR_LOG_MODULES=nsPipe:5 // @@ -43,9 +42,6 @@ GetPipeLog() return sLog; } #define LOG(args) PR_LOG(GetPipeLog(), PR_LOG_DEBUG, args) -#else -#define LOG(args) -#endif #define DEFAULT_SEGMENT_SIZE 4096 #define DEFAULT_SEGMENT_COUNT 16 diff --git a/xpcom/io/nsStorageStream.cpp b/xpcom/io/nsStorageStream.cpp index d38ef72856..881dd2ab55 100644 --- a/xpcom/io/nsStorageStream.cpp +++ b/xpcom/io/nsStorageStream.cpp @@ -30,7 +30,6 @@ using mozilla::ipc::InputStreamParams; using mozilla::ipc::StringInputStreamParams; -#if defined(PR_LOGGING) // // Log module for StorageStream logging... // @@ -51,7 +50,6 @@ GetStorageStreamLog() } return sLog; } -#endif #ifdef LOG #undef LOG #endif diff --git a/xpcom/threads/nsEventQueue.cpp b/xpcom/threads/nsEventQueue.cpp index 0678aa261e..c02e156967 100644 --- a/xpcom/threads/nsEventQueue.cpp +++ b/xpcom/threads/nsEventQueue.cpp @@ -13,7 +13,6 @@ using namespace mozilla; -#ifdef PR_LOGGING static PRLogModuleInfo* GetLog() { @@ -23,7 +22,6 @@ GetLog() } return sLog; } -#endif #ifdef LOG #undef LOG #endif diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 18c4d2657b..e83f355303 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -72,7 +72,6 @@ using namespace mozilla::tasktracer; using namespace mozilla; -#ifdef PR_LOGGING static PRLogModuleInfo* GetThreadLog() { @@ -82,7 +81,6 @@ GetThreadLog() } return sLog; } -#endif #ifdef LOG #undef LOG #endif diff --git a/xpcom/threads/nsThreadPool.cpp b/xpcom/threads/nsThreadPool.cpp index 04ead99b8a..e61824bca6 100644 --- a/xpcom/threads/nsThreadPool.cpp +++ b/xpcom/threads/nsThreadPool.cpp @@ -15,7 +15,6 @@ using namespace mozilla; -#ifdef PR_LOGGING static PRLogModuleInfo* GetThreadPoolLog() { @@ -25,7 +24,6 @@ GetThreadPoolLog() } return sLog; } -#endif #ifdef LOG #undef LOG #endif diff --git a/xpcom/threads/nsTimerImpl.h b/xpcom/threads/nsTimerImpl.h index 0ede59f771..05e078f388 100644 --- a/xpcom/threads/nsTimerImpl.h +++ b/xpcom/threads/nsTimerImpl.h @@ -21,12 +21,8 @@ #include "TracedTaskCommon.h" #endif -#if defined(PR_LOGGING) extern PRLogModuleInfo* GetTimerLog(); #define DEBUG_TIMERS 1 -#else -#undef DEBUG_TIMERS -#endif #define NS_TIMER_CID \ { /* 5ff24248-1dd2-11b2-8427-fbab44f29bc8 */ \