Skip to content

Instantly share code, notes, and snippets.

@Constellation
Created June 19, 2016 17:28
Show Gist options
  • Save Constellation/b07896538f12bd16d34fabbc8d760ad4 to your computer and use it in GitHub Desktop.
Save Constellation/b07896538f12bd16d34fabbc8d760ad4 to your computer and use it in GitHub Desktop.
ok
diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt
index 4d68a73..8499e8e 100644
--- a/Source/JavaScriptCore/CMakeLists.txt
+++ b/Source/JavaScriptCore/CMakeLists.txt
@@ -171,6 +171,7 @@ set(JavaScriptCore_SOURCES
bindings/ScriptObject.cpp
bindings/ScriptValue.cpp
+ builtins/StaticSymbols.cpp
builtins/BuiltinExecutables.cpp
builtins/BuiltinExecutableCreator.cpp
diff --git a/Source/JavaScriptCore/builtins/BuiltinNames.h b/Source/JavaScriptCore/builtins/BuiltinNames.h
index b6f4e38..f938ebb 100644
--- a/Source/JavaScriptCore/builtins/BuiltinNames.h
+++ b/Source/JavaScriptCore/builtins/BuiltinNames.h
@@ -29,6 +29,7 @@
#include "BuiltinUtils.h"
#include "CommonIdentifiers.h"
#include "JSCBuiltins.h"
+#include "StaticSymbols.h"
namespace JSC {
diff --git a/Source/JavaScriptCore/builtins/BuiltinUtils.h b/Source/JavaScriptCore/builtins/BuiltinUtils.h
index ceeac29..b5dda2a 100644
--- a/Source/JavaScriptCore/builtins/BuiltinUtils.h
+++ b/Source/JavaScriptCore/builtins/BuiltinUtils.h
@@ -31,13 +31,13 @@
namespace JSC {
-#define INITIALIZE_BUILTIN_NAMES(name) , m_##name(JSC::Identifier::fromString(vm, #name)), m_##name##PrivateName(JSC::Identifier::fromUid(JSC::PrivateName(JSC::PrivateName::Description, ASCIILiteral("PrivateSymbol." #name))))
+#define INITIALIZE_BUILTIN_NAMES(name) , m_##name(JSC::Identifier::fromString(vm, #name)), m_##name##PrivateName(JSC::Identifier::fromUid(JSC::PrivateName(StaticSymbols::name##PrivateName())))
#define DECLARE_BUILTIN_NAMES(name) const JSC::Identifier m_##name; const JSC::Identifier m_##name##PrivateName;
#define DECLARE_BUILTIN_IDENTIFIER_ACCESSOR(name) \
const JSC::Identifier& name##PublicName() const { return m_##name; } \
const JSC::Identifier& name##PrivateName() const { return m_##name##PrivateName; }
-#define INITIALIZE_BUILTIN_SYMBOLS(name) , m_##name##Symbol(JSC::Identifier::fromUid(JSC::PrivateName(JSC::PrivateName::Description, ASCIILiteral("Symbol." #name)))), m_##name##SymbolPrivateIdentifier(JSC::Identifier::fromString(vm, #name "Symbol"))
+#define INITIALIZE_BUILTIN_SYMBOLS(name) , m_##name##Symbol(JSC::Identifier::fromUid(JSC::PrivateName(StaticSymbols::name##Symbol()))), m_##name##SymbolPrivateIdentifier(JSC::Identifier::fromString(vm, #name "Symbol"))
#define DECLARE_BUILTIN_SYMBOLS(name) const JSC::Identifier m_##name##Symbol; const JSC::Identifier m_##name##SymbolPrivateIdentifier;
#define DECLARE_BUILTIN_SYMBOL_ACCESSOR(name) \
const JSC::Identifier& name##Symbol() const { return m_##name##Symbol; }
diff --git a/Source/JavaScriptCore/builtins/StaticSymbols.cpp b/Source/JavaScriptCore/builtins/StaticSymbols.cpp
new file mode 100644
index 0000000..c944c1d
--- /dev/null
+++ b/Source/JavaScriptCore/builtins/StaticSymbols.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 Yusuke Suzuki <[email protected]>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "StaticSymbols.h"
+
+#include <wtf/NeverDestroyed.h>
+#include <wtf/text/SymbolImpl.h>
+
+namespace JSC {
+
+#define DECLARE_STATIC_PRIVATE_NAMES(name)\
+static StringImpl& name##PrivateNameDescription()\
+{\
+ static NeverDestroyed<StringImpl> description(StringImpl::ConstructStaticString, "PrivateSymbol." #name);\
+ return description.get();\
+}\
+\
+SymbolImpl& StaticSymbols::name##PrivateName()\
+{\
+ static NeverDestroyed<SymbolImpl> symbol(SymbolImpl::ConstructStaticString, name##PrivateNameDescription());\
+ return symbol.get();\
+}
+
+#define DECLARE_STATIC_SYMBOLS(name)\
+static StringImpl& name##SymbolDescription()\
+{\
+ static NeverDestroyed<StringImpl> description(StringImpl::ConstructStaticString, "Symbol." #name);\
+ return description.get();\
+}\
+\
+SymbolImpl& StaticSymbols::name##Symbol()\
+{\
+ static NeverDestroyed<SymbolImpl> symbol(SymbolImpl::ConstructStaticString, name##SymbolDescription());\
+ return symbol.get();\
+}
+
+JSC_FOREACH_BUILTIN_FUNCTION_NAME(DECLARE_STATIC_PRIVATE_NAMES)
+JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(DECLARE_STATIC_PRIVATE_NAMES)
+JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(DECLARE_STATIC_SYMBOLS)
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/builtins/StaticSymbols.h b/Source/JavaScriptCore/builtins/StaticSymbols.h
new file mode 100644
index 0000000..4fd1448
--- /dev/null
+++ b/Source/JavaScriptCore/builtins/StaticSymbols.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 Yusuke Suzuki <[email protected]>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "CommonIdentifiers.h"
+#include "JSCBuiltins.h"
+
+namespace JSC {
+
+// Symbols are not registered in the atomic string table. It allows Symbols to be allocated
+// statically! StaticSymbols allocates and offers static StringImpls. The ref counts of
+// these static SymbolImpl and static description StringImpls are marked as static as
+// the same to empty StringImpl. Since this ensures that the ref count becomes meaningless,
+// SymbolImpls offered by this registry can be shared across the threads.
+// Furthermore, these SymbolImpls can be used from the other static variables, for example,
+// static hash tables for JS object properties.
+class StaticSymbols {
+public:
+#define DECLARE_STATIC_PRIVATE_NAMES(name) static SymbolImpl& name##PrivateName();
+#define DECLARE_STATIC_SYMBOLS(name) static SymbolImpl& name##Symbol();
+ JSC_FOREACH_BUILTIN_FUNCTION_NAME(DECLARE_STATIC_PRIVATE_NAMES)
+ JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(DECLARE_STATIC_PRIVATE_NAMES)
+ JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(DECLARE_STATIC_SYMBOLS)
+#undef DECLARE_STATIC_PRIVATE_NAMES
+#undef DECLARE_STATIC_SYMBOLS
+};
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/create_hash_table b/Source/JavaScriptCore/create_hash_table
old mode 100755
new mode 100644
index 1cb2e49..1ba7a24
--- a/Source/JavaScriptCore/create_hash_table
+++ b/Source/JavaScriptCore/create_hash_table
@@ -1,11 +1,11 @@
-#! /usr/bin/perl -w
-#
+#!/usr/bin/ruby
# Static Hashtable Generator
#
# (c) 2000-2002 by Harri Porten <[email protected]> and
# David Faure <[email protected]>
# Modified (c) 2004 by Nikolas Zimmermann <[email protected]>
# Copyright (C) 2007, 2008, 2009, 2015-2016 Apple Inc. All rights reserved.
+# Copyright (C) 2016 Yusuke Suzuki <[email protected]>.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -20,324 +20,439 @@
# 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 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 (<IN>) {
- 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*$/ && $inside) {
- my $key = $1;
- my $val = $2;
- my $att = $3;
- my $param = $4;
-
- push(@keys, $key);
- push(@attrs, length($att) > 0 ? $att : "0");
-
- if ($val eq "JSBuiltin") {
- $includeBuiltin = 1;
- }
-
- if ($att =~ m/Function/) {
- push(@values, { "type" => "Function", "function" => $val, "params" => (length($param) ? $param : "") });
- #printf STDERR "WARNING: Number of arguments missing for $key/$val\n" if (length($param) == 0);
- } elsif ($att =~ m/Accessor/) {
- my $get = $val;
- my $put = "nullptr";
- $hasSetter = "true";
- push(@values, { "type" => "Accessor", "get" => $get, "put" => $put });
- } elsif ($att =~ m/CellProperty/) {
- my $property = $val;
- push(@values, { "type" => "CellProperty", "property" => $property });
- } elsif ($att =~ m/ClassStructure/) {
- my $property = $val;
- push(@values, { "type" => "ClassStructure", "property" => $property });
- } elsif ($att =~ m/PropertyCallback/) {
- my $cback = $val;
- push(@values, { "type" => "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" => "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);
- }
-}
+
+def upcaseFirst value
+ characters = value.split ""
+ unless characters.empty?
+ characters[0].upcase!
+ end
+ characters.join ""
+end
+
+def jscUpcaseFirst value
+ if value =~ /js/
+ value.sub /js/, "JS"
+ else
+ upcaseFirst value
+ end
+end
+
+def leftShift value, distance
+ return ((value << distance) & 0xFFFFFFFF);
+end
# 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";
- 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";
- }
- print "};\n";
- print "\n";
-
- my $packedSize = scalar @keys;
- print "static const struct HashTableValue ${nameEntries}\[$packedSize\] = {\n";
- my $i = 0;
- foreach my $key (@keys) {
- my $firstValue = "";
- my $secondValue = "";
- my $firstCastStr = "";
- my $secondCastStr = "";
-
- if ($values[$i]{"type"} eq "Function") {
- $firstCastStr = "static_cast<NativeFunction>";
- $firstValue = $values[$i]{"function"};
- $secondValue = $values[$i]{"params"};
- } elsif ($values[$i]{"type"} eq "Accessor") {
- $firstCastStr = "static_cast<NativeFunction>";
- $secondCastStr = "static_cast<NativeFunction>";
- $firstValue = $values[$i]{"get"};
- $secondValue = $values[$i]{"put"};
- } elsif ($values[$i]{"type"} eq "Property") {
- $firstCastStr = "static_cast<PropertySlot::GetValueFunc>";
- $secondCastStr = "static_cast<PutPropertySlot::PutValueFunc>";
- $firstValue = $values[$i]{"get"};
- $secondValue = $values[$i]{"put"};
- } elsif ($values[$i]{"type"} eq "Lexer") {
- $firstValue = $values[$i]{"value"};
- $secondValue = "0";
- } elsif ($values[$i]{"type"} eq "CellProperty" || $values[$i]{"type"} eq "ClassStructure") {
- $values[$i]{"property"} =~ /\A([a-zA-Z0-9_]+)::(.*)\Z/ or die;
- $firstValue = "OBJECT_OFFSETOF($1, $2)";
- $secondValue = "0";
- } elsif ($values[$i]{"type"} eq "PropertyCallback") {
- $firstCastStr = "static_cast<LazyPropertyCallback>";
- $firstValue = $values[$i]{"cback"};
- $secondValue = "0";
- }
-
- my $intrinsic = "NoIntrinsic";
- $intrinsic = "FromCharCodeIntrinsic" if ($key eq "fromCharCode");
- if ($name eq "arrayPrototypeTable") {
- $intrinsic = "ArrayPushIntrinsic" if ($key eq "push");
- $intrinsic = "ArrayPopIntrinsic" if ($key eq "pop");
- }
-
- if ($values[$i]{"type"} eq "Function" && $firstValue eq "JSBuiltin") {
- my $tableHead = $name;
- $tableHead =~ s/Table$//;
- print " { \"$key\", (($attrs[$i]) & ~Function) | Builtin, $intrinsic, { (intptr_t)static_cast<BuiltinGenerator>(" . $tableHead . ucfirst($key) . "CodeGenerator), (intptr_t)$secondValue } },\n";
- }
- else {
- print " { \"$key\", $attrs[$i], $intrinsic, { (intptr_t)" . $firstCastStr . "($firstValue), (intptr_t)" . $secondCastStr . "($secondValue) } },\n";
- }
- $i++;
- }
- print "};\n";
- print "\n";
- print "static const struct HashTable $name =\n";
- print " \{ $packedSize, $compactHashSizeMask, $hasSetter, $nameEntries, $nameIndex \};\n";
- print "\n";
- print "} // namespace JSC\n";
-}
+EXP2_32 = 4294967296;
+def calcHashValue string
+ characters = string.split ''
+
+ # 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
+
+ hash = 0x9e3779b9;
+ length = characters.size
+ rem = length & 1
+ length = length >> 1
+
+ s = 0
+ length.times do
+ hash += characters[s].ord
+ temp = leftShift(characters[s + 1].ord, 11) ^ hash
+ hash = (leftShift(hash, 16) % EXP2_32) ^ temp
+ s += 2
+ hash += hash >> 11
+ hash %= EXP2_32
+ end
+
+ if rem != 0
+ hash += characters[s].ord
+ hash ^= (leftShift(hash, 11) % EXP2_32)
+ hash += hash >> 17
+ end
+
+ # 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
+end
+
+def ceilingToPowerOf2 perfectHashSize
+ powerOf2 = 1;
+ while perfectHashSize > powerOf2
+ powerOf2 <<= 1;
+ end
+ powerOf2;
+end
+
+def calcPerfectHashSize properties
+ perfectHashSize = ceilingToPowerOf2(properties.size)
+ begin
+ table = {}
+ properties.each do |property|
+ hashValue = property.hashValue % perfectHashSize
+ throw "Incorrect" unless table[hashValue].nil?
+ table[hashValue] = true
+ end
+ rescue
+ perfectHashSize += perfectHashSize
+ retry
+ end
+ perfectHashSize
+end
+
+class CompactTable
+ attr_reader :size, :mask
+ def initialize table, links, size, mask
+ @table = table
+ @links = links
+ @size = size
+ @mask = mask
+ end
+
+ def linkIndex index
+ if @links[index].nil?
+ -1
+ else
+ @links[index]
+ end
+ end
+
+ def tableIndex index
+ if @table[index].nil?
+ -1
+ else
+ @table[index]
+ end
+ end
+end
+
+def calcCompactHashSize properties
+ compactHashSize = ceilingToPowerOf2(2 * properties.size);
+ compactHashSizeMask = compactHashSize - 1;
+ compactSize = compactHashSize;
+ table = {}
+ links = {}
+
+ collisions = 0;
+ maxDepth = 0;
+
+ properties.each.with_index do |property, index|
+ depth = 0
+ hashValue = property.hashValue % compactHashSize
+ while !table[hashValue].nil?
+ if !links[hashValue].nil?
+ hashValue = links[hashValue]
+ depth += 1
+ else
+ collisions += 1
+ links[hashValue] = compactSize
+ hashValue = compactSize
+ compactSize += 1
+ end
+ end
+ table[hashValue] = index
+ maxDepth = [depth, maxDepth].max
+ end
+
+ CompactTable.new(table, links, compactSize, compactHashSizeMask)
+end
+
+class Property
+ attr_reader :hashValue
+ def initialize key, value, attributes, param, intrinsic
+ @key = key
+ @propertyName = @key
+ @attributes = attributes
+ @param = param
+ @intrinsic = intrinsic.empty? ? "NoIntrinsic" : intrinsic
+ @type = nil
+ @hasSetter = false
+ @builtin = value == "JSBuiltin"
+ @symbolKeyed = false
+ @privateSymbolKeyed = false
+
+ if @key =~ /^@@(.+)/
+ @symbolKeyed = true
+ @key = "Symbol.#{$1}"
+ @propertyName = $1
+ elsif @key =~ /^@(.+)/
+ @symbolKeyed = true
+ @privateSymbolKeyed = true
+ @key = "PrivateSymbol.#{$1}"
+ @propertyName = $1
+ end
+
+ case @attributes
+ when /Function/m
+ @type = :Function
+ @value = { "function" => value, "params" => (param.empty? ? "" : param) }
+ when /Accessor/m
+ @hasSetter = true
+ @type = :Accessor
+ @value = { "get" => value, "put" => "nullptr" }
+ when /CellProperty/m
+ @type = :CellProperty
+ @value = { "property" => value }
+ when /ClassStructure/m
+ @type = :ClassStructure
+ @value = { "property" => value }
+ when /PropertyCallback/m
+ @type = :PropertyCallback
+ @value = { "cback" => value }
+ else
+ if @attributes.empty?
+ @type = :Lexer
+ @attributes = "0"
+ @value = { "value" => value }
+ else
+ @type = :Property
+ get = value
+ put = "nullptr"
+ unless @attributes =~ /ReadOnly/m
+ put = "set" + jscUpcaseFirst(value)
+ end
+ @hasSetter = true
+ @value = { "get" => get, "put" => put }
+ end
+ end
+
+ @hashValue = calcHashValue(@key)
+ end
+
+ def builtin?
+ @builtin
+ end
+
+ def hasSetter?
+ @hasSetter
+ end
+
+ def symbolKeyed?
+ @symbolKeyed
+ end
+
+ def privateSymbolKeyed?
+ @privateSymbolKeyed
+ end
+
+ def stringKeyed?
+ !symbolKeyed?
+ end
+
+ def output tableName
+ firstValue = "";
+ secondValue = "";
+ firstCastStr = "";
+ secondCastStr = "";
+
+ case @type
+ when :Function
+ firstCastStr = "static_cast<NativeFunction>"
+ firstValue = @value["function"]
+ secondValue = @value["params"]
+ when :Accessor
+ firstCastStr = "static_cast<NativeFunction>"
+ secondCastStr = "static_cast<NativeFunction>"
+ firstValue = @value["get"]
+ secondValue = @value["put"]
+ when :Property
+ firstCastStr = "static_cast<PropertySlot::GetValueFunc>"
+ secondCastStr = "static_cast<PutPropertySlot::PutValueFunc>"
+ firstValue = @value["get"]
+ secondValue = @value["put"]
+ when :Lexer
+ firstValue = @value["value"]
+ secondValue = "0"
+ when :CellProperty, :ClassStructure
+ abort unless @value["property"] =~ /\A([a-zA-Z0-9_]+)::(.*)\Z/
+ firstValue = "OBJECT_OFFSETOF(#{$1}, #{$2})"
+ secondValue = "0"
+ when :PropertyCallback
+ firstCastStr = "static_cast<LazyPropertyCallback>"
+ firstValue = @value["cback"]
+ secondValue = "0"
+ end
+
+ propertyNameInSource = "\"#{@key}\""
+ if symbolKeyed?
+ if privateSymbolKeyed?
+ propertyNameInSource = "reinterpret_cast<const char*>(&StaticSymbols::#{@propertyName}PrivateName())"
+ else
+ propertyNameInSource = "reinterpret_cast<const char*>(&StaticSymbols::#{@propertyName}Symbol())"
+ end
+ @attributes += '|HoldingSymbolKey'
+ end
+
+ if @type == :Function && firstValue == "JSBuiltin"
+ tableHead = tableName.sub(/Table$/, "")
+ "{ #{propertyNameInSource}, ((#{@attributes}) & ~Function) | Builtin, #{@intrinsic}, { (intptr_t)static_cast<BuiltinGenerator>(" + tableHead + upcaseFirst(@propertyName) + "CodeGenerator), (intptr_t)#{secondValue} } },"
+
+ else
+ "{ #{propertyNameInSource}, #{@attributes}, #{@intrinsic}, { (intptr_t)" + firstCastStr + "(#{firstValue}), (intptr_t)" + secondCastStr + "(#{secondValue}) } },"
+ end
+ end
+
+ def inspect
+ "#{@key} = #{@value},hash:(#{@hashValue})"
+ end
+end
+
+class PropertyTable
+ attr_reader :name
+
+ def initialize name
+ @name = name
+ @includeBuiltin = false
+ @includeSymbolKeyed = false
+ @hasSetter = false
+ @properties = []
+ end
+
+ def push property
+ @properties.push(property)
+ @includeBuiltin = true if property.builtin?
+ @includeSymbolKeyed = true if property.symbolKeyed?
+ @hasSetter = true if property.hasSetter?
+ end
+
+ def includeBuiltin?
+ @includeBuiltin
+ end
+
+ def includeSymbolKeyed?
+ @includeSymbolKeyed
+ end
+
+ def hasSetter?
+ @hasSetter
+ end
+
+ def inspect
+ <<-EOS
+#{@name} includeBuiltin:(#{@includeBuiltin})
+#{allProperties.map {|property| " " + property.inspect }.join("\n")}
+ EOS
+ end
+
+ def stringProperties
+ @properties.select {|property| property.stringKeyed? }
+ end
+
+ def symbolProperties
+ @properties.select {|property| property.symbolKeyed? }
+ end
+
+ def allProperties
+ stringProperties + symbolProperties
+ end
+end
+
+def outputCompactTable compactTable, nameIndex
+ puts "static const struct CompactHashIndex #{nameIndex}\[#{compactTable.size}\] = {"
+ compactTable.size.times do |index|
+ puts " { #{compactTable.tableIndex(index)}, #{compactTable.linkIndex(index)} },"
+ end
+ puts "};"
+ puts ""
+end
+
+def output tables, filename
+ puts "// Automatically generated from #{filename} using #{__FILE__}. DO NOT EDIT!"
+ tables.each do |table|
+ nameEntries = "#{table.name}Values";
+ nameEntries.gsub!(/:/, "_")
+
+ stringNameIndex = "#{table.name}Index";
+ stringNameIndex.gsub!(/:/, "_")
+
+ puts ""
+ puts "#include \"JSCBuiltins.h\"" if table.includeBuiltin?
+ puts "#include \"Lookup.h\""
+ puts "#include \"StaticSymbols.h\"" if table.includeSymbolKeyed?
+ puts ""
+ puts "namespace JSC {"
+ puts ""
+
+ stringCompactTable = calcCompactHashSize(table.stringProperties)
+ outputCompactTable(stringCompactTable, stringNameIndex)
+
+ symbolNameIndex = "nullptr"
+ symbolCompactTableMask = "0"
+ if table.includeSymbolKeyed?
+ symbolNameIndex = "#{table.name}SymbolIndex";
+ symbolNameIndex.gsub!(/:/, "_")
+ symbolCompactTable = calcCompactHashSize(table.symbolProperties)
+ outputCompactTable(symbolCompactTable, symbolNameIndex)
+ symbolCompactTableMask = symbolCompactTable.mask
+ end
+
+ allProperties = table.allProperties
+ puts "static const struct HashTableValue #{nameEntries}\[#{allProperties.size}\] = {"
+ allProperties.each do |property|
+ puts " #{property.output(table.name)}"
+ end
+
+ puts "};"
+ puts ""
+ puts "static const struct HashTable #{table.name} ="
+ puts " \{ #{allProperties.size}, #{table.stringProperties.size}, #{stringCompactTable.mask}, #{symbolCompactTableMask}, #{table.hasSetter?}, #{nameEntries}, #{stringNameIndex}, #{symbolNameIndex} \};"
+ puts ""
+ puts "} // namespace JSC"
+ end
+end
+
+def parse(file)
+ tables = []
+ table = nil
+
+ file.each do |line|
+ line.strip!
+ next if line =~ /^\#|^$/ # Comment or blank line. Do nothing.
+
+ if line =~ /^\@begin/ && !table
+ if line =~ /^\@begin\s*([:_\w]+)\s*\d*\s*$/
+ table = PropertyTable.new($1)
+ else
+ STDERR.puts "WARNING: \@begin without table name, skipping \"#{line}\"";
+ end
+ elsif line =~ /^\@end\s*$/ && table
+ tables.push table
+ table = nil
+ elsif line =~ /^(\S+)\s*(\S+)\s*([\w\|]*)\s*(\w*)\s*(\w*)\s*$/ && table
+ key = $1
+ value = $2
+ attributes = $3
+ param = $4
+ intrinsic = $5
+ table.push(Property.new(key, value, attributes, param, intrinsic))
+ elsif table
+ abort "invalid data \"#{line}\""
+ end
+ end
+
+ abort "missing closing \@end" if table
+
+ tables
+end
+
+$filename = ARGV.first
+File.open($filename) do |file|
+ output(parse(file), $filename)
+end
diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp
index 8b33731..ba9dc59 100644
--- a/Source/JavaScriptCore/runtime/JSObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSObject.cpp
@@ -81,9 +81,17 @@ static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* class
if (!table)
continue;
- for (auto iter = table->begin(); iter != table->end(); ++iter) {
- if (!(iter->attributes() & DontEnum) || mode.includeDontEnumProperties())
- propertyNames.add(Identifier::fromString(&vm, iter.key()));
+ for (const HashTableValue& value : *table) {
+ if (!(value.attributes() & DontEnum) || mode.includeDontEnumProperties()) {
+ if (value.isSymbolKeyed()) {
+ if (propertyNames.includeSymbolProperties())
+ propertyNames.add(Identifier::fromUid(&vm, value.symbolKey()));
+ continue;
+ }
+
+ if (propertyNames.includeStringProperties())
+ propertyNames.add(Identifier::fromString(&vm, value.stringKey()));
+ }
}
}
}
@@ -1987,7 +1995,7 @@ void JSObject::reifyAllStaticProperties(ExecState* exec)
for (auto& value : *hashTable) {
unsigned attributes;
- auto key = Identifier::fromString(&vm, value.m_key);
+ Identifier key(value.isSymbolKeyed() ? Identifier::fromUid(&vm, value.symbolKey()) : Identifier::fromString(&vm, value.stringKey()));
PropertyOffset offset = getDirectOffset(vm, key, attributes);
if (!isValidOffset(offset))
reifyStaticProperty(vm, key, value, *this);
diff --git a/Source/JavaScriptCore/runtime/Lookup.h b/Source/JavaScriptCore/runtime/Lookup.h
index 5b2c4a4..df2f2a5 100644
--- a/Source/JavaScriptCore/runtime/Lookup.h
+++ b/Source/JavaScriptCore/runtime/Lookup.h
@@ -50,7 +50,7 @@ typedef JSValue (*LazyPropertyCallback)(VM&, JSObject*);
// Hash table generated by the create_hash_table script.
struct HashTableValue {
- const char* m_key; // property name
+ const char* m_propertyName; // property name
unsigned m_attributes; // JSObject attributes
Intrinsic m_intrinsic;
union ValueStorage {
@@ -70,6 +70,11 @@ struct HashTableValue {
} m_values;
unsigned attributes() const { return m_attributes; }
+ const char* stringKey() const { return m_propertyName; }
+ SymbolImpl* symbolKey() const { return const_cast<SymbolImpl*>(reinterpret_cast<const SymbolImpl*>(m_propertyName)); }
+ bool isEmpty() const { return m_propertyName == nullptr; }
+ bool isStringKeyed() const { return !isSymbolKeyed(); }
+ bool isSymbolKeyed() const { return m_attributes & HoldingSymbolKey; }
Intrinsic intrinsic() const { ASSERT(m_attributes & Function); return m_intrinsic; }
BuiltinGenerator builtinGenerator() const { ASSERT(m_attributes & Builtin); return reinterpret_cast<BuiltinGenerator>(m_values.value1); }
@@ -94,30 +99,25 @@ struct HashTableValue {
};
struct HashTable {
- int numberOfValues;
- int indexMask;
+ int numberOfAllValues;
+ int numberOfStringKeyedValues;
+ int stringKeyedIndexMask;
+ int symbolKeyedIndexMask;
bool hasSetterOrReadonlyProperties;
const HashTableValue* values; // Fixed values generated by script.
- const CompactHashIndex* index;
+ const CompactHashIndex* stringKeyedIndex;
+ const CompactHashIndex* symbolKeyedIndex;
- // Find an entry in the table, and return the entry.
- ALWAYS_INLINE const HashTableValue* entry(PropertyName propertyName) const
+ template<typename EqualPredicate>
+ static ALWAYS_INLINE const HashTableValue* searchInTable(int indexEntry, const CompactHashIndex* index, const HashTableValue* values, EqualPredicate predicate)
{
- if (propertyName.isSymbol())
- return nullptr;
-
- auto uid = propertyName.uid();
- if (!uid)
- return nullptr;
-
- int indexEntry = IdentifierRepHash::hash(uid) & indexMask;
int valueIndex = index[indexEntry].value;
if (valueIndex == -1)
return nullptr;
while (true) {
- if (WTF::equal(uid, values[valueIndex].m_key))
+ if (predicate(values[valueIndex]))
return &values[valueIndex];
indexEntry = index[indexEntry].next;
@@ -128,6 +128,30 @@ struct HashTable {
};
}
+ // Find an entry in the table, and return the entry.
+ ALWAYS_INLINE const HashTableValue* entry(PropertyName propertyName) const
+ {
+ auto uid = propertyName.uid();
+ if (!uid)
+ return nullptr;
+
+ if (uid->isSymbol()) {
+ if (!symbolKeyedIndex)
+ return nullptr;
+ // Call StringImpl::hash() explicitly since we would not like to retrieve symbolAwareHash() here.
+ int indexEntry = uid->hash() & symbolKeyedIndexMask;
+ return searchInTable(indexEntry, symbolKeyedIndex, values + numberOfStringKeyedValues, [&] (const HashTableValue& value) {
+ return uid == value.symbolKey();
+ });
+ }
+
+ ASSERT_WITH_MESSAGE(numberOfStringKeyedValues, "Currently, all the static hash table should hold string keyed properties");
+ int indexEntry = IdentifierRepHash::hash(uid) & stringKeyedIndexMask;
+ return searchInTable(indexEntry, stringKeyedIndex, values, [&] (const HashTableValue& value) {
+ return WTF::equal(uid, value.stringKey());
+ });
+ }
+
class ConstIterator {
public:
ConstIterator(const HashTable* table, int position)
@@ -144,11 +168,6 @@ struct HashTable {
const HashTableValue& operator*() const { return *value(); }
- const char* key() const
- {
- return m_table->values[m_position].m_key;
- }
-
const HashTableValue* operator->() const
{
return value();
@@ -162,7 +181,7 @@ struct HashTable {
ConstIterator& operator++()
{
- ASSERT(m_position < m_table->numberOfValues);
+ ASSERT(m_position < m_table->numberOfAllValues);
++m_position;
skipInvalidKeys();
return *this;
@@ -171,10 +190,10 @@ struct HashTable {
private:
void skipInvalidKeys()
{
- ASSERT(m_position <= m_table->numberOfValues);
- while (m_position < m_table->numberOfValues && !m_table->values[m_position].m_key)
+ ASSERT(m_position <= m_table->numberOfAllValues);
+ while (m_position < m_table->numberOfAllValues && m_table->values[m_position].isEmpty())
++m_position;
- ASSERT(m_position <= m_table->numberOfValues);
+ ASSERT(m_position <= m_table->numberOfAllValues);
}
const HashTable* m_table;
@@ -187,7 +206,7 @@ struct HashTable {
}
ConstIterator end() const
{
- return ConstIterator(this, numberOfValues);
+ return ConstIterator(this, numberOfAllValues);
}
};
@@ -346,9 +365,9 @@ inline void reifyStaticProperties(VM& vm, const HashTableValue (&values)[numberO
{
BatchedTransitionOptimizer transitionOptimizer(vm, &thisObj);
for (auto& value : values) {
- if (!value.m_key)
+ if (value.isEmpty())
continue;
- auto key = Identifier::fromString(&vm, reinterpret_cast<const LChar*>(value.m_key), strlen(value.m_key));
+ Identifier key(value.isSymbolKeyed() ? Identifier::fromUid(&vm, value.symbolKey()) : Identifier::fromString(&vm, reinterpret_cast<const LChar*>(value.stringKey()), strlen(value.stringKey())));
reifyStaticProperty(vm, key, value, thisObj);
}
}
diff --git a/Source/JavaScriptCore/runtime/PropertySlot.h b/Source/JavaScriptCore/runtime/PropertySlot.h
index d7d76c7..28632fc 100644
--- a/Source/JavaScriptCore/runtime/PropertySlot.h
+++ b/Source/JavaScriptCore/runtime/PropertySlot.h
@@ -49,6 +49,7 @@ enum Attribute {
CellProperty = 1 << 11, // property is a lazy property - only used by static hashtables
ClassStructure = 1 << 12, // property is a lazy class structure - only used by static hashtables
PropertyCallback = 1 << 13, // property that is a lazy property callback - only used by static hashtables
+ HoldingSymbolKey = 1 << 14, // property that key is symbol - only used by static hashtables
BuiltinOrFunction = Builtin | Function, // helper only used by static hashtables
BuiltinOrFunctionOrLazyProperty = Builtin | Function | CellProperty | ClassStructure | PropertyCallback, // helper only used by static hashtables
BuiltinOrFunctionOrAccessorOrLazyProperty = Builtin | Function | Accessor | CellProperty | ClassStructure | PropertyCallback, // helper only used by static hashtables
diff --git a/Source/JavaScriptCore/runtime/StringConstructor.cpp b/Source/JavaScriptCore/runtime/StringConstructor.cpp
index db564bb..ef1becd 100644
--- a/Source/JavaScriptCore/runtime/StringConstructor.cpp
+++ b/Source/JavaScriptCore/runtime/StringConstructor.cpp
@@ -45,7 +45,7 @@ const ClassInfo StringConstructor::s_info = { "Function", &InternalFunction::s_i
/* Source for StringConstructor.lut.h
@begin stringConstructorTable
fromCharCode stringFromCharCode DontEnum|Function 1
- fromCodePoint stringFromCodePoint DontEnum|Function 1
+ fromCodePoint stringFromCodePoint DontEnum|Function 1 FromCharCodeIntrinsic
raw JSBuiltin DontEnum|Function 1
@end
*/
diff --git a/Source/WTF/wtf/StdLibExtras.h b/Source/WTF/wtf/StdLibExtras.h
index 4457ba2..746aa06 100644
--- a/Source/WTF/wtf/StdLibExtras.h
+++ b/Source/WTF/wtf/StdLibExtras.h
@@ -184,10 +184,11 @@ inline size_t roundUpToMultipleOf(size_t divisor, size_t x)
return (x + remainderMask) & ~remainderMask;
}
-template<size_t divisor> inline size_t roundUpToMultipleOf(size_t x)
+template<size_t divisor> inline constexpr size_t roundUpToMultipleOf(size_t x)
{
static_assert(divisor && !(divisor & (divisor - 1)), "divisor must be a power of two!");
- return roundUpToMultipleOf(divisor, x);
+ constexpr size_t remainderMask = divisor - 1;
+ return (x + remainderMask) & ~remainderMask;
}
enum BinarySearchMode {
diff --git a/Source/WTF/wtf/text/AtomicStringImpl.cpp b/Source/WTF/wtf/text/AtomicStringImpl.cpp
index ac60b63..5d20a94 100644
--- a/Source/WTF/wtf/text/AtomicStringImpl.cpp
+++ b/Source/WTF/wtf/text/AtomicStringImpl.cpp
@@ -401,7 +401,7 @@ Ref<AtomicStringImpl> AtomicStringImpl::addSlowCase(StringImpl& string)
if (!string.length())
return *static_cast<AtomicStringImpl*>(StringImpl::empty());
- if (string.isSymbol()) {
+ if (string.isSymbol() || string.isStatic()) {
if (string.is8Bit())
return *add(string.characters8(), string.length());
return *add(string.characters16(), string.length());
@@ -425,7 +425,7 @@ Ref<AtomicStringImpl> AtomicStringImpl::addSlowCase(AtomicStringTable& stringTab
if (!string.length())
return *static_cast<AtomicStringImpl*>(StringImpl::empty());
- if (string.isSymbol()) {
+ if (string.isSymbol() || string.isStatic()) {
if (string.is8Bit())
return *add(string.characters8(), string.length());
return *add(string.characters16(), string.length());
@@ -461,7 +461,7 @@ RefPtr<AtomicStringImpl> AtomicStringImpl::lookUpSlowCase(StringImpl& string)
if (!string.length())
return static_cast<AtomicStringImpl*>(StringImpl::empty());
- if (string.isSymbol()) {
+ if (string.isSymbol() || string.isStatic()) {
if (string.is8Bit())
return lookUpInternal(string.characters8(), string.length());
return lookUpInternal(string.characters16(), string.length());
diff --git a/Source/WTF/wtf/text/StringImpl.cpp b/Source/WTF/wtf/text/StringImpl.cpp
index 2d0da35..61cd24d 100644
--- a/Source/WTF/wtf/text/StringImpl.cpp
+++ b/Source/WTF/wtf/text/StringImpl.cpp
@@ -294,13 +294,7 @@ Ref<StringImpl> StringImpl::create(const LChar* string)
Ref<SymbolImpl> StringImpl::createSymbol(StringImpl& rep)
{
auto* ownerRep = (rep.bufferOwnership() == BufferSubstring) ? rep.substringBuffer() : &rep;
-
- // We allocate a buffer that contains
- // 1. the StringImpl struct
- // 2. the pointer to the owner string
- // 3. the pointer to the symbol registry
- // 4. the placeholder for symbol aware hash value (allocated size is pointer size, but only 4 bytes are used)
- auto* stringImpl = static_cast<StringImpl*>(fastMalloc(allocationSize<StringImpl*>(3)));
+ auto* stringImpl = static_cast<StringImpl*>(fastMalloc(SymbolImpl::allocationSize()));
if (rep.is8Bit())
return adoptRef(static_cast<SymbolImpl&>(*new (NotNull, stringImpl) StringImpl(CreateSymbol, rep.m_data8, rep.length(), *ownerRep)));
return adoptRef(static_cast<SymbolImpl&>(*new (NotNull, stringImpl) StringImpl(CreateSymbol, rep.m_data16, rep.length(), *ownerRep)));
diff --git a/Source/WTF/wtf/text/StringImpl.h b/Source/WTF/wtf/text/StringImpl.h
index 53484fe..2c6e5a3 100644
--- a/Source/WTF/wtf/text/StringImpl.h
+++ b/Source/WTF/wtf/text/StringImpl.h
@@ -140,6 +140,10 @@ class StringImpl {
friend class JSC::LLInt::Data;
friend class JSC::LLIntOffsetsExtractor;
+public:
+ enum ConstructStaticStringTag { ConstructStaticString };
+ enum CreateSymbolTag { CreateSymbol };
+
private:
enum BufferOwnership {
BufferInternal,
@@ -298,7 +302,6 @@ class StringImpl {
STRING_STATS_ADD_16BIT_STRING2(m_length, true);
}
- enum CreateSymbolTag { CreateSymbol };
// Used to create new symbol strings that holds existing 8-bit [[Description]] string as a substring buffer (BufferSubstring).
StringImpl(CreateSymbolTag, const LChar* characters, unsigned length, Ref<StringImpl>&& base)
: m_refCount(s_refCountIncrement)
@@ -335,6 +338,45 @@ class StringImpl {
STRING_STATS_ADD_16BIT_STRING2(m_length, true);
}
+protected:
+ // Static StringImpl will be constructed with NeverDestroyed. And all its members are initialized and not changed (logically),
+ // and it is never destroyed. Thus it can be shared across the threads (as the same to StringImpl::empty()).
+ template<unsigned charactersCount>
+ StringImpl(ConstructStaticStringTag, const char (&characters)[charactersCount])
+ : m_refCount(s_refCountFlagIsStaticString)
+ , m_length(charactersCount - 1)
+ , m_data8(reinterpret_cast<const LChar*>(characters))
+ , m_hashAndFlags(s_hashFlag8BitBuffer | StringNormal | BufferInternal)
+ {
+ // Ensure that the hash is computed so that AtomicStringHash can call existingHash() with impunity.
+ STRING_STATS_ADD_8BIT_STRING(m_length);
+
+ hash();
+ }
+
+ // Used to create a new static symbol strings derived from the static string.
+ StringImpl(CreateSymbolTag, ConstructStaticStringTag, Ref<StringImpl>&& base)
+ : m_refCount(s_refCountFlagIsStaticString)
+ , m_length(base->length())
+ , m_data8(base->characters8())
+ , m_hashAndFlags(s_hashFlag8BitBuffer | StringSymbol | BufferSubstring)
+ {
+ ASSERT(is8Bit());
+ ASSERT(isStatic());
+ ASSERT(m_data8);
+ ASSERT(base->bufferOwnership() != BufferSubstring);
+ ASSERT(base->isStatic());
+ ASSERT(base->is8Bit());
+
+ // Ensure all its members are initialized in the constructor.
+ substringBuffer() = &base.leakRef();
+ symbolRegistry() = nullptr;
+ hashForSymbol() = nextHashForSymbol();
+ hash();
+
+ STRING_STATS_ADD_8BIT_STRING2(m_length, true);
+ }
+
public:
WTF_EXPORT_STRING_API static void destroy(StringImpl*);
@@ -793,6 +835,12 @@ class StringImpl {
protected:
~StringImpl();
+ template<typename T>
+ static constexpr size_t allocationSize(unsigned tailElementCount)
+ {
+ return tailOffset<T>() + tailElementCount * sizeof(T);
+ }
+
private:
bool requiresCopy() const
{
@@ -805,13 +853,7 @@ class StringImpl {
}
template<typename T>
- static size_t allocationSize(unsigned tailElementCount)
- {
- return tailOffset<T>() + tailElementCount * sizeof(T);
- }
-
- template<typename T>
- static ptrdiff_t tailOffset()
+ static constexpr ptrdiff_t tailOffset()
{
#if COMPILER(MSVC)
// MSVC doesn't support alignof yet.
diff --git a/Source/WTF/wtf/text/SymbolImpl.h b/Source/WTF/wtf/text/SymbolImpl.h
index 4043bf4..b162b1a 100644
--- a/Source/WTF/wtf/text/SymbolImpl.h
+++ b/Source/WTF/wtf/text/SymbolImpl.h
@@ -33,9 +33,33 @@ namespace WTF {
// SymbolImpl is used to represent the symbol string impl.
// It is uniqued string impl, but is not registered in Atomic String tables, so it's not atomic.
class SymbolImpl : public UniquedStringImpl {
+public:
+ static constexpr size_t allocationSize()
+ {
+ // We allocate a buffer that contains
+ // 1. the StringImpl struct
+ // 2. the pointer to the owner string
+ // 3. the pointer to the symbol registry
+ // 4. the placeholder for symbol aware hash value (allocated size is pointer size, but only 4 bytes are used)
+ return StringImpl::allocationSize<StringImpl*>(3);
+ }
+
private:
SymbolImpl() = delete;
+
+ // Called from NeverDestroyed.
+ friend class NeverDestroyed<SymbolImpl>;
+ SymbolImpl(ConstructStaticStringTag, StringImpl& staticStringImpl)
+ : UniquedStringImpl(CreateSymbol, ConstructStaticString, staticStringImpl)
+ {
+ }
+
+ StringImpl* m_owner;
+ SymbolRegistry* m_symbolRegistry;
+ void* m_symbolAwareHash;
};
+static_assert(SymbolImpl::allocationSize() == sizeof(SymbolImpl), "sizeof(SymbolImpl) should contain 3 tail pointers.");
+
#if !ASSERT_DISABLED
// SymbolImpls created from StaticASCIILiteral will ASSERT
diff --git a/Source/WTF/wtf/text/UniquedStringImpl.h b/Source/WTF/wtf/text/UniquedStringImpl.h
index 554e533..3bdc329 100644
--- a/Source/WTF/wtf/text/UniquedStringImpl.h
+++ b/Source/WTF/wtf/text/UniquedStringImpl.h
@@ -35,6 +35,12 @@ namespace WTF {
class UniquedStringImpl : public StringImpl {
private:
UniquedStringImpl() = delete;
+
+protected:
+ UniquedStringImpl(CreateSymbolTag, ConstructStaticStringTag, StringImpl& staticStringImpl)
+ : StringImpl(CreateSymbol, ConstructStaticString, staticStringImpl)
+ {
+ }
};
#if !ASSERT_DISABLED
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment