From 36866c4d7941d36656ab684e4dccecab2f06ebef Mon Sep 17 00:00:00 2001 From: dave caruso Date: Wed, 19 Jul 2023 19:28:53 -0700 Subject: [PATCH] Fix vite (#3694) * Fix ServerWebSocket.readyState * Add create_hash_table to our repo * Fix #3216 --- Makefile | 2 +- src/bun.js/javascript.zig | 3 +- src/bun.js/scripts/create_hash_table | 347 +++++++++++++++++++++++++++ src/js/builtins/codegen/index.ts | 7 +- src/js/out/modules/thirdparty/ws.js | 2 +- src/js/thirdparty/ws.js | 2 +- 6 files changed, 353 insertions(+), 10 deletions(-) create mode 100755 src/bun.js/scripts/create_hash_table diff --git a/Makefile b/Makefile index 8f883bc3f67267..931c2b8fd0db95 100644 --- a/Makefile +++ b/Makefile @@ -1489,7 +1489,7 @@ generate-classes: generate-sink: bun src/bun.js/scripts/generate-jssink.js $(CLANG_FORMAT) -i src/bun.js/bindings/JSSink.cpp src/bun.js/bindings/JSSink.h - $(WEBKIT_DIR)/Source/JavaScriptCore/create_hash_table src/bun.js/bindings/JSSink.cpp > src/bun.js/bindings/JSSinkLookupTable.h + ./src/bun.js/scripts/create_hash_table src/bun.js/bindings/JSSink.cpp > src/bun.js/bindings/JSSinkLookupTable.h $(SED) -i -e 's/#include "Lookup.h"//' src/bun.js/bindings/JSSinkLookupTable.h $(SED) -i -e 's/namespace JSC {//' src/bun.js/bindings/JSSinkLookupTable.h $(SED) -i -e 's/} \/\/ namespace JSC//' src/bun.js/bindings/JSSinkLookupTable.h diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index f331fbf9f0c1ec..a198259ed371dc 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -1239,8 +1239,8 @@ pub const VirtualMachine = struct { if (strings.hasPrefixComptime(specifier, "file://")) specifier = specifier["file://".len..]; if (strings.indexOfChar(specifier, '?')) |i| { - specifier = specifier[0..i]; query_string.* = specifier[i..]; + specifier = specifier[0..i]; } return specifier; @@ -1323,6 +1323,7 @@ pub const VirtualMachine = struct { var parts = [_]string{ source_to_use, normalized_specifier, + "../", }; break :name bun.path.joinAbsStringBuf( diff --git a/src/bun.js/scripts/create_hash_table b/src/bun.js/scripts/create_hash_table new file mode 100755 index 00000000000000..e2645b429d99d7 --- /dev/null +++ b/src/bun.js/scripts/create_hash_table @@ -0,0 +1,347 @@ +#! /usr/bin/env perl +# +# Static Hashtable Generator +# +# (c) 2000-2002 by Harri Porten and +# David Faure +# Modified (c) 2004 by Nikolas Zimmermann +# Copyright (C) 2007-2022 Apple Inc. All rights reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +use strict; +use warnings; +use Getopt::Long qw(:config pass_through); + +my $file = shift @ARGV or die("Must provide source file as final argument."); + +open(IN, $file) or die "No such file $file"; + +my @keys = (); +my @attrs = (); +my @values = (); +my @hashes = (); +my @table = (); +my @links = (); + +my $hasSetter = "false"; + +my $includeBuiltin = 0; +my $inside = 0; +my $name; +my $pefectHashSize; +my $compactSize; +my $compactHashSizeMask; +my $banner = 0; +sub calcPerfectHashSize(); +sub calcCompactHashSize(); +sub output(); +sub jsc_ucfirst($); +sub hashValue($); + +while () { + chomp; + s/^\s+//; + next if /^\#|^$/; # Comment or blank line. Do nothing. + if (/^\@begin/ && !$inside) { + if (/^\@begin\s*([:_\w]+)\s*\d*\s*$/) { + $inside = 1; + $name = $1; + } else { + print STDERR "WARNING: \@begin without table name, skipping $_\n"; + } + } elsif (/^\@end\s*$/ && $inside) { + calcPerfectHashSize(); + calcCompactHashSize(); + output(); + + @keys = (); + @attrs = (); + @values = (); + @hashes = (); + @table = (); + @links = (); + $includeBuiltin = 0; + + $inside = 0; + } elsif (/^(\S+)\s*(\S+)\s*([\w\|]*)\s*(\w*)\s*(\w*)\s*$/ && $inside) { + my $key = $1; + my $val = $2; + my $att = $3; + my $param = $4; + my $intrinsic = $5; + + push(@keys, $key); + push(@attrs, length($att) > 0 ? $att : "None"); + + if ($val eq "JSBuiltin") { + $includeBuiltin = 1; + } + + if ($att =~ m/Function/) { + push(@values, { "type" => "PropertyAttribute::Function", "function" => $val, "params" => (length($param) ? $param : ""), "intrinsic" => (length($intrinsic) ? $intrinsic : "NoIntrinsic") }); + #printf STDERR "WARNING: Number of arguments missing for $key/$val\n" if (length($param) == 0); + } elsif ($att =~ m/CellProperty/) { + my $property = $val; + push(@values, { "type" => "PropertyAttribute::CellProperty", "property" => $property }); + } elsif ($att =~ m/ClassStructure/) { + my $property = $val; + push(@values, { "type" => "PropertyAttribute::ClassStructure", "property" => $property }); + } elsif ($att =~ m/PropertyCallback/) { + my $cback = $val; + push(@values, { "type" => "PropertyAttribute::PropertyCallback", "cback" => $cback }); + } elsif (length($att)) { + my $get = $val; + my $put = "0"; + if (!($att =~ m/ReadOnly/)) { + $put = "set" . jsc_ucfirst($val); + } + $hasSetter = "true"; + push(@values, { "type" => "PropertyAttribute::Property", "get" => $get, "put" => $put }); + } else { + push(@values, { "type" => "Lexer", "value" => $val }); + } + push(@hashes, hashValue($key)); + } elsif ($inside) { + die "invalid data {" . $_ . "}"; + } +} + +die "missing closing \@end" if ($inside); + +sub jsc_ucfirst($) +{ + my ($value) = @_; + + if ($value =~ /js/) { + $value =~ s/js/JS/; + return $value; + } + + return ucfirst($value); +} + + +sub ceilingToPowerOf2 +{ + my ($pefectHashSize) = @_; + + my $powerOf2 = 1; + while ($pefectHashSize > $powerOf2) { + $powerOf2 <<= 1; + } + + return $powerOf2; +} + +sub calcPerfectHashSize() +{ +tableSizeLoop: + for ($pefectHashSize = ceilingToPowerOf2(scalar @keys); ; $pefectHashSize += $pefectHashSize) { + my @table = (); + foreach my $key (@keys) { + my $h = hashValue($key) % $pefectHashSize; + next tableSizeLoop if $table[$h]; + $table[$h] = 1; + } + last; + } +} + +sub leftShift($$) { + my ($value, $distance) = @_; + return (($value << $distance) & 0xFFFFFFFF); +} + +sub calcCompactHashSize() +{ + my $compactHashSize = ceilingToPowerOf2(2 * @keys); + $compactHashSizeMask = $compactHashSize - 1; + $compactSize = $compactHashSize; + my $collisions = 0; + my $maxdepth = 0; + my $i = 0; + foreach my $key (@keys) { + my $depth = 0; + my $h = hashValue($key) % $compactHashSize; + while (defined($table[$h])) { + if (defined($links[$h])) { + $h = $links[$h]; + $depth++; + } else { + $collisions++; + $links[$h] = $compactSize; + $h = $compactSize; + $compactSize++; + } + } + $table[$h] = $i; + $i++; + $maxdepth = $depth if ( $depth > $maxdepth); + } +} + +# Paul Hsieh's SuperFastHash +# http://www.azillionmonkeys.com/qed/hash.html +sub hashValue($) { + my @chars = split(/ */, $_[0]); + + # 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 $EXP2_32 = 4294967296; + + 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)% $EXP2_32) ^ $tmp; + $s += 2; + $hash += $hash >> 11; + $hash %= $EXP2_32; + } + + # Handle end case + if ($rem != 0) { + $hash += ord($chars[$s]); + $hash ^= (leftShift($hash, 11)% $EXP2_32); + $hash += $hash >> 17; + } + + # Force "avalanching" of final 127 bits + $hash ^= leftShift($hash, 3); + $hash += ($hash >> 5); + $hash = ($hash% $EXP2_32); + $hash ^= (leftShift($hash, 2)% $EXP2_32); + $hash += ($hash >> 15); + $hash = $hash% $EXP2_32; + $hash ^= (leftShift($hash, 10)% $EXP2_32); + + # Save 8 bits for StringImpl to use as flags. + $hash &= 0xffffff; + + # This avoids ever returning a hash code of 0, since that is used to + # signal "hash not computed yet". Setting the high bit maintains + # reasonable fidelity to a hash code of 0 because it is likely to yield + # exactly 0 when hash lookup masks out the high bits. + $hash = (0x80000000 >> 8) if ($hash == 0); + + return $hash; +} + +sub output() { + if (!$banner) { + $banner = 1; + print "// Automatically generated from $file using $0. DO NOT EDIT!\n"; + } + + my $nameEntries = "${name}Values"; + $nameEntries =~ s/:/_/g; + my $nameIndex = "${name}Index"; + $nameIndex =~ s/:/_/g; + + print "\n"; + print "#include \"JSCBuiltins.h\"\n" if ($includeBuiltin); + print "#include \"Lookup.h\"\n"; + print "\n"; + print "namespace JSC {\n"; + print "\n"; + if ($compactSize != 0) { + print "static const struct CompactHashIndex ${nameIndex}\[$compactSize\] = {\n"; + for (my $i = 0; $i < $compactSize; $i++) { + my $T = -1; + if (defined($table[$i])) { $T = $table[$i]; } + my $L = -1; + if (defined($links[$i])) { $L = $links[$i]; } + print " { $T, $L },\n"; + } + } else { + # MSVC dislikes empty arrays. + print "static const struct CompactHashIndex ${nameIndex}\[1\] = {\n"; + print " { 0, 0 }\n"; + } + print "};\n"; + print "\n"; + + my $packedSize = scalar @keys; + if ($packedSize != 0) { + print "static const struct HashTableValue ${nameEntries}\[$packedSize\] = {\n"; + } else { + # MSVC dislikes empty arrays. + print "static const struct HashTableValue ${nameEntries}\[1\] = {\n"; + print " { { }, 0, NoIntrinsic, { HashTableValue::End } }\n"; + } + my $i = 0; + foreach my $key (@keys) { + my $typeTag = ""; + my $firstValue = ""; + my $secondValue = ""; + my $hasSecondValue = 1; + my $intrinsic = "NoIntrinsic"; + + if ($values[$i]{"type"} eq "PropertyAttribute::Function") { + $typeTag = "NativeFunction"; + $firstValue = $values[$i]{"function"}; + $secondValue = $values[$i]{"params"}; + $intrinsic = $values[$i]{"intrinsic"}; + } elsif ($values[$i]{"type"} eq "PropertyAttribute::Property") { + $typeTag = "GetterSetter"; + $firstValue = $values[$i]{"get"}; + $secondValue = $values[$i]{"put"}; + } elsif ($values[$i]{"type"} eq "Lexer") { + $typeTag = "Lexer"; + $firstValue = $values[$i]{"value"}; + $hasSecondValue = 0; + } elsif ($values[$i]{"type"} eq "PropertyAttribute::CellProperty" || $values[$i]{"type"} eq "PropertyAttribute::ClassStructure") { + $typeTag = ($values[$i]{"type"} eq "PropertyAttribute::CellProperty") ? "LazyCellProperty" : "LazyClassStructure"; + $values[$i]{"property"} =~ /\A([a-zA-Z0-9_]+)::(.*)\Z/ or die; + $firstValue = "OBJECT_OFFSETOF($1, $2)"; + $hasSecondValue = 0; + } elsif ($values[$i]{"type"} eq "PropertyAttribute::PropertyCallback") { + $typeTag = "LazyProperty"; + $firstValue = $values[$i]{"cback"}; + $hasSecondValue = 0; + } + + my $attributes = "PropertyAttribute::" . $attrs[$i]; + $attributes =~ s/\|/\|PropertyAttribute::/g; + $attributes = "static_cast(" . $attributes . ")"; + if ($values[$i]{"type"} eq "PropertyAttribute::Function" && $firstValue eq "JSBuiltin") { + $typeTag = "BuiltinGenerator"; + my $tableHead = $name; + $tableHead =~ s/Table$//; + print " { \"$key\"_s, (($attributes) & ~PropertyAttribute::Function) | PropertyAttribute::Builtin, $intrinsic, { HashTableValue::" . $typeTag . "Type, " . $tableHead . ucfirst($key) . "CodeGenerator, $secondValue } },\n"; + } + else { + print " { \"$key\"_s, $attributes, $intrinsic, { HashTableValue::" . $typeTag . "Type, $firstValue" . ($hasSecondValue ? ", " . $secondValue : "") . " } },\n"; + } + $i++; + } + print "};\n"; + print "\n"; + print "static const struct HashTable $name =\n"; + print " \{ $packedSize, $compactHashSizeMask, $hasSetter, nullptr, $nameEntries, $nameIndex \};\n"; + print "\n"; + print "} // namespace JSC\n"; +} diff --git a/src/js/builtins/codegen/index.ts b/src/js/builtins/codegen/index.ts index 5fbc152798716b..4171533b2000cd 100644 --- a/src/js/builtins/codegen/index.ts +++ b/src/js/builtins/codegen/index.ts @@ -8,12 +8,7 @@ import { spawn } from "bun"; async function createStaticHashtables() { const STATIC_HASH_TABLES = ["src/bun.js/bindings/Process.cpp"]; console.time("Creating static hash tables..."); - const create_hash_table = [ - "src/bun.js/WebKit/Source/JavaScriptCore/create_hash_table", - "bun-webkit/Source/JavaScriptCore/create_hash_table", - ] - .map(x => path.join(import.meta.dir, "../../../../" + x)) - .find(x => existsSync(x)); + const create_hash_table = path.join(import.meta.dir, "../../../../src/bun.js/scripts/create_hash_table"); if (!create_hash_table) { console.warn( "Could not find create_hash_table executable. Run `bun i` or clone webkit to build static hash tables", diff --git a/src/js/out/modules/thirdparty/ws.js b/src/js/out/modules/thirdparty/ws.js index 175ab5fa1052d4..a30a4c688e7329 100644 --- a/src/js/out/modules/thirdparty/ws.js +++ b/src/js/out/modules/thirdparty/ws.js @@ -446,7 +446,7 @@ class BunWebSocketMocked extends EventEmitter { this.#binaryType = type; } get readyState() { - return readyStates[this.#state]; + return this.#state; } get url() { return this.#url; diff --git a/src/js/thirdparty/ws.js b/src/js/thirdparty/ws.js index 722d37347229f6..8fa5491e3994ca 100644 --- a/src/js/thirdparty/ws.js +++ b/src/js/thirdparty/ws.js @@ -631,7 +631,7 @@ class BunWebSocketMocked extends EventEmitter { } get readyState() { - return readyStates[this.#state]; + return this.#state; } get url() { return this.#url;