Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| //------------------------------------------------------------------------------------------------------- | |
| // Copyright (C) Microsoft. All rights reserved. | |
| // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. | |
| //------------------------------------------------------------------------------------------------------- | |
| #include "RuntimeLibraryPch.h" | |
| #include "EngineInterfaceObject.h" | |
| #include "IntlEngineInterfaceExtensionObject.h" | |
| #include "Types/DeferredTypeHandler.h" | |
| #include "Base/WindowsGlobalizationAdapter.h" | |
| #ifdef ENABLE_INTL_OBJECT | |
| #include "ByteCode/ByteCodeSerializer.h" | |
| #include "errstr.h" | |
| #include "ByteCode/ByteCodeDumper.h" | |
| #ifdef INTL_WINGLOB | |
| using namespace Windows::Globalization; | |
| #endif | |
| #ifdef INTL_ICU | |
| #include <CommonPal.h> | |
| #include "PlatformAgnostic/IPlatformAgnosticResource.h" | |
| #include "PlatformAgnostic/Intl.h" | |
| using namespace PlatformAgnostic::Intl; | |
| using namespace PlatformAgnostic::Resource; | |
| #endif | |
| #pragma warning(push) | |
| #pragma warning(disable:4309) // truncation of constant value | |
| #pragma warning(disable:4838) // conversion from 'int' to 'const char' requires a narrowing conversion | |
| #if DISABLE_JIT | |
| #if TARGET_64 | |
| #include "InJavascript/Intl.js.nojit.bc.64b.h" | |
| #else | |
| #include "InJavascript/Intl.js.nojit.bc.32b.h" | |
| #endif | |
| #else | |
| #if TARGET_64 | |
| #include "InJavascript/Intl.js.bc.64b.h" | |
| #else | |
| #include "InJavascript/Intl.js.bc.32b.h" | |
| #endif | |
| #endif | |
| #pragma warning(pop) | |
| #define TO_JSBOOL(sc, b) ((b) ? (sc)->GetLibrary()->GetTrue() : (sc)->GetLibrary()->GetFalse()) | |
| #define IfCOMFailIgnoreSilentlyAndReturn(op) \ | |
| if(FAILED(hr=(op))) \ | |
| { \ | |
| return; \ | |
| } \ | |
| #define IfFailAssertMsgAndThrowHr(op, msg) \ | |
| if (FAILED(hr=(op))) \ | |
| { \ | |
| AssertMsg(false, msg); \ | |
| JavascriptError::MapAndThrowError(scriptContext, hr); \ | |
| } \ | |
| #define HandleOOMSOEHR(hr) \ | |
| if (hr == E_OUTOFMEMORY) \ | |
| { \ | |
| JavascriptError::ThrowOutOfMemoryError(scriptContext); \ | |
| } \ | |
| else if(hr == VBSERR_OutOfStack) \ | |
| { \ | |
| JavascriptError::ThrowStackOverflowError(scriptContext); \ | |
| } \ | |
| #define IfFailThrowHr(op) \ | |
| if (FAILED(hr=(op))) \ | |
| { \ | |
| JavascriptError::MapAndThrowError(scriptContext, hr); \ | |
| } \ | |
| #define IfFailAssertAndThrowHr(op) \ | |
| if (FAILED(hr=(op))) \ | |
| { \ | |
| AssertMsg(false, "HRESULT was a failure."); \ | |
| JavascriptError::MapAndThrowError(scriptContext, hr); \ | |
| } \ | |
| #define SetPropertyOn(obj, propID, value) \ | |
| obj->SetProperty(propID, value, Js::PropertyOperationFlags::PropertyOperation_None, nullptr) \ | |
| #define SetStringPropertyOn(obj, propID, propValue) \ | |
| SetPropertyOn(obj, propID, Js::JavascriptString::NewCopySz(propValue, scriptContext)) \ | |
| #define SetPropertyLOn(obj, literalProperty, value) \ | |
| obj->SetProperty(Js::JavascriptString::NewCopySz(literalProperty, scriptContext), value, Js::PropertyOperationFlags::PropertyOperation_None, nullptr) \ | |
| #define SetStringPropertyLOn(obj, literalProperty, propValue) \ | |
| SetPropertyLOn(obj, literalProperty, Js::JavascriptString::NewCopySz(propValue, scriptContext)) \ | |
| #define SetPropertyBuiltInOn(obj, builtInPropID, value) \ | |
| SetPropertyOn(obj, Js::PropertyIds::builtInPropID, value) \ | |
| #define SetStringPropertyBuiltInOn(obj, builtInPropID, propValue) \ | |
| SetPropertyBuiltInOn(obj, builtInPropID, Js::JavascriptString::NewCopySz(propValue, scriptContext)) | |
| #define GetPropertyFrom(obj, propertyID) \ | |
| Js::JavascriptOperators::GetProperty(obj, propertyID, &propertyValue, scriptContext) \ | |
| #define GetPropertyLFrom(obj, propertyName) \ | |
| GetPropertyFrom(obj, scriptContext->GetOrAddPropertyIdTracked(propertyName, wcslen(propertyName))) | |
| #define GetPropertyBuiltInFrom(obj, builtInPropID) \ | |
| GetPropertyFrom(obj, Js::PropertyIds::builtInPropID) \ | |
| #define GetTypedPropertyBuiltInFrom(obj, builtInPropID, Type) \ | |
| (GetPropertyFrom(obj, Js::PropertyIds::builtInPropID) && Type::Is(propertyValue)) \ | |
| #define HasPropertyOn(obj, propID) \ | |
| Js::JavascriptOperators::HasProperty(obj, propID) \ | |
| #define HasPropertyBuiltInOn(obj, builtInPropID) \ | |
| HasPropertyOn(obj, Js::PropertyIds::builtInPropID) \ | |
| #define HasPropertyLOn(obj, propertyName) \ | |
| HasPropertyOn(obj, scriptContext->GetOrAddPropertyIdTracked(propertyName, wcslen(propertyName))) | |
| #ifdef INTL_WINGLOB | |
| #define SetHSTRINGPropertyOn(obj, propID, hstringValue) \ | |
| SetStringPropertyOn(obj, propID, wgl->WindowsGetStringRawBuffer(hstringValue, &length)) \ | |
| #define SetHSTRINGPropertyLOn(obj, literalProperty, hstringValue) \ | |
| SetStringPropertyLOn(obj, literalProperty, wgl->WindowsGetStringRawBuffer(hstringValue, &length)) \ | |
| #define SetHSTRINGPropertyBuiltInOn(obj, builtInPropID, hstringValue) \ | |
| SetStringPropertyBuiltInOn(obj, builtInPropID, wgl->WindowsGetStringRawBuffer(hstringValue, &length)) \ | |
| #endif | |
| namespace Js | |
| { | |
| #ifdef ENABLE_INTL_OBJECT | |
| #ifdef INTL_WINGLOB | |
| class AutoHSTRING | |
| { | |
| PREVENT_COPY(AutoHSTRING) | |
| private: | |
| HSTRING value; | |
| public: | |
| HSTRING *operator&() { Assert(value == nullptr); return &value; } | |
| HSTRING operator*() const { Assert(value != nullptr); return value; } | |
| AutoHSTRING() | |
| : value(nullptr) | |
| { } | |
| ~AutoHSTRING() | |
| { | |
| Clear(); | |
| } | |
| void Clear() | |
| { | |
| if (value != nullptr) | |
| { | |
| WindowsDeleteString(value); | |
| value = nullptr; | |
| } | |
| } | |
| }; | |
| #endif | |
| // Defining Finalizable wrappers for Intl data | |
| #if defined(INTL_WINGLOB) | |
| class AutoCOMJSObject : public FinalizableObject | |
| { | |
| IInspectable *instance; | |
| public: | |
| DEFINE_VTABLE_CTOR_NOBASE(AutoCOMJSObject); | |
| AutoCOMJSObject(IInspectable *object) | |
| : instance(object) | |
| { } | |
| static AutoCOMJSObject * New(Recycler * recycler, IInspectable *object) | |
| { | |
| return RecyclerNewFinalized(recycler, AutoCOMJSObject, object); | |
| } | |
| void Finalize(bool isShutdown) override | |
| { | |
| } | |
| void Dispose(bool isShutdown) override | |
| { | |
| if (!isShutdown) | |
| { | |
| instance->Release(); | |
| } | |
| } | |
| void Mark(Recycler * recycler) override | |
| { | |
| } | |
| IInspectable *GetInstance() | |
| { | |
| return instance; | |
| } | |
| }; | |
| #elif defined(INTL_ICU) | |
| template<typename T> | |
| class AutoIcuJsObject : public FinalizableObject | |
| { | |
| private: | |
| FieldNoBarrier(T *) instance; | |
| public: | |
| DEFINE_VTABLE_CTOR_NOBASE(AutoIcuJsObject<T>); | |
| AutoIcuJsObject(T *object) | |
| : instance(object) | |
| { } | |
| static AutoIcuJsObject<T> * New(Recycler *recycler, T *object) | |
| { | |
| return RecyclerNewFinalized(recycler, AutoIcuJsObject<T>, object); | |
| } | |
| void Finalize(bool isShutdown) override | |
| { | |
| } | |
| void Dispose(bool isShutdown) override | |
| { | |
| if (!isShutdown) | |
| { | |
| // Here we use Cleanup() because we can't rely on delete (not dealing with virtual destructors). | |
| // The template thus requires that the type implement the Cleanup function. | |
| instance->Cleanup(); // e.g. deletes the object held in the IPlatformAgnosticResource | |
| // REVIEW (doilij): Is cleanup in this way necessary or are the trivial destructors enough, assuming Cleanup() has been called? | |
| // Note: delete here introduces a build break on Linux complaining of non-virtual dtor | |
| // delete instance; // deletes the instance itself | |
| // instance = nullptr; | |
| } | |
| } | |
| void Mark(Recycler *recycler) override | |
| { | |
| } | |
| T * GetInstance() | |
| { | |
| return instance; | |
| } | |
| }; | |
| #endif | |
| IntlEngineInterfaceExtensionObject::IntlEngineInterfaceExtensionObject(Js::ScriptContext* scriptContext) : | |
| EngineExtensionObjectBase(EngineInterfaceExtensionKind_Intl, scriptContext), | |
| dateToLocaleString(nullptr), | |
| dateToLocaleTimeString(nullptr), | |
| dateToLocaleDateString(nullptr), | |
| numberToLocaleString(nullptr), | |
| stringLocaleCompare(nullptr), | |
| intlNativeInterfaces(nullptr), | |
| intlByteCode(nullptr), | |
| wasInitialized(false) | |
| { | |
| } | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_RaiseAssert(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_RaiseAssert)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_IsWellFormedLanguageTag(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_IsWellFormedLanguageTag)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_NormalizeLanguageTag(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_NormalizeLanguageTag)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_ResolveLocaleLookup(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_ResolveLocaleLookup)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_ResolveLocaleBestFit(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_ResolveLocaleBestFit)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_GetDefaultLocale(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_GetDefaultLocale)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_GetExtensions(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_GetExtensions)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_CompareString(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_CompareString)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_CurrencyDigits(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_CurrencyDigits)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_FormatNumber(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_FormatNumber)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_CacheNumberFormat(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_CacheNumberFormat)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_CreateDateTimeFormat(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_CreateDateTimeFormat)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_FormatDateTime(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_FormatDateTime)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_ValidateAndCanonicalizeTimeZone(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_ValidateAndCanonicalizeTimeZone)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_GetDefaultTimeZone(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_GetDefaultTimeZone)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_RegisterBuiltInFunction(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_RegisterBuiltInFunction)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_GetHiddenObject(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_GetHiddenObject)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_SetHiddenObject(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_SetHiddenObject)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_BuiltIn_GetArrayLength(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_BuiltIn_GetArrayLength)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_BuiltIn_SetPrototype(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_BuiltIn_SetPrototype)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_BuiltIn_RegexMatch(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_BuiltIn_RegexMatch)); | |
| NoProfileFunctionInfo IntlEngineInterfaceExtensionObject::EntryInfo::Intl_BuiltIn_CallInstanceFunction(FORCE_NO_WRITE_BARRIER_TAG(IntlEngineInterfaceExtensionObject::EntryIntl_BuiltIn_CallInstanceFunction)); | |
| #ifdef INTL_WINGLOB | |
| WindowsGlobalizationAdapter* IntlEngineInterfaceExtensionObject::GetWindowsGlobalizationAdapter(_In_ ScriptContext * scriptContext) | |
| { | |
| return scriptContext->GetThreadContext()->GetWindowsGlobalizationAdapter(); | |
| } | |
| #endif | |
| void IntlEngineInterfaceExtensionObject::Initialize() | |
| { | |
| if (wasInitialized) | |
| { | |
| return; | |
| } | |
| JavascriptLibrary* library = scriptContext->GetLibrary(); | |
| DynamicObject* commonObject = library->GetEngineInterfaceObject()->GetCommonNativeInterfaces(); | |
| if (scriptContext->IsIntlEnabled()) | |
| { | |
| Assert(library->GetEngineInterfaceObject() != nullptr); | |
| this->intlNativeInterfaces = DynamicObject::New(library->GetRecycler(), | |
| DynamicType::New(scriptContext, TypeIds_Object, commonObject, nullptr, | |
| DeferredTypeHandler<InitializeIntlNativeInterfaces>::GetDefaultInstance())); | |
| library->AddMember(library->GetEngineInterfaceObject(), Js::PropertyIds::Intl, this->intlNativeInterfaces); | |
| } | |
| // TODO: Move these to IntlNativeInterfaces? | |
| library->AddFunctionToLibraryObject(commonObject, Js::PropertyIds::builtInSetPrototype, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_BuiltIn_SetPrototype, 1); | |
| library->AddFunctionToLibraryObject(commonObject, Js::PropertyIds::builtInGetArrayLength, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_BuiltIn_GetArrayLength, 1); | |
| library->AddFunctionToLibraryObject(commonObject, Js::PropertyIds::builtInRegexMatch, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_BuiltIn_RegexMatch, 1); | |
| library->AddFunctionToLibraryObject(commonObject, Js::PropertyIds::builtInCallInstanceFunction, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_BuiltIn_CallInstanceFunction, 1); | |
| wasInitialized = true; | |
| } | |
| #if DBG | |
| void IntlEngineInterfaceExtensionObject::DumpByteCode() | |
| { | |
| Output::Print(_u("Dumping Intl Byte Code:")); | |
| Assert(this->intlByteCode); | |
| Js::ByteCodeDumper::DumpRecursively(intlByteCode); | |
| } | |
| #endif | |
| bool IntlEngineInterfaceExtensionObject::InitializeIntlNativeInterfaces(DynamicObject* intlNativeInterfaces, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode) | |
| { | |
| typeHandler->Convert(intlNativeInterfaces, mode, 16); | |
| ScriptContext* scriptContext = intlNativeInterfaces->GetScriptContext(); | |
| JavascriptLibrary* library = scriptContext->GetLibrary(); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::raiseAssert, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_RaiseAssert, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::isWellFormedLanguageTag, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_IsWellFormedLanguageTag, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::normalizeLanguageTag, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_NormalizeLanguageTag, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::compareString, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_CompareString, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::resolveLocaleLookup, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_ResolveLocaleLookup, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::resolveLocaleBestFit, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_ResolveLocaleBestFit, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::getDefaultLocale, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_GetDefaultLocale, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::getExtensions, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_GetExtensions, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::formatNumber, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_FormatNumber, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::cacheNumberFormat, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_CacheNumberFormat, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::createDateTimeFormat, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_CreateDateTimeFormat, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::currencyDigits, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_CurrencyDigits, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::formatDateTime, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_FormatDateTime, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::validateAndCanonicalizeTimeZone, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_ValidateAndCanonicalizeTimeZone, 2); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::getDefaultTimeZone, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_GetDefaultTimeZone, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::registerBuiltInFunction, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_RegisterBuiltInFunction, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::getHiddenObject, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_GetHiddenObject, 1); | |
| library->AddFunctionToLibraryObject(intlNativeInterfaces, Js::PropertyIds::setHiddenObject, &IntlEngineInterfaceExtensionObject::EntryInfo::Intl_SetHiddenObject, 1); | |
| #if INTL_WINGLOB | |
| library->AddMember(intlNativeInterfaces, Js::PropertyIds::winglob, library->GetTrue()); | |
| #else | |
| library->AddMember(intlNativeInterfaces, Js::PropertyIds::winglob, library->GetFalse()); | |
| #endif | |
| intlNativeInterfaces->SetHasNoEnumerableProperties(true); | |
| return true; | |
| } | |
| void IntlEngineInterfaceExtensionObject::deletePrototypePropertyHelper(ScriptContext* scriptContext, DynamicObject* intlObject, Js::PropertyId objectPropertyId, Js::PropertyId getterFunctionId) | |
| { | |
| DynamicObject *prototypeObject = nullptr; | |
| DynamicObject *functionObj = nullptr; | |
| Var propertyValue = nullptr; | |
| Var prototypeValue = nullptr; | |
| Var resolvedOptionsValue = nullptr; | |
| Var getter = nullptr; | |
| Var setter = nullptr; | |
| if (!JavascriptOperators::GetProperty(intlObject, objectPropertyId, &propertyValue, scriptContext) || | |
| !JavascriptOperators::IsObject(propertyValue)) | |
| { | |
| return; | |
| } | |
| if (!JavascriptOperators::GetProperty(DynamicObject::FromVar(propertyValue), Js::PropertyIds::prototype, &prototypeValue, scriptContext) || | |
| !JavascriptOperators::IsObject(prototypeValue)) | |
| { | |
| return; | |
| } | |
| prototypeObject = DynamicObject::FromVar(prototypeValue); | |
| if (!JavascriptOperators::GetProperty(prototypeObject, Js::PropertyIds::resolvedOptions, &resolvedOptionsValue, scriptContext) || | |
| !JavascriptOperators::IsObject(resolvedOptionsValue)) | |
| { | |
| return; | |
| } | |
| functionObj = DynamicObject::FromVar(resolvedOptionsValue); | |
| functionObj->SetConfigurable(Js::PropertyIds::prototype, true); | |
| functionObj->DeleteProperty(Js::PropertyIds::prototype, Js::PropertyOperationFlags::PropertyOperation_None); | |
| if (!JavascriptOperators::GetOwnAccessors(prototypeObject, getterFunctionId, &getter, &setter, scriptContext) || | |
| !JavascriptOperators::IsObject(getter)) | |
| { | |
| return; | |
| } | |
| functionObj = DynamicObject::FromVar(getter); | |
| functionObj->SetConfigurable(Js::PropertyIds::prototype, true); | |
| functionObj->DeleteProperty(Js::PropertyIds::prototype, Js::PropertyOperationFlags::PropertyOperation_None); | |
| } | |
| void IntlEngineInterfaceExtensionObject::cleanUpIntl(ScriptContext *scriptContext, DynamicObject* intlObject) | |
| { | |
| this->dateToLocaleString = nullptr; | |
| this->dateToLocaleTimeString = nullptr; | |
| this->dateToLocaleDateString = nullptr; | |
| this->numberToLocaleString = nullptr; | |
| this->stringLocaleCompare = nullptr; | |
| //Failed to setup Intl; Windows.Globalization.dll is most likely missing. | |
| if (Js::JavascriptOperators::HasProperty(intlObject, Js::PropertyIds::Collator)) | |
| { | |
| intlObject->DeleteProperty(Js::PropertyIds::Collator, Js::PropertyOperationFlags::PropertyOperation_None); | |
| } | |
| if (Js::JavascriptOperators::HasProperty(intlObject, Js::PropertyIds::NumberFormat)) | |
| { | |
| intlObject->DeleteProperty(Js::PropertyIds::NumberFormat, Js::PropertyOperationFlags::PropertyOperation_None); | |
| } | |
| if (Js::JavascriptOperators::HasProperty(intlObject, Js::PropertyIds::DateTimeFormat)) | |
| { | |
| intlObject->DeleteProperty(Js::PropertyIds::DateTimeFormat, Js::PropertyOperationFlags::PropertyOperation_None); | |
| } | |
| } | |
| void IntlEngineInterfaceExtensionObject::EnsureIntlByteCode(_In_ ScriptContext * scriptContext) | |
| { | |
| if (this->intlByteCode == nullptr) | |
| { | |
| SourceContextInfo * sourceContextInfo = scriptContext->GetSourceContextInfo(Js::Constants::NoHostSourceContext, NULL); | |
| Assert(sourceContextInfo != nullptr); | |
| SRCINFO si; | |
| memset(&si, 0, sizeof(si)); | |
| si.sourceContextInfo = sourceContextInfo; | |
| SRCINFO *hsi = scriptContext->AddHostSrcInfo(&si); | |
| uint32 flags = fscrIsLibraryCode | (CONFIG_FLAG(CreateFunctionProxy) && !scriptContext->IsProfiling() ? fscrAllowFunctionProxy : 0); | |
| HRESULT hr = Js::ByteCodeSerializer::DeserializeFromBuffer(scriptContext, flags, (LPCUTF8)nullptr, hsi, (byte*)Library_Bytecode_Intl, nullptr, &this->intlByteCode); | |
| IfFailAssertMsgAndThrowHr(hr, "Failed to deserialize Intl.js bytecode - very probably the bytecode needs to be rebuilt."); | |
| this->SetHasBytecode(); | |
| } | |
| } | |
| void IntlEngineInterfaceExtensionObject::InjectIntlLibraryCode(_In_ ScriptContext * scriptContext, DynamicObject* intlObject, IntlInitializationType intlInitializationType) | |
| { | |
| JavascriptExceptionObject *pExceptionObject = nullptr; | |
| #ifdef INTL_WINGLOB | |
| WindowsGlobalizationAdapter* globAdapter = GetWindowsGlobalizationAdapter(scriptContext); | |
| #endif | |
| try { | |
| this->EnsureIntlByteCode(scriptContext); | |
| Assert(intlByteCode != nullptr); | |
| #ifdef INTL_WINGLOB | |
| DelayLoadWindowsGlobalization *library = scriptContext->GetThreadContext()->GetWindowsGlobalizationLibrary(); | |
| #endif | |
| JavascriptString* initType = nullptr; | |
| #ifdef INTL_WINGLOB | |
| HRESULT hr; | |
| //Ensure we have initialized all appropriate COM objects for the adapter (we will be using them now) | |
| IfCOMFailIgnoreSilentlyAndReturn(globAdapter->EnsureCommonObjectsInitialized(library)); | |
| #endif | |
| switch (intlInitializationType) | |
| { | |
| default: | |
| AssertMsg(false, "Not a valid intlInitializationType."); | |
| // fall thru | |
| case IntlInitializationType::Intl: | |
| #ifdef INTL_WINGLOB | |
| IfCOMFailIgnoreSilentlyAndReturn(globAdapter->EnsureNumberFormatObjectsInitialized(library)); | |
| IfCOMFailIgnoreSilentlyAndReturn(globAdapter->EnsureDateTimeFormatObjectsInitialized(library)); | |
| #endif | |
| initType = scriptContext->GetPropertyString(PropertyIds::Intl); | |
| break; | |
| case IntlInitializationType::StringPrototype: | |
| // No other windows globalization adapter needed. Common adapter should suffice | |
| initType = scriptContext->GetPropertyString(PropertyIds::String); | |
| break; | |
| case IntlInitializationType::DatePrototype: | |
| #ifdef INTL_WINGLOB | |
| IfCOMFailIgnoreSilentlyAndReturn(globAdapter->EnsureDateTimeFormatObjectsInitialized(library)); | |
| #endif | |
| initType = scriptContext->GetPropertyString(PropertyIds::Date); | |
| break; | |
| case IntlInitializationType::NumberPrototype: | |
| #ifdef INTL_WINGLOB | |
| IfCOMFailIgnoreSilentlyAndReturn(globAdapter->EnsureNumberFormatObjectsInitialized(library)); | |
| #endif | |
| initType = scriptContext->GetPropertyString(PropertyIds::Number); | |
| break; | |
| } | |
| Js::ScriptFunction *function = scriptContext->GetLibrary()->CreateScriptFunction(intlByteCode->GetNestedFunctionForExecution(0)); | |
| #ifdef ENABLE_SCRIPT_PROFILING | |
| // If we are profiling, we need to register the script to the profiler callback, so the script compiled event will be sent. | |
| if (scriptContext->IsProfiling()) | |
| { | |
| scriptContext->RegisterScript(function->GetFunctionProxy()); | |
| } | |
| #endif | |
| #ifdef ENABLE_SCRIPT_DEBUGGING | |
| // Mark we are profiling library code already, so that any initialization library code called here won't be reported to profiler. | |
| // Also tell the debugger not to record events during intialization so that we don't leak information about initialization. | |
| AutoInitLibraryCodeScope autoInitLibraryCodeScope(scriptContext); | |
| #endif | |
| Js::Var args[] = { scriptContext->GetLibrary()->GetUndefined(), scriptContext->GetLibrary()->GetEngineInterfaceObject(), initType }; | |
| Js::CallInfo callInfo(Js::CallFlags_Value, _countof(args)); | |
| // Clear disable implicit call bit as initialization code doesn't have any side effect | |
| Js::ImplicitCallFlags saveImplicitCallFlags = scriptContext->GetThreadContext()->GetImplicitCallFlags(); | |
| scriptContext->GetThreadContext()->ClearDisableImplicitFlags(); | |
| JavascriptFunction::CallRootFunctionInScript(function, Js::Arguments(callInfo, args)); | |
| scriptContext->GetThreadContext()->SetImplicitCallFlags((Js::ImplicitCallFlags)(saveImplicitCallFlags)); | |
| // Delete prototypes on functions if initialized Intl object | |
| if (intlInitializationType == IntlInitializationType::Intl) | |
| { | |
| deletePrototypePropertyHelper(scriptContext, intlObject, Js::PropertyIds::Collator, Js::PropertyIds::compare); | |
| deletePrototypePropertyHelper(scriptContext, intlObject, Js::PropertyIds::NumberFormat, Js::PropertyIds::format); | |
| deletePrototypePropertyHelper(scriptContext, intlObject, Js::PropertyIds::DateTimeFormat, Js::PropertyIds::format); | |
| } | |
| #if DBG_DUMP | |
| if (PHASE_DUMP(Js::ByteCodePhase, function->GetFunctionProxy()) && Js::Configuration::Global.flags.Verbose) | |
| { | |
| DumpByteCode(); | |
| } | |
| #endif | |
| } | |
| catch (const JavascriptException& err) | |
| { | |
| pExceptionObject = err.GetAndClear(); | |
| } | |
| if (pExceptionObject) | |
| { | |
| if (intlInitializationType == IntlInitializationType::Intl) | |
| { | |
| cleanUpIntl(scriptContext, intlObject); | |
| } | |
| if (pExceptionObject == ThreadContext::GetContextForCurrentThread()->GetPendingOOMErrorObject() || | |
| pExceptionObject == ThreadContext::GetContextForCurrentThread()->GetPendingSOErrorObject()) | |
| { | |
| // Reset factory objects that are might not have fully initialized | |
| #ifdef INTL_WINGLOB | |
| globAdapter->ResetCommonFactoryObjects(); | |
| #endif | |
| switch (intlInitializationType) { | |
| default: | |
| AssertMsg(false, "Not a valid intlInitializationType."); | |
| // fall thru | |
| case IntlInitializationType::Intl: | |
| #ifdef INTL_WINGLOB | |
| globAdapter->ResetNumberFormatFactoryObjects(); | |
| globAdapter->ResetDateTimeFormatFactoryObjects(); | |
| #endif | |
| scriptContext->GetLibrary()->ResetIntlObject(); | |
| break; | |
| case IntlInitializationType::StringPrototype: | |
| // No other windows globalization adapter is created. Resetting common adapter should suffice | |
| break; | |
| case IntlInitializationType::DatePrototype: | |
| #ifdef INTL_WINGLOB | |
| globAdapter->ResetDateTimeFormatFactoryObjects(); | |
| #endif | |
| break; | |
| case IntlInitializationType::NumberPrototype: | |
| #ifdef INTL_WINGLOB | |
| globAdapter->ResetNumberFormatFactoryObjects(); | |
| #endif | |
| break; | |
| } | |
| JavascriptExceptionOperators::DoThrowCheckClone(pExceptionObject, scriptContext); | |
| } | |
| #if DEBUG | |
| JavascriptExceptionOperators::DoThrowCheckClone(pExceptionObject, scriptContext); | |
| #else | |
| JavascriptError::ThrowTypeError(scriptContext, JSERR_IntlNotAvailable); | |
| #endif | |
| } | |
| } | |
| // First parameter is boolean. | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_RaiseAssert(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| if (args.Info.Count < 2 || !JavascriptError::Is(args.Values[1])) | |
| { | |
| AssertMsg(false, "Intl's Assert platform API was called incorrectly."); | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| #if DEBUG | |
| #ifdef INTL_ICU_DEBUG | |
| Output::Print(_u("EntryIntl_RaiseAssert\n")); | |
| #endif | |
| JavascriptExceptionOperators::Throw(JavascriptError::FromVar(args.Values[1]), scriptContext); | |
| #else | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| #endif | |
| } | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_IsWellFormedLanguageTag(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| if (args.Info.Count < 2 || !JavascriptString::Is(args.Values[1])) | |
| { | |
| // IsWellFormedLanguageTag of undefined or non-string is false | |
| return scriptContext->GetLibrary()->GetFalse(); | |
| } | |
| JavascriptString *argString = JavascriptString::FromVar(args.Values[1]); | |
| #if defined(INTL_ICU) | |
| return TO_JSBOOL(scriptContext, IsWellFormedLanguageTag(argString->GetSz(), argString->GetLength())); | |
| #else | |
| return TO_JSBOOL(scriptContext, GetWindowsGlobalizationAdapter(scriptContext)->IsWellFormedLanguageTag(scriptContext, argString->GetSz())); | |
| #endif | |
| } | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_NormalizeLanguageTag(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| if (args.Info.Count < 2 || !JavascriptString::Is(args.Values[1])) | |
| { | |
| // NormalizeLanguageTag of undefined or non-string is undefined | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| JavascriptString *argString = JavascriptString::FromVar(args.Values[1]); | |
| HRESULT hr; | |
| JavascriptString *retVal; | |
| #if defined(INTL_ICU) | |
| // `normalized` will be filled by converting a char* (of utf8 bytes) to char16* | |
| // Since `len(utf8bytes) >= len(to_char16s(utf8bytes))`, | |
| // Therefore the max length of that char* (ULOC_FULLNAME_CAPACITY) is big enough to hold the result (including null terminator) | |
| char16 normalized[ULOC_FULLNAME_CAPACITY] = { 0 }; | |
| size_t normalizedLength = 0; | |
| hr = NormalizeLanguageTag(argString->GetSz(), argString->GetLength(), normalized, &normalizedLength); | |
| retVal = Js::JavascriptString::NewCopyBuffer(normalized, static_cast<charcount_t>(normalizedLength), scriptContext); | |
| #else | |
| AutoHSTRING str; | |
| hr = GetWindowsGlobalizationAdapter(scriptContext)->NormalizeLanguageTag(scriptContext, argString->GetSz(), &str); | |
| DelayLoadWindowsGlobalization *wsl = scriptContext->GetThreadContext()->GetWindowsGlobalizationLibrary(); | |
| PCWSTR strBuf = wsl->WindowsGetStringRawBuffer(*str, NULL); | |
| retVal = Js::JavascriptString::NewCopySz(strBuf, scriptContext); | |
| #endif | |
| if (FAILED(hr)) | |
| { | |
| HandleOOMSOEHR(hr); | |
| //If we can't normalize the tag; return undefined. | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| return retVal; | |
| } | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_ResolveLocaleLookup(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| if (args.Info.Count < 2 || !JavascriptString::Is(args.Values[1])) | |
| { | |
| // NormalizeLanguageTag of undefined or non-string is undefined | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| JavascriptString *argString = JavascriptString::FromVar(args.Values[1]); | |
| PCWSTR passedLocale = argString->GetSz(); | |
| #if defined(INTL_ICU) | |
| char16 resolvedLocaleName[ULOC_FULLNAME_CAPACITY] = { 0 }; | |
| if (ResolveLocaleLookup(passedLocale, resolvedLocaleName)) | |
| { | |
| return JavascriptString::NewCopySz(resolvedLocaleName, scriptContext); | |
| } | |
| #ifdef INTL_ICU_DEBUG | |
| Output::Print(_u("Intl::ResolveLocaleLookup returned false: EntryIntl_ResolveLocaleLookup returning null to fallback to JS\n")); | |
| #endif | |
| return scriptContext->GetLibrary()->GetNull(); | |
| #else | |
| // REVIEW should we zero the whole array for safety? | |
| WCHAR resolvedLocaleName[LOCALE_NAME_MAX_LENGTH]; | |
| resolvedLocaleName[0] = '\0'; | |
| ResolveLocaleName(passedLocale, resolvedLocaleName, _countof(resolvedLocaleName)); | |
| if (resolvedLocaleName[0] == '\0') | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| return JavascriptString::NewCopySz(resolvedLocaleName, scriptContext); | |
| #endif | |
| } | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_ResolveLocaleBestFit(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| if (args.Info.Count < 2 || !JavascriptString::Is(args.Values[1])) | |
| { | |
| // NormalizeLanguageTag of undefined or non-string is undefined | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| Var toReturn = nullptr; | |
| JavascriptString *localeStrings = JavascriptString::FromVar(args.Values[1]); | |
| PCWSTR passedLocale = localeStrings->GetSz(); | |
| #if defined(INTL_ICU) | |
| char16 resolvedLocaleName[ULOC_FULLNAME_CAPACITY] = { 0 }; | |
| if (ResolveLocaleBestFit(passedLocale, resolvedLocaleName)) | |
| { | |
| toReturn = JavascriptString::NewCopySz(resolvedLocaleName, scriptContext); | |
| } | |
| else | |
| { | |
| #ifdef INTL_ICU_DEBUG | |
| Output::Print(_u("Intl::ResolveLocaleBestFit returned false: EntryIntl_ResolveLocaleBestFit returning null to fallback to JS\n")); | |
| #endif | |
| toReturn = scriptContext->GetLibrary()->GetNull(); | |
| } | |
| #else // !INTL_ICU | |
| DelayLoadWindowsGlobalization* wgl = scriptContext->GetThreadContext()->GetWindowsGlobalizationLibrary(); | |
| WindowsGlobalizationAdapter* wga = GetWindowsGlobalizationAdapter(scriptContext); | |
| AutoCOMPtr<DateTimeFormatting::IDateTimeFormatter> formatter; | |
| HRESULT hr; | |
| if (FAILED(hr = wga->CreateDateTimeFormatter(scriptContext, _u("longdate"), &passedLocale, 1, nullptr, nullptr, &formatter))) | |
| { | |
| HandleOOMSOEHR(hr); | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| AutoHSTRING locale; | |
| if (FAILED(hr = wga->GetResolvedLanguage(formatter, &locale))) | |
| { | |
| HandleOOMSOEHR(hr); | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| toReturn = JavascriptString::NewCopySz(wgl->WindowsGetStringRawBuffer(*locale, NULL), scriptContext); | |
| #endif | |
| return toReturn; | |
| } | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_GetDefaultLocale(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| char16 defaultLocale[LOCALE_NAME_MAX_LENGTH]; | |
| defaultLocale[0] = '\0'; | |
| if ( | |
| #if defined(INTL_WINGLOB) | |
| // XPLAT-TODO (doilij): Implement this in PlatformAgnostic | |
| GetUserDefaultLocaleName(defaultLocale, _countof(defaultLocale)) == 0 | |
| #else | |
| GetUserDefaultLanguageTag(defaultLocale, _countof(defaultLocale)) == 0 | |
| #endif | |
| || defaultLocale[0] == '\0') | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| return JavascriptString::NewCopySz(defaultLocale, scriptContext); | |
| } | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_GetExtensions(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| if (args.Info.Count < 2 || !JavascriptString::Is(args.Values[1])) | |
| { | |
| // NormalizeLanguageTag of undefined or non-string is undefined | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| #ifdef INTL_WINGLOB | |
| DelayLoadWindowsGlobalization* wgl = scriptContext->GetThreadContext()->GetWindowsGlobalizationLibrary(); | |
| WindowsGlobalizationAdapter* wga = GetWindowsGlobalizationAdapter(scriptContext); | |
| AutoCOMPtr<ILanguage> language; | |
| AutoCOMPtr<ILanguageExtensionSubtags> extensionSubtags; | |
| HRESULT hr; | |
| if (FAILED(hr = wga->CreateLanguage(scriptContext, JavascriptString::FromVar(args.Values[1])->GetSz(), &language))) | |
| { | |
| HandleOOMSOEHR(hr); | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| if (FAILED(hr = language->QueryInterface(__uuidof(ILanguageExtensionSubtags), reinterpret_cast<void**>(&extensionSubtags)))) | |
| { | |
| HandleOOMSOEHR(hr); | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| Assert(extensionSubtags); | |
| AutoHSTRING singletonString; | |
| AutoCOMPtr<Windows::Foundation::Collections::IVectorView<HSTRING>> subtags; | |
| uint32 length; | |
| if (FAILED(hr = wgl->WindowsCreateString(_u("u"), 1, &singletonString)) || FAILED(hr = extensionSubtags->GetExtensionSubtags(*singletonString, &subtags)) || FAILED(subtags->get_Size(&length))) | |
| { | |
| HandleOOMSOEHR(hr); | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| JavascriptArray *toReturn = scriptContext->GetLibrary()->CreateArray(length); | |
| for (uint32 i = 0; i < length; i++) | |
| { | |
| AutoHSTRING str; | |
| if (!FAILED(hr = wga->GetItemAt(subtags, i, &str))) | |
| { | |
| toReturn->SetItem(i, JavascriptString::NewCopySz(wgl->WindowsGetStringRawBuffer(*str, NULL), scriptContext), Js::PropertyOperationFlags::PropertyOperation_None); | |
| } | |
| else | |
| { | |
| HandleOOMSOEHR(hr); | |
| } | |
| } | |
| return toReturn; | |
| #else | |
| // TODO (doilij): implement INTL_ICU version | |
| #ifdef INTL_ICU_DEBUG | |
| Output::Print(_u("EntryIntl_GetExtensions > returning null: fallback to JS function getExtensionSubtags\n")); | |
| #endif | |
| return scriptContext->GetLibrary()->GetNull(); // fallback to Intl.js | |
| #endif | |
| } | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_CacheNumberFormat(RecyclableObject * function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| // The passed object is the hidden state object | |
| if (args.Info.Count < 2 || !DynamicObject::Is(args.Values[1])) | |
| { | |
| // Call with undefined or non-number is undefined | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| DynamicObject *options = DynamicObject::FromVar(args.Values[1]); | |
| HRESULT hr = S_OK; | |
| Var propertyValue = nullptr; // set by the GetTypedPropertyBuiltInFrom macro | |
| JavascriptString *localeJSstr = nullptr; | |
| #if defined(INTL_ICU) | |
| // Verify locale is present | |
| // REVIEW (doilij): Fix comparison of the unsigned value <= 0 | |
| if (!GetTypedPropertyBuiltInFrom(options, __locale, JavascriptString) || (localeJSstr = JavascriptString::FromVar(propertyValue))->GetLength() <= 0) | |
| { | |
| // REVIEW (doilij): We return undefined from all paths here, so should we throw instead to indicate to Intl.js that something went wrong? | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| // First we have to determine which formatter(number, percent, or currency) we will be using. | |
| // Note some options might not be present. | |
| IPlatformAgnosticResource *numberFormatter = nullptr; | |
| const char16 *locale = localeJSstr->GetSz(); | |
| const charcount_t cch = localeJSstr->GetLength(); | |
| NumberFormatStyle formatterToUseVal = NumberFormatStyle::DEFAULT; | |
| if (GetTypedPropertyBuiltInFrom(options, __formatterToUse, TaggedInt) | |
| && (formatterToUseVal = static_cast<NumberFormatStyle>(TaggedInt::ToUInt16(propertyValue))) == NumberFormatStyle::PERCENT) | |
| { | |
| IfFailThrowHr(CreatePercentFormatter(locale, cch, &numberFormatter)); | |
| } | |
| else if (formatterToUseVal == NumberFormatStyle::CURRENCY) | |
| { | |
| if (!GetTypedPropertyBuiltInFrom(options, __currency, JavascriptString)) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| JavascriptString *currencyCodeJsString = JavascriptString::FromVar(propertyValue); | |
| const char16 *currencyCode = currencyCodeJsString->GetSz(); | |
| if (GetTypedPropertyBuiltInFrom(options, __currencyDisplayToUse, TaggedInt)) | |
| { | |
| NumberFormatCurrencyDisplay currencyDisplay = static_cast<NumberFormatCurrencyDisplay>(TaggedInt::ToUInt16(propertyValue)); | |
| IfFailThrowHr(CreateCurrencyFormatter(locale, cch, currencyCode, currencyDisplay, &numberFormatter)); | |
| } | |
| } | |
| else | |
| { | |
| // Use the number formatter (0 or default) | |
| IfFailThrowHr(CreateNumberFormatter(locale, cch, &numberFormatter)); | |
| } | |
| Assert(numberFormatter); | |
| if (GetTypedPropertyBuiltInFrom(options, __useGrouping, JavascriptBoolean)) | |
| { | |
| bool useGrouping = JavascriptBoolean::FromVar(propertyValue)->GetValue(); | |
| SetNumberFormatGroupingUsed(numberFormatter, useGrouping); | |
| } | |
| // Numeral system is in the locale and is therefore already set on the icu::NumberFormat | |
| // TODO (doilij): extract the numbering system from the locale name (JS-side) and use that to set the __numberingSystem with fallback | |
| // TODO (doilij): determine the numbering system for the ICU locale (so that works even if it wasn't specified directly) | |
| // REVIEW (doilij): assuming the resolved language has already been set in __locale | |
| // TODO (doilij): find out whether numberFormat->getLocale() has relevant extension tags for things like numeral system (-nu-) | |
| if (HasPropertyBuiltInOn(options, __minimumSignificantDigits) || HasPropertyBuiltInOn(options, __maximumSignificantDigits)) | |
| { | |
| // Do significant digit rounding | |
| uint16 minSignificantDigits = 1, maxSignificantDigits = 21; | |
| if (GetTypedPropertyBuiltInFrom(options, __minimumSignificantDigits, TaggedInt)) | |
| { | |
| minSignificantDigits = max<uint16>(min<uint16>(TaggedInt::ToUInt16(propertyValue), 21), 1); | |
| } | |
| if (GetTypedPropertyBuiltInFrom(options, __maximumSignificantDigits, TaggedInt)) | |
| { | |
| maxSignificantDigits = max<uint16>(min<uint16>(TaggedInt::ToUInt16(propertyValue), 21), minSignificantDigits); | |
| } | |
| SetNumberFormatSignificantDigits(numberFormatter, minSignificantDigits, maxSignificantDigits); | |
| } | |
| else | |
| { | |
| // Do fraction/integer digit rounding | |
| uint16 minFractionDigits = 0, maxFractionDigits = 3, minIntegerDigits = 1; | |
| if (GetTypedPropertyBuiltInFrom(options, __minimumIntegerDigits, TaggedInt)) | |
| { | |
| minIntegerDigits = max<uint16>(min<uint16>(TaggedInt::ToUInt16(propertyValue), 21), 1); | |
| } | |
| if (GetTypedPropertyBuiltInFrom(options, __minimumFractionDigits, TaggedInt)) | |
| { | |
| minFractionDigits = min<uint16>(TaggedInt::ToUInt16(propertyValue), 20); // ToUInt16 will get rid of negatives by making them high | |
| } | |
| if (GetTypedPropertyBuiltInFrom(options, __maximumFractionDigits, TaggedInt)) | |
| { | |
| maxFractionDigits = max(min<uint16>(TaggedInt::ToUInt16(propertyValue), 20), minFractionDigits); // ToUInt16 will get rid of negatives by making them high | |
| } | |
| SetNumberFormatIntFracDigits(numberFormatter, minFractionDigits, maxFractionDigits, minIntegerDigits); | |
| } | |
| // Set the object as a cache | |
| auto *autoObject = AutoIcuJsObject<IPlatformAgnosticResource>::New(scriptContext->GetRecycler(), numberFormatter); | |
| options->SetInternalProperty(Js::InternalPropertyIds::HiddenObject, autoObject, Js::PropertyOperationFlags::PropertyOperation_None, NULL); | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| #else | |
| DelayLoadWindowsGlobalization* wgl = scriptContext->GetThreadContext()->GetWindowsGlobalizationLibrary(); | |
| WindowsGlobalizationAdapter* wga = GetWindowsGlobalizationAdapter(scriptContext); | |
| // Verify locale is present | |
| // REVIEW (doilij): Fix comparison of the unsigned value <= 0 | |
| if (!GetTypedPropertyBuiltInFrom(options, __locale, JavascriptString) || (localeJSstr = JavascriptString::FromVar(propertyValue))->GetLength() <= 0) | |
| { | |
| // REVIEW (doilij): Should we throw? Or otherwise, from Intl.js, should detect something didn't work right here... | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| //First we have to determine which formatter(number, percent, or currency) we will be using. | |
| //Note some options might not be present. | |
| AutoCOMPtr<NumberFormatting::INumberFormatter> numberFormatter(nullptr); | |
| PCWSTR locale = localeJSstr->GetSz(); | |
| uint16 formatterToUseVal = 0; // 0 (default) is number, 1 is percent, 2 is currency | |
| if (GetTypedPropertyBuiltInFrom(options, __formatterToUse, TaggedInt) && (formatterToUseVal = TaggedInt::ToUInt16(propertyValue)) == 1) | |
| { | |
| //Use the percent formatter | |
| IfFailThrowHr(wga->CreatePercentFormatter(scriptContext, &locale, 1, &numberFormatter)); | |
| } | |
| else if (formatterToUseVal == 2) | |
| { | |
| //Use the currency formatter | |
| AutoCOMPtr<NumberFormatting::ICurrencyFormatter> currencyFormatter(nullptr); | |
| if (!GetTypedPropertyBuiltInFrom(options, __currency, JavascriptString)) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| //API call retrieves a currency formatter, have to query its interface for numberFormatter | |
| IfFailThrowHr(GetWindowsGlobalizationAdapter(scriptContext)->CreateCurrencyFormatter(scriptContext, &locale, 1, JavascriptString::FromVar(propertyValue)->GetSz(), ¤cyFormatter)); | |
| if (GetTypedPropertyBuiltInFrom(options, __currencyDisplayToUse, TaggedInt)) // 0 is for symbol, 1 is for code, 2 is for name. | |
| //Currently name isn't supported; so it will default to code in that case. | |
| { | |
| AutoCOMPtr<NumberFormatting::ICurrencyFormatter2> currencyFormatter2(nullptr); | |
| IfFailThrowHr(currencyFormatter->QueryInterface(__uuidof(NumberFormatting::ICurrencyFormatter2), reinterpret_cast<void**>(¤cyFormatter2))); | |
| if (TaggedInt::ToUInt16(propertyValue) == 0) | |
| { | |
| IfFailThrowHr(currencyFormatter2->put_Mode(NumberFormatting::CurrencyFormatterMode::CurrencyFormatterMode_UseSymbol)); | |
| } | |
| else | |
| { | |
| IfFailThrowHr(currencyFormatter2->put_Mode(NumberFormatting::CurrencyFormatterMode::CurrencyFormatterMode_UseCurrencyCode)); | |
| } | |
| } | |
| IfFailThrowHr(currencyFormatter->QueryInterface(__uuidof(NumberFormatting::INumberFormatter), reinterpret_cast<void**>(&numberFormatter))); | |
| } | |
| else | |
| { | |
| //Use the number formatter (default) | |
| IfFailThrowHr(wga->CreateNumberFormatter(scriptContext, &locale, 1, &numberFormatter)); | |
| } | |
| Assert(numberFormatter); | |
| AutoCOMPtr<NumberFormatting::ISignedZeroOption> signedZeroOption(nullptr); | |
| IfFailThrowHr(numberFormatter->QueryInterface(__uuidof(NumberFormatting::ISignedZeroOption), reinterpret_cast<void**>(&signedZeroOption))); | |
| IfFailThrowHr(signedZeroOption->put_IsZeroSigned(true)); | |
| //Configure non-digit related options | |
| AutoCOMPtr<NumberFormatting::INumberFormatterOptions> numberFormatterOptions(nullptr); | |
| IfFailThrowHr(numberFormatter->QueryInterface(__uuidof(NumberFormatting::INumberFormatterOptions), reinterpret_cast<void**>(&numberFormatterOptions))); | |
| Assert(numberFormatterOptions); | |
| if (GetTypedPropertyBuiltInFrom(options, __isDecimalPointAlwaysDisplayed, JavascriptBoolean)) | |
| { | |
| IfFailThrowHr(numberFormatterOptions->put_IsDecimalPointAlwaysDisplayed((boolean)(JavascriptBoolean::FromVar(propertyValue)->GetValue()))); | |
| } | |
| if (GetTypedPropertyBuiltInFrom(options, __useGrouping, JavascriptBoolean)) | |
| { | |
| IfFailThrowHr(numberFormatterOptions->put_IsGrouped((boolean)(JavascriptBoolean::FromVar(propertyValue)->GetValue()))); | |
| } | |
| //Get the numeral system and add it to the object since it will be located in the locale | |
| AutoHSTRING hNumeralSystem; | |
| AutoHSTRING hResolvedLanguage; | |
| uint32 length; | |
| IfFailThrowHr(wga->GetNumeralSystem(numberFormatterOptions, &hNumeralSystem)); | |
| SetHSTRINGPropertyBuiltInOn(options, __numberingSystem, *hNumeralSystem); | |
| IfFailThrowHr(wga->GetResolvedLanguage(numberFormatterOptions, &hResolvedLanguage)); | |
| SetHSTRINGPropertyBuiltInOn(options, __locale, *hResolvedLanguage); | |
| AutoCOMPtr<NumberFormatting::INumberRounderOption> rounderOptions(nullptr); | |
| IfFailThrowHr(numberFormatter->QueryInterface(__uuidof(NumberFormatting::INumberRounderOption), reinterpret_cast<void**>(&rounderOptions))); | |
| Assert(rounderOptions); | |
| if (HasPropertyBuiltInOn(options, __minimumSignificantDigits) || HasPropertyBuiltInOn(options, __maximumSignificantDigits)) | |
| { | |
| uint16 minSignificantDigits = 1, maxSignificantDigits = 21; | |
| //Do significant digit rounding | |
| if (GetTypedPropertyBuiltInFrom(options, __minimumSignificantDigits, TaggedInt)) | |
| { | |
| minSignificantDigits = max<uint16>(min<uint16>(TaggedInt::ToUInt16(propertyValue), 21), 1); | |
| } | |
| if (GetTypedPropertyBuiltInFrom(options, __maximumSignificantDigits, TaggedInt)) | |
| { | |
| maxSignificantDigits = max<uint16>(min<uint16>(TaggedInt::ToUInt16(propertyValue), 21), minSignificantDigits); | |
| } | |
| prepareWithSignificantDigits(scriptContext, rounderOptions, numberFormatter, numberFormatterOptions, minSignificantDigits, maxSignificantDigits); | |
| } | |
| else | |
| { | |
| uint16 minFractionDigits = 0, maxFractionDigits = 3, minIntegerDigits = 1; | |
| //Do fraction/integer digit rounding | |
| if (GetTypedPropertyBuiltInFrom(options, __minimumIntegerDigits, TaggedInt)) | |
| { | |
| minIntegerDigits = max<uint16>(min<uint16>(TaggedInt::ToUInt16(propertyValue), 21), 1); | |
| } | |
| if (GetTypedPropertyBuiltInFrom(options, __minimumFractionDigits, TaggedInt)) | |
| { | |
| minFractionDigits = min<uint16>(TaggedInt::ToUInt16(propertyValue), 20);//ToUInt16 will get rid of negatives by making them high | |
| } | |
| if (GetTypedPropertyBuiltInFrom(options, __maximumFractionDigits, TaggedInt)) | |
| { | |
| maxFractionDigits = max(min<uint16>(TaggedInt::ToUInt16(propertyValue), 20), minFractionDigits);//ToUInt16 will get rid of negatives by making them high | |
| } | |
| prepareWithFractionIntegerDigits(scriptContext, rounderOptions, numberFormatterOptions, minFractionDigits, maxFractionDigits + (formatterToUseVal == 1 ? 2 : 0), minIntegerDigits);//extend max fractions for percent | |
| } | |
| //Set the object as a cache | |
| numberFormatter->AddRef(); | |
| options->SetInternalProperty(Js::InternalPropertyIds::HiddenObject, AutoCOMJSObject::New(scriptContext->GetRecycler(), numberFormatter), Js::PropertyOperationFlags::PropertyOperation_None, NULL); | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| #endif | |
| } | |
| // Unlike CacheNumberFormat; this call takes an additional parameter to specify whether we are going to cache it. | |
| // We have to create this formatter twice; first time get the date/time patterns; and second time cache with correct format string. | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_CreateDateTimeFormat(RecyclableObject * function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| if (args.Info.Count < 3 || !DynamicObject::Is(args.Values[1]) || !JavascriptBoolean::Is(args.Values[2])) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| #ifdef INTL_WINGLOB | |
| DynamicObject* obj = DynamicObject::FromVar(args.Values[1]); | |
| DelayLoadWindowsGlobalization* wgl = scriptContext->GetThreadContext()->GetWindowsGlobalizationLibrary(); | |
| WindowsGlobalizationAdapter* wga = GetWindowsGlobalizationAdapter(scriptContext); | |
| HRESULT hr; | |
| Var propertyValue = nullptr; | |
| uint32 length; | |
| PCWSTR locale = GetTypedPropertyBuiltInFrom(obj, __locale, JavascriptString) ? JavascriptString::FromVar(propertyValue)->GetSz() : nullptr; | |
| PCWSTR templateString = GetTypedPropertyBuiltInFrom(obj, __templateString, JavascriptString) ? JavascriptString::FromVar(propertyValue)->GetSz() : nullptr; | |
| if (locale == nullptr || templateString == nullptr) | |
| { | |
| AssertMsg(false, "For some reason, locale and templateString aren't defined or aren't a JavascriptString."); | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| PCWSTR clock = GetTypedPropertyBuiltInFrom(obj, __windowsClock, JavascriptString) ? JavascriptString::FromVar(propertyValue)->GetSz() : nullptr; | |
| AutoHSTRING hDummyCalendar; | |
| if (clock != nullptr) | |
| { | |
| //Because both calendar and clock are needed to pass into the datetimeformatter constructor (or neither); create a dummy one to get the value of calendar out so clock can be passed in with it. | |
| AutoCOMPtr<DateTimeFormatting::IDateTimeFormatter> dummyFormatter; | |
| IfFailThrowHr(wga->CreateDateTimeFormatter(scriptContext, templateString, &locale, 1, nullptr, nullptr, &dummyFormatter)); | |
| IfFailThrowHr(wga->GetCalendar(dummyFormatter, &hDummyCalendar)); | |
| } | |
| //Now create the real formatter. | |
| AutoCOMPtr<DateTimeFormatting::IDateTimeFormatter> cachedFormatter; | |
| IfFailThrowHr(wga->CreateDateTimeFormatter(scriptContext, templateString, &locale, 1, | |
| clock == nullptr ? nullptr : wgl->WindowsGetStringRawBuffer(*hDummyCalendar, &length), clock, &cachedFormatter)); | |
| AutoHSTRING hCalendar; | |
| AutoHSTRING hClock; | |
| AutoHSTRING hLocale; | |
| AutoHSTRING hNumberingSystem; | |
| //In case the upper code path wasn't hit; extract the calendar string again so it can be set. | |
| IfFailThrowHr(wga->GetCalendar(cachedFormatter, &hCalendar)); | |
| SetHSTRINGPropertyBuiltInOn(obj, __windowsCalendar, *hCalendar); | |
| IfFailThrowHr(wga->GetClock(cachedFormatter, &hClock)); | |
| SetHSTRINGPropertyBuiltInOn(obj, __windowsClock, *hClock); | |
| IfFailThrowHr(wga->GetResolvedLanguage(cachedFormatter, &hLocale)); | |
| SetHSTRINGPropertyBuiltInOn(obj, __locale, *hLocale); | |
| //Get the numbering system | |
| IfFailThrowHr(wga->GetNumeralSystem(cachedFormatter, &hNumberingSystem)); | |
| SetHSTRINGPropertyBuiltInOn(obj, __numberingSystem, *hNumberingSystem); | |
| //Extract the pattern strings | |
| AutoCOMPtr<Windows::Foundation::Collections::IVectorView<HSTRING>> dateResult; | |
| IfFailThrowHr(cachedFormatter->get_Patterns(&dateResult)); | |
| IfFailThrowHr(dateResult->get_Size(&length)); | |
| JavascriptArray *patternStrings = scriptContext->GetLibrary()->CreateArray(length); | |
| for (uint32 i = 0; i < length; i++) | |
| { | |
| AutoHSTRING item; | |
| IfFailThrowHr(wga->GetItemAt(dateResult, i, &item)); | |
| patternStrings->SetItem(i, Js::JavascriptString::NewCopySz(wgl->WindowsGetStringRawBuffer(*item, NULL), scriptContext), PropertyOperation_None); | |
| } | |
| SetPropertyBuiltInOn(obj, __patternStrings, patternStrings); | |
| //This parameter tells us whether we are caching it this time around; or just validating pattern strings | |
| if ((boolean)(JavascriptBoolean::FromVar(args.Values[2])->GetValue())) | |
| { | |
| //If timeZone is undefined; then use the standard dateTimeFormatter to format in local time; otherwise use the IDateTimeFormatter2 to format using specified timezone (UTC) | |
| if (!GetPropertyBuiltInFrom(obj, __timeZone) || JavascriptOperators::IsUndefinedObject(propertyValue)) | |
| { | |
| cachedFormatter->AddRef(); | |
| obj->SetInternalProperty(Js::InternalPropertyIds::HiddenObject, AutoCOMJSObject::New(scriptContext->GetRecycler(), cachedFormatter), Js::PropertyOperationFlags::PropertyOperation_None, NULL); | |
| } | |
| else | |
| { | |
| AutoCOMPtr<DateTimeFormatting::IDateTimeFormatter2> tzCachedFormatter; | |
| IfFailThrowHr(cachedFormatter->QueryInterface(__uuidof(DateTimeFormatting::IDateTimeFormatter2), reinterpret_cast<void**>(&tzCachedFormatter))); | |
| tzCachedFormatter->AddRef(); | |
| //Set the object as a cache | |
| obj->SetInternalProperty(Js::InternalPropertyIds::HiddenObject, AutoCOMJSObject::New(scriptContext->GetRecycler(), tzCachedFormatter), Js::PropertyOperationFlags::PropertyOperation_None, NULL); | |
| } | |
| } | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| #else | |
| // TODO (doilij): implement INTL_ICU version | |
| #ifdef INTL_ICU_DEBUG | |
| Output::Print(_u("EntryIntl_CreateDateTimeFormat > returning null, fallback to JS\n")); | |
| #endif | |
| return scriptContext->GetLibrary()->GetNull(); | |
| #endif | |
| } | |
| DWORD getFlagsForSensitivity(LPCWSTR sensitivity) | |
| { | |
| if (wcscmp(sensitivity, _u("base")) == 0) | |
| { | |
| return LINGUISTIC_IGNOREDIACRITIC | LINGUISTIC_IGNORECASE | NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH; | |
| } | |
| else if (wcscmp(sensitivity, _u("accent")) == 0) | |
| { | |
| return LINGUISTIC_IGNORECASE | NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH; | |
| } | |
| else if (wcscmp(sensitivity, _u("case")) == 0) | |
| { | |
| return NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH | LINGUISTIC_IGNOREDIACRITIC; | |
| } | |
| else if (wcscmp(sensitivity, _u("variant")) == 0) | |
| { | |
| return NORM_LINGUISTIC_CASING; | |
| } | |
| return 0; | |
| } | |
| // Takes arguments as follows(all required): | |
| // - [1] - String 1 for comparison | |
| // - [2] - String 2 for comparison | |
| // - [3] - Locale string (or undefined) | |
| // - [4] - Sensitivity string (or undefined) | |
| // - [5] - IgnorePunctuation boolean (or undefined) | |
| // - [6] - Numeric boolean (or undefined) | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_CompareString(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| if (args.Info.Count < 7 || !JavascriptString::Is(args.Values[1]) || !JavascriptString::Is(args.Values[2])) | |
| { | |
| // CompareStringEx of undefined or non-strings is undefined | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| DWORD compareFlags = 0; | |
| int compareResult = 0; | |
| DWORD lastError = S_OK; | |
| WCHAR defaultLocale[LOCALE_NAME_MAX_LENGTH]; | |
| const char16 *givenLocale = nullptr; | |
| defaultLocale[0] = '\0'; | |
| JavascriptString *str1 = JavascriptString::FromVar(args.Values[1]); | |
| JavascriptString *str2 = JavascriptString::FromVar(args.Values[2]); | |
| if (!JavascriptOperators::IsUndefinedObject(args.Values[3])) | |
| { | |
| if (!JavascriptString::Is(args.Values[3])) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| givenLocale = JavascriptString::FromVar(args.Values[3])->GetSz(); | |
| } | |
| if (!JavascriptOperators::IsUndefinedObject(args.Values[4])) | |
| { | |
| if (!JavascriptString::Is(args.Values[4])) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| compareFlags |= getFlagsForSensitivity(JavascriptString::FromVar(args.Values[4])->GetSz()); | |
| } | |
| else | |
| { | |
| compareFlags |= NORM_LINGUISTIC_CASING; | |
| } | |
| if (!JavascriptOperators::IsUndefinedObject(args.Values[5])) | |
| { | |
| if (!JavascriptBoolean::Is(args.Values[5])) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| else if ((boolean)(JavascriptBoolean::FromVar(args.Values[5])->GetValue())) | |
| { | |
| compareFlags |= NORM_IGNORESYMBOLS; | |
| } | |
| } | |
| if (!JavascriptOperators::IsUndefinedObject(args.Values[6])) | |
| { | |
| if (!JavascriptBoolean::Is(args.Values[6])) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| else if ((boolean)(JavascriptBoolean::FromVar(args.Values[6])->GetValue())) | |
| { | |
| compareFlags |= SORT_DIGITSASNUMBERS; | |
| } | |
| } | |
| if (givenLocale == nullptr && | |
| #if defined(INTL_WINGLOB) | |
| // XPLAT-TODO (doilij): Implement this in PlatformAgnostic | |
| GetUserDefaultLocaleName(defaultLocale, _countof(defaultLocale)) == 0 | |
| #else | |
| GetUserDefaultLanguageTag(defaultLocale, _countof(defaultLocale)) == 0 | |
| #endif | |
| ) | |
| { | |
| JavascriptError::MapAndThrowError(scriptContext, HRESULT_FROM_WIN32(GetLastError())); | |
| } | |
| BEGIN_TEMP_ALLOCATOR(tempAllocator, scriptContext, _u("localeCompare")) | |
| { | |
| using namespace PlatformAgnostic; | |
| char16 * aLeft = nullptr; | |
| char16 * aRight = nullptr; | |
| charcount_t size1 = 0; | |
| charcount_t size2 = 0; | |
| auto canonicalEquivalentForm = UnicodeText::NormalizationForm::C; | |
| if (!UnicodeText::IsNormalizedString(canonicalEquivalentForm, str1->GetSz(), -1)) | |
| { | |
| aLeft = str1->GetNormalizedString(canonicalEquivalentForm, tempAllocator, size1); | |
| } | |
| if (!UnicodeText::IsNormalizedString(canonicalEquivalentForm, str2->GetSz(), -1)) | |
| { | |
| aRight = str2->GetNormalizedString(canonicalEquivalentForm, tempAllocator, size2); | |
| } | |
| if (aLeft == nullptr) | |
| { | |
| aLeft = const_cast<char16*>(str1->GetSz()); | |
| size1 = str1->GetLength(); | |
| } | |
| if (aRight == nullptr) | |
| { | |
| aRight = const_cast<char16*>(str2->GetSz()); | |
| size2 = str2->GetLength(); | |
| } | |
| #ifdef _WIN32 | |
| // xplat-todo: Need to replace this with platform-agnostic API | |
| compareResult = CompareStringEx(givenLocale != nullptr ? givenLocale : defaultLocale, compareFlags, aLeft, size1, aRight, size2, NULL, NULL, 0); | |
| #else | |
| // FIXME (doilij): when CompareStringEx is implemented in PlatformAgnostic, call that function here | |
| compareResult = 2; // 2 means strings are equal (reasonable default) | |
| #endif | |
| // Get the last error code so that it won't be affected by END_TEMP_ALLOCATOR. | |
| if (compareResult == 0) | |
| { | |
| lastError = GetLastError(); | |
| } | |
| } | |
| END_TEMP_ALLOCATOR(tempAllocator, scriptContext); | |
| // CompareStringEx returns 1, 2, 3 on success; 2 if the strings are equal, 1 if the first string is lexically less than second, 3 otherwise. | |
| if (compareResult != 0) | |
| { | |
| return JavascriptNumber::ToVar(compareResult - 2, scriptContext); // Convert 1,2,3 to -1,0,1 | |
| } | |
| JavascriptError::MapAndThrowError(scriptContext, HRESULT_FROM_WIN32(lastError)); | |
| } | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_CurrencyDigits(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| if (args.Info.Count < 2 || !JavascriptString::Is(args.Values[1])) | |
| { | |
| // Call with undefined or non-string is undefined | |
| return scriptContext->GetLibrary()->GetFalse(); | |
| } | |
| JavascriptString *argString = JavascriptString::FromVar(args.Values[1]); | |
| const char16 *currencyCode = argString->GetSz(); | |
| #if defined(INTL_ICU) | |
| int32_t digits = GetCurrencyFractionDigits(currencyCode); | |
| return JavascriptNumber::ToVar(digits, scriptContext); | |
| #else | |
| HRESULT hr; | |
| AutoCOMPtr<NumberFormatting::ICurrencyFormatter> currencyFormatter(nullptr); | |
| IfFailThrowHr(GetWindowsGlobalizationAdapter(scriptContext)->CreateCurrencyFormatterCode(scriptContext, currencyCode, ¤cyFormatter)); | |
| AutoCOMPtr<NumberFormatting::INumberFormatterOptions> numberFormatterOptions; | |
| IfFailThrowHr(currencyFormatter->QueryInterface(__uuidof(NumberFormatting::INumberFormatterOptions), reinterpret_cast<void**>(&numberFormatterOptions))); | |
| Assert(numberFormatterOptions); | |
| INT32 fractionDigits; | |
| IfFailThrowHr(numberFormatterOptions->get_FractionDigits(&fractionDigits)); | |
| return JavascriptNumber::ToVar(fractionDigits, scriptContext); | |
| #endif | |
| } | |
| #ifdef INTL_WINGLOB | |
| //Helper, this just prepares based on fraction and integer format options | |
| void IntlEngineInterfaceExtensionObject::prepareWithFractionIntegerDigits(ScriptContext* scriptContext, NumberFormatting::INumberRounderOption* rounderOptions, | |
| NumberFormatting::INumberFormatterOptions* formatterOptions, uint16 minFractionDigits, uint16 maxFractionDigits, uint16 minIntegerDigits) | |
| { | |
| HRESULT hr; | |
| WindowsGlobalizationAdapter* wga = GetWindowsGlobalizationAdapter(scriptContext); | |
| AutoCOMPtr<NumberFormatting::INumberRounder> numberRounder(nullptr); | |
| AutoCOMPtr<NumberFormatting::IIncrementNumberRounder> incrementNumberRounder(nullptr); | |
| IfFailThrowHr(wga->CreateIncrementNumberRounder(scriptContext, &numberRounder)); | |
| IfFailThrowHr(numberRounder->QueryInterface(__uuidof(NumberFormatting::IIncrementNumberRounder), reinterpret_cast<void**>(&incrementNumberRounder))); | |
| Assert(incrementNumberRounder); | |
| IfFailThrowHr(incrementNumberRounder->put_RoundingAlgorithm(Windows::Globalization::NumberFormatting::RoundingAlgorithm::RoundingAlgorithm_RoundHalfAwayFromZero)); | |
| IfFailThrowHr(incrementNumberRounder->put_Increment(pow(10.0, -maxFractionDigits))); | |
| IfFailThrowHr(rounderOptions->put_NumberRounder(numberRounder)); | |
| IfFailThrowHr(formatterOptions->put_FractionDigits(minFractionDigits)); | |
| IfFailThrowHr(formatterOptions->put_IntegerDigits(minIntegerDigits)); | |
| } | |
| //Helper, this just prepares based on significant digits format options | |
| void IntlEngineInterfaceExtensionObject::prepareWithSignificantDigits(ScriptContext* scriptContext, NumberFormatting::INumberRounderOption* rounderOptions, NumberFormatting::INumberFormatter *numberFormatter, | |
| NumberFormatting::INumberFormatterOptions* formatterOptions, uint16 minSignificantDigits, uint16 maxSignificantDigits) | |
| { | |
| HRESULT hr; | |
| WindowsGlobalizationAdapter* wga = GetWindowsGlobalizationAdapter(scriptContext); | |
| AutoCOMPtr<NumberFormatting::INumberRounder> numberRounder(nullptr); | |
| AutoCOMPtr<NumberFormatting::ISignificantDigitsNumberRounder> incrementNumberRounder(nullptr); | |
| AutoCOMPtr<NumberFormatting::ISignificantDigitsOption> significantDigitsOptions(nullptr); | |
| IfFailThrowHr(wga->CreateSignificantDigitsRounder(scriptContext, &numberRounder)); | |
| IfFailThrowHr(numberRounder->QueryInterface(__uuidof(NumberFormatting::ISignificantDigitsNumberRounder), reinterpret_cast<void**>(&incrementNumberRounder))); | |
| Assert(incrementNumberRounder); | |
| IfFailThrowHr(incrementNumberRounder->put_RoundingAlgorithm(Windows::Globalization::NumberFormatting::RoundingAlgorithm::RoundingAlgorithm_RoundHalfAwayFromZero)); | |
| IfFailThrowHr(incrementNumberRounder->put_SignificantDigits(maxSignificantDigits)); | |
| IfFailThrowHr(rounderOptions->put_NumberRounder(numberRounder)); | |
| IfFailThrowHr(numberFormatter->QueryInterface(__uuidof(NumberFormatting::ISignificantDigitsOption), reinterpret_cast<void**>(&significantDigitsOptions))); | |
| IfFailThrowHr(significantDigitsOptions->put_SignificantDigits(minSignificantDigits)); | |
| Assert(significantDigitsOptions); | |
| //Clear minimum fraction digits as in the case of percent 2 is supplied | |
| IfFailThrowHr(formatterOptions->put_FractionDigits(0)); | |
| } | |
| #endif | |
| /* | |
| * This function has the following options: | |
| * - Format as Percent. | |
| * - Format as Number. | |
| * - If significant digits are present, format using the significant digts; | |
| * - Otherwise format using minimumFractionDigits, maximumFractionDigits, minimumIntegerDigits | |
| */ | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_FormatNumber(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| //First argument is required and must be either a tagged integer or a number; second is also required and is the internal state object | |
| if (args.Info.Count < 3 || !(TaggedInt::Is(args.Values[1]) || JavascriptNumber::Is(args.Values[1])) || !DynamicObject::Is(args.Values[2])) | |
| { | |
| // Call with undefined or non-number is undefined | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| DynamicObject *options = DynamicObject::FromVar(args.Values[2]); | |
| Var hiddenObject = nullptr; | |
| AssertOrFailFastMsg(options->GetInternalProperty(options, Js::InternalPropertyIds::HiddenObject, &hiddenObject, NULL, scriptContext), | |
| "EntryIntl_FormatNumber: Could not retrieve hiddenObject."); | |
| #if defined(INTL_ICU) | |
| // REVIEW (doilij): Assumes the logic doesn't allow us to get to this point such that this cast is invalid (otherwise, we would throw earlier). | |
| auto *numberFormatter = reinterpret_cast<AutoIcuJsObject<IPlatformAgnosticResource> *>(hiddenObject)->GetInstance(); | |
| const char16 *strBuf = nullptr; | |
| Var propertyValue = nullptr; | |
| NumberFormatStyle formatterToUse = NumberFormatStyle::DEFAULT; | |
| NumberFormatCurrencyDisplay currencyDisplay = NumberFormatCurrencyDisplay::DEFAULT; | |
| JavascriptString *currencyCodeJsString = nullptr; | |
| // It is okay for currencyCode to be nullptr if we are NOT formatting a currency. | |
| // If we are formatting a currency, the Intl.js logic will ensure __currency is set correctly or otherwise will throw so we don't reach here. | |
| const char16 *currencyCode = nullptr; | |
| if (GetTypedPropertyBuiltInFrom(options, __formatterToUse, TaggedInt)) | |
| { | |
| formatterToUse = static_cast<NumberFormatStyle>(TaggedInt::ToUInt16(propertyValue)); | |
| } | |
| if (GetTypedPropertyBuiltInFrom(options, __currencyDisplayToUse, TaggedInt)) | |
| { | |
| currencyDisplay = static_cast<NumberFormatCurrencyDisplay>(TaggedInt::ToUInt16(propertyValue)); | |
| } | |
| if (GetTypedPropertyBuiltInFrom(options, __currency, JavascriptString)) | |
| { | |
| currencyCodeJsString = JavascriptString::FromVar(propertyValue); | |
| currencyCode = currencyCodeJsString->GetSz(); | |
| } | |
| if (TaggedInt::Is(args.Values[1])) | |
| { | |
| int32 val = TaggedInt::ToInt32(args.Values[1]); | |
| strBuf = FormatNumber(numberFormatter, val, formatterToUse, currencyDisplay, currencyCode); | |
| } | |
| else | |
| { | |
| double val = JavascriptNumber::GetValue(args.Values[1]); | |
| strBuf = FormatNumber(numberFormatter, val, formatterToUse, currencyDisplay, currencyCode); | |
| } | |
| StringBufferAutoPtr<char16> guard(strBuf); // ensure strBuf is deleted no matter what | |
| #else | |
| DelayLoadWindowsGlobalization* wsl = scriptContext->GetThreadContext()->GetWindowsGlobalizationLibrary(); | |
| NumberFormatting::INumberFormatter *numberFormatter; | |
| numberFormatter = static_cast<NumberFormatting::INumberFormatter *>(((AutoCOMJSObject *)hiddenObject)->GetInstance()); | |
| AutoHSTRING result; | |
| HRESULT hr; | |
| if (TaggedInt::Is(args.Values[1])) | |
| { | |
| IfFailThrowHr(numberFormatter->FormatInt(TaggedInt::ToInt32(args.Values[1]), &result)); | |
| } | |
| else | |
| { | |
| IfFailThrowHr(numberFormatter->FormatDouble(JavascriptNumber::GetValue(args.Values[1]), &result)); | |
| } | |
| PCWSTR strBuf = wsl->WindowsGetStringRawBuffer(*result, NULL); | |
| #endif | |
| if (strBuf == nullptr) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| else | |
| { | |
| JavascriptStringObject *retVal = scriptContext->GetLibrary()->CreateStringObject(Js::JavascriptString::NewCopySz(strBuf, scriptContext)); | |
| return retVal; | |
| } | |
| } | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_FormatDateTime(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| if (args.Info.Count < 3 || !(TaggedInt::Is(args.Values[1]) || JavascriptNumber::Is(args.Values[1])) || !DynamicObject::Is(args.Values[2])) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| #ifdef INTL_WINGLOB | |
| Windows::Foundation::DateTime winDate; | |
| HRESULT hr; | |
| if (TaggedInt::Is(args.Values[1])) | |
| { | |
| hr = Js::DateUtilities::ES5DateToWinRTDate(TaggedInt::ToInt32(args.Values[1]), &(winDate.UniversalTime)); | |
| } | |
| else | |
| { | |
| hr = Js::DateUtilities::ES5DateToWinRTDate(JavascriptNumber::GetValue(args.Values[1]), &(winDate.UniversalTime)); | |
| } | |
| if (FAILED(hr)) | |
| { | |
| HandleOOMSOEHR(hr); | |
| // If conversion failed, double value is outside the range of WinRT DateTime | |
| Js::JavascriptError::ThrowRangeError(scriptContext, JSERR_OutOfDateTimeRange); | |
| } | |
| DynamicObject* obj = DynamicObject::FromVar(args.Values[2]); | |
| Var hiddenObject = nullptr; | |
| AssertOrFailFastMsg(obj->GetInternalProperty(obj, Js::InternalPropertyIds::HiddenObject, &hiddenObject, NULL, scriptContext), | |
| "EntryIntl_FormatDateTime: Could not retrieve hiddenObject."); | |
| //We are going to perform the same check for timeZone as when caching the formatter. | |
| Var propertyValue = nullptr; | |
| AutoHSTRING result; | |
| //If timeZone is undefined; then use the standard dateTimeFormatter to format in local time; otherwise use the IDateTimeFormatter2 to format using specified timezone (UTC) | |
| if (!GetPropertyBuiltInFrom(obj, __timeZone) || JavascriptOperators::IsUndefinedObject(propertyValue)) | |
| { | |
| DateTimeFormatting::IDateTimeFormatter *formatter = static_cast<DateTimeFormatting::IDateTimeFormatter *>(((AutoCOMJSObject *)hiddenObject)->GetInstance()); | |
| Assert(formatter); | |
| IfFailThrowHr(formatter->Format(winDate, &result)); | |
| } | |
| else | |
| { | |
| DateTimeFormatting::IDateTimeFormatter2 *formatter = static_cast<DateTimeFormatting::IDateTimeFormatter2 *>(((AutoCOMJSObject *)hiddenObject)->GetInstance()); | |
| Assert(formatter); | |
| HSTRING timeZone; | |
| HSTRING_HEADER timeZoneHeader; | |
| // IsValidTimeZone() has already verified that this is JavascriptString. | |
| JavascriptString* userDefinedTimeZoneId = JavascriptString::FromVar(propertyValue); | |
| IfFailThrowHr(WindowsCreateStringReference(userDefinedTimeZoneId->GetSz(), userDefinedTimeZoneId->GetLength(), &timeZoneHeader, &timeZone)); | |
| Assert(timeZone); | |
| IfFailThrowHr(formatter->FormatUsingTimeZone(winDate, timeZone, &result)); | |
| } | |
| PCWSTR strBuf = scriptContext->GetThreadContext()->GetWindowsGlobalizationLibrary()->WindowsGetStringRawBuffer(*result, NULL); | |
| return Js::JavascriptString::NewCopySz(strBuf, scriptContext); | |
| #else | |
| // TODO (doilij): implement INTL_ICU version | |
| #ifdef INTL_ICU_DEBUG | |
| Output::Print(_u("EntryIntl_FormatDateTime > returning null, fallback to JS\n")); | |
| #endif | |
| return scriptContext->GetLibrary()->GetNull(); | |
| #endif | |
| } | |
| /* | |
| * This function validates the timeZone passed by user has defined in IsValidTimeZoneName() section | |
| * of ECMA-402 dated June 2015. | |
| * Returns true if timeZoneId is a valid zone or link name of the IANA time zone database | |
| */ | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_ValidateAndCanonicalizeTimeZone(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| // Return false if timeZoneId is not string | |
| if (args.Info.Count < 2 || !JavascriptString::Is(args.Values[1])) | |
| { | |
| AssertMsg(false, "Need valid timeZoneId"); | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| #ifdef INTL_WINGLOB | |
| JavascriptString *argString = JavascriptString::FromVar(args.Values[1]); | |
| AutoHSTRING canonicalizedTimeZone; | |
| boolean isValidTimeZone = GetWindowsGlobalizationAdapter(scriptContext)->ValidateAndCanonicalizeTimeZone(scriptContext, argString->GetSz(), &canonicalizedTimeZone); | |
| if (isValidTimeZone) | |
| { | |
| DelayLoadWindowsGlobalization* wsl = scriptContext->GetThreadContext()->GetWindowsGlobalizationLibrary(); | |
| PCWSTR strBuf = wsl->WindowsGetStringRawBuffer(*canonicalizedTimeZone, NULL); | |
| return Js::JavascriptString::NewCopySz(strBuf, scriptContext); | |
| } | |
| else | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| #else | |
| // TODO (doilij): implement INTL_ICU version | |
| #ifdef INTL_ICU_DEBUG | |
| Output::Print(_u("EntryIntl_ValidateAndCanonicalizeTimeZone > returning null, fallback to JS\n")); | |
| #endif | |
| return scriptContext->GetLibrary()->GetNull(); | |
| #endif | |
| } | |
| /* | |
| * This function returns defaultTimeZone for host's current environment as specified in | |
| * DefaultTimeZone () section of ECMA-402 dated June 2015. | |
| */ | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_GetDefaultTimeZone(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| #ifdef INTL_WINGLOB | |
| WindowsGlobalizationAdapter* wga = GetWindowsGlobalizationAdapter(scriptContext); | |
| DelayLoadWindowsGlobalization* wsl = scriptContext->GetThreadContext()->GetWindowsGlobalizationLibrary(); | |
| AutoHSTRING str; | |
| HRESULT hr; | |
| if (FAILED(hr = wga->GetDefaultTimeZoneId(scriptContext, &str))) | |
| { | |
| HandleOOMSOEHR(hr); | |
| //If we can't get default timeZone, return undefined. | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| PCWSTR strBuf = wsl->WindowsGetStringRawBuffer(*str, NULL); | |
| return Js::JavascriptString::NewCopySz(strBuf, scriptContext); | |
| #else | |
| // TODO (doilij): implement INTL_ICU version | |
| #ifdef INTL_ICU_DEBUG | |
| Output::Print(_u("EntryIntl_GetDefaultTimeZone > returning null, fallback to JS\n")); | |
| #endif | |
| return scriptContext->GetLibrary()->GetNull(); | |
| #endif | |
| } | |
| /* | |
| * This function registers built in functions when Intl initializes. | |
| * Call with (Function : toRegister, integer : id) | |
| * ID Mappings: | |
| * - 0 for Date.prototype.toLocaleString | |
| * - 1 for Date.prototype.toLocaleDateString | |
| * - 2 for Date.prototype.toLocaleTimeString | |
| * - 3 for Number.prototype.toLocaleString | |
| * - 4 for String.prototype.localeCompare | |
| */ | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_RegisterBuiltInFunction(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| // Don't put this in a header or add it to the namespace even in this file. Keep it to the minimum scope needed. | |
| enum class IntlBuiltInFunctionID : int32 { | |
| Min = 0, | |
| DateToLocaleString = Min, | |
| DateToLocaleDateString, | |
| DateToLocaleTimeString, | |
| NumberToLocaleString, | |
| StringLocaleCompare, | |
| Max | |
| }; | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| // This function will only be used during the construction of the Intl object, hence Asserts are in place. | |
| Assert(args.Info.Count >= 3 && JavascriptFunction::Is(args.Values[1]) && TaggedInt::Is(args.Values[2])); | |
| JavascriptFunction *func = JavascriptFunction::FromVar(args.Values[1]); | |
| int32 id = TaggedInt::ToInt32(args.Values[2]); | |
| Assert(id >= (int32)IntlBuiltInFunctionID::Min && id < (int32)IntlBuiltInFunctionID::Max); | |
| EngineInterfaceObject* nativeEngineInterfaceObj = scriptContext->GetLibrary()->GetEngineInterfaceObject(); | |
| IntlEngineInterfaceExtensionObject* extensionObject = static_cast<IntlEngineInterfaceExtensionObject*>(nativeEngineInterfaceObj->GetEngineExtension(EngineInterfaceExtensionKind_Intl)); | |
| IntlBuiltInFunctionID functionID = static_cast<IntlBuiltInFunctionID>(id); | |
| switch (functionID) | |
| { | |
| case IntlBuiltInFunctionID::DateToLocaleString: | |
| extensionObject->dateToLocaleString = func; | |
| break; | |
| case IntlBuiltInFunctionID::DateToLocaleDateString: | |
| extensionObject->dateToLocaleDateString = func; | |
| break; | |
| case IntlBuiltInFunctionID::DateToLocaleTimeString: | |
| extensionObject->dateToLocaleTimeString = func; | |
| break; | |
| case IntlBuiltInFunctionID::NumberToLocaleString: | |
| extensionObject->numberToLocaleString = func; | |
| break; | |
| case IntlBuiltInFunctionID::StringLocaleCompare: | |
| extensionObject->stringLocaleCompare = func; | |
| break; | |
| default: | |
| AssertMsg(false, "functionID was not one of the allowed values. The previous assert should catch this."); | |
| break; | |
| } | |
| // Don't need to return anything | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_GetHiddenObject(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| if (callInfo.Count < 2 || !DynamicObject::Is(args.Values[1])) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| DynamicObject* obj = DynamicObject::FromVar(args.Values[1]); | |
| Var hiddenObject = nullptr; | |
| if (!obj->GetInternalProperty(obj, Js::InternalPropertyIds::HiddenObject, &hiddenObject, NULL, scriptContext)) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| return hiddenObject; | |
| } | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_SetHiddenObject(RecyclableObject* function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| if (callInfo.Count < 3 || !DynamicObject::Is(args.Values[1]) || !DynamicObject::Is(args.Values[2])) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| DynamicObject* obj = DynamicObject::FromVar(args.Values[1]); | |
| DynamicObject* value = DynamicObject::FromVar(args.Values[2]); | |
| if (obj->SetInternalProperty(Js::InternalPropertyIds::HiddenObject, value, Js::PropertyOperationFlags::PropertyOperation_None, NULL)) | |
| { | |
| return scriptContext->GetLibrary()->GetTrue(); | |
| } | |
| else | |
| { | |
| return scriptContext->GetLibrary()->GetFalse(); | |
| } | |
| } | |
| /* | |
| * First parameter is the object onto which prototype should be set; second is the value | |
| */ | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_BuiltIn_SetPrototype(RecyclableObject *function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| if (callInfo.Count < 3 || !DynamicObject::Is(args.Values[1]) || !RecyclableObject::Is(args.Values[2])) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| DynamicObject* obj = DynamicObject::FromVar(args.Values[1]); | |
| RecyclableObject* value = RecyclableObject::FromVar(args.Values[2]); | |
| obj->SetPrototype(value); | |
| return obj; | |
| } | |
| /* | |
| * First parameter is the array object. | |
| */ | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_BuiltIn_GetArrayLength(RecyclableObject *function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| if (callInfo.Count < 2) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| if (DynamicObject::IsAnyArray(args.Values[1])) | |
| { | |
| JavascriptArray* arr = JavascriptArray::FromAnyArray(args.Values[1]); | |
| return TaggedInt::ToVarUnchecked(arr->GetLength()); | |
| } | |
| else | |
| { | |
| AssertMsg(false, "Object passed in with unknown type ID, verify Intl.js is correct."); | |
| return TaggedInt::ToVarUnchecked(0); | |
| } | |
| } | |
| /* | |
| * First parameter is the string on which to match. | |
| * Second parameter is the regex object | |
| */ | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_BuiltIn_RegexMatch(RecyclableObject *function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| if (callInfo.Count < 2 || !JavascriptString::Is(args.Values[1]) || !JavascriptRegExp::Is(args.Values[2])) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| JavascriptString *stringToUse = JavascriptString::FromVar(args.Values[1]); | |
| JavascriptRegExp *regexpToUse = JavascriptRegExp::FromVar(args.Values[2]); | |
| return RegexHelper::RegexMatchNoHistory(scriptContext, regexpToUse, stringToUse, false); | |
| } | |
| /* | |
| * First parameter is the function, then its the this arg; so at least 2 are needed. | |
| */ | |
| Var IntlEngineInterfaceExtensionObject::EntryIntl_BuiltIn_CallInstanceFunction(RecyclableObject *function, CallInfo callInfo, ...) | |
| { | |
| EngineInterfaceObject_CommonFunctionProlog(function, callInfo); | |
| Assert(args.Info.Count <= 5); | |
| if (callInfo.Count < 3 || args.Info.Count > 5 || !JavascriptConversion::IsCallable(args.Values[1]) || !RecyclableObject::Is(args.Values[2])) | |
| { | |
| return scriptContext->GetLibrary()->GetUndefined(); | |
| } | |
| RecyclableObject *func = RecyclableObject::FromVar(args.Values[1]); | |
| //Shift the arguments by 2 so argument at index 2 becomes the 'this' argument at index 0 | |
| Var newVars[3]; | |
| Js::Arguments newArgs(callInfo, newVars); | |
| for (uint i = 0; i<args.Info.Count - 2; ++i) | |
| { | |
| newArgs.Values[i] = args.Values[i + 2]; | |
| } | |
| newArgs.Info.Count = args.Info.Count - 2; | |
| return JavascriptFunction::CallFunction<true>(func, func->GetEntryPoint(), newArgs); | |
| } | |
| #endif | |
| } | |
| #endif |