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