|  | /* | 
|  | * Copyright (C) 2016 Yusuke Suzuki <yusuke.suzuki@sslab.ics.keio.ac.jp> | 
|  | * Copyright (C) 2016 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 "BuiltinNames.h" | 
|  | #include "IntlObject.h" | 
|  | #include "JSBoundFunction.h" | 
|  | #include "JSObject.h" | 
|  | #include "ObjectConstructor.h" | 
|  | #include <unicode/ucol.h> | 
|  |  | 
|  | namespace JSC { | 
|  |  | 
|  | template<typename StringType> | 
|  | static constexpr uint32_t computeTwoCharacters16Code(const StringType& string) | 
|  | { | 
|  | return static_cast<uint16_t>(string.characterAt(0)) | (static_cast<uint32_t>(static_cast<uint16_t>(string.characterAt(1))) << 16); | 
|  | } | 
|  |  | 
|  | template<typename Predicate> String bestAvailableLocale(const String& locale, Predicate predicate) | 
|  | { | 
|  | // BestAvailableLocale (availableLocales, locale) | 
|  | // https://tc39.github.io/ecma402/#sec-bestavailablelocale | 
|  |  | 
|  | String candidate = locale; | 
|  | while (!candidate.isEmpty()) { | 
|  | if (predicate(candidate)) | 
|  | return candidate; | 
|  |  | 
|  | size_t pos = candidate.reverseFind('-'); | 
|  | if (pos == notFound) | 
|  | return String(); | 
|  |  | 
|  | if (pos >= 2 && candidate[pos - 2] == '-') | 
|  | pos -= 2; | 
|  |  | 
|  | candidate = candidate.substring(0, pos); | 
|  | } | 
|  |  | 
|  | return String(); | 
|  | } | 
|  |  | 
|  | template<typename Constructor, typename Factory> | 
|  | JSValue constructIntlInstanceWithWorkaroundForLegacyIntlConstructor(JSGlobalObject* globalObject, JSValue thisValue, Constructor* callee, Factory factory) | 
|  | { | 
|  | // FIXME: Workaround to provide compatibility with ECMA-402 1.0 call/apply patterns. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=153679 | 
|  | VM& vm = globalObject->vm(); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  |  | 
|  | auto* instance = factory(vm); | 
|  | RETURN_IF_EXCEPTION(scope, JSValue()); | 
|  |  | 
|  | if (thisValue.isObject()) { | 
|  | JSObject* thisObject = asObject(thisValue); | 
|  | ASSERT(!callee->template inherits<JSBoundFunction>(vm)); | 
|  | JSValue prototype = callee->getDirect(vm, vm.propertyNames->prototype); // Passed constructors always have `prototype` which cannot be deleted. | 
|  | ASSERT(prototype); | 
|  | bool hasInstance = JSObject::defaultHasInstance(globalObject, thisObject, prototype); | 
|  | RETURN_IF_EXCEPTION(scope, JSValue()); | 
|  | if (hasInstance) { | 
|  | PropertyDescriptor descriptor(instance, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum | PropertyAttribute::DontDelete); | 
|  | scope.release(); | 
|  | thisObject->methodTable(vm)->defineOwnProperty(thisObject, globalObject, vm.propertyNames->builtinNames().intlLegacyConstructedSymbol(), descriptor, true); | 
|  | return thisObject; | 
|  | } | 
|  | } | 
|  | return instance; | 
|  | } | 
|  |  | 
|  | template<typename InstanceType> | 
|  | InstanceType* unwrapForLegacyIntlConstructor(JSGlobalObject* globalObject, JSValue thisValue, JSObject* constructor) | 
|  | { | 
|  | VM& vm = globalObject->vm(); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  |  | 
|  | JSObject* thisObject = jsDynamicCast<JSObject*>(vm, thisValue); | 
|  | if (UNLIKELY(!thisObject)) | 
|  | return nullptr; | 
|  |  | 
|  | auto* instance = jsDynamicCast<InstanceType*>(vm, thisObject); | 
|  | if (LIKELY(instance)) | 
|  | return instance; | 
|  |  | 
|  | ASSERT(!constructor->template inherits<JSBoundFunction>(vm)); | 
|  | JSValue prototype = constructor->getDirect(vm, vm.propertyNames->prototype); // Passed constructors always have `prototype` which cannot be deleted. | 
|  | ASSERT(prototype); | 
|  | bool hasInstance = JSObject::defaultHasInstance(globalObject, thisObject, prototype); | 
|  | RETURN_IF_EXCEPTION(scope, nullptr); | 
|  | if (!hasInstance) | 
|  | return nullptr; | 
|  |  | 
|  | JSValue value = thisObject->get(globalObject, vm.propertyNames->builtinNames().intlLegacyConstructedSymbol()); | 
|  | RETURN_IF_EXCEPTION(scope, nullptr); | 
|  | return jsDynamicCast<InstanceType*>(vm, value); | 
|  | } | 
|  |  | 
|  | template<typename ResultType> | 
|  | ResultType intlOption(JSGlobalObject* globalObject, JSObject* options, PropertyName property, std::initializer_list<std::pair<ASCIILiteral, ResultType>> values, ASCIILiteral notFoundMessage, ResultType fallback) | 
|  | { | 
|  | // GetOption (options, property, type="string", values, fallback) | 
|  | // https://tc39.github.io/ecma402/#sec-getoption | 
|  |  | 
|  | ASSERT(values.size() > 0); | 
|  |  | 
|  | VM& vm = globalObject->vm(); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  |  | 
|  | if (!options) | 
|  | return fallback; | 
|  |  | 
|  | JSValue value = options->get(globalObject, property); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  |  | 
|  | if (!value.isUndefined()) { | 
|  | String stringValue = value.toWTFString(globalObject); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  |  | 
|  | for (const auto& entry : values) { | 
|  | if (entry.first == stringValue) | 
|  | return entry.second; | 
|  | } | 
|  | throwException(globalObject, scope, createRangeError(globalObject, notFoundMessage)); | 
|  | return { }; | 
|  | } | 
|  |  | 
|  | return fallback; | 
|  | } | 
|  |  | 
|  |  | 
|  | ALWAYS_INLINE bool canUseASCIIUCADUCETComparison(UChar character) | 
|  | { | 
|  | return isASCII(character) && ducetLevel1Weights[character]; | 
|  | } | 
|  |  | 
|  | template<typename CharacterType1, typename CharacterType2> | 
|  | UCollationResult compareASCIIWithUCADUCETLevel3(const CharacterType1* characters1, const CharacterType2* characters2, unsigned length) | 
|  | { | 
|  | for (unsigned position = 0; position < length; ++position) { | 
|  | auto lhs = characters1[position]; | 
|  | auto rhs = characters2[position]; | 
|  | uint8_t leftWeight = ducetLevel3Weights[lhs]; | 
|  | uint8_t rightWeight = ducetLevel3Weights[rhs]; | 
|  | if (leftWeight == rightWeight) | 
|  | continue; | 
|  | return leftWeight > rightWeight ? UCOL_GREATER : UCOL_LESS; | 
|  | } | 
|  | return UCOL_EQUAL; | 
|  | } | 
|  |  | 
|  | template<typename CharacterType1, typename CharacterType2> | 
|  | inline UCollationResult compareASCIIWithUCADUCET(const CharacterType1* characters1, unsigned length1, const CharacterType2* characters2, unsigned length2) | 
|  | { | 
|  | bool notSameCharacters = false; | 
|  | unsigned commonLength = std::min(length1, length2); | 
|  | for (unsigned position = 0; position < commonLength; ++position) { | 
|  | auto lhs = characters1[position]; | 
|  | auto rhs = characters2[position]; | 
|  | ASSERT(canUseASCIIUCADUCETComparison(lhs)); | 
|  | ASSERT(canUseASCIIUCADUCETComparison(rhs)); | 
|  | if (lhs == rhs) | 
|  | continue; | 
|  | notSameCharacters = true; | 
|  | uint8_t leftWeight = ducetLevel1Weights[lhs]; | 
|  | uint8_t rightWeight = ducetLevel1Weights[rhs]; | 
|  | if (leftWeight == rightWeight) | 
|  | continue; | 
|  | return leftWeight > rightWeight ? UCOL_GREATER : UCOL_LESS; | 
|  | } | 
|  |  | 
|  | if (length1 == length2) { | 
|  | if (notSameCharacters) | 
|  | return compareASCIIWithUCADUCETLevel3(characters1, characters2, length1); | 
|  | return UCOL_EQUAL; | 
|  | } | 
|  | return length1 > length2 ? UCOL_GREATER : UCOL_LESS; | 
|  | } | 
|  |  | 
|  | // https://tc39.es/ecma402/#sec-getoptionsobject | 
|  | inline JSObject* intlGetOptionsObject(JSGlobalObject* globalObject, JSValue options) | 
|  | { | 
|  | VM& vm = globalObject->vm(); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  | if (options.isUndefined()) | 
|  | return nullptr; | 
|  | if (LIKELY(options.isObject())) | 
|  | return asObject(options); | 
|  | throwTypeError(globalObject, scope, "options argument is not an object or undefined"_s); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | // https://tc39.es/ecma402/#sec-coerceoptionstoobject | 
|  | inline JSObject* intlCoerceOptionsToObject(JSGlobalObject* globalObject, JSValue optionsValue) | 
|  | { | 
|  | VM& vm = globalObject->vm(); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  | if (optionsValue.isUndefined()) | 
|  | return nullptr; | 
|  | JSObject* options = optionsValue.toObject(globalObject); | 
|  | RETURN_IF_EXCEPTION(scope, nullptr); | 
|  | return options; | 
|  | } | 
|  |  | 
|  | } // namespace JSC |