Compare commits

...

7 Commits

Author SHA1 Message Date
Jarred Sumner
99ebc3aa5c Fix build 2026-02-11 20:25:29 -08:00
Jarred Sumner
a1cf1e8bbf Update create_hash_table 2026-02-11 19:33:09 -08:00
Jarred Sumner
469678a00f fix: update create_hash_table to always use WYHash
WebKit commit 14ba1421ca08 changed JSC to always use WYHash for string
hashing, but Bun's copy of create_hash_table still used SuperFastHash
for the non-Mac path. This caused hash table lookups to fail at runtime
because the generated .lut.h tables had SuperFastHash indices while the
runtime computed WYHash indices.

Also fix vendor/zlib/zutil.h TARGET_OS_MAC triggering Classic Mac OS
code path on modern macOS, which defined fdopen as NULL and conflicted
with the system stdio.h.
2026-02-11 19:23:21 -08:00
Sosuke Suzuki
06ba02451a bench: add microtask throughput benchmark
Tests queueMicrotask, Promise chains, Promise.all,
AsyncLocalStorage + queueMicrotask, and async/await chains.
2026-02-11 19:23:21 -08:00
Sosuke Suzuki
b96fe564a6 perf: remove performMicrotaskFunction from BunPerformMicrotaskJob call sites
Companion change to WebKit's maxMicrotaskArguments 5→4 reduction.

- Remove performMicrotaskFunction from all QueuedTask construction sites
  (functionQueueMicrotask, JSC__JSPromise__reject_, queueMicrotaskJob)
