JavascriptConversion.cpp 61 KB

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