Bug 1677045 - Replace JS_MORE_DETERMINISTIC with a runtime flag. r=jandem

Differential Revision: https://phabricator.services.mozilla.com/D96973
This commit is contained in:
Christian Holler 2020-12-03 08:23:07 +00:00
Родитель b8d4e0b10e
Коммит d491828238
22 изменённых файлов: 238 добавлений и 171 удалений

Просмотреть файл

@ -456,22 +456,6 @@ set_define("JS_MASM_VERBOSE", depends_if("--enable-masm-verbose")(lambda _: True
set_config("JS_MASM_VERBOSE", depends_if("--enable-masm-verbose")(lambda _: True))
option(
"--enable-more-deterministic",
env="JS_MORE_DETERMINISTIC",
help="Enable changes that make the shell more deterministic",
)
@depends("--enable-more-deterministic")
def more_deterministic(value):
if value:
return True
set_define("JS_MORE_DETERMINISTIC", more_deterministic)
# CTypes
# =======================================================
@depends(js_standalone)

Просмотреть файл

@ -26,6 +26,7 @@
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/PropertySpec.h"
#include "js/Wrapper.h"
#include "util/DifferentialTesting.h"
#include "util/Windows.h"
#include "vm/ArrayBufferObject.h"
#include "vm/GlobalObject.h"
@ -455,12 +456,10 @@ bool DataViewObject::write(JSContext* cx, Handle<DataViewObject*> obj,
return false;
}
#ifdef JS_MORE_DETERMINISTIC
// See the comment in ElementSpecific::doubleToNative.
if (TypeIsFloatingPoint<NativeType>()) {
if (js::SupportDifferentialTesting() && TypeIsFloatingPoint<NativeType>()) {
value = JS::CanonicalizeNaN(value);
}
#endif
// Step 6.
bool isLittleEndian = args.length() >= 3 && ToBoolean(args[2]);

Просмотреть файл

@ -89,6 +89,7 @@
# include "unicode/utypes.h"
# include "unicode/uversion.h"
#endif
#include "util/DifferentialTesting.h"
#include "util/StringBuffer.h"
#include "util/Text.h"
#include "vm/AsyncFunction.h"
@ -418,15 +419,6 @@ static bool GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
#ifdef JS_MORE_DETERMINISTIC
value = BooleanValue(true);
#else
value = BooleanValue(false);
#endif
if (!JS_SetProperty(cx, info, "more-deterministic", value)) {
return false;
}
#ifdef MOZ_PROFILING
value = BooleanValue(true);
#else
@ -588,9 +580,7 @@ static bool GC(JSContext* cx, unsigned argc, Value* vp) {
}
}
#ifndef JS_MORE_DETERMINISTIC
size_t preBytes = cx->runtime()->gc.heapSize.bytes();
#endif
if (zone) {
PrepareForDebugGC(cx->runtime());
@ -601,10 +591,10 @@ static bool GC(JSContext* cx, unsigned argc, Value* vp) {
JS::NonIncrementalGC(cx, gckind, reason);
char buf[256] = {'\0'};
#ifndef JS_MORE_DETERMINISTIC
SprintfLiteral(buf, "before %zu, after %zu\n", preBytes,
cx->runtime()->gc.heapSize.bytes());
#endif
if (!js::SupportDifferentialTesting()) {
SprintfLiteral(buf, "before %zu, after %zu\n", preBytes,
cx->runtime()->gc.heapSize.bytes());
}
return ReturnStringCopy(cx, args, buf);
}
@ -3059,11 +3049,11 @@ static bool DumpHeap(JSContext* cx, unsigned argc, Value* vp) {
}
static bool Terminate(JSContext* cx, unsigned arg, Value* vp) {
#ifdef JS_MORE_DETERMINISTIC
// Print a message to stderr in more-deterministic builds to help jsfunfuzz
// Print a message to stderr in differential testing to help jsfunfuzz
// find uncatchable-exception bugs.
fprintf(stderr, "terminate called\n");
#endif
if (js::SupportDifferentialTesting()) {
fprintf(stderr, "terminate called\n");
}
JS_ClearPendingException(cx);
return false;
@ -3948,16 +3938,18 @@ static bool DetachArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
static bool HelperThreadCount(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
#ifdef JS_MORE_DETERMINISTIC
// Always return 0 to get consistent output with and without --no-threads.
args.rval().setInt32(0);
#else
if (js::SupportDifferentialTesting()) {
// Always return 0 to get consistent output with and without --no-threads.
args.rval().setInt32(0);
return true;
}
if (CanUseExtraThreads()) {
args.rval().setInt32(HelperThreadState().threadCount);
} else {
args.rval().setInt32(0);
}
#endif
return true;
}
@ -4040,6 +4032,14 @@ static bool SharedArrayRawBufferRefcount(JSContext* cx, unsigned argc,
#ifdef NIGHTLY_BUILD
static bool ObjectAddress(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (js::SupportDifferentialTesting()) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee,
"Function unavailable in differential testing mode.");
return false;
}
if (args.length() != 1) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
@ -4051,20 +4051,23 @@ static bool ObjectAddress(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
# ifdef JS_MORE_DETERMINISTIC
args.rval().setInt32(0);
return true;
# else
void* ptr = js::UncheckedUnwrap(&args[0].toObject(), true);
char buffer[64];
SprintfLiteral(buffer, "%p", ptr);
return ReturnStringCopy(cx, args, buffer);
# endif
}
static bool SharedAddress(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (js::SupportDifferentialTesting()) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee,
"Function unavailable in differential testing mode.");
return false;
}
if (args.length() != 1) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
@ -4076,9 +4079,6 @@ static bool SharedAddress(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
# ifdef JS_MORE_DETERMINISTIC
args.rval().setString(cx->staticStrings().getUint(0));
# else
RootedObject obj(cx, CheckedUnwrapStatic(&args[0].toObject()));
if (!obj) {
ReportAccessDenied(cx);
@ -4100,7 +4100,6 @@ static bool SharedAddress(JSContext* cx, unsigned argc, Value* vp) {
}
args.rval().setString(str);
# endif
return true;
}
@ -6106,14 +6105,12 @@ static bool BaselineCompile(JSContext* cx, unsigned argc, Value* vp) {
const char* returnedStr = nullptr;
do {
#ifdef JS_MORE_DETERMINISTIC
// In order to check for differential behaviour, baselineCompile should have
// the same output whether --no-baseline is used or not.
if (fuzzingSafe) {
returnedStr = "skipped (fuzzing-safe)";
if (js::SupportDifferentialTesting()) {
returnedStr = "skipped (differential testing)";
break;
}
#endif
AutoRealm ar(cx, script);
if (script->hasBaselineScript()) {

Просмотреть файл

@ -233,6 +233,7 @@
#include "js/Object.h" // JS::GetClass
#include "js/SliceBudget.h"
#include "proxy/DeadObjectProxy.h"
#include "util/DifferentialTesting.h"
#include "util/Poison.h"
#include "util/Windows.h"
#include "vm/BigIntType.h"
@ -6941,7 +6942,10 @@ GCRuntime::IncrementalResult GCRuntime::budgetIncrementalGC(
}
void GCRuntime::maybeIncreaseSliceBudget(SliceBudget& budget) {
#ifndef JS_MORE_DETERMINISTIC
if (js::SupportDifferentialTesting()) {
return;
}
// Increase time budget for long-running incremental collections. Enforce a
// minimum time budget that increases linearly with time/slice count up to a
// maximum.
@ -6966,7 +6970,6 @@ void GCRuntime::maybeIncreaseSliceBudget(SliceBudget& budget) {
budget = SliceBudget(TimeBudget(minBudget));
}
}
#endif // JS_MORE_DETERMINISTIC
}
static void ScheduleZones(GCRuntime* gc) {
@ -8524,7 +8527,7 @@ static bool ZoneGCNumberGetter(JSContext* cx, unsigned argc, Value* vp) {
return true;
}
#ifdef JS_MORE_DETERMINISTIC
#ifdef DEBUG
static bool DummyGetter(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setUndefined();
@ -8554,11 +8557,14 @@ JSObject* NewMemoryInfoObject(JSContext* cx) {
{"sliceCount", GCSliceCountGetter}};
for (auto pair : getters) {
#ifdef JS_MORE_DETERMINISTIC
JSNative getter = DummyGetter;
#else
JSNative getter = pair.getter;
#ifdef DEBUG
if (js::SupportDifferentialTesting()) {
getter = DummyGetter;
}
#endif
if (!JS_DefineProperty(cx, obj, pair.name, getter, nullptr,
JSPROP_ENUMERATE)) {
return nullptr;
@ -8585,11 +8591,14 @@ JSObject* NewMemoryInfoObject(JSContext* cx) {
{"gcNumber", ZoneGCNumberGetter}};
for (auto pair : zoneGetters) {
#ifdef JS_MORE_DETERMINISTIC
JSNative getter = DummyGetter;
#else
JSNative getter = pair.getter;
#ifdef DEBUG
if (js::SupportDifferentialTesting()) {
getter = DummyGetter;
}
#endif
if (!JS_DefineProperty(cx, zoneObj, pair.name, getter, nullptr,
JSPROP_ENUMERATE)) {
return nullptr;

Просмотреть файл

@ -25,6 +25,7 @@
#include "gc/PublicIterators.h"
#include "jit/JitFrames.h"
#include "jit/JitRealm.h"
#include "util/DifferentialTesting.h"
#include "util/Poison.h"
#include "vm/ArrayObject.h"
#include "vm/JSONPrinter.h"
@ -219,9 +220,7 @@ js::Nursery::Nursery(GCRuntime* gc)
canAllocateBigInts_(true),
reportTenurings_(0),
minorGCTriggerReason_(JS::GCReason::NO_REASON),
#ifndef JS_MORE_DETERMINISTIC
smoothedGrowthFactor(1.0),
#endif
decommitTask(gc)
#ifdef JS_GC_ZEAL
,
@ -1552,13 +1551,11 @@ size_t js::Nursery::targetSize(JSGCInvocationKind kind, JS::GCReason reason) {
// Calculate the fraction of time spent collecting the nursery.
double timeFraction = 0.0;
#ifndef JS_MORE_DETERMINISTIC
if (lastResizeTime) {
if (lastResizeTime && !js::SupportDifferentialTesting()) {
TimeDuration collectorTime = now - collectionStartTime();
TimeDuration totalTime = now - lastResizeTime;
timeFraction = collectorTime.ToSeconds() / totalTime.ToSeconds();
}
#endif
// Adjust the nursery size to try to achieve a target promotion rate and
// collector time goals.
@ -1572,17 +1569,16 @@ size_t js::Nursery::targetSize(JSGCInvocationKind kind, JS::GCReason reason) {
static const double GrowthRange = 2.0;
growthFactor = ClampDouble(growthFactor, 1.0 / GrowthRange, GrowthRange);
#ifndef JS_MORE_DETERMINISTIC
// Use exponential smoothing on the desired growth rate to take into account
// the promotion rate from recent previous collections.
if (lastResizeTime &&
now - lastResizeTime < TimeDuration::FromMilliseconds(200)) {
now - lastResizeTime < TimeDuration::FromMilliseconds(200) &&
!js::SupportDifferentialTesting()) {
growthFactor = 0.75 * smoothedGrowthFactor + 0.25 * growthFactor;
}
lastResizeTime = now;
smoothedGrowthFactor = growthFactor;
#endif
// Leave size untouched if we are close to the promotion goal.
static const double GoalWidth = 1.5;
@ -1599,10 +1595,12 @@ size_t js::Nursery::targetSize(JSGCInvocationKind kind, JS::GCReason reason) {
}
void js::Nursery::clearRecentGrowthData() {
#ifndef JS_MORE_DETERMINISTIC
if (js::SupportDifferentialTesting()) {
return;
}
lastResizeTime = TimeStamp();
smoothedGrowthFactor = 1.0;
#endif
}
/* static */

Просмотреть файл

@ -518,10 +518,8 @@ class Nursery {
};
PreviousGC previousGC;
#ifndef JS_MORE_DETERMINISTIC
mozilla::TimeStamp lastResizeTime;
double smoothedGrowthFactor;
#endif
// Calculate the promotion rate of the most recent minor GC.
// The valid_for_tenuring parameter is used to return whether this

Просмотреть файл

@ -318,6 +318,7 @@
#include "js/HeapAPI.h"
#include "js/SliceBudget.h"
#include "threading/ProtectedData.h"
#include "util/DifferentialTesting.h"
namespace js {
@ -669,11 +670,13 @@ class GCSchedulingState {
void updateHighFrequencyMode(const mozilla::TimeStamp& lastGCTime,
const mozilla::TimeStamp& currentTime,
const GCSchedulingTunables& tunables) {
#ifndef JS_MORE_DETERMINISTIC
if (js::SupportDifferentialTesting()) {
return;
}
inHighFrequencyGCMode_ =
!lastGCTime.IsNull() &&
lastGCTime + tunables.highFrequencyThreshold() > currentTime;
#endif
}
};

Просмотреть файл

@ -33,6 +33,7 @@
#include "js/RegExpFlags.h"
#include "js/Value.h"
#include "threading/ExclusiveData.h"
#include "util/DifferentialTesting.h"
#include "vm/JSContext.h"
#include "vm/MutexIDs.h"
#include "vm/NativeObject.h"
@ -1094,14 +1095,12 @@ class StackLimitCheck {
// Use this to check for stack-overflows in C++ code.
bool HasOverflowed() {
bool overflowed = !js::CheckRecursionLimitDontReport(cx_);
#ifdef JS_MORE_DETERMINISTIC
if (overflowed) {
if (overflowed && js::SupportDifferentialTesting()) {
// We don't report overrecursion here, but we throw an exception later
// and this still affects differential testing. Mimic ReportOverRecursed
// (the fuzzers check for this particular string).
fprintf(stderr, "ReportOverRecursed called\n");
}
#endif
return overflowed;
}

Просмотреть файл

@ -37,6 +37,7 @@
#include "js/ScalarType.h" // js::Scalar::Type
#include "js/Value.h"
#include "js/Vector.h"
#include "util/DifferentialTesting.h"
#include "vm/ArrayObject.h"
#include "vm/BuiltinObjectKind.h"
#include "vm/EnvironmentObject.h"
@ -5418,11 +5419,7 @@ class MRandom : public MNullaryInstruction {
CompactBufferWriter& writer) const override;
bool canRecoverOnBailout() const override {
#ifdef JS_MORE_DETERMINISTIC
return false;
#else
return true;
#endif
return !js::SupportDifferentialTesting();
}
ALLOW_CLONE(MRandom)

Просмотреть файл

@ -17,6 +17,7 @@
#include "jit/CompileWrappers.h"
#include "jit/JitFrames.h"
#include "jit/JSJitFrameIter.h"
#include "util/DifferentialTesting.h"
#include "vm/ProxyObject.h"
#include "vm/Runtime.h"
@ -810,10 +811,10 @@ void MacroAssembler::canonicalizeFloat(FloatRegister reg) {
}
void MacroAssembler::canonicalizeFloatIfDeterministic(FloatRegister reg) {
#ifdef JS_MORE_DETERMINISTIC
// See the comment in TypedArrayObjectTemplate::getElement.
canonicalizeFloat(reg);
#endif // JS_MORE_DETERMINISTIC
if (js::SupportDifferentialTesting()) {
canonicalizeFloat(reg);
}
}
void MacroAssembler::canonicalizeDouble(FloatRegister reg) {
@ -824,10 +825,10 @@ void MacroAssembler::canonicalizeDouble(FloatRegister reg) {
}
void MacroAssembler::canonicalizeDoubleIfDeterministic(FloatRegister reg) {
#ifdef JS_MORE_DETERMINISTIC
// See the comment in TypedArrayObjectTemplate::getElement.
canonicalizeDouble(reg);
#endif // JS_MORE_DETERMINISTIC
if (js::SupportDifferentialTesting()) {
canonicalizeDouble(reg);
}
}
// ========================================================================

Просмотреть файл

@ -16,6 +16,7 @@
#include "jit/JitRuntime.h"
#include "jit/RangeAnalysis.h"
#include "js/ScalarType.h" // js::Scalar::Type
#include "util/DifferentialTesting.h"
#include "vm/TraceLogging.h"
#include "jit/MacroAssembler-inl.h"
@ -2152,7 +2153,11 @@ void CodeGeneratorX86Shared::visitOutOfLineWasmTruncateCheck(
void CodeGeneratorX86Shared::canonicalizeIfDeterministic(
Scalar::Type type, const LAllocation* value) {
#ifdef JS_MORE_DETERMINISTIC
#ifdef DEBUG
if (!js::SupportDifferentialTesting()) {
return;
}
switch (type) {
case Scalar::Float32: {
FloatRegister in = ToFloatRegister(value);
@ -2169,7 +2174,7 @@ void CodeGeneratorX86Shared::canonicalizeIfDeterministic(
break;
}
}
#endif // JS_MORE_DETERMINISTIC
#endif // DEBUG
}
void CodeGenerator::visitCopySignF(LCopySignF* lir) {

Просмотреть файл

@ -76,6 +76,7 @@
#include "js/Wrapper.h"
#include "proxy/DOMProxy.h"
#include "util/CompleteFile.h"
#include "util/DifferentialTesting.h"
#include "util/StringBuffer.h"
#include "util/Text.h"
#include "vm/AsyncFunction.h"
@ -5955,4 +5956,16 @@ JS_PUBLIC_API void NoteIntentionalCrash() {
#endif
}
#ifdef DEBUG
bool gSupportDifferentialTesting = false;
#endif // DEBUG
} // namespace js
#ifdef DEBUG
JS_PUBLIC_API void JS::SetSupportDifferentialTesting(bool value) {
js::gSupportDifferentialTesting = value;
}
#endif // DEBUG

Просмотреть файл

@ -3019,4 +3019,12 @@ enum class CompletionKind { Normal, Return, Throw };
} /* namespace js */
#ifdef DEBUG
namespace JS {
extern JS_PUBLIC_API void SetSupportDifferentialTesting(bool value);
}
#endif /* DEBUG */
#endif /* jsapi_h */

Просмотреть файл

@ -145,6 +145,7 @@
#include "threading/LockGuard.h"
#include "threading/Thread.h"
#include "util/CompleteFile.h" // js::FileContents, js::ReadCompleteFile
#include "util/DifferentialTesting.h"
#include "util/StringBuffer.h"
#include "util/Text.h"
#include "util/Windows.h"
@ -2880,11 +2881,11 @@ static bool Help(JSContext* cx, unsigned argc, Value* vp);
static bool Quit(JSContext* cx, unsigned argc, Value* vp) {
ShellContext* sc = GetShellContext(cx);
#ifdef JS_MORE_DETERMINISTIC
// Print a message to stderr in more-deterministic builds to help jsfunfuzz
// Print a message to stderr in differential testing to help jsfunfuzz
// find uncatchable-exception bugs.
fprintf(stderr, "quit called\n");
#endif
if (js::SupportDifferentialTesting()) {
fprintf(stderr, "quit called\n");
}
CallArgs args = CallArgsFromVp(argc, vp);
int32_t code;
@ -5589,6 +5590,14 @@ static bool DumpStencil(JSContext* cx, unsigned argc, Value* vp) {
}
static bool Parse(JSContext* cx, unsigned argc, Value* vp) {
// Parse returns local scope information with variables ordered
// differently, depending on the underlying JIT implementation.
if (js::SupportDifferentialTesting()) {
JS_ReportErrorASCII(cx,
"Function not available in differential testing mode.");
return false;
}
return FrontendTest(cx, argc, vp, "parse", DumpType::ParseNode);
}
@ -7727,6 +7736,12 @@ static bool DumpScopeChain(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject callee(cx, &args.callee());
if (js::SupportDifferentialTesting()) {
ReportUsageErrorASCII(
cx, callee, "Function not available in differential testing mode.");
return false;
}
if (args.length() != 1) {
ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
return false;
@ -7761,11 +7776,7 @@ static bool DumpScopeChain(JSContext* cx, unsigned argc, Value* vp) {
}
}
#ifndef JS_MORE_DETERMINISTIC
// Don't dump anything in more-deterministic builds because the output
// includes pointer values.
script->bodyScope()->dump();
#endif
args.rval().setUndefined();
return true;
@ -11003,6 +11014,12 @@ static int Shell(JSContext* cx, OptionParser* op, char** envp) {
(getenv("MOZ_FUZZING_SAFE") && getenv("MOZ_FUZZING_SAFE")[0] != '0');
}
#ifdef DEBUG
if (op->getBoolOption("differential-testing")) {
JS::SetSupportDifferentialTesting(true);
}
#endif
if (op->getBoolOption("disable-oom-functions")) {
disableOOMFunctions = true;
}
@ -11455,6 +11472,11 @@ int main(int argc, char** argv, char** envp) {
!op.addBoolOption('\0', "fuzzing-safe",
"Don't expose functions that aren't safe for "
"fuzzers to call") ||
#ifdef DEBUG
!op.addBoolOption('\0', "differential-testing",
"Avoid random/undefined behavior that disturbs "
"differential testing (correctness fuzzing)") ||
#endif
!op.addBoolOption('\0', "disable-oom-functions",
"Disable functions that cause "
"artificial OOMs") ||

Просмотреть файл

@ -0,0 +1,27 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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/. */
/*
* Definitions for differential testing.
*/
#ifndef util_DifferentialTesting_h
#define util_DifferentialTesting_h
namespace js {
inline bool SupportDifferentialTesting() {
#ifdef DEBUG
extern bool gSupportDifferentialTesting;
return gSupportDifferentialTesting;
#else
return false;
#endif
}
} // namespace js
#endif /* util_DifferentialTesting_h */

Просмотреть файл

@ -38,6 +38,7 @@
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/Printf.h"
#include "js/Symbol.h"
#include "util/DifferentialTesting.h"
#include "util/Memory.h"
#include "util/StringBuffer.h"
#include "util/Text.h"
@ -2363,14 +2364,14 @@ static bool DecompileExpressionFromStack(JSContext* cx, int spindex,
*res = nullptr;
#ifdef JS_MORE_DETERMINISTIC
/*
* Give up if we need deterministic behavior for differential testing.
* IonMonkey doesn't use InterpreterFrames and this ensures we get the same
* error messages.
*/
return true;
#endif
if (js::SupportDifferentialTesting()) {
return true;
}
if (spindex == JSDVG_IGNORE_STACK) {
return true;
@ -2461,10 +2462,10 @@ static bool DecompileArgumentFromStack(JSContext* cx, int formalIndex,
*res = nullptr;
#ifdef JS_MORE_DETERMINISTIC
/* See note in DecompileExpressionFromStack. */
return true;
#endif
if (js::SupportDifferentialTesting()) {
return true;
}
/*
* Settle on the nearest script frame, which should be the builtin that

Просмотреть файл

@ -29,6 +29,7 @@
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/PropertySpec.h"
#include "js/Proxy.h"
#include "util/DifferentialTesting.h"
#include "util/Poison.h"
#include "vm/BytecodeUtil.h"
#include "vm/GlobalObject.h"
@ -375,14 +376,17 @@ static bool EnumerateProxyProperties(JSContext* cx, HandleObject pobj,
return true;
}
#ifdef JS_MORE_DETERMINISTIC
#ifdef DEBUG
struct SortComparatorIds {
JSContext* const cx;
SortComparatorIds(JSContext* cx) : cx(cx) {}
explicit SortComparatorIds(JSContext* cx) : cx(cx) {}
bool operator()(jsid aArg, jsid bArg, bool* lessOrEqualp) {
RootedId a(cx, aArg);
RootedId b(cx, bArg);
bool operator()(jsid a, jsid b, bool* lessOrEqualp) {
// Pick an arbitrary order on jsids that is as stable as possible
// across executions.
if (a == b) {
@ -390,8 +394,8 @@ struct SortComparatorIds {
return true;
}
size_t ta = JSID_BITS(a) & JSID_TYPE_MASK;
size_t tb = JSID_BITS(b) & JSID_TYPE_MASK;
size_t ta = JSID_BITS(a.get()) & JSID_TYPE_MASK;
size_t tb = JSID_BITS(b.get()) & JSID_TYPE_MASK;
if (ta != tb) {
*lessOrEqualp = (ta <= tb);
return true;
@ -444,7 +448,7 @@ struct SortComparatorIds {
}
};
#endif /* JS_MORE_DETERMINISTIC */
#endif /* DEBUG */
static bool Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags,
MutableHandleIdVector props) {
@ -514,37 +518,37 @@ static bool Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags,
}
} while (pobj != nullptr);
#ifdef JS_MORE_DETERMINISTIC
#ifdef DEBUG
if (js::SupportDifferentialTesting()) {
/*
* In some cases the enumeration order for an object depends on the
* execution mode (interpreter vs. JIT), especially for native objects
* with a class enumerate hook (where resolving a property changes the
* resulting enumeration order). These aren't really bugs, but the
* differences can change the generated output and confuse correctness
* fuzzers, so we sort the ids if such a fuzzer is running.
*
* We don't do this in the general case because (a) doing so is slow,
* and (b) it also breaks the web, which expects enumeration order to
* follow the order in which properties are added, in certain cases.
* Since ECMA does not specify an enumeration order for objects, both
* behaviors are technically correct to do.
*/
/*
* In some cases the enumeration order for an object depends on the
* execution mode (interpreter vs. JIT), especially for native objects
* with a class enumerate hook (where resolving a property changes the
* resulting enumeration order). These aren't really bugs, but the
* differences can change the generated output and confuse correctness
* fuzzers, so we sort the ids if such a fuzzer is running.
*
* We don't do this in the general case because (a) doing so is slow,
* and (b) it also breaks the web, which expects enumeration order to
* follow the order in which properties are added, in certain cases.
* Since ECMA does not specify an enumeration order for objects, both
* behaviors are technically correct to do.
*/
jsid* ids = props.begin();
size_t n = props.length();
jsid* ids = props.begin();
size_t n = props.length();
RootedIdVector tmp(cx);
if (!tmp.resize(n)) {
return false;
}
PodCopy(tmp.begin(), ids, n);
RootedIdVector tmp(cx);
if (!tmp.resize(n)) {
return false;
if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx))) {
return false;
}
}
PodCopy(tmp.begin(), ids, n);
if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx))) {
return false;
}
#endif /* JS_MORE_DETERMINISTIC */
#endif
return true;
}

Просмотреть файл

@ -47,6 +47,7 @@
#include "js/friend/StackLimits.h" // js::ReportOverRecursed
#include "js/Printf.h"
#include "util/DiagnosticAssertions.h"
#include "util/DifferentialTesting.h"
#include "util/DoubleToString.h"
#include "util/NativeStack.h"
#include "util/Windows.h"
@ -246,14 +247,14 @@ bool AutoResolving::alreadyStartedSlow() const {
* not occur, so GC must be avoided or suppressed.
*/
JS_FRIEND_API void js::ReportOutOfMemory(JSContext* cx) {
#ifdef JS_MORE_DETERMINISTIC
/*
* OOMs are non-deterministic, especially across different execution modes
* (e.g. interpreter vs JIT). In more-deterministic builds, print to stderr
* (e.g. interpreter vs JIT). When doing differential testing, print to stderr
* so that the fuzzers can detect this.
*/
fprintf(stderr, "ReportOutOfMemory called\n");
#endif
if (js::SupportDifferentialTesting()) {
fprintf(stderr, "ReportOutOfMemory called\n");
}
if (cx->isHelperThreadContext()) {
return cx->addPendingOutOfMemory();
@ -283,7 +284,6 @@ mozilla::GenericErrorResult<OOM> js::ReportOutOfMemoryResult(JSContext* cx) {
}
void js::ReportOverRecursed(JSContext* maybecx, unsigned errorNumber) {
#ifdef JS_MORE_DETERMINISTIC
/*
* We cannot make stack depth deterministic across different
* implementations (e.g. JIT vs. interpreter will differ in
@ -292,8 +292,10 @@ void js::ReportOverRecursed(JSContext* maybecx, unsigned errorNumber) {
* stack depth which is useful for external testing programs
* like fuzzers.
*/
fprintf(stderr, "ReportOverRecursed called\n");
#endif
if (js::SupportDifferentialTesting()) {
fprintf(stderr, "ReportOverRecursed called\n");
}
if (maybecx) {
if (!maybecx->isHelperThreadContext()) {
JS_ReportErrorNumberASCII(maybecx, GetErrorMessage, nullptr, errorNumber);

Просмотреть файл

@ -29,6 +29,7 @@
#include "js/PropertySpec.h"
#include "js/SavedFrameAPI.h"
#include "js/Vector.h"
#include "util/DifferentialTesting.h"
#include "util/StringBuffer.h"
#include "vm/GeckoProfiler.h"
#include "vm/JSScript.h"
@ -193,9 +194,9 @@ struct MOZ_STACK_CLASS SavedFrame::Lookup {
activation(activation) {
MOZ_ASSERT(source);
MOZ_ASSERT_IF(framePtr.isSome(), activation);
#ifdef JS_MORE_DETERMINISTIC
column = 0;
#endif
if (js::SupportDifferentialTesting()) {
column = 0;
}
}
explicit Lookup(SavedFrame& savedFrame)
@ -482,9 +483,9 @@ void SavedFrame::initLine(uint32_t line) {
}
void SavedFrame::initColumn(uint32_t column) {
#ifdef JS_MORE_DETERMINISTIC
column = 0;
#endif
if (js::SupportDifferentialTesting()) {
column = 0;
}
initReservedSlot(JSSLOT_COLUMN, PrivateUint32Value(column));
}

Просмотреть файл

@ -26,6 +26,7 @@
#include "js/Conversions.h"
#include "js/ScalarType.h" // js::Scalar::Type
#include "js/Value.h"
#include "util/DifferentialTesting.h"
#include "util/Memory.h"
#include "vm/BigIntType.h"
#include "vm/JSContext.h"
@ -719,14 +720,14 @@ class ElementSpecific {
static T doubleToNative(double d) {
if (TypeIsFloatingPoint<T>()) {
#ifdef JS_MORE_DETERMINISTIC
// The JS spec doesn't distinguish among different NaN values, and
// it deliberately doesn't specify the bit pattern written to a
// typed array when NaN is written into it. This bit-pattern
// inconsistency could confuse deterministic testing, so always
// canonicalize NaN values in more-deterministic builds.
d = JS::CanonicalizeNaN(d);
#endif
// inconsistency could confuse differential testing, so always
// canonicalize NaN values in differential testing.
if (js::SupportDifferentialTesting()) {
d = JS::CanonicalizeNaN(d);
}
return T(d);
}
if (MOZ_UNLIKELY(mozilla::IsNaN(d))) {

Просмотреть файл

@ -37,6 +37,7 @@
#include "js/ScalarType.h" // js::Scalar::Type
#include "js/UniquePtr.h"
#include "js/Wrapper.h"
#include "util/DifferentialTesting.h"
#include "util/Text.h"
#include "util/Windows.h"
#include "vm/ArrayBufferObject.h"
@ -973,10 +974,10 @@ bool TypedArrayObjectTemplate<NativeType>::convertValue(JSContext* cx,
return false;
}
#ifdef JS_MORE_DETERMINISTIC
// See the comment in ElementSpecific::doubleToNative.
d = JS::CanonicalizeNaN(d);
#endif
if (js::SupportDifferentialTesting()) {
// See the comment in ElementSpecific::doubleToNative.
d = JS::CanonicalizeNaN(d);
}
// Assign based on characteristics of the destination type
if constexpr (ArrayTypeIsFloatingPoint()) {

Просмотреть файл

@ -47,6 +47,7 @@
#include "js/SourceText.h"
#include "js/StableStringChars.h"
#include "js/Wrapper.h"
#include "util/DifferentialTesting.h"
#include "util/StringBuffer.h"
#include "util/Text.h"
#include "vm/ErrorReporting.h"
@ -6983,13 +6984,9 @@ static bool NoExceptionPending(JSContext* cx) {
static bool SuccessfulValidation(frontend::ParserBase& parser,
unsigned compilationTime) {
constexpr unsigned errNum =
#ifdef JS_MORE_DETERMINISTIC
JSMSG_USE_ASM_TYPE_OK_NO_TIME
#else
JSMSG_USE_ASM_TYPE_OK
#endif
;
unsigned errNum = js::SupportDifferentialTesting()
? JSMSG_USE_ASM_TYPE_OK_NO_TIME
: JSMSG_USE_ASM_TYPE_OK;
char timeChars[20];
SprintfLiteral(timeChars, "%u", compilationTime);