From a63911fdc3dd0c24e5c47d6106e8884eb27e74b3 Mon Sep 17 00:00:00 2001 From: Geoffrey Garen Date: Thu, 17 Apr 2025 13:42:10 -0700 Subject: [PATCH] StringImpl refcounting should be atomic https://bugs.webkit.org/show_bug.cgi?id=289962 rdar://147313174 Backport notes: I removed some optimizations; ref churn is not great, but still correct. Original notes: Reviewed by Antti Koivisto, Yusuke Suzuki, Chris Dumez, and Keith Miller. This patch takes the first step to making WTF::String thread-safe: Making m_refcount increment and decrement atomic. (Hash, flags, and the AtomString table are not yet atomic. I plan to address those in my next patch.) The direct code change is pretty small, but it measured as a performance regression. So most of this code is optimization. I got rid of about 1/3 of WTF::StringImpl refcounts on Speedometer and about 1/2 on JetStream. ARM has a special ldadd instruction for fast atomics. Net net, it measures as about 1.5X slower than just add. That's pretty good! But it still required some optimizations to be affordable. Intel atomics are much slower, so it took far more optimizations to get Intel across the finish line. A/B testers suggest that we might still have a < 0.5% JetStream regression on Intel. I checked in with Yusuke, Keith, and Maciej, and we can move forward because we have a large total JetStream speedup on Intel this year, so customers on older hardware will still see a net speedup when they get new software. --- .../bytecode/DirectEvalCodeCache.cpp | 4 +- .../bytecode/DirectEvalCodeCache.h | 71 ++- Source/JavaScriptCore/dfg/DFGOperations.cpp | 29 +- Source/JavaScriptCore/heap/GCOwnedDataScope.h | 9 +- .../interpreter/Interpreter.cpp | 37 +- Source/JavaScriptCore/jit/JITOperations.cpp | 146 +++---- .../JavaScriptCore/llint/LLIntSlowPaths.cpp | 4 +- Source/JavaScriptCore/parser/Parser.cpp | 10 +- Source/JavaScriptCore/parser/Parser.h | 12 +- .../parser/VariableEnvironment.cpp | 8 +- .../parser/VariableEnvironment.h | 16 +- Source/JavaScriptCore/runtime/ArgList.h | 3 + .../JavaScriptCore/runtime/ArrayPrototype.cpp | 6 + .../runtime/CacheableIdentifier.h | 4 + .../runtime/CacheableIdentifierInlines.h | 19 + .../runtime/CommonSlowPaths.cpp | 4 +- .../runtime/EnsureStillAliveHere.h | 3 + Source/JavaScriptCore/runtime/Identifier.h | 5 +- .../runtime/JSGlobalObjectFunctions.cpp | 15 +- .../runtime/JSONAtomStringCache.h | 11 +- .../runtime/JSONAtomStringCacheInlines.h | 26 +- Source/JavaScriptCore/runtime/JSONObject.cpp | 5 +- Source/JavaScriptCore/runtime/JSObject.cpp | 12 +- Source/JavaScriptCore/runtime/JSObject.h | 4 +- .../JavaScriptCore/runtime/JSObjectInlines.h | 7 +- .../runtime/JSPropertyNameEnumerator.cpp | 6 +- Source/JavaScriptCore/runtime/JSString.cpp | 18 +- Source/JavaScriptCore/runtime/JSString.h | 43 +- .../JavaScriptCore/runtime/JSStringInlines.h | 25 ++ .../JavaScriptCore/runtime/JSStringJoiner.cpp | 82 +++- .../JavaScriptCore/runtime/JSStringJoiner.h | 49 ++- .../JavaScriptCore/runtime/LiteralParser.cpp | 119 ++++-- Source/JavaScriptCore/runtime/LiteralParser.h | 6 +- .../runtime/ObjectConstructor.cpp | 10 +- .../runtime/ObjectConstructorInlines.h | 6 +- .../runtime/ObjectPrototype.cpp | 18 +- .../JavaScriptCore/runtime/ObjectPrototype.h | 2 +- Source/JavaScriptCore/runtime/PropertyName.h | 11 + .../JavaScriptCore/runtime/SmallStrings.cpp | 7 + Source/JavaScriptCore/runtime/SmallStrings.h | 1 + .../runtime/StringPrototype.cpp | 16 +- Source/JavaScriptCore/runtime/Structure.cpp | 2 +- Source/JavaScriptCore/runtime/Structure.h | 2 + .../JavaScriptCore/runtime/StructureInlines.h | 8 +- .../runtime/StructureTransitionTable.h | 4 +- Source/WTF/WTF.xcodeproj/project.pbxproj | 4 + Source/WTF/wtf/CMakeLists.txt | 1 + Source/WTF/wtf/RefCountedFixedVector.h | 23 +- Source/WTF/wtf/ValueOrReference.h | 78 ++++ Source/WTF/wtf/Vector.h | 10 + Source/WTF/wtf/text/StringImpl.cpp | 1 + Source/WTF/wtf/text/StringImpl.h | 22 +- .../AccessibilityRenderObject.cpp | 6 +- .../accessibility/AccessibilitySlider.cpp | 2 +- .../ios/AccessibilityObjectIOS.mm | 2 +- .../bindings/js/JSDOMConvertStrings.cpp | 4 +- .../WebCore/bindings/js/JSDOMConvertStrings.h | 4 +- .../bindings/js/JSHTMLAllCollectionCustom.cpp | 2 +- Source/WebCore/css/SelectorFilter.cpp | 2 +- .../WebCore/css/query/MediaQueryEvaluator.cpp | 10 +- Source/WebCore/dom/Attribute.h | 6 + Source/WebCore/dom/CharacterData.h | 5 +- Source/WebCore/dom/Text.cpp | 5 - Source/WebCore/dom/Text.h | 5 +- Source/WebCore/domjit/DOMJITIDLConvert.h | 4 +- .../editing/cocoa/AutofillElements.cpp | 4 +- Source/WebCore/editing/cocoa/HTMLConverter.mm | 4 +- .../WebCore/html/BaseCheckableInputType.cpp | 4 +- Source/WebCore/html/BaseCheckableInputType.h | 2 +- .../WebCore/html/BaseDateAndTimeInputType.cpp | 12 +- .../WebCore/html/BaseDateAndTimeInputType.h | 2 +- Source/WebCore/html/ColorInputType.cpp | 6 +- Source/WebCore/html/ColorInputType.h | 4 +- .../WebCore/html/DateTimeLocalInputType.cpp | 8 +- Source/WebCore/html/DateTimeLocalInputType.h | 2 +- Source/WebCore/html/EmailInputType.cpp | 2 +- Source/WebCore/html/EmailInputType.h | 2 +- Source/WebCore/html/HTMLInputElement.cpp | 26 +- Source/WebCore/html/HTMLInputElement.h | 5 +- Source/WebCore/html/HTMLTextAreaElement.cpp | 21 +- Source/WebCore/html/HTMLTextAreaElement.h | 6 +- .../html/HTMLTextFormControlElement.cpp | 9 +- .../WebCore/html/HTMLTextFormControlElement.h | 5 +- Source/WebCore/html/InputType.cpp | 4 +- Source/WebCore/html/InputType.h | 7 +- Source/WebCore/html/MonthInputType.cpp | 2 +- Source/WebCore/html/NumberInputType.cpp | 8 +- Source/WebCore/html/NumberInputType.h | 2 +- Source/WebCore/html/RadioNodeList.cpp | 2 +- Source/WebCore/html/RangeInputType.cpp | 8 +- Source/WebCore/html/RangeInputType.h | 4 +- Source/WebCore/html/SearchInputType.cpp | 2 +- Source/WebCore/html/TextFieldInputType.cpp | 8 +- Source/WebCore/html/TextFieldInputType.h | 2 +- Source/WebCore/html/URLInputType.cpp | 4 +- Source/WebCore/html/URLInputType.h | 2 +- Source/WebCore/html/parser/AtomHTMLToken.h | 13 +- .../parser/HTMLDocumentParserFastPath.cpp | 14 +- .../html/shadow/DateTimeFieldElement.cpp | 4 +- .../html/shadow/DateTimeFieldElement.h | 3 +- .../shadow/DateTimeNumericFieldElement.cpp | 6 +- .../html/shadow/DateTimeNumericFieldElement.h | 2 +- .../shadow/DateTimeSymbolicFieldElement.cpp | 6 +- .../shadow/DateTimeSymbolicFieldElement.h | 2 +- .../html/shadow/SliderThumbElement.cpp | 2 +- .../html/shadow/TextControlInnerElements.cpp | 2 +- .../inline/InlineItemsBuilder.cpp | 2 +- .../inline/text/TextUtil.cpp | 11 +- Source/WebCore/page/InteractionRegion.cpp | 2 +- .../WebCore/platform/graphics/FontCascade.cpp | 9 +- .../WebCore/platform/graphics/FontCascade.h | 7 + .../platform/graphics/WidthIterator.cpp | 4 +- .../WebCore/rendering/RenderSearchField.cpp | 2 +- .../WebCore/rendering/style/RenderStyle.cpp | 11 + Source/WebCore/rendering/style/RenderStyle.h | 4 + Source/WebCore/style/StyleAdjuster.cpp | 4 +- Source/WebCore/style/StyleBuilder.cpp | 6 +- Source/WebCore/style/StyleBuilderCustom.h | 153 +++---- Source/WebCore/style/StyleBuilderState.h | 58 ++- .../WebCore/style/StyleBuilderStateInlines.h | 404 +++++++++++++++++- .../WebProcess/WebPage/ios/WebPageIOS.mm | 2 +- .../ios/WebCoreSupport/WebVisiblePosition.mm | 4 +- .../mac/DOM/DOMHTMLInputElement.mm | 2 +- .../mac/DOM/DOMHTMLTextAreaElement.mm | 2 +- 124 files changed, 1486 insertions(+), 535 deletions(-) create mode 100644 Source/WTF/wtf/ValueOrReference.h diff --git a/Source/JavaScriptCore/bytecode/DirectEvalCodeCache.cpp b/Source/JavaScriptCore/bytecode/DirectEvalCodeCache.cpp index 724063c75d9cf..d87b5b8289876 100644 --- a/Source/JavaScriptCore/bytecode/DirectEvalCodeCache.cpp +++ b/Source/JavaScriptCore/bytecode/DirectEvalCodeCache.cpp @@ -30,13 +30,13 @@ namespace JSC { -void DirectEvalCodeCache::setSlow(JSGlobalObject* globalObject, JSCell* owner, const String& evalSource, BytecodeIndex bytecodeIndex, DirectEvalExecutable* evalExecutable) +void DirectEvalCodeCache::setSlow(JSGlobalObject* globalObject, JSCell* owner, const CacheLookupKey& cacheKey, DirectEvalExecutable* evalExecutable) { if (!evalExecutable->allowDirectEvalCache()) return; Locker locker { m_lock }; - m_cacheMap.set(CacheKey(evalSource, bytecodeIndex), WriteBarrier(globalObject->vm(), owner, evalExecutable)); + m_cacheMap.set(cacheKey, WriteBarrier(globalObject->vm(), owner, evalExecutable)); } void DirectEvalCodeCache::clear() diff --git a/Source/JavaScriptCore/bytecode/DirectEvalCodeCache.h b/Source/JavaScriptCore/bytecode/DirectEvalCodeCache.h index 70b2d8bfa38f8..d312b2423925a 100644 --- a/Source/JavaScriptCore/bytecode/DirectEvalCodeCache.h +++ b/Source/JavaScriptCore/bytecode/DirectEvalCodeCache.h @@ -39,11 +39,20 @@ namespace JSC { class DirectEvalCodeCache { public: + enum class RopeSuffix : uint8_t { + None, + FunctionCall + }; + + class CacheLookupKey; + class CacheKey { + friend class CacheLookupKey; public: - CacheKey(const String& source, BytecodeIndex bytecodeIndex) - : m_source(source.impl()) + CacheKey(StringImpl* source, BytecodeIndex bytecodeIndex, RopeSuffix ropeSuffix) + : m_source(source) , m_bytecodeIndex(bytecodeIndex) + , m_ropeSuffix(ropeSuffix) { } @@ -54,13 +63,13 @@ namespace JSC { CacheKey() = default; - unsigned hash() const { return m_source->hash() ^ m_bytecodeIndex.asBits(); } + unsigned hash() const { return m_source->hash() + m_bytecodeIndex.asBits() + enumToUnderlyingType(m_ropeSuffix); } bool isEmptyValue() const { return !m_source; } bool operator==(const CacheKey& other) const { - return m_bytecodeIndex == other.m_bytecodeIndex && WTF::equal(m_source.get(), other.m_source.get()); + return m_bytecodeIndex == other.m_bytecodeIndex && m_ropeSuffix == other.m_ropeSuffix && WTF::equal(m_source.get(), other.m_source.get()); } bool isHashTableDeletedValue() const { return m_source.isHashTableDeletedValue(); } @@ -82,17 +91,61 @@ namespace JSC { private: RefPtr m_source; BytecodeIndex m_bytecodeIndex; + RopeSuffix m_ropeSuffix; + }; + + class CacheLookupKey { + void* operator new(size_t) = delete; + + public: + CacheLookupKey(StringImpl* source, BytecodeIndex bytecodeIndex, RopeSuffix ropeSuffix) + : m_source(source) + , m_bytecodeIndex(bytecodeIndex) + , m_ropeSuffix(ropeSuffix) + { + } + + CacheLookupKey() = default; + + unsigned hash() const { return m_source->hash() + m_bytecodeIndex.asBits() + enumToUnderlyingType(m_ropeSuffix); } + + bool operator==(const CacheKey& other) const + { + return m_bytecodeIndex == other.m_bytecodeIndex && m_ropeSuffix == other.m_ropeSuffix && WTF::equal(m_source, other.m_source.get()); + } + + operator CacheKey() const + { + return CacheKey(m_source, m_bytecodeIndex, m_ropeSuffix); + } + + private: + SUPPRESS_UNCOUNTED_MEMBER StringImpl* m_source; + BytecodeIndex m_bytecodeIndex; + RopeSuffix m_ropeSuffix; + }; + + struct CacheLookupKeyHashTranslator { + static unsigned hash(const CacheLookupKey& key) + { + return key.hash(); + } + + static bool equal(const CacheKey& a, const CacheLookupKey& b) + { + return b == a; + } }; - DirectEvalExecutable* tryGet(const String& evalSource, BytecodeIndex bytecodeIndex) + DirectEvalExecutable* get(const CacheLookupKey& cacheKey) { - return m_cacheMap.inlineGet(CacheKey(evalSource, bytecodeIndex)).get(); + return m_cacheMap.inlineGet(cacheKey).get(); } - void set(JSGlobalObject* globalObject, JSCell* owner, const String& evalSource, BytecodeIndex bytecodeIndex, DirectEvalExecutable* evalExecutable) + void set(JSGlobalObject* globalObject, JSCell* owner, const CacheLookupKey& cacheKey, DirectEvalExecutable* evalExecutable) { if (m_cacheMap.size() < maxCacheEntries) - setSlow(globalObject, owner, evalSource, bytecodeIndex, evalExecutable); + setSlow(globalObject, owner, cacheKey, evalExecutable); } bool isEmpty() const { return m_cacheMap.isEmpty(); } @@ -104,7 +157,7 @@ namespace JSC { private: static constexpr int maxCacheEntries = 64; - void setSlow(JSGlobalObject*, JSCell* owner, const String& evalSource, BytecodeIndex, DirectEvalExecutable*); + void setSlow(JSGlobalObject*, JSCell* owner, const CacheLookupKey& cacheKey, DirectEvalExecutable*); typedef UncheckedKeyHashMap, CacheKey::Hash, CacheKey::HashTraits> EvalCacheMap; EvalCacheMap m_cacheMap; diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index f971f0e32df51..50c18c730cfd3 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -303,7 +303,7 @@ JSC_DEFINE_JIT_OPERATION(operationObjectAssignObject, void, (JSGlobalObject* glo auto scope = DECLARE_THROW_SCOPE(vm); if (auto* targetObject = jsDynamicCast(target); targetObject && targetObject->canPerformFastPutInlineExcludingProto() && targetObject->isStructureExtensible()) { - Vector, 8> properties; + Vector properties; MarkedArgumentBuffer values; if (!source->staticPropertiesReified()) { source->reifyAllStaticProperties(globalObject); @@ -351,7 +351,7 @@ JSC_DEFINE_JIT_OPERATION(operationObjectAssignUntyped, void, (JSGlobalObject* gl OPERATION_RETURN_IF_EXCEPTION(scope); } - Vector, 8> properties; + Vector properties; MarkedArgumentBuffer values; bool objectAssignFastSucceeded = objectAssignFast(globalObject, targetObject, source, properties, values); OPERATION_RETURN_IF_EXCEPTION(scope); @@ -855,10 +855,10 @@ JSC_DEFINE_JIT_OPERATION(operationGetByValObjectString, EncodedJSValue, (JSGloba auto scope = DECLARE_THROW_SCOPE(vm); - auto propertyName = asString(string)->toIdentifier(globalObject); + auto propertyName = asString(string)->toAtomString(globalObject); OPERATION_RETURN_IF_EXCEPTION(scope, encodedJSValue()); - OPERATION_RETURN(scope, JSValue::encode(getByValObject(globalObject, vm, asObject(base), propertyName))); + OPERATION_RETURN(scope, JSValue::encode(getByValObject(globalObject, vm, asObject(base), propertyName.data))); } JSC_DEFINE_JIT_OPERATION(operationGetByValObjectSymbol, EncodedJSValue, (JSGlobalObject* globalObject, JSCell* base, JSCell* symbol)) @@ -2003,7 +2003,7 @@ JSC_DEFINE_JIT_OPERATION(operationPutByValWithThis, void, (JSGlobalObject* globa OPERATION_RETURN(scope); } -ALWAYS_INLINE static void defineDataProperty(JSGlobalObject* globalObject, JSObject* base, const Identifier& propertyName, JSValue value, int32_t attributes) +ALWAYS_INLINE static void defineDataProperty(JSGlobalObject* globalObject, JSObject* base, PropertyName propertyName, JSValue value, int32_t attributes) { PropertyDescriptor descriptor = toPropertyDescriptor(value, jsUndefined(), jsUndefined(), DefinePropertyAttributes(attributes)); ASSERT((descriptor.attributes() & PropertyAttribute::Accessor) || (!descriptor.isAccessorDescriptor())); @@ -2047,7 +2047,7 @@ JSC_DEFINE_JIT_OPERATION(operationDefineDataPropertyStringIdent, void, (JSGlobal CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); - defineDataProperty(globalObject, base, Identifier::fromUid(vm, property), JSValue::decode(encodedValue), attributes); + defineDataProperty(globalObject, base, property, JSValue::decode(encodedValue), attributes); OPERATION_RETURN(scope); } @@ -2667,7 +2667,7 @@ JSC_DEFINE_JIT_OPERATION(operationEnumeratorNextUpdatePropertyName, JSString*, ( if (modeNumber == JSPropertyNameEnumerator::IndexedMode) { if (index < enumerator->indexedLength()) - OPERATION_RETURN(scope, jsString(vm, Identifier::from(vm, index).string())); + OPERATION_RETURN(scope, jsString(vm, Identifier::from(vm, index).releaseImpl())); OPERATION_RETURN(scope, vm.smallStrings.sentinelString()); } @@ -3633,6 +3633,21 @@ JSC_DEFINE_JIT_OPERATION(operationHasOwnProperty, size_t, (JSGlobalObject* globa auto scope = DECLARE_THROW_SCOPE(vm); JSValue key = JSValue::decode(encodedKey); + + if (LIKELY(key.isString())) { + auto propertyName = asString(key)->toAtomString(globalObject); + OPERATION_RETURN_IF_EXCEPTION(scope, false); + + PropertySlot slot(thisObject, PropertySlot::InternalMethodType::GetOwnProperty); + bool result = thisObject->hasOwnProperty(globalObject, propertyName.data, slot); + OPERATION_RETURN_IF_EXCEPTION(scope, false); + + HasOwnPropertyCache* hasOwnPropertyCache = vm.hasOwnPropertyCache(); + ASSERT(hasOwnPropertyCache); + hasOwnPropertyCache->tryAdd(slot, thisObject, propertyName.data, result); + OPERATION_RETURN(scope, result); + } + Identifier propertyName = key.toPropertyKey(globalObject); OPERATION_RETURN_IF_EXCEPTION(scope, false); diff --git a/Source/JavaScriptCore/heap/GCOwnedDataScope.h b/Source/JavaScriptCore/heap/GCOwnedDataScope.h index 3e6837581e4da..6d50ca4ac16a7 100644 --- a/Source/JavaScriptCore/heap/GCOwnedDataScope.h +++ b/Source/JavaScriptCore/heap/GCOwnedDataScope.h @@ -26,6 +26,7 @@ #pragma once #include "EnsureStillAliveHere.h" +#include namespace JSC { @@ -66,6 +67,10 @@ class JSCell; template struct GCOwnedDataScope { + WTF_FORBID_HEAP_ALLOCATION; +public: + GCOwnedDataScope() = default; + GCOwnedDataScope(const JSCell* cell, T value) : owner(cell) , data(value) @@ -87,8 +92,8 @@ struct GCOwnedDataScope { // Convenience conversion for String -> StringView operator StringView() const requires (std::is_same_v, String>) { return data; } - const JSCell* owner; - T data; + const JSCell* owner { nullptr }; + SUPPRESS_UNCOUNTED_MEMBER T data { }; }; } diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp index 91f75c2a4e066..3d8d4763f38f2 100644 --- a/Source/JavaScriptCore/interpreter/Interpreter.cpp +++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp @@ -92,6 +92,17 @@ namespace JSC { +static inline DirectEvalCodeCache::CacheLookupKey directEvalCacheKey(JSGlobalObject* globalObject, JSString* string, BytecodeIndex bytecodeIndex) +{ + if (string->isRope()) { + auto rope = string->asRope(); + if (auto source = rope->tryGetLHS("()"_s)) + return DirectEvalCodeCache::CacheLookupKey(source, bytecodeIndex, DirectEvalCodeCache::RopeSuffix::FunctionCall); + return DirectEvalCodeCache::CacheLookupKey(rope->resolveRope(globalObject).impl(), bytecodeIndex, DirectEvalCodeCache::RopeSuffix::None); + } + return DirectEvalCodeCache::CacheLookupKey(string->getValueImpl(), bytecodeIndex, DirectEvalCodeCache::RopeSuffix::None); +} + JSValue eval(CallFrame* callFrame, JSValue thisValue, JSScope* callerScopeChain, LexicallyScopedFeatures lexicallyScopedFeatures) { CallFrame* callerFrame = callFrame->callerFrame(); @@ -123,32 +134,31 @@ JSValue eval(CallFrame* callFrame, JSValue thisValue, JSScope* callerScopeChain, return jsUndefined(); JSValue program = callFrame->argument(0); - String programSource; + JSString* programString = nullptr; bool isTrusted = false; - if (LIKELY(program.isString())) { - programSource = program.toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, JSValue()); - } else if (Options::useTrustedTypes() && program.isObject()) { + if (LIKELY(program.isString())) + programString = asString(program); + else if (Options::useTrustedTypes() && program.isObject()) { auto* structure = globalObject->trustedScriptStructure(); if (structure == asObject(program)->structure()) { - programSource = program.toWTFString(globalObject); + programString = program.toString(globalObject); RETURN_IF_EXCEPTION(scope, { }); isTrusted = true; } else { auto code = globalObject->globalObjectMethodTable()->codeForEval(globalObject, program); RETURN_IF_EXCEPTION(scope, { }); if (!code.isNull()) { - programSource = code; + programString = jsString(vm, code); isTrusted = true; } } } - if (programSource.isNull()) + if (!programString) return program; if (Options::useTrustedTypes() && globalObject->requiresTrustedTypes() && !isTrusted) { - bool canCompileStrings = globalObject->globalObjectMethodTable()->canCompileStrings(globalObject, CompilationType::DirectEval, programSource, *vm.emptyList); + bool canCompileStrings = globalObject->globalObjectMethodTable()->canCompileStrings(globalObject, CompilationType::DirectEval, programString->value(globalObject).data, *vm.emptyList); RETURN_IF_EXCEPTION(scope, { }); if (!canCompileStrings) { throwException(globalObject, scope, createEvalError(globalObject, "Refused to evaluate a string as JavaScript because this document requires a 'Trusted Type' assignment."_s)); @@ -158,7 +168,7 @@ JSValue eval(CallFrame* callFrame, JSValue thisValue, JSScope* callerScopeChain, TopCallFrameSetter topCallFrame(vm, callFrame); if (!globalObject->evalEnabled()) { - globalObject->globalObjectMethodTable()->reportViolationForUnsafeEval(globalObject, programSource); + globalObject->globalObjectMethodTable()->reportViolationForUnsafeEval(globalObject, programString->value(globalObject).data); throwException(globalObject, scope, createEvalError(globalObject, globalObject->evalDisabledErrorMessage())); return { }; } @@ -182,8 +192,11 @@ JSValue eval(CallFrame* callFrame, JSValue thisValue, JSScope* callerScopeChain, else evalContextType = EvalContextType::None; - DirectEvalExecutable* eval = callerBaselineCodeBlock->directEvalCodeCache().tryGet(programSource, bytecodeIndex); + auto cacheKey = directEvalCacheKey(globalObject, programString, bytecodeIndex); + RETURN_IF_EXCEPTION(scope, { }); + DirectEvalExecutable* eval = callerBaselineCodeBlock->directEvalCodeCache().get(cacheKey); if (!eval) { + auto programSource = programString->value(globalObject).data; if (!(lexicallyScopedFeatures & StrictModeLexicallyScopedFeature)) { if (programSource.is8Bit()) { LiteralParser preparser(globalObject, programSource.span8(), SloppyJSON, callerBaselineCodeBlock); @@ -210,7 +223,7 @@ JSValue eval(CallFrame* callFrame, JSValue thisValue, JSScope* callerScopeChain, // Skip the eval cache if tainted since another eval call could have a different taintedness. if (sourceTaintedOrigin == SourceTaintedOrigin::Untainted) - callerBaselineCodeBlock->directEvalCodeCache().set(globalObject, callerBaselineCodeBlock, programSource, bytecodeIndex, eval); + callerBaselineCodeBlock->directEvalCodeCache().set(globalObject, callerBaselineCodeBlock, cacheKey, eval); } RELEASE_AND_RETURN(scope, vm.interpreter.executeEval(eval, thisValue, callerScopeChain)); diff --git a/Source/JavaScriptCore/jit/JITOperations.cpp b/Source/JavaScriptCore/jit/JITOperations.cpp index 25b6f4150fbd5..f017171a2697f 100644 --- a/Source/JavaScriptCore/jit/JITOperations.cpp +++ b/Source/JavaScriptCore/jit/JITOperations.cpp @@ -1639,13 +1639,21 @@ static void putByVal(JSGlobalObject* globalObject, JSValue baseValue, JSValue su } } - auto property = subscript.toPropertyKey(globalObject); - // Don't put to an object if toString threw an exception. + GCOwnedDataScope propertyName; + Identifier propertyKey; + UniquedStringImpl* uid = nullptr; + if (subscript.isString()) { + propertyName = asString(subscript)->toAtomString(globalObject); + uid = propertyName.data; + } else { + propertyKey = subscript.toPropertyKey(globalObject); + uid = propertyKey.impl(); + } RETURN_IF_EXCEPTION(scope, void()); scope.release(); PutPropertySlot slot(baseValue, ecmaMode.isStrict()); - baseValue.putInline(globalObject, property, value, slot); + baseValue.putInline(globalObject, uid, value, slot); } static void directPutByVal(JSGlobalObject* globalObject, JSObject* baseObject, JSValue subscript, JSValue value, ArrayProfile* arrayProfile, ECMAMode ecmaMode) @@ -1714,15 +1722,14 @@ static ALWAYS_INLINE void putByValOptimize(JSGlobalObject* globalObject, CodeBlo } } - if (CacheableIdentifier::isCacheableIdentifierCell(subscript)) { - const Identifier propertyName = subscript.toPropertyKey(globalObject); + if (auto propertyName = CacheableIdentifier::getCacheableIdentifier(subscript)) { RETURN_IF_EXCEPTION(scope, void()); - if (subscript.isSymbol() || !parseIndex(propertyName)) { + if (subscript.isSymbol() || !parseIndex(*propertyName.data)) { AccessType accessType = static_cast(stubInfo->accessType); PutPropertySlot slot(baseValue, isStrict, codeBlock->putByIdContext()); Structure* structure = CommonSlowPaths::originalStructureBeforePut(baseValue); - baseObject->putInline(globalObject, propertyName, value, slot); + baseObject->putInline(globalObject, propertyName.data, value, slot); RETURN_IF_EXCEPTION(scope, void()); if (accessType != static_cast(stubInfo->accessType)) @@ -3340,21 +3347,25 @@ ALWAYS_INLINE static JSValue getByVal(JSGlobalObject* globalObject, CallFrame* c VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (LIKELY(baseValue.isCell() && subscript.isString())) { - Structure& structure = *baseValue.asCell()->structure(); - if (JSCell::canUseFastGetOwnProperty(structure)) { - auto existingAtomString = asString(subscript)->toExistingAtomString(globalObject); - RETURN_IF_EXCEPTION(scope, JSValue()); - if (!existingAtomString.isNull()) { - if (JSValue result = baseValue.asCell()->fastGetOwnProperty(vm, structure, existingAtomString.impl())) { + if (subscript.isString()) { + auto propertyName = asString(subscript)->toAtomString(globalObject); + RETURN_IF_EXCEPTION(scope, JSValue()); + + if (LIKELY(baseValue.isCell())) { + Structure& structure = *baseValue.asCell()->structure(); + if (JSCell::canUseFastGetOwnProperty(structure)) { + if (JSValue result = baseValue.asCell()->fastGetOwnProperty(vm, structure, propertyName.data)) { ASSERT(callFrame->bytecodeIndex() != BytecodeIndex(0)); return result; } } + + RELEASE_AND_RETURN(scope, baseValue.get(globalObject, propertyName.data)); } - } - if (std::optional index = subscript.tryGetAsUint32Index()) { + ASSERT(callFrame->bytecodeIndex() != BytecodeIndex(0)); + RELEASE_AND_RETURN(scope, baseValue.get(globalObject, propertyName.data)); + } else if (std::optional index = subscript.tryGetAsUint32Index()) { uint32_t i = *index; if (isJSString(baseValue)) { if (asString(baseValue)->canGetIndex(i)) @@ -3396,11 +3407,11 @@ ALWAYS_INLINE static JSValue getByVal(JSGlobalObject* globalObject, CallFrame* c baseValue.requireObjectCoercible(globalObject); RETURN_IF_EXCEPTION(scope, JSValue()); - auto property = subscript.toPropertyKey(globalObject); + auto propertyKey = subscript.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, JSValue()); ASSERT(callFrame->bytecodeIndex() != BytecodeIndex(0)); - RELEASE_AND_RETURN(scope, baseValue.get(globalObject, property)); + RELEASE_AND_RETURN(scope, baseValue.get(globalObject, propertyKey)); } JSC_DEFINE_JIT_OPERATION(operationGetByValGaveUp, EncodedJSValue, (EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, StructureStubInfo* stubInfo, ArrayProfile* profile)) @@ -3441,19 +3452,20 @@ JSC_DEFINE_JIT_OPERATION(operationGetByValOptimize, EncodedJSValue, (EncodedJSVa } } - if (baseValue.isCell() && CacheableIdentifier::isCacheableIdentifierCell(subscript)) { - const Identifier propertyName = subscript.toPropertyKey(globalObject); - OPERATION_RETURN_IF_EXCEPTION(scope, encodedJSValue()); - if (subscript.isSymbol() || !parseIndex(propertyName)) { - scope.release(); - OPERATION_RETURN(scope, JSValue::encode(baseValue.getPropertySlot(globalObject, propertyName, [&] (bool found, PropertySlot& slot) -> JSValue { - LOG_IC((vm, ICEvent::OperationGetByValOptimize, baseValue.classInfoOrNull(), propertyName, baseValue == slot.slotBase())); - - CacheableIdentifier identifier = CacheableIdentifier::createFromCell(subscript.asCell()); - if (stubInfo->considerRepatchingCacheBy(vm, codeBlock, baseValue.structureOrNull(), identifier)) - repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::ByVal); - return found ? slot.getValue(globalObject, propertyName) : jsUndefined(); - }))); + if (baseValue.isCell()) { + if (auto propertyName = CacheableIdentifier::getCacheableIdentifier(subscript)) { + OPERATION_RETURN_IF_EXCEPTION(scope, encodedJSValue()); + if (subscript.isSymbol() || !parseIndex(*propertyName.data)) { + scope.release(); + OPERATION_RETURN(scope, JSValue::encode(baseValue.getPropertySlot(globalObject, propertyName.data, [&] (bool found, PropertySlot& slot) -> JSValue { + LOG_IC((vm, ICEvent::OperationGetByValOptimize, baseValue.classInfoOrNull(), propertyName.data, baseValue == slot.slotBase())); + + CacheableIdentifier identifier = CacheableIdentifier::createFromCell(subscript.asCell()); + if (stubInfo->considerRepatchingCacheBy(vm, codeBlock, baseValue.structureOrNull(), identifier)) + repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::ByVal); + return found ? slot.getValue(globalObject, propertyName.data) : jsUndefined(); + }))); + } } } @@ -3466,22 +3478,25 @@ ALWAYS_INLINE static JSValue getByValWithThis(JSGlobalObject* globalObject, Call VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - if (LIKELY(baseValue.isCell() && subscript.isString())) { - Structure& structure = *baseValue.asCell()->structure(); - if (JSCell::canUseFastGetOwnProperty(structure)) { - auto existingAtomString = asString(subscript)->toExistingAtomString(globalObject); - RETURN_IF_EXCEPTION(scope, { }); - if (!existingAtomString.isNull()) { - if (JSValue result = baseValue.asCell()->fastGetOwnProperty(vm, structure, existingAtomString.impl())) { + PropertySlot slot(thisValue, PropertySlot::PropertySlot::InternalMethodType::Get); + + if (subscript.isString()) { + auto propertyName = asString(subscript)->toAtomString(globalObject); + RETURN_IF_EXCEPTION(scope, { }); + + if (LIKELY(baseValue.isCell())) { + Structure& structure = *baseValue.asCell()->structure(); + if (JSCell::canUseFastGetOwnProperty(structure)) { + if (JSValue result = baseValue.asCell()->fastGetOwnProperty(vm, structure, propertyName.data)) { ASSERT(callFrame->bytecodeIndex() != BytecodeIndex(0)); return result; } } } - } - PropertySlot slot(thisValue, PropertySlot::PropertySlot::InternalMethodType::Get); - if (std::optional index = subscript.tryGetAsUint32Index()) { + ASSERT(callFrame->bytecodeIndex() != BytecodeIndex(0)); + RELEASE_AND_RETURN(scope, baseValue.get(globalObject, propertyName.data, slot)); + } else if (std::optional index = subscript.tryGetAsUint32Index()) { uint32_t i = *index; if (isJSString(baseValue)) { if (asString(baseValue)->canGetIndex(i)) @@ -3523,9 +3538,9 @@ ALWAYS_INLINE static JSValue getByValWithThis(JSGlobalObject* globalObject, Call baseValue.requireObjectCoercible(globalObject); RETURN_IF_EXCEPTION(scope, { }); - auto property = subscript.toPropertyKey(globalObject); + auto propertyKey = subscript.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, { }); - RELEASE_AND_RETURN(scope, baseValue.get(globalObject, property, slot)); + RELEASE_AND_RETURN(scope, baseValue.get(globalObject, propertyKey, slot)); } static ALWAYS_INLINE JSValue getByValMegamorphic(JSGlobalObject* globalObject, VM& vm, CallFrame* callFrame, StructureStubInfo* stubInfo, ArrayProfile* profile, JSValue baseValue, JSValue thisValue, JSValue subscript, GetByKind kind) @@ -3540,10 +3555,18 @@ static ALWAYS_INLINE JSValue getByValMegamorphic(JSGlobalObject* globalObject, V RELEASE_AND_RETURN(scope, getByValWithThis(globalObject, callFrame, profile, baseValue, subscript, thisValue)); } - Identifier propertyName = subscript.toPropertyKey(globalObject); + GCOwnedDataScope propertyName; + Identifier propertyKey; + UniquedStringImpl* uid = nullptr; + if (subscript.isString()) { + propertyName = asString(subscript)->toAtomString(globalObject); + uid = propertyName.data; + } else { + propertyKey = subscript.toPropertyKey(globalObject); + uid = propertyKey.impl(); + } RETURN_IF_EXCEPTION(scope, { }); - UniquedStringImpl* uid = propertyName.impl(); if (UNLIKELY(!canUseMegamorphicGetById(vm, uid))) { if (stubInfo && stubInfo->considerRepatchingCacheMegamorphic(vm)) repatchGetBySlowPathCall(callFrame->codeBlock(), *stubInfo, kind); @@ -3632,7 +3655,7 @@ JSC_DEFINE_JIT_OPERATION(operationGetByValMegamorphicGeneric, EncodedJSValue, (J OPERATION_RETURN(scope, JSValue::encode(getByValMegamorphic(globalObject, vm, callFrame, nullptr, nullptr, baseValue, baseValue, JSValue::decode(encodedSubscript), GetByKind::ByVal))); } -JSC_DEFINE_JIT_OPERATION(operationGetByValGeneric, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedBase, EncodedJSValue encodedProperty)) +JSC_DEFINE_JIT_OPERATION(operationGetByValGeneric, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); @@ -3640,32 +3663,9 @@ JSC_DEFINE_JIT_OPERATION(operationGetByValGeneric, EncodedJSValue, (JSGlobalObje auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(encodedBase); - JSValue property = JSValue::decode(encodedProperty); - - if (LIKELY(baseValue.isCell())) { - JSCell* base = baseValue.asCell(); - - if (std::optional index = property.tryGetAsUint32Index()) - OPERATION_RETURN(scope, getByValWithIndex(globalObject, base, *index)); - - if (property.isString()) { - Structure& structure = *base->structure(); - if (JSCell::canUseFastGetOwnProperty(structure)) { - auto existingAtomString = asString(property)->toExistingAtomString(globalObject); - OPERATION_RETURN_IF_EXCEPTION(scope, encodedJSValue()); - if (!existingAtomString.isNull()) { - if (JSValue result = base->fastGetOwnProperty(vm, structure, existingAtomString.impl())) - OPERATION_RETURN(scope, JSValue::encode(result)); - } - } - } - } + JSValue subscript = JSValue::decode(encodedSubscript); - baseValue.requireObjectCoercible(globalObject); - OPERATION_RETURN_IF_EXCEPTION(scope, encodedJSValue()); - auto propertyName = property.toPropertyKey(globalObject); - OPERATION_RETURN_IF_EXCEPTION(scope, encodedJSValue()); - OPERATION_RETURN(scope, JSValue::encode(baseValue.get(globalObject, propertyName))); + OPERATION_RETURN(scope, JSValue::encode(getByVal(globalObject, callFrame, nullptr, baseValue, subscript))); } JSC_DEFINE_JIT_OPERATION(operationGetByValWithThisGaveUp, EncodedJSValue, (EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, EncodedJSValue encodedThis, StructureStubInfo* stubInfo, ArrayProfile* profile)) @@ -3750,8 +3750,8 @@ JSC_DEFINE_JIT_OPERATION(operationGetByValWithThisGeneric, EncodedJSValue, (JSGl if (JSCell::canUseFastGetOwnProperty(structure)) { auto existingAtomString = asString(property)->toExistingAtomString(globalObject); OPERATION_RETURN_IF_EXCEPTION(scope, encodedJSValue()); - if (!existingAtomString.isNull()) { - if (JSValue result = base->fastGetOwnProperty(vm, structure, existingAtomString.impl())) + if (existingAtomString) { + if (JSValue result = base->fastGetOwnProperty(vm, structure, existingAtomString.data)) OPERATION_RETURN(scope, JSValue::encode(result)); } } diff --git a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp index 4028d0e58e309..da635a6521202 100644 --- a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp +++ b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp @@ -1197,8 +1197,8 @@ static ALWAYS_INLINE JSValue getByVal(VM& vm, JSGlobalObject* globalObject, Code if (JSCell::canUseFastGetOwnProperty(structure)) { auto existingAtomString = asString(subscript)->toExistingAtomString(globalObject); RETURN_IF_EXCEPTION(scope, JSValue()); - if (!existingAtomString.isNull()) { - if (JSValue result = baseValue.asCell()->fastGetOwnProperty(vm, structure, existingAtomString.impl())) + if (existingAtomString) { + if (JSValue result = baseValue.asCell()->fastGetOwnProperty(vm, structure, existingAtomString.data)) return result; } } diff --git a/Source/JavaScriptCore/parser/Parser.cpp b/Source/JavaScriptCore/parser/Parser.cpp index 11016067e29cf..90944abb0c997 100644 --- a/Source/JavaScriptCore/parser/Parser.cpp +++ b/Source/JavaScriptCore/parser/Parser.cpp @@ -315,7 +315,7 @@ Expected::ParseInnerResult, String> Parser VariableEnvironment& varDeclarations = scope->declaredVariables(); for (auto& entry : capturedVariables) - varDeclarations.markVariableAsCaptured(entry); + varDeclarations.markVariableAsCaptured(entry.get()); scope->finalizeLexicalEnvironment(); if (isGeneratorWrapperParseMode(parseMode) || isAsyncFunctionOrAsyncGeneratorWrapperParseMode(parseMode)) { @@ -503,13 +503,13 @@ template TreeSourceElements Parser::parseModuleSo for (const auto& pair : m_moduleScopeData->exportedBindings()) { const auto& uid = pair.key; - if (currentScope()->hasDeclaredVariable(uid)) { - currentScope()->declaredVariables().markVariableAsExported(uid); + if (currentScope()->hasDeclaredVariable(uid.get())) { + currentScope()->declaredVariables().markVariableAsExported(uid.get()); continue; } - if (currentScope()->hasLexicallyDeclaredVariable(uid)) { - currentScope()->lexicalVariables().markVariableAsExported(uid); + if (currentScope()->hasLexicallyDeclaredVariable(uid.get())) { + currentScope()->lexicalVariables().markVariableAsExported(uid.get()); continue; } diff --git a/Source/JavaScriptCore/parser/Parser.h b/Source/JavaScriptCore/parser/Parser.h index f33c3b2867231..1cddc2fbf1741 100644 --- a/Source/JavaScriptCore/parser/Parser.h +++ b/Source/JavaScriptCore/parser/Parser.h @@ -484,9 +484,9 @@ struct Scope { return hasDeclaredVariable(ident.impl()); } - bool hasDeclaredVariable(const RefPtr& ident) + bool hasDeclaredVariable(const UniquedStringImpl* ident) { - auto iter = m_declaredVariables.find(ident.get()); + auto iter = m_declaredVariables.find(ident); if (iter == m_declaredVariables.end()) return false; VariableEnvironmentEntry entry = iter->value; @@ -498,9 +498,9 @@ struct Scope { return hasLexicallyDeclaredVariable(ident.impl()); } - bool hasLexicallyDeclaredVariable(const RefPtr& ident) const + bool hasLexicallyDeclaredVariable(const UniquedStringImpl* ident) const { - return m_lexicalVariables.contains(ident.get()); + return m_lexicalVariables.contains(ident); } bool hasPrivateName(const Identifier& ident) @@ -569,9 +569,9 @@ struct Scope { return hasDeclaredParameter(ident.impl()); } - bool hasDeclaredParameter(const RefPtr& ident) + bool hasDeclaredParameter(UniquedStringImpl* ident) { - return m_declaredParameters.contains(ident.get()) || hasDeclaredVariable(ident); + return m_declaredParameters.contains(ident) || hasDeclaredVariable(ident); } void preventAllVariableDeclarations() diff --git a/Source/JavaScriptCore/parser/VariableEnvironment.cpp b/Source/JavaScriptCore/parser/VariableEnvironment.cpp index 35a458f2a95eb..9c1f96140f4cd 100644 --- a/Source/JavaScriptCore/parser/VariableEnvironment.cpp +++ b/Source/JavaScriptCore/parser/VariableEnvironment.cpp @@ -47,14 +47,14 @@ VariableEnvironment& VariableEnvironment::operator=(const VariableEnvironment& o return *this; } -void VariableEnvironment::markVariableAsCapturedIfDefined(const RefPtr& identifier) +void VariableEnvironment::markVariableAsCapturedIfDefined(const UniquedStringImpl* identifier) { auto findResult = m_map.find(identifier); if (findResult != m_map.end()) findResult->value.setIsCaptured(); } -void VariableEnvironment::markVariableAsCaptured(const RefPtr& identifier) +void VariableEnvironment::markVariableAsCaptured(const UniquedStringImpl* identifier) { auto findResult = m_map.find(identifier); RELEASE_ASSERT(findResult != m_map.end()); @@ -101,14 +101,14 @@ void VariableEnvironment::swap(VariableEnvironment& other) m_rareData.swap(other.m_rareData); } -void VariableEnvironment::markVariableAsImported(const RefPtr& identifier) +void VariableEnvironment::markVariableAsImported(const UniquedStringImpl* identifier) { auto findResult = m_map.find(identifier); RELEASE_ASSERT(findResult != m_map.end()); findResult->value.setIsImported(); } -void VariableEnvironment::markVariableAsExported(const RefPtr& identifier) +void VariableEnvironment::markVariableAsExported(const UniquedStringImpl* identifier) { auto findResult = m_map.find(identifier); RELEASE_ASSERT(findResult != m_map.end()); diff --git a/Source/JavaScriptCore/parser/VariableEnvironment.h b/Source/JavaScriptCore/parser/VariableEnvironment.h index 36daf112e5196..efcedc0396e7b 100644 --- a/Source/JavaScriptCore/parser/VariableEnvironment.h +++ b/Source/JavaScriptCore/parser/VariableEnvironment.h @@ -180,18 +180,18 @@ class VariableEnvironment { ALWAYS_INLINE unsigned size() const { return m_map.size() + privateNamesSize(); } ALWAYS_INLINE unsigned mapSize() const { return m_map.size(); } - ALWAYS_INLINE bool contains(const RefPtr& identifier) const { return m_map.contains(identifier); } - ALWAYS_INLINE bool remove(const RefPtr& identifier) { return m_map.remove(identifier); } - ALWAYS_INLINE Map::iterator find(const RefPtr& identifier) { return m_map.find(identifier); } - ALWAYS_INLINE Map::const_iterator find(const RefPtr& identifier) const { return m_map.find(identifier); } + ALWAYS_INLINE bool contains(const UniquedStringImpl* identifier) const { return m_map.contains(identifier); } + ALWAYS_INLINE bool remove(const UniquedStringImpl* identifier) { return m_map.remove(identifier); } + ALWAYS_INLINE Map::iterator find(const UniquedStringImpl* identifier) { return m_map.find(identifier); } + ALWAYS_INLINE Map::const_iterator find(const UniquedStringImpl* identifier) const { return m_map.find(identifier); } void swap(VariableEnvironment& other); - void markVariableAsCapturedIfDefined(const RefPtr& identifier); - void markVariableAsCaptured(const RefPtr& identifier); + void markVariableAsCapturedIfDefined(const UniquedStringImpl* identifier); + void markVariableAsCaptured(const UniquedStringImpl* identifier); void markAllVariablesAsCaptured(); bool hasCapturedVariables() const; bool captures(UniquedStringImpl* identifier) const; - void markVariableAsImported(const RefPtr& identifier); - void markVariableAsExported(const RefPtr& identifier); + void markVariableAsImported(const UniquedStringImpl* identifier); + void markVariableAsExported(const UniquedStringImpl* identifier); bool isEverythingCaptured() const { return m_isEverythingCaptured; } bool isEmpty() const { return !m_map.size() && !privateNamesSize(); } diff --git a/Source/JavaScriptCore/runtime/ArgList.h b/Source/JavaScriptCore/runtime/ArgList.h index 5ef40ab698a50..4fa7dec56eecf 100644 --- a/Source/JavaScriptCore/runtime/ArgList.h +++ b/Source/JavaScriptCore/runtime/ArgList.h @@ -139,6 +139,9 @@ class MarkedVector : public OverflowHandler, public MarkedVectorBase { } } + EncodedJSValue* begin() { return m_buffer; } + EncodedJSValue* end() { return m_buffer + m_size; } + auto at(unsigned i) const -> decltype(auto) { if constexpr (std::is_same_v) { diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp index b6b5404c8949d..087ca16586b60 100644 --- a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -271,6 +271,12 @@ inline JSValue fastJoin(JSGlobalObject* globalObject, JSObject* thisObject, Stri break; auto data = butterfly.contiguous().data(); bool holesKnownToBeOK = false; + + JSOnlyStringsJoiner onlyStringsJoiner(separator); + if (auto joined = onlyStringsJoiner.tryJoin(globalObject, data, length)) + RELEASE_AND_RETURN(scope, joined); + RETURN_IF_EXCEPTION(scope, { }); + for (; i < length; ++i) { if (JSValue value = data[i].get()) { if (!joiner.appendWithoutSideEffects(globalObject, value)) diff --git a/Source/JavaScriptCore/runtime/CacheableIdentifier.h b/Source/JavaScriptCore/runtime/CacheableIdentifier.h index 2e7ca882d0227..f954c146635df 100644 --- a/Source/JavaScriptCore/runtime/CacheableIdentifier.h +++ b/Source/JavaScriptCore/runtime/CacheableIdentifier.h @@ -25,6 +25,7 @@ #pragma once +#include "GCOwnedDataScope.h" #include "JSCJSValue.h" #include @@ -79,6 +80,9 @@ class CacheableIdentifier { static inline bool isCacheableIdentifierCell(JSCell*); static inline bool isCacheableIdentifierCell(JSValue); + static inline GCOwnedDataScope getCacheableIdentifier(JSCell*); + static inline GCOwnedDataScope getCacheableIdentifier(JSValue); + uintptr_t rawBits() const { return m_bits; } template inline void visitAggregate(Visitor&) const; diff --git a/Source/JavaScriptCore/runtime/CacheableIdentifierInlines.h b/Source/JavaScriptCore/runtime/CacheableIdentifierInlines.h index dab69fc3b6017..084976852d5d3 100644 --- a/Source/JavaScriptCore/runtime/CacheableIdentifierInlines.h +++ b/Source/JavaScriptCore/runtime/CacheableIdentifierInlines.h @@ -112,6 +112,25 @@ inline bool CacheableIdentifier::isCacheableIdentifierCell(JSValue value) return isCacheableIdentifierCell(value.asCell()); } +inline GCOwnedDataScope CacheableIdentifier::getCacheableIdentifier(JSCell* cell) +{ + if (cell->isSymbol()) + return { cell, &asSymbol(cell)->uid() }; + if (!cell->isString()) + return { }; + JSString* string = jsCast(cell); + if (const StringImpl* impl = string->tryGetValueImpl(); impl && impl->isAtom()) + return { cell, static_cast(impl) }; + return { }; +} + +inline GCOwnedDataScope CacheableIdentifier::getCacheableIdentifier(JSValue value) +{ + if (!value.isCell()) + return { }; + return getCacheableIdentifier(value.asCell()); +} + inline bool CacheableIdentifier::isSymbolCell() const { return isCell() && cell()->isSymbol(); diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp index aef0afe52c1c2..55572979f1661 100644 --- a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp +++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp @@ -1164,8 +1164,8 @@ JSC_DEFINE_COMMON_SLOW_PATH(slow_path_get_by_val_with_this) if (JSCell::canUseFastGetOwnProperty(structure)) { auto existingAtomString = asString(subscript)->toExistingAtomString(globalObject); CHECK_EXCEPTION(); - if (!existingAtomString.isNull()) { - if (JSValue result = baseValue.asCell()->fastGetOwnProperty(vm, structure, existingAtomString.impl())) + if (existingAtomString) { + if (JSValue result = baseValue.asCell()->fastGetOwnProperty(vm, structure, existingAtomString.data)) RETURN_PROFILED(result); } } diff --git a/Source/JavaScriptCore/runtime/EnsureStillAliveHere.h b/Source/JavaScriptCore/runtime/EnsureStillAliveHere.h index aef213a271603..1cd556bdf931d 100644 --- a/Source/JavaScriptCore/runtime/EnsureStillAliveHere.h +++ b/Source/JavaScriptCore/runtime/EnsureStillAliveHere.h @@ -25,6 +25,9 @@ #pragma once +#include +#include + namespace JSC { ALWAYS_INLINE void ensureStillAliveHere(uint64_t value) diff --git a/Source/JavaScriptCore/runtime/Identifier.h b/Source/JavaScriptCore/runtime/Identifier.h index 568931a639ef3..6caceda265a3c 100644 --- a/Source/JavaScriptCore/runtime/Identifier.h +++ b/Source/JavaScriptCore/runtime/Identifier.h @@ -79,7 +79,7 @@ ALWAYS_INLINE std::optional parseIndex(std::span chara return value; } -ALWAYS_INLINE std::optional parseIndex(StringImpl& impl) +ALWAYS_INLINE std::optional parseIndex(const StringImpl& impl) { return impl.is8Bit() ? parseIndex(impl.span8()) : parseIndex(impl.span16()); } @@ -93,7 +93,8 @@ class Identifier { const AtomString& string() const { return m_string; } - UniquedStringImpl* impl() const { return static_cast(m_string.impl()); } + UniquedStringImpl* impl() const { return m_string.impl(); } + RefPtr releaseImpl() { return m_string.releaseImpl(); } int length() const { return m_string.length(); } diff --git a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp index eeb703b22585f..018d19d8f09f4 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp @@ -936,8 +936,10 @@ JSC_DEFINE_HOST_FUNCTION(globalFuncCopyDataProperties, (JSGlobalObject* globalOb RETURN_IF_EXCEPTION(scope, { }); } - if (LIKELY(canPerformFastPropertyEnumerationForCopyDataProperties(source->structure()))) { - Vector, 8> properties; + auto sourceStructure = source->structure(); + if (LIKELY(canPerformFastPropertyEnumerationForCopyDataProperties(sourceStructure))) { + EnsureStillAliveScope sourceStructureScope(sourceStructure); + Vector properties; // sourceStructure ensures the lifetimes of these strings. MarkedArgumentBuffer values; // FIXME: It doesn't seem like we should have to do this in two phases, but @@ -947,7 +949,7 @@ JSC_DEFINE_HOST_FUNCTION(globalFuncCopyDataProperties, (JSGlobalObject* globalOb // that ends up transitioning the structure underneath us. // https://bugs.webkit.org/show_bug.cgi?id=187837 - source->structure()->forEachProperty(vm, [&](const PropertyTableEntry& entry) ALWAYS_INLINE_LAMBDA { + sourceStructure->forEachProperty(vm, [&](const PropertyTableEntry& entry) ALWAYS_INLINE_LAMBDA { PropertyName propertyName(entry.key()); if (propertyName.isPrivateName()) return true; @@ -971,8 +973,9 @@ JSC_DEFINE_HOST_FUNCTION(globalFuncCopyDataProperties, (JSGlobalObject* globalOb target->putOwnDataPropertyBatching(vm, properties.data(), values.data(), properties.size()); else { for (size_t i = 0; i < properties.size(); ++i) - target->putDirect(vm, properties[i].get(), values.at(i)); + target->putDirect(vm, properties[i], values.at(i)); } + return JSValue::encode(target); } @@ -1033,7 +1036,8 @@ JSC_DEFINE_HOST_FUNCTION(globalFuncCloneObject, (JSGlobalObject* globalObject, C RETURN_IF_EXCEPTION(scope, { }); if (LIKELY(canPerformFastPropertyEnumerationForCopyDataProperties(sourceStructure))) { - Vector, 8> properties; + EnsureStillAliveScope sourceStructureScope(sourceStructure); + Vector properties; // sourceStructure ensures the lifetimes of these strings. MarkedArgumentBuffer values; // FIXME: It doesn't seem like we should have to do this in two phases, but @@ -1058,6 +1062,7 @@ JSC_DEFINE_HOST_FUNCTION(globalFuncCloneObject, (JSGlobalObject* globalObject, C RETURN_IF_EXCEPTION(scope, { }); target->putOwnDataPropertyBatching(vm, properties.data(), values.data(), properties.size()); + return JSValue::encode(target); } diff --git a/Source/JavaScriptCore/runtime/JSONAtomStringCache.h b/Source/JavaScriptCore/runtime/JSONAtomStringCache.h index f6f0c5e5f5552..6e219c899eb0a 100644 --- a/Source/JavaScriptCore/runtime/JSONAtomStringCache.h +++ b/Source/JavaScriptCore/runtime/JSONAtomStringCache.h @@ -46,10 +46,10 @@ class JSONAtomStringCache { using Cache = std::array; template - ALWAYS_INLINE Ref makeIdentifier(std::span characters) - { - return make(characters); - } + ALWAYS_INLINE Ref makeIdentifier(std::span characters); + + template + ALWAYS_INLINE AtomStringImpl* existingIdentifier(std::span characters); ALWAYS_INLINE void clear() { @@ -59,9 +59,6 @@ class JSONAtomStringCache { VM& vm() const; private: - template - Ref make(std::span); - ALWAYS_INLINE Slot& cacheSlot(UChar firstCharacter, UChar lastCharacter, UChar length) { unsigned hash = (firstCharacter << 6) ^ ((lastCharacter << 14) ^ firstCharacter); diff --git a/Source/JavaScriptCore/runtime/JSONAtomStringCacheInlines.h b/Source/JavaScriptCore/runtime/JSONAtomStringCacheInlines.h index 919ec73456cac..875d01e3d0f41 100644 --- a/Source/JavaScriptCore/runtime/JSONAtomStringCacheInlines.h +++ b/Source/JavaScriptCore/runtime/JSONAtomStringCacheInlines.h @@ -32,12 +32,11 @@ namespace JSC { -// FIXME: This should take in a std::span. template -ALWAYS_INLINE Ref JSONAtomStringCache::make(std::span characters) +ALWAYS_INLINE Ref JSONAtomStringCache::makeIdentifier(std::span characters) { if (characters.empty()) - return *static_cast(StringImpl::empty()); + return *emptyAtom().impl(); auto firstCharacter = characters.front(); if (characters.size() == 1) { @@ -59,6 +58,27 @@ ALWAYS_INLINE Ref JSONAtomStringCache::make(std::span +ALWAYS_INLINE AtomStringImpl* JSONAtomStringCache::existingIdentifier(std::span characters) +{ + if (characters.empty()) + return emptyAtom().impl(); + + auto firstCharacter = characters.front(); + if (characters.size() == 1) { + if (firstCharacter <= maxSingleCharacterString) + return vm().smallStrings.existingSingleCharacterStringRep(firstCharacter); + } else if (UNLIKELY(characters.size() > maxStringLengthForCache)) + return nullptr; + + auto lastCharacter = characters.back(); + auto& slot = cacheSlot(firstCharacter, lastCharacter, characters.size()); + if (UNLIKELY(slot.m_length != characters.size() || !equal(slot.m_buffer, characters))) + return nullptr; + + return slot.m_impl.get(); +} + ALWAYS_INLINE VM& JSONAtomStringCache::vm() const { return *std::bit_cast(std::bit_cast(this) - OBJECT_OFFSETOF(VM, jsonAtomStringCache)); diff --git a/Source/JavaScriptCore/runtime/JSONObject.cpp b/Source/JavaScriptCore/runtime/JSONObject.cpp index cf9d46506a1db..de95971295482 100644 --- a/Source/JavaScriptCore/runtime/JSONObject.cpp +++ b/Source/JavaScriptCore/runtime/JSONObject.cpp @@ -220,8 +220,7 @@ inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(PropertyName pro } inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(unsigned number) - : m_propertyName(nullptr) - , m_number(number) + : m_number(number) { } @@ -604,7 +603,7 @@ bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, StringBui stringifyResult = stringifier.appendStringifiedValue(builder, value, *this, index); ASSERT(stringifyResult != StringifyFailedDueToUndefinedOrSymbolValue); } else { - PropertyName propertyName { nullptr }; + PropertyName propertyName; JSValue value; if (m_hasFastObjectProperties) { propertyName = std::get<0>(m_propertiesAndOffsets[index]); diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp index c60f2ac282be6..de918dc916388 100644 --- a/Source/JavaScriptCore/runtime/JSObject.cpp +++ b/Source/JavaScriptCore/runtime/JSObject.cpp @@ -4170,13 +4170,13 @@ TransitionKind JSObject::suggestedArrayStorageTransition() const return TransitionKind::AllocateArrayStorage; } -void JSObject::putOwnDataPropertyBatching(VM& vm, const RefPtr* properties, const EncodedJSValue* values, unsigned size) +void JSObject::putOwnDataPropertyBatching(VM& vm, UniquedStringImpl** properties, const EncodedJSValue* values, unsigned size) { unsigned i = 0; Structure* structure = this->structure(); if (!(structure->isDictionary() || (structure->transitionCountEstimate() + size) > Structure::s_maxTransitionLength || !structure->canPerformFastPropertyEnumerationCommon())) { Vector offsets(size, [&](size_t index) -> std::optional { - PropertyName propertyName(properties[index].get()); + PropertyName propertyName(properties[index]); PropertyOffset offset; if (Structure* newStructure = Structure::addPropertyTransitionToExistingStructure(structure, propertyName, 0, offset)) { @@ -4233,7 +4233,7 @@ void JSObject::putOwnDataPropertyBatching(VM& vm, const RefPtr(vm, propertyName, value, 0, slot); -} - } // namespace JSC WTF_ALLOW_UNSAFE_BUFFER_USAGE_END diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h index c0d871d8a3c08..1305004f48663 100644 --- a/Source/JavaScriptCore/runtime/JSObject.h +++ b/Source/JavaScriptCore/runtime/JSObject.h @@ -105,7 +105,6 @@ class JSObject : public JSCell { enum PutMode : uint8_t { PutModePut, PutModeDefineOwnProperty, - PutModeDefineOwnPropertyForJSONSlow, }; public: @@ -738,7 +737,6 @@ class JSObject : public JSCell { bool putDirect(VM&, PropertyName, JSValue, unsigned attributes = 0); bool putDirect(VM&, PropertyName, JSValue, unsigned attributes, PutPropertySlot&); bool putDirect(VM&, PropertyName, JSValue, PutPropertySlot&); - void putDirectForJSONSlow(VM&, PropertyName, JSValue); void putDirectWithoutTransition(VM&, PropertyName, JSValue, unsigned attributes = 0); bool putDirectNonIndexAccessor(VM&, PropertyName, GetterSetter*, unsigned attributes); void putDirectNonIndexAccessorWithoutTransition(VM&, PropertyName, GetterSetter*, unsigned attributes); @@ -878,7 +876,7 @@ class JSObject : public JSCell { bool putOwnDataProperty(VM&, PropertyName, JSValue, PutPropertySlot&); bool putOwnDataPropertyMayBeIndex(JSGlobalObject*, PropertyName, JSValue, PutPropertySlot&); - void putOwnDataPropertyBatching(VM&, const RefPtr*, const EncodedJSValue*, unsigned size); + void putOwnDataPropertyBatching(VM&, UniquedStringImpl**, const EncodedJSValue*, unsigned size); private: void validatePutOwnDataProperty(VM&, PropertyName, JSValue); public: diff --git a/Source/JavaScriptCore/runtime/JSObjectInlines.h b/Source/JavaScriptCore/runtime/JSObjectInlines.h index 9c105350a8f3b..6e4be487e9584 100644 --- a/Source/JavaScriptCore/runtime/JSObjectInlines.h +++ b/Source/JavaScriptCore/runtime/JSObjectInlines.h @@ -408,7 +408,7 @@ ALWAYS_INLINE ASCIILiteral JSObject::putDirectInternal(VM& vm, PropertyName prop // FIXME: Check attributes against PropertyAttribute::CustomAccessorOrValue. Changing GetterSetter should work w/o transition. // https://bugs.webkit.org/show_bug.cgi?id=214342 - if ((mode == PutModeDefineOwnProperty || mode == PutModeDefineOwnPropertyForJSONSlow) && (newAttributes != attributes || (newAttributes & PropertyAttribute::AccessorOrCustomAccessorOrValue))) { + if ((mode == PutModeDefineOwnProperty) && (newAttributes != attributes || (newAttributes & PropertyAttribute::AccessorOrCustomAccessorOrValue))) { DeferredStructureTransitionWatchpointFire deferred(vm, structure); setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, newAttributes, &deferred)); if (UNLIKELY(mayBePrototype())) @@ -430,8 +430,7 @@ ALWAYS_INLINE ASCIILiteral JSObject::putDirectInternal(VM& vm, PropertyName prop return { }; } - // In PutModeDefineOwnPropertyForJSONSlow, this is already checked. - if constexpr (mode != PutModeDefineOwnPropertyForJSONSlow) { + { PropertyOffset offset; Structure* newStructure = Structure::addPropertyTransitionToExistingStructure(structure, propertyName, newAttributes, offset); if (newStructure) { @@ -468,7 +467,7 @@ ALWAYS_INLINE ASCIILiteral JSObject::putDirectInternal(VM& vm, PropertyName prop // FIXME: Check attributes against PropertyAttribute::CustomAccessorOrValue. Changing GetterSetter should work w/o transition. // https://bugs.webkit.org/show_bug.cgi?id=214342 - if ((mode == PutModeDefineOwnProperty || mode == PutModeDefineOwnPropertyForJSONSlow) && (newAttributes != currentAttributes || (newAttributes & PropertyAttribute::AccessorOrCustomAccessorOrValue))) { + if ((mode == PutModeDefineOwnProperty) && (newAttributes != currentAttributes || (newAttributes & PropertyAttribute::AccessorOrCustomAccessorOrValue))) { // We want the structure transition watchpoint to fire after this object has switched structure. // This allows adaptive watchpoints to observe if the new structure is the one we want. DeferredStructureTransitionWatchpointFire deferredWatchpointFire(vm, structure); diff --git a/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.cpp b/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.cpp index 10ae36ad465ef..1dd13029b82f4 100644 --- a/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.cpp +++ b/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.cpp @@ -181,7 +181,7 @@ JSString* JSPropertyNameEnumerator::computeNext(JSGlobalObject* globalObject, JS scope.assertNoException(); if (index < indexedLength()) - return shouldAllocateIndexedNameString ? jsString(vm, Identifier::from(vm, index).string()) : nullptr; + return shouldAllocateIndexedNameString ? jsString(vm, Identifier::from(vm, index).releaseImpl()) : nullptr; if (!sizeOfPropertyNames()) return nullptr; @@ -202,9 +202,9 @@ JSString* JSPropertyNameEnumerator::computeNext(JSGlobalObject* globalObject, JS break; if (index < endStructurePropertyIndex() && base->structureID() == cachedStructureID()) break; - auto id = name->toIdentifier(globalObject); + auto id = name->toAtomString(globalObject); RETURN_IF_EXCEPTION(scope, nullptr); - if (base->hasEnumerableProperty(globalObject, id)) + if (base->hasEnumerableProperty(globalObject, id.data)) break; RETURN_IF_EXCEPTION(scope, nullptr); name = nullptr; diff --git a/Source/JavaScriptCore/runtime/JSString.cpp b/Source/JavaScriptCore/runtime/JSString.cpp index e76be7f41078b..b9d566f3fa3f7 100644 --- a/Source/JavaScriptCore/runtime/JSString.cpp +++ b/Source/JavaScriptCore/runtime/JSString.cpp @@ -147,14 +147,14 @@ void JSRopeString::resolveRopeInternalNoSubstring(std::span buffe resolveToBuffer(fiber0(), fiber1(), fiber2(), buffer, stackLimit); } -AtomString JSRopeString::resolveRopeToAtomString(JSGlobalObject* globalObject) const +GCOwnedDataScope JSRopeString::resolveRopeToAtomString(JSGlobalObject* globalObject) const { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - auto convertToAtomString = [](const String& string) -> AtomString { + auto convertToAtomString = [this](const String& string) -> GCOwnedDataScope { ASSERT(!string.impl() || string.impl()->isAtom()); - return static_cast(string.impl()); + return { this, static_cast(string.impl()) }; }; if (length() > maxLengthForOnStackResolve) { @@ -181,13 +181,13 @@ AtomString JSRopeString::resolveRopeToAtomString(JSGlobalObject* globalObject) c atomString = StringView { substringBase()->valueInternal() }.substring(substringOffset(), length()).toAtomString(); size_t sizeToReport = atomString.impl()->hasOneRef() ? atomString.impl()->cost() : 0; - convertToNonRope(String { atomString }); + convertToNonRope(String { atomString.releaseImpl() }); // If we resolved a string that didn't previously exist, notify the heap that we've grown. vm.heap.reportExtraMemoryAllocated(this, sizeToReport); - return atomString; + return { this, static_cast(valueInternal().impl()) }; } -RefPtr JSRopeString::resolveRopeToExistingAtomString(JSGlobalObject* globalObject) const +GCOwnedDataScope JSRopeString::resolveRopeToExistingAtomString(JSGlobalObject* globalObject) const { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); @@ -201,8 +201,8 @@ RefPtr JSRopeString::resolveRopeToExistingAtomString(JSGlobalObj return Ref { *existingAtomString }; return WTFMove(newImpl); }); - RETURN_IF_EXCEPTION(scope, nullptr); - return existingAtomString; + RETURN_IF_EXCEPTION(scope, { }); + return { this, existingAtomString.get() }; } RefPtr existingAtomString; @@ -222,7 +222,7 @@ RefPtr JSRopeString::resolveRopeToExistingAtomString(JSGlobalObj if (existingAtomString) convertToNonRope(*existingAtomString); - return existingAtomString; + return { this, existingAtomString.get() }; } template diff --git a/Source/JavaScriptCore/runtime/JSString.h b/Source/JavaScriptCore/runtime/JSString.h index 3e6da6a20a60b..1091081959ee8 100644 --- a/Source/JavaScriptCore/runtime/JSString.h +++ b/Source/JavaScriptCore/runtime/JSString.h @@ -219,8 +219,8 @@ class JSString : public JSCell { public: Identifier toIdentifier(JSGlobalObject*) const; - AtomString toAtomString(JSGlobalObject*) const; - AtomString toExistingAtomString(JSGlobalObject*) const; + GCOwnedDataScope toAtomString(JSGlobalObject*) const; + GCOwnedDataScope toExistingAtomString(JSGlobalObject*) const; GCOwnedDataScope view(JSGlobalObject*) const; @@ -229,8 +229,8 @@ class JSString : public JSCell { GCOwnedDataScope value(JSGlobalObject*) const; inline GCOwnedDataScope tryGetValue(bool allocationAllowed = true) const; GCOwnedDataScope tryGetValueWithoutGC() const; - const StringImpl* getValueImpl() const; - const StringImpl* tryGetValueImpl() const; + StringImpl* getValueImpl() const; + StringImpl* tryGetValueImpl() const; ALWAYS_INLINE unsigned length() const; JSValue toPrimitive(JSGlobalObject*, PreferredPrimitiveType) const; @@ -514,6 +514,8 @@ class JSRopeString final : public JSString { return m_compactFibers.length(); } + inline StringImpl* tryGetLHS(ASCIILiteral rhs) const; + private: friend class LLIntOffsetsExtractor; @@ -651,8 +653,8 @@ class JSRopeString final : public JSString { friend JSValue jsStringFromRegisterArray(JSGlobalObject*, Register*, unsigned); template const String& resolveRopeWithFunction(JSGlobalObject* nullOrGlobalObjectForOOM, Function&&) const; - JS_EXPORT_PRIVATE AtomString resolveRopeToAtomString(JSGlobalObject*) const; - JS_EXPORT_PRIVATE RefPtr resolveRopeToExistingAtomString(JSGlobalObject*) const; + JS_EXPORT_PRIVATE GCOwnedDataScope resolveRopeToAtomString(JSGlobalObject*) const; + JS_EXPORT_PRIVATE GCOwnedDataScope resolveRopeToExistingAtomString(JSGlobalObject*) const; template void resolveRopeInternalNoSubstring(std::span, uint8_t* stackLimit) const; Identifier toIdentifier(JSGlobalObject*) const; void outOfMemory(JSGlobalObject* nullOrGlobalObjectForOOM) const; @@ -765,13 +767,13 @@ ALWAYS_INLINE unsigned JSString::length() const return std::bit_cast(pointer)->length(); } -inline const StringImpl* JSString::getValueImpl() const +inline StringImpl* JSString::getValueImpl() const { ASSERT(!isRope()); return std::bit_cast(m_fiber); } -inline const StringImpl* JSString::tryGetValueImpl() const +inline StringImpl* JSString::tryGetValueImpl() const { uintptr_t pointer = fiberConcurrently(); if (pointer & isRopeInPointer) @@ -826,7 +828,7 @@ ALWAYS_INLINE Identifier JSRopeString::toIdentifier(JSGlobalObject* globalObject auto scope = DECLARE_THROW_SCOPE(vm); auto atomString = static_cast(this)->resolveRopeToAtomString(globalObject); RETURN_IF_EXCEPTION(scope, { }); - return Identifier::fromString(vm, atomString); + return Identifier::fromString(vm, Ref { *atomString }); } ALWAYS_INLINE void JSString::swapToAtomString(VM& vm, RefPtr&& atom) const @@ -862,29 +864,32 @@ ALWAYS_INLINE Identifier JSString::toIdentifier(JSGlobalObject* globalObject) co return Identifier::fromString(vm, Ref { vm.lastAtomizedIdentifierAtomStringImpl }); } -ALWAYS_INLINE AtomString JSString::toAtomString(JSGlobalObject* globalObject) const +ALWAYS_INLINE GCOwnedDataScope JSString::toAtomString(JSGlobalObject* globalObject) const { if constexpr (validateDFGDoesGC) vm().verifyCanGC(); if (isRope()) - return static_cast(this)->resolveRopeToAtomString(globalObject); + return { this, static_cast(this)->resolveRopeToAtomString(globalObject) }; + if (valueInternal().impl()->isAtom()) + return { this, static_cast(valueInternal().impl()) }; AtomString atom(valueInternal()); - // It is possible that AtomString constructor converts existing valueInternal()'s StringImpl to AtomicStringImpl, - // thus we need to recheck atomicity status here. - if (!valueInternal().impl()->isAtom()) - swapToAtomString(getVM(globalObject), RefPtr { atom.impl() }); - return atom; + swapToAtomString(getVM(globalObject), atom.releaseImpl()); + return { this, static_cast(valueInternal().impl()) }; } -ALWAYS_INLINE AtomString JSString::toExistingAtomString(JSGlobalObject* globalObject) const +ALWAYS_INLINE GCOwnedDataScope JSString::toExistingAtomString(JSGlobalObject* globalObject) const { if constexpr (validateDFGDoesGC) vm().verifyCanGC(); if (isRope()) return static_cast(this)->resolveRopeToExistingAtomString(globalObject); if (valueInternal().impl()->isAtom()) - return static_cast(valueInternal().impl()); - return AtomStringImpl::lookUp(valueInternal().impl()); + return { this, static_cast(valueInternal().impl()) }; + if (auto atom = AtomStringImpl::lookUp(valueInternal().impl())) { + swapToAtomString(getVM(globalObject), WTFMove(atom)); + return { this, static_cast(valueInternal().impl()) }; + } + return { }; } inline GCOwnedDataScope JSString::value(JSGlobalObject* globalObject) const diff --git a/Source/JavaScriptCore/runtime/JSStringInlines.h b/Source/JavaScriptCore/runtime/JSStringInlines.h index 4f67502e72474..7f8c338769386 100644 --- a/Source/JavaScriptCore/runtime/JSStringInlines.h +++ b/Source/JavaScriptCore/runtime/JSStringInlines.h @@ -207,6 +207,31 @@ inline void JSRopeString::convertToNonRope(String&& string) const ASSERT(!JSString::isRope()); } +inline StringImpl* JSRopeString::tryGetLHS(ASCIILiteral rhs) const +{ + if (isSubstring()) + return nullptr; + + JSString* fiber2 = this->fiber2(); + if (fiber2) + return nullptr; + + JSString* fiber1 = this->fiber1(); + ASSERT(fiber1); + if (fiber1->isRope()) + return nullptr; + + JSString* fiber0 = this->fiber0(); + ASSERT(fiber0); + if (fiber0->isRope()) + return nullptr; + + if (fiber1->valueInternal() != rhs) + return nullptr; + + return fiber0->valueInternal().impl(); +} + // Overview: These functions convert a JSString from holding a string in rope form // down to a simple String representation. It does so by building up the string // backwards, since we want to avoid recursion, we expect that the tree structure diff --git a/Source/JavaScriptCore/runtime/JSStringJoiner.cpp b/Source/JavaScriptCore/runtime/JSStringJoiner.cpp index a0a84fe3ef8be..b6f1a51de6396 100644 --- a/Source/JavaScriptCore/runtime/JSStringJoiner.cpp +++ b/Source/JavaScriptCore/runtime/JSStringJoiner.cpp @@ -165,6 +165,55 @@ static inline String joinStrings(const JSStringJoiner::Entries& strings, std::sp return result; } +template +static inline String joinStrings(JSGlobalObject* globalObject, const WriteBarrier* strings, unsigned size, std::span separator, unsigned joinedLength) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + ASSERT(joinedLength); + + std::span data; + String result = StringImpl::tryCreateUninitialized(joinedLength, data); + if (UNLIKELY(result.isNull())) { + throwOutOfMemoryError(globalObject, scope); + return { }; + } + + switch (separator.size()) { + case 0: { + for (unsigned i = 0; i < size; ++i) { + JSValue value = strings[i].get(); + auto view = asString(value)->view(globalObject); + RETURN_IF_EXCEPTION(scope, String()); + + appendStringToData(data, view); + } + break; + } + default: { + JSValue value = strings[0].get(); + auto view = asString(value)->view(globalObject); + RETURN_IF_EXCEPTION(scope, String()); + + appendStringToData(data, view); + + for (unsigned i = 1; i < size; ++i) { + JSValue value = strings[i].get(); + auto view = asString(value)->view(globalObject); + RETURN_IF_EXCEPTION(scope, String()); + + appendStringToData(data, separator); + appendStringToData(data, view); + } + break; + } + } + ASSERT(data.data() == result.span().data() + joinedLength); + + return result; +} + inline unsigned JSStringJoiner::joinedLength(JSGlobalObject* globalObject) const { VM& vm = globalObject->vm(); @@ -183,7 +232,7 @@ inline unsigned JSStringJoiner::joinedLength(JSGlobalObject* globalObject) const return totalLength; } -JSValue JSStringJoiner::joinSlow(JSGlobalObject* globalObject) +JSValue JSStringJoiner::joinImpl(JSGlobalObject* globalObject) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); @@ -218,6 +267,37 @@ JSValue JSStringJoiner::joinSlow(JSGlobalObject* globalObject) return jsString(vm, WTFMove(result)); } +JSValue JSOnlyStringsJoiner::joinImpl(JSGlobalObject* globalObject, const WriteBarrier* data, unsigned length) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (!length) + return jsEmptyString(vm); + + CheckedInt32 separatorLength = m_separator.length(); + CheckedInt32 totalSeparatorsLength = separatorLength * (CheckedInt32(length) - 1); + CheckedInt32 totalLength = totalSeparatorsLength + m_accumulatedStringsLength; + if (UNLIKELY(totalLength.hasOverflowed())) { + throwOutOfMemoryError(globalObject, scope); + return { }; + } + + String result; + if (m_isAll8Bit) + result = joinStrings(globalObject, data, length, m_separator.span8(), totalLength); + else { + if (m_separator.is8Bit()) + result = joinStrings(globalObject, data, length, m_separator.span8(), totalLength); + else + result = joinStrings(globalObject, data, length, m_separator.span16(), totalLength); + } + + RETURN_IF_EXCEPTION(scope, { }); + + return jsString(vm, WTFMove(result)); +} + } // namespace JSC WTF_ALLOW_UNSAFE_BUFFER_USAGE_END diff --git a/Source/JavaScriptCore/runtime/JSStringJoiner.h b/Source/JavaScriptCore/runtime/JSStringJoiner.h index d6c5329a5a354..d16390afbb2d4 100644 --- a/Source/JavaScriptCore/runtime/JSStringJoiner.h +++ b/Source/JavaScriptCore/runtime/JSStringJoiner.h @@ -58,7 +58,7 @@ class JSStringJoiner { void append(JSString*, StringViewWithUnderlyingString&&); void append8Bit(const String&); unsigned joinedLength(JSGlobalObject*) const; - JSValue joinSlow(JSGlobalObject*); + JSValue joinImpl(JSGlobalObject*); StringView m_separator; Entries m_strings; @@ -93,7 +93,7 @@ inline JSValue JSStringJoiner::join(JSGlobalObject* globalObject) return m_lastString; return jsString(globalObject->vm(), m_strings[0].m_view.toString()); } - return joinSlow(globalObject); + return joinImpl(globalObject); } ALWAYS_INLINE void JSStringJoiner::append(JSString* jsString, StringViewWithUnderlyingString&& string) @@ -217,4 +217,49 @@ ALWAYS_INLINE void JSStringJoiner::appendNumber(VM& vm, double value) append8Bit(vm.numericStrings.add(value)); } +WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN + +// Avoids the overhead of accumulating intermediate vectors of values when +// we're only joining existing strings. +class JSOnlyStringsJoiner { +public: + JSOnlyStringsJoiner(StringView separator) + : m_separator(separator) + , m_isAll8Bit(m_separator.is8Bit()) + { + } + + JSValue tryJoin(JSGlobalObject* globalObject, const WriteBarrier* data, unsigned length) + { + if (length == 1) { + JSValue value = data[0].get(); + if (!value || !value.isString()) + return JSValue(); + return value; + } + + for (size_t i = 0; i < length; ++i) { + JSValue value = data[i].get(); + if (!value || !value.isString()) + return JSValue(); + + JSString* string = asString(value); + + m_accumulatedStringsLength += string->length(); + m_isAll8Bit &= string->is8Bit(); + } + + return joinImpl(globalObject, data, length); + } + +private: + JSValue joinImpl(JSGlobalObject*, const WriteBarrier*, unsigned); + + StringView m_separator; + CheckedUint32 m_accumulatedStringsLength; + bool m_isAll8Bit { true }; +}; + +WTF_ALLOW_UNSAFE_BUFFER_USAGE_END + } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/LiteralParser.cpp b/Source/JavaScriptCore/runtime/LiteralParser.cpp index 23b0388f8b9eb..f7435230e28bf 100644 --- a/Source/JavaScriptCore/runtime/LiteralParser.cpp +++ b/Source/JavaScriptCore/runtime/LiteralParser.cpp @@ -145,6 +145,28 @@ bool LiteralParser::tryJSONPParse(Vector& resu return m_lexer.currentToken()->type == TokEnd; } +template +ALWAYS_INLINE bool LiteralParser::equalIdentifier(UniquedStringImpl* rep, typename Lexer::LiteralParserTokenPtr token) +{ + if (token->type == TokIdentifier) + return WTF::equal(rep, token->identifier()); + ASSERT(token->type == TokString); + if (token->stringIs8Bit) + return WTF::equal(rep, token->string8()); + return WTF::equal(rep, token->string16()); +} + +template +ALWAYS_INLINE AtomStringImpl* LiteralParser::existingIdentifier(VM& vm, typename Lexer::LiteralParserTokenPtr token) +{ + if (token->type == TokIdentifier) + return vm.jsonAtomStringCache.existingIdentifier(token->identifier()); + ASSERT(token->type == TokString); + if (token->stringIs8Bit) + return vm.jsonAtomStringCache.existingIdentifier(token->string8()); + return vm.jsonAtomStringCache.existingIdentifier(token->string16()); +} + template ALWAYS_INLINE Identifier LiteralParser::makeIdentifier(VM& vm, typename Lexer::LiteralParserTokenPtr token) { @@ -163,11 +185,11 @@ ALWAYS_INLINE JSString* LiteralParser::makeJSString(VM& v if (token->stringIs8Bit) { if (token->stringOrIdentifierLength > maxAtomizeStringLength) return jsNontrivialString(vm, String({ token->stringStart8, token->stringOrIdentifierLength })); - return jsString(vm, Identifier::fromString(vm, token->string8()).string()); + return jsString(vm, Identifier::fromString(vm, token->string8()).releaseImpl()); } if (token->stringOrIdentifierLength > maxAtomizeStringLength) return jsNontrivialString(vm, String({ token->stringStart16, token->stringOrIdentifierLength })); - return jsString(vm, Identifier::fromString(vm, token->string16()).string()); + return jsString(vm, Identifier::fromString(vm, token->string16()).releaseImpl()); } [[maybe_unused]] static ALWAYS_INLINE bool cannotBeIdentPartOrEscapeStart(LChar) @@ -1325,7 +1347,33 @@ JSValue LiteralParser::parseRecursively(VM& vm, uint8_t* type = m_lexer.next(); if (type == TokString) { while (true) { - Identifier ident = makeIdentifier(vm, m_lexer.currentToken()); + struct ExistingProperty { + Structure* structure; + PropertyOffset offset; + }; + + auto* structure = object->structure(); + auto property = [&, &vm = vm] ALWAYS_INLINE_LAMBDA -> std::variant { + if (Structure* transition = structure->trySingleTransition()) { + // This check avoids hash lookup and refcount churn in the common case of a matching single transition. + SUPPRESS_UNCOUNTED_ARG if (transition->transitionKind() == TransitionKind::PropertyAddition + && !transition->transitionPropertyAttributes() + && equalIdentifier(transition->transitionPropertyName(), m_lexer.currentToken()) + && (m_mode == StrictJSON || transition->transitionPropertyName() != vm.propertyNames->underscoreProto)) + return ExistingProperty { transition, transition->transitionOffset() }; + } else if (!structure->isDictionary()) { + // This check avoids refcount churn in the common case of a cached Identifier. + if (SUPPRESS_UNCOUNTED_LOCAL AtomStringImpl* ident = existingIdentifier(vm, m_lexer.currentToken())) { + PropertyOffset offset = 0; + Structure* newStructure = Structure::addPropertyTransitionToExistingStructure(structure, ident, 0, offset); + if (LIKELY(newStructure && (m_mode == StrictJSON || newStructure->transitionPropertyName() != vm.propertyNames->underscoreProto))) + return ExistingProperty { newStructure, offset }; + return Identifier::fromString(vm, ident); + } + } + + return makeIdentifier(vm, m_lexer.currentToken()); + }(); if (UNLIKELY(m_lexer.next() != TokColon)) { setErrorMessageForToken(TokColon); @@ -1342,37 +1390,46 @@ JSValue LiteralParser::parseRecursively(VM& vm, uint8_t* if (UNLIKELY(!value)) return { }; - if (std::optional index = parseIndex(ident)) { - object->putDirectIndex(m_globalObject, index.value(), value); - RETURN_IF_EXCEPTION(scope, { }); - } else { - // When creating JSON object in this fast path, we know the following. - // 1. The object is definitely JSFinalObject. - // 2. The object rarely has duplicate properties. - // 3. Many same-shaped objects would be created from JSON. Thus very likely, there is already an existing Structure. - // Let's make the above case super fast, and fallback to the normal implementation when it is not true. - auto* structure = object->structure(); - PropertyOffset offset = 0; - Structure* newStructure = nullptr; - if (LIKELY(!structure->isDictionary() && (newStructure = Structure::addPropertyTransitionToExistingStructure(structure, ident, 0, offset)))) { - Butterfly* newButterfly = object->butterfly(); - if (structure->outOfLineCapacity() != newStructure->outOfLineCapacity()) { - ASSERT(newStructure != structure); - newButterfly = object->allocateMoreOutOfLineStorage(vm, structure->outOfLineCapacity(), newStructure->outOfLineCapacity()); - object->nukeStructureAndSetButterfly(vm, structure->id(), newButterfly); - } + // When creating JSON object in this fast path, we know the following. + // 1. The object is definitely JSFinalObject. + // 2. The object rarely has duplicate properties. + // 3. Many same-shaped objects would be created from JSON. Thus very likely, there is already an existing Structure. + // Let's make the above case super fast, and fallback to the normal implementation when it is not true. + if (std::holds_alternative(property)) { + auto& [newStructure, offset] = std::get(property); + + Butterfly* newButterfly = object->butterfly(); + if (structure->outOfLineCapacity() != newStructure->outOfLineCapacity()) { + ASSERT(newStructure != structure); + newButterfly = object->allocateMoreOutOfLineStorage(vm, structure->outOfLineCapacity(), newStructure->outOfLineCapacity()); + object->nukeStructureAndSetButterfly(vm, structure->id(), newButterfly); + } - validateOffset(offset); - ASSERT(newStructure->isValidOffset(offset)); + validateOffset(offset); + ASSERT(newStructure->isValidOffset(offset)); - // This assertion verifies that the concurrent GC won't read garbage if the concurrentGC - // is running at the same time we put without transitioning. - ASSERT(!object->getDirect(offset) || !JSValue::encode(object->getDirect(offset))); - object->putDirectOffset(vm, offset, value); - object->setStructure(vm, newStructure); - ASSERT(!newStructure->mayBePrototype()); // There is no way to make it prototype object. + // This assertion verifies that the concurrent GC won't read garbage if the concurrentGC + // is running at the same time we put without transitioning. + ASSERT(!object->getDirect(offset) || !JSValue::encode(object->getDirect(offset))); + object->putDirectOffset(vm, offset, value); + object->setStructure(vm, newStructure); + ASSERT(!newStructure->mayBePrototype()); // There is no way to make it prototype object. + } else { + ASSERT(std::holds_alternative(property)); + auto& ident = std::get(property); + if (UNLIKELY(m_mode != StrictJSON && ident == vm.propertyNames->underscoreProto)) { + if (UNLIKELY(!m_visitedUnderscoreProto.add(object).isNewEntry)) { + m_parseErrorMessage = "Attempted to redefine __proto__ property"_s; + return { }; + } + PutPropertySlot slot(object, m_nullOrCodeBlock ? m_nullOrCodeBlock->ownerExecutable()->isInStrictContext() : false); + JSValue(object).put(m_globalObject, ident, value, slot); + RETURN_IF_EXCEPTION(scope, { }); + } else if (std::optional index = parseIndex(ident)) { + object->putDirectIndex(m_globalObject, index.value(), value); + RETURN_IF_EXCEPTION(scope, { }); } else - object->putDirectForJSONSlow(vm, ident, value); + object->putDirect(vm, ident, value); } type = m_lexer.currentToken()->type; diff --git a/Source/JavaScriptCore/runtime/LiteralParser.h b/Source/JavaScriptCore/runtime/LiteralParser.h index 0e0fbde75379e..05e5b37747376 100644 --- a/Source/JavaScriptCore/runtime/LiteralParser.h +++ b/Source/JavaScriptCore/runtime/LiteralParser.h @@ -279,8 +279,10 @@ class LiteralParser { JSValue parsePrimitiveValue(VM&); - ALWAYS_INLINE Identifier makeIdentifier(VM&, typename Lexer::LiteralParserTokenPtr); - ALWAYS_INLINE JSString* makeJSString(VM&, typename Lexer::LiteralParserTokenPtr); + static ALWAYS_INLINE bool equalIdentifier(UniquedStringImpl*, typename Lexer::LiteralParserTokenPtr); + static ALWAYS_INLINE AtomStringImpl* existingIdentifier(VM&, typename Lexer::LiteralParserTokenPtr); + static ALWAYS_INLINE Identifier makeIdentifier(VM&, typename Lexer::LiteralParserTokenPtr); + static ALWAYS_INLINE JSString* makeJSString(VM&, typename Lexer::LiteralParserTokenPtr); void setErrorMessageForToken(TokenType); diff --git a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp index e55804b6cf680..d6327eecfa5c6 100644 --- a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp +++ b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp @@ -345,12 +345,15 @@ JSC_DEFINE_HOST_FUNCTION(objectConstructorAssign, (JSGlobalObject* globalObject, } } if (willBatch) { - Vector, 32> properties; + Vector properties; // structures ensures the lifetimes of these strings. MarkedArgumentBufferWithSize<32> values; + MarkedArgumentBuffer structures; for (unsigned i = 1; i < argsCount; ++i) { JSValue sourceValue = callFrame->uncheckedArgument(i); JSObject* source = asObject(sourceValue); - source->structure()->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool { + auto sourceStructure = source->structure(); + structures.append(sourceStructure); + sourceStructure->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool { if (entry.attributes() & PropertyAttribute::DontEnum) return true; @@ -368,11 +371,12 @@ JSC_DEFINE_HOST_FUNCTION(objectConstructorAssign, (JSGlobalObject* globalObject, // Actually, assigning with empty object (option for example) is common. (`Object.assign(defaultOptions, passedOptions)` where `passedOptions` is empty object.) if (!properties.isEmpty()) target->putOwnDataPropertyBatching(vm, properties.data(), values.data(), properties.size()); + return JSValue::encode(target); } } - Vector, 8> properties; + Vector properties; MarkedArgumentBuffer values; for (unsigned i = 1; i < argsCount; ++i) { JSValue sourceValue = callFrame->uncheckedArgument(i); diff --git a/Source/JavaScriptCore/runtime/ObjectConstructorInlines.h b/Source/JavaScriptCore/runtime/ObjectConstructorInlines.h index 1be49618faa90..7e28f66f62c07 100644 --- a/Source/JavaScriptCore/runtime/ObjectConstructorInlines.h +++ b/Source/JavaScriptCore/runtime/ObjectConstructorInlines.h @@ -281,7 +281,7 @@ ALWAYS_INLINE JSObject* tryCreateObjectViaCloning(VM& vm, JSGlobalObject* global return target; } -ALWAYS_INLINE bool objectAssignFast(JSGlobalObject* globalObject, JSFinalObject* target, JSObject* source, Vector, 8>& properties, MarkedArgumentBuffer& values) +ALWAYS_INLINE bool objectAssignFast(JSGlobalObject* globalObject, JSFinalObject* target, JSObject* source, Vector& properties, MarkedArgumentBuffer& values) { // |source| Structure does not have any getters. And target can perform fast put. // So enumerating properties and putting properties are non observable. @@ -315,6 +315,7 @@ ALWAYS_INLINE bool objectAssignFast(JSGlobalObject* globalObject, JSFinalObject* if (source->canHaveExistingOwnIndexedGetterSetterProperties()) return false; + EnsureStillAliveScope sourceStructureScope(sourceStructure); sourceStructure->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool { if (entry.attributes() & PropertyAttribute::DontEnum) return true; @@ -323,7 +324,7 @@ ALWAYS_INLINE bool objectAssignFast(JSGlobalObject* globalObject, JSFinalObject* if (propertyName.isPrivateName()) return true; - properties.append(entry.key()); + properties.append(entry.key()); // sourceStructure ensures the lifetimes of these strings. values.appendWithCrashOnOverflow(source->getDirect(entry.offset())); return true; @@ -337,6 +338,7 @@ ALWAYS_INLINE bool objectAssignFast(JSGlobalObject* globalObject, JSFinalObject* // Actually, assigning with empty object (option for example) is common. (`Object.assign(defaultOptions, passedOptions)` where `passedOptions` is empty object.) if (properties.size()) target->putOwnDataPropertyBatching(vm, properties.data(), values.data(), properties.size()); + return true; } diff --git a/Source/JavaScriptCore/runtime/ObjectPrototype.cpp b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp index 369414790c522..e3de3ccc2df9c 100644 --- a/Source/JavaScriptCore/runtime/ObjectPrototype.cpp +++ b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp @@ -100,7 +100,7 @@ JSC_DEFINE_HOST_FUNCTION(objectProtoFuncValueOf, (JSGlobalObject* globalObject, return JSValue::encode(valueObj); } -bool objectPrototypeHasOwnProperty(JSGlobalObject* globalObject, JSObject* thisObject, const Identifier& propertyName) +bool objectPrototypeHasOwnProperty(JSGlobalObject* globalObject, JSObject* thisObject, PropertyName propertyName) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); @@ -126,11 +126,23 @@ JSC_DEFINE_HOST_FUNCTION(objectProtoFuncHasOwnProperty, (JSGlobalObject* globalO auto scope = DECLARE_THROW_SCOPE(vm); JSValue base = callFrame->thisValue(); - auto propertyName = callFrame->argument(0).toPropertyKey(globalObject); + auto subscript = callFrame->argument(0); + + GCOwnedDataScope propertyName; + Identifier propertyKey; + SUPPRESS_UNCOUNTED_LOCAL UniquedStringImpl* uid = nullptr; + if (subscript.isString()) { + propertyName = asString(subscript)->toAtomString(globalObject); + uid = propertyName.data; + } else { + propertyKey = subscript.toPropertyKey(globalObject); + uid = propertyKey.impl(); + } RETURN_IF_EXCEPTION(scope, encodedJSValue()); + JSObject* thisObject = base.toThis(globalObject, ECMAMode::strict()).toObject(globalObject); RETURN_IF_EXCEPTION(scope, { }); - RELEASE_AND_RETURN(scope, JSValue::encode(jsBoolean(objectPrototypeHasOwnProperty(globalObject, thisObject, propertyName)))); + RELEASE_AND_RETURN(scope, JSValue::encode(jsBoolean(objectPrototypeHasOwnProperty(globalObject, thisObject, uid)))); } JSC_DEFINE_HOST_FUNCTION(objectProtoFuncIsPrototypeOf, (JSGlobalObject* globalObject, CallFrame* callFrame)) diff --git a/Source/JavaScriptCore/runtime/ObjectPrototype.h b/Source/JavaScriptCore/runtime/ObjectPrototype.h index 190143a483821..16ea956fe7fed 100644 --- a/Source/JavaScriptCore/runtime/ObjectPrototype.h +++ b/Source/JavaScriptCore/runtime/ObjectPrototype.h @@ -49,6 +49,6 @@ class ObjectPrototype final : public JSNonFinalObject { JS_EXPORT_PRIVATE JSC_DECLARE_HOST_FUNCTION(objectProtoFuncToString); JSString* objectPrototypeToString(JSGlobalObject*, JSValue thisValue); -bool objectPrototypeHasOwnProperty(JSGlobalObject*, JSObject* base, const Identifier& property); +bool objectPrototypeHasOwnProperty(JSGlobalObject*, JSObject* base, PropertyName); } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/PropertyName.h b/Source/JavaScriptCore/runtime/PropertyName.h index 833ed99da283f..084f01ebcbb26 100644 --- a/Source/JavaScriptCore/runtime/PropertyName.h +++ b/Source/JavaScriptCore/runtime/PropertyName.h @@ -37,6 +37,17 @@ namespace JSC { class PropertyName { public: + PropertyName() + : m_impl(nullptr) + { + } + + // FIXME: Make PropertyName const-correct. + PropertyName(const UniquedStringImpl* propertyName) + : m_impl(const_cast(propertyName)) + { + } + PropertyName(UniquedStringImpl* propertyName) : m_impl(propertyName) { diff --git a/Source/JavaScriptCore/runtime/SmallStrings.cpp b/Source/JavaScriptCore/runtime/SmallStrings.cpp index 7ec76a9741531..186371ccf7f61 100644 --- a/Source/JavaScriptCore/runtime/SmallStrings.cpp +++ b/Source/JavaScriptCore/runtime/SmallStrings.cpp @@ -122,6 +122,13 @@ Ref SmallStrings::singleCharacterStringRep(unsigned char charact return AtomStringImpl::add(string).releaseNonNull(); } +AtomStringImpl* SmallStrings::existingSingleCharacterStringRep(unsigned char character) +{ + if (UNLIKELY(!m_isInitialized)) + return nullptr; + return static_cast(const_cast(m_singleCharacterStrings[character]->tryGetValueImpl())); +} + void SmallStrings::initialize(VM* vm, JSString*& string, ASCIILiteral value) { string = JSString::create(*vm, AtomStringImpl::add(value)); diff --git a/Source/JavaScriptCore/runtime/SmallStrings.h b/Source/JavaScriptCore/runtime/SmallStrings.h index 2a16ab1d1e415..482caad94a2fe 100644 --- a/Source/JavaScriptCore/runtime/SmallStrings.h +++ b/Source/JavaScriptCore/runtime/SmallStrings.h @@ -75,6 +75,7 @@ WTF_ALLOW_UNSAFE_BUFFER_USAGE_END } JS_EXPORT_PRIVATE Ref singleCharacterStringRep(unsigned char character); + JS_EXPORT_PRIVATE AtomStringImpl* existingSingleCharacterStringRep(unsigned char character); void setIsInitialized(bool isInitialized) { m_isInitialized = isInitialized; } diff --git a/Source/JavaScriptCore/runtime/StringPrototype.cpp b/Source/JavaScriptCore/runtime/StringPrototype.cpp index 61490fc3a9b56..2d5fc60362bde 100644 --- a/Source/JavaScriptCore/runtime/StringPrototype.cpp +++ b/Source/JavaScriptCore/runtime/StringPrototype.cpp @@ -279,7 +279,7 @@ inline void substituteBackreferencesInline(StringBuilder& result, const String& void substituteBackreferences(StringBuilder& result, const String& replacement, StringView source, const int* ovector, RegExp* reg) { - substituteBackreferencesInline(result, replacement, source, ovector, reg); + return substituteBackreferencesInline(result, replacement, source, ovector, reg); } static ALWAYS_INLINE JSString* jsSpliceSubstrings(JSGlobalObject* globalObject, JSString* sourceVal, const String& source, std::span> substringRanges) @@ -996,10 +996,11 @@ JSC_DEFINE_HOST_FUNCTION(stringProtoFuncReplaceUsingRegExp, (JSGlobalObject* glo RETURN_IF_EXCEPTION(scope, encodedJSValue()); JSValue searchValue = callFrame->argument(0); - if (!searchValue.inherits()) + RegExpObject* regExpObject = jsDynamicCast(searchValue); + if (!regExpObject) return JSValue::encode(jsUndefined()); - RELEASE_AND_RETURN(scope, JSValue::encode(replaceUsingRegExpSearch(vm, globalObject, string, searchValue, callFrame->argument(1)))); + RELEASE_AND_RETURN(scope, JSValue::encode(replaceUsingRegExpSearch(vm, globalObject, string, regExpObject, callFrame->argument(1)))); } JSC_DEFINE_HOST_FUNCTION(stringProtoFuncReplaceUsingStringSearch, (JSGlobalObject* globalObject, CallFrame* callFrame)) @@ -1354,9 +1355,10 @@ JSC_DEFINE_HOST_FUNCTION(stringProtoFuncSplitFast, (JSGlobalObject* globalObject // 9. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator; // otherwise let R = ToString(separator). JSValue separatorValue = callFrame->uncheckedArgument(0); - String separator = separatorValue.toWTFString(globalObject); + JSString* separatorString = separatorValue.toString(globalObject); + auto separator = separatorString->value(globalObject); RETURN_IF_EXCEPTION(scope, { }); - unsigned separatorLength = separator.length(); + unsigned separatorLength = separator.data.length(); // 10. If lim == 0, return A. if (!limit) @@ -1422,7 +1424,7 @@ JSC_DEFINE_HOST_FUNCTION(stringProtoFuncSplitFast, (JSGlobalObject* globalObject // c. Call CreateDataProperty(A, "0", S). // d. Return A. scope.release(); - if (!separator.isEmpty()) + if (!separator.data.isEmpty()) result.append(input->length()); return JSValue::encode(cacheAndCreateArray()); } @@ -1456,7 +1458,7 @@ JSC_DEFINE_HOST_FUNCTION(stringProtoFuncSplitFast, (JSGlobalObject* globalObject // -separator length == 1, 16 bits // -separator length > 1 StringImpl* stringImpl = input->impl(); - StringImpl* separatorImpl = separator.impl(); + StringImpl* separatorImpl = separator.data.impl(); if (separatorLength == 1) { UChar separatorCharacter = separatorImpl->at(0); diff --git a/Source/JavaScriptCore/runtime/Structure.cpp b/Source/JavaScriptCore/runtime/Structure.cpp index 061b339b3c523..e4c0352061e39 100644 --- a/Source/JavaScriptCore/runtime/Structure.cpp +++ b/Source/JavaScriptCore/runtime/Structure.cpp @@ -81,7 +81,7 @@ void StructureTransitionTable::add(VM& vm, JSCell* owner, Structure* structure) } // Add the structure to the map. - map()->set(StructureTransitionTable::Hash::createFromStructure(structure), structure); + map()->set(StructureTransitionTable::Hash::createKeyFromStructure(structure), structure); } void Structure::dumpStatistics() diff --git a/Source/JavaScriptCore/runtime/Structure.h b/Source/JavaScriptCore/runtime/Structure.h index fcb9900dd1c51..a7f220e331106 100644 --- a/Source/JavaScriptCore/runtime/Structure.h +++ b/Source/JavaScriptCore/runtime/Structure.h @@ -301,6 +301,8 @@ class Structure : public JSCell { return false; } + Structure* trySingleTransition() { return m_transitionTable.trySingleTransition(); } + JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&); JS_EXPORT_PRIVATE static Structure* addNewPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&, PutPropertySlot::Context = PutPropertySlot::UnknownContext, DeferredStructureTransitionWatchpointFire* = nullptr); static Structure* addPropertyTransitionToExistingStructureConcurrently(Structure*, UniquedStringImpl* uid, unsigned attributes, PropertyOffset&); diff --git a/Source/JavaScriptCore/runtime/StructureInlines.h b/Source/JavaScriptCore/runtime/StructureInlines.h index 557a5ced0e6b9..d4e986ca45081 100644 --- a/Source/JavaScriptCore/runtime/StructureInlines.h +++ b/Source/JavaScriptCore/runtime/StructureInlines.h @@ -799,7 +799,7 @@ ALWAYS_INLINE Structure* Structure::addPropertyTransitionToExistingStructureConc return addPropertyTransitionToExistingStructureImpl(structure, uid, attributes, offset); } -ALWAYS_INLINE StructureTransitionTable::Hash::Key StructureTransitionTable::Hash::createFromStructure(Structure* structure) +ALWAYS_INLINE StructureTransitionTable::Hash::Key StructureTransitionTable::Hash::createKeyFromStructure(Structure* structure) { switch (structure->transitionKind()) { case TransitionKind::ChangePrototype: @@ -821,7 +821,11 @@ inline Structure* StructureTransitionTable::get(PointerKey rep, unsigned attribu { if (isUsingSingleSlot()) { auto* transition = trySingleTransition(); - return (transition && Hash::createFromStructure(transition) == Hash::createKey(rep, attributes, transitionKind)) ? transition : nullptr; + if (!transition) + return nullptr; + if (Hash::createKeyFromStructure(transition) != Hash::createKey(rep, attributes, transitionKind)) + return nullptr; + return transition; } return map()->get(StructureTransitionTable::Hash::createKey(rep, attributes, transitionKind)); } diff --git a/Source/JavaScriptCore/runtime/StructureTransitionTable.h b/Source/JavaScriptCore/runtime/StructureTransitionTable.h index 86fb03e9bdf3d..03e9427339028 100644 --- a/Source/JavaScriptCore/runtime/StructureTransitionTable.h +++ b/Source/JavaScriptCore/runtime/StructureTransitionTable.h @@ -231,7 +231,7 @@ class StructureTransitionTable { return a == b; } - static Key createFromStructure(Structure*); + static Key createKeyFromStructure(Structure*); static Key createKey(PointerKey impl, unsigned attributes, TransitionKind transitionKind) { return Key { impl, attributes, transitionKind }; @@ -254,7 +254,7 @@ class StructureTransitionTable { return a == b; } - static Key createFromStructure(Structure*); + static Key createKeyFromStructure(Structure*); static Key createKey(PointerKey impl, unsigned attributes, TransitionKind transitionKind) { return Key { impl.pointer(), attributes, transitionKind }; diff --git a/Source/WTF/WTF.xcodeproj/project.pbxproj b/Source/WTF/WTF.xcodeproj/project.pbxproj index 74f1cc055d2be..881802dc3f54f 100644 --- a/Source/WTF/WTF.xcodeproj/project.pbxproj +++ b/Source/WTF/WTF.xcodeproj/project.pbxproj @@ -41,6 +41,7 @@ 0FFF19DC1BB334EB00886D91 /* ParallelHelperPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FFF19DA1BB334EB00886D91 /* ParallelHelperPool.cpp */; }; 143DDE9620C8BC37007F76FA /* Entitlements.mm in Sources */ = {isa = PBXBuildFile; fileRef = 143DDE9520C8BC37007F76FA /* Entitlements.mm */; }; 143F611F1565F0F900DB514A /* RAMSize.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 143F611D1565F0F900DB514A /* RAMSize.cpp */; }; + 144177D62D89B7C100F5099E /* ValueOrReference.h in Headers */ = {isa = PBXBuildFile; fileRef = 144177D52D89B7C100F5099E /* ValueOrReference.h */; settings = {ATTRIBUTES = (Private, ); }; }; 1469419D16EAB10A0024E146 /* AutodrainedPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1469419B16EAB10A0024E146 /* AutodrainedPool.cpp */; }; 1470EAF32BD6F6D900E26254 /* WeakPtrImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 1470EAF22BD6F6D900E26254 /* WeakPtrImpl.h */; settings = {ATTRIBUTES = (Private, ); }; }; 1470EAF52BD6F8AF00E26254 /* WeakPtrFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 1470EAF42BD6F8AF00E26254 /* WeakPtrFactory.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -1097,6 +1098,7 @@ 143DDE9720C8BE99007F76FA /* Entitlements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Entitlements.h; sourceTree = ""; }; 143F611D1565F0F900DB514A /* RAMSize.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RAMSize.cpp; sourceTree = ""; }; 143F611E1565F0F900DB514A /* RAMSize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RAMSize.h; sourceTree = ""; }; + 144177D52D89B7C100F5099E /* ValueOrReference.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ValueOrReference.h; sourceTree = ""; }; 1447AEC518FCE57700B3D7FF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 1447AECA18FCE5B900B3D7FF /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = /usr/lib/libicucore.dylib; sourceTree = ""; }; 1469419416EAAFF80024E146 /* SchedulePair.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SchedulePair.h; sourceTree = ""; }; @@ -2511,6 +2513,7 @@ 7AFEC6B01EB22B5900DADE36 /* UUID.cpp */, 7AFEC6AE1EB22AC600DADE36 /* UUID.h */, A8A4736F151A825B004123FF /* ValueCheck.h */, + 144177D52D89B7C100F5099E /* ValueOrReference.h */, 0F95B63420CB53C100479635 /* Vector.cpp */, A8A47370151A825B004123FF /* Vector.h */, E419F2E623AB9E2300B26129 /* VectorHash.h */, @@ -3610,6 +3613,7 @@ DDF306F527C086CC006A526F /* utils.h in Headers */, DD3DC8C227A4BF8E007E5B61 /* UUID.h in Headers */, DD3DC86D27A4BF8E007E5B61 /* ValueCheck.h in Headers */, + 144177D62D89B7C100F5099E /* ValueOrReference.h in Headers */, DD3DC90B27A4BF8E007E5B61 /* Vector.h in Headers */, 4448265228D18CA600D916E8 /* VectorCF.h in Headers */, DDF3079727C086CD006A526F /* VectorCocoa.h in Headers */, diff --git a/Source/WTF/wtf/CMakeLists.txt b/Source/WTF/wtf/CMakeLists.txt index a1c4469d73844..519c4f66fcb95 100644 --- a/Source/WTF/wtf/CMakeLists.txt +++ b/Source/WTF/wtf/CMakeLists.txt @@ -355,6 +355,7 @@ set(WTF_PUBLIC_HEADERS UniqueRefVector.h VMTags.h ValueCheck.h + ValueOrReference.h VariantExtras.h VariantList.h VariantListOperations.h diff --git a/Source/WTF/wtf/RefCountedFixedVector.h b/Source/WTF/wtf/RefCountedFixedVector.h index 1dbe544d49434..5d0602d97730b 100644 --- a/Source/WTF/wtf/RefCountedFixedVector.h +++ b/Source/WTF/wtf/RefCountedFixedVector.h @@ -70,17 +70,6 @@ class RefCountedFixedVectorBase final : public std::conditional +inline bool operator==(const RefCountedFixedVectorBase& a, const U& b) +{ + if (a.size() != b.size()) + return false; + for (size_t i = 0; i < a.size(); ++i) { + if (a.at(i) != b.at(i)) + return false; + } + return true; +} + template using RefCountedFixedVector = RefCountedFixedVectorBase; template diff --git a/Source/WTF/wtf/ValueOrReference.h b/Source/WTF/wtf/ValueOrReference.h new file mode 100644 index 0000000000000..b89d767332c5a --- /dev/null +++ b/Source/WTF/wtf/ValueOrReference.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2025 Apple Inc. All rights reserved. + * + * 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. AND ITS CONTRIBUTORS ``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 ITS 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 + +namespace WTF { + +// ValueOrReference is just like const T&, except that it can also optionally hold T. + +// ValueOrReference is an optimization when you need to return a value that is +// usually an existing reference, but sometimes a temporary, e.g.: +// +// ValueOrReference append(const String& string LIFETIME_BOUND, std::optional suffix) +// { +// if (LIKELY(!suffix)) +// return string; // existing reference -- ValueOrReference avoids a copy +// return makeString(string, suffix.value()); // temporary -- ValueOrReference holds T +// } +template class ValueOrReference { +public: + ValueOrReference() + : m_reference(m_value) + { + } + + ValueOrReference(ValueOrReference&& other) + : m_value(WTFMove(other.m_value)) + , m_reference(&other.m_reference == &other.m_value ? m_value : other.m_reference) + { + } + + ValueOrReference(const T& reference LIFETIME_BOUND) + : m_reference(reference) + { + } + + ValueOrReference(T&& temporary) + : m_value(WTFMove(temporary)) + , m_reference(m_value) + { + } + + operator const T&() const LIFETIME_BOUND { return m_reference; } + const T& get() const LIFETIME_BOUND { return m_reference; } + const T* operator->() const LIFETIME_BOUND { return &m_reference; } + +private: + T m_value; + const T& m_reference; +}; + +} // namespace WTF + +using WTF::ValueOrReference; diff --git a/Source/WTF/wtf/Vector.h b/Source/WTF/wtf/Vector.h index 2ff4bd391653a..13f56cf683048 100644 --- a/Source/WTF/wtf/Vector.h +++ b/Source/WTF/wtf/Vector.h @@ -2093,6 +2093,15 @@ inline Vector::Type> moveToVector( return moveToVectorOf::Type>(collection); } +template static bool insertInUniquedSortedVector(Vector& vector, const T& value) +{ + auto it = std::lower_bound(vector.begin(), vector.end(), value); + if (UNLIKELY(it != vector.end() && *it == value)) + return false; + vector.insert(it - vector.begin(), value); + return true; +} + template Vector(const T*, size_t) -> Vector; template Vector(std::span) -> Vector; @@ -2105,4 +2114,5 @@ using WTF::copyToVectorOf; using WTF::copyToVectorSpecialization; using WTF::compactMap; using WTF::flatMap; +using WTF::insertInUniquedSortedVector; using WTF::removeRepeatedElements; diff --git a/Source/WTF/wtf/text/StringImpl.cpp b/Source/WTF/wtf/text/StringImpl.cpp index 3c4b20ae32e5f..0851102a7f397 100644 --- a/Source/WTF/wtf/text/StringImpl.cpp +++ b/Source/WTF/wtf/text/StringImpl.cpp @@ -25,6 +25,7 @@ #include "config.h" #include +#include #include #include #include diff --git a/Source/WTF/wtf/text/StringImpl.h b/Source/WTF/wtf/text/StringImpl.h index 3e4d7e974905b..5eb4d5c1932d3 100644 --- a/Source/WTF/wtf/text/StringImpl.h +++ b/Source/WTF/wtf/text/StringImpl.h @@ -26,6 +26,7 @@ WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN +#include #include #include #include @@ -161,7 +162,7 @@ class STRING_IMPL_ALIGNMENT StringImplShape { template constexpr StringImplShape(unsigned refCount, unsigned length, const char (&characters)[characterCount], unsigned hashAndFlags, ConstructWithConstExprTag); template constexpr StringImplShape(unsigned refCount, unsigned length, const char16_t (&characters)[characterCount], unsigned hashAndFlags, ConstructWithConstExprTag); - unsigned m_refCount; + std::atomic m_refCount; unsigned m_length; union { const LChar* m_data8; @@ -367,11 +368,11 @@ class StringImpl : private StringImplShape { unsigned symbolAwareHash() const; unsigned existingSymbolAwareHash() const; - SUPPRESS_TSAN bool isStatic() const { return m_refCount & s_refCountFlagIsStaticString; } + SUPPRESS_TSAN bool isStatic() const { return m_refCount.load(std::memory_order_relaxed) & s_refCountFlagIsStaticString; } - size_t refCount() const { return m_refCount / s_refCountIncrement; } - bool hasOneRef() const { return m_refCount == s_refCountIncrement; } - bool hasAtLeastOneRef() const { return m_refCount; } // For assertions. + size_t refCount() const { return m_refCount.load(std::memory_order_relaxed) / s_refCountIncrement; } + bool hasOneRef() const { return m_refCount.load(std::memory_order_relaxed) == s_refCountIncrement; } + bool hasAtLeastOneRef() const { return m_refCount.load(std::memory_order_relaxed); } // For assertions. void ref(); void deref(); @@ -1209,7 +1210,7 @@ inline void StringImpl::ref() return; #endif - m_refCount += s_refCountIncrement; + m_refCount.fetch_add(s_refCountIncrement, std::memory_order_relaxed); } inline void StringImpl::deref() @@ -1221,12 +1222,11 @@ inline void StringImpl::deref() return; #endif - unsigned tempRefCount = m_refCount - s_refCountIncrement; - if (!tempRefCount) { - StringImpl::destroy(this); + auto oldRefCount = m_refCount.fetch_sub(s_refCountIncrement, std::memory_order_relaxed); + if (oldRefCount != s_refCountIncrement) return; - } - m_refCount = tempRefCount; + + StringImpl::destroy(this); } inline UChar StringImpl::at(unsigned i) const diff --git a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp index 9d84ae738561b..679d1570d0c83 100644 --- a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp +++ b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp @@ -1709,7 +1709,7 @@ VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(int index) co if (m_renderer) { if (isNativeTextControl()) { auto& textControl = uncheckedDowncast(*m_renderer).textFormControlElement(); - return textControl.visiblePositionForIndex(std::clamp(index, 0, static_cast(textControl.value().length()))); + return textControl.visiblePositionForIndex(std::clamp(index, 0, static_cast(textControl.value()->length()))); } if (!allowsTextRanges() && !is(*m_renderer)) @@ -1804,13 +1804,13 @@ void AccessibilityRenderObject::setSelectedVisiblePositionRange(const VisiblePos auto innerRange = makeVisiblePositionRange(AXObjectCache::rangeForNodeContents(*innerText)); if (range.start.equals(textControlRange.end)) - start = textControl->value().length(); + start = textControl->value()->length(); else if (range.start <= innerRange.start) start = 0; if (range.end >= innerRange.end || range.end.equals(textControlRange.end)) - end = textControl->value().length(); + end = textControl->value()->length(); } } diff --git a/Source/WebCore/accessibility/AccessibilitySlider.cpp b/Source/WebCore/accessibility/AccessibilitySlider.cpp index c3af8d2a7475c..5512f3c650872 100644 --- a/Source/WebCore/accessibility/AccessibilitySlider.cpp +++ b/Source/WebCore/accessibility/AccessibilitySlider.cpp @@ -119,7 +119,7 @@ AccessibilityObject* AccessibilitySlider::elementAccessibilityHitTest(const IntP float AccessibilitySlider::valueForRange() const { if (auto* input = inputElement()) - return input->value().toFloat(); + return input->value()->toFloat(); return 0; } diff --git a/Source/WebCore/accessibility/ios/AccessibilityObjectIOS.mm b/Source/WebCore/accessibility/ios/AccessibilityObjectIOS.mm index d327018e5ad28..d1f1835b1135d 100644 --- a/Source/WebCore/accessibility/ios/AccessibilityObjectIOS.mm +++ b/Source/WebCore/accessibility/ios/AccessibilityObjectIOS.mm @@ -103,7 +103,7 @@ return 0; auto* inputElement = dynamicDowncast(renderer->node()); - return inputElement ? inputElement->value().length() : 0; + return inputElement ? inputElement->value()->length() : 0; } bool AccessibilityObject::accessibilityIgnoreAttachment() const diff --git a/Source/WebCore/bindings/js/JSDOMConvertStrings.cpp b/Source/WebCore/bindings/js/JSDOMConvertStrings.cpp index fae5b311af481..0c09d0d3b3f7a 100644 --- a/Source/WebCore/bindings/js/JSDOMConvertStrings.cpp +++ b/Source/WebCore/bindings/js/JSDOMConvertStrings.cpp @@ -84,7 +84,7 @@ ConversionResult> valueToByteAtomString(JSC: VM& vm = lexicalGlobalObject.vm(); auto scope = DECLARE_THROW_SCOPE(vm); - auto string = value.toString(&lexicalGlobalObject)->toAtomString(&lexicalGlobalObject); + AtomString string = value.toString(&lexicalGlobalObject)->toAtomString(&lexicalGlobalObject).data; RETURN_IF_EXCEPTION(scope, ConversionResult>::exception()); if (UNLIKELY(throwIfInvalidByteString(lexicalGlobalObject, scope, string.string()))) @@ -117,7 +117,7 @@ ConversionResult> valueToUSVAtomString(JSGlob auto string = value.toString(&lexicalGlobalObject)->toAtomString(&lexicalGlobalObject); RETURN_IF_EXCEPTION(scope, ConversionResult>::exception()); - return replaceUnpairedSurrogatesWithReplacementCharacter(WTFMove(string)); + return replaceUnpairedSurrogatesWithReplacementCharacter(AtomString(string)); } // https://w3c.github.io/trusted-types/dist/spec/#get-trusted-type-compliant-string-algorithm diff --git a/Source/WebCore/bindings/js/JSDOMConvertStrings.h b/Source/WebCore/bindings/js/JSDOMConvertStrings.h index 4d15eedaca857..f8d241d1244f7 100644 --- a/Source/WebCore/bindings/js/JSDOMConvertStrings.h +++ b/Source/WebCore/bindings/js/JSDOMConvertStrings.h @@ -271,7 +271,7 @@ template struct Converter> : DefaultConverte RETURN_IF_EXCEPTION(scope, Result::exception()); - return Result { WTFMove(string) }; + return Result { string.data }; } }; @@ -348,7 +348,7 @@ template struct Converter::value, "This adaptor is only supported for IDLDOMString at the moment."); - return value.toString(&lexicalGlobalObject)->toExistingAtomString(&lexicalGlobalObject); + return value.toString(&lexicalGlobalObject)->toExistingAtomString(&lexicalGlobalObject).data; } }; diff --git a/Source/WebCore/bindings/js/JSHTMLAllCollectionCustom.cpp b/Source/WebCore/bindings/js/JSHTMLAllCollectionCustom.cpp index dbefe5eb0718e..df3f87872e845 100644 --- a/Source/WebCore/bindings/js/JSHTMLAllCollectionCustom.cpp +++ b/Source/WebCore/bindings/js/JSHTMLAllCollectionCustom.cpp @@ -52,7 +52,7 @@ JSC_DEFINE_HOST_FUNCTION(callJSHTMLAllCollection, (JSGlobalObject* lexicalGlobal if (callFrame->argument(0).isUndefined()) return JSValue::encode(jsNull()); - AtomString nameOrIndex = callFrame->uncheckedArgument(0).toString(lexicalGlobalObject)->toAtomString(lexicalGlobalObject); + AtomString nameOrIndex = callFrame->uncheckedArgument(0).toString(lexicalGlobalObject)->toAtomString(lexicalGlobalObject).data; RETURN_IF_EXCEPTION(scope, { }); RELEASE_AND_RETURN(scope, JSValue::encode(toJS, IDLInterface>>>(*lexicalGlobalObject, *castedThis->globalObject(), impl.namedOrIndexedItemOrItems(WTFMove(nameOrIndex))))); } diff --git a/Source/WebCore/css/SelectorFilter.cpp b/Source/WebCore/css/SelectorFilter.cpp index a267d82531849..79d41726cb4b6 100644 --- a/Source/WebCore/css/SelectorFilter.cpp +++ b/Source/WebCore/css/SelectorFilter.cpp @@ -62,7 +62,7 @@ void SelectorFilter::collectElementIdentifierHashes(const Element& element, Vect if (element.hasAttributesWithoutUpdate()) { for (auto& attribute : element.attributesIterator()) { - auto attributeName = element.isHTMLElement() ? attribute.localName() : attribute.localNameLowercase(); + auto& attributeName = element.isHTMLElement() ? attribute.localName() : attribute.localNameLowercase(); if (isExcludedAttribute(attributeName)) continue; identifierHashes.append(attributeName.impl()->existingHash() * AttributeSalt); diff --git a/Source/WebCore/css/query/MediaQueryEvaluator.cpp b/Source/WebCore/css/query/MediaQueryEvaluator.cpp index 9e08b56980556..8373bc3f6a55d 100644 --- a/Source/WebCore/css/query/MediaQueryEvaluator.cpp +++ b/Source/WebCore/css/query/MediaQueryEvaluator.cpp @@ -81,11 +81,13 @@ bool MediaQueryEvaluator::evaluate(const MediaQuery& query) const return EvaluationResult::Unknown; auto defaultStyle = RenderStyle::create(); - auto fontDescription = defaultStyle.fontDescription(); auto size = Style::fontSizeForKeyword(CSSValueMedium, false, *document); - fontDescription.setComputedSize(size); - fontDescription.setSpecifiedSize(size); - defaultStyle.setFontDescription(WTFMove(fontDescription)); + if (size != defaultStyle.fontDescription().specifiedSize()) { + auto fontDescription = defaultStyle.fontDescription(); + fontDescription.setSpecifiedSize(size); + fontDescription.setComputedSize(size); + defaultStyle.setFontDescription(WTFMove(fontDescription)); + } defaultStyle.fontCascade().update(); FeatureEvaluationContext context { *document, { *m_rootElementStyle, &defaultStyle, nullptr, document->renderView() }, nullptr }; diff --git a/Source/WebCore/dom/Attribute.h b/Source/WebCore/dom/Attribute.h index b8ac3dd2ae252..32dea1369ddab 100644 --- a/Source/WebCore/dom/Attribute.h +++ b/Source/WebCore/dom/Attribute.h @@ -41,6 +41,12 @@ class Attribute { { } + Attribute(QualifiedName&& name, AtomString&& value) + : m_name(WTFMove(name)) + , m_value(WTFMove(value)) + { + } + // NOTE: The references returned by these functions are only valid for as long // as the Attribute stays in place. For example, calling a function that mutates // an Element's internal attribute storage may invalidate them. diff --git a/Source/WebCore/dom/CharacterData.h b/Source/WebCore/dom/CharacterData.h index c3518cfe59456..0197b4811ffcd 100644 --- a/Source/WebCore/dom/CharacterData.h +++ b/Source/WebCore/dom/CharacterData.h @@ -49,8 +49,11 @@ class CharacterData : public Node { protected: CharacterData(Document& document, String&& text, NodeType type, OptionSet typeFlags = { }) : Node(document, type, typeFlags | TypeFlag::IsCharacterData) - , m_data(!text.isNull() ? WTFMove(text) : emptyString()) + , m_data(WTFMove(text)) { + if (m_data.isNull()) + m_data = emptyString(); + ASSERT(isCharacterDataNode()); ASSERT(!isContainerNode()); } diff --git a/Source/WebCore/dom/Text.cpp b/Source/WebCore/dom/Text.cpp index cb6f95786fa6e..e925676b1562b 100644 --- a/Source/WebCore/dom/Text.cpp +++ b/Source/WebCore/dom/Text.cpp @@ -44,11 +44,6 @@ namespace WebCore { WTF_MAKE_TZONE_OR_ISO_ALLOCATED_IMPL(Text); -Ref Text::create(Document& document, String&& data) -{ - return adoptRef(*new Text(document, WTFMove(data), TEXT_NODE, { })); -} - Ref Text::createEditingText(Document& document, String&& data) { auto node = adoptRef(*new Text(document, WTFMove(data), TEXT_NODE, { TypeFlag::IsSpecialInternalNode })); diff --git a/Source/WebCore/dom/Text.h b/Source/WebCore/dom/Text.h index 8a7c563412bb5..3845c32b8db94 100644 --- a/Source/WebCore/dom/Text.h +++ b/Source/WebCore/dom/Text.h @@ -35,7 +35,10 @@ class Text : public CharacterData { public: static const unsigned defaultLengthLimit = 1 << 16; - static Ref create(Document&, String&&); + static Ref create(Document& document, String&& data) + { + return adoptRef(*new Text(document, WTFMove(data), TEXT_NODE, { })); + } static Ref createEditingText(Document&, String&&); virtual ~Text(); diff --git a/Source/WebCore/domjit/DOMJITIDLConvert.h b/Source/WebCore/domjit/DOMJITIDLConvert.h index 81395e125bd00..ce9cc8dcfa855 100644 --- a/Source/WebCore/domjit/DOMJITIDLConvert.h +++ b/Source/WebCore/domjit/DOMJITIDLConvert.h @@ -44,7 +44,7 @@ template<> struct DirectConverter> { static AtomString directConvert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSString* string) { - return string->toAtomString(&lexicalGlobalObject); + return string->toAtomString(&lexicalGlobalObject).data; } }; @@ -52,7 +52,7 @@ template<> struct DirectConverter> { static AtomString directConvert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSString* string) { - return string->toExistingAtomString(&lexicalGlobalObject); + return string->toExistingAtomString(&lexicalGlobalObject).data; } }; diff --git a/Source/WebCore/editing/cocoa/AutofillElements.cpp b/Source/WebCore/editing/cocoa/AutofillElements.cpp index 34be86605ad23..0910559f37616 100644 --- a/Source/WebCore/editing/cocoa/AutofillElements.cpp +++ b/Source/WebCore/editing/cocoa/AutofillElements.cpp @@ -86,7 +86,7 @@ std::optional AutofillElements::computeAutofillElements(RefisPasswordField(); - bool hasSecondPasswordFieldToFill = nextElement && nextElement->isPasswordField() && nextElement->value().isEmpty(); + bool hasSecondPasswordFieldToFill = nextElement && nextElement->isPasswordField() && nextElement->value()->isEmpty(); // Always allow AutoFill in a password field, even if we fill information only into it. return {{ previousFieldIsTextField ? WTFMove(previousElement) : nullptr, WTFMove(start), hasSecondPasswordFieldToFill ? WTFMove(nextElement) : nullptr }}; @@ -95,7 +95,7 @@ std::optional AutofillElements::computeAutofillElements(Ref(*nextElement)) { if (nextElement->isPasswordField()) { auto elementAfterNextElement = nextAutofillableElement(nextElement.get(), focusController); - bool hasSecondPasswordFieldToFill = elementAfterNextElement && elementAfterNextElement->isPasswordField() && elementAfterNextElement->value().isEmpty(); + bool hasSecondPasswordFieldToFill = elementAfterNextElement && elementAfterNextElement->isPasswordField() && elementAfterNextElement->value()->isEmpty(); return {{ WTFMove(start), WTFMove(nextElement), hasSecondPasswordFieldToFill ? WTFMove(elementAfterNextElement) : nullptr }}; } diff --git a/Source/WebCore/editing/cocoa/HTMLConverter.mm b/Source/WebCore/editing/cocoa/HTMLConverter.mm index 506d831f3280a..c761b316a4cc2 100644 --- a/Source/WebCore/editing/cocoa/HTMLConverter.mm +++ b/Source/WebCore/editing/cocoa/HTMLConverter.mm @@ -1880,14 +1880,14 @@ static NSInteger _colCompare(id block1, id block2, void *) } else if (element.hasTagName(inputTag)) { if (RefPtr inputElement = dynamicDowncast(element)) { if (inputElement->type() == textAtom()) { - RetainPtr value = (NSString *)inputElement->value(); + RetainPtr value = (NSString *)inputElement->value().get(); if (value && [value length] > 0) _addValue(value.get(), element); } } } else if (element.hasTagName(textareaTag)) { if (RefPtr textAreaElement = dynamicDowncast(element)) { - RetainPtr value = (NSString *)textAreaElement->value(); + RetainPtr value = (NSString *)textAreaElement->value().get(); if (value && [value length] > 0) _addValue(value.get(), element); } diff --git a/Source/WebCore/html/BaseCheckableInputType.cpp b/Source/WebCore/html/BaseCheckableInputType.cpp index fc1eac9622ad4..664a739e7560b 100644 --- a/Source/WebCore/html/BaseCheckableInputType.cpp +++ b/Source/WebCore/html/BaseCheckableInputType.cpp @@ -98,9 +98,9 @@ bool BaseCheckableInputType::accessKeyAction(bool sendMouseEvents) return InputType::accessKeyAction(sendMouseEvents) || element()->dispatchSimulatedClick(0, sendMouseEvents ? SendMouseUpDownEvents : SendNoEvents); } -String BaseCheckableInputType::fallbackValue() const +ValueOrReference BaseCheckableInputType::fallbackValue() const { - return onAtom(); + return onAtom().string(); } bool BaseCheckableInputType::storesValueSeparateFromAttribute() diff --git a/Source/WebCore/html/BaseCheckableInputType.h b/Source/WebCore/html/BaseCheckableInputType.h index 425958a02a643..e04c2b1255a31 100644 --- a/Source/WebCore/html/BaseCheckableInputType.h +++ b/Source/WebCore/html/BaseCheckableInputType.h @@ -54,7 +54,7 @@ class BaseCheckableInputType : public InputType { bool appendFormData(DOMFormData&) const final; void handleKeypressEvent(KeyboardEvent&) final; bool accessKeyAction(bool sendMouseEvents) final; - String fallbackValue() const final; + ValueOrReference fallbackValue() const final; bool storesValueSeparateFromAttribute() final; void setValue(const String&, bool, TextFieldEventBehavior, TextControlSetValueSelection) final; }; diff --git a/Source/WebCore/html/BaseDateAndTimeInputType.cpp b/Source/WebCore/html/BaseDateAndTimeInputType.cpp index 32d757b282f65..00ebbfeb408b9 100644 --- a/Source/WebCore/html/BaseDateAndTimeInputType.cpp +++ b/Source/WebCore/html/BaseDateAndTimeInputType.cpp @@ -194,7 +194,7 @@ bool BaseDateAndTimeInputType::typeMismatch() const bool BaseDateAndTimeInputType::hasBadInput() const { ASSERT(element()); - return element()->value().isEmpty() && m_dateTimeEditElement && m_dateTimeEditElement->editableFieldsHaveValues(); + return protectedElement()->value()->isEmpty() && m_dateTimeEditElement && m_dateTimeEditElement->editableFieldsHaveValues(); } Decimal BaseDateAndTimeInputType::defaultValueForStepUp() const @@ -257,9 +257,11 @@ String BaseDateAndTimeInputType::visibleValue() const return localizeValue(element()->value()); } -String BaseDateAndTimeInputType::sanitizeValue(const String& proposedValue) const +ValueOrReference BaseDateAndTimeInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { - return typeMismatchFor(proposedValue) ? emptyString() : proposedValue; + if (typeMismatchFor(proposedValue)) + return emptyString(); + return proposedValue; } bool BaseDateAndTimeInputType::supportsReadOnly() const @@ -398,7 +400,7 @@ void BaseDateAndTimeInputType::updateInnerTextValue() DateTimeEditElement::LayoutParameters layoutParameters(element()->locale()); - auto date = parseToDateComponents(element()->value()); + auto date = parseToDateComponents(element()->value().get()); if (date) setupLayoutParameters(layoutParameters, *date); else { @@ -619,7 +621,7 @@ bool BaseDateAndTimeInputType::setupDateTimeChooserParameters(DateTimeChooserPar auto* computedStyle = element.computedStyle(); parameters.isAnchorElementRTL = computedStyle->direction() == TextDirection::RTL; parameters.useDarkAppearance = document.useDarkAppearance(computedStyle); - auto date = valueOrDefault(parseToDateComponents(element.value())); + auto date = valueOrDefault(parseToDateComponents(element.value().get())); parameters.hasSecondField = shouldHaveSecondField(date); parameters.hasMillisecondField = shouldHaveMillisecondField(date); diff --git a/Source/WebCore/html/BaseDateAndTimeInputType.h b/Source/WebCore/html/BaseDateAndTimeInputType.h index b60d87fe12ec4..490502bc80a5a 100644 --- a/Source/WebCore/html/BaseDateAndTimeInputType.h +++ b/Source/WebCore/html/BaseDateAndTimeInputType.h @@ -103,7 +103,7 @@ class BaseDateAndTimeInputType : public InputType, private DateTimeChooserClient // InputType functions: String visibleValue() const final; - String sanitizeValue(const String&) const override; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const override; void setValue(const String&, bool valueChanged, TextFieldEventBehavior, TextControlSetValueSelection) final; WallTime valueAsDate() const override; ExceptionOr setValueAsDate(WallTime) const override; diff --git a/Source/WebCore/html/ColorInputType.cpp b/Source/WebCore/html/ColorInputType.cpp index 71805feb683a8..07347dc9fcc83 100644 --- a/Source/WebCore/html/ColorInputType.cpp +++ b/Source/WebCore/html/ColorInputType.cpp @@ -119,12 +119,12 @@ bool ColorInputType::supportsRequired() const return false; } -String ColorInputType::fallbackValue() const +ValueOrReference ColorInputType::fallbackValue() const { return "#000000"_s; } -String ColorInputType::sanitizeValue(const String& proposedValue) const +ValueOrReference ColorInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { if (!isValidSimpleColor(proposedValue)) return fallbackValue(); @@ -135,7 +135,7 @@ String ColorInputType::sanitizeValue(const String& proposedValue) const Color ColorInputType::valueAsColor() const { ASSERT(element()); - return parseSimpleColorValue(element()->value()).value(); + return parseSimpleColorValue(element()->value().get()).value(); } void ColorInputType::createShadowSubtree() diff --git a/Source/WebCore/html/ColorInputType.h b/Source/WebCore/html/ColorInputType.h index 00d0957d39102..389b26c423aff 100644 --- a/Source/WebCore/html/ColorInputType.h +++ b/Source/WebCore/html/ColorInputType.h @@ -70,8 +70,8 @@ class ColorInputType final : public BaseClickableWithKeyInputType, private Color bool isPresentingAttachedView() const final; const AtomString& formControlType() const final; bool supportsRequired() const final; - String fallbackValue() const final; - String sanitizeValue(const String&) const final; + ValueOrReference fallbackValue() const final; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const final; void createShadowSubtree() final; void setValue(const String&, bool valueChanged, TextFieldEventBehavior, TextControlSetValueSelection) final; void attributeChanged(const QualifiedName&) final; diff --git a/Source/WebCore/html/DateTimeLocalInputType.cpp b/Source/WebCore/html/DateTimeLocalInputType.cpp index b5513b0db3155..fef9a20ba42d7 100644 --- a/Source/WebCore/html/DateTimeLocalInputType.cpp +++ b/Source/WebCore/html/DateTimeLocalInputType.cpp @@ -100,13 +100,15 @@ bool DateTimeLocalInputType::isValidFormat(OptionSet DateTimeLocalInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { if (proposedValue.isEmpty()) return proposedValue; - auto components = DateComponents::fromParsingDateTimeLocal(proposedValue); - return components ? components->toString() : emptyString(); + if (auto components = DateComponents::fromParsingDateTimeLocal(proposedValue)) + return components->toString(); + + return emptyString(); } String DateTimeLocalInputType::formatDateTimeFieldsState(const DateTimeFieldsState& state) const diff --git a/Source/WebCore/html/DateTimeLocalInputType.h b/Source/WebCore/html/DateTimeLocalInputType.h index 15b5fec12327e..46a6cd6b504ef 100644 --- a/Source/WebCore/html/DateTimeLocalInputType.h +++ b/Source/WebCore/html/DateTimeLocalInputType.h @@ -56,7 +56,7 @@ class DateTimeLocalInputType final : public BaseDateAndTimeInputType { StepRange createStepRange(AnyStepHandling) const final; std::optional parseToDateComponents(StringView) const final; std::optional setMillisecondToDateComponents(double) const final; - String sanitizeValue(const String&) const final; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const final; bool isValidFormat(OptionSet) const final; String formatDateTimeFieldsState(const DateTimeFieldsState&) const final; diff --git a/Source/WebCore/html/EmailInputType.cpp b/Source/WebCore/html/EmailInputType.cpp index b3d0fcec7d3b8..16e720f572658 100644 --- a/Source/WebCore/html/EmailInputType.cpp +++ b/Source/WebCore/html/EmailInputType.cpp @@ -99,7 +99,7 @@ void EmailInputType::attributeChanged(const QualifiedName& name) BaseTextInputType::attributeChanged(name); } -String EmailInputType::sanitizeValue(const String& proposedValue) const +ValueOrReference EmailInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { // Passing a lambda instead of a function name helps the compiler inline isHTMLLineBreak. String noLineBreakValue = proposedValue; diff --git a/Source/WebCore/html/EmailInputType.h b/Source/WebCore/html/EmailInputType.h index aa3801296e0c3..59ddfa84d123c 100644 --- a/Source/WebCore/html/EmailInputType.h +++ b/Source/WebCore/html/EmailInputType.h @@ -53,7 +53,7 @@ class EmailInputType final : public BaseTextInputType { const AtomString& formControlType() const final; String typeMismatchText() const final; bool supportsSelectionAPI() const final; - String sanitizeValue(const String&) const final; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const final; void attributeChanged(const QualifiedName&) final; }; diff --git a/Source/WebCore/html/HTMLInputElement.cpp b/Source/WebCore/html/HTMLInputElement.cpp index e67560963d032..00d721939775f 100644 --- a/Source/WebCore/html/HTMLInputElement.cpp +++ b/Source/WebCore/html/HTMLInputElement.cpp @@ -254,12 +254,12 @@ bool HTMLInputElement::isValidValue(const String& value) const bool HTMLInputElement::tooShort() const { - return tooShort(value(), CheckDirtyFlag); + return tooShort(value().get(), CheckDirtyFlag); } bool HTMLInputElement::tooLong() const { - return tooLong(value(), CheckDirtyFlag); + return tooLong(value().get(), CheckDirtyFlag); } bool HTMLInputElement::typeMismatch() const @@ -357,8 +357,8 @@ bool HTMLInputElement::stepMismatch() const bool HTMLInputElement::computeValidity() const { - String value = this->value(); - bool someError = m_inputType->isInvalid(value) || tooShort(value, CheckDirtyFlag) || tooLong(value, CheckDirtyFlag) || customError(); + auto value = this->value(); + bool someError = m_inputType->isInvalid(value) || tooShort(value.get(), CheckDirtyFlag) || tooLong(value.get(), CheckDirtyFlag) || customError(); return !someError; } @@ -384,7 +384,7 @@ std::optional HTMLInputElement::listOptionValueAsDouble(const HTMLOption if (!isValidValue(optionValue)) return std::nullopt; - return parseToDoubleForNumberType(sanitizeValue(optionValue)); + return parseToDoubleForNumberType(sanitizeValue(optionValue).get()); } #endif @@ -1143,7 +1143,7 @@ void HTMLInputElement::copyNonAttributePropertiesFromElement(const Element& sour m_inputType->updateInnerTextValue(); } -String HTMLInputElement::value() const +ValueOrReference HTMLInputElement::value() const { if (auto* fileInput = dynamicDowncast(*m_inputType)) return fileInput->firstElementPathForInputValue(); @@ -1152,7 +1152,7 @@ String HTMLInputElement::value() const return m_valueIfDirty; if (auto& valueString = attributeWithoutSynchronization(valueAttr); !valueString.isNull()) { - if (auto sanitizedValue = sanitizeValue(valueString); !sanitizedValue.isNull()) + if (auto sanitizedValue = sanitizeValue(valueString); !sanitizedValue->isNull()) return sanitizedValue; } @@ -1161,7 +1161,7 @@ String HTMLInputElement::value() const String HTMLInputElement::valueWithDefault() const { - if (auto value = this->value(); !value.isNull()) + if (auto value = this->value(); !value->isNull()) return value; return m_inputType->defaultValue(); @@ -1179,12 +1179,12 @@ ExceptionOr HTMLInputElement::setValue(const String& value, TextFieldEvent setLastChangeWasNotUserEdit(); setFormControlValueMatchesRenderer(false); - m_inputType->setValue(WTFMove(sanitizedValue), valueChanged, eventBehavior, selection); + m_inputType->setValue(sanitizedValue, valueChanged, eventBehavior, selection); if (selfOrPrecedingNodesAffectDirAuto()) updateEffectiveDirectionalityOfDirAuto(); if (valueChanged && eventBehavior == DispatchNoEvent) - setTextAsOfLastFormControlChangeEvent(sanitizedValue); + setTextAsOfLastFormControlChangeEvent(String(sanitizedValue)); bool wasModifiedProgrammatically = eventBehavior == DispatchNoEvent; if (wasModifiedProgrammatically) { @@ -1240,7 +1240,7 @@ void HTMLInputElement::setValueFromRenderer(const String& value) // Input types that support the selection API do *not* sanitize their // user input in order to retain parity between what's in the model and // what's on the screen. - ASSERT(m_inputType->supportsSelectionAPI() || value == sanitizeValue(value) || sanitizeValue(value).isEmpty()); + ASSERT(m_inputType->supportsSelectionAPI() || value == sanitizeValue(value) || sanitizeValue(value)->isEmpty()); // Workaround for bug where trailing \n is included in the result of textContent. // The assert macro above may also be simplified by removing the expression @@ -1614,7 +1614,7 @@ String HTMLInputElement::visibleValue() const return m_inputType->visibleValue(); } -String HTMLInputElement::sanitizeValue(const String& proposedValue) const +ValueOrReference HTMLInputElement::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { if (proposedValue.isNull()) return proposedValue; @@ -2076,7 +2076,7 @@ void HTMLInputElement::minLengthAttributeChanged(const AtomString& newValue) void HTMLInputElement::updateValueIfNeeded() { auto newValue = sanitizeValue(m_valueIfDirty); - ASSERT(!m_valueIfDirty.isNull() || newValue.isNull()); + ASSERT(!m_valueIfDirty.isNull() || newValue->isNull()); if (newValue != m_valueIfDirty) setValue(newValue); } diff --git a/Source/WebCore/html/HTMLInputElement.h b/Source/WebCore/html/HTMLInputElement.h index 785a7f5aa9af7..b5a5ab60f72f8 100644 --- a/Source/WebCore/html/HTMLInputElement.h +++ b/Source/WebCore/html/HTMLInputElement.h @@ -26,6 +26,7 @@ #include "HTMLTextFormControlElement.h" #include +#include namespace WebCore { @@ -83,7 +84,7 @@ class HTMLInputElement final : public HTMLTextFormControlElement { WEBCORE_EXPORT const AtomString& defaultValue() const; WEBCORE_EXPORT void setDefaultValue(const AtomString&); WEBCORE_EXPORT void setType(const AtomString&); - WEBCORE_EXPORT String value() const final; + WEBCORE_EXPORT ValueOrReference value() const final; WEBCORE_EXPORT ExceptionOr setValue(const String&, TextFieldEventBehavior = DispatchNoEvent, TextControlSetValueSelection = TextControlSetValueSelection::SetSelectionToEnd) final; void setValueForUser(const String& value) { setValue(value, DispatchInputAndChangeEvent); } WEBCORE_EXPORT WallTime valueAsDate() const; @@ -207,7 +208,7 @@ class HTMLInputElement final : public HTMLTextFormControlElement { String placeholder() const; - String sanitizeValue(const String&) const; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const; String localizeValue(const String&) const; diff --git a/Source/WebCore/html/HTMLTextAreaElement.cpp b/Source/WebCore/html/HTMLTextAreaElement.cpp index ce8fb4562018f..380166f5c3498 100644 --- a/Source/WebCore/html/HTMLTextAreaElement.cpp +++ b/Source/WebCore/html/HTMLTextAreaElement.cpp @@ -113,7 +113,7 @@ void HTMLTextAreaElement::childrenChanged(const ChildChange& change) HTMLElement::childrenChanged(change); setLastChangeWasNotUserEdit(); if (m_isDirty) - setInnerTextValue(value()); + setInnerTextValue(String { value() }); else setNonDirtyValue(defaultValue(), TextControlSetValueSelection::Clamp); } @@ -212,7 +212,7 @@ bool HTMLTextAreaElement::appendFormData(DOMFormData& formData) Ref protectedThis(*this); document().updateLayout(); - formData.append(name(), m_wrap == HardWrap ? valueWithHardLineBreaks() : value()); + formData.append(name(), m_wrap == HardWrap ? valueWithHardLineBreaks() : value().get()); if (auto& dirname = attributeWithoutSynchronization(dirnameAttr); !dirname.isNull()) formData.append(dirname, directionForFormData()); return true; @@ -336,7 +336,7 @@ void HTMLTextAreaElement::updateValue() const const_cast(this)->updatePlaceholderVisibility(); } -String HTMLTextAreaElement::value() const +ValueOrReference HTMLTextAreaElement::value() const { updateValue(); return m_value; @@ -394,7 +394,7 @@ void HTMLTextAreaElement::setValueCommon(const String& newValue, TextFieldEventB } else if (shouldClamp) cacheSelection(std::min(endOfString, selectionStartValue), std::min(endOfString, selectionEndValue), SelectionHasNoDirection); - setTextAsOfLastFormControlChangeEvent(normalizedValue); + setTextAsOfLastFormControlChangeEvent(String(normalizedValue)); if (CheckedPtr cache = document().existingAXObjectCache()) cache->valueChanged(*this); @@ -422,10 +422,10 @@ String HTMLTextAreaElement::validationMessage() const return validationMessageValueMissingText(); if (tooShort()) - return validationMessageTooShortText(value().length(), minLength()); + return validationMessageTooShortText(value()->length(), minLength()); if (tooLong()) - return validationMessageTooLongText(value().length(), maxLength()); + return validationMessageTooLongText(value()->length(), maxLength()); return String(); } @@ -455,7 +455,7 @@ bool HTMLTextAreaElement::valueMissing(StringView value) const if (!(isRequired() && isMutable())) return false; if (value.isNull()) - value = this->value(); + return this->value()->isEmpty(); return value.isEmpty(); } @@ -475,11 +475,8 @@ bool HTMLTextAreaElement::tooShort(StringView value, NeedsToCheckDirtyFlag check if (min <= 0) return false; - if (value.isNull()) - value = this->value(); - // The empty string is excluded from tooShort validation. - unsigned length = value.isNull() ? this->value().length() : computeLengthForAPIValue(value); + unsigned length = value.isNull() ? this->value()->length() : computeLengthForAPIValue(value); return length > 0 && length < static_cast(min); } @@ -494,7 +491,7 @@ bool HTMLTextAreaElement::tooLong(StringView value, NeedsToCheckDirtyFlag check) if (max < 0) return false; - unsigned length = value.isNull() ? this->value().length() : computeLengthForAPIValue(value); + unsigned length = value.isNull() ? this->value()->length() : computeLengthForAPIValue(value); return length > static_cast(max); } diff --git a/Source/WebCore/html/HTMLTextAreaElement.h b/Source/WebCore/html/HTMLTextAreaElement.h index e30a45198f108..57af728553ed7 100644 --- a/Source/WebCore/html/HTMLTextAreaElement.h +++ b/Source/WebCore/html/HTMLTextAreaElement.h @@ -45,9 +45,9 @@ class HTMLTextAreaElement final : public HTMLTextFormControlElement { WEBCORE_EXPORT void setCols(unsigned); WEBCORE_EXPORT String defaultValue() const; WEBCORE_EXPORT void setDefaultValue(String&&); - WEBCORE_EXPORT String value() const final; + WEBCORE_EXPORT ValueOrReference value() const final; WEBCORE_EXPORT ExceptionOr setValue(const String&, TextFieldEventBehavior = DispatchNoEvent, TextControlSetValueSelection = TextControlSetValueSelection::SetSelectionToEnd) final; - unsigned textLength() const { return value().length(); } + unsigned textLength() const { return value()->length(); } String validationMessage() const final; void setSelectionRangeForBindings(unsigned start, unsigned end, const String& direction); @@ -75,7 +75,7 @@ class HTMLTextAreaElement final : public HTMLTextFormControlElement { HTMLElement* placeholderElement() const final { return m_placeholder.get(); } RefPtr protectedPlaceholderElement() const; void updatePlaceholderText() final; - bool isEmptyValue() const final { return value().isEmpty(); } + bool isEmptyValue() const final { return value()->isEmpty(); } bool isOptionalFormControl() const final { return !isRequiredFormControl(); } bool isRequiredFormControl() const final { return isRequired(); } diff --git a/Source/WebCore/html/HTMLTextFormControlElement.cpp b/Source/WebCore/html/HTMLTextFormControlElement.cpp index d2e976c046220..8e03c655123b1 100644 --- a/Source/WebCore/html/HTMLTextFormControlElement.cpp +++ b/Source/WebCore/html/HTMLTextFormControlElement.cpp @@ -96,7 +96,7 @@ Node::InsertedIntoAncestorResult HTMLTextFormControlElement::insertedIntoAncesto InsertedIntoAncestorResult InsertedIntoAncestorResult = HTMLFormControlElement::insertedIntoAncestor(insertionType, parentOfInsertedTree); if (insertionType.connectedToDocument) { String initialValue = value(); - setTextAsOfLastFormControlChangeEvent(initialValue.isNull() ? emptyString() : initialValue); + setTextAsOfLastFormControlChangeEvent(initialValue.isNull() ? String(emptyString()) : WTFMove(initialValue)); } return InsertedIntoAncestorResult; } @@ -223,13 +223,14 @@ String HTMLTextFormControlElement::selectedText() const { if (!isTextField()) return String(); - return value().substring(selectionStart(), selectionEnd() - selectionStart()); + return value()->substring(selectionStart(), selectionEnd() - selectionStart()); } void HTMLTextFormControlElement::dispatchFormControlChangeEvent() { - if (m_textAsOfLastFormControlChangeEvent != value()) { - setTextAsOfLastFormControlChangeEvent(value()); + auto value = this->value(); + if (m_textAsOfLastFormControlChangeEvent != value.get()) { + setTextAsOfLastFormControlChangeEvent(String { value }); dispatchChangeEvent(); } setChangedSinceLastFormControlChangeEvent(false); diff --git a/Source/WebCore/html/HTMLTextFormControlElement.h b/Source/WebCore/html/HTMLTextFormControlElement.h index c0b11ab8929d6..6224ebecfd510 100644 --- a/Source/WebCore/html/HTMLTextFormControlElement.h +++ b/Source/WebCore/html/HTMLTextFormControlElement.h @@ -26,6 +26,7 @@ #include "HTMLFormControlElement.h" #include "PointerEventTypeNames.h" +#include namespace WebCore { @@ -94,7 +95,7 @@ class HTMLTextFormControlElement : public HTMLFormControlElement { void dispatchFormControlChangeEvent() final; void scheduleSelectEvent(); - virtual String value() const = 0; + virtual ValueOrReference value() const = 0; virtual ExceptionOr setValue(const String&, TextFieldEventBehavior = DispatchNoEvent, TextControlSetValueSelection = TextControlSetValueSelection::SetSelectionToEnd) = 0; virtual RefPtr innerTextElement() const = 0; @@ -110,7 +111,7 @@ class HTMLTextFormControlElement : public HTMLFormControlElement { String directionForFormData() const; - void setTextAsOfLastFormControlChangeEvent(const String& text) { m_textAsOfLastFormControlChangeEvent = text; } + void setTextAsOfLastFormControlChangeEvent(String&& text) { m_textAsOfLastFormControlChangeEvent = WTFMove(text); } WEBCORE_EXPORT virtual bool isInnerTextElementEditable() const; diff --git a/Source/WebCore/html/InputType.cpp b/Source/WebCore/html/InputType.cpp index 3526d7ed6bd3f..ebded83780a0f 100644 --- a/Source/WebCore/html/InputType.cpp +++ b/Source/WebCore/html/InputType.cpp @@ -780,7 +780,7 @@ bool InputType::rendererIsNeeded() return true; } -String InputType::fallbackValue() const +ValueOrReference InputType::fallbackValue() const { return String(); } @@ -855,7 +855,7 @@ bool InputType::isEmptyValue() const return true; } -String InputType::sanitizeValue(const String& proposedValue) const +ValueOrReference InputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { return proposedValue; } diff --git a/Source/WebCore/html/InputType.h b/Source/WebCore/html/InputType.h index 5c22644c2cc03..ce4f0a6b31a93 100644 --- a/Source/WebCore/html/InputType.h +++ b/Source/WebCore/html/InputType.h @@ -40,6 +40,8 @@ #include #include #include +#include +#include namespace WebCore { @@ -226,7 +228,7 @@ class InputType : public RefCounted { // DOM property functions. - virtual String fallbackValue() const; // Checked last, if both internal storage and value attribute are missing. + virtual ValueOrReference fallbackValue() const; // Checked last, if both internal storage and value attribute are missing. virtual String defaultValue() const; // Checked after even fallbackValue, only when the valueWithDefault function is called. virtual WallTime valueAsDate() const; virtual ExceptionOr setValueAsDate(WallTime) const; @@ -269,9 +271,8 @@ class InputType : public RefCounted { // though typeMismatchFor() does something for them because of value sanitization. virtual bool typeMismatch() const { return false; } - // Return value of null string means "use the default value". // This function must be called only by HTMLInputElement::sanitizeValue(). - virtual String sanitizeValue(const String&) const; + virtual ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const; // Event handlers. diff --git a/Source/WebCore/html/MonthInputType.cpp b/Source/WebCore/html/MonthInputType.cpp index 5b62560bf09b8..ad69a145b3296 100644 --- a/Source/WebCore/html/MonthInputType.cpp +++ b/Source/WebCore/html/MonthInputType.cpp @@ -68,7 +68,7 @@ DateComponentsType MonthInputType::dateType() const WallTime MonthInputType::valueAsDate() const { ASSERT(element()); - auto date = parseToDateComponents(element()->value()); + auto date = parseToDateComponents(protectedElement()->value().get()); if (!date) return WallTime::nan(); double msec = date->millisecondsSinceEpoch(); diff --git a/Source/WebCore/html/NumberInputType.cpp b/Source/WebCore/html/NumberInputType.cpp index 60a224afb043e..64fd7c48b04fe 100644 --- a/Source/WebCore/html/NumberInputType.cpp +++ b/Source/WebCore/html/NumberInputType.cpp @@ -107,7 +107,7 @@ void NumberInputType::setValue(const String& sanitizedValue, bool valueChanged, double NumberInputType::valueAsDouble() const { ASSERT(element()); - return parseToDoubleForNumberType(element()->value()); + return parseToDoubleForNumberType(element()->value().get()); } ExceptionOr NumberInputType::setValueAsDouble(double newValue, TextFieldEventBehavior eventBehavior) const @@ -258,11 +258,13 @@ String NumberInputType::convertFromVisibleValue(const String& visibleValue) cons return element()->locale().convertFromLocalizedNumber(visibleValue); } -String NumberInputType::sanitizeValue(const String& proposedValue) const +ValueOrReference NumberInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { if (proposedValue.isEmpty()) return proposedValue; - return std::isfinite(parseToDoubleForNumberType(proposedValue)) ? proposedValue : emptyString(); + if (std::isfinite(parseToDoubleForNumberType(proposedValue))) + return proposedValue; + return emptyString(); } bool NumberInputType::hasBadInput() const diff --git a/Source/WebCore/html/NumberInputType.h b/Source/WebCore/html/NumberInputType.h index d5927eb208615..886d49fd34a47 100644 --- a/Source/WebCore/html/NumberInputType.h +++ b/Source/WebCore/html/NumberInputType.h @@ -66,7 +66,7 @@ class NumberInputType final : public TextFieldInputType { String localizeValue(const String&) const final; String visibleValue() const final; String convertFromVisibleValue(const String&) const final; - String sanitizeValue(const String&) const final; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const final; String badInputText() const final; bool supportsPlaceholder() const final; void attributeChanged(const QualifiedName&) final; diff --git a/Source/WebCore/html/RadioNodeList.cpp b/Source/WebCore/html/RadioNodeList.cpp index 7490649ef0658..1099243f10f44 100644 --- a/Source/WebCore/html/RadioNodeList.cpp +++ b/Source/WebCore/html/RadioNodeList.cpp @@ -63,7 +63,7 @@ static RefPtr nonEmptyRadioButton(Node& node) if (!inputElement) return nullptr; - if (!inputElement->isRadioButton() || inputElement->value().isEmpty()) + if (!inputElement->isRadioButton() || inputElement->value()->isEmpty()) return nullptr; return inputElement; } diff --git a/Source/WebCore/html/RangeInputType.cpp b/Source/WebCore/html/RangeInputType.cpp index 835868b6879f4..3999e0df2a977 100644 --- a/Source/WebCore/html/RangeInputType.cpp +++ b/Source/WebCore/html/RangeInputType.cpp @@ -96,7 +96,7 @@ const AtomString& RangeInputType::formControlType() const double RangeInputType::valueAsDouble() const { ASSERT(element()); - return parseToDoubleForNumberType(element()->value()); + return parseToDoubleForNumberType(element()->value().get()); } ExceptionOr RangeInputType::setValueAsDecimal(const Decimal& newValue, TextFieldEventBehavior eventBehavior) const @@ -352,19 +352,19 @@ void RangeInputType::setValue(const String& value, bool valueChanged, TextFieldE if (eventBehavior == DispatchNoEvent) { ASSERT(element()); - element()->setTextAsOfLastFormControlChangeEvent(value); + element()->setTextAsOfLastFormControlChangeEvent(String(value)); } if (hasCreatedShadowSubtree()) typedSliderThumbElement().setPositionFromValue(); } -String RangeInputType::fallbackValue() const +ValueOrReference RangeInputType::fallbackValue() const { return serializeForNumberType(createStepRange(AnyStepHandling::Reject).defaultValue()); } -String RangeInputType::sanitizeValue(const String& proposedValue) const +ValueOrReference RangeInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { StepRange stepRange(createStepRange(AnyStepHandling::Reject)); const Decimal proposedNumericValue = parseToNumber(proposedValue, stepRange.defaultValue()); diff --git a/Source/WebCore/html/RangeInputType.h b/Source/WebCore/html/RangeInputType.h index 55cb78478d4e7..ad09577d63ff0 100644 --- a/Source/WebCore/html/RangeInputType.h +++ b/Source/WebCore/html/RangeInputType.h @@ -63,8 +63,8 @@ class RangeInputType final : public InputType { bool accessKeyAction(bool sendMouseEvents) final; void attributeChanged(const QualifiedName&) final; void setValue(const String&, bool valueChanged, TextFieldEventBehavior, TextControlSetValueSelection) final; - String fallbackValue() const final; - String sanitizeValue(const String& proposedValue) const final; + ValueOrReference fallbackValue() const final; + ValueOrReference sanitizeValue(const String& proposedValue LIFETIME_BOUND) const final; bool shouldRespectListAttribute() final; HTMLElement* sliderThumbElement() const final; HTMLElement* sliderTrackElement() const final; diff --git a/Source/WebCore/html/SearchInputType.cpp b/Source/WebCore/html/SearchInputType.cpp index 8a9bfefae2ad8..6e6fb43a97b26 100644 --- a/Source/WebCore/html/SearchInputType.cpp +++ b/Source/WebCore/html/SearchInputType.cpp @@ -236,7 +236,7 @@ float SearchInputType::decorationWidth() const void SearchInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior, TextControlSetValueSelection selection) { - bool emptinessChanged = valueChanged && sanitizedValue.isEmpty() != element()->value().isEmpty(); + bool emptinessChanged = valueChanged && sanitizedValue.isEmpty() != protectedElement()->value()->isEmpty(); BaseTextInputType::setValue(sanitizedValue, valueChanged, eventBehavior, selection); diff --git a/Source/WebCore/html/TextFieldInputType.cpp b/Source/WebCore/html/TextFieldInputType.cpp index 7bfa3f436a7a8..b16673b58fd8e 100644 --- a/Source/WebCore/html/TextFieldInputType.cpp +++ b/Source/WebCore/html/TextFieldInputType.cpp @@ -175,7 +175,7 @@ void TextFieldInputType::setValue(const String& sanitizedValue, bool valueChange } if (!input->focused()) - input->setTextAsOfLastFormControlChangeEvent(sanitizedValue); + input->setTextAsOfLastFormControlChangeEvent(String(sanitizedValue)); if (UserTypingGestureIndicator::processingUserTypingGesture()) didSetValueByUserEdit(); @@ -498,7 +498,7 @@ void TextFieldInputType::createDataListDropdownIndicator() #endif // ENABLE(DATALIST_ELEMENT) -static String limitLength(const String& string, unsigned maxLength) +static ValueOrReference limitLength(const String& string LIFETIME_BOUND, unsigned maxLength) { if (LIKELY(string.length() <= maxLength)) return string; @@ -584,7 +584,7 @@ static bool isAutoFillButtonTypeChanged(const AtomString& attribute, AutoFillBut return false; } -String TextFieldInputType::sanitizeValue(const String& proposedValue) const +ValueOrReference TextFieldInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { if (LIKELY(!containsHTMLLineBreak(proposedValue))) return limitLength(proposedValue, HTMLInputElement::maxEffectiveLength); @@ -593,7 +593,7 @@ String TextFieldInputType::sanitizeValue(const String& proposedValue) const auto proposedValueWithoutLineBreaks = proposedValue.removeCharacters([](auto character) { return isHTMLLineBreak(character); }); - return limitLength(WTFMove(proposedValueWithoutLineBreaks), HTMLInputElement::maxEffectiveLength); + return String(limitLength(proposedValueWithoutLineBreaks, HTMLInputElement::maxEffectiveLength)); } void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent& event) diff --git a/Source/WebCore/html/TextFieldInputType.h b/Source/WebCore/html/TextFieldInputType.h index f88985a77a7ff..89f4bbbad469d 100644 --- a/Source/WebCore/html/TextFieldInputType.h +++ b/Source/WebCore/html/TextFieldInputType.h @@ -82,7 +82,7 @@ class TextFieldInputType : public InputType, protected SpinButtonOwner, protecte void handleBlurEvent() final; void setValue(const String&, bool valueChanged, TextFieldEventBehavior, TextControlSetValueSelection) override; void updateInnerTextValue() final; - String sanitizeValue(const String&) const override; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const override; virtual String convertFromVisibleValue(const String&) const; virtual void didSetValueByUserEdit(); diff --git a/Source/WebCore/html/URLInputType.cpp b/Source/WebCore/html/URLInputType.cpp index 1c0b557087789..f802bbd4d9140 100644 --- a/Source/WebCore/html/URLInputType.cpp +++ b/Source/WebCore/html/URLInputType.cpp @@ -60,9 +60,9 @@ String URLInputType::typeMismatchText() const return validationMessageTypeMismatchForURLText(); } -String URLInputType::sanitizeValue(const String& proposedValue) const +ValueOrReference URLInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { - return BaseTextInputType::sanitizeValue(proposedValue).trim(isASCIIWhitespace); + return BaseTextInputType::sanitizeValue(proposedValue)->trim(isASCIIWhitespace); } } // namespace WebCore diff --git a/Source/WebCore/html/URLInputType.h b/Source/WebCore/html/URLInputType.h index 4e674b813c729..da05a9030f82e 100644 --- a/Source/WebCore/html/URLInputType.h +++ b/Source/WebCore/html/URLInputType.h @@ -52,7 +52,7 @@ class URLInputType final : public BaseTextInputType { const AtomString& formControlType() const final; String typeMismatchText() const final; - String sanitizeValue(const String&) const final; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const final; }; } // namespace WebCore diff --git a/Source/WebCore/html/parser/AtomHTMLToken.h b/Source/WebCore/html/parser/AtomHTMLToken.h index 5b00ac1bd9ee6..e0c4723b9acf6 100644 --- a/Source/WebCore/html/parser/AtomHTMLToken.h +++ b/Source/WebCore/html/parser/AtomHTMLToken.h @@ -228,17 +228,20 @@ inline void AtomHTMLToken::initializeAttributes(const HTMLToken::AttributeList& if (!size) return; - HashSet addedAttributes; + Vector addedAttributes; addedAttributes.reserveInitialCapacity(size); + m_attributes = WTF::compactMap(attributes, [&](auto& attribute) -> std::optional { if (attribute.name.isEmpty()) return std::nullopt; auto qualifiedName = HTMLNameCache::makeAttributeQualifiedName(attribute.name); - if (addedAttributes.add(qualifiedName.localName()).isNewEntry) - return Attribute(WTFMove(qualifiedName), HTMLNameCache::makeAttributeValue(attribute.value)); - m_hasDuplicateAttribute = true; - return std::nullopt; + if (UNLIKELY(!insertInUniquedSortedVector(addedAttributes, qualifiedName.localName().impl()))) { + m_hasDuplicateAttribute = true; + return std::nullopt; + } + + return Attribute(WTFMove(qualifiedName), HTMLNameCache::makeAttributeValue(attribute.value)); }); } diff --git a/Source/WebCore/html/parser/HTMLDocumentParserFastPath.cpp b/Source/WebCore/html/parser/HTMLDocumentParserFastPath.cpp index ea7fa367d9644..faef458f35d6c 100644 --- a/Source/WebCore/html/parser/HTMLDocumentParserFastPath.cpp +++ b/Source/WebCore/html/parser/HTMLDocumentParserFastPath.cpp @@ -125,15 +125,6 @@ template static inline bool isCharAfterUnquotedAttribute return character == ' ' || character == '>' || isASCIIWhitespace(character); } -template static bool insertInUniquedSortedVector(Vector& vector, const T& value) -{ - auto it = std::lower_bound(vector.begin(), vector.end(), value); - if (UNLIKELY(it != vector.end() && *it == value)) - return false; - vector.insert(it - vector.begin(), value); - return true; -} - #define FOR_EACH_SUPPORTED_TAG(APPLY) \ APPLY(a, A) \ APPLY(b, B) \ @@ -808,11 +799,12 @@ class HTMLFastPathParser { return didFail(HTMLFastPathResult::FailedParsingAttributes); } skipWhile(m_parsingBuffer); - AtomString attributeValue { emptyAtom() }; + AtomString attributeValue; if (skipExactly(m_parsingBuffer, '=')) { attributeValue = scanAttributeValue(); skipWhile(m_parsingBuffer); - } + } else + attributeValue = emptyAtom(); if (UNLIKELY(!insertInUniquedSortedVector(m_attributeNames, attributeName.localName().impl()))) { hasDuplicateAttributes = true; continue; diff --git a/Source/WebCore/html/shadow/DateTimeFieldElement.cpp b/Source/WebCore/html/shadow/DateTimeFieldElement.cpp index f73a155baf955..97b08acb6c8bc 100644 --- a/Source/WebCore/html/shadow/DateTimeFieldElement.cpp +++ b/Source/WebCore/html/shadow/DateTimeFieldElement.cpp @@ -186,7 +186,9 @@ AtomString DateTimeFieldElement::localeIdentifier() const String DateTimeFieldElement::visibleValue() const { - return hasValue() ? value() : placeholderValue(); + if (hasValue()) + return value(); + return placeholderValue(); } void DateTimeFieldElement::updateVisibleValue(EventBehavior eventBehavior) diff --git a/Source/WebCore/html/shadow/DateTimeFieldElement.h b/Source/WebCore/html/shadow/DateTimeFieldElement.h index d3736316d96c0..c4a930cd73b02 100644 --- a/Source/WebCore/html/shadow/DateTimeFieldElement.h +++ b/Source/WebCore/html/shadow/DateTimeFieldElement.h @@ -31,6 +31,7 @@ #include "HTMLDivElement.h" #include +#include #include namespace WebCore { @@ -84,7 +85,7 @@ class DateTimeFieldElement : public HTMLDivElement { virtual void setValueAsInteger(int, EventBehavior = DispatchNoEvent) = 0; virtual void stepDown() = 0; virtual void stepUp() = 0; - virtual String value() const = 0; + virtual ValueOrReference value() const = 0; virtual String placeholderValue() const = 0; protected: diff --git a/Source/WebCore/html/shadow/DateTimeNumericFieldElement.cpp b/Source/WebCore/html/shadow/DateTimeNumericFieldElement.cpp index e284300c4f837..a94e019ac68df 100644 --- a/Source/WebCore/html/shadow/DateTimeNumericFieldElement.cpp +++ b/Source/WebCore/html/shadow/DateTimeNumericFieldElement.cpp @@ -154,9 +154,11 @@ void DateTimeNumericFieldElement::stepUp() setValueAsIntegerByStepping(newValue); } -String DateTimeNumericFieldElement::value() const +ValueOrReference DateTimeNumericFieldElement::value() const { - return m_hasValue ? formatValue(m_value) : emptyString(); + if (m_hasValue) + return formatValue(m_value); + return emptyString(); } String DateTimeNumericFieldElement::placeholderValue() const diff --git a/Source/WebCore/html/shadow/DateTimeNumericFieldElement.h b/Source/WebCore/html/shadow/DateTimeNumericFieldElement.h index b5e05fb72862f..064f7979b6e6f 100644 --- a/Source/WebCore/html/shadow/DateTimeNumericFieldElement.h +++ b/Source/WebCore/html/shadow/DateTimeNumericFieldElement.h @@ -65,7 +65,7 @@ class DateTimeNumericFieldElement : public DateTimeFieldElement { private: // DateTimeFieldElement functions: void adjustMinInlineSize(RenderStyle&) const final; - String value() const final; + ValueOrReference value() const final; String placeholderValue() const final; void handleKeyboardEvent(KeyboardEvent&) final; void handleBlurEvent(Event&) final; diff --git a/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.cpp b/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.cpp index ebf058511ca6d..ce82edc40e122 100644 --- a/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.cpp +++ b/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.cpp @@ -99,9 +99,11 @@ void DateTimeSymbolicFieldElement::stepUp() setValueAsInteger(newValue, DispatchInputAndChangeEvents); } -String DateTimeSymbolicFieldElement::value() const +ValueOrReference DateTimeSymbolicFieldElement::value() const { - return hasValue() ? m_symbols[m_selectedIndex] : emptyString(); + if (hasValue()) + return m_symbols[m_selectedIndex]; + return emptyString(); } String DateTimeSymbolicFieldElement::placeholderValue() const diff --git a/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.h b/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.h index 38686a11409b5..99ddc2b1cf50c 100644 --- a/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.h +++ b/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.h @@ -51,7 +51,7 @@ class DateTimeSymbolicFieldElement : public DateTimeFieldElement, public TypeAhe void adjustMinInlineSize(RenderStyle&) const final; void stepDown() final; void stepUp() final; - String value() const final; + ValueOrReference value() const final; String placeholderValue() const final; void handleKeyboardEvent(KeyboardEvent&) final; diff --git a/Source/WebCore/html/shadow/SliderThumbElement.cpp b/Source/WebCore/html/shadow/SliderThumbElement.cpp index 3ca01596014df..e492b133d6405 100644 --- a/Source/WebCore/html/shadow/SliderThumbElement.cpp +++ b/Source/WebCore/html/shadow/SliderThumbElement.cpp @@ -72,7 +72,7 @@ WTF_MAKE_TZONE_OR_ISO_ALLOCATED_IMPL(SliderContainerElement); inline static Decimal sliderPosition(HTMLInputElement& element) { const StepRange stepRange(element.createStepRange(AnyStepHandling::Reject)); - const Decimal oldValue = parseToDecimalForNumberType(element.value(), stepRange.defaultValue()); + const Decimal oldValue = parseToDecimalForNumberType(element.value().get(), stepRange.defaultValue()); return stepRange.proportionFromValue(stepRange.clampValue(oldValue)); } diff --git a/Source/WebCore/html/shadow/TextControlInnerElements.cpp b/Source/WebCore/html/shadow/TextControlInnerElements.cpp index 999a3870ecc25..38f8c5c286633 100644 --- a/Source/WebCore/html/shadow/TextControlInnerElements.cpp +++ b/Source/WebCore/html/shadow/TextControlInnerElements.cpp @@ -337,7 +337,7 @@ std::optional SearchFieldCancelButtonElement::resolveCusto { auto elementStyle = resolveStyle(resolutionContext); Ref inputElement = downcast(*shadowHost()); - elementStyle.style->setVisibility(elementStyle.style->usedVisibility() == Visibility::Hidden || inputElement->value().isEmpty() ? Visibility::Hidden : Visibility::Visible); + elementStyle.style->setVisibility(elementStyle.style->usedVisibility() == Visibility::Hidden || inputElement->value()->isEmpty() ? Visibility::Hidden : Visibility::Visible); if (shadowHostStyle && searchFieldStyleHasExplicitlySpecifiedTextFieldAppearance(*shadowHostStyle)) elementStyle.style->setDisplay(DisplayType::None); diff --git a/Source/WebCore/layout/formattingContexts/inline/InlineItemsBuilder.cpp b/Source/WebCore/layout/formattingContexts/inline/InlineItemsBuilder.cpp index 6d4187315242e..3ed6694f64576 100644 --- a/Source/WebCore/layout/formattingContexts/inline/InlineItemsBuilder.cpp +++ b/Source/WebCore/layout/formattingContexts/inline/InlineItemsBuilder.cpp @@ -696,7 +696,7 @@ void InlineItemsBuilder::computeInlineTextItemWidths(InlineItemList& inlineItemL bool InlineItemsBuilder::buildInlineItemListForTextFromBreakingPositionsCache(const InlineTextBox& inlineTextBox, InlineItemList& inlineItemList) { - auto text = inlineTextBox.content(); + auto& text = inlineTextBox.content(); auto* breakingPositions = TextBreakingPositionCache::singleton().get({ text, { inlineTextBox.style() }, m_securityOrigin.data() }); if (!breakingPositions) return false; diff --git a/Source/WebCore/layout/formattingContexts/inline/text/TextUtil.cpp b/Source/WebCore/layout/formattingContexts/inline/text/TextUtil.cpp index c56e0889c728b..f6ab7946bf606 100644 --- a/Source/WebCore/layout/formattingContexts/inline/text/TextUtil.cpp +++ b/Source/WebCore/layout/formattingContexts/inline/text/TextUtil.cpp @@ -59,7 +59,7 @@ InlineLayoutUnit TextUtil::width(const InlineTextBox& inlineTextBox, const FontC if (inlineTextBox.isCombined()) return fontCascade.size(); - auto text = inlineTextBox.content(); + auto& text = inlineTextBox.content(); ASSERT(to <= text.length()); auto hasKerningOrLigatures = fontCascade.enableKerning() || fontCascade.requiresShaping(); // The "non-whitespace" + "whitespace" pattern is very common for inline content and since most of the "non-whitespace" runs end up with @@ -121,9 +121,8 @@ InlineLayoutUnit TextUtil::width(const InlineTextItem& inlineTextItem, const Fon InlineLayoutUnit TextUtil::trailingWhitespaceWidth(const InlineTextBox& inlineTextBox, const FontCascade& fontCascade, size_t startPosition, size_t endPosition) { - auto text = inlineTextBox.content(); ASSERT(endPosition > startPosition + 1); - ASSERT(text[endPosition - 1] == space); + ASSERT(inlineTextBox.content()[endPosition - 1] == space); return width(inlineTextBox, fontCascade, startPosition, endPosition, { }, UseTrailingWhitespaceMeasuringOptimization::Yes) - width(inlineTextBox, fontCascade, startPosition, endPosition - 1, { }, UseTrailingWhitespaceMeasuringOptimization::No); } @@ -166,7 +165,7 @@ TextUtil::FallbackFontList TextUtil::fallbackFontsForText(StringView textContent { TextUtil::FallbackFontList fallbackFonts; - auto collectFallbackFonts = [&](const auto& textRun) { + auto collectFallbackFonts = [&](auto&& textRun) { if (textRun.text().isEmpty()) return; @@ -237,7 +236,7 @@ TextUtil::WordBreakLeft TextUtil::breakWord(const InlineTextBox& inlineTextBox, { ASSERT(availableWidth >= 0); ASSERT(length); - auto text = inlineTextBox.content(); + auto& text = inlineTextBox.content(); if (UNLIKELY(!textWidth)) { ASSERT_NOT_REACHED(); @@ -541,7 +540,7 @@ bool TextUtil::containsStrongDirectionalityText(StringView text) size_t TextUtil::firstUserPerceivedCharacterLength(const InlineTextBox& inlineTextBox, size_t startPosition, size_t length) { - auto textContent = inlineTextBox.content(); + auto& textContent = inlineTextBox.content(); RELEASE_ASSERT(!textContent.isEmpty()); if (textContent.is8Bit()) diff --git a/Source/WebCore/page/InteractionRegion.cpp b/Source/WebCore/page/InteractionRegion.cpp index c645b38977be7..aff53be55609c 100644 --- a/Source/WebCore/page/InteractionRegion.cpp +++ b/Source/WebCore/page/InteractionRegion.cpp @@ -163,7 +163,7 @@ static bool shouldAllowNonInteractiveCursorForElement(const Element& element) #endif if (RefPtr textElement = dynamicDowncast(element)) - return !textElement->focused() || !textElement->lastChangeWasUserEdit() || textElement->value().isEmpty(); + return !textElement->focused() || !textElement->lastChangeWasUserEdit() || textElement->value()->isEmpty(); if (is(element)) return true; diff --git a/Source/WebCore/platform/graphics/FontCascade.cpp b/Source/WebCore/platform/graphics/FontCascade.cpp index 9dcc984005dd0..35bb15f43a457 100644 --- a/Source/WebCore/platform/graphics/FontCascade.cpp +++ b/Source/WebCore/platform/graphics/FontCascade.cpp @@ -61,7 +61,7 @@ FontCascade::FontCascade() FontCascade::FontCascade(FontCascadeDescription&& description) : m_fontDescription(WTFMove(description)) , m_generation(++lastFontCascadeGeneration) - , m_useBackslashAsYenSymbol(FontCache::forCurrentThread().useBackslashAsYenSignForFamily(m_fontDescription.firstFamily())) + , m_useBackslashAsYenSymbol(computeUseBackslashAsYenSymbol()) , m_enableKerning(computeEnableKerning()) , m_requiresShaping(computeRequiresShaping()) { @@ -72,7 +72,7 @@ FontCascade::FontCascade(FontCascadeDescription&& description, const FontCascade : m_fontDescription(WTFMove(description)) , m_spacing(other.m_spacing) , m_generation(++lastFontCascadeGeneration) - , m_useBackslashAsYenSymbol(FontCache::forCurrentThread().useBackslashAsYenSignForFamily(m_fontDescription.firstFamily())) + , m_useBackslashAsYenSymbol(computeUseBackslashAsYenSymbol()) , m_enableKerning(computeEnableKerning()) , m_requiresShaping(computeRequiresShaping()) { @@ -1267,6 +1267,11 @@ bool FontCascade::isLoadingCustomFonts() const return fonts && fonts->isLoadingCustomFonts(); } +bool FontCascade::computeUseBackslashAsYenSymbol() const +{ + return FontCache::forCurrentThread().useBackslashAsYenSignForFamily(m_fontDescription.firstFamily()); +} + enum class GlyphUnderlineType : uint8_t { SkipDescenders, SkipGlyph, diff --git a/Source/WebCore/platform/graphics/FontCascade.h b/Source/WebCore/platform/graphics/FontCascade.h index 181becbced7a2..35108ea3db5f6 100644 --- a/Source/WebCore/platform/graphics/FontCascade.h +++ b/Source/WebCore/platform/graphics/FontCascade.h @@ -124,6 +124,7 @@ class FontCascade final : public CanMakeWeakPtr, public CanMakeChec WEBCORE_EXPORT bool operator==(const FontCascade& other) const; const FontCascadeDescription& fontDescription() const { return m_fontDescription; } + FontCascadeDescription& mutableFontDescription() const { return m_fontDescription; } float size() const { return fontDescription().computedSize(); } @@ -326,8 +327,14 @@ class FontCascade final : public CanMakeWeakPtr, public CanMakeChec static ResolvedEmojiPolicy resolveEmojiPolicy(FontVariantEmoji, char32_t); + void updateUseBackslashAsYenSymbol() { m_useBackslashAsYenSymbol = computeUseBackslashAsYenSymbol(); } + void updateEnableKerning() { m_enableKerning = computeEnableKerning(); } + void updateRequiresShaping() { m_requiresShaping = computeRequiresShaping(); } + private: + bool computeUseBackslashAsYenSymbol() const; + bool advancedTextRenderingMode() const { return m_fontDescription.textRenderingMode() != TextRenderingMode::OptimizeSpeed; diff --git a/Source/WebCore/platform/graphics/WidthIterator.cpp b/Source/WebCore/platform/graphics/WidthIterator.cpp index 2eb2a44b08e66..64eed73286a97 100644 --- a/Source/WebCore/platform/graphics/WidthIterator.cpp +++ b/Source/WebCore/platform/graphics/WidthIterator.cpp @@ -105,7 +105,7 @@ inline auto WidthIterator::applyFontTransforms(GlyphBuffer& glyphBuffer, unsigne }); if (iterator == charactersTreatedAsSpace.end() || iterator->stringOffset != characterIndex) continue; - const auto& originalAdvances = *iterator; + auto& originalAdvances = *iterator; setWidth(*glyphBuffer.advances(i), originalAdvances.advance); } charactersTreatedAsSpace.clear(); @@ -373,7 +373,7 @@ inline void WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuff { // The core logic here needs to match FontCascade::widthForTextUsingSimplifiedMeasuring() FloatRect bounds; - auto fontDescription = m_font->fontDescription(); + auto& fontDescription = m_font->fontDescription(); Ref primaryFont = m_font->primaryFont(); AdvanceInternalState advanceInternalState(glyphBuffer, primaryFont, textIterator.currentIndex()); SmallCapsState smallCapsState(fontDescription); diff --git a/Source/WebCore/rendering/RenderSearchField.cpp b/Source/WebCore/rendering/RenderSearchField.cpp index eeb669eaf71c0..06a5e1d78f011 100644 --- a/Source/WebCore/rendering/RenderSearchField.cpp +++ b/Source/WebCore/rendering/RenderSearchField.cpp @@ -212,7 +212,7 @@ void RenderSearchField::updateCancelButtonVisibility() const Visibility RenderSearchField::visibilityForCancelButton() const { - return (style().usedVisibility() == Visibility::Hidden || inputElement().value().isEmpty()) ? Visibility::Hidden : Visibility::Visible; + return (style().usedVisibility() == Visibility::Hidden || inputElement().value()->isEmpty()) ? Visibility::Hidden : Visibility::Visible; } const AtomString& RenderSearchField::autosaveName() const diff --git a/Source/WebCore/rendering/style/RenderStyle.cpp b/Source/WebCore/rendering/style/RenderStyle.cpp index dcdea3301601d..43870c7324dd8 100644 --- a/Source/WebCore/rendering/style/RenderStyle.cpp +++ b/Source/WebCore/rendering/style/RenderStyle.cpp @@ -2727,6 +2727,17 @@ const FontCascadeDescription& RenderStyle::fontDescription() const return m_inheritedData->fontCascade.fontDescription(); } +FontCascadeDescription& RenderStyle::mutableFontDescriptionWithoutUpdate() +{ + auto& cascade = m_inheritedData.access().fontData.access().fontCascade; + return cascade.mutableFontDescription(); +} + +FontCascade& RenderStyle::mutableFontCascadeWithoutUpdate() +{ + return m_inheritedData.access().fontData.access().fontCascade; +} + float RenderStyle::specifiedFontSize() const { return fontDescription().specifiedSize(); diff --git a/Source/WebCore/rendering/style/RenderStyle.h b/Source/WebCore/rendering/style/RenderStyle.h index f457c3f91653a..3a97a5c3a74ec 100644 --- a/Source/WebCore/rendering/style/RenderStyle.h +++ b/Source/WebCore/rendering/style/RenderStyle.h @@ -549,6 +549,10 @@ class RenderStyle final : public CanMakeCheckedPtr { WEBCORE_EXPORT const FontCascade& fontCascade() const; WEBCORE_EXPORT const FontMetrics& metricsOfPrimaryFont() const; WEBCORE_EXPORT const FontCascadeDescription& fontDescription() const; + + WEBCORE_EXPORT FontCascade& mutableFontCascadeWithoutUpdate(); + WEBCORE_EXPORT FontCascadeDescription& mutableFontDescriptionWithoutUpdate(); + float specifiedFontSize() const; float computedFontSize() const; std::pair fontAndGlyphOrientation(); diff --git a/Source/WebCore/style/StyleAdjuster.cpp b/Source/WebCore/style/StyleAdjuster.cpp index 11f9a5053410c..27e184fabe85d 100644 --- a/Source/WebCore/style/StyleAdjuster.cpp +++ b/Source/WebCore/style/StyleAdjuster.cpp @@ -950,7 +950,7 @@ void Adjuster::adjustForSiteSpecificQuirks(RenderStyle& style) const if (m_document.quirks().needsWeChatScrollingQuirk()) { static MainThreadNeverDestroyed class1("tree-select"_s); static MainThreadNeverDestroyed class2("v-tree-select"_s); - const auto& flexBasis = style.flexBasis(); + auto& flexBasis = style.flexBasis(); if (style.minHeight().isAuto() && style.display() == DisplayType::Flex && style.flexGrow() == 1 @@ -1114,7 +1114,7 @@ auto Adjuster::adjustmentForTextAutosizing(const RenderStyle& style, const Eleme adjustmentForTextAutosizing.newLineHeight = minimumLineHeight; }; - auto fontDescription = style.fontDescription(); + auto& fontDescription = style.fontDescription(); auto initialComputedFontSize = fontDescription.computedSize(); auto specifiedFontSize = fontDescription.specifiedSize(); bool isCandidate = style.isIdempotentTextAutosizingCandidate(newStatus); diff --git a/Source/WebCore/style/StyleBuilder.cpp b/Source/WebCore/style/StyleBuilder.cpp index 396dd1f207eca..bef9cb6913038 100644 --- a/Source/WebCore/style/StyleBuilder.cpp +++ b/Source/WebCore/style/StyleBuilder.cpp @@ -240,9 +240,9 @@ void Builder::applyCustomPropertyImpl(const AtomString& name, const PropertyCasc SetForScope scopedLinkMatchMutation(m_state.m_linkMatch, SelectorChecker::MatchDefault); applyProperty(CSSPropertyCustom, *resolvedValue, SelectorChecker::MatchDefault); - m_state.m_inProgressCustomProperties.remove(name); - m_state.m_appliedCustomProperties.add(name); - m_state.m_inCycleCustomProperties.formUnion(WTFMove(savedInCycleProperties)); + AtomString takenName = m_state.m_inProgressCustomProperties.take(name); + m_state.m_appliedCustomProperties.add(WTFMove(takenName)); + m_state.m_inCycleCustomProperties.formUnion(savedInCycleProperties); } inline void Builder::applyCascadeProperty(const PropertyCascade::Property& property) diff --git a/Source/WebCore/style/StyleBuilderCustom.h b/Source/WebCore/style/StyleBuilderCustom.h index 5ea3af2a01cb8..90b7252984949 100644 --- a/Source/WebCore/style/StyleBuilderCustom.h +++ b/Source/WebCore/style/StyleBuilderCustom.h @@ -624,12 +624,10 @@ inline void BuilderCustom::applyValueWebkitLocale(BuilderState& builderState, CS { auto& primitiveValue = downcast(value); - FontCascadeDescription fontDescription = builderState.fontDescription(); if (primitiveValue.valueID() == CSSValueAuto) - fontDescription.setSpecifiedLocale(nullAtom()); + builderState.setFontDescriptionSpecifiedLocale(nullAtom()); else - fontDescription.setSpecifiedLocale(AtomString { primitiveValue.stringValue() }); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionSpecifiedLocale(AtomString { primitiveValue.stringValue() }); } inline void BuilderCustom::applyValueWritingMode(BuilderState& builderState, CSSValue& value) @@ -742,33 +740,29 @@ inline void BuilderCustom::applyValueBoxShadow(BuilderState& builderState, CSSVa inline void BuilderCustom::applyInitialFontFamily(BuilderState& builderState) { - auto fontDescription = builderState.fontDescription(); + auto& fontDescription = builderState.fontDescription(); auto initialDesc = FontCascadeDescription(); // We need to adjust the size to account for the generic family change from monospace to non-monospace. if (fontDescription.useFixedDefaultSize()) { if (CSSValueID sizeIdentifier = fontDescription.keywordSizeAsIdentifier()) - builderState.setFontSize(fontDescription, Style::fontSizeForKeyword(sizeIdentifier, false, builderState.document())); + builderState.setFontDescriptionFontSize(Style::fontSizeForKeyword(sizeIdentifier, false, builderState.document())); } if (!initialDesc.firstFamily().isEmpty()) - fontDescription.setFamilies(initialDesc.families()); - - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionFamilies(initialDesc.families()); } inline void BuilderCustom::applyInheritFontFamily(BuilderState& builderState) { - auto fontDescription = builderState.fontDescription(); auto parentFontDescription = builderState.parentStyle().fontDescription(); - fontDescription.setFamilies(parentFontDescription.families()); - fontDescription.setIsSpecifiedFont(parentFontDescription.isSpecifiedFont()); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionFamilies(parentFontDescription.families()); + builderState.setFontDescriptionIsSpecifiedFont(parentFontDescription.isSpecifiedFont()); } inline void BuilderCustom::applyValueFontFamily(BuilderState& builderState, CSSValue& value) { - auto fontDescription = builderState.fontDescription(); + auto& fontDescription = builderState.fontDescription(); // Before mapping in a new font-family property, we should reset the generic family. bool oldFamilyUsedFixedDefaultSize = fontDescription.useFixedDefaultSize(); @@ -782,7 +776,7 @@ inline void BuilderCustom::applyValueFontFamily(BuilderState& builderState, CSSV } AtomString family = SystemFontDatabase::singleton().systemFontShorthandFamily(CSSPropertyParserHelpers::lowerFontShorthand(valueID)); ASSERT(!family.isEmpty()); - fontDescription.setIsSpecifiedFont(false); + builderState.setFontDescriptionIsSpecifiedFont(false); families = Vector::from(WTFMove(family)); } else { auto& valueList = downcast(value); @@ -803,7 +797,7 @@ inline void BuilderCustom::applyValueFontFamily(BuilderState& builderState, CSSV if (family.isNull()) return std::nullopt; if (isFirstFont) { - fontDescription.setIsSpecifiedFont(!isGenericFamily); + builderState.setFontDescriptionIsSpecifiedFont(!isGenericFamily); isFirstFont = false; } return family; @@ -812,14 +806,12 @@ inline void BuilderCustom::applyValueFontFamily(BuilderState& builderState, CSSV return; } - fontDescription.setFamilies(families); + builderState.setFontDescriptionFamilies(families); if (fontDescription.useFixedDefaultSize() != oldFamilyUsedFixedDefaultSize) { if (CSSValueID sizeIdentifier = fontDescription.keywordSizeAsIdentifier()) - builderState.setFontSize(fontDescription, Style::fontSizeForKeyword(sizeIdentifier, !oldFamilyUsedFixedDefaultSize, builderState.document())); + builderState.setFontDescriptionFontSize(Style::fontSizeForKeyword(sizeIdentifier, !oldFamilyUsedFixedDefaultSize, builderState.document())); } - - builderState.setFontDescription(WTFMove(fontDescription)); } inline void BuilderCustom::applyInitialBorderBottomLeftRadius(BuilderState& builderState) @@ -1333,22 +1325,18 @@ inline void BuilderCustom::applyValueContent(BuilderState& builderState, CSSValu inline void BuilderCustom::applyInheritFontVariantLigatures(BuilderState& builderState) { - auto fontDescription = builderState.fontDescription(); - fontDescription.setVariantCommonLigatures(builderState.parentFontDescription().variantCommonLigatures()); - fontDescription.setVariantDiscretionaryLigatures(builderState.parentFontDescription().variantDiscretionaryLigatures()); - fontDescription.setVariantHistoricalLigatures(builderState.parentFontDescription().variantHistoricalLigatures()); - fontDescription.setVariantContextualAlternates(builderState.parentFontDescription().variantContextualAlternates()); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionVariantCommonLigatures(builderState.parentFontDescription().variantCommonLigatures()); + builderState.setFontDescriptionVariantDiscretionaryLigatures(builderState.parentFontDescription().variantDiscretionaryLigatures()); + builderState.setFontDescriptionVariantHistoricalLigatures(builderState.parentFontDescription().variantHistoricalLigatures()); + builderState.setFontDescriptionVariantContextualAlternates(builderState.parentFontDescription().variantContextualAlternates()); } inline void BuilderCustom::applyInitialFontVariantLigatures(BuilderState& builderState) { - auto fontDescription = builderState.fontDescription(); - fontDescription.setVariantCommonLigatures(FontVariantLigatures::Normal); - fontDescription.setVariantDiscretionaryLigatures(FontVariantLigatures::Normal); - fontDescription.setVariantHistoricalLigatures(FontVariantLigatures::Normal); - fontDescription.setVariantContextualAlternates(FontVariantLigatures::Normal); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionVariantCommonLigatures(FontVariantLigatures::Normal); + builderState.setFontDescriptionVariantDiscretionaryLigatures(FontVariantLigatures::Normal); + builderState.setFontDescriptionVariantHistoricalLigatures(FontVariantLigatures::Normal); + builderState.setFontDescriptionVariantContextualAlternates(FontVariantLigatures::Normal); } inline void BuilderCustom::applyValueFontVariantLigatures(BuilderState& builderState, CSSValue& value) @@ -1357,35 +1345,29 @@ inline void BuilderCustom::applyValueFontVariantLigatures(BuilderState& builderS applyInitialFontVariantLigatures(builderState); return; } - auto fontDescription = builderState.fontDescription(); auto variantLigatures = extractFontVariantLigatures(value); - fontDescription.setVariantCommonLigatures(variantLigatures.commonLigatures); - fontDescription.setVariantDiscretionaryLigatures(variantLigatures.discretionaryLigatures); - fontDescription.setVariantHistoricalLigatures(variantLigatures.historicalLigatures); - fontDescription.setVariantContextualAlternates(variantLigatures.contextualAlternates); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionVariantCommonLigatures(variantLigatures.commonLigatures); + builderState.setFontDescriptionVariantDiscretionaryLigatures(variantLigatures.discretionaryLigatures); + builderState.setFontDescriptionVariantHistoricalLigatures(variantLigatures.historicalLigatures); + builderState.setFontDescriptionVariantContextualAlternates(variantLigatures.contextualAlternates); } inline void BuilderCustom::applyInheritFontVariantNumeric(BuilderState& builderState) { - auto fontDescription = builderState.fontDescription(); - fontDescription.setVariantNumericFigure(builderState.parentFontDescription().variantNumericFigure()); - fontDescription.setVariantNumericSpacing(builderState.parentFontDescription().variantNumericSpacing()); - fontDescription.setVariantNumericFraction(builderState.parentFontDescription().variantNumericFraction()); - fontDescription.setVariantNumericOrdinal(builderState.parentFontDescription().variantNumericOrdinal()); - fontDescription.setVariantNumericSlashedZero(builderState.parentFontDescription().variantNumericSlashedZero()); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionVariantNumericFigure(builderState.parentFontDescription().variantNumericFigure()); + builderState.setFontDescriptionVariantNumericSpacing(builderState.parentFontDescription().variantNumericSpacing()); + builderState.setFontDescriptionVariantNumericFraction(builderState.parentFontDescription().variantNumericFraction()); + builderState.setFontDescriptionVariantNumericOrdinal(builderState.parentFontDescription().variantNumericOrdinal()); + builderState.setFontDescriptionVariantNumericSlashedZero(builderState.parentFontDescription().variantNumericSlashedZero()); } inline void BuilderCustom::applyInitialFontVariantNumeric(BuilderState& builderState) { - auto fontDescription = builderState.fontDescription(); - fontDescription.setVariantNumericFigure(FontVariantNumericFigure::Normal); - fontDescription.setVariantNumericSpacing(FontVariantNumericSpacing::Normal); - fontDescription.setVariantNumericFraction(FontVariantNumericFraction::Normal); - fontDescription.setVariantNumericOrdinal(FontVariantNumericOrdinal::Normal); - fontDescription.setVariantNumericSlashedZero(FontVariantNumericSlashedZero::Normal); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionVariantNumericFigure(FontVariantNumericFigure::Normal); + builderState.setFontDescriptionVariantNumericSpacing(FontVariantNumericSpacing::Normal); + builderState.setFontDescriptionVariantNumericFraction(FontVariantNumericFraction::Normal); + builderState.setFontDescriptionVariantNumericOrdinal(FontVariantNumericOrdinal::Normal); + builderState.setFontDescriptionVariantNumericSlashedZero(FontVariantNumericSlashedZero::Normal); } inline void BuilderCustom::applyValueFontVariantNumeric(BuilderState& builderState, CSSValue& value) @@ -1394,32 +1376,26 @@ inline void BuilderCustom::applyValueFontVariantNumeric(BuilderState& builderSta applyInitialFontVariantNumeric(builderState); return; } - auto fontDescription = builderState.fontDescription(); auto variantNumeric = extractFontVariantNumeric(value); - fontDescription.setVariantNumericFigure(variantNumeric.figure); - fontDescription.setVariantNumericSpacing(variantNumeric.spacing); - fontDescription.setVariantNumericFraction(variantNumeric.fraction); - fontDescription.setVariantNumericOrdinal(variantNumeric.ordinal); - fontDescription.setVariantNumericSlashedZero(variantNumeric.slashedZero); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionVariantNumericFigure(variantNumeric.figure); + builderState.setFontDescriptionVariantNumericSpacing(variantNumeric.spacing); + builderState.setFontDescriptionVariantNumericFraction(variantNumeric.fraction); + builderState.setFontDescriptionVariantNumericOrdinal(variantNumeric.ordinal); + builderState.setFontDescriptionVariantNumericSlashedZero(variantNumeric.slashedZero); } inline void BuilderCustom::applyInheritFontVariantEastAsian(BuilderState& builderState) { - auto fontDescription = builderState.fontDescription(); - fontDescription.setVariantEastAsianVariant(builderState.parentFontDescription().variantEastAsianVariant()); - fontDescription.setVariantEastAsianWidth(builderState.parentFontDescription().variantEastAsianWidth()); - fontDescription.setVariantEastAsianRuby(builderState.parentFontDescription().variantEastAsianRuby()); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionVariantEastAsianVariant(builderState.parentFontDescription().variantEastAsianVariant()); + builderState.setFontDescriptionVariantEastAsianWidth(builderState.parentFontDescription().variantEastAsianWidth()); + builderState.setFontDescriptionVariantEastAsianRuby(builderState.parentFontDescription().variantEastAsianRuby()); } inline void BuilderCustom::applyInitialFontVariantEastAsian(BuilderState& builderState) { - auto fontDescription = builderState.fontDescription(); - fontDescription.setVariantEastAsianVariant(FontVariantEastAsianVariant::Normal); - fontDescription.setVariantEastAsianWidth(FontVariantEastAsianWidth::Normal); - fontDescription.setVariantEastAsianRuby(FontVariantEastAsianRuby::Normal); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionVariantEastAsianVariant(FontVariantEastAsianVariant::Normal); + builderState.setFontDescriptionVariantEastAsianWidth(FontVariantEastAsianWidth::Normal); + builderState.setFontDescriptionVariantEastAsianRuby(FontVariantEastAsianRuby::Normal); } inline void BuilderCustom::applyValueFontVariantEastAsian(BuilderState& builderState, CSSValue& value) @@ -1428,26 +1404,20 @@ inline void BuilderCustom::applyValueFontVariantEastAsian(BuilderState& builderS applyInitialFontVariantEastAsian(builderState); return; } - auto fontDescription = builderState.fontDescription(); auto variantEastAsian = extractFontVariantEastAsian(value); - fontDescription.setVariantEastAsianVariant(variantEastAsian.variant); - fontDescription.setVariantEastAsianWidth(variantEastAsian.width); - fontDescription.setVariantEastAsianRuby(variantEastAsian.ruby); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionVariantEastAsianVariant(variantEastAsian.variant); + builderState.setFontDescriptionVariantEastAsianWidth(variantEastAsian.width); + builderState.setFontDescriptionVariantEastAsianRuby(variantEastAsian.ruby); } inline void BuilderCustom::applyInheritFontVariantAlternates(BuilderState& builderState) { - auto fontDescription = builderState.fontDescription(); - fontDescription.setVariantAlternates(builderState.parentFontDescription().variantAlternates()); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionVariantAlternates(builderState.parentFontDescription().variantAlternates()); } inline void BuilderCustom::applyInitialFontVariantAlternates(BuilderState& builderState) { - auto fontDescription = builderState.fontDescription(); - fontDescription.setVariantAlternates(FontVariantAlternates::Normal()); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionVariantAlternates(FontVariantAlternates::Normal()); } inline void BuilderCustom::applyValueFontVariantAlternates(BuilderState& builderState, CSSValue& value) @@ -1501,10 +1471,8 @@ inline void BuilderCustom::applyInheritFontSize(BuilderState& builderState) if (size < 0) return; - auto fontDescription = builderState.fontDescription(); - fontDescription.setKeywordSize(parentFontDescription.keywordSize()); - builderState.setFontSize(fontDescription, size); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionKeywordSize(parentFontDescription.keywordSize()); + builderState.setFontDescriptionFontSize(size); } // When the CSS keyword "larger" is used, this function will attempt to match within the keyword @@ -1577,8 +1545,8 @@ inline void BuilderCustom::applyValueFontStyle(BuilderState& state, CSSValue& va inline void BuilderCustom::applyValueFontSize(BuilderState& builderState, CSSValue& value) { - auto fontDescription = builderState.fontDescription(); - fontDescription.setKeywordSizeFromIdentifier(CSSValueInvalid); + auto& fontDescription = builderState.fontDescription(); + builderState.setFontDescriptionKeywordSizeFromIdentifier(CSSValueInvalid); float parentSize = builderState.parentStyle().fontDescription().specifiedSize(); bool parentIsAbsoluteSize = builderState.parentStyle().fontDescription().isAbsoluteSize(); @@ -1586,7 +1554,7 @@ inline void BuilderCustom::applyValueFontSize(BuilderState& builderState, CSSVal auto& primitiveValue = downcast(value); float size = 0; if (CSSValueID ident = primitiveValue.valueID()) { - fontDescription.setIsAbsoluteSize((parentIsAbsoluteSize && (ident == CSSValueLarger || ident == CSSValueSmaller || ident == CSSValueWebkitRubyText)) || CSSPropertyParserHelpers::isSystemFontShorthand(ident)); + builderState.setFontDescriptionIsAbsoluteSize((parentIsAbsoluteSize && (ident == CSSValueLarger || ident == CSSValueSmaller || ident == CSSValueWebkitRubyText)) || CSSPropertyParserHelpers::isSystemFontShorthand(ident)); if (CSSPropertyParserHelpers::isSystemFontShorthand(ident)) size = SystemFontDatabase::singleton().systemFontShorthandSize(CSSPropertyParserHelpers::lowerFontShorthand(ident)); @@ -1601,7 +1569,7 @@ inline void BuilderCustom::applyValueFontSize(BuilderState& builderState, CSSVal case CSSValueXxLarge: case CSSValueXxxLarge: size = Style::fontSizeForKeyword(ident, fontDescription.useFixedDefaultSize(), builderState.document()); - fontDescription.setKeywordSizeFromIdentifier(ident); + builderState.setFontDescriptionKeywordSizeFromIdentifier(ident); break; case CSSValueLarger: size = largerFontSize(parentSize); @@ -1616,7 +1584,7 @@ inline void BuilderCustom::applyValueFontSize(BuilderState& builderState, CSSVal break; } } else { - fontDescription.setIsAbsoluteSize(parentIsAbsoluteSize || !primitiveValue.isParentFontRelativeLength()); + builderState.setFontDescriptionIsAbsoluteSize(parentIsAbsoluteSize || !primitiveValue.isParentFontRelativeLength()); if (primitiveValue.isLength()) { auto conversionData = builderState.cssToLengthConversionData().copyForFontSize(); size = primitiveValue.computeLength(conversionData); @@ -1632,15 +1600,12 @@ inline void BuilderCustom::applyValueFontSize(BuilderState& builderState, CSSVal if (size < 0) return; - builderState.setFontSize(fontDescription, std::min(maximumAllowedFontSize, size)); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionFontSize(std::min(maximumAllowedFontSize, size)); } inline void BuilderCustom::applyValueFontSizeAdjust(BuilderState& builderState, CSSValue& value) { - auto fontDescription = builderState.fontDescription(); - fontDescription.setFontSizeAdjust(BuilderConverter::convertFontSizeAdjust(builderState, value)); - builderState.setFontDescription(WTFMove(fontDescription)); + builderState.setFontDescriptionFontSizeAdjust(BuilderConverter::convertFontSizeAdjust(builderState, value)); } inline void BuilderCustom::applyInitialGridTemplateAreas(BuilderState& builderState) diff --git a/Source/WebCore/style/StyleBuilderState.h b/Source/WebCore/style/StyleBuilderState.h index 789926ca13959..3f4eb558a2c64 100644 --- a/Source/WebCore/style/StyleBuilderState.h +++ b/Source/WebCore/style/StyleBuilderState.h @@ -28,19 +28,28 @@ #include "CSSToLengthConversionData.h" #include "CSSToStyleMap.h" #include "CascadeLevel.h" +#include "FontTaggedSettings.h" #include "PropertyCascade.h" #include "RuleSet.h" #include "SelectorChecker.h" +#include "TextFlags.h" #include +#include namespace WebCore { class FilterOperations; class FontCascadeDescription; +class FontSelectionValue; class RenderStyle; class StyleColor; class StyleImage; class StyleResolver; +class TextAutospace; +class TextSpacingTrim; + +struct FontPalette; +struct FontSizeAdjust; namespace Style { @@ -53,7 +62,7 @@ enum class ForVisitedLink : bool { No, Yes }; enum class ApplyValueType : uint8_t { Value, Initial, Inherit }; struct BuilderContext { - Ref document; + const Ref document; const RenderStyle& parentStyle; const RenderStyle* rootElementStyle = nullptr; RefPtr element = nullptr; @@ -74,8 +83,6 @@ class BuilderState { const Document& document() const { return m_context.document.get(); } const Element* element() const { return m_context.element.get(); } - inline void setFontDescription(FontCascadeDescription&&); - void setFontSize(FontCascadeDescription&, float size); inline void setZoom(float); inline void setUsedZoom(float); inline void setWritingMode(WritingMode); @@ -115,6 +122,51 @@ class BuilderState { CSSPropertyID cssPropertyID() const; + // FIXME: Copying a FontCascadeDescription is really inefficient. Migrate all callers to + // setFontDescriptionXXX() variants below, then remove these functions. + inline void setFontDescription(FontCascadeDescription&&); + void setFontSize(FontCascadeDescription&, float size); + + void setFontDescriptionKeywordSizeFromIdentifier(CSSValueID); + void setFontDescriptionIsAbsoluteSize(bool); + void setFontDescriptionFontSize(float); + void setFontDescriptionFamilies(RefCountedFixedVector&); + void setFontDescriptionFamilies(Vector&); + void setFontDescriptionIsSpecifiedFont(bool); + void setFontDescriptionFeatureSettings(FontFeatureSettings&&); + void setFontDescriptionFontPalette(const FontPalette&); + void setFontDescriptionFontSizeAdjust(FontSizeAdjust); + void setFontDescriptionFontSmoothing(FontSmoothingMode); + void setFontDescriptionFontSynthesisSmallCaps(FontSynthesisLonghandValue); + void setFontDescriptionFontSynthesisStyle(FontSynthesisLonghandValue); + void setFontDescriptionFontSynthesisWeight(FontSynthesisLonghandValue); + void setFontDescriptionKerning(Kerning); + void setFontDescriptionOpticalSizing(FontOpticalSizing); + void setFontDescriptionSpecifiedLocale(const AtomString&); + void setFontDescriptionTextAutospace(TextAutospace); + void setFontDescriptionTextRenderingMode(TextRenderingMode); + void setFontDescriptionTextSpacingTrim(TextSpacingTrim); + void setFontDescriptionVariantCaps(FontVariantCaps); + void setFontDescriptionVariantEmoji(FontVariantEmoji); + void setFontDescriptionVariantPosition(FontVariantPosition); + void setFontDescriptionVariationSettings(FontVariationSettings&&); + void setFontDescriptionWeight(FontSelectionValue); + void setFontDescriptionWidth(FontSelectionValue); + void setFontDescriptionVariantAlternates(const FontVariantAlternates&); + void setFontDescriptionVariantEastAsianVariant(FontVariantEastAsianVariant); + void setFontDescriptionVariantEastAsianWidth(FontVariantEastAsianWidth); + void setFontDescriptionVariantEastAsianRuby(FontVariantEastAsianRuby); + void setFontDescriptionKeywordSize(unsigned); + void setFontDescriptionVariantCommonLigatures(FontVariantLigatures); + void setFontDescriptionVariantDiscretionaryLigatures(FontVariantLigatures); + void setFontDescriptionVariantHistoricalLigatures(FontVariantLigatures); + void setFontDescriptionVariantContextualAlternates(FontVariantLigatures); + void setFontDescriptionVariantNumericFigure(FontVariantNumericFigure); + void setFontDescriptionVariantNumericSpacing(FontVariantNumericSpacing); + void setFontDescriptionVariantNumericFraction(FontVariantNumericFraction); + void setFontDescriptionVariantNumericOrdinal(FontVariantNumericOrdinal); + void setFontDescriptionVariantNumericSlashedZero(FontVariantNumericSlashedZero); + private: // See the comment in maybeUpdateFontForLetterSpacing() about why this needs to be a friend. friend void maybeUpdateFontForLetterSpacing(BuilderState&, CSSValue&); diff --git a/Source/WebCore/style/StyleBuilderStateInlines.h b/Source/WebCore/style/StyleBuilderStateInlines.h index eae127aed5418..a375fb3ff8009 100644 --- a/Source/WebCore/style/StyleBuilderStateInlines.h +++ b/Source/WebCore/style/StyleBuilderStateInlines.h @@ -25,19 +25,421 @@ #pragma once +#include "RenderStyle.h" #include "RenderStyleSetters.h" #include "StyleBuilderState.h" +#include "StyleFontSizeFunctions.h" namespace WebCore { namespace Style { inline const FontCascadeDescription& BuilderState::fontDescription() { return m_style.fontDescription(); } +inline void BuilderState::setFontDescription(FontCascadeDescription&& description) { m_fontDirty |= m_style.setFontDescription(WTFMove(description)); } + inline const FontCascadeDescription& BuilderState::parentFontDescription() { return parentStyle().fontDescription(); } inline void BuilderState::setUsedZoom(float zoom) { m_fontDirty |= m_style.setUsedZoom(zoom); } -inline void BuilderState::setFontDescription(FontCascadeDescription&& description) { m_fontDirty |= m_style.setFontDescription(WTFMove(description)); } inline void BuilderState::setTextOrientation(TextOrientation orientation) { m_fontDirty |= m_style.setTextOrientation(orientation); } inline void BuilderState::setWritingMode(WritingMode mode) { m_fontDirty |= m_style.setWritingMode(mode); } inline void BuilderState::setZoom(float zoom) { m_fontDirty |= m_style.setZoom(zoom); } +inline void BuilderState::setFontDescriptionKeywordSizeFromIdentifier(CSSValueID identifier) +{ + if (m_style.fontDescription().keywordSizeAsIdentifier() == identifier) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setKeywordSizeFromIdentifier(identifier); +} + +inline void BuilderState::setFontDescriptionIsAbsoluteSize(bool isAbsoluteSize) +{ + if (m_style.fontDescription().isAbsoluteSize() == isAbsoluteSize) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setIsAbsoluteSize(isAbsoluteSize); +} + +inline void BuilderState::setFontDescriptionFontSize(float fontSize) +{ + if (m_style.fontDescription().specifiedSize() != fontSize) { + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setSpecifiedSize(fontSize); + } + + SUPPRESS_UNCOUNTED_ARG auto computedSize = Style::computedFontSizeFromSpecifiedSize(fontSize, m_style.fontDescription().isAbsoluteSize(), useSVGZoomRules(), &style(), document()); + if (m_style.fontDescription().computedSize() != computedSize) { + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setComputedSize(computedSize); + } +} + +inline void BuilderState::setFontDescriptionFamilies(RefCountedFixedVector& families) +{ + if (m_style.fontDescription().families() == families) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setFamilies(families); + fontCascade.updateUseBackslashAsYenSymbol(); +} + +inline void BuilderState::setFontDescriptionFamilies(Vector& families) +{ + if (m_style.fontDescription().families() == families) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setFamilies(families); + fontCascade.updateUseBackslashAsYenSymbol(); +} + +inline void BuilderState::setFontDescriptionIsSpecifiedFont(bool isSpecifiedFont) +{ + if (m_style.fontDescription().isSpecifiedFont() == isSpecifiedFont) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setIsSpecifiedFont(isSpecifiedFont); +} + +inline void BuilderState::setFontDescriptionFeatureSettings(FontFeatureSettings&& featureSettings) +{ + if (m_style.fontDescription().featureSettings() == featureSettings) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setFeatureSettings(WTFMove(featureSettings)); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionFontPalette(const FontPalette& fontPalette) +{ + if (m_style.fontDescription().fontPalette() == fontPalette) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setFontPalette(fontPalette); +} + +inline void BuilderState::setFontDescriptionFontSizeAdjust(FontSizeAdjust fontSizeAdjust) +{ + if (m_style.fontDescription().fontSizeAdjust() == fontSizeAdjust) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setFontSizeAdjust(WTFMove(fontSizeAdjust)); +} + +inline void BuilderState::setFontDescriptionFontSmoothing(FontSmoothingMode fontSmoothing) +{ + if (m_style.fontDescription().fontSmoothing() == fontSmoothing) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setFontSmoothing(WTFMove(fontSmoothing)); +} + +inline void BuilderState::setFontDescriptionFontSynthesisSmallCaps(FontSynthesisLonghandValue fontSynthesisSmallCaps) +{ + if (m_style.fontDescription().fontSynthesisSmallCaps() == fontSynthesisSmallCaps) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setFontSynthesisSmallCaps(WTFMove(fontSynthesisSmallCaps)); +} + +inline void BuilderState::setFontDescriptionFontSynthesisStyle(FontSynthesisLonghandValue fontSynthesisStyle) +{ + if (m_style.fontDescription().fontSynthesisStyle() == fontSynthesisStyle) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setFontSynthesisStyle(fontSynthesisStyle); +} + +inline void BuilderState::setFontDescriptionFontSynthesisWeight(FontSynthesisLonghandValue fontSynthesisWeight) +{ + if (m_style.fontDescription().fontSynthesisWeight() == fontSynthesisWeight) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setFontSynthesisWeight(fontSynthesisWeight); +} + +inline void BuilderState::setFontDescriptionKerning(Kerning kerning) +{ + if (m_style.fontDescription().kerning() == kerning) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setKerning(kerning); + fontCascade.updateEnableKerning(); +} + +inline void BuilderState::setFontDescriptionOpticalSizing(FontOpticalSizing opticalSizing) +{ + if (m_style.fontDescription().opticalSizing() == opticalSizing) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setOpticalSizing(opticalSizing); +} + +inline void BuilderState::setFontDescriptionSpecifiedLocale(const AtomString& specifiedLocale) +{ + if (m_style.fontDescription().specifiedLocale() == specifiedLocale) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setSpecifiedLocale(specifiedLocale); +} + +inline void BuilderState::setFontDescriptionTextAutospace(TextAutospace textAutospace) +{ + if (m_style.fontDescription().textAutospace() == textAutospace) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setTextAutospace(textAutospace); +} + +inline void BuilderState::setFontDescriptionTextRenderingMode(TextRenderingMode textRenderingMode) +{ + if (m_style.fontDescription().textRenderingMode() == textRenderingMode) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setTextRenderingMode(textRenderingMode); + fontCascade.updateEnableKerning(); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionTextSpacingTrim(TextSpacingTrim textSpacingTrim) +{ + if (m_style.fontDescription().textSpacingTrim() == textSpacingTrim) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setTextSpacingTrim(textSpacingTrim); +} + +inline void BuilderState::setFontDescriptionVariantCaps(FontVariantCaps variantCaps) +{ + if (m_style.fontDescription().variantCaps() == variantCaps) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantCaps(variantCaps); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionVariantEmoji(FontVariantEmoji variantEmoji) +{ + if (m_style.fontDescription().variantEmoji() == variantEmoji) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantEmoji(variantEmoji); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionVariantPosition(FontVariantPosition variantPosition) +{ + if (m_style.fontDescription().variantPosition() == variantPosition) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantPosition(variantPosition); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionVariationSettings(FontVariationSettings&& variationSettings) +{ + if (m_style.fontDescription().variationSettings() == variationSettings) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setVariationSettings(WTFMove(variationSettings)); +} + +inline void BuilderState::setFontDescriptionWeight(FontSelectionValue weight) +{ + if (m_style.fontDescription().weight() == weight) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setWeight(weight); +} + +inline void BuilderState::setFontDescriptionWidth(FontSelectionValue width) +{ + if (m_style.fontDescription().stretch() == width) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setStretch(width); +} + +inline void BuilderState::setFontDescriptionVariantAlternates(const FontVariantAlternates& variantAlternates) +{ + if (m_style.fontDescription().variantAlternates() == variantAlternates) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantAlternates(variantAlternates); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionVariantEastAsianVariant(FontVariantEastAsianVariant variantEastAsianVariant) +{ + if (m_style.fontDescription().variantEastAsianVariant() == variantEastAsianVariant) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantEastAsianVariant(variantEastAsianVariant); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionVariantEastAsianWidth(FontVariantEastAsianWidth variantEastAsianWidth) +{ + if (m_style.fontDescription().variantEastAsianWidth() == variantEastAsianWidth) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantEastAsianWidth(variantEastAsianWidth); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionVariantEastAsianRuby(FontVariantEastAsianRuby variantEastAsianRuby) +{ + if (m_style.fontDescription().variantEastAsianRuby() == variantEastAsianRuby) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantEastAsianRuby(variantEastAsianRuby); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionKeywordSize(unsigned keywordSize) +{ + if (m_style.fontDescription().keywordSize() == keywordSize) + return; + + m_fontDirty = true; + m_style.mutableFontDescriptionWithoutUpdate().setKeywordSize(keywordSize); +} + +inline void BuilderState::setFontDescriptionVariantCommonLigatures(FontVariantLigatures variantCommonLigatures) +{ + if (m_style.fontDescription().variantCommonLigatures() == variantCommonLigatures) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantCommonLigatures(variantCommonLigatures); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionVariantDiscretionaryLigatures(FontVariantLigatures variantDiscretionaryLigatures) +{ + if (m_style.fontDescription().variantDiscretionaryLigatures() == variantDiscretionaryLigatures) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantDiscretionaryLigatures(variantDiscretionaryLigatures); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionVariantHistoricalLigatures(FontVariantLigatures variantHistoricalLigatures) +{ + if (m_style.fontDescription().variantHistoricalLigatures() == variantHistoricalLigatures) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantHistoricalLigatures(variantHistoricalLigatures); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionVariantContextualAlternates(FontVariantLigatures variantContextualAlternates) +{ + if (m_style.fontDescription().variantContextualAlternates() == variantContextualAlternates) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantContextualAlternates(variantContextualAlternates); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionVariantNumericFigure(FontVariantNumericFigure variantNumericFigure) +{ + if (m_style.fontDescription().variantNumericFigure() == variantNumericFigure) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantNumericFigure(variantNumericFigure); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionVariantNumericSpacing(FontVariantNumericSpacing variantNumericSpacing) +{ + if (m_style.fontDescription().variantNumericSpacing() == variantNumericSpacing) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantNumericSpacing(variantNumericSpacing); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionVariantNumericFraction(FontVariantNumericFraction variantNumericFraction) +{ + if (m_style.fontDescription().variantNumericFraction() == variantNumericFraction) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantNumericFraction(variantNumericFraction); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionVariantNumericOrdinal(FontVariantNumericOrdinal variantNumericOrdinal) +{ + if (m_style.fontDescription().variantNumericOrdinal() == variantNumericOrdinal) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantNumericOrdinal(variantNumericOrdinal); + fontCascade.updateRequiresShaping(); +} + +inline void BuilderState::setFontDescriptionVariantNumericSlashedZero(FontVariantNumericSlashedZero variantNumericSlashedZero) +{ + if (m_style.fontDescription().variantNumericSlashedZero() == variantNumericSlashedZero) + return; + + m_fontDirty = true; + auto& fontCascade = m_style.mutableFontCascadeWithoutUpdate(); + fontCascade.mutableFontDescription().setVariantNumericSlashedZero(variantNumericSlashedZero); + fontCascade.updateRequiresShaping(); +} + } } diff --git a/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm b/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm index 8d3cc3af2686d..1b0d2895a9210 100644 --- a/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm +++ b/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm @@ -5140,7 +5140,7 @@ static VisiblePositionRange constrainRangeToSelection(const VisiblePositionRange rangeOfInterest.end = closestEditablePositionInElementForAbsolutePoint(*element, roundedIntPoint(request.rect.maxXMaxYCorner())); } else if (RefPtr textFormControlElement = dynamicDowncast(element)) { rangeOfInterest.start = textFormControlElement->visiblePositionForIndex(0); - rangeOfInterest.end = textFormControlElement->visiblePositionForIndex(textFormControlElement->value().length()); + rangeOfInterest.end = textFormControlElement->visiblePositionForIndex(textFormControlElement->value()->length()); } else { rangeOfInterest.start = firstPositionInOrBeforeNode(element.get()); rangeOfInterest.end = lastPositionInOrAfterNode(element.get()); diff --git a/Source/WebKitLegacy/ios/WebCoreSupport/WebVisiblePosition.mm b/Source/WebKitLegacy/ios/WebCoreSupport/WebVisiblePosition.mm index 5e8e725b899ca..b60ffde48c8a0 100644 --- a/Source/WebKitLegacy/ios/WebCoreSupport/WebVisiblePosition.mm +++ b/Source/WebKitLegacy/ios/WebCoreSupport/WebVisiblePosition.mm @@ -535,7 +535,7 @@ - (WebVisiblePosition *)endPosition return [super endPosition]; RenderTextControl& textControl = downcast(*object); - VisiblePosition visiblePosition = textControl.textFormControlElement().visiblePositionForIndex(textControl.textFormControlElement().value().length()); + VisiblePosition visiblePosition = textControl.textFormControlElement().visiblePositionForIndex(textControl.textFormControlElement().value()->length()); return [WebVisiblePosition _wrapVisiblePosition:visiblePosition]; } @@ -562,7 +562,7 @@ - (WebVisiblePosition *)endPosition return [super endPosition]; RenderTextControl& textControl = downcast(*object); - VisiblePosition visiblePosition = textControl.textFormControlElement().visiblePositionForIndex(textControl.textFormControlElement().value().length()); + VisiblePosition visiblePosition = textControl.textFormControlElement().visiblePositionForIndex(textControl.textFormControlElement().value()->length()); return [WebVisiblePosition _wrapVisiblePosition:visiblePosition]; } diff --git a/Source/WebKitLegacy/mac/DOM/DOMHTMLInputElement.mm b/Source/WebKitLegacy/mac/DOM/DOMHTMLInputElement.mm index 86cd359358086..9a6fea70ab4e7 100644 --- a/Source/WebKitLegacy/mac/DOM/DOMHTMLInputElement.mm +++ b/Source/WebKitLegacy/mac/DOM/DOMHTMLInputElement.mm @@ -451,7 +451,7 @@ - (void)setDefaultValue:(NSString *)newDefaultValue - (NSString *)value { WebCore::JSMainThreadNullState state; - return IMPL->value(); + return IMPL->value().get(); } - (void)setValue:(NSString *)newValue diff --git a/Source/WebKitLegacy/mac/DOM/DOMHTMLTextAreaElement.mm b/Source/WebKitLegacy/mac/DOM/DOMHTMLTextAreaElement.mm index 20639ef3c303f..9a139f3331a5b 100644 --- a/Source/WebKitLegacy/mac/DOM/DOMHTMLTextAreaElement.mm +++ b/Source/WebKitLegacy/mac/DOM/DOMHTMLTextAreaElement.mm @@ -213,7 +213,7 @@ - (void)setDefaultValue:(NSString *)newDefaultValue - (NSString *)value { WebCore::JSMainThreadNullState state; - return unwrap(*self).value(); + return unwrap(*self).value().get(); } - (void)setValue:(NSString *)newValue