JavascriptConversion.cpp 60 KB

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