mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 16:25:38 +00:00
Backed out changeset 5e65de3569fe (bug 1506969) for causing spidermonkey bustages on testScriptSourceCompression.cpp CLOSED TREE
This commit is contained in:
parent
6e3babac6f
commit
b32fed790c
@ -94,7 +94,6 @@ UNIFIED_SOURCES += [
|
||||
'testSavedStacks.cpp',
|
||||
'testScriptInfo.cpp',
|
||||
'testScriptObject.cpp',
|
||||
'testScriptSourceCompression.cpp',
|
||||
'testSetProperty.cpp',
|
||||
'testSetPropertyIgnoringNamedGetter.cpp',
|
||||
'testSharedImmutableStringsCache.cpp',
|
||||
|
@ -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 <algorithm> // std::all_of, std::copy_n, std::equal, std::move
|
||||
#include <memory> // std::uninitialized_fill_n
|
||||
#include <stddef.h> // size_t
|
||||
#include <stdint.h> // 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<typename CharT>
|
||||
using Source = js::UniquePtr<CharT[], JS::FreePolicy>;
|
||||
|
||||
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<typename CharT>
|
||||
static Source<CharT>
|
||||
MakeSourceAllWhitespace(JSContext* cx, size_t len)
|
||||
{
|
||||
static_assert(ChunkSize % sizeof(CharT) == 0,
|
||||
"chunk size presumed to be a multiple of char size");
|
||||
|
||||
Source<CharT> source(reinterpret_cast<CharT*>(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<char16_t> sourceText;
|
||||
if (!sourceText.init(cx, src, srclen, JS::SourceOwnership::Borrowed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> dummy(cx);
|
||||
return JS::Evaluate(cx, options, sourceText, &dummy);
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
static JSFunction*
|
||||
EvaluateChars(JSContext* cx, Source<CharT> 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<JS::Value> rval(cx);
|
||||
const char16_t name[] = { char16_t(functionName) };
|
||||
JS::SourceText<char16_t> 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<JSFunction*> fun, JSContext* cx)
|
||||
{
|
||||
JS::Rooted<JSScript*> 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<typename CharT>
|
||||
static void
|
||||
WriteFunctionOfSizeAtOffset(Source<CharT>& 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<JSFunction*> fun)
|
||||
{
|
||||
JS::Rooted<JS::Value> fval(cx, JS::ObjectValue(*JS_GetFunctionObject(fun)));
|
||||
return JS::ToString(cx, fval);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsExpectedFunctionString(JS::Handle<JSString*> 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<char16_t>());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool
|
||||
run()
|
||||
{
|
||||
constexpr size_t len = MinimumCompressibleLength + 55;
|
||||
auto source = MakeSourceAllWhitespace<CharT>(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<JSFunction*> fun(cx);
|
||||
fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__);
|
||||
CHECK(fun);
|
||||
|
||||
CompressSourceSync(fun, cx);
|
||||
|
||||
JS::Rooted<JSString*> str(cx, DecompressSource(cx, fun));
|
||||
CHECK(str);
|
||||
CHECK(IsExpectedFunctionString(str, FunctionName, cx));
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testScriptSourceCompression_inOneChunk)
|
||||
|
||||
BEGIN_TEST(testScriptSourceCompression_endsAtBoundaryInOneChunk)
|
||||
{
|
||||
CHECK(run<char16_t>());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool
|
||||
run()
|
||||
{
|
||||
constexpr size_t len = ChunkSize / sizeof(CharT);
|
||||
auto source = MakeSourceAllWhitespace<CharT>(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<JSFunction*> fun(cx);
|
||||
fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__);
|
||||
CHECK(fun);
|
||||
|
||||
CompressSourceSync(fun, cx);
|
||||
|
||||
JS::Rooted<JSString*> str(cx, DecompressSource(cx, fun));
|
||||
CHECK(str);
|
||||
CHECK(IsExpectedFunctionString(str, FunctionName, cx));
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testScriptSourceCompression_endsAtBoundaryInOneChunk)
|
||||
|
||||
BEGIN_TEST(testScriptSourceCompression_isExactChunk)
|
||||
{
|
||||
CHECK(run<char16_t>());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool
|
||||
run()
|
||||
{
|
||||
constexpr size_t len = ChunkSize / sizeof(CharT);
|
||||
auto source = MakeSourceAllWhitespace<CharT>(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<JSFunction*> fun(cx);
|
||||
fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__);
|
||||
CHECK(fun);
|
||||
|
||||
CompressSourceSync(fun, cx);
|
||||
|
||||
JS::Rooted<JSString*> str(cx, DecompressSource(cx, fun));
|
||||
CHECK(str);
|
||||
CHECK(IsExpectedFunctionString(str, FunctionName, cx));
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testScriptSourceCompression_isExactChunk)
|
||||
|
||||
BEGIN_TEST(testScriptSourceCompression_crossesChunkBoundary)
|
||||
{
|
||||
CHECK(run<char16_t>());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool
|
||||
run()
|
||||
{
|
||||
constexpr size_t len = ChunkSize / sizeof(CharT) + 293;
|
||||
auto source = MakeSourceAllWhitespace<CharT>(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<JSFunction*> fun(cx);
|
||||
fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__);
|
||||
CHECK(fun);
|
||||
|
||||
CompressSourceSync(fun, cx);
|
||||
|
||||
JS::Rooted<JSString*> 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<char16_t>());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool
|
||||
run()
|
||||
{
|
||||
// Exactly two chunks.
|
||||
constexpr size_t len = (2 * ChunkSize) / sizeof(CharT);
|
||||
auto source = MakeSourceAllWhitespace<CharT>(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<JSFunction*> fun(cx);
|
||||
fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__);
|
||||
CHECK(fun);
|
||||
|
||||
CompressSourceSync(fun, cx);
|
||||
|
||||
JS::Rooted<JSString*> 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<char16_t>());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool
|
||||
run()
|
||||
{
|
||||
constexpr size_t len = (2 * ChunkSize) / sizeof(CharT) + 17;
|
||||
auto source = MakeSourceAllWhitespace<CharT>(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<JSFunction*> fun(cx);
|
||||
fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__);
|
||||
CHECK(fun);
|
||||
|
||||
CompressSourceSync(fun, cx);
|
||||
|
||||
JS::Rooted<JSString*> 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<char16_t>());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool
|
||||
run()
|
||||
{
|
||||
// Exactly three chunks.
|
||||
constexpr size_t len = (3 * ChunkSize) / sizeof(CharT);
|
||||
auto source = MakeSourceAllWhitespace<CharT>(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<JSFunction*> fun(cx);
|
||||
fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__);
|
||||
CHECK(fun);
|
||||
|
||||
CompressSourceSync(fun, cx);
|
||||
|
||||
JS::Rooted<JSString*> 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<char16_t>());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename CharT>
|
||||
bool
|
||||
run()
|
||||
{
|
||||
// Four chunks.
|
||||
constexpr size_t len = (4 * ChunkSize) / sizeof(CharT);
|
||||
auto source = MakeSourceAllWhitespace<CharT>(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<JSFunction*> fun(cx);
|
||||
fun = EvaluateChars(cx, std::move(source), len, FunctionName, __FUNCTION__);
|
||||
CHECK(fun);
|
||||
|
||||
CompressSourceSync(fun, cx);
|
||||
|
||||
JS::Rooted<JSString*> str(cx, DecompressSource(cx, fun));
|
||||
CHECK(str);
|
||||
CHECK(IsExpectedFunctionString(str, FunctionName, cx));
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testScriptSourceCompression_spansMultipleMiddleChunks)
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -681,12 +681,6 @@ class ScriptSource
|
||||
const JS::ReadOnlyCompileOptions& options,
|
||||
const mozilla::Maybe<uint32_t>& 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<typename Unit>
|
||||
MOZ_MUST_USE bool setSourceCopy(JSContext* cx, JS::SourceText<Unit>& srcBuf);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user