JavascriptConversion.cpp 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeLanguagePch.h"
  6. #include "RuntimeMathPch.h"
  7. #include "Library/JavascriptNumberObject.h"
  8. #include "Library/JavascriptStringObject.h"
  9. #include "Library/DateImplementation.h"
  10. #include "Library/JavascriptDate.h"
  11. namespace Js
  12. {
  13. static const double k_2to16 = 65536.0;
  14. static const double k_2to31 = 2147483648.0;
  15. static const double k_2to32 = 4294967296.0;
  16. // ES5 9.10 indicates that this method should throw a TypeError if the supplied value is Undefined or Null.
  17. // Our implementation returns FALSE in this scenario, expecting the caller to throw the TypeError.
  18. // This allows the caller to provide more context in the error message without having to unnecessarily
  19. // construct the message string before knowing whether or not the object is coercible.
  20. BOOL JavascriptConversion::CheckObjectCoercible(Var aValue, ScriptContext* scriptContext)
  21. {
  22. return !JavascriptOperators::IsUndefinedOrNull(aValue);
  23. }
  24. //ES5 9.11 Undefined, Null, Boolean, Number, String - return false
  25. //If Object has a [[Call]] internal method, then return true, otherwise return false
  26. bool JavascriptConversion::IsCallable(Var aValue)
  27. {
  28. if (!RecyclableObject::Is(aValue))
  29. {
  30. return false;
  31. }
  32. return IsCallable(RecyclableObject::UnsafeFromVar(aValue));
  33. }
  34. bool JavascriptConversion::IsCallable(_In_ RecyclableObject* aValue)
  35. {
  36. JavascriptMethod entryPoint = RecyclableObject::UnsafeFromVar(aValue)->GetEntryPoint();
  37. return RecyclableObject::DefaultEntryPoint != entryPoint;
  38. }
  39. //----------------------------------------------------------------------------
  40. // ES5 9.12 SameValue algorithm implementation.
  41. // 1. If Type(x) is different from Type(y), return false.
  42. // 2. If Type(x) is Undefined, return true.
  43. // 3. If Type(x) is Null, return true.
  44. // 4. If Type(x) is Number, then.
  45. // a. If x is NaN and y is NaN, return true.
  46. // b. If x is +0 and y is -0, return false.
  47. // c. If x is -0 and y is +0, return false.
  48. // d. If x is the same number value as y, return true.
  49. // e. Return false.
  50. // 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.
  51. // 6. If Type(x) is Boolean, return true if x and y are both true or both false; otherwise, return false.
  52. // 7. Return true if x and y refer to the same object. Otherwise, return false.
  53. //----------------------------------------------------------------------------
  54. template<bool zero>
  55. bool JavascriptConversion::SameValueCommon(Var aLeft, Var aRight)
  56. {
  57. if (aLeft == aRight)
  58. {
  59. return true;
  60. }
  61. TypeId leftType = JavascriptOperators::GetTypeId(aLeft);
  62. if (JavascriptOperators::IsUndefinedOrNullType(leftType))
  63. {
  64. return false;
  65. }
  66. TypeId rightType = JavascriptOperators::GetTypeId(aRight);
  67. double dblLeft, dblRight;
  68. switch (leftType)
  69. {
  70. case TypeIds_Integer:
  71. switch (rightType)
  72. {
  73. case TypeIds_Integer:
  74. return false;
  75. case TypeIds_Number:
  76. dblLeft = TaggedInt::ToDouble(aLeft);
  77. dblRight = JavascriptNumber::GetValue(aRight);
  78. goto CommonNumber;
  79. case TypeIds_Int64Number:
  80. {
  81. int leftValue = TaggedInt::ToInt32(aLeft);
  82. __int64 rightValue = JavascriptInt64Number::UnsafeFromVar(aRight)->GetValue();
  83. return leftValue == rightValue;
  84. }
  85. case TypeIds_UInt64Number:
  86. {
  87. int leftValue = TaggedInt::ToInt32(aLeft);
  88. unsigned __int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
  89. return leftValue == rightValue;
  90. }
  91. }
  92. break;
  93. case TypeIds_Int64Number:
  94. switch (rightType)
  95. {
  96. case TypeIds_Integer:
  97. {
  98. __int64 leftValue = JavascriptInt64Number::UnsafeFromVar(aLeft)->GetValue();
  99. int rightValue = TaggedInt::ToInt32(aRight);
  100. return leftValue == rightValue;
  101. }
  102. case TypeIds_Number:
  103. dblLeft = (double)JavascriptInt64Number::UnsafeFromVar(aLeft)->GetValue();
  104. dblRight = JavascriptNumber::GetValue(aRight);
  105. goto CommonNumber;
  106. case TypeIds_Int64Number:
  107. {
  108. __int64 leftValue = JavascriptInt64Number::UnsafeFromVar(aLeft)->GetValue();
  109. __int64 rightValue = JavascriptInt64Number::UnsafeFromVar(aRight)->GetValue();
  110. return leftValue == rightValue;
  111. }
  112. case TypeIds_UInt64Number:
  113. {
  114. __int64 leftValue = JavascriptInt64Number::UnsafeFromVar(aLeft)->GetValue();
  115. unsigned __int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
  116. return ((unsigned __int64)leftValue == rightValue);
  117. }
  118. }
  119. break;
  120. case TypeIds_UInt64Number:
  121. switch (rightType)
  122. {
  123. case TypeIds_Integer:
  124. {
  125. unsigned __int64 leftValue = JavascriptUInt64Number::UnsafeFromVar(aLeft)->GetValue();
  126. __int64 rightValue = TaggedInt::ToInt32(aRight);
  127. return (leftValue == (unsigned __int64)rightValue);
  128. }
  129. case TypeIds_Number:
  130. dblLeft = (double)JavascriptUInt64Number::UnsafeFromVar(aLeft)->GetValue();
  131. dblRight = JavascriptNumber::GetValue(aRight);
  132. goto CommonNumber;
  133. case TypeIds_Int64Number:
  134. {
  135. unsigned __int64 leftValue = JavascriptUInt64Number::UnsafeFromVar(aLeft)->GetValue();
  136. __int64 rightValue = JavascriptInt64Number::UnsafeFromVar(aRight)->GetValue();
  137. return (leftValue == (unsigned __int64)rightValue);
  138. }
  139. case TypeIds_UInt64Number:
  140. {
  141. unsigned __int64 leftValue = JavascriptUInt64Number::UnsafeFromVar(aLeft)->GetValue();
  142. unsigned __int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
  143. return leftValue == rightValue;
  144. }
  145. }
  146. break;
  147. case TypeIds_Number:
  148. switch (rightType)
  149. {
  150. case TypeIds_Integer:
  151. dblLeft = JavascriptNumber::GetValue(aLeft);
  152. dblRight = TaggedInt::ToDouble(aRight);
  153. goto CommonNumber;
  154. case TypeIds_Int64Number:
  155. dblLeft = JavascriptNumber::GetValue(aLeft);
  156. dblRight = (double)JavascriptInt64Number::UnsafeFromVar(aRight)->GetValue();
  157. goto CommonNumber;
  158. case TypeIds_UInt64Number:
  159. dblLeft = JavascriptNumber::GetValue(aLeft);
  160. dblRight = (double)JavascriptUInt64Number::UnsafeFromVar(aRight)->GetValue();
  161. goto CommonNumber;
  162. case TypeIds_Number:
  163. dblLeft = JavascriptNumber::GetValue(aLeft);
  164. dblRight = JavascriptNumber::GetValue(aRight);
  165. CommonNumber:
  166. if (JavascriptNumber::IsNan(dblLeft) && JavascriptNumber::IsNan(dblRight))
  167. {
  168. return true;
  169. }
  170. if (zero)
  171. {
  172. // SameValueZero(+0,-0) returns true;
  173. return dblLeft == dblRight;
  174. }
  175. else
  176. {
  177. // SameValue(+0,-0) returns false;
  178. return (NumberUtilities::LuLoDbl(dblLeft) == NumberUtilities::LuLoDbl(dblRight) &&
  179. NumberUtilities::LuHiDbl(dblLeft) == NumberUtilities::LuHiDbl(dblRight));
  180. }
  181. }
  182. break;
  183. case TypeIds_Boolean:
  184. return false;
  185. case TypeIds_String:
  186. switch (rightType)
  187. {
  188. case TypeIds_String:
  189. return JavascriptString::Equals(JavascriptString::UnsafeFromVar(aLeft), JavascriptString::UnsafeFromVar(aRight));
  190. }
  191. break;
  192. #if DBG
  193. case TypeIds_Symbol:
  194. if (rightType == TypeIds_Symbol)
  195. {
  196. JavascriptSymbol* leftSymbol = JavascriptSymbol::UnsafeFromVar(aLeft);
  197. JavascriptSymbol* rightSymbol = JavascriptSymbol::UnsafeFromVar(aRight);
  198. Assert(leftSymbol->GetValue() != rightSymbol->GetValue());
  199. }
  200. #endif
  201. default:
  202. break;
  203. }
  204. return false;
  205. }
  206. template bool JavascriptConversion::SameValueCommon<false>(Var aLeft, Var aRight);
  207. template bool JavascriptConversion::SameValueCommon<true>(Var aLeft, Var aRight);
  208. //----------------------------------------------------------------------------
  209. // ToObject() takes a value and converts it to an Object type
  210. // Implementation of ES5 9.9
  211. // The spec indicates that this method should throw a TypeError if the supplied value is Undefined or Null.
  212. // Our implementation returns FALSE in this scenario, expecting the caller to throw the TypeError.
  213. // This allows the caller to provide more context in the error message without having to unnecessarily
  214. // construct the message string before knowing whether or not the value can be converted to an object.
  215. //
  216. // Undefined Return FALSE.
  217. // Null Return FALSE.
  218. // Boolean Create a new Boolean object whose [[PrimitiveValue]]
  219. // internal property is set to the value of the boolean.
  220. // See 15.6 for a description of Boolean objects.
  221. // Return TRUE.
  222. // Number Create a new Number object whose [[PrimitiveValue]]
  223. // internal property is set to the value of the number.
  224. // See 15.7 for a description of Number objects.
  225. // Return TRUE.
  226. // String Create a new String object whose [[PrimitiveValue]]
  227. // internal property is set to the value of the string.
  228. // See 15.5 for a description of String objects.
  229. // Return TRUE.
  230. // Object The result is the input argument (no conversion).
  231. // Return TRUE.
  232. //----------------------------------------------------------------------------
  233. BOOL JavascriptConversion::ToObject(Var aValue, ScriptContext* scriptContext, RecyclableObject** object)
  234. {
  235. Assert(object);
  236. switch (JavascriptOperators::GetTypeId(aValue))
  237. {
  238. case TypeIds_Undefined:
  239. case TypeIds_Null:
  240. return FALSE;
  241. case TypeIds_Number:
  242. case TypeIds_Integer:
  243. *object = scriptContext->GetLibrary()->CreateNumberObject(aValue);
  244. return TRUE;
  245. default:
  246. *object = RecyclableObject::FromVar(aValue)->ToObject(scriptContext);
  247. return TRUE;
  248. }
  249. }
  250. //----------------------------------------------------------------------------
  251. // ToPropertyKey() takes a value and converts it to a property key
  252. // Implementation of ES6 7.1.14
  253. //----------------------------------------------------------------------------
  254. void JavascriptConversion::ToPropertyKey(
  255. Var argument,
  256. _In_ ScriptContext* scriptContext,
  257. _Out_ const PropertyRecord** propertyRecord,
  258. _Out_opt_ PropertyString** propString)
  259. {
  260. Var key = JavascriptConversion::ToPrimitive<JavascriptHint::HintString>(argument, scriptContext);
  261. PropertyString * propertyString = nullptr;
  262. if (JavascriptSymbol::Is(key))
  263. {
  264. // If we are looking up a property keyed by a symbol, we already have the PropertyId in the symbol
  265. *propertyRecord = JavascriptSymbol::UnsafeFromVar(key)->GetValue();
  266. }
  267. else
  268. {
  269. // For all other types, convert the key into a string and use that as the property name
  270. JavascriptString * propName = JavascriptConversion::ToString(key, scriptContext);
  271. propName->GetPropertyRecord(propertyRecord);
  272. }
  273. if (propString)
  274. {
  275. *propString = propertyString;
  276. }
  277. }
  278. //----------------------------------------------------------------------------
  279. // ToPrimitive() takes a value and an optional argument and converts it to a non Object type
  280. // Implementation of ES5 9.1
  281. //
  282. // Undefined:The result equals the input argument (no conversion).
  283. // Null: The result equals the input argument (no conversion).
  284. // Boolean: The result equals the input argument (no conversion).
  285. // Number: The result equals the input argument (no conversion).
  286. // String: The result equals the input argument (no conversion).
  287. // VariantDate:Returns the value for variant date by calling ToPrimitive directly.
  288. // Object: Return a default value for the Object.
  289. // The default value of an object is retrieved by calling the [[DefaultValue]]
  290. // internal method of the object, passing the optional hint PreferredType.
  291. // The behavior of the [[DefaultValue]] internal method is defined by this specification
  292. // for all native ECMAScript objects (8.12.9).
  293. //----------------------------------------------------------------------------
  294. template <JavascriptHint hint>
  295. Var JavascriptConversion::ToPrimitive(_In_ Var aValue, _In_ ScriptContext * requestContext)
  296. {
  297. switch (JavascriptOperators::GetTypeId(aValue))
  298. {
  299. case TypeIds_Undefined:
  300. case TypeIds_Null:
  301. case TypeIds_Integer:
  302. case TypeIds_Boolean:
  303. case TypeIds_Number:
  304. case TypeIds_String:
  305. case TypeIds_Symbol:
  306. return aValue;
  307. case TypeIds_VariantDate:
  308. {
  309. Var result = nullptr;
  310. if (JavascriptVariantDate::UnsafeFromVar(aValue)->ToPrimitive(hint, &result, requestContext) != TRUE)
  311. {
  312. result = nullptr;
  313. }
  314. return result;
  315. }
  316. case TypeIds_StringObject:
  317. {
  318. JavascriptStringObject * stringObject = JavascriptStringObject::UnsafeFromVar(aValue);
  319. ScriptContext * objectScriptContext = stringObject->GetScriptContext();
  320. if (objectScriptContext->optimizationOverrides.GetSideEffects() & (hint == JavascriptHint::HintString ? SideEffects_ToString : SideEffects_ValueOf))
  321. {
  322. return MethodCallToPrimitive<hint>(stringObject, requestContext);
  323. }
  324. return CrossSite::MarshalVar(requestContext, stringObject->Unwrap(), objectScriptContext);
  325. }
  326. case TypeIds_NumberObject:
  327. {
  328. JavascriptNumberObject * numberObject = JavascriptNumberObject::UnsafeFromVar(aValue);
  329. ScriptContext * objectScriptContext = numberObject->GetScriptContext();
  330. if (hint == JavascriptHint::HintString)
  331. {
  332. if (objectScriptContext->optimizationOverrides.GetSideEffects() & SideEffects_ToString)
  333. {
  334. return MethodCallToPrimitive<hint>(numberObject, requestContext);
  335. }
  336. return JavascriptNumber::ToStringRadix10(numberObject->GetValue(), requestContext);
  337. }
  338. else
  339. {
  340. if (objectScriptContext->optimizationOverrides.GetSideEffects() & SideEffects_ValueOf)
  341. {
  342. return MethodCallToPrimitive<hint>(numberObject, requestContext);
  343. }
  344. return CrossSite::MarshalVar(requestContext, numberObject->Unwrap(), objectScriptContext);
  345. }
  346. }
  347. case TypeIds_SymbolObject:
  348. {
  349. JavascriptSymbolObject* symbolObject = JavascriptSymbolObject::UnsafeFromVar(aValue);
  350. return CrossSite::MarshalVar(requestContext, symbolObject->Unwrap(), symbolObject->GetScriptContext());
  351. }
  352. case TypeIds_Date:
  353. case TypeIds_WinRTDate:
  354. {
  355. JavascriptDate* dateObject = JavascriptDate::UnsafeFromVar(aValue);
  356. if(hint == JavascriptHint::HintNumber)
  357. {
  358. if (dateObject->GetScriptContext()->optimizationOverrides.GetSideEffects() & SideEffects_ValueOf)
  359. {
  360. // if no Method exists this function falls back to OrdinaryToPrimitive
  361. // if IsES6ToPrimitiveEnabled flag is off we also fall back to OrdinaryToPrimitive
  362. return MethodCallToPrimitive<hint>(dateObject, requestContext);
  363. }
  364. return JavascriptNumber::ToVarNoCheck(dateObject->GetTime(), requestContext);
  365. }
  366. else
  367. {
  368. if (dateObject->GetScriptContext()->optimizationOverrides.GetSideEffects() & SideEffects_ToString)
  369. {
  370. // if no Method exists this function falls back to OrdinaryToPrimitive
  371. // if IsES6ToPrimitiveEnabled flag is off we also fall back to OrdinaryToPrimitive
  372. return MethodCallToPrimitive<hint>(dateObject, requestContext);
  373. }
  374. return JavascriptDate::ToString(dateObject, requestContext);
  375. }
  376. }
  377. // convert to JavascriptNumber
  378. case TypeIds_Int64Number:
  379. return JavascriptInt64Number::UnsafeFromVar(aValue)->ToJavascriptNumber();
  380. case TypeIds_UInt64Number:
  381. return JavascriptUInt64Number::UnsafeFromVar(aValue)->ToJavascriptNumber();
  382. default:
  383. // if no Method exists this function falls back to OrdinaryToPrimitive
  384. // if IsES6ToPrimitiveEnabled flag is off we also fall back to OrdinaryToPrimitive
  385. return MethodCallToPrimitive<hint>(RecyclableObject::UnsafeFromVar(aValue), requestContext);
  386. }
  387. }
  388. //----------------------------------------------------------------------------
  389. //https://tc39.github.io/ecma262/#sec-canonicalnumericindexstring
  390. //1. Assert : Type(argument) is String.
  391. //2. If argument is "-0", then return -0.
  392. //3. Let n be ToNumber(argument).
  393. //4. If SameValue(ToString(n), argument) is false, then return undefined.
  394. //5. Return n.
  395. //----------------------------------------------------------------------------
  396. BOOL JavascriptConversion::CanonicalNumericIndexString(JavascriptString *aValue, double *indexValue, ScriptContext * scriptContext)
  397. {
  398. if (JavascriptString::IsNegZero(aValue))
  399. {
  400. *indexValue = -0;
  401. return TRUE;
  402. }
  403. double indexDoubleValue = aValue->ToDouble();
  404. if (JavascriptString::Equals(JavascriptNumber::ToStringRadix10(indexDoubleValue, scriptContext), aValue))
  405. {
  406. *indexValue = indexDoubleValue;
  407. return TRUE;
  408. }
  409. return FALSE;
  410. }
  411. template <JavascriptHint hint>
  412. Var JavascriptConversion::MethodCallToPrimitive(_In_ RecyclableObject* value, _In_ ScriptContext * requestContext)
  413. {
  414. Var result = nullptr;
  415. ScriptContext *const scriptContext = value->GetScriptContext();
  416. //7.3.9 GetMethod(V, P)
  417. // The abstract operation GetMethod is used to get the value of a specific property of an ECMAScript language value when the value of the
  418. // 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
  419. // property key. This abstract operation performs the following steps:
  420. // 1. Assert: IsPropertyKey(P) is true.
  421. // 2. Let func be ? GetV(V, P).
  422. // 3. If func is either undefined or null, return undefined.
  423. // 4. If IsCallable(func) is false, throw a TypeError exception.
  424. // 5. Return func.
  425. Var varMethod = nullptr;
  426. if (!requestContext->GetConfig()->IsES6ToPrimitiveEnabled()
  427. || JavascriptOperators::CheckIfObjectAndProtoChainHasNoSpecialProperties(value)
  428. || !JavascriptOperators::GetPropertyReference(value, PropertyIds::_symbolToPrimitive, &varMethod, requestContext)
  429. || JavascriptOperators::IsUndefinedOrNull(varMethod))
  430. {
  431. return OrdinaryToPrimitive<hint>(value, requestContext);
  432. }
  433. if (!JavascriptFunction::Is(varMethod))
  434. {
  435. // Don't error if we disabled implicit calls
  436. JavascriptError::TryThrowTypeError(scriptContext, requestContext, JSERR_Property_NeedFunction, requestContext->GetPropertyName(PropertyIds::_symbolToPrimitive)->GetBuffer());
  437. return requestContext->GetLibrary()->GetNull();
  438. }
  439. // Let exoticToPrim be GetMethod(input, @@toPrimitive).
  440. JavascriptFunction* exoticToPrim = JavascriptFunction::UnsafeFromVar(varMethod);
  441. JavascriptString* hintString = nullptr;
  442. if (hint == JavascriptHint::HintString)
  443. {
  444. hintString = requestContext->GetLibrary()->GetStringTypeDisplayString();
  445. }
  446. else if (hint == JavascriptHint::HintNumber)
  447. {
  448. hintString = requestContext->GetLibrary()->GetNumberTypeDisplayString();
  449. }
  450. else
  451. {
  452. hintString = requestContext->GetPropertyString(PropertyIds::default_);
  453. }
  454. // If exoticToPrim is not undefined, then
  455. Assert(nullptr != exoticToPrim);
  456. ThreadContext * threadContext = requestContext->GetThreadContext();
  457. result = threadContext->ExecuteImplicitCall(exoticToPrim, ImplicitCall_ToPrimitive, [=]()->Js::Var
  458. {
  459. // Stack object should have a pre-op bail on implicit call. We shouldn't see them here.
  460. Assert(!ThreadContext::IsOnStack(value));
  461. // Let result be the result of calling the[[Call]] internal method of exoticToPrim, with input as thisArgument and(hint) as argumentsList.
  462. return CALL_FUNCTION(threadContext, exoticToPrim, CallInfo(CallFlags_Value, 2), value, hintString);
  463. });
  464. if (!result)
  465. {
  466. // There was an implicit call and implicit calls are disabled. This would typically cause a bailout.
  467. Assert(threadContext->IsDisableImplicitCall());
  468. return requestContext->GetLibrary()->GetNull();
  469. }
  470. Assert(!CrossSite::NeedMarshalVar(result, requestContext));
  471. // If result is an ECMAScript language value and Type(result) is not Object, then return result.
  472. if (TaggedInt::Is(result) || !JavascriptOperators::IsObjectType(JavascriptOperators::GetTypeId(result)))
  473. {
  474. return result;
  475. }
  476. // Else, throw a TypeError exception.
  477. else
  478. {
  479. // Don't error if we disabled implicit calls
  480. JavascriptError::TryThrowTypeError(scriptContext, requestContext, JSERR_FunctionArgument_Invalid, _u("[Symbol.toPrimitive]"));
  481. return requestContext->GetLibrary()->GetNull();
  482. }
  483. }
  484. template <JavascriptHint hint>
  485. Var JavascriptConversion::OrdinaryToPrimitive(_In_ RecyclableObject* value, _In_ ScriptContext * requestContext)
  486. {
  487. Var result;
  488. if (!value->ToPrimitive(hint, &result, requestContext))
  489. {
  490. ScriptContext *const scriptContext = value->GetScriptContext();
  491. int32 hCode;
  492. switch (hint)
  493. {
  494. case JavascriptHint::HintNumber:
  495. hCode = JSERR_NeedNumber;
  496. break;
  497. case JavascriptHint::HintString:
  498. hCode = JSERR_NeedString;
  499. break;
  500. default:
  501. hCode = VBSERR_OLENoPropOrMethod;
  502. break;
  503. }
  504. JavascriptError::TryThrowTypeError(scriptContext, scriptContext, hCode);
  505. return requestContext->GetLibrary()->GetNull();
  506. }
  507. return result;
  508. }
  509. JavascriptString *JavascriptConversion::CoerseString(Var aValue, ScriptContext* scriptContext, const char16* apiNameForErrorMsg)
  510. {
  511. if (!JavascriptConversion::CheckObjectCoercible(aValue, scriptContext))
  512. {
  513. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, apiNameForErrorMsg);
  514. }
  515. return ToString(aValue, scriptContext);
  516. }
  517. //----------------------------------------------------------------------------
  518. // ToString - abstract operation
  519. // ES5 9.8
  520. //Input Type Result
  521. // Undefined
  522. // "undefined"
  523. // Null
  524. // "null"
  525. // Boolean
  526. // If the argument is true, then the result is "true". If the argument is false, then the result is "false".
  527. // Number
  528. // See 9.8.1 below.
  529. // String
  530. // Return the input argument (no conversion)
  531. // Object
  532. // Apply the following steps:
  533. // 1. Let primValue be ToPrimitive(input argument, hint String).
  534. // 2. Return ToString(primValue).
  535. //----------------------------------------------------------------------------
  536. JavascriptString *JavascriptConversion::ToString(Var aValue, ScriptContext* scriptContext)
  537. {
  538. Assert(scriptContext->GetThreadContext()->IsScriptActive());
  539. BOOL fPrimitiveOnly = false;
  540. while(true)
  541. {
  542. switch (JavascriptOperators::GetTypeId(aValue))
  543. {
  544. case TypeIds_Undefined:
  545. return scriptContext->GetLibrary()->GetUndefinedDisplayString();
  546. case TypeIds_Null:
  547. return scriptContext->GetLibrary()->GetNullDisplayString();
  548. case TypeIds_Integer:
  549. return scriptContext->GetIntegerString(aValue);
  550. case TypeIds_Boolean:
  551. return JavascriptBoolean::UnsafeFromVar(aValue)->GetValue() ? scriptContext->GetLibrary()->GetTrueDisplayString() : scriptContext->GetLibrary()->GetFalseDisplayString();
  552. case TypeIds_Number:
  553. return JavascriptNumber::ToStringRadix10(JavascriptNumber::GetValue(aValue), scriptContext);
  554. case TypeIds_Int64Number:
  555. {
  556. __int64 value = JavascriptInt64Number::UnsafeFromVar(aValue)->GetValue();
  557. if (!TaggedInt::IsOverflow(value))
  558. {
  559. return scriptContext->GetIntegerString((int)value);
  560. }
  561. else
  562. {
  563. return JavascriptInt64Number::ToString(aValue, scriptContext);
  564. }
  565. }
  566. case TypeIds_UInt64Number:
  567. {
  568. unsigned __int64 value = JavascriptUInt64Number::UnsafeFromVar(aValue)->GetValue();
  569. if (!TaggedInt::IsOverflow(value))
  570. {
  571. return scriptContext->GetIntegerString((uint)value);
  572. }
  573. else
  574. {
  575. return JavascriptUInt64Number::ToString(aValue, scriptContext);
  576. }
  577. }
  578. case TypeIds_String:
  579. {
  580. ScriptContext* aValueScriptContext = Js::RecyclableObject::UnsafeFromVar(aValue)->GetScriptContext();
  581. return JavascriptString::UnsafeFromVar(CrossSite::MarshalVar(scriptContext,
  582. aValue, aValueScriptContext));
  583. }
  584. case TypeIds_VariantDate:
  585. return JavascriptVariantDate::FromVar(aValue)->GetValueString(scriptContext);
  586. case TypeIds_Symbol:
  587. return JavascriptSymbol::UnsafeFromVar(aValue)->ToString(scriptContext);
  588. case TypeIds_SymbolObject:
  589. return JavascriptSymbol::ToString(JavascriptSymbolObject::UnsafeFromVar(aValue)->GetValue(), scriptContext);
  590. case TypeIds_GlobalObject:
  591. aValue = static_cast<Js::GlobalObject*>(aValue)->ToThis();
  592. // fall through
  593. default:
  594. {
  595. AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToString");
  596. if(fPrimitiveOnly)
  597. {
  598. AssertMsg(FALSE, "wrong call in ToString, no dynamic objects should get here");
  599. JavascriptError::ThrowError(scriptContext, VBSERR_InternalError);
  600. }
  601. fPrimitiveOnly = true;
  602. aValue = ToPrimitive<JavascriptHint::HintString>(aValue, scriptContext);
  603. }
  604. }
  605. }
  606. }
  607. JavascriptString *JavascriptConversion::ToLocaleString(Var aValue, ScriptContext* scriptContext)
  608. {
  609. switch (JavascriptOperators::GetTypeId(aValue))
  610. {
  611. case TypeIds_Undefined:
  612. return scriptContext->GetLibrary()->GetUndefinedDisplayString();
  613. case TypeIds_Null:
  614. return scriptContext->GetLibrary()->GetNullDisplayString();
  615. case TypeIds_Integer:
  616. return JavascriptNumber::ToLocaleString(TaggedInt::ToInt32(aValue), scriptContext);
  617. case TypeIds_Boolean:
  618. return JavascriptBoolean::UnsafeFromVar(aValue)->GetValue() ? scriptContext->GetLibrary()->GetTrueDisplayString() : scriptContext->GetLibrary()->GetFalseDisplayString();
  619. case TypeIds_Int64Number:
  620. return JavascriptNumber::ToLocaleString((double)JavascriptInt64Number::UnsafeFromVar(aValue)->GetValue(), scriptContext);
  621. case TypeIds_UInt64Number:
  622. return JavascriptNumber::ToLocaleString((double)JavascriptUInt64Number::UnsafeFromVar(aValue)->GetValue(), scriptContext);
  623. case TypeIds_Number:
  624. return JavascriptNumber::ToLocaleString(JavascriptNumber::GetValue(aValue), scriptContext);
  625. case TypeIds_String:
  626. return JavascriptString::UnsafeFromVar(aValue);
  627. case TypeIds_VariantDate:
  628. // Legacy behavior was to create an empty object and call toLocaleString on it, which would result in this value
  629. return scriptContext->GetLibrary()->GetObjectDisplayString();
  630. case TypeIds_Symbol:
  631. return JavascriptSymbol::UnsafeFromVar(aValue)->ToString(scriptContext);
  632. default:
  633. {
  634. RecyclableObject* object = RecyclableObject::FromVar(aValue);
  635. Var value = JavascriptOperators::GetProperty(object, PropertyIds::toLocaleString, scriptContext, NULL);
  636. if (JavascriptConversion::IsCallable(value))
  637. {
  638. RecyclableObject* toLocaleStringFunction = RecyclableObject::FromVar(value);
  639. Var aResult = CALL_FUNCTION(scriptContext->GetThreadContext(), toLocaleStringFunction, CallInfo(1), aValue);
  640. if (JavascriptString::Is(aResult))
  641. {
  642. return JavascriptString::UnsafeFromVar(aResult);
  643. }
  644. else
  645. {
  646. return JavascriptConversion::ToString(aResult, scriptContext);
  647. }
  648. }
  649. JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_NeedFunction, scriptContext->GetPropertyName(PropertyIds::toLocaleString)->GetBuffer());
  650. }
  651. }
  652. }
  653. //----------------------------------------------------------------------------
  654. // ToBoolean_Full:
  655. // (ES3.0: S9.2):
  656. //
  657. // Input Output
  658. // ----- ------
  659. // 'undefined' 'false'
  660. // 'null' 'false'
  661. // Boolean Value
  662. // Number 'false' if +0, -0, or Nan
  663. // 'true' otherwise
  664. // String 'false' if argument is ""
  665. // 'true' otherwise
  666. // Object 'true'
  667. // Falsy Object 'false'
  668. //----------------------------------------------------------------------------
  669. BOOL JavascriptConversion::ToBoolean_Full(Var aValue, ScriptContext* scriptContext)
  670. {
  671. AssertMsg(!TaggedInt::Is(aValue), "Should be detected");
  672. AssertMsg(RecyclableObject::Is(aValue), "Should be handled already");
  673. auto type = RecyclableObject::UnsafeFromVar(aValue)->GetType();
  674. switch (type->GetTypeId())
  675. {
  676. case TypeIds_Undefined:
  677. case TypeIds_Null:
  678. case TypeIds_VariantDate:
  679. return false;
  680. case TypeIds_Symbol:
  681. return true;
  682. case TypeIds_Boolean:
  683. return JavascriptBoolean::UnsafeFromVar(aValue)->GetValue();
  684. #if !FLOATVAR
  685. case TypeIds_Number:
  686. {
  687. double value = JavascriptNumber::GetValue(aValue);
  688. return (!JavascriptNumber::IsNan(value)) && (!JavascriptNumber::IsZero(value));
  689. }
  690. #endif
  691. case TypeIds_Int64Number:
  692. {
  693. __int64 value = JavascriptInt64Number::UnsafeFromVar(aValue)->GetValue();
  694. return value != 0;
  695. }
  696. case TypeIds_UInt64Number:
  697. {
  698. unsigned __int64 value = JavascriptUInt64Number::UnsafeFromVar(aValue)->GetValue();
  699. return value != 0;
  700. }
  701. case TypeIds_String:
  702. {
  703. JavascriptString * pstValue = JavascriptString::UnsafeFromVar(aValue);
  704. return pstValue->GetLength() > 0;
  705. }
  706. default:
  707. {
  708. AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToBoolean");
  709. // Falsy objects evaluate to false when converted to Boolean.
  710. return !type->IsFalsy();
  711. }
  712. }
  713. }
  714. void JavascriptConversion::ToFloat_Helper(Var aValue, float *pResult, ScriptContext* scriptContext)
  715. {
  716. *pResult = (float)ToNumber_Full(aValue, scriptContext);
  717. }
  718. void JavascriptConversion::ToNumber_Helper(Var aValue, double *pResult, ScriptContext* scriptContext)
  719. {
  720. Assert(Js::JavascriptStackWalker::ValidateTopJitFrame(scriptContext));
  721. *pResult = ToNumber_Full(aValue, scriptContext);
  722. }
  723. // Used for the JIT's float type specialization
  724. // Convert aValue to double, but only allow primitives. Return false otherwise.
  725. BOOL JavascriptConversion::ToNumber_FromPrimitive(Var aValue, double *pResult, BOOL allowUndefined, ScriptContext* scriptContext)
  726. {
  727. Assert(Js::JavascriptStackWalker::ValidateTopJitFrame(scriptContext));
  728. Assert(!TaggedNumber::Is(aValue));
  729. RecyclableObject *obj = RecyclableObject::FromVar(aValue);
  730. // NOTE: Don't allow strings, otherwise JIT's float type specialization has to worry about concats
  731. if (obj->GetTypeId() >= TypeIds_String)
  732. {
  733. return false;
  734. }
  735. if (!allowUndefined && obj->GetTypeId() == TypeIds_Undefined)
  736. {
  737. return false;
  738. }
  739. *pResult = ToNumber_Full(aValue, scriptContext);
  740. return true;
  741. }
  742. //----------------------------------------------------------------------------
  743. // ToNumber_Full:
  744. // Implements ES6 Draft Rev 26 July 18, 2014
  745. //
  746. // Undefined: NaN
  747. // Null: 0
  748. // boolean: v==true ? 1 : 0 ;
  749. // number: v (original number)
  750. // String: conversion by spec algorithm
  751. // object: ToNumber(PrimitiveValue(v, hint_number))
  752. // Symbol: TypeError
  753. //----------------------------------------------------------------------------
  754. double JavascriptConversion::ToNumber_Full(Var aValue,ScriptContext* scriptContext)
  755. {
  756. AssertMsg(!TaggedInt::Is(aValue), "Should be detected");
  757. ScriptContext * objectScriptContext = RecyclableObject::Is(aValue) ? RecyclableObject::UnsafeFromVar(aValue)->GetScriptContext() : nullptr;
  758. BOOL fPrimitiveOnly = false;
  759. while(true)
  760. {
  761. switch (JavascriptOperators::GetTypeId(aValue))
  762. {
  763. case TypeIds_Symbol:
  764. JavascriptError::TryThrowTypeError(objectScriptContext, scriptContext, JSERR_NeedNumber);
  765. // Fallthrough to return NaN if exceptions are disabled
  766. case TypeIds_Undefined:
  767. return JavascriptNumber::GetValue(scriptContext->GetLibrary()->GetNaN());
  768. case TypeIds_Null:
  769. return 0;
  770. case TypeIds_Integer:
  771. return TaggedInt::ToDouble(aValue);
  772. case TypeIds_Boolean:
  773. return JavascriptBoolean::UnsafeFromVar(aValue)->GetValue() ? 1 : +0;
  774. case TypeIds_Number:
  775. return JavascriptNumber::GetValue(aValue);
  776. case TypeIds_Int64Number:
  777. return (double)JavascriptInt64Number::UnsafeFromVar(aValue)->GetValue();
  778. case TypeIds_UInt64Number:
  779. return (double)JavascriptUInt64Number::UnsafeFromVar(aValue)->GetValue();
  780. case TypeIds_String:
  781. return JavascriptString::UnsafeFromVar(aValue)->ToDouble();
  782. case TypeIds_VariantDate:
  783. return Js::DateImplementation::GetTvUtc(Js::DateImplementation::JsLocalTimeFromVarDate(JavascriptVariantDate::UnsafeFromVar(aValue)->GetValue()), scriptContext);
  784. default:
  785. {
  786. AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToInteger");
  787. if(fPrimitiveOnly)
  788. {
  789. JavascriptError::ThrowError(scriptContext, VBSERR_OLENoPropOrMethod);
  790. }
  791. fPrimitiveOnly = true;
  792. aValue = ToPrimitive<JavascriptHint::HintNumber>(aValue, scriptContext);
  793. }
  794. }
  795. }
  796. }
  797. //----------------------------------------------------------------------------
  798. // second part of the ToInteger() implementation.(ES5.0: S9.4).
  799. //----------------------------------------------------------------------------
  800. double JavascriptConversion::ToInteger_Full(Var aValue,ScriptContext* scriptContext)
  801. {
  802. AssertMsg(!TaggedInt::Is(aValue), "Should be detected");
  803. ScriptContext * objectScriptContext = RecyclableObject::Is(aValue) ? RecyclableObject::UnsafeFromVar(aValue)->GetScriptContext() : nullptr;
  804. BOOL fPrimitiveOnly = false;
  805. while(true)
  806. {
  807. switch (JavascriptOperators::GetTypeId(aValue))
  808. {
  809. case TypeIds_Symbol:
  810. JavascriptError::TryThrowTypeError(objectScriptContext, scriptContext, JSERR_NeedNumber);
  811. // Fallthrough to return 0 if exceptions are disabled
  812. case TypeIds_Undefined:
  813. case TypeIds_Null:
  814. return 0;
  815. case TypeIds_Integer:
  816. return TaggedInt::ToInt32(aValue);
  817. case TypeIds_Boolean:
  818. return JavascriptBoolean::UnsafeFromVar(aValue)->GetValue() ? 1 : +0;
  819. case TypeIds_Number:
  820. return ToInteger(JavascriptNumber::GetValue(aValue));
  821. case TypeIds_Int64Number:
  822. return ToInteger((double)JavascriptInt64Number::UnsafeFromVar(aValue)->GetValue());
  823. case TypeIds_UInt64Number:
  824. return ToInteger((double)JavascriptUInt64Number::UnsafeFromVar(aValue)->GetValue());
  825. case TypeIds_String:
  826. return ToInteger(JavascriptString::UnsafeFromVar(aValue)->ToDouble());
  827. case TypeIds_VariantDate:
  828. return ToInteger(ToNumber_Full(aValue, scriptContext));
  829. default:
  830. {
  831. AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToInteger");
  832. if(fPrimitiveOnly)
  833. {
  834. AssertMsg(FALSE, "wrong call in ToInteger_Full, no dynamic objects should get here");
  835. JavascriptError::ThrowError(scriptContext, VBSERR_OLENoPropOrMethod);
  836. }
  837. fPrimitiveOnly = true;
  838. aValue = ToPrimitive<JavascriptHint::HintNumber>(aValue, scriptContext);
  839. }
  840. }
  841. }
  842. }
  843. double JavascriptConversion::ToInteger(double val)
  844. {
  845. if(JavascriptNumber::IsNan(val))
  846. return 0;
  847. if(JavascriptNumber::IsPosInf(val) || JavascriptNumber::IsNegInf(val) ||
  848. JavascriptNumber::IsZero(val))
  849. {
  850. return val;
  851. }
  852. return ( ((val < 0) ? -1 : 1 ) * floor(fabs(val)));
  853. }
  854. //----------------------------------------------------------------------------
  855. // ToInt32() converts the given Var to an Int32 value, as described in
  856. // (ES3.0: S9.5).
  857. //----------------------------------------------------------------------------
  858. int32 JavascriptConversion::ToInt32_Full(Var aValue, ScriptContext* scriptContext)
  859. {
  860. Assert(Js::JavascriptStackWalker::ValidateTopJitFrame(scriptContext));
  861. AssertMsg(!TaggedInt::Is(aValue), "Should be detected");
  862. ScriptContext * objectScriptContext = RecyclableObject::Is(aValue) ? RecyclableObject::UnsafeFromVar(aValue)->GetScriptContext() : nullptr;
  863. // This is used when TaggedInt's overflow but remain under int32
  864. // so Number is our most critical case:
  865. TypeId typeId = JavascriptOperators::GetTypeId(aValue);
  866. if (typeId == TypeIds_Number)
  867. {
  868. return JavascriptMath::ToInt32Core(JavascriptNumber::GetValue(aValue));
  869. }
  870. switch (typeId)
  871. {
  872. case TypeIds_Symbol:
  873. JavascriptError::TryThrowTypeError(objectScriptContext, scriptContext, JSERR_NeedNumber);
  874. // Fallthrough to return 0 if exceptions are disabled
  875. case TypeIds_Undefined:
  876. case TypeIds_Null:
  877. return 0;
  878. case TypeIds_Integer:
  879. return TaggedInt::ToInt32(aValue);
  880. case TypeIds_Boolean:
  881. return JavascriptBoolean::UnsafeFromVar(aValue)->GetValue() ? 1 : +0;
  882. case TypeIds_Int64Number:
  883. // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
  884. // treat it as double anyhow.
  885. return JavascriptMath::ToInt32Core((double)JavascriptInt64Number::UnsafeFromVar(aValue)->GetValue());
  886. case TypeIds_UInt64Number:
  887. // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
  888. // treat it as double anyhow.
  889. return JavascriptMath::ToInt32Core((double)JavascriptUInt64Number::UnsafeFromVar(aValue)->GetValue());
  890. case TypeIds_String:
  891. {
  892. double result;
  893. if (JavascriptString::UnsafeFromVar(aValue)->ToDouble(&result))
  894. {
  895. return JavascriptMath::ToInt32Core(result);
  896. }
  897. // If the string isn't a valid number, ToDouble returns NaN, and ToInt32 of that is 0
  898. return 0;
  899. }
  900. case TypeIds_VariantDate:
  901. return ToInt32(ToNumber_Full(aValue, scriptContext));
  902. default:
  903. AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToInteger32");
  904. aValue = ToPrimitive<JavascriptHint::HintNumber>(aValue, scriptContext);
  905. }
  906. switch (JavascriptOperators::GetTypeId(aValue))
  907. {
  908. case TypeIds_Symbol:
  909. JavascriptError::TryThrowTypeError(objectScriptContext, scriptContext, JSERR_NeedNumber);
  910. // Fallthrough to return 0 if exceptions are disabled
  911. case TypeIds_Undefined:
  912. case TypeIds_Null:
  913. return 0;
  914. case TypeIds_Integer:
  915. return TaggedInt::ToInt32(aValue);
  916. case TypeIds_Boolean:
  917. return JavascriptBoolean::UnsafeFromVar(aValue)->GetValue() ? 1 : +0;
  918. case TypeIds_Number:
  919. return ToInt32(JavascriptNumber::GetValue(aValue));
  920. case TypeIds_Int64Number:
  921. // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
  922. // treat it as double anyhow.
  923. return JavascriptMath::ToInt32Core((double)JavascriptInt64Number::UnsafeFromVar(aValue)->GetValue());
  924. case TypeIds_UInt64Number:
  925. // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
  926. // treat it as double anyhow.
  927. return JavascriptMath::ToInt32Core((double)JavascriptUInt64Number::UnsafeFromVar(aValue)->GetValue());
  928. case TypeIds_String:
  929. {
  930. double result;
  931. if (JavascriptString::UnsafeFromVar(aValue)->ToDouble(&result))
  932. {
  933. return ToInt32(result);
  934. }
  935. // If the string isn't a valid number, ToDouble returns NaN, and ToInt32 of that is 0
  936. return 0;
  937. }
  938. case TypeIds_VariantDate:
  939. return ToInt32(ToNumber_Full(aValue, scriptContext));
  940. default:
  941. AssertMsg(FALSE, "wrong call in ToInteger32_Full, no dynamic objects should get here.");
  942. JavascriptError::ThrowError(scriptContext, VBSERR_OLENoPropOrMethod);
  943. }
  944. }
  945. // a strict version of ToInt32 conversion that returns false for non int32 values like, inf, NaN, undef
  946. BOOL JavascriptConversion::ToInt32Finite(Var aValue, ScriptContext* scriptContext, int32* result)
  947. {
  948. ScriptContext * objectScriptContext = RecyclableObject::Is(aValue) ? RecyclableObject::UnsafeFromVar(aValue)->GetScriptContext() : nullptr;
  949. BOOL fPrimitiveOnly = false;
  950. while(true)
  951. {
  952. switch (JavascriptOperators::GetTypeId(aValue))
  953. {
  954. case TypeIds_Symbol:
  955. JavascriptError::TryThrowTypeError(objectScriptContext, scriptContext, JSERR_NeedNumber);
  956. // Fallthrough to return false and set result to 0 if exceptions are disabled
  957. case TypeIds_Undefined:
  958. *result = 0;
  959. return false;
  960. case TypeIds_Null:
  961. *result = 0;
  962. return true;
  963. case TypeIds_Integer:
  964. *result = TaggedInt::ToInt32(aValue);
  965. return true;
  966. case TypeIds_Boolean:
  967. *result = JavascriptBoolean::UnsafeFromVar(aValue)->GetValue() ? 1 : +0;
  968. return true;
  969. case TypeIds_Number:
  970. return ToInt32Finite(JavascriptNumber::GetValue(aValue), result);
  971. case TypeIds_Int64Number:
  972. // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
  973. // treat it as double anyhow.
  974. return ToInt32Finite((double)JavascriptInt64Number::UnsafeFromVar(aValue)->GetValue(), result);
  975. case TypeIds_UInt64Number:
  976. // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
  977. // treat it as double anyhow.
  978. return ToInt32Finite((double)JavascriptUInt64Number::UnsafeFromVar(aValue)->GetValue(), result);
  979. case TypeIds_String:
  980. return ToInt32Finite(JavascriptString::UnsafeFromVar(aValue)->ToDouble(), result);
  981. case TypeIds_VariantDate:
  982. return ToInt32Finite(ToNumber_Full(aValue, scriptContext), result);
  983. default:
  984. {
  985. AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToInteger32");
  986. if(fPrimitiveOnly)
  987. {
  988. AssertMsg(FALSE, "wrong call in ToInteger32_Full, no dynamic objects should get here");
  989. JavascriptError::ThrowError(scriptContext, VBSERR_OLENoPropOrMethod);
  990. }
  991. fPrimitiveOnly = true;
  992. aValue = ToPrimitive<JavascriptHint::HintNumber>(aValue, scriptContext);
  993. }
  994. }
  995. }
  996. }
  997. int32 JavascriptConversion::ToInt32(double T1)
  998. {
  999. return JavascriptMath::ToInt32Core(T1);
  1000. }
  1001. __int64 JavascriptConversion::ToInt64(Var aValue, ScriptContext* scriptContext)
  1002. {
  1003. switch (JavascriptOperators::GetTypeId(aValue))
  1004. {
  1005. case TypeIds_Integer:
  1006. {
  1007. return TaggedInt::ToInt32(aValue);
  1008. }
  1009. case TypeIds_Int64Number:
  1010. {
  1011. JavascriptInt64Number* int64Number = JavascriptInt64Number::UnsafeFromVar(aValue);
  1012. return int64Number->GetValue();
  1013. }
  1014. case TypeIds_UInt64Number:
  1015. {
  1016. JavascriptUInt64Number* uint64Number = JavascriptUInt64Number::UnsafeFromVar(aValue);
  1017. return (__int64)uint64Number->GetValue();
  1018. }
  1019. case TypeIds_Number:
  1020. return JavascriptMath::TryToInt64(JavascriptNumber::GetValue(aValue));
  1021. default:
  1022. return (unsigned __int64)JavascriptConversion::ToInt32_Full(aValue, scriptContext);
  1023. }
  1024. }
  1025. unsigned __int64 JavascriptConversion::ToUInt64(Var aValue, ScriptContext* scriptContext)
  1026. {
  1027. switch (JavascriptOperators::GetTypeId(aValue))
  1028. {
  1029. case TypeIds_Integer:
  1030. {
  1031. return (unsigned __int64)TaggedInt::ToInt32(aValue);
  1032. }
  1033. case TypeIds_Int64Number:
  1034. {
  1035. JavascriptInt64Number* int64Number = JavascriptInt64Number::UnsafeFromVar(aValue);
  1036. return (unsigned __int64)int64Number->GetValue();
  1037. }
  1038. case TypeIds_UInt64Number:
  1039. {
  1040. JavascriptUInt64Number* uint64Number = JavascriptUInt64Number::UnsafeFromVar(aValue);
  1041. return uint64Number->GetValue();
  1042. }
  1043. case TypeIds_Number:
  1044. return static_cast<unsigned __int64>(JavascriptMath::TryToInt64(JavascriptNumber::GetValue(aValue)));
  1045. default:
  1046. return (unsigned __int64)JavascriptConversion::ToInt32_Full(aValue, scriptContext);
  1047. }
  1048. }
  1049. BOOL JavascriptConversion::ToInt32Finite(double value, int32* result)
  1050. {
  1051. if((!NumberUtilities::IsFinite(value)) || JavascriptNumber::IsNan(value))
  1052. {
  1053. *result = 0;
  1054. return false;
  1055. }
  1056. else
  1057. {
  1058. *result = JavascriptMath::ToInt32Core(value);
  1059. return true;
  1060. }
  1061. }
  1062. //----------------------------------------------------------------------------
  1063. // (ES3.0: S9.6).
  1064. //----------------------------------------------------------------------------
  1065. uint32 JavascriptConversion::ToUInt32_Full(Var aValue, ScriptContext* scriptContext)
  1066. {
  1067. AssertMsg(!TaggedInt::Is(aValue), "Should be detected");
  1068. ScriptContext * objectScriptContext = RecyclableObject::Is(aValue) ? RecyclableObject::UnsafeFromVar(aValue)->GetScriptContext() : nullptr;
  1069. BOOL fPrimitiveOnly = false;
  1070. while(true)
  1071. {
  1072. switch (JavascriptOperators::GetTypeId(aValue))
  1073. {
  1074. case TypeIds_Symbol:
  1075. JavascriptError::TryThrowTypeError(objectScriptContext, scriptContext, JSERR_NeedNumber);
  1076. // Fallthrough to return 0 if exceptions are disabled
  1077. case TypeIds_Undefined:
  1078. case TypeIds_Null:
  1079. return 0;
  1080. case TypeIds_Integer:
  1081. return TaggedInt::ToUInt32(aValue);
  1082. case TypeIds_Boolean:
  1083. return JavascriptBoolean::UnsafeFromVar(aValue)->GetValue() ? 1 : +0;
  1084. case TypeIds_Number:
  1085. return JavascriptMath::ToUInt32(JavascriptNumber::GetValue(aValue));
  1086. case TypeIds_Int64Number:
  1087. // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
  1088. // treat it as double anyhow.
  1089. return JavascriptMath::ToUInt32((double)JavascriptInt64Number::UnsafeFromVar(aValue)->GetValue());
  1090. case TypeIds_UInt64Number:
  1091. // we won't lose precision if the int64 is within 32bit boundary; otherwise we need to
  1092. // treat it as double anyhow.
  1093. return JavascriptMath::ToUInt32((double)JavascriptUInt64Number::UnsafeFromVar(aValue)->GetValue());
  1094. case TypeIds_String:
  1095. {
  1096. double result;
  1097. if (JavascriptString::UnsafeFromVar(aValue)->ToDouble(&result))
  1098. {
  1099. return JavascriptMath::ToUInt32(result);
  1100. }
  1101. // If the string isn't a valid number, ToDouble returns NaN, and ToUInt32 of that is 0
  1102. return 0;
  1103. }
  1104. case TypeIds_VariantDate:
  1105. return JavascriptMath::ToUInt32(ToNumber_Full(aValue, scriptContext));
  1106. default:
  1107. {
  1108. AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToUInt32");
  1109. if(fPrimitiveOnly)
  1110. {
  1111. AssertMsg(FALSE, "wrong call in ToUInt32_Full, no dynamic objects should get here");
  1112. JavascriptError::ThrowError(scriptContext, VBSERR_OLENoPropOrMethod);
  1113. }
  1114. fPrimitiveOnly = true;
  1115. aValue = ToPrimitive<JavascriptHint::HintNumber>(aValue, scriptContext);
  1116. }
  1117. }
  1118. }
  1119. }
  1120. uint32 JavascriptConversion::ToUInt32(double T1)
  1121. {
  1122. // Same as doing ToInt32 and reinterpret the bits as uint32
  1123. return (uint32)JavascriptMath::ToInt32Core(T1);
  1124. }
  1125. //----------------------------------------------------------------------------
  1126. // ToUInt16() converts the given Var to a UInt16 value, as described in
  1127. // (ES3.0: S9.6).
  1128. //----------------------------------------------------------------------------
  1129. uint16 JavascriptConversion::ToUInt16_Full(IN Var aValue, ScriptContext* scriptContext)
  1130. {
  1131. AssertMsg(!TaggedInt::Is(aValue), "Should be detected");
  1132. ScriptContext * objectScriptContext = RecyclableObject::Is(aValue) ? RecyclableObject::UnsafeFromVar(aValue)->GetScriptContext() : nullptr;
  1133. BOOL fPrimitiveOnly = false;
  1134. while(true)
  1135. {
  1136. switch (JavascriptOperators::GetTypeId(aValue))
  1137. {
  1138. case TypeIds_Symbol:
  1139. JavascriptError::TryThrowTypeError(objectScriptContext, scriptContext, JSERR_NeedNumber);
  1140. // Fallthrough to return 0 if exceptions are disabled
  1141. case TypeIds_Undefined:
  1142. case TypeIds_Null:
  1143. return 0;
  1144. case TypeIds_Integer:
  1145. return TaggedInt::ToUInt16(aValue);
  1146. case TypeIds_Boolean:
  1147. return JavascriptBoolean::UnsafeFromVar(aValue)->GetValue() ? 1 : +0;
  1148. case TypeIds_Number:
  1149. return ToUInt16(JavascriptNumber::GetValue(aValue));
  1150. case TypeIds_Int64Number:
  1151. // we won't lose precision if the int64 is within 16bit boundary; otherwise we need to
  1152. // treat it as double anyhow.
  1153. return ToUInt16((double)JavascriptInt64Number::UnsafeFromVar(aValue)->GetValue());
  1154. case TypeIds_UInt64Number:
  1155. // we won't lose precision if the int64 is within 16bit boundary; otherwise we need to
  1156. // treat it as double anyhow.
  1157. return ToUInt16((double)JavascriptUInt64Number::UnsafeFromVar(aValue)->GetValue());
  1158. case TypeIds_String:
  1159. {
  1160. double result;
  1161. if (JavascriptString::UnsafeFromVar(aValue)->ToDouble(&result))
  1162. {
  1163. return ToUInt16(result);
  1164. }
  1165. // If the string isn't a valid number, ToDouble is NaN, and ToUInt16 of that is 0
  1166. return 0;
  1167. }
  1168. case TypeIds_VariantDate:
  1169. return ToUInt16(ToNumber_Full(aValue, scriptContext));
  1170. default:
  1171. {
  1172. AssertMsg(JavascriptOperators::IsObject(aValue), "bad type object in conversion ToUIn16");
  1173. if(fPrimitiveOnly)
  1174. {
  1175. AssertMsg(FALSE, "wrong call in ToUInt16, no dynamic objects should get here");
  1176. JavascriptError::ThrowError(scriptContext, VBSERR_OLENoPropOrMethod);
  1177. }
  1178. fPrimitiveOnly = true;
  1179. aValue = ToPrimitive<JavascriptHint::HintNumber>(aValue, scriptContext);
  1180. }
  1181. }
  1182. }
  1183. }
  1184. uint16 JavascriptConversion::ToUInt16(double T1)
  1185. {
  1186. //
  1187. // VC does the right thing here, if we first convert to uint32 and then to uint16
  1188. // Spec says mod should be done.
  1189. //
  1190. uint32 result = JavascriptConversion::ToUInt32(T1);
  1191. #if defined(_M_IX86) && _MSC_FULL_VER < 160030329
  1192. // Well VC doesn't actually do the right thing... It takes (uint16)(uint32)double and removes the
  1193. // middle uint32 cast to (uint16)double, which isn't the same thing. Somehow, it only seems to be a
  1194. // problem for x86. Forcing a store to uint32 prevents the incorrect optimization.
  1195. //
  1196. // A bug has been filled in the Dev11 database: TF bug id #901495
  1197. // Fixed in compiler 16.00.30329.00
  1198. volatile uint32 volResult = result;
  1199. #endif
  1200. return (uint16) result;
  1201. }
  1202. int16 JavascriptConversion::ToInt16(double aValue)
  1203. {
  1204. return (int16)ToInt32(aValue);
  1205. }
  1206. int8 JavascriptConversion::ToInt8(double aValue)
  1207. {
  1208. return (int8)ToInt32(aValue);
  1209. }
  1210. uint8 JavascriptConversion::ToUInt8(double aValue)
  1211. {
  1212. return (uint8)ToUInt32(aValue);
  1213. }
  1214. JavascriptString * JavascriptConversion::ToPrimitiveString(Var aValue, ScriptContext * scriptContext)
  1215. {
  1216. return ToString(ToPrimitive<JavascriptHint::None>(aValue, scriptContext), scriptContext);
  1217. }
  1218. double JavascriptConversion::LongToDouble(__int64 aValue)
  1219. {
  1220. return static_cast<double>(aValue);
  1221. }
  1222. // Windows x64 version implemented in masm to work around precision limitation
  1223. #if !defined(_WIN32 ) || !defined(_M_X64)
  1224. double JavascriptConversion::ULongToDouble(unsigned __int64 aValue)
  1225. {
  1226. return static_cast<double>(aValue);
  1227. }
  1228. #endif
  1229. float JavascriptConversion::LongToFloat(__int64 aValue)
  1230. {
  1231. return static_cast<float>(aValue);
  1232. }
  1233. float JavascriptConversion::ULongToFloat (unsigned __int64 aValue)
  1234. {
  1235. return static_cast<float>(aValue);
  1236. }
  1237. int32 JavascriptConversion::F32TOI32(float src, ScriptContext * ctx)
  1238. {
  1239. if (Wasm::WasmMath::isInRange<float, uint32, NumberConstants::k_Float32TwoTo31, NumberConstants::k_Float32NegZero, NumberConstants::k_Float32NegTwoTo31,
  1240. &Wasm::WasmMath::LessThan<uint32>, &Wasm::WasmMath::LessOrEqual<uint32>>(src) &&
  1241. !Wasm::WasmMath::isNaN<float>(src))
  1242. {
  1243. return (int32)src;
  1244. }
  1245. JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
  1246. }
  1247. uint32 JavascriptConversion::F32TOU32(float src, ScriptContext * ctx)
  1248. {
  1249. if (Wasm::WasmMath::isInRange<float, uint32, NumberConstants::k_Float32TwoTo32, NumberConstants::k_Float32NegZero, NumberConstants::k_Float32NegOne,
  1250. &Wasm::WasmMath::LessThan<uint32>, &Wasm::WasmMath::LessThan<uint32>>(src) &&
  1251. !Wasm::WasmMath::isNaN<float>(src))
  1252. {
  1253. return (uint32)src;
  1254. }
  1255. JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
  1256. }
  1257. int32 JavascriptConversion::F64TOI32(double src, ScriptContext * ctx)
  1258. {
  1259. if (Wasm::WasmMath::isInRange<double, uint64, NumberConstants::k_TwoTo31, NumberConstants::k_NegZero, NumberConstants::k_NegTwoTo31,
  1260. &Wasm::WasmMath::LessOrEqual<uint64>, &Wasm::WasmMath::LessOrEqual<uint64>>(src) &&
  1261. !Wasm::WasmMath::isNaN<double>(src))
  1262. {
  1263. return (int32)src;
  1264. }
  1265. JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
  1266. }
  1267. uint32 JavascriptConversion::F64TOU32(double src, ScriptContext * ctx)
  1268. {
  1269. if (Wasm::WasmMath::isInRange<double, uint64, NumberConstants::k_TwoTo32, NumberConstants::k_NegZero, NumberConstants::k_NegOne,
  1270. &Wasm::WasmMath::LessOrEqual<uint64>, &Wasm::WasmMath::LessThan<uint64>>(src)
  1271. && !Wasm::WasmMath::isNaN<double>(src))
  1272. {
  1273. return (uint32)src;
  1274. }
  1275. JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
  1276. }
  1277. int64 JavascriptConversion::F32TOI64(float src, ScriptContext * ctx)
  1278. {
  1279. if (Wasm::WasmMath::isInRange<float, uint32, NumberConstants::k_Float32TwoTo63, NumberConstants::k_Float32NegZero, NumberConstants::k_Float32NegTwoTo63,
  1280. &Wasm::WasmMath::LessThan<uint32>, &Wasm::WasmMath::LessOrEqual<uint32>>(src) &&
  1281. !Wasm::WasmMath::isNaN<float>(src))
  1282. {
  1283. return (int64)src;
  1284. }
  1285. JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
  1286. }
  1287. uint64 JavascriptConversion::F32TOU64(float src, ScriptContext * ctx)
  1288. {
  1289. if (Wasm::WasmMath::isInRange<float, uint32, NumberConstants::k_Float32TwoTo64, NumberConstants::k_Float32NegZero, NumberConstants::k_Float32NegOne,
  1290. &Wasm::WasmMath::LessThan<uint32>, &Wasm::WasmMath::LessThan<uint32>>(src) &&
  1291. !Wasm::WasmMath::isNaN<float>(src))
  1292. {
  1293. return (uint64)src;
  1294. }
  1295. JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
  1296. }
  1297. int64 JavascriptConversion::F64TOI64(double src, ScriptContext * ctx)
  1298. {
  1299. if (Wasm::WasmMath::isInRange<double, uint64, NumberConstants::k_TwoTo63, NumberConstants::k_NegZero, NumberConstants::k_NegTwoTo63,
  1300. &Wasm::WasmMath::LessThan<uint64>, &Wasm::WasmMath::LessOrEqual<uint64>>(src) &&
  1301. !Wasm::WasmMath::isNaN<double>(src))
  1302. {
  1303. return (int64)src;
  1304. }
  1305. JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
  1306. }
  1307. uint64 JavascriptConversion::F64TOU64(double src, ScriptContext * ctx)
  1308. {
  1309. if (Wasm::WasmMath::isInRange<double, uint64, NumberConstants::k_TwoTo64, NumberConstants::k_NegZero, NumberConstants::k_NegOne,
  1310. &Wasm::WasmMath::LessThan<uint64>, &Wasm::WasmMath::LessThan<uint64>>(src) &&
  1311. !Wasm::WasmMath::isNaN<double>(src))
  1312. {
  1313. return (uint64)src;
  1314. }
  1315. JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow);
  1316. }
  1317. int64 JavascriptConversion::ToLength(Var aValue, ScriptContext* scriptContext)
  1318. {
  1319. if (TaggedInt::Is(aValue))
  1320. {
  1321. int64 length = TaggedInt::ToInt64(aValue);
  1322. return (length < 0) ? 0 : length;
  1323. }
  1324. double length = JavascriptConversion::ToInteger(aValue, scriptContext);
  1325. if (length < 0.0 || JavascriptNumber::IsNegZero(length))
  1326. {
  1327. length = 0.0;
  1328. }
  1329. else if (length > Math::MAX_SAFE_INTEGER)
  1330. {
  1331. length = Math::MAX_SAFE_INTEGER;
  1332. }
  1333. return NumberUtilities::TryToInt64(length);
  1334. }
  1335. } // namespace Js