From b32fed790c7850e1fc751492b0e935524583b753 Mon Sep 17 00:00:00 2001 From: "arthur.iakab" Date: Fri, 30 Nov 2018 08:42:51 +0200 Subject: [PATCH] Backed out changeset 5e65de3569fe (bug 1506969) for causing spidermonkey bustages on testScriptSourceCompression.cpp CLOSED TREE --- js/src/jsapi-tests/moz.build | 1 - .../testScriptSourceCompression.cpp | 481 ------------------ js/src/vm/JSScript.cpp | 3 +- js/src/vm/JSScript.h | 6 - 4 files changed, 2 insertions(+), 489 deletions(-) delete mode 100644 js/src/jsapi-tests/testScriptSourceCompression.cpp diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index 2f20a61351e9..d30b65c3dfea 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -94,7 +94,6 @@ UNIFIED_SOURCES += [ 'testSavedStacks.cpp', 'testScriptInfo.cpp', 'testScriptObject.cpp', - 'testScriptSourceCompression.cpp', 'testSetProperty.cpp', 'testSetPropertyIgnoringNamedGetter.cpp', 'testSharedImmutableStringsCache.cpp', diff --git a/js/src/jsapi-tests/testScriptSourceCompression.cpp b/js/src/jsapi-tests/testScriptSourceCompression.cpp deleted file mode 100644 index e890ec8df6f9..000000000000 --- a/js/src/jsapi-tests/testScriptSourceCompression.cpp +++ /dev/null @@ -1,481 +0,0 @@ -/* -*- 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/ArrayUtils.h" // mozilla::ArrayLength -#include "mozilla/Assertions.h" // MOZ_RELEASE_ASSERT - -#include // std::all_of, std::copy_n, std::equal, std::move -#include // std::uninitialized_fill_n -#include // size_t -#include // uint32_t - -#include "jsapi.h" // JS_FlattenString, JS_GC, JS_Get{Latin1,TwoByte}FlatStringChars, JS_GetStringLength, JS_ValueToFunction - -#include "js/CompilationAndEvaluation.h" // JS::Evaluate -#include "js/CompileOptions.h" // JS::CompileOptions -#include "js/Conversions.h" // JS::ToString -#include "js/GCAPI.h" // JS_GC -#include "js/MemoryFunctions.h" // JS_malloc -#include "js/RootingAPI.h" // JS::MutableHandle, JS::Rooted -#include "js/SourceText.h" // JS::SourceOwnership, JS::SourceText -#include "js/UniquePtr.h" // js::UniquePtr -#include "js/Utility.h" // JS::FreePolicy -#include "js/Value.h" // JS::NullValue, JS::ObjectValue, JS::Value -#include "jsapi-tests/tests.h" -#include "vm/Compression.h" // js::Compressor::CHUNK_SIZE -#include "vm/JSFunction.h" // JSFunction::getOrCreateScript -#include "vm/JSScript.h" // JSScript, js::ScriptSource::MinimumCompressibleLength - -using mozilla::ArrayLength; - -struct JSContext; -class JSString; - -template -using Source = js::UniquePtr; - -constexpr size_t ChunkSize = js::Compressor::CHUNK_SIZE; -constexpr size_t MinimumCompressibleLength = js::ScriptSource::MinimumCompressibleLength; - -// Don't use ' ' to spread stuff across lines. -constexpr char FillerWhitespace = '\n'; - -template -static Source -MakeSourceAllWhitespace(JSContext* cx, size_t len) -{ - static_assert(ChunkSize % sizeof(CharT) == 0, - "chunk size presumed to be a multiple of char size"); - - Source source(reinterpret_cast(JS_malloc(cx, len * sizeof(CharT)))); - if (source) { - std::uninitialized_fill_n(source.get(), len, FillerWhitespace); - } - return source; -} - -static bool -Evaluate(JSContext* cx, const JS::CompileOptions& options, const char16_t* src, size_t srclen) -{ - JS::SourceText sourceText; - if (!sourceText.init(cx, src, srclen, JS::SourceOwnership::Borrowed)) { - return false; - } - - JS::Rooted dummy(cx); - return JS::Evaluate(cx, options, sourceText, &dummy); -} - -template -static JSFunction* -EvaluateChars(JSContext* cx, Source chars, size_t len, char functionName, const char* func) -{ - JS::CompileOptions options(cx); - options.setFileAndLine(func, 1); - - if (!Evaluate(cx, options, chars.get(), len)) { - return nullptr; - } - - JS::Rooted rval(cx); - const char16_t name[] = { char16_t(functionName) }; - JS::SourceText srcbuf; - if (!srcbuf.init(cx, name, ArrayLength(name), JS::SourceOwnership::Borrowed)) { - return nullptr; - } - if (!JS::Evaluate(cx, options, srcbuf, &rval)) { - return nullptr; - } - - MOZ_RELEASE_ASSERT(rval.isObject()); - return JS_ValueToFunction(cx, rval); -} - -static void -CompressSourceSync(JS::Handle fun, JSContext* cx) -{ - JS::Rooted script(cx, JSFunction::getOrCreateScript(cx, fun)); - MOZ_RELEASE_ASSERT(script); - MOZ_RELEASE_ASSERT(script->scriptSource()->hasSourceText()); - - // Hoodoo voodoo that presently compresses |fun|'s source. - // XXX Be a mensch: replace this with targeted actions to do this! - JS_GC(cx); - JS_GC(cx); - - MOZ_RELEASE_ASSERT(script->scriptSource()->hasCompressedSource()); -} - -static constexpr char FunctionStart[] = "function @() {"; -constexpr size_t FunctionStartLength = ArrayLength(FunctionStart) - 1; -constexpr size_t FunctionNameOffset = 9; - -static_assert(FunctionStart[FunctionNameOffset] == '@', - "offset must correctly point at the function name location"); - -static constexpr char FunctionEnd[] = "return 42; }"; -constexpr size_t FunctionEndLength = ArrayLength(FunctionEnd) - 1; - -template -static void -WriteFunctionOfSizeAtOffset(Source& source, size_t usableSourceLen, - char functionName, size_t functionLength, size_t offset) -{ - MOZ_RELEASE_ASSERT(functionLength >= MinimumCompressibleLength, - "function must be a certain size to be compressed"); - MOZ_RELEASE_ASSERT(offset <= usableSourceLen, - "offset must not exceed usable source"); - MOZ_RELEASE_ASSERT(functionLength <= usableSourceLen, - "function must fit in usable source"); - MOZ_RELEASE_ASSERT(offset <= usableSourceLen - functionLength, - "function must not extend past usable source"); - - // Fill in the function start. - std::copy_n(FunctionStart, FunctionStartLength, &source[offset]); - source[offset + FunctionNameOffset] = functionName; - - // Fill in the function end. - std::copy_n(FunctionEnd, FunctionEndLength, - &source[offset + functionLength - FunctionEndLength]); -} - -static JSString* -DecompressSource(JSContext* cx, JS::Handle fun) -{ - JS::Rooted fval(cx, JS::ObjectValue(*JS_GetFunctionObject(fun))); - return JS::ToString(cx, fval); -} - -static bool -IsExpectedFunctionString(JS::Handle str, char functionName, JSContext* cx) -{ - JSFlatString* fstr = JS_FlattenString(cx, str); - MOZ_RELEASE_ASSERT(fstr); - - size_t len = JS_GetStringLength(str); - if (len < FunctionStartLength || len < FunctionEndLength) { - return false; - } - - JS::AutoAssertNoGC nogc(cx); - - auto CheckContents = [functionName, len](const auto* chars) { - // Check the function in parts: - // - // * "function " - // * "A" - // * "() {" - // * "\n...\n" - // * "return 42; }" - return std::equal(chars, chars + FunctionNameOffset, FunctionStart) && - chars[FunctionNameOffset] == functionName && - std::equal(chars + FunctionNameOffset + 1, chars + FunctionStartLength, - FunctionStart + FunctionNameOffset + 1) && - std::all_of(chars + FunctionStartLength, chars + len - FunctionEndLength, - [](auto c) { return c == FillerWhitespace; }) && - std::equal(chars + len - FunctionEndLength, chars + len, - FunctionEnd); - }; - - bool hasExpectedContents; - if (JS_StringHasLatin1Chars(str)) { - const JS::Latin1Char* chars = JS_GetLatin1FlatStringChars(nogc, fstr); - hasExpectedContents = CheckContents(chars); - } else { - const char16_t* chars = JS_GetTwoByteFlatStringChars(nogc, fstr); - hasExpectedContents = CheckContents(chars); - } - - return hasExpectedContents; -} - -BEGIN_TEST(testScriptSourceCompression_inOneChunk) -{ - CHECK(run()); - return true; -} - -template -bool -run() -{ - constexpr size_t len = MinimumCompressibleLength + 55; - auto source = MakeSourceAllWhitespace(cx, len); - CHECK(source); - - // Write out a 'b' or 'c' function that is long enough to be compressed, - // that starts after source start and ends before source end. - constexpr char FunctionName = 'a' + sizeof(CharT); - WriteFunctionOfSizeAtOffset(source, len, FunctionName, MinimumCompressibleLength, - len - MinimumCompressibleLength); - - JS::Rooted fun(cx); - fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__); - CHECK(fun); - - CompressSourceSync(fun, cx); - - JS::Rooted str(cx, DecompressSource(cx, fun)); - CHECK(str); - CHECK(IsExpectedFunctionString(str, FunctionName, cx)); - - return true; -} -END_TEST(testScriptSourceCompression_inOneChunk) - -BEGIN_TEST(testScriptSourceCompression_endsAtBoundaryInOneChunk) -{ - CHECK(run()); - return true; -} - -template -bool -run() -{ - constexpr size_t len = ChunkSize / sizeof(CharT); - auto source = MakeSourceAllWhitespace(cx, len); - CHECK(source); - - // Write out a 'd' or 'e' function that is long enough to be compressed, - // that (for no particular reason) starts after source start and ends - // before usable source end. - constexpr char FunctionName = 'c' + sizeof(CharT); - WriteFunctionOfSizeAtOffset(source, len, FunctionName, MinimumCompressibleLength, - len - MinimumCompressibleLength); - - JS::Rooted fun(cx); - fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__); - CHECK(fun); - - CompressSourceSync(fun, cx); - - JS::Rooted str(cx, DecompressSource(cx, fun)); - CHECK(str); - CHECK(IsExpectedFunctionString(str, FunctionName, cx)); - - return true; -} -END_TEST(testScriptSourceCompression_endsAtBoundaryInOneChunk) - -BEGIN_TEST(testScriptSourceCompression_isExactChunk) -{ - CHECK(run()); - return true; -} - -template -bool -run() -{ - constexpr size_t len = ChunkSize / sizeof(CharT); - auto source = MakeSourceAllWhitespace(cx, len); - CHECK(source); - - // Write out a 'f' or 'g' function that occupies the entire source (and - // entire chunk, too). - constexpr char FunctionName = 'e' + sizeof(CharT); - WriteFunctionOfSizeAtOffset(source, len, FunctionName, len, 0); - - JS::Rooted fun(cx); - fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__); - CHECK(fun); - - CompressSourceSync(fun, cx); - - JS::Rooted str(cx, DecompressSource(cx, fun)); - CHECK(str); - CHECK(IsExpectedFunctionString(str, FunctionName, cx)); - - return true; -} -END_TEST(testScriptSourceCompression_isExactChunk) - -BEGIN_TEST(testScriptSourceCompression_crossesChunkBoundary) -{ - CHECK(run()); - return true; -} - -template -bool -run() -{ - constexpr size_t len = ChunkSize / sizeof(CharT) + 293; - auto source = MakeSourceAllWhitespace(cx, len); - CHECK(source); - - // This function crosses a chunk boundary but does not end at one. - constexpr size_t FunctionSize = 177 + ChunkSize / sizeof(CharT); - - // Write out a 'h' or 'i' function. - constexpr char FunctionName = 'g' + sizeof(CharT); - WriteFunctionOfSizeAtOffset(source, len, FunctionName, FunctionSize, 37); - - JS::Rooted fun(cx); - fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__); - CHECK(fun); - - CompressSourceSync(fun, cx); - - JS::Rooted str(cx, DecompressSource(cx, fun)); - CHECK(str); - CHECK(IsExpectedFunctionString(str, FunctionName, cx)); - - return true; -} -END_TEST(testScriptSourceCompression_crossesChunkBoundary) - -BEGIN_TEST(testScriptSourceCompression_crossesChunkBoundary_endsAtBoundary) -{ - CHECK(run()); - return true; -} - -template -bool -run() -{ - // Exactly two chunks. - constexpr size_t len = (2 * ChunkSize) / sizeof(CharT); - auto source = MakeSourceAllWhitespace(cx, len); - CHECK(source); - - // This function crosses a chunk boundary, and it ends exactly at the end - // of both the second chunk and the full source. - constexpr size_t FunctionSize = 1 + ChunkSize / sizeof(CharT); - - // Write out a 'j' or 'k' function. - constexpr char FunctionName = 'i' + sizeof(CharT); - WriteFunctionOfSizeAtOffset(source, len, FunctionName, - FunctionSize, len - FunctionSize); - - JS::Rooted fun(cx); - fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__); - CHECK(fun); - - CompressSourceSync(fun, cx); - - JS::Rooted str(cx, DecompressSource(cx, fun)); - CHECK(str); - CHECK(IsExpectedFunctionString(str, FunctionName, cx)); - - return true; -} -END_TEST(testScriptSourceCompression_crossesChunkBoundary_endsAtBoundary) - -BEGIN_TEST(testScriptSourceCompression_containsWholeChunk) -{ - CHECK(run()); - return true; -} - -template -bool -run() -{ - constexpr size_t len = (2 * ChunkSize) / sizeof(CharT) + 17; - auto source = MakeSourceAllWhitespace(cx, len); - CHECK(source); - - // This function crosses two chunk boundaries and begins/ends in the middle - // of chunk boundaries. - constexpr size_t FunctionSize = 2 + ChunkSize / sizeof(CharT); - - // Write out a 'l' or 'm' function. - constexpr char FunctionName = 'k' + sizeof(CharT); - WriteFunctionOfSizeAtOffset(source, len, FunctionName, - FunctionSize, ChunkSize / sizeof(CharT) - 1); - - JS::Rooted fun(cx); - fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__); - CHECK(fun); - - CompressSourceSync(fun, cx); - - JS::Rooted str(cx, DecompressSource(cx, fun)); - CHECK(str); - CHECK(IsExpectedFunctionString(str, FunctionName, cx)); - - return true; -} -END_TEST(testScriptSourceCompression_containsWholeChunk) - -BEGIN_TEST(testScriptSourceCompression_containsWholeChunk_endsAtBoundary) -{ - CHECK(run()); - return true; -} - -template -bool -run() -{ - // Exactly three chunks. - constexpr size_t len = (3 * ChunkSize) / sizeof(CharT); - auto source = MakeSourceAllWhitespace(cx, len); - CHECK(source); - - // This function crosses two chunk boundaries and ends at a chunk boundary. - constexpr size_t FunctionSize = 1 + (2 * ChunkSize) / sizeof(CharT); - - // Write out a 'n' or 'o' function. - constexpr char FunctionName = 'm' + sizeof(CharT); - WriteFunctionOfSizeAtOffset(source, len, FunctionName, - FunctionSize, ChunkSize / sizeof(CharT) - 1); - - JS::Rooted fun(cx); - fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__); - CHECK(fun); - - CompressSourceSync(fun, cx); - - JS::Rooted str(cx, DecompressSource(cx, fun)); - CHECK(str); - CHECK(IsExpectedFunctionString(str, FunctionName, cx)); - - return true; -} -END_TEST(testScriptSourceCompression_containsWholeChunk_endsAtBoundary) - -BEGIN_TEST(testScriptSourceCompression_spansMultipleMiddleChunks) -{ - CHECK(run()); - return true; -} - -template -bool -run() -{ - // Four chunks. - constexpr size_t len = (4 * ChunkSize) / sizeof(CharT); - auto source = MakeSourceAllWhitespace(cx, len); - CHECK(source); - - // This function spans the two middle chunks and further extends one - // character to each side. - constexpr size_t FunctionSize = 2 + (2 * ChunkSize) / sizeof(CharT); - - // Write out a 'p' or 'q' function. - constexpr char FunctionName = 'o' + sizeof(CharT); - WriteFunctionOfSizeAtOffset(source, len, FunctionName, - FunctionSize, ChunkSize / sizeof(CharT) - 1); - - JS::Rooted fun(cx); - fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__); - CHECK(fun); - - CompressSourceSync(fun, cx); - - JS::Rooted str(cx, DecompressSource(cx, fun)); - CHECK(str); - CHECK(IsExpectedFunctionString(str, FunctionName, cx)); - - return true; -} -END_TEST(testScriptSourceCompression_spansMultipleMiddleChunks) diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp index fcd200a643b0..86981a5ef72e 100644 --- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -1991,7 +1991,8 @@ ScriptSource::tryCompressOffThread(JSContext* cx) HelperThreadState().cpuCount > 1 && HelperThreadState().threadCount >= 2 && CanUseExtraThreads(); - if (length() < ScriptSource::MinimumCompressibleLength || !canCompressOffThread) { + const size_t TINY_SCRIPT = 256; + if (TINY_SCRIPT > length() || !canCompressOffThread) { return true; } diff --git a/js/src/vm/JSScript.h b/js/src/vm/JSScript.h index 98922b353f47..542eb9be7def 100644 --- a/js/src/vm/JSScript.h +++ b/js/src/vm/JSScript.h @@ -681,12 +681,6 @@ class ScriptSource const JS::ReadOnlyCompileOptions& options, const mozilla::Maybe& parameterListEnd = mozilla::Nothing()); - /** - * The minimum script length (in code units) necessary for a script to be - * eligible to be compressed. - */ - static constexpr size_t MinimumCompressibleLength = 256; - template MOZ_MUST_USE bool setSourceCopy(JSContext* cx, JS::SourceText& srcBuf);