- Delete jsFunctionPerformMicrotask (now inlined in JSC's handler)
- Remove m_performMicrotaskFunction LazyProperty and accessor

sizeof(QueuedTask) drops from 56 to 48 bytes (-14%).
2026-02-11 19:23:21 -08:00
Jarred Sumner
a82efdfe20 Apply suggestion from @Jarred-Sumner 2026-02-11 19:23:20 -08:00
Jarred Sumner
4cf974f628 Upgrade WebKit to f03492d0636f
- Merge 404 upstream WebKit commits
- Fix JSBigInt::tryRightTrim removal (use tryCreateFrom with span)
- Fix CloneSerializer/CloneDeserializer inheritance (private -> public)
- Explicitly disable ENABLE_MEDIA_SOURCE, ENABLE_MEDIA_STREAM, ENABLE_WEB_RTC
  in build.ts and SetupWebKit.cmake for JSCOnly port
2026-02-11 19:23:20 -08:00
9 changed files with 176 additions and 210 deletions

View File

@@ -0,0 +1,108 @@
import { AsyncLocalStorage } from "node:async_hooks";
import { bench, group, run } from "../runner.mjs";
// Benchmark 1: queueMicrotask throughput
// Tests the BunPerformMicrotaskJob handler path directly.
// The optimization removes the JS trampoline and uses callMicrotask.
group("queueMicrotask throughput", () => {
bench("queueMicrotask 1k", () => {
return new Promise(resolve => {
let remaining = 1000;
const tick = () => {
if (--remaining === 0) resolve();
else queueMicrotask(tick);
};
queueMicrotask(tick);
});
});
bench("queueMicrotask 10k", () => {
return new Promise(resolve => {
let remaining = 10000;
const tick = () => {
if (--remaining === 0) resolve();
else queueMicrotask(tick);
};
queueMicrotask(tick);
});
});
});
// Benchmark 2: Promise.resolve chain
// Each .then() queues a microtask via the promise machinery.
// Benefits from smaller QueuedTask (better cache locality in the Deque).
group("Promise.resolve chain", () => {
bench("Promise chain 1k", () => {
let p = Promise.resolve();
for (let i = 0; i < 1000; i++) {
p = p.then(() => {});
}
return p;
});
bench("Promise chain 10k", () => {
let p = Promise.resolve();
for (let i = 0; i < 10000; i++) {
p = p.then(() => {});
}
return p;
});
});
// Benchmark 3: Promise.all (many simultaneous resolves)
// All promises resolve at once, flooding the microtask queue.
// Smaller QueuedTask = less memory, better cache utilization.
group("Promise.all simultaneous", () => {
bench("Promise.all 1k", () => {
const promises = [];
for (let i = 0; i < 1000; i++) {
promises.push(Promise.resolve(i));
}
return Promise.all(promises);
});
bench("Promise.all 10k", () => {
const promises = [];
for (let i = 0; i < 10000; i++) {
promises.push(Promise.resolve(i));
}
return Promise.all(promises);
});
});
// Benchmark 4: queueMicrotask with AsyncLocalStorage
// Tests the inlined async context save/restore path.
// Previously went through performMicrotaskFunction JS trampoline.
group("queueMicrotask + AsyncLocalStorage", () => {
const als = new AsyncLocalStorage();
bench("ALS.run + queueMicrotask 1k", () => {
return als.run({ id: 1 }, () => {
return new Promise(resolve => {
let remaining = 1000;
const tick = () => {
als.getStore(); // force context read
if (--remaining === 0) resolve();
else queueMicrotask(tick);
};
queueMicrotask(tick);
});
});
});
});
// Benchmark 5: async/await (each await queues microtasks)
group("async/await chain", () => {
async function asyncChain(n) {
let sum = 0;
for (let i = 0; i < n; i++) {
sum += await Promise.resolve(i);
}
return sum;
}
bench("async/await 1k", () => asyncChain(1000));
bench("async/await 10k", () => asyncChain(10000));
});
await run();

View File

@@ -546,6 +546,7 @@ set(BUN_OBJECT_LUT_SOURCES
${CWD}/src/bun.js/bindings/ProcessBindingHTTPParser.cpp
${CWD}/src/bun.js/modules/NodeModuleModule.cpp
${CODEGEN_PATH}/ZigGeneratedClasses.lut.txt
${CWD}/src/bun.js/bindings/webcore/JSEvent.cpp
)
set(BUN_OBJECT_LUT_OUTPUTS
@@ -560,6 +561,7 @@ set(BUN_OBJECT_LUT_OUTPUTS
${CODEGEN_PATH}/ProcessBindingHTTPParser.lut.h
${CODEGEN_PATH}/NodeModuleModule.lut.h
${CODEGEN_PATH}/ZigGeneratedClasses.lut.h
${CODEGEN_PATH}/JSEvent.lut.h
)
macro(WEBKIT_ADD_SOURCE_DEPENDENCIES _source _deps)
@@ -593,6 +595,7 @@ foreach(i RANGE 0 ${BUN_OBJECT_LUT_SOURCES_MAX_INDEX})
"Generating ${filename}.lut.h"
DEPENDS
${BUN_OBJECT_LUT_SOURCE}
${CWD}/src/codegen/create_hash_table
COMMAND
${BUN_EXECUTABLE}
${BUN_FLAGS}
@@ -602,6 +605,7 @@ foreach(i RANGE 0 ${BUN_OBJECT_LUT_SOURCES_MAX_INDEX})
${BUN_OBJECT_LUT_OUTPUT}
SOURCES
${BUN_OBJECT_LUT_SCRIPT}
${CWD}/src/codegen/create_hash_table
${BUN_OBJECT_LUT_SOURCE}
OUTPUTS
${BUN_OBJECT_LUT_OUTPUT}

View File

@@ -6,7 +6,7 @@ option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of down
option(WEBKIT_BUILD_TYPE "The build type for local WebKit (defaults to CMAKE_BUILD_TYPE)")
if(NOT WEBKIT_VERSION)
set(WEBKIT_VERSION 8af7958ff0e2a4787569edf64641a1ae7cfe074a)
set(WEBKIT_VERSION 2b0822aee577b4da18cd2b5b20c9f2b63614a6f3)
endif()
# Use preview build URL for Windows ARM64 until the fix is merged to main
@@ -95,6 +95,9 @@ if(WEBKIT_LOCAL)
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DENABLE_REMOTE_INSPECTOR=ON
-DENABLE_MEDIA_SOURCE=OFF
-DENABLE_MEDIA_STREAM=OFF
-DENABLE_WEB_RTC=OFF
)
if(WIN32)

View File

@@ -1061,9 +1061,7 @@ JSC_DEFINE_HOST_FUNCTION(functionQueueMicrotask,
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
JSC::JSValue asyncContext = globalObject->m_asyncContextData.get()->getInternalField(0);
auto function = globalObject->performMicrotaskFunction();
#if ASSERT_ENABLED
ASSERT_WITH_MESSAGE(function, "Invalid microtask function");
ASSERT_WITH_MESSAGE(!callback.isEmpty(), "Invalid microtask callback");
#endif
@@ -1071,10 +1069,8 @@ JSC_DEFINE_HOST_FUNCTION(functionQueueMicrotask,
asyncContext = JSC::jsUndefined();
}
// BunPerformMicrotaskJob accepts a variable number of arguments (up to: performMicrotask, job, asyncContext, arg0, arg1).
// The runtime inspects argumentCount to determine which arguments are present, so callers may pass only the subset they need.
// Here we pass: function, callback, asyncContext.
JSC::QueuedTask task { nullptr, JSC::InternalMicrotask::BunPerformMicrotaskJob, 0, globalObject, function, callback, asyncContext };
// BunPerformMicrotaskJob: callback, asyncContext
JSC::QueuedTask task { nullptr, JSC::InternalMicrotask::BunPerformMicrotaskJob, 0, globalObject, callback, asyncContext };
globalObject->vm().queueMicrotask(WTF::move(task));
return JSC::JSValue::encode(JSC::jsUndefined());
@@ -1554,63 +1550,6 @@ extern "C" napi_env ZigGlobalObject__makeNapiEnvForFFI(Zig::GlobalObject* global
return globalObject->makeNapiEnvForFFI();
}
JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotask, (JSGlobalObject * globalObject, CallFrame* callframe))
{
auto& vm = JSC::getVM(globalObject);
auto scope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
auto job = callframe->argument(0);
if (!job || job.isUndefinedOrNull()) [[unlikely]] {
return JSValue::encode(jsUndefined());
}
auto callData = JSC::getCallData(job);
MarkedArgumentBuffer arguments;
if (callData.type == CallData::Type::None) [[unlikely]] {
return JSValue::encode(jsUndefined());
}
JSValue result;
WTF::NakedPtr<JSC::Exception> exceptionPtr;
JSValue restoreAsyncContext = {};
InternalFieldTuple* asyncContextData = nullptr;
auto setAsyncContext = callframe->argument(1);
if (!setAsyncContext.isUndefined()) {
asyncContextData = globalObject->m_asyncContextData.get();
restoreAsyncContext = asyncContextData->getInternalField(0);
asyncContextData->putInternalField(vm, 0, setAsyncContext);
}
size_t argCount = callframe->argumentCount();
switch (argCount) {
case 3: {
arguments.append(callframe->uncheckedArgument(2));
break;
}
case 4: {
arguments.append(callframe->uncheckedArgument(2));
arguments.append(callframe->uncheckedArgument(3));
break;
}
default:
break;
}
JSC::profiledCall(globalObject, ProfilingReason::API, job, callData, jsUndefined(), arguments, exceptionPtr);
if (asyncContextData) {
asyncContextData->putInternalField(vm, 0, restoreAsyncContext);
}
if (auto* exception = exceptionPtr.get()) {
Bun__reportUnhandledError(globalObject, JSValue::encode(exception));
}
return JSValue::encode(jsUndefined());
}
JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotaskVariadic, (JSGlobalObject * globalObject, CallFrame* callframe))
{
auto& vm = JSC::getVM(globalObject);
@@ -1940,11 +1879,6 @@ void GlobalObject::finishCreation(VM& vm)
scope.assertNoExceptionExceptTermination();
init.set(subclassStructure);
});
m_performMicrotaskFunction.initLater(
[](const Initializer<JSFunction>& init) {
init.set(JSFunction::create(init.vm, init.owner, 4, "performMicrotask"_s, jsFunctionPerformMicrotask, ImplementationVisibility::Public));
});
m_performMicrotaskVariadicFunction.initLater(
[](const Initializer<JSFunction>& init) {
init.set(JSFunction::create(init.vm, init.owner, 4, "performMicrotaskVariadic"_s, jsFunctionPerformMicrotaskVariadic, ImplementationVisibility::Public));

View File

@@ -272,7 +272,6 @@ public:
JSC::JSObject* performanceObject() const { return m_performanceObject.getInitializedOnMainThread(this); }
JSC::JSFunction* performMicrotaskFunction() const { return m_performMicrotaskFunction.getInitializedOnMainThread(this); }
JSC::JSFunction* performMicrotaskVariadicFunction() const { return m_performMicrotaskVariadicFunction.getInitializedOnMainThread(this); }
JSC::Structure* utilInspectOptionsStructure() const { return m_utilInspectOptionsStructure.getInitializedOnMainThread(this); }
@@ -569,7 +568,6 @@ public:
V(private, LazyPropertyOfGlobalObject<Structure>, m_jsonlParseResultStructure) \
V(private, LazyPropertyOfGlobalObject<Structure>, m_pathParsedObjectStructure) \
V(private, LazyPropertyOfGlobalObject<Structure>, m_pendingVirtualModuleResultStructure) \
V(private, LazyPropertyOfGlobalObject<JSFunction>, m_performMicrotaskFunction) \
V(private, LazyPropertyOfGlobalObject<JSFunction>, m_nativeMicrotaskTrampoline) \
V(private, LazyPropertyOfGlobalObject<JSFunction>, m_performMicrotaskVariadicFunction) \
V(private, LazyPropertyOfGlobalObject<JSFunction>, m_utilInspectFunction) \

View File

@@ -3538,13 +3538,11 @@ void JSC__JSPromise__rejectOnNextTickWithHandled(JSC::JSPromise* promise, JSC::J
promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(flags | JSC::JSPromise::isFirstResolvingFunctionCalledFlag));
auto* globalObject = jsCast<Zig::GlobalObject*>(promise->globalObject());
auto microtaskFunction = globalObject->performMicrotaskFunction();
auto rejectPromiseFunction = globalObject->rejectPromiseFunction();
auto asyncContext = globalObject->m_asyncContextData.get()->getInternalField(0);
#if ASSERT_ENABLED
ASSERT_WITH_MESSAGE(microtaskFunction, "Invalid microtask function");
ASSERT_WITH_MESSAGE(rejectPromiseFunction, "Invalid microtask callback");
ASSERT_WITH_MESSAGE(!value.isEmpty(), "Invalid microtask value");
#endif
@@ -3557,7 +3555,8 @@ void JSC__JSPromise__rejectOnNextTickWithHandled(JSC::JSPromise* promise, JSC::J
value = jsUndefined();
}
JSC::QueuedTask task { nullptr, JSC::InternalMicrotask::BunPerformMicrotaskJob, 0, globalObject, microtaskFunction, rejectPromiseFunction, globalObject->m_asyncContextData.get()->getInternalField(0), promise, value };
// BunPerformMicrotaskJob: rejectPromiseFunction, asyncContext, promise, value
JSC::QueuedTask task { nullptr, JSC::InternalMicrotask::BunPerformMicrotaskJob, 0, globalObject, rejectPromiseFunction, globalObject->m_asyncContextData.get()->getInternalField(0), promise, value };
globalObject->vm().queueMicrotask(WTF::move(task));
RETURN_IF_EXCEPTION(scope, );
}
@@ -5438,9 +5437,7 @@ extern "C" void JSC__JSGlobalObject__queueMicrotaskJob(JSC::JSGlobalObject* arg0
if (microtaskArgs[3].isEmpty()) {
microtaskArgs[3] = jsUndefined();
}
JSC::JSFunction* microTaskFunction = globalObject->performMicrotaskFunction();
#if ASSERT_ENABLED
ASSERT_WITH_MESSAGE(microTaskFunction, "Invalid microtask function");
auto& vm = globalObject->vm();
if (microtaskArgs[0].isCell()) {
JSC::Integrity::auditCellFully(vm, microtaskArgs[0].asCell());
@@ -5460,7 +5457,8 @@ extern "C" void JSC__JSGlobalObject__queueMicrotaskJob(JSC::JSGlobalObject* arg0
#endif
JSC::QueuedTask task { nullptr, JSC::InternalMicrotask::BunPerformMicrotaskJob, 0, globalObject, microTaskFunction, WTF::move(microtaskArgs[0]), WTF::move(microtaskArgs[1]), WTF::move(microtaskArgs[2]), WTF::move(microtaskArgs[3]) };
// BunPerformMicrotaskJob: job, asyncContext, arg0, arg1
JSC::QueuedTask task { nullptr, JSC::InternalMicrotask::BunPerformMicrotaskJob, 0, globalObject, WTF::move(microtaskArgs[0]), WTF::move(microtaskArgs[1]), WTF::move(microtaskArgs[2]), WTF::move(microtaskArgs[3]) };
globalObject->vm().queueMicrotask(WTF::move(task));
}

View File

@@ -122,16 +122,18 @@ STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSEventPrototype, JSEventPrototype::Base);
using JSEventDOMConstructor = JSDOMConstructor<JSEvent>;
/* Hash table */
/* Source for JSEvent.lut.h
@begin JSEventTable
isTrusted jsEvent_isTrusted DontDelete|ReadOnly|CustomAccessor|DOMAttribute
@end
*/
static const struct CompactHashIndex JSEventTableIndex[2] = {
{ 0, -1 },
{ -1, -1 },
};
static const HashTableValue JSEventTableValues[] = {
{ "isTrusted"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsEvent_isTrusted, 0 } },
};
// The generated .lut.h defines JSEventTable with nullptr for classForThis,
// but DOMAttribute properties require it for type checking. Rename the
// generated table and redefine it with the correct classForThis.
#define JSEventTable JSEventTable_GENERATED
#include "JSEvent.lut.h"
#undef JSEventTable
static const HashTable JSEventTable = { 1, 1, true, JSEvent::info(), JSEventTableValues, JSEventTableIndex };
/* Hash table for constructor */

View File

@@ -832,7 +832,7 @@ template<> bool writeLittleEndian<uint8_t>(Vector<uint8_t>& buffer, const uint8_
return true;
}
class CloneSerializer : CloneBase {
class CloneSerializer : public CloneBase {
WTF_FORBID_HEAP_ALLOCATION;
public:
@@ -2831,7 +2831,7 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in)
return SerializationReturnCode::SuccessfullyCompleted;
}
class CloneDeserializer : CloneBase {
class CloneDeserializer : public CloneBase {
WTF_FORBID_HEAP_ALLOCATION;
public:
@@ -4688,36 +4688,28 @@ private:
return tryConvertToBigInt32(bigInt);
}
#endif
JSBigInt* bigInt = nullptr;
Vector<JSBigInt::Digit, 16> digits;
if constexpr (sizeof(JSBigInt::Digit) == sizeof(uint64_t)) {
bigInt = JSBigInt::tryCreateWithLength(m_lexicalGlobalObject->vm(), lengthInUint64);
if (!bigInt) {
fail();
return JSValue();
}
digits.reserveInitialCapacity(lengthInUint64);
for (uint32_t index = 0; index < lengthInUint64; ++index) {
uint64_t digit64 = 0;
if (!read(digit64))
return JSValue();
bigInt->setDigit(index, digit64);
digits.append(digit64);
}
} else {
ASSERT(sizeof(JSBigInt::Digit) == sizeof(uint32_t));
bigInt = JSBigInt::tryCreateWithLength(m_lexicalGlobalObject->vm(), lengthInUint64 * 2);
if (!bigInt) {
fail();
return JSValue();
}
digits.reserveInitialCapacity(lengthInUint64 * 2);
for (uint32_t index = 0; index < lengthInUint64; ++index) {
uint64_t digit64 = 0;
if (!read(digit64))
return JSValue();
bigInt->setDigit(index * 2, static_cast<uint32_t>(digit64));
bigInt->setDigit(index * 2 + 1, static_cast<uint32_t>(digit64 >> 32));
digits.append(static_cast<uint32_t>(digit64));
digits.append(static_cast<uint32_t>(digit64 >> 32));
}
}
bigInt->setSign(sign);
bigInt = bigInt->tryRightTrim(m_lexicalGlobalObject->vm());
auto* bigInt = JSBigInt::tryCreateFrom(nullptr, m_lexicalGlobalObject->vm(), sign, digits.span());
if (!bigInt) {
fail();
return JSValue();

View File

@@ -24,7 +24,7 @@
use strict;
use warnings;
use Math::BigInt;
use bigint;
use Getopt::Long qw(:config pass_through);
my $file = shift @ARGV or die("Must provide source file as final argument.");
@@ -42,17 +42,17 @@ my $hasSetter = "false";
my $includeBuiltin = 0;
my $inside = 0;
my $name;
my $pefectHashSize;
my $perfectHashSize;
my $compactSize;
my $compactHashSizeMask;
my $banner = 0;
my $mask64 = 2**64 - 1;
my $mask32 = 2**32 - 1;
sub calcPerfectHashSize($);
sub calcCompactHashSize($);
sub calcPerfectHashSize();
sub calcCompactHashSize();
sub output();
sub jsc_ucfirst($);
sub hashValue($$);
sub hashValue($);
while (<IN>) {
chomp;
@@ -140,24 +140,26 @@ sub jsc_ucfirst($)
sub ceilingToPowerOf2
{
my ($pefectHashSize) = @_;
my ($perfectHashSize) = @_;
my $powerOf2 = 1;
while ($pefectHashSize > $powerOf2) {
while ($perfectHashSize > $powerOf2) {
$powerOf2 <<= 1;
}
return $powerOf2;
}
sub calcPerfectHashSize($)
sub calcPerfectHashSize()
{
my ($isMac) = @_;
tableSizeLoop:
for ($pefectHashSize = ceilingToPowerOf2(scalar @keys); ; $pefectHashSize += $pefectHashSize) {
for ($perfectHashSize = ceilingToPowerOf2(scalar @keys); ; $perfectHashSize += $perfectHashSize) {
if ($perfectHashSize > 2**15) {
die "The hash size is far too big. This should not be reached.";
}
my @table = ();
foreach my $key (@keys) {
my $h = hashValue($key, $isMac) % $pefectHashSize;
my $h = hashValue($key) % $perfectHashSize;
next tableSizeLoop if $table[$h];
$table[$h] = 1;
}
@@ -165,14 +167,8 @@ tableSizeLoop:
}
}
sub leftShift($$) {
my ($value, $distance) = @_;
return (($value << $distance) & 0xFFFFFFFF);
}
sub calcCompactHashSize($)
sub calcCompactHashSize()
{
my ($isMac) = @_;
my $compactHashSize = ceilingToPowerOf2(2 * @keys);
$compactHashSizeMask = $compactHashSize - 1;
$compactSize = $compactHashSize;
@@ -181,8 +177,14 @@ sub calcCompactHashSize($)
my $i = 0;
foreach my $key (@keys) {
my $depth = 0;
my $h = hashValue($key, $isMac) % $compactHashSize;
my $h = hashValue($key) % $compactHashSize;
while (defined($table[$h])) {
if ($compactSize > 1000) {
die "The hash size is far too big. This should not be reached.";
}
if ($depth > 100) {
die "The depth is far too big. This should not be reached.";
}
if (defined($links[$h])) {
$h = $links[$h];
$depth++;
@@ -195,27 +197,10 @@ sub calcCompactHashSize($)
}
$table[$h] = $i;
$i++;
$maxdepth = $depth if ( $depth > $maxdepth);
$maxdepth = $depth if ($depth > $maxdepth);
}
}
sub avalancheBits($) {
my ($value) = @_;
$value &= $mask32;
# Force "avalanching" of lower 32 bits
$value ^= leftShift($value, 3);
$value += ($value >> 5);
$value = ($value & $mask32);
$value ^= (leftShift($value, 2) & $mask32);
$value += ($value >> 15);
$value = $value & $mask32;
$value ^= (leftShift($value, 10) & $mask32);
return $value;
}
sub maskTop8BitsAndAvoidZero($) {
my ($value) = @_;
@@ -233,43 +218,6 @@ sub maskTop8BitsAndAvoidZero($) {
return $value;
}
# Paul Hsieh's SuperFastHash
# http://www.azillionmonkeys.com/qed/hash.html
sub superFastHash {
my @chars = @_;
# This hash is designed to work on 16-bit chunks at a time. But since the normal case
# (above) is to hash UTF-16 characters, we just treat the 8-bit chars as if they
# were 16-bit chunks, which should give matching results
my $hash = 0x9e3779b9;
my $l = scalar @chars; #I wish this was in Ruby --- Maks
my $rem = $l & 1;
$l = $l >> 1;
my $s = 0;
# Main loop
for (; $l > 0; $l--) {
$hash += ord($chars[$s]);
my $tmp = leftShift(ord($chars[$s+1]), 11) ^ $hash;
$hash = (leftShift($hash, 16) & $mask32) ^ $tmp;
$s += 2;
$hash += $hash >> 11;
$hash &= $mask32;
}
# Handle end case
if ($rem != 0) {
$hash += ord($chars[$s]);
$hash ^= (leftShift($hash, 11) & $mask32);
$hash += $hash >> 17;
}
$hash = avalancheBits($hash);
return maskTop8BitsAndAvoidZero($hash);
}
sub uint64_add($$) {
my ($a, $b) = @_;
my $sum = $a + $b;
@@ -403,18 +351,10 @@ sub wyhash {
return maskTop8BitsAndAvoidZero($hash);
}
sub hashValue($$) {
my ($string, $isMac) = @_;
sub hashValue($) {
my ($string) = @_;
my @chars = split(/ */, $string);
my $charCount = scalar @chars;
if ($isMac) {
if ($charCount <= 48) {
return superFastHash(@chars);
}
return wyhash(@chars);
} else {
return superFastHash(@chars);
}
return wyhash(@chars);
}
sub output() {
@@ -436,16 +376,13 @@ sub output() {
print "\n";
local *generateHashTableHelper = sub {
my ($isMac, $setToOldValues) = @_;
my $oldCompactSize = $compactSize;
my $oldCompactHashSizeMask = $compactHashSizeMask;
calcPerfectHashSize($isMac);
calcCompactHashSize($isMac);
calcPerfectHashSize();
calcCompactHashSize();
my $hashTableString = "";
if ($compactSize != 0) {
$hashTableString .= "static const struct CompactHashIndex ${nameIndex}\[$compactSize\] = {\n";
$hashTableString .= "static constinit const struct CompactHashIndex ${nameIndex}\[$compactSize\] = {\n";
for (my $i = 0; $i < $compactSize; $i++) {
my $T = -1;
if (defined($table[$i])) { $T = $table[$i]; }
@@ -455,7 +392,7 @@ sub output() {
}
} else {
# MSVC dislikes empty arrays.
$hashTableString .= "static const struct CompactHashIndex ${nameIndex}\[1\] = {\n";
$hashTableString .= "static constinit const struct CompactHashIndex ${nameIndex}\[1\] = {\n";
$hashTableString .= " { 0, 0 }\n";
}
$hashTableString .= "};\n";
@@ -463,10 +400,10 @@ sub output() {
my $packedSize = scalar @keys;
if ($packedSize != 0) {
$hashTableString .= "static const struct HashTableValue ${nameEntries}\[$packedSize\] = {\n";
$hashTableString .= "static constinit const struct HashTableValue ${nameEntries}\[$packedSize\] = {\n";
} else {
# MSVC dislikes empty arrays.
$hashTableString .= "static const struct HashTableValue ${nameEntries}\[1\] = {\n";
$hashTableString .= "static constinit const struct HashTableValue ${nameEntries}\[1\] = {\n";
$hashTableString .= " { { }, 0, NoIntrinsic, { HashTableValue::End } }\n";
}
@@ -526,26 +463,16 @@ sub output() {
}
$hashTableString .= "};\n";
$hashTableString .= "\n";
$hashTableString .= "static const struct HashTable $name =\n";
$hashTableString .= "static constinit const struct HashTable $name =\n";
$hashTableString .= " \{ $packedSize, $compactHashSizeMask, $hasSetter, nullptr, $nameEntries, $nameIndex \};\n";
$hashTableString .= "\n";
@table = ();
@links = ();
if ($setToOldValues) {
$compactSize = $oldCompactSize;
$compactHashSizeMask = $oldCompactHashSizeMask;
}
return $hashTableString;
};
my $hashTableForMacOS = generateHashTableHelper(1, 1);
my $hashTableForIOS = generateHashTableHelper(0, 0);
my $hashTableToWrite = $hashTableForMacOS;
if ($hashTableForMacOS ne $hashTableForIOS) {
$hashTableToWrite = "#if PLATFORM(MAC)\n" . $hashTableForMacOS . "#else\n" . $hashTableForIOS . "#endif\n";
}
print $hashTableToWrite;
print generateHashTableHelper();
print "} // namespace JSC\n";
}