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