| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeLanguagePch.h"
- #include "RuntimeMathPch.h"
- #include "Library/JavascriptNumberObject.h"
- #include "Library/JavascriptStringObject.h"
- #include "Library/JavascriptSimdObject.h"
- #include "Library/DateImplementation.h"
- #include "Library/JavascriptDate.h"
- namespace Js
- {
- static const double k_2to16 = 65536.0;
- static const double k_2to31 = 2147483648.0;
- static const double k_2to32 = 4294967296.0;
- // ES5 9.10 indicates that this method should throw a TypeError if the supplied value is Undefined or Null.
- // Our implementation returns FALSE in this scenario, expecting the caller to throw the TypeError.
- // This allows the caller to provide more context in the error message without having to unnecessarily
- // construct the message string before knowing whether or not the object is coercible.
- BOOL JavascriptConversion::CheckObjectCoercible(Var aValue, ScriptContext* scriptContext)
- {
- TypeId typeId = JavascriptOperators::GetTypeId(aValue);
- if (typeId == TypeIds_Null || typeId == TypeIds_Undefined)
- {
- return FALSE;
- }
- return TRUE;
- }
- //ES5 9.11 Undefined, Null, Boolean, Number, String - return false
- //If Object has a [[Call]] internal method, then return true, otherwise return false
- bool JavascriptConversion::IsCallable(Var aValue)
- {
- if (!RecyclableObject::Is(aValue))
- {
- return false;
- }
- JavascriptMethod entryPoint = RecyclableObject::FromVar(aValue)->GetEntryPoint();
- return RecyclableObject::DefaultEntryPoint != entryPoint;
- }
- //----------------------------------------------------------------------------
- // ES5 9.12 SameValue algorithm implementation.
- // 1. If Type(x) is different from Type(y), return false.
- // 2. If Type(x) is Undefined, return true.
- // 3. If Type(x) is Null, return true.
- // 4. If Type(x) is Number, then.
- // a. If x is NaN and y is NaN, return true.
- // b. If x is +0 and y is -0, return false.
- // c. If x is -0 and y is +0, return false.
- // d. If x is the same number value as y, return true.
- // e. Return false.
- // 5. If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions); otherwise, return false.
- // 6. If Type(x) is Boolean, return true if x and y are both true or both false; otherwise, return false.
- // 7. Return true if x and y refer to the same object. Otherwise, return false.
- //----------------------------------------------------------------------------
- template<bool zero>
- bool JavascriptConversion::SameValueCommon(Var aLeft, Var aRight)
- {
- TypeId leftType = JavascriptOperators::GetTypeId(aLeft);
- TypeId rightType = JavascriptOperators::GetTypeId(aRight);
- //Check for undefined and null type;
- if (leftType == TypeIds_Undefined )
- {
- return rightType == TypeIds_Undefined;
- }
- if (leftType == TypeIds_Null)
- {
- return rightType == TypeIds_Null;
- }
- double dblLeft, dblRight;
- switch (leftType)
- {
- case TypeIds_Integer:
- switch (rightType)
- {
- case TypeIds_Integer:
- return aLeft == aRight;
- case TypeIds_Number:
- dblLeft = TaggedInt::ToDouble(aLeft);
- dblRight = JavascriptNumber::GetValue(aRight);
- goto CommonNumber;
- case TypeIds_Int64Number:
- {
- int leftValue = TaggedInt::ToInt32(aLeft);
- __int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
- return leftValue == rightValue;
- }
- case TypeIds_UInt64Number:
- {
- int leftValue = TaggedInt::ToInt32(aLeft);
- unsigned __int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
- return leftValue == rightValue;
- }
- }
- break;
- case TypeIds_Int64Number:
- switch (rightType)
- {
- case TypeIds_Integer:
- {
- __int64 leftValue = JavascriptInt64Number::FromVar(aLeft)->GetValue();
- int rightValue = TaggedInt::ToInt32(aRight);
- return leftValue == rightValue;
- }
- case TypeIds_Number:
- dblLeft = (double)JavascriptInt64Number::FromVar(aLeft)->GetValue();
- dblRight = JavascriptNumber::GetValue(aRight);
- goto CommonNumber;
- case TypeIds_Int64Number:
- {
- __int64 leftValue = JavascriptInt64Number::FromVar(aLeft)->GetValue();
- __int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
- return leftValue == rightValue;
- }
- case TypeIds_UInt64Number:
- {
- __int64 leftValue = JavascriptInt64Number::FromVar(aLeft)->GetValue();
- unsigned __int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
- return ((unsigned __int64)leftValue == rightValue);
- }
- }
- break;
- case TypeIds_UInt64Number:
- switch (rightType)
- {
- case TypeIds_Integer:
- {
- unsigned __int64 leftValue = JavascriptUInt64Number::FromVar(aLeft)->GetValue();
- __int64 rightValue = TaggedInt::ToInt32(aRight);
- return (leftValue == (unsigned __int64)rightValue);
- }
- case TypeIds_Number:
- dblLeft = (double)JavascriptUInt64Number::FromVar(aLeft)->GetValue();
- dblRight = JavascriptNumber::GetValue(aRight);
- goto CommonNumber;
- case TypeIds_Int64Number:
- {
- unsigned __int64 leftValue = JavascriptUInt64Number::FromVar(aLeft)->GetValue();
- __int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
- return (leftValue == (unsigned __int64)rightValue);
- }
- case TypeIds_UInt64Number:
- {
- unsigned __int64 leftValue = JavascriptUInt64Number::FromVar(aLeft)->GetValue();
- unsigned __int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
- return leftValue == rightValue;
- }
- }
- break;
- case TypeIds_Number:
- switch (rightType)
- {
- case TypeIds_Integer:
- dblLeft = JavascriptNumber::GetValue(aLeft);
- dblRight = TaggedInt::ToDouble(aRight);
- goto CommonNumber;
- case TypeIds_Int64Number:
- dblLeft = JavascriptNumber::GetValue(aLeft);
- dblRight = (double)JavascriptInt64Number::FromVar(aRight)->GetValue();
- goto CommonNumber;
- case TypeIds_UInt64Number:
- dblLeft = JavascriptNumber::GetValue(aLeft);
- dblRight = (double)JavascriptUInt64Number::FromVar(aRight)->GetValue();
- goto CommonNumber;
- case TypeIds_Number:
- dblLeft = JavascriptNumber::GetValue(aLeft);
- dblRight = JavascriptNumber::GetValue(aRight);
- CommonNumber:
- if (JavascriptNumber::IsNan(dblLeft) && JavascriptNumber::IsNan(dblRight))
- {
- return true;
- }
- if (zero)
- {
- // SameValueZero(+0,-0) returns true;
- return dblLeft == dblRight;
- }
- else
- {
- // SameValue(+0,-0) returns false;
- return (NumberUtilities::LuLoDbl(dblLeft) == NumberUtilities::LuLoDbl(dblRight) &&
- NumberUtilities::LuHiDbl(dblLeft) == NumberUtilities::LuHiDbl(dblRight));
- }
- }
- break;
- case TypeIds_Boolean:
- switch (rightType)
- {
- case TypeIds_Boolean:
- return aLeft == aRight;
- }
- break;
- case TypeIds_String:
- switch (rightType)
- {
- case TypeIds_String:
- return JavascriptString::Equals(aLeft, aRight);
- }
- break;
- case TypeIds_Symbol:
- switch (rightType)
- {
- case TypeIds_Symbol:
- {
- JavascriptSymbol* leftSymbol = JavascriptSymbol::FromVar(aLeft);
- JavascriptSymbol* rightSymbol = JavascriptSymbol::FromVar(aRight);
- return leftSymbol->GetValue() == rightSymbol->GetValue();
- }
- }
- return false;
- default:
- break;
- }
- return aLeft == aRight;
- }
- template bool JavascriptConversion::SameValueCommon<false>(Var aLeft, Var aRight);
- template bool JavascriptConversion::SameValueCommon<true>(Var aLeft, Var aRight);
- //----------------------------------------------------------------------------
- // ToObject() takes a value and converts it to an Object type
- // Implementation of ES5 9.9
- // The spec indicates that this method should throw a TypeError if the supplied value is Undefined or Null.
- // Our implementation returns FALSE in this scenario, expecting the caller to throw the TypeError.
- // This allows the caller to provide more context in the error message without having to unnecessarily
- // construct the message string before knowing whether or not the value can be converted to an object.
- //
- // Undefined Return FALSE.
- // Null Return FALSE.
- // Boolean Create a new Boolean object whose [[PrimitiveValue]]
- // internal property is set to the value of the boolean.
- // See 15.6 for a description of Boolean objects.
- // Return TRUE.
- // Number Create a new Number object whose [[PrimitiveValue]]
- // internal property is set to the value of the number.
- // See 15.7 for a description of Number objects.
- // Return TRUE.
- // String Create a new String object whose [[PrimitiveValue]]
- // internal property is set to the value of the string.
- // See 15.5 for a description of String objects.
- // Return TRUE.
- // Object The result is the input argument (no conversion).
- // Return TRUE.
- //----------------------------------------------------------------------------
- BOOL JavascriptConversion::ToObject(Var aValue, ScriptContext* scriptContext, RecyclableObject** object)
- {
- Assert(object);
- switch (JavascriptOperators::GetTypeId(aValue))
- {
- case TypeIds_Undefined:
- case TypeIds_Null:
- return FALSE;
- case TypeIds_Number:
- case TypeIds_Integer:
- *object = scriptContext->GetLibrary()->CreateNumberObject(aValue);
- return TRUE;
- default:
- {
- #ifdef ENABLE_SIMDJS
- if (SIMDUtils::IsSimdType(aValue))
- {
- *object = scriptContext->GetLibrary()->CreateSIMDObject(aValue, JavascriptOperators::GetTypeId(aValue));
- }
- else
- #endif
- {
- *object = RecyclableObject::FromVar(aValue)->ToObject(scriptContext);
- }
- return TRUE;
- }
- }
- }
- //----------------------------------------------------------------------------
- // ToPropertyKey() takes a value and converts it to a property key
- // Implementation of ES6 7.1.14
- //----------------------------------------------------------------------------
- void JavascriptConversion::ToPropertyKey(Var argument, ScriptContext* scriptContext, const PropertyRecord** propertyRecord)
- {
- Var key = JavascriptConversion::ToPrimitive(argument, JavascriptHint::HintString, scriptContext);
- if (JavascriptSymbol::Is(key))
- {
- // If we are looking up a property keyed by a symbol, we already have the PropertyId in the symbol
- *propertyRecord = JavascriptSymbol::FromVar(key)->GetValue();
- }
- else
- {
- // For all other types, convert the key into a string and use that as the property name
- JavascriptString * propName = JavascriptConversion::ToString(key, scriptContext);
- if (VirtualTableInfo<Js::PropertyString>::HasVirtualTable(propName))
- {
- PropertyString * propertyString = (PropertyString *)propName;
- *propertyRecord = propertyString->GetPropertyRecord();
- }
- else if (VirtualTableInfo<Js::LiteralStringWithPropertyStringPtr>::HasVirtualTable(propName))
- {
- LiteralStringWithPropertyStringPtr * str = (LiteralStringWithPropertyStringPtr *)propName;
- if (str->GetPropertyString())
- {
- *propertyRecord = str->GetPropertyString()->GetPropertyRecord();
- }
- else
- {
- scriptContext->GetOrAddPropertyRecord(propName->GetString(), propName->GetLength(), propertyRecord);
- PropertyString * propStr = scriptContext->GetPropertyString((*propertyRecord)->GetPropertyId());
- str->SetPropertyString(propStr);
- }
- }
- else
- {
- scriptContext->GetOrAddPropertyRecord(propName->GetString(), propName->GetLength(), propertyRecord);
- }
- }
- }
- //----------------------------------------------------------------------------
- // ToPrimitive() takes a value and an optional argument and converts it to a non Object type
- // Implementation of ES5 9.1
- //
- // Undefined:The result equals the input argument (no conversion).
- // Null: The result equals the input argument (no conversion).
- // Boolean: The result equals the input argument (no conversion).
- // Number: The result equals the input argument (no conversion).
- // String: The result equals the input argument (no conversion).
- // VariantDate:Returns the value for variant date by calling ToPrimitve directly.
- // Object: Return a default value for the Object.
- // The default value of an object is retrieved by calling the [[DefaultValue]]
- // internal method of the object, passing the optional hint PreferredType.
- // The behavior of the [[DefaultValue]] internal method is defined by this specification
- // for all native ECMAScript objects (8.12.9).
- //----------------------------------------------------------------------------
- Var JavascriptConversion::ToPrimitive(Var aValue, JavascriptHint hint, ScriptContext * requestContext)
- {
- switch (JavascriptOperators::GetTypeId(aValue))
- {
- case TypeIds_Undefined:
- case TypeIds_Null:
- case TypeIds_Integer:
- case TypeIds_Boolean:
- case TypeIds_Number:
- case TypeIds_String:
- case TypeIds_Symbol:
- return aValue;
- case TypeIds_VariantDate:
- {
- Var result = nullptr;
- if (JavascriptVariantDate::FromVar(aValue)->ToPrimitive(hint, &result, requestContext) != TRUE)
- {
- result = nullptr;
- }
- return result;
- }
- case TypeIds_StringObject:
- {
- JavascriptStringObject * stringObject = JavascriptStringObject::FromVar(aValue);
- if (stringObject->GetScriptContext()->optimizationOverrides.GetSideEffects() & (hint == JavascriptHint::HintString ? SideEffects_ToString : SideEffects_ValueOf))
- {
- return MethodCallToPrimitive(aValue, hint, requestContext);
- }
- return CrossSite::MarshalVar(requestContext, stringObject->Unwrap());
- }
- case TypeIds_NumberObject:
- {
- JavascriptNumberObject * numberObject = JavascriptNumberObject::FromVar(aValue);
- if (hint == JavascriptHint::HintString)
- {
- if (numberObject->GetScriptContext()->optimizationOverrides.GetSideEffects() & SideEffects_ToString)
- {
- return MethodCallToPrimitive(aValue, hint, requestContext);
- }
- return JavascriptNumber::ToStringRadix10(numberObject->GetValue(), requestContext);
- }
- else
- {
- if (numberObject->GetScriptContext()->optimizationOverrides.GetSideEffects() & SideEffects_ValueOf)
- {
- return MethodCallToPrimitive(aValue, hint, requestContext);
- }
- return CrossSite::MarshalVar(requestContext, numberObject->Unwrap());
- }
- }
- case TypeIds_SymbolObject:
- {
- JavascriptSymbolObject* symbolObject = JavascriptSymbolObject::FromVar(aValue);
- return requestContext->GetLibrary()->CreateSymbol(symbolObject->GetValue());
- }
- case TypeIds_Date:
- case TypeIds_WinRTDate:
- {
- JavascriptDate* dateObject = JavascriptDate::FromVar(aValue);
- if(hint == JavascriptHint::HintNumber)
- {
- if (dateObject->GetScriptContext()->optimizationOverrides.GetSideEffects() & SideEffects_ValueOf)
- {
- // if no Method exists this function falls back to OrdinaryToPrimitive
- // if IsES6ToPrimitiveEnabled flag is off we also fall back to OrdinaryToPrimitive
- return MethodCallToPrimitive(aValue, hint, requestContext);
- }
- return JavascriptNumber::ToVarNoCheck(dateObject->GetTime(), requestContext);
- }
- else
- {
- if (dateObject->GetScriptContext()->optimizationOverrides.GetSideEffects() & SideEffects_ToString)
- {
- // if no Method exists this function falls back to OrdinaryToPrimitive
- // if IsES6ToPrimitiveEnabled flag is off we also fall back to OrdinaryToPrimitive
- return MethodCallToPrimitive(aValue, hint, requestContext);
- }
- return JavascriptDate::ToString(dateObject, requestContext);
- }
- }
- // convert to JavascriptNumber
- case TypeIds_Int64Number:
- return JavascriptInt64Number::FromVar(aValue)->ToJavascriptNumber();
- case TypeIds_UInt64Number:
- return JavascriptUInt64Number::FromVar(aValue)->ToJavascriptNumber();
- default:
- #ifdef ENABLE_SIMDJS
- if (SIMDUtils::IsSimdType(aValue))
- {
- return aValue;
- }
- else
- #endif
- {
- // if no Method exists this function falls back to OrdinaryToPrimitive
- // if IsES6ToPrimitiveEnabled flag is off we also fall back to OrdinaryToPrimitive
- return MethodCallToPrimitive(aValue, hint, requestContext);
- }
- }
- }
- //----------------------------------------------------------------------------
- //https://tc39.github.io/ecma262/#sec-canonicalnumericindexstring
- //1. Assert : Type(argument) is String.
- //2. If argument is "-0", then return -0.
- //3. Let n be ToNumber(argument).
- //4. If SameValue(ToString(n), argument) is false, then return undefined.
- //5. Return n.
- //----------------------------------------------------------------------------
- BOOL JavascriptConversion::CanonicalNumericIndexString(JavascriptString *aValue, double *indexValue, ScriptContext * scriptContext)
- {
- if (JavascriptString::IsNegZero(aValue))
- {
- *indexValue = -0;
- return TRUE;
- }
- double indexDoubleValue = aValue->ToDouble();
- if (JavascriptString::Equals(JavascriptNumber::ToStringRadix10(indexDoubleValue, scriptContext), aValue))
- {
- *indexValue = indexDoubleValue;
- return TRUE;
- }
- return FALSE;
- }
- Var JavascriptConversion::MethodCallToPrimitive(Var aValue, JavascriptHint hint, ScriptContext * requestContext)
- {
- Var result = nullptr;
- RecyclableObject *const recyclableObject = RecyclableObject::FromVar(aValue);
- ScriptContext *const scriptContext = recyclableObject->GetScriptContext();
- //7.3.9 GetMethod(V, P)
- // The abstract operation GetMethod is used to get the value of a specific property of an ECMAScript language value when the value of the
- // property is expected to be a function. The operation is called with arguments V and P where V is the ECMAScript language value, P is the
- // property key. This abstract operation performs the following steps:
- // 1. Assert: IsPropertyKey(P) is true.
- // 2. Let func be ? GetV(V, P).
- // 3. If func is either undefined or null, return undefined.
- // 4. If IsCallable(func) is false, throw a TypeError exception.
- // 5. Return func.
- Var varMethod = nullptr;
- if (!(requestContext->GetConfig()->IsES6ToPrimitiveEnabled()
- && JavascriptOperators::GetPropertyReference(recyclableObject, PropertyIds::_symbolToPrimitive, &varMethod, requestContext)
- && !JavascriptOperators::IsUndefinedOrNull(varMethod)))
- {
- return OrdinaryToPrimitive(aValue, hint, requestContext);
- }
- if (!JavascriptFunction::Is(varMethod))
- {
- // Don't error if we disabled implicit calls
- JavascriptError::TryThrowTypeError(scriptContext, requestContext, JSERR_Property_NeedFunction, requestContext->GetPropertyName(PropertyIds::_symbolToPrimitive)->GetBuffer());
- return requestContext->GetLibrary()->GetNull();
- }
- // Let exoticToPrim be GetMethod(input, @@toPrimitive).
- JavascriptFunction* exoticToPrim = JavascriptFunction::FromVar(varMethod);
- JavascriptString* hintString = nullptr;
- if (hint == JavascriptHint::HintString)
- {
- hintString = requestContext->GetLibrary()->GetStringTypeDisplayString();
- }
- else if (hint == JavascriptHint::HintNumber)
- {
- hintString = requestContext->GetLibrary()->GetNumberTypeDisplayString();
- }
- else
- {
- hintString = requestContext->GetPropertyString(PropertyIds::default_);
- }
- // If exoticToPrim is not undefined, then
- if (nullptr != exoticToPrim)
- {
- ThreadContext * threadContext = requestContext->GetThreadContext();
- result = threadContext->ExecuteImplicitCall(exoticToPrim, ImplicitCall_ToPrimitive, [=]()->Js::Var
- {
- // Stack object should have a pre-op bail on implicit call. We shouldn't see them here.
- Assert(!ThreadContext::IsOnStack(recyclableObject));
- // Let result be the result of calling the[[Call]] internal method of exoticToPrim, with input as thisArgument and(hint) as argumentsList.
- return CALL_FUNCTION(threadContext, exoticToPrim, CallInfo(CallFlags_Value, 2), recyclableObject, hintString);
- });
- if (!result)
- {
- // There was an implicit call and implicit calls are disabled. This would typically cause a bailout.
- Assert(threadContext->IsDisableImplicitCall());
- return requestContext->GetLibrary()->GetNull();
- }
- Assert(!CrossSite::NeedMarshalVar(result, requestContext));
- }
- // If result is an ECMAScript language value and Type(result) is not Object, then return result.
- if (TaggedInt::Is(result) || !JavascriptOperators::IsObjectType(JavascriptOperators::GetTypeId(result)))
- {
- return result;
- }
- // Else, throw a TypeError exception.
- else
- {
- // Don't error if we disabled implicit calls
- JavascriptError::TryThrowTypeError(scriptContext, requestContext, JSERR_FunctionArgument_Invalid, _u("[Symbol.toPrimitive]"));
- return requestContext->GetLibrary()->GetNull();
- }
- }
- Var JavascriptConversion::OrdinaryToPrimitive(Var aValue, JavascriptHint hint, ScriptContext * requestContext)
- {
- Var result;
- RecyclableObject *const recyclableObject = RecyclableObject::FromVar(aValue);
- if (!recyclableObject->ToPrimitive(hint, &result, requestContext))
- {
- ScriptContext *const scriptContext = recyclableObject->GetScriptContext();
- int32 hCode;
- switch (hint)
- {
- case JavascriptHint::HintNumber:
- hCode = JSERR_NeedNumber;
- break;
- case JavascriptHint::HintString:
- hCode = JSERR_NeedString;
- break;
- default:
- hCode = VBSERR_OLENoPropOrMethod;
- break;
- }
- JavascriptError::TryThrowTypeError(scriptContext, scriptContext, hCode);
- return requestContext->GetLibrary()->GetNull();
- }
- return result;
- }
- JavascriptString *JavascriptConversion::CoerseString(Var aValue, ScriptContext* scriptContext, const char16* apiNameForErrorMsg)
- {
- if (!JavascriptConversion::CheckObjectCoercible(aValue, scriptContext))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, apiNameForErrorMsg);
- }
- return ToString(aValue, scriptContext);
- }
- //----------------------------------------------------------------------------
- // ToString - abstract operation
- // ES5 9.8
- //Input Type Result
- // Undefined
- // "undefined"
- // Null
- // "null"
- // Boolean
- // If the argument is true, then the result is "true". If the argument is false, then the result is "false".
- // Number
- // See 9.8.1 below.
- // String
- // Return the input argument (no conversion)
- // Object
- // Apply the following steps:
- // 1. Let primValue be ToPrimitive(input argument, hint String).
- // 2. Return ToString(primValue).
- //----------------------------------------------------------------------------
- JavascriptString *JavascriptConversion::ToString(Var aValue, ScriptContext* scriptContext)
- {
- Assert(scriptContext->GetThreadContext()->IsScriptActive());
- BOOL fPrimitiveOnly = false;
- while(true)
- {
- switch (JavascriptOperators::GetTypeId(aValue))
- {
- case TypeIds_Undefined:
- return scriptContext->GetLibrary()->GetUndefinedDisplayString();
- case TypeIds_Null:
- return scriptContext->GetLibrary()->GetNullDisplayString();
- case TypeIds_Integer:
- return scriptContext->GetIntegerString(aValue);
- case TypeIds_Boolean:
- return JavascriptBoolean::FromVar(aValue)->GetValue() ? scriptContext->GetLibrary()->GetTrueDisplayString() : scriptContext->GetLibrary()->GetFalseDisplayString();
- case TypeIds_Number:
- return JavascriptNumber::ToStringRadix10(JavascriptNumber::GetValue(aValue), scriptContext);
- case TypeIds_Int64Number:
- {
- __int64 value = JavascriptInt64Number::FromVar(aValue)->GetValue();
- if (!TaggedInt::IsOverflow(value))
- {
- return scriptContext->GetIntegerString((int)value);
- }
- else
- {
- return JavascriptInt64Number::ToString(aValue, scriptContext);
- }
- }
- case TypeIds_UInt64Number:
- {
- unsigned __int64 value = JavascriptUInt64Number::FromVar(aValue)->GetValue();
- if (!TaggedInt::IsOverflow(value))
- {
- return scriptContext->GetIntegerString((uint)value);
- }
- else
- {
- return JavascriptUInt64Number::ToString(aValue, scriptContext);
- }
- }
- case TypeIds_String:
- return JavascriptString::FromVar(CrossSite::MarshalVar(scriptContext, aValue));
- case TypeIds_VariantDate:
- return JavascriptVariantDate::FromVar(aValue)->GetValueString(scriptContext);
- case TypeIds_Symbol:
- return JavascriptSymbol::FromVar(aValue)->ToString(scriptContext);
- case TypeIds_SymbolObject:
- return JavascriptSymbol::ToString(JavascriptSymbolObject::FromVar(aValue)->GetValue(), scriptContext);
- #ifdef ENABLE_SIMDJS
- case TypeIds_SIMDBool8x16:
- case TypeIds_SIMDBool16x8:
- case TypeIds_SIMDBool32x4:
- case TypeIds_SIMDInt8x16:
- case TypeIds_SIMDInt16x8:
- case TypeIds_SIMDInt32x4:
- case TypeIds_SIMDUint8x16:
- case TypeIds_SIMDUint16x8:
- case TypeIds_SIMDUint32x4:
- case TypeIds_SIMDFloat32x4:
- {
- Assert(aValue);
- RecyclableObject *obj = nullptr;
- if (!JavascriptConversion::ToObject(aValue, scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedSimd, _u("SIMDType.toString"));
- }
- JavascriptSIMDObject* simdObject = static_cast<JavascriptSIMDObject*>(obj);
- return JavascriptString::FromVar(simdObject->ToString(scriptContext));
- }
- #endif
- case TypeIds_GlobalObject:
- aValue = static_cast<Js::GlobalObject*>(aValue)->ToThis();
- // fall through
- default:
- {
- AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToString");
- if(fPrimitiveOnly)
- {
- AssertMsg(FALSE, "wrong call in ToString, no dynamic objects should get here");
- JavascriptError::ThrowError(scriptContext, VBSERR_InternalError);
- }
- fPrimitiveOnly = true;
- aValue = ToPrimitive(aValue, JavascriptHint::HintString, scriptContext);
- }
- }
- }
- }
- JavascriptString *JavascriptConversion::ToLocaleString(Var aValue, ScriptContext* scriptContext)
- {
- switch (JavascriptOperators::GetTypeId(aValue))
- {
- case TypeIds_Undefined:
- return scriptContext->GetLibrary()->GetUndefinedDisplayString();
- case TypeIds_Null:
- return scriptContext->GetLibrary()->GetNullDisplayString();
- case TypeIds_Integer:
- return JavascriptNumber::ToLocaleString(TaggedInt::ToInt32(aValue), scriptContext);
- case TypeIds_Boolean:
- return JavascriptBoolean::FromVar(aValue)->GetValue() ? scriptContext->GetLibrary()->GetTrueDisplayString() : scriptContext->GetLibrary()->GetFalseDisplayString();
- case TypeIds_Int64Number:
- return JavascriptNumber::ToLocaleString((double)JavascriptInt64Number::FromVar(aValue)->GetValue(), scriptContext);
- case TypeIds_UInt64Number:
- return JavascriptNumber::ToLocaleString((double)JavascriptUInt64Number::FromVar(aValue)->GetValue(), scriptContext);
- case TypeIds_Number:
- return JavascriptNumber::ToLocaleString(JavascriptNumber::GetValue(aValue), scriptContext);
- case TypeIds_String:
- return JavascriptString::FromVar(aValue);
- case TypeIds_VariantDate:
- // Legacy behavior was to create an empty object and call toLocaleString on it, which would result in this value
- return scriptContext->GetLibrary()->GetObjectDisplayString();
- case TypeIds_Symbol:
- return JavascriptSymbol::FromVar(aValue)->ToString(scriptContext);
- default:
- {
- RecyclableObject* object = RecyclableObject::FromVar(aValue);
- Var value = JavascriptOperators::GetProperty(object, PropertyIds::toLocaleString, scriptContext, NULL);
- if (JavascriptConversion::IsCallable(value))
- {
- RecyclableObject* toLocaleStringFunction = RecyclableObject::FromVar(value);
- Var aResult = CALL_FUNCTION(scriptContext->GetThreadContext(), toLocaleStringFunction, CallInfo(1), aValue);
- if (JavascriptString::Is(aResult))
- {
- return JavascriptString::FromVar(aResult);
- }
- else
- {
- return JavascriptConversion::ToString(aResult, scriptContext);
- }
- }
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_NeedFunction, scriptContext->GetPropertyName(PropertyIds::toLocaleString)->GetBuffer());
- }
- }
- }
- //----------------------------------------------------------------------------
- // ToBoolean_Full:
- // (ES3.0: S9.2):
- //
- // Input Output
- // ----- ------
- // 'undefined' 'false'
- // 'null' 'false'
- // Boolean Value
- // Number 'false' if +0, -0, or Nan
- // 'true' otherwise
- // String 'false' if argument is ""
- // 'true' otherwise
- // Object 'true'
- // Falsy Object 'false'
- //----------------------------------------------------------------------------
- BOOL JavascriptConversion::ToBoolean_Full(Var aValue, ScriptContext* scriptContext)
- {
- AssertMsg(!TaggedInt::Is(aValue), "Should be detected");
- AssertMsg(RecyclableObject::Is(aValue), "Should be handled already");
- auto type = RecyclableObject::FromVar(aValue)->GetType();
- switch (type->GetTypeId())
- {
- case TypeIds_Undefined:
- case TypeIds_Null:
- case TypeIds_VariantDate:
- return false;
- case TypeIds_Symbol:
- return true;
- case TypeIds_Boolean:
- return JavascriptBoolean::FromVar(aValue)->GetValue();
- #if !FLOATVAR
- case TypeIds_Number:
- {
- double value = JavascriptNumber::GetValue(aValue);
- return (!JavascriptNumber::IsNan(value)) && (!JavascriptNumber::IsZero(value));
- }
- #endif
- case TypeIds_Int64Number:
- {
- __int64 value = JavascriptInt64Number::FromVar(aValue)->GetValue();
- return value != 0;
- }
- case TypeIds_UInt64Number:
- {
- unsigned __int64 value = JavascriptUInt64Number::FromVar(aValue)->GetValue();
- return value != 0;
- }
- case TypeIds_String:
- {
- JavascriptString * pstValue = JavascriptString::FromVar(aValue);
- return pstValue->GetLength() > 0;
- }
- #ifdef ENABLE_SIMDJS
- case TypeIds_SIMDFloat32x4:
- case TypeIds_SIMDFloat64x2:
- case TypeIds_SIMDInt32x4:
- case TypeIds_SIMDInt16x8:
- case TypeIds_SIMDInt8x16:
- case TypeIds_SIMDBool32x4:
- case TypeIds_SIMDBool16x8:
- case TypeIds_SIMDBool8x16:
- case TypeIds_SIMDUint32x4:
- case TypeIds_SIMDUint16x8:
- case TypeIds_SIMDUint8x16:
- {
- return true;
- }
- #endif
- default:
- {
- AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToBoolean");
- // Falsy objects evaluate to false when converted to Boolean.
- return !type->IsFalsy();
- }
- }
- }
- void JavascriptConversion::ToFloat_Helper(Var aValue, float *pResult, ScriptContext* scriptContext)
- {
- *pResult = (float)ToNumber_Full(aValue, scriptContext);
- }
- void JavascriptConversion::ToNumber_Helper(Var aValue, double *pResult, ScriptContext* scriptContext)
- {
- Assert(Js::JavascriptStackWalker::ValidateTopJitFrame(scriptContext));
- *pResult = ToNumber_Full(aValue, scriptContext);
- }
- // Used for the JIT's float type specialization
- // Convert aValue to double, but only allow primitives. Return false otherwise.
- BOOL JavascriptConversion::ToNumber_FromPrimitive(Var aValue, double *pResult, BOOL allowUndefined, ScriptContext* scriptContext)
- {
- Assert(Js::JavascriptStackWalker::ValidateTopJitFrame(scriptContext));
- Assert(!TaggedNumber::Is(aValue));
- RecyclableObject *obj = RecyclableObject::FromVar(aValue);
- // NOTE: Don't allow strings, otherwise JIT's float type specialization has to worry about concats
- if (obj->GetTypeId() >= TypeIds_String)
- {
- return false;
- }
- if (!allowUndefined && obj->GetTypeId() == TypeIds_Undefined)
- {
- return false;
- }
- *pResult = ToNumber_Full(aValue, scriptContext);
- return true;
- }
- //----------------------------------------------------------------------------
- // ToNumber_Full:
- // Implements ES6 Draft Rev 26 July 18, 2014
- //
- // Undefined: NaN
- // Null: 0
- // boolean: v==true ? 1 : 0 ;
- // number: v (original number)
- // String: conversion by spec algorithm
- // object: ToNumber(PrimitiveValue(v, hint_number))
- // Symbol: TypeError
- //----------------------------------------------------------------------------
- double JavascriptConversion::ToNumber_Full(Var aValue,ScriptContext* scriptContext)
- {
- AssertMsg(!TaggedInt::Is(aValue), "Should be detected");
- ScriptContext * objectScriptContext = RecyclableObject::Is(aValue) ? RecyclableObject::FromVar(aValue)->GetScriptContext() : nullptr;
- BOOL fPrimitiveOnly = false;
- while(true)
- {
- switch (JavascriptOperators::GetTypeId(aValue))
- {
- case TypeIds_Symbol:
- JavascriptError::TryThrowTypeError(objectScriptContext, scriptContext, JSERR_NeedNumber);
- // Fallthrough to return NaN if exceptions are disabled
- case TypeIds_Undefined:
- return JavascriptNumber::GetValue(scriptContext->GetLibrary()->GetNaN());
- case TypeIds_Null:
- return 0;
- case TypeIds_Integer:
- return TaggedInt::ToDouble(aValue);
- case TypeIds_Boolean:
- return JavascriptBoolean::FromVar(aValue)->GetValue() ? 1 : +0;
- case TypeIds_Number:
- return JavascriptNumber::GetValue(aValue);
- case TypeIds_Int64Number:
- return (double)JavascriptInt64Number::FromVar(aValue)->GetValue();
- case TypeIds_UInt64Number:
- return (double)JavascriptUInt64Number::FromVar(aValue)->GetValue();
- case TypeIds_String:
- return JavascriptString::FromVar(aValue)->ToDouble();
- case TypeIds_VariantDate:
- return Js::DateImplementation::GetTvUtc(Js::DateImplementation::JsLocalTimeFromVarDate(JavascriptVariantDate::FromVar(aValue)->GetValue()), scriptContext);
- #ifdef ENABLE_SIMDJS
- case TypeIds_SIMDFloat32x4:
- case TypeIds_SIMDInt32x4:
- case TypeIds_SIMDInt16x8:
- case TypeIds_SIMDInt8x16:
- case TypeIds_SIMDFloat64x2:
- case TypeIds_SIMDBool32x4:
- case TypeIds_SIMDBool16x8:
- case TypeIds_SIMDBool8x16:
- case TypeIds_SIMDUint32x4:
- case TypeIds_SIMDUint16x8:
- case TypeIds_SIMDUint8x16:
- JavascriptError::ThrowError(scriptContext, JSERR_NeedNumber);
- #endif
- default:
- {
- AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToInteger");
- if(fPrimitiveOnly)
- {
- JavascriptError::ThrowError(scriptContext, VBSERR_OLENoPropOrMethod);
- }
- fPrimitiveOnly = true;
- aValue = ToPrimitive(aValue, JavascriptHint::HintNumber, scriptContext);
- }
- }
- }
- }
- //----------------------------------------------------------------------------
- // second part of the ToInteger() implementation.(ES5.0: S9.4).
- //----------------------------------------------------------------------------
- double JavascriptConversion::ToInteger_Full(Var aValue,ScriptContext* scriptContext)
- {
- AssertMsg(!TaggedInt::Is(aValue), "Should be detected");
- ScriptContext * objectScriptContext = RecyclableObject::Is(aValue) ? RecyclableObject::FromVar(aValue)->GetScriptContext() : nullptr;
- BOOL fPrimitiveOnly = false;
- while(true)
- {
- switch (JavascriptOperators::GetTypeId(aValue))
- {
- case TypeIds_Symbol:
- JavascriptError::TryThrowTypeError(objectScriptContext, scriptContext, JSERR_NeedNumber);
- // Fallthrough to return 0 if exceptions are disabled
- case TypeIds_Undefined:
- case TypeIds_Null:
- return 0;
- case TypeIds_Integer:
- return TaggedInt::ToInt32(aValue);
- case TypeIds_Boolean:
- return JavascriptBoolean::FromVar(aValue)->GetValue() ? 1 : +0;
- case TypeIds_Number:
- return ToInteger(JavascriptNumber::GetValue(aValue));
- case TypeIds_Int64Number:
- return ToInteger((double)JavascriptInt64Number::FromVar(aValue)->GetValue());
- case TypeIds_UInt64Number:
- return ToInteger((double)JavascriptUInt64Number::FromVar(aValue)->GetValue());
- case TypeIds_String:
- return ToInteger(JavascriptString::FromVar(aValue)->ToDouble());
- case TypeIds_VariantDate:
- return ToInteger(ToNumber_Full(aValue, scriptContext));
- #ifdef ENABLE_SIMDJS
- case TypeIds_SIMDFloat32x4:
- case TypeIds_SIMDFloat64x2:
- case TypeIds_SIMDInt32x4:
- case TypeIds_SIMDInt16x8:
- case TypeIds_SIMDInt8x16:
- case TypeIds_SIMDBool32x4:
- case TypeIds_SIMDBool16x8:
- case TypeIds_SIMDBool8x16:
- case TypeIds_SIMDUint32x4:
- case TypeIds_SIMDUint16x8:
- case TypeIds_SIMDUint8x16:
- JavascriptError::ThrowError(scriptContext, JSERR_NeedNumber);
- #endif
- default:
- {
- AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToInteger");
- if(fPrimitiveOnly)
- {
- AssertMsg(FALSE, "wrong call in ToInteger_Full, no dynamic objects should get here");
- JavascriptError::ThrowError(scriptContext, VBSERR_OLENoPropOrMethod);
- }
- fPrimitiveOnly = true;
- aValue = ToPrimitive(aValue, JavascriptHint::HintNumber, scriptContext);
- }
- }
- }
- }
- double JavascriptConversion::ToInteger(double val)
- {
- if(JavascriptNumber::IsNan(val))
- return 0;
- if(JavascriptNumber::IsPosInf(val) || JavascriptNumber::IsNegInf(val) ||
- JavascriptNumber::IsZero(val))
- {
- return val;
- }
- return ( ((val < 0) ? -1 : 1 ) * floor(fabs(val)));
- }
- //----------------------------------------------------------------------------
- // ToInt32() converts the given Var to an Int32 value, as described in
- // (ES3.0: S9.5).
- //----------------------------------------------------------------------------
- int32 JavascriptConversion::ToInt32_Full(Var aValue, ScriptContext* scriptContext)
- {
- Assert(Js::JavascriptStackWalker::ValidateTopJitFrame(scriptContext));
- AssertMsg(!TaggedInt::Is(aValue), "Should be detected");
- ScriptContext * objectScriptContext = RecyclableObject::Is(aValue) ? RecyclableObject::FromVar(aValue)->GetScriptContext() : nullptr;
- // This is used when TaggedInt's overflow but remain under int32
- // so Number is our most critical case:
- TypeId typeId = JavascriptOperators::GetTypeId(aValue);
- if (typeId == TypeIds_Number)
- {
- return JavascriptMath::ToInt32Core(JavascriptNumber::GetValue(aValue));
- }
- switch (typeId)
- {
- case TypeIds_Symbol:
- JavascriptError::TryThrowTypeError(objectScriptContext, scriptContext, JSERR_NeedNumber);
- // Fallthrough to return 0 if exceptions are disabled
- case TypeIds_Undefined:
- case TypeIds_Null:
- return 0;
- case TypeIds_Integer:
- return TaggedInt::ToInt32(aValue);
- case TypeIds_Boolean:
- return JavascriptBoolean::FromVar(aValue)->GetValue() ? 1 : +0;
- case TypeIds_Int64Number:
- // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
- // treat it as double anyhow.
- return JavascriptMath::ToInt32Core((double)JavascriptInt64Number::FromVar(aValue)->GetValue());
- case TypeIds_UInt64Number:
- // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
- // treat it as double anyhow.
- return JavascriptMath::ToInt32Core((double)JavascriptUInt64Number::FromVar(aValue)->GetValue());
- case TypeIds_String:
- {
- double result;
- if (JavascriptString::FromVar(aValue)->ToDouble(&result))
- {
- return JavascriptMath::ToInt32Core(result);
- }
- // If the string isn't a valid number, ToDouble returns NaN, and ToInt32 of that is 0
- return 0;
- }
- case TypeIds_VariantDate:
- return ToInt32(ToNumber_Full(aValue, scriptContext));
- #ifdef ENABLE_SIMDJS
- case TypeIds_SIMDFloat32x4:
- case TypeIds_SIMDFloat64x2:
- case TypeIds_SIMDInt32x4:
- case TypeIds_SIMDInt16x8:
- case TypeIds_SIMDInt8x16:
- case TypeIds_SIMDBool32x4:
- case TypeIds_SIMDBool16x8:
- case TypeIds_SIMDBool8x16:
- case TypeIds_SIMDUint32x4:
- case TypeIds_SIMDUint16x8:
- case TypeIds_SIMDUint8x16:
- JavascriptError::ThrowError(scriptContext, JSERR_NeedNumber);
- #endif
- default:
- AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToInteger32");
- aValue = ToPrimitive(aValue, JavascriptHint::HintNumber, scriptContext);
- }
- switch (JavascriptOperators::GetTypeId(aValue))
- {
- case TypeIds_Symbol:
- JavascriptError::TryThrowTypeError(objectScriptContext, scriptContext, JSERR_NeedNumber);
- // Fallthrough to return 0 if exceptions are disabled
- case TypeIds_Undefined:
- case TypeIds_Null:
- return 0;
- case TypeIds_Integer:
- return TaggedInt::ToInt32(aValue);
- case TypeIds_Boolean:
- return JavascriptBoolean::FromVar(aValue)->GetValue() ? 1 : +0;
- case TypeIds_Number:
- return ToInt32(JavascriptNumber::GetValue(aValue));
- case TypeIds_Int64Number:
- // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
- // treat it as double anyhow.
- return JavascriptMath::ToInt32Core((double)JavascriptInt64Number::FromVar(aValue)->GetValue());
- case TypeIds_UInt64Number:
- // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
- // treat it as double anyhow.
- return JavascriptMath::ToInt32Core((double)JavascriptUInt64Number::FromVar(aValue)->GetValue());
- case TypeIds_String:
- {
- double result;
- if (JavascriptString::FromVar(aValue)->ToDouble(&result))
- {
- return ToInt32(result);
- }
- // If the string isn't a valid number, ToDouble returns NaN, and ToInt32 of that is 0
- return 0;
- }
- case TypeIds_VariantDate:
- return ToInt32(ToNumber_Full(aValue, scriptContext));
- default:
- AssertMsg(FALSE, "wrong call in ToInteger32_Full, no dynamic objects should get here.");
- JavascriptError::ThrowError(scriptContext, VBSERR_OLENoPropOrMethod);
- }
- }
- // a strict version of ToInt32 conversion that returns false for non int32 values like, inf, NaN, undef
- BOOL JavascriptConversion::ToInt32Finite(Var aValue, ScriptContext* scriptContext, int32* result)
- {
- ScriptContext * objectScriptContext = RecyclableObject::Is(aValue) ? RecyclableObject::FromVar(aValue)->GetScriptContext() : nullptr;
- BOOL fPrimitiveOnly = false;
- while(true)
- {
- switch (JavascriptOperators::GetTypeId(aValue))
- {
- case TypeIds_Symbol:
- JavascriptError::TryThrowTypeError(objectScriptContext, scriptContext, JSERR_NeedNumber);
- // Fallthrough to return false and set result to 0 if exceptions are disabled
- case TypeIds_Undefined:
- *result = 0;
- return false;
- case TypeIds_Null:
- *result = 0;
- return true;
- case TypeIds_Integer:
- *result = TaggedInt::ToInt32(aValue);
- return true;
- case TypeIds_Boolean:
- *result = JavascriptBoolean::FromVar(aValue)->GetValue() ? 1 : +0;
- return true;
- case TypeIds_Number:
- return ToInt32Finite(JavascriptNumber::GetValue(aValue), result);
- case TypeIds_Int64Number:
- // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
- // treat it as double anyhow.
- return ToInt32Finite((double)JavascriptInt64Number::FromVar(aValue)->GetValue(), result);
- case TypeIds_UInt64Number:
- // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
- // treat it as double anyhow.
- return ToInt32Finite((double)JavascriptUInt64Number::FromVar(aValue)->GetValue(), result);
- case TypeIds_String:
- return ToInt32Finite(JavascriptString::FromVar(aValue)->ToDouble(), result);
- case TypeIds_VariantDate:
- return ToInt32Finite(ToNumber_Full(aValue, scriptContext), result);
- #ifdef ENABLE_SIMDJS
- case TypeIds_SIMDFloat32x4:
- case TypeIds_SIMDFloat64x2:
- case TypeIds_SIMDInt32x4:
- case TypeIds_SIMDInt16x8:
- case TypeIds_SIMDInt8x16:
- case TypeIds_SIMDBool32x4:
- case TypeIds_SIMDBool16x8:
- case TypeIds_SIMDBool8x16:
- case TypeIds_SIMDUint32x4:
- case TypeIds_SIMDUint16x8:
- case TypeIds_SIMDUint8x16:
- JavascriptError::ThrowError(scriptContext, JSERR_NeedNumber);
- #endif
- default:
- {
- AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToInteger32");
- if(fPrimitiveOnly)
- {
- AssertMsg(FALSE, "wrong call in ToInteger32_Full, no dynamic objects should get here");
- JavascriptError::ThrowError(scriptContext, VBSERR_OLENoPropOrMethod);
- }
- fPrimitiveOnly = true;
- aValue = ToPrimitive(aValue, JavascriptHint::HintNumber, scriptContext);
- }
- }
- }
- }
- int32 JavascriptConversion::ToInt32(double T1)
- {
- return JavascriptMath::ToInt32Core(T1);
- }
- __int64 JavascriptConversion::ToInt64(Var aValue, ScriptContext* scriptContext)
- {
- switch (JavascriptOperators::GetTypeId(aValue))
- {
- case TypeIds_Integer:
- {
- return TaggedInt::ToInt32(aValue);
- }
- case TypeIds_Int64Number:
- {
- JavascriptInt64Number* int64Number = JavascriptInt64Number::FromVar(aValue);
- return int64Number->GetValue();
- }
- case TypeIds_UInt64Number:
- {
- JavascriptUInt64Number* uint64Number = JavascriptUInt64Number::FromVar(aValue);
- return (__int64)uint64Number->GetValue();
- }
- case TypeIds_Number:
- return JavascriptMath::TryToInt64(JavascriptNumber::GetValue(aValue));
- default:
- return (unsigned __int64)JavascriptConversion::ToInt32_Full(aValue, scriptContext);
- }
- }
- unsigned __int64 JavascriptConversion::ToUInt64(Var aValue, ScriptContext* scriptContext)
- {
- switch (JavascriptOperators::GetTypeId(aValue))
- {
- case TypeIds_Integer:
- {
- return (unsigned __int64)TaggedInt::ToInt32(aValue);
- }
- case TypeIds_Int64Number:
- {
- JavascriptInt64Number* int64Number = JavascriptInt64Number::FromVar(aValue);
- return (unsigned __int64)int64Number->GetValue();
- }
- case TypeIds_UInt64Number:
- {
- JavascriptUInt64Number* uint64Number = JavascriptUInt64Number::FromVar(aValue);
- return uint64Number->GetValue();
- }
- case TypeIds_Number:
- return static_cast<unsigned __int64>(JavascriptMath::TryToInt64(JavascriptNumber::GetValue(aValue)));
- default:
- return (unsigned __int64)JavascriptConversion::ToInt32_Full(aValue, scriptContext);
- }
- }
- BOOL JavascriptConversion::ToInt32Finite(double value, int32* result)
- {
- if((!NumberUtilities::IsFinite(value)) || JavascriptNumber::IsNan(value))
- {
- *result = 0;
- return false;
- }
- else
- {
- *result = JavascriptMath::ToInt32Core(value);
- return true;
- }
- }
- //----------------------------------------------------------------------------
- // (ES3.0: S9.6).
- //----------------------------------------------------------------------------
- uint32 JavascriptConversion::ToUInt32_Full(Var aValue, ScriptContext* scriptContext)
- {
- AssertMsg(!TaggedInt::Is(aValue), "Should be detected");
- ScriptContext * objectScriptContext = RecyclableObject::Is(aValue) ? RecyclableObject::FromVar(aValue)->GetScriptContext() : nullptr;
- BOOL fPrimitiveOnly = false;
- while(true)
- {
- switch (JavascriptOperators::GetTypeId(aValue))
- {
- case TypeIds_Symbol:
- JavascriptError::TryThrowTypeError(objectScriptContext, scriptContext, JSERR_NeedNumber);
- // Fallthrough to return 0 if exceptions are disabled
- case TypeIds_Undefined:
- case TypeIds_Null:
- return 0;
- case TypeIds_Integer:
- return TaggedInt::ToUInt32(aValue);
- case TypeIds_Boolean:
- return JavascriptBoolean::FromVar(aValue)->GetValue() ? 1 : +0;
- case TypeIds_Number:
- return JavascriptMath::ToUInt32(JavascriptNumber::GetValue(aValue));
- case TypeIds_Int64Number:
- // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
- // treat it as double anyhow.
- return JavascriptMath::ToUInt32((double)JavascriptInt64Number::FromVar(aValue)->GetValue());
- case TypeIds_UInt64Number:
- // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
- // treat it as double anyhow.
- return JavascriptMath::ToUInt32((double)JavascriptUInt64Number::FromVar(aValue)->GetValue());
- case TypeIds_String:
- {
- double result;
- if (JavascriptString::FromVar(aValue)->ToDouble(&result))
- {
- return JavascriptMath::ToUInt32(result);
- }
- // If the string isn't a valid number, ToDouble returns NaN, and ToUInt32 of that is 0
- return 0;
- }
- case TypeIds_VariantDate:
- return JavascriptMath::ToUInt32(ToNumber_Full(aValue, scriptContext));
- #ifdef ENABLE_SIMDJS
- case TypeIds_SIMDFloat32x4:
- case TypeIds_SIMDFloat64x2:
- case TypeIds_SIMDInt32x4:
- case TypeIds_SIMDInt16x8:
- case TypeIds_SIMDInt8x16:
- case TypeIds_SIMDBool32x4:
- case TypeIds_SIMDBool16x8:
- case TypeIds_SIMDBool8x16:
- case TypeIds_SIMDUint32x4:
- case TypeIds_SIMDUint16x8:
- case TypeIds_SIMDUint8x16:
- JavascriptError::ThrowError(scriptContext, JSERR_NeedNumber);
- #endif
- default:
- {
- AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToUInt32");
- if(fPrimitiveOnly)
- {
- AssertMsg(FALSE, "wrong call in ToUInt32_Full, no dynamic objects should get here");
- JavascriptError::ThrowError(scriptContext, VBSERR_OLENoPropOrMethod);
- }
- fPrimitiveOnly = true;
- aValue = ToPrimitive(aValue, JavascriptHint::HintNumber, scriptContext);
- }
- }
- }
- }
- uint32 JavascriptConversion::ToUInt32(double T1)
- {
- // Same as doing ToInt32 and reinterpret the bits as uint32
- return (uint32)JavascriptMath::ToInt32Core(T1);
- }
- //----------------------------------------------------------------------------
- // ToUInt16() converts the given Var to a UInt16 value, as described in
- // (ES3.0: S9.6).
- //----------------------------------------------------------------------------
- uint16 JavascriptConversion::ToUInt16_Full(IN Var aValue, ScriptContext* scriptContext)
- {
- AssertMsg(!TaggedInt::Is(aValue), "Should be detected");
- ScriptContext * objectScriptContext = RecyclableObject::Is(aValue) ? RecyclableObject::FromVar(aValue)->GetScriptContext() : nullptr;
- BOOL fPrimitiveOnly = false;
- while(true)
- {
- switch (JavascriptOperators::GetTypeId(aValue))
- {
- case TypeIds_Symbol:
- JavascriptError::TryThrowTypeError(objectScriptContext, scriptContext, JSERR_NeedNumber);
- // Fallthrough to return 0 if exceptions are disabled
- case TypeIds_Undefined:
- case TypeIds_Null:
- return 0;
- case TypeIds_Integer:
- return TaggedInt::ToUInt16(aValue);
- case TypeIds_Boolean:
- return JavascriptBoolean::FromVar(aValue)->GetValue() ? 1 : +0;
- case TypeIds_Number:
- return ToUInt16(JavascriptNumber::GetValue(aValue));
- case TypeIds_Int64Number:
- // we won't lose precision if the int64 is within 16bit boundary; otherwise we need to
- // treat it as double anyhow.
- return ToUInt16((double)JavascriptInt64Number::FromVar(aValue)->GetValue());
- case TypeIds_UInt64Number:
- // we won't lose precision if the int64 is within 16bit boundary; otherwise we need to
- // treat it as double anyhow.
- return ToUInt16((double)JavascriptUInt64Number::FromVar(aValue)->GetValue());
- case TypeIds_String:
- {
- double result;
- if (JavascriptString::FromVar(aValue)->ToDouble(&result))
- {
- return ToUInt16(result);
- }
- // If the string isn't a valid number, ToDouble is NaN, and ToUInt16 of that is 0
- return 0;
- }
- case TypeIds_VariantDate:
- return ToUInt16(ToNumber_Full(aValue, scriptContext));
- #ifdef ENABLE_SIMDJS
- case TypeIds_SIMDFloat32x4:
- case TypeIds_SIMDFloat64x2:
- case TypeIds_SIMDInt32x4:
- case TypeIds_SIMDInt16x8:
- case TypeIds_SIMDInt8x16:
- case TypeIds_SIMDBool32x4:
- case TypeIds_SIMDBool16x8:
- case TypeIds_SIMDBool8x16:
- case TypeIds_SIMDUint32x4:
- case TypeIds_SIMDUint16x8:
- case TypeIds_SIMDUint8x16:
- JavascriptError::ThrowError(scriptContext, JSERR_NeedNumber);
- #endif
- default:
- {
- AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToUIn16");
- if(fPrimitiveOnly)
- {
- AssertMsg(FALSE, "wrong call in ToUInt16, no dynamic objects should get here");
- JavascriptError::ThrowError(scriptContext, VBSERR_OLENoPropOrMethod);
- }
- fPrimitiveOnly = true;
- aValue = ToPrimitive(aValue, JavascriptHint::HintNumber, scriptContext);
- }
- }
- }
- }
- inline uint16 JavascriptConversion::ToUInt16(double T1)
- {
- //
- // VC does the right thing here, if we first convert to uint32 and then to uint16
- // Spec says mod should be done.
- //
- uint32 result = JavascriptConversion::ToUInt32(T1);
- #if defined(_M_IX86) && _MSC_FULL_VER < 160030329
- // Well VC doesn't actually do the right thing... It takes (uint16)(uint32)double and removes the
- // middle uint32 cast to (uint16)double, which isn't the same thing. Somehow, it only seems to be a
- // problem for x86. Forcing a store to uint32 prevents the incorrect optimization.
- //
- // A bug has been filled in the Dev11 database: TF bug id #901495
- // Fixed in compiler 16.00.30329.00
- volatile uint32 volResult = result;
- #endif
- return (uint16) result;
- }
- JavascriptString * JavascriptConversion::ToPrimitiveString(Var aValue, ScriptContext * scriptContext)
- {
- return ToString(ToPrimitive(aValue, JavascriptHint::None, scriptContext), scriptContext);
- }
- double JavascriptConversion::LongToDouble(__int64 aValue)
- {
- return static_cast<double>(aValue);
- }
- // Windows x64 version implemented in masm to work around precision limitation
- #if !defined(_WIN32 ) || !defined(_M_X64)
- double JavascriptConversion::ULongToDouble(unsigned __int64 aValue)
- {
- return static_cast<double>(aValue);
- }
- #endif
- float JavascriptConversion::LongToFloat(__int64 aValue)
- {
- return static_cast<float>(aValue);
- }
- float JavascriptConversion::ULongToFloat (unsigned __int64 aValue)
- {
- return static_cast<float>(aValue);
- }
- int32 JavascriptConversion::F32TOI32(float src, ScriptContext * ctx)
- {
- if (Wasm::WasmMath::isInRange<float, uint32, NumberConstants::k_Float32TwoTo31, NumberConstants::k_Float32NegZero, NumberConstants::k_Float32NegTwoTo31,
- &Wasm::WasmMath::LessThan<uint32>, &Wasm::WasmMath::LessOrEqual<uint32>>(src) &&
- !Wasm::WasmMath::isNaN<float>(src))
- {
- return (int32)src;
- }
- JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
- }
- uint32 JavascriptConversion::F32TOU32(float src, ScriptContext * ctx)
- {
- if (Wasm::WasmMath::isInRange<float, uint32, NumberConstants::k_Float32TwoTo32, NumberConstants::k_Float32NegZero, NumberConstants::k_Float32NegOne,
- &Wasm::WasmMath::LessThan<uint32>, &Wasm::WasmMath::LessThan<uint32>>(src) &&
- !Wasm::WasmMath::isNaN<float>(src))
- {
- return (uint32)src;
- }
- JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
- }
- int32 JavascriptConversion::F64TOI32(double src, ScriptContext * ctx)
- {
- if (Wasm::WasmMath::isInRange<double, uint64, NumberConstants::k_TwoTo31, NumberConstants::k_NegZero, NumberConstants::k_NegTwoTo31,
- &Wasm::WasmMath::LessOrEqual<uint64>, &Wasm::WasmMath::LessOrEqual<uint64>>(src) &&
- !Wasm::WasmMath::isNaN<double>(src))
- {
- return (int32)src;
- }
- JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
- }
- uint32 JavascriptConversion::F64TOU32(double src, ScriptContext * ctx)
- {
- if (Wasm::WasmMath::isInRange<double, uint64, NumberConstants::k_TwoTo32, NumberConstants::k_NegZero, NumberConstants::k_NegOne,
- &Wasm::WasmMath::LessOrEqual<uint64>, &Wasm::WasmMath::LessThan<uint64>>(src)
- && !Wasm::WasmMath::isNaN<double>(src))
- {
- return (uint32)src;
- }
- JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
- }
- int64 JavascriptConversion::F32TOI64(float src, ScriptContext * ctx)
- {
- if (Wasm::WasmMath::isInRange<float, uint32, NumberConstants::k_Float32TwoTo63, NumberConstants::k_Float32NegZero, NumberConstants::k_Float32NegTwoTo63,
- &Wasm::WasmMath::LessThan<uint32>, &Wasm::WasmMath::LessOrEqual<uint32>>(src) &&
- !Wasm::WasmMath::isNaN<float>(src))
- {
- return (int64)src;
- }
- JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
- }
- uint64 JavascriptConversion::F32TOU64(float src, ScriptContext * ctx)
- {
- if (Wasm::WasmMath::isInRange<float, uint32, NumberConstants::k_Float32TwoTo64, NumberConstants::k_Float32NegZero, NumberConstants::k_Float32NegOne,
- &Wasm::WasmMath::LessThan<uint32>, &Wasm::WasmMath::LessThan<uint32>>(src) &&
- !Wasm::WasmMath::isNaN<float>(src))
- {
- return (uint64)src;
- }
- JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
- }
- int64 JavascriptConversion::F64TOI64(double src, ScriptContext * ctx)
- {
- if (Wasm::WasmMath::isInRange<double, uint64, NumberConstants::k_TwoTo63, NumberConstants::k_NegZero, NumberConstants::k_NegTwoTo63,
- &Wasm::WasmMath::LessThan<uint64>, &Wasm::WasmMath::LessOrEqual<uint64>>(src) &&
- !Wasm::WasmMath::isNaN<double>(src))
- {
- return (int64)src;
- }
- JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
- }
- uint64 JavascriptConversion::F64TOU64(double src, ScriptContext * ctx)
- {
- if (Wasm::WasmMath::isInRange<double, uint64, NumberConstants::k_TwoTo64, NumberConstants::k_NegZero, NumberConstants::k_NegOne,
- &Wasm::WasmMath::LessThan<uint64>, &Wasm::WasmMath::LessThan<uint64>>(src) &&
- !Wasm::WasmMath::isNaN<double>(src))
- {
- return (uint64)src;
- }
- JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
- }
- int64 JavascriptConversion::ToLength(Var aValue, ScriptContext* scriptContext)
- {
- if (TaggedInt::Is(aValue))
- {
- int64 length = TaggedInt::ToInt64(aValue);
- return (length < 0) ? 0 : length;
- }
- double length = JavascriptConversion::ToInteger(aValue, scriptContext);
- if (length < 0.0 || JavascriptNumber::IsNegZero(length))
- {
- length = 0.0;
- }
- else if (length > Math::MAX_SAFE_INTEGER)
- {
- length = Math::MAX_SAFE_INTEGER;
- }
- return NumberUtilities::TryToInt64(length);
- }
- } // namespace Js
|