JavascriptNumber.cpp 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167
  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 "RuntimeLibraryPch.h"
  6. #include "errstr.h"
  7. #include "Library/EngineInterfaceObject.h"
  8. #include "Library/IntlEngineInterfaceExtensionObject.h"
  9. using namespace PlatformAgnostic;
  10. namespace Js
  11. {
  12. DEFINE_RECYCLER_TRACKER_PERF_COUNTER(JavascriptNumber);
  13. Var JavascriptNumber::ToVarNoCheck(double value, ScriptContext* scriptContext)
  14. {
  15. return JavascriptNumber::NewInlined(value, scriptContext);
  16. }
  17. Var JavascriptNumber::ToVarWithCheck(double value, ScriptContext* scriptContext)
  18. {
  19. #if FLOATVAR
  20. if (IsNan(value))
  21. {
  22. value = IsNegative(value) ? JavascriptNumber::NegativeNaN : JavascriptNumber::NaN;
  23. }
  24. #endif
  25. return JavascriptNumber::NewInlined(value, scriptContext);
  26. }
  27. Var JavascriptNumber::ToVarInPlace(double value, ScriptContext* scriptContext, JavascriptNumber *result)
  28. {
  29. return InPlaceNew(value, scriptContext, result);
  30. }
  31. Var JavascriptNumber::ToVarInPlace(int64 value, ScriptContext* scriptContext, JavascriptNumber *result)
  32. {
  33. if (!TaggedInt::IsOverflow(value))
  34. {
  35. return TaggedInt::ToVarUnchecked(static_cast<int>(value));
  36. }
  37. return InPlaceNew(static_cast<double>(value), scriptContext, result);
  38. }
  39. Var JavascriptNumber::ToVarMaybeInPlace(double value, ScriptContext* scriptContext, JavascriptNumber *result)
  40. {
  41. if (result)
  42. {
  43. return InPlaceNew(value, scriptContext, result);
  44. }
  45. return ToVarNoCheck(value, scriptContext);
  46. }
  47. Var JavascriptNumber::ToVarInPlace(int32 nValue, ScriptContext* scriptContext, Js::JavascriptNumber *result)
  48. {
  49. if (!TaggedInt::IsOverflow(nValue))
  50. {
  51. return TaggedInt::ToVarUnchecked(nValue);
  52. }
  53. return InPlaceNew(static_cast<double>(nValue), scriptContext, result);
  54. }
  55. Var JavascriptNumber::ToVarInPlace(uint32 nValue, ScriptContext* scriptContext, Js::JavascriptNumber *result)
  56. {
  57. if (!TaggedInt::IsOverflow(nValue))
  58. {
  59. return TaggedInt::ToVarUnchecked(nValue);
  60. }
  61. return InPlaceNew(static_cast<double>(nValue), scriptContext, result);
  62. }
  63. Var JavascriptNumber::ToVarIntCheck(double value,ScriptContext* scriptContext)
  64. {
  65. //
  66. // Check if a well-known value:
  67. // - This significantly cuts down on the below floating-point to integer conversions.
  68. //
  69. if (value == 0.0)
  70. {
  71. if(IsNegZero(value))
  72. {
  73. return scriptContext->GetLibrary()->GetNegativeZero();
  74. }
  75. return TaggedInt::ToVarUnchecked(0);
  76. }
  77. if (value == 1.0)
  78. {
  79. return TaggedInt::ToVarUnchecked(1);
  80. }
  81. //
  82. // Check if number can be reduced back into a TaggedInt:
  83. // - This avoids extra GC.
  84. //
  85. int nValue = (int) value;
  86. double dblCheck = (double) nValue;
  87. if ((dblCheck == value) && (!TaggedInt::IsOverflow(nValue)))
  88. {
  89. return TaggedInt::ToVarUnchecked(nValue);
  90. }
  91. return JavascriptNumber::NewInlined(value,scriptContext);
  92. }
  93. bool JavascriptNumber::TryGetInt32OrUInt32Value(const double value, int32 *const int32Value, bool *const isInt32)
  94. {
  95. Assert(int32Value);
  96. Assert(isInt32);
  97. if(value <= 0)
  98. {
  99. return *isInt32 = TryGetInt32Value(value, int32Value);
  100. }
  101. const uint32 i = static_cast<uint32>(value);
  102. if(static_cast<double>(i) != value)
  103. {
  104. return false;
  105. }
  106. *int32Value = i;
  107. *isInt32 = static_cast<int32>(i) >= 0;
  108. return true;
  109. }
  110. bool JavascriptNumber::IsInt32(const double value)
  111. {
  112. int32 i;
  113. return TryGetInt32Value(value, &i);
  114. }
  115. bool JavascriptNumber::IsInt32OrUInt32(const double value)
  116. {
  117. int32 i;
  118. bool isInt32;
  119. return TryGetInt32OrUInt32Value(value, &i, &isInt32);
  120. }
  121. bool JavascriptNumber::IsInt32_NoChecks(const Var number)
  122. {
  123. Assert(number);
  124. Assert(Is(number));
  125. return IsInt32(GetValue(number));
  126. }
  127. bool JavascriptNumber::IsInt32OrUInt32_NoChecks(const Var number)
  128. {
  129. Assert(number);
  130. Assert(Is(number));
  131. return IsInt32OrUInt32(GetValue(number));
  132. }
  133. int32 JavascriptNumber::GetNonzeroInt32Value_NoTaggedIntCheck(const Var object)
  134. {
  135. Assert(object);
  136. Assert(!TaggedInt::Is(object));
  137. int32 i;
  138. return Is_NoTaggedIntCheck(object) && TryGetInt32Value(GetValue(object), &i) ? i : 0;
  139. }
  140. int32 JavascriptNumber::DirectPowIntInt(bool* isOverflow, int32 x, int32 y)
  141. {
  142. if (y < 0)
  143. {
  144. *isOverflow = true;
  145. return 0;
  146. }
  147. uint32 uexp = static_cast<uint32>(y);
  148. int32 result = 1;
  149. while (true)
  150. {
  151. if ((uexp & 1) != 0)
  152. {
  153. if (Int32Math::Mul(result, x, &result))
  154. {
  155. *isOverflow = true;
  156. break;
  157. }
  158. }
  159. if ((uexp >>= 1) == 0)
  160. {
  161. *isOverflow = false;
  162. break;
  163. }
  164. if (Int32Math::Mul(x, x, &x))
  165. {
  166. *isOverflow = true;
  167. break;
  168. }
  169. }
  170. return *isOverflow ? 0 : result;
  171. }
  172. double JavascriptNumber::DirectPowDoubleInt(double x, int32 y)
  173. {
  174. // For exponent in [-8, 8], aggregate the product according to binary representation
  175. // of exponent. This acceleration may lead to significant deviation for larger exponent
  176. if (y >= -8 && y <= 8)
  177. {
  178. uint32 uexp = static_cast<uint32>(y >= 0 ? y : -y);
  179. for (double result = 1.0; ; x *= x)
  180. {
  181. if ((uexp & 1) != 0)
  182. {
  183. result *= x;
  184. }
  185. if ((uexp >>= 1) == 0)
  186. {
  187. return (y < 0 ? (1.0 / result) : result);
  188. }
  189. }
  190. }
  191. // always call pow(double, double) in C runtime which has a bug to process pow(double, int).
  192. return ::pow(x, static_cast<double>(y));
  193. }
  194. #if _M_IX86
  195. extern "C" double __cdecl __libm_sse2_pow(double, double);
  196. static const double d1_0 = 1.0;
  197. #if !ENABLE_NATIVE_CODEGEN
  198. double JavascriptNumber::DirectPow(double x, double y)
  199. {
  200. return ::pow(x, y);
  201. }
  202. #else
  203. #pragma warning(push)
  204. // C4740: flow in or out of inline asm code suppresses global optimization
  205. // It is fine to disable glot opt on this function which is mostly written in assembly
  206. #pragma warning(disable:4740)
  207. __declspec(naked)
  208. double JavascriptNumber::DirectPow(double x, double y)
  209. {
  210. UNREFERENCED_PARAMETER(x);
  211. UNREFERENCED_PARAMETER(y);
  212. double savedX, savedY, result;
  213. // This function is called directly from jitted, float-preferenced code.
  214. // It looks for x and y in xmm0 and xmm1 and returns the result in xmm0.
  215. // Check for pow(1, Infinity/NaN) and return NaN in that case;
  216. // then check fast path of small integer exponent, otherwise,
  217. // go to the fast CRT helper.
  218. __asm {
  219. // check y for 1.0
  220. ucomisd xmm1, d1_0
  221. jne pow_full
  222. jp pow_full
  223. ret
  224. pow_full:
  225. // Check y for non-finite value
  226. pextrw eax, xmm1, 3
  227. not eax
  228. test eax, 0x7ff0
  229. jne normal
  230. // check for |x| == 1
  231. movsd xmm2, xmm0
  232. andpd xmm2, AbsDoubleCst
  233. movsd xmm3, d1_0
  234. ucomisd xmm2, xmm3
  235. lahf
  236. test ah, 68
  237. jp normal
  238. movsd xmm0, JavascriptNumber::k_Nan
  239. ret
  240. normal:
  241. push ebp
  242. mov ebp, esp // prepare stack frame for sub function call
  243. sub esp, 0x40 // 4 variables, reserve 0x10 for 1
  244. movsd savedX, xmm0
  245. movsd savedY, xmm1
  246. }
  247. int intY;
  248. if (TryGetInt32Value(savedY, &intY) && intY >= -8 && intY <= 8)
  249. {
  250. result = DirectPowDoubleInt(savedX, intY);
  251. __asm {
  252. movsd xmm0, result
  253. }
  254. }
  255. else
  256. {
  257. __asm {
  258. movsd xmm0, savedX
  259. movsd xmm1, savedY
  260. call dword ptr[__libm_sse2_pow]
  261. }
  262. }
  263. __asm {
  264. mov esp, ebp
  265. pop ebp
  266. ret
  267. }
  268. }
  269. #pragma warning(pop)
  270. #endif
  271. #elif defined(_M_AMD64) || defined(_M_ARM32_OR_ARM64)
  272. double JavascriptNumber::DirectPow(double x, double y)
  273. {
  274. if(y == 1.0)
  275. {
  276. return x;
  277. }
  278. // For AMD64/ARM calling convention already uses SSE2/VFP registers so we don't have to use assembler.
  279. // We can't just use "if (0 == y)" because NaN compares
  280. // equal to 0 according to our compilers.
  281. int32 intY;
  282. if (0 == NumberUtilities::LuLoDbl(y) && 0 == (NumberUtilities::LuHiDbl(y) & 0x7FFFFFFF))
  283. {
  284. // pow(x, 0) = 1 even if x is NaN.
  285. return 1;
  286. }
  287. else if (1.0 == fabs(x) && !NumberUtilities::IsFinite(y))
  288. {
  289. // pow([+/-] 1, Infinity) = NaN according to javascript, but not for CRT pow.
  290. return JavascriptNumber::NaN;
  291. }
  292. else if (TryGetInt32Value(y, &intY))
  293. {
  294. // check fast path
  295. return DirectPowDoubleInt(x, intY);
  296. }
  297. return ::pow(x, y);
  298. }
  299. #else
  300. double JavascriptNumber::DirectPow(double x, double y)
  301. {
  302. UNREFERENCED_PARAMETER(x);
  303. UNREFERENCED_PARAMETER(y);
  304. AssertMsg(0, "DirectPow NYI");
  305. return 0;
  306. }
  307. #endif
  308. Var JavascriptNumber::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  309. {
  310. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  311. ARGUMENTS(args, callInfo);
  312. ScriptContext* scriptContext = function->GetScriptContext();
  313. //
  314. // Determine if called as a constructor or a function.
  315. //
  316. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  317. Var newTarget = args.GetNewTarget();
  318. bool isCtorSuperCall = JavascriptOperators::GetAndAssertIsConstructorSuperCall(args);
  319. Var result;
  320. if (args.Info.Count > 1)
  321. {
  322. if (TaggedInt::Is(args[1]) || JavascriptNumber::Is(args[1]))
  323. {
  324. result = args[1];
  325. }
  326. else if (VarIs<JavascriptNumberObject>(args[1]))
  327. {
  328. result = JavascriptNumber::ToVarNoCheck(VarTo<JavascriptNumberObject>(args[1])->GetValue(), scriptContext);
  329. }
  330. else
  331. {
  332. result = JavascriptNumber::ToVarNoCheck(JavascriptConversion::ToNumber(args[1], scriptContext), scriptContext);
  333. }
  334. }
  335. else
  336. {
  337. result = TaggedInt::ToVarUnchecked(0);
  338. }
  339. if (callInfo.Flags & CallFlags_New)
  340. {
  341. JavascriptNumberObject* obj = scriptContext->GetLibrary()->CreateNumberObject(result);
  342. result = obj;
  343. }
  344. return isCtorSuperCall ?
  345. JavascriptOperators::OrdinaryCreateFromConstructor(VarTo<RecyclableObject>(newTarget), VarTo<RecyclableObject>(result), nullptr, scriptContext) :
  346. result;
  347. }
  348. ///----------------------------------------------------------------------------
  349. /// ParseInt() returns an integer value dictated by the interpretation of the
  350. /// given string argument according to the given radix argument, as described
  351. /// in (ES6.0: S20.1.2.13).
  352. ///
  353. /// Note: This is the same as the global parseInt() function as described in
  354. /// (ES6.0: S18.2.5)
  355. ///
  356. /// We actually reuse GlobalObject::EntryParseInt, so no implementation here.
  357. ///----------------------------------------------------------------------------
  358. //Var JavascriptNumber::EntryParseInt(RecyclableObject* function, CallInfo callInfo, ...)
  359. ///----------------------------------------------------------------------------
  360. /// ParseFloat() returns a Number value dictated by the interpretation of the
  361. /// given string argument as a decimal literal, as described in
  362. /// (ES6.0: S20.1.2.12).
  363. ///
  364. /// Note: This is the same as the global parseFloat() function as described in
  365. /// (ES6.0: S18.2.4)
  366. ///
  367. /// We actually reuse GlobalObject::EntryParseFloat, so no implementation here.
  368. ///----------------------------------------------------------------------------
  369. //Var JavascriptNumber::EntryParseFloat(RecyclableObject* function, CallInfo callInfo, ...)
  370. ///----------------------------------------------------------------------------
  371. /// IsNaN() return true if the given value is a Number value and is NaN, as
  372. /// described in (ES6.0: S20.1.2.4).
  373. /// Note: This is the same as the global isNaN() function as described in
  374. /// (ES6.0: S18.2.3) except that it does not coerce the argument to
  375. /// Number, instead returns false if not already a Number.
  376. ///----------------------------------------------------------------------------
  377. Var JavascriptNumber::EntryIsNaN(RecyclableObject* function, CallInfo callInfo, ...)
  378. {
  379. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  380. ARGUMENTS(args, callInfo);
  381. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  382. ScriptContext* scriptContext = function->GetScriptContext();
  383. Assert(!(callInfo.Flags & CallFlags_New));
  384. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Number_Constructor_isNaN);
  385. if (args.Info.Count < 2 || !JavascriptOperators::IsAnyNumberValue(args[1]))
  386. {
  387. return scriptContext->GetLibrary()->GetFalse();
  388. }
  389. return JavascriptBoolean::ToVar(
  390. JavascriptNumber::IsNan(JavascriptConversion::ToNumber(args[1],scriptContext)),
  391. scriptContext);
  392. }
  393. ///----------------------------------------------------------------------------
  394. /// IsFinite() returns true if the given value is a Number value and is not
  395. /// one of NaN, +Infinity, or -Infinity, as described in (ES6.0: S20.1.2.2).
  396. /// Note: This is the same as the global isFinite() function as described in
  397. /// (ES6.0: S18.2.2) except that it does not coerce the argument to
  398. /// Number, instead returns false if not already a Number.
  399. ///----------------------------------------------------------------------------
  400. Var JavascriptNumber::EntryIsFinite(RecyclableObject* function, CallInfo callInfo, ...)
  401. {
  402. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  403. ARGUMENTS(args, callInfo);
  404. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  405. ScriptContext* scriptContext = function->GetScriptContext();
  406. Assert(!(callInfo.Flags & CallFlags_New));
  407. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Number_Constructor_isFinite);
  408. if (args.Info.Count < 2 || !JavascriptOperators::IsAnyNumberValue(args[1]))
  409. {
  410. return scriptContext->GetLibrary()->GetFalse();
  411. }
  412. return JavascriptBoolean::ToVar(
  413. NumberUtilities::IsFinite(JavascriptConversion::ToNumber(args[1],scriptContext)),
  414. scriptContext);
  415. }
  416. ///----------------------------------------------------------------------------
  417. /// IsInteger() returns true if the given value is a Number value and is an
  418. /// integer, as described in (ES6.0: S20.1.2.3).
  419. ///----------------------------------------------------------------------------
  420. Var JavascriptNumber::EntryIsInteger(RecyclableObject* function, CallInfo callInfo, ...)
  421. {
  422. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  423. ARGUMENTS(args, callInfo);
  424. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  425. ScriptContext* scriptContext = function->GetScriptContext();
  426. Assert(!(callInfo.Flags & CallFlags_New));
  427. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Number_Constructor_isInteger);
  428. if (args.Info.Count < 2 || !JavascriptOperators::IsAnyNumberValue(args[1]))
  429. {
  430. return scriptContext->GetLibrary()->GetFalse();
  431. }
  432. double number = JavascriptConversion::ToNumber(args[1], scriptContext);
  433. return JavascriptBoolean::ToVar(
  434. number == JavascriptConversion::ToInteger(args[1], scriptContext) &&
  435. NumberUtilities::IsFinite(number),
  436. scriptContext);
  437. }
  438. ///----------------------------------------------------------------------------
  439. /// IsSafeInteger() returns true if the given value is a Number value and is an
  440. /// integer, as described in (ES6.0: S20.1.2.5).
  441. ///----------------------------------------------------------------------------
  442. Var JavascriptNumber::EntryIsSafeInteger(RecyclableObject* function, CallInfo callInfo, ...)
  443. {
  444. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  445. ARGUMENTS(args, callInfo);
  446. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  447. ScriptContext* scriptContext = function->GetScriptContext();
  448. Assert(!(callInfo.Flags & CallFlags_New));
  449. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Number_Constructor_isSafeInteger);
  450. if (args.Info.Count < 2 || !JavascriptOperators::IsAnyNumberValue(args[1]))
  451. {
  452. return scriptContext->GetLibrary()->GetFalse();
  453. }
  454. double number = JavascriptConversion::ToNumber(args[1], scriptContext);
  455. return JavascriptBoolean::ToVar(
  456. number == JavascriptConversion::ToInteger(args[1], scriptContext) &&
  457. number >= Js::Math::MIN_SAFE_INTEGER && number <= Js::Math::MAX_SAFE_INTEGER,
  458. scriptContext);
  459. }
  460. Var JavascriptNumber::EntryToExponential(RecyclableObject* function, CallInfo callInfo, ...)
  461. {
  462. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  463. ARGUMENTS(args, callInfo);
  464. ScriptContext* scriptContext = function->GetScriptContext();
  465. Assert(!(callInfo.Flags & CallFlags_New));
  466. if (args.Info.Count == 0)
  467. {
  468. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toExponential"));
  469. }
  470. AssertMsg(args.Info.Count > 0, "negative arg count");
  471. // spec implies ToExp is not generic. 'this' must be a number
  472. double value;
  473. if (!GetThisValue(args[0], &value))
  474. {
  475. if (JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch)
  476. {
  477. Var result;
  478. if (VarTo<RecyclableObject>(args[0])->InvokeBuiltInOperationRemotely(EntryToExponential, args, &result))
  479. {
  480. return result;
  481. }
  482. }
  483. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toExponential"));
  484. }
  485. JavascriptString * nanF;
  486. if (nullptr != (nanF = ToStringNanOrInfinite(value, scriptContext)))
  487. return nanF;
  488. // If the Fraction param. is not present we have to output as many fractional digits as we can
  489. int fractionDigits = -1;
  490. if(args.Info.Count > 1)
  491. {
  492. //use the first arg as the fraction digits, ignore the rest.
  493. Var aFractionDigits = args[1];
  494. bool noRangeCheck = false;
  495. // shortcut for tagged int's
  496. if(TaggedInt::Is(aFractionDigits))
  497. {
  498. fractionDigits = TaggedInt::ToInt32(aFractionDigits);
  499. }
  500. else if(JavascriptOperators::GetTypeId(aFractionDigits) == TypeIds_Undefined)
  501. {
  502. // fraction undefined -> digits = -1, output as many fractional digits as we can
  503. noRangeCheck = true;
  504. }
  505. else
  506. {
  507. fractionDigits = (int)JavascriptConversion::ToInteger(aFractionDigits, scriptContext);
  508. }
  509. if(!noRangeCheck && (fractionDigits < 0 || fractionDigits >20))
  510. {
  511. JavascriptError::ThrowRangeError(scriptContext, JSERR_FractionOutOfRange);
  512. }
  513. }
  514. return FormatDoubleToString(value, Js::NumberUtilities::FormatExponential, fractionDigits, scriptContext);
  515. }
  516. Var JavascriptNumber::EntryToFixed(RecyclableObject* function, CallInfo callInfo, ...)
  517. {
  518. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  519. ARGUMENTS(args, callInfo);
  520. ScriptContext* scriptContext = function->GetScriptContext();
  521. Assert(!(callInfo.Flags & CallFlags_New));
  522. if (args.Info.Count == 0)
  523. {
  524. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toFixed"));
  525. }
  526. AssertMsg(args.Info.Count > 0, "negative arg count");
  527. // spec implies ToFixed is not generic. 'this' must be a number
  528. double value;
  529. if (!GetThisValue(args[0], &value))
  530. {
  531. if (JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch)
  532. {
  533. Var result;
  534. if (VarTo<RecyclableObject>(args[0])->InvokeBuiltInOperationRemotely(EntryToFixed, args, &result))
  535. {
  536. return result;
  537. }
  538. }
  539. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toFixed"));
  540. }
  541. int fractionDigits = 0;
  542. bool isFractionDigitsInfinite = false;
  543. if(args.Info.Count > 1)
  544. {
  545. //use the first arg as the fraction digits, ignore the rest.
  546. Var aFractionDigits = args[1];
  547. // shortcut for tagged int's
  548. if(TaggedInt::Is(aFractionDigits))
  549. {
  550. fractionDigits = TaggedInt::ToInt32(aFractionDigits);
  551. }
  552. else if(JavascriptOperators::GetTypeId(aFractionDigits) == TypeIds_Undefined)
  553. {
  554. // fraction digits = 0
  555. }
  556. else
  557. {
  558. double fractionDigitsRaw = JavascriptConversion::ToInteger(aFractionDigits, scriptContext);
  559. isFractionDigitsInfinite =
  560. fractionDigitsRaw == JavascriptNumber::NEGATIVE_INFINITY ||
  561. fractionDigitsRaw == JavascriptNumber::POSITIVE_INFINITY;
  562. fractionDigits = (int)fractionDigitsRaw;
  563. }
  564. }
  565. if (fractionDigits < 0 || fractionDigits > 20)
  566. {
  567. JavascriptError::ThrowRangeError(scriptContext, JSERR_FractionOutOfRange);
  568. }
  569. if(IsNan(value))
  570. {
  571. return ToStringNan(scriptContext);
  572. }
  573. if(value >= 1e21 || value <= -1e21)
  574. {
  575. return ToStringRadix10(value, scriptContext);
  576. }
  577. return FormatDoubleToString(value, NumberUtilities::FormatFixed, fractionDigits, scriptContext);
  578. }
  579. Var JavascriptNumber::EntryToPrecision(RecyclableObject* function, CallInfo callInfo, ...)
  580. {
  581. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  582. ARGUMENTS(args, callInfo);
  583. ScriptContext* scriptContext = function->GetScriptContext();
  584. Assert(!(callInfo.Flags & CallFlags_New));
  585. if (args.Info.Count == 0)
  586. {
  587. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toPrecision"));
  588. }
  589. AssertMsg(args.Info.Count > 0, "negative arg count");
  590. // spec implies ToPrec is not generic. 'this' must be a number
  591. double value;
  592. if (!GetThisValue(args[0], &value))
  593. {
  594. if (JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch)
  595. {
  596. Var result;
  597. if (VarTo<RecyclableObject>(args[0])->InvokeBuiltInOperationRemotely(EntryToPrecision, args, &result))
  598. {
  599. return result;
  600. }
  601. }
  602. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toPrecision"));
  603. }
  604. if(args.Info.Count < 2 || JavascriptOperators::GetTypeId(args[1]) == TypeIds_Undefined)
  605. {
  606. return JavascriptConversion::ToString(args[0], scriptContext);
  607. }
  608. int precision;
  609. Var aPrecision = args[1];
  610. if(TaggedInt::Is(aPrecision))
  611. {
  612. precision = TaggedInt::ToInt32(aPrecision);
  613. }
  614. else
  615. {
  616. precision = (int) JavascriptConversion::ToInt32(aPrecision, scriptContext);
  617. }
  618. JavascriptString * nanF;
  619. if (nullptr != (nanF = ToStringNanOrInfinite(value, scriptContext)))
  620. {
  621. return nanF;
  622. }
  623. if(precision < 1 || precision > 21)
  624. {
  625. JavascriptError::ThrowRangeError(scriptContext, JSERR_PrecisionOutOfRange);
  626. }
  627. return FormatDoubleToString(value, NumberUtilities::FormatPrecision, precision, scriptContext);
  628. }
  629. Var JavascriptNumber::EntryToLocaleString(RecyclableObject* function, CallInfo callInfo, ...)
  630. {
  631. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  632. ARGUMENTS(args, callInfo);
  633. ScriptContext* scriptContext = function->GetScriptContext();
  634. Assert(!(callInfo.Flags & CallFlags_New));
  635. if (args.Info.Count == 0)
  636. {
  637. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toLocaleString"));
  638. }
  639. return JavascriptNumber::ToLocaleStringIntl(args, callInfo, scriptContext);
  640. }
  641. JavascriptString* JavascriptNumber::ToLocaleStringIntl(Var* values, CallInfo callInfo, ScriptContext* scriptContext)
  642. {
  643. Assert(values);
  644. ArgumentReader args(&callInfo, values);
  645. return JavascriptNumber::ToLocaleStringIntl(args, callInfo, scriptContext);
  646. }
  647. JavascriptString* JavascriptNumber::ToLocaleStringIntl(ArgumentReader& args, CallInfo callInfo, ScriptContext* scriptContext)
  648. {
  649. Assert(scriptContext);
  650. #ifdef ENABLE_INTL_OBJECT
  651. if(CONFIG_FLAG(IntlBuiltIns) && scriptContext->IsIntlEnabled()){
  652. EngineInterfaceObject* nativeEngineInterfaceObj = scriptContext->GetLibrary()->GetEngineInterfaceObject();
  653. if (nativeEngineInterfaceObj)
  654. {
  655. IntlEngineInterfaceExtensionObject* intlExtensionObject = static_cast<IntlEngineInterfaceExtensionObject*>(nativeEngineInterfaceObj->GetEngineExtension(EngineInterfaceExtensionKind_Intl));
  656. JavascriptFunction* func = intlExtensionObject->GetNumberToLocaleString();
  657. if (func)
  658. {
  659. BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
  660. {
  661. return VarTo<JavascriptString>(func->CallFunction(args));
  662. }
  663. END_SAFE_REENTRANT_CALL
  664. }
  665. // Initialize Number.prototype.toLocaleString
  666. scriptContext->GetLibrary()->InitializeIntlForNumberPrototype();
  667. func = intlExtensionObject->GetNumberToLocaleString();
  668. if (func)
  669. {
  670. BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
  671. {
  672. return VarTo<JavascriptString>(func->CallFunction(args));
  673. }
  674. END_SAFE_REENTRANT_CALL
  675. }
  676. }
  677. }
  678. #endif
  679. double value;
  680. if (!GetThisValue(args[0], &value))
  681. {
  682. if (JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch)
  683. {
  684. Var result;
  685. if (VarTo<RecyclableObject>(args[0])->InvokeBuiltInOperationRemotely(EntryToLocaleString, args, &result))
  686. {
  687. return VarTo<JavascriptString>(result);
  688. }
  689. }
  690. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toLocaleString"));
  691. }
  692. return JavascriptNumber::ToLocaleString(value, scriptContext);
  693. }
  694. Var JavascriptNumber::EntryToString(RecyclableObject* function, CallInfo callInfo, ...)
  695. {
  696. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  697. ARGUMENTS(args, callInfo);
  698. ScriptContext* scriptContext = function->GetScriptContext();
  699. Assert(!(callInfo.Flags & CallFlags_New));
  700. if (args.Info.Count == 0)
  701. {
  702. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toString"));
  703. }
  704. // Optimize base 10 of TaggedInt numbers
  705. if (TaggedInt::Is(args[0]) && (args.Info.Count == 1 || (TaggedInt::Is(args[1]) && TaggedInt::ToInt32(args[1]) == 10)))
  706. {
  707. return scriptContext->GetIntegerString(args[0]);
  708. }
  709. double value;
  710. if (!GetThisValue(args[0], &value))
  711. {
  712. if (JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch)
  713. {
  714. Var result;
  715. if (VarTo<RecyclableObject>(args[0])->InvokeBuiltInOperationRemotely(EntryToString, args, &result))
  716. {
  717. return result;
  718. }
  719. }
  720. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toString"));
  721. }
  722. int radix = 10;
  723. if(args.Info.Count > 1)
  724. {
  725. //use the first arg as the radix, ignore the rest.
  726. Var aRadix = args[1];
  727. // shortcut for tagged int's
  728. if(TaggedInt::Is(aRadix))
  729. {
  730. radix = TaggedInt::ToInt32(aRadix);
  731. }
  732. else if(JavascriptOperators::GetTypeId(aRadix) != TypeIds_Undefined)
  733. {
  734. radix = (int)JavascriptConversion::ToInteger(aRadix,scriptContext);
  735. }
  736. }
  737. if(10 == radix)
  738. {
  739. return ToStringRadix10(value, scriptContext);
  740. }
  741. if( radix < 2 || radix >36 )
  742. {
  743. JavascriptError::ThrowRangeError(scriptContext, JSERR_FunctionArgument_Invalid, _u("Number.prototype.toString"));
  744. }
  745. return ToStringRadixHelper(value, radix, scriptContext);
  746. }
  747. Var JavascriptNumber::EntryValueOf(RecyclableObject* function, CallInfo callInfo, ...)
  748. {
  749. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  750. ARGUMENTS(args, callInfo);
  751. ScriptContext* scriptContext = function->GetScriptContext();
  752. Var value = args[0];
  753. Assert(!(callInfo.Flags & CallFlags_New));
  754. if (args.Info.Count == 0)
  755. {
  756. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.valueOf"));
  757. }
  758. //avoid creation of a new Number
  759. if (TaggedInt::Is(value) || JavascriptNumber::Is_NoTaggedIntCheck(value))
  760. {
  761. return value;
  762. }
  763. else if (VarIs<JavascriptNumberObject>(value))
  764. {
  765. JavascriptNumberObject* obj = VarTo<JavascriptNumberObject>(value);
  766. return CrossSite::MarshalVar(scriptContext, obj->Unwrap(), obj->GetScriptContext());
  767. }
  768. else if (Js::JavascriptOperators::GetTypeId(value) == TypeIds_Int64Number)
  769. {
  770. return value;
  771. }
  772. else if (Js::JavascriptOperators::GetTypeId(value) == TypeIds_UInt64Number)
  773. {
  774. return value;
  775. }
  776. else
  777. {
  778. if (JavascriptOperators::GetTypeId(value) == TypeIds_HostDispatch)
  779. {
  780. Var result;
  781. if (VarTo<RecyclableObject>(value)->InvokeBuiltInOperationRemotely(EntryValueOf, args, &result))
  782. {
  783. return result;
  784. }
  785. }
  786. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.valueOf"));
  787. }
  788. }
  789. // The largest string representing a number is the base 2 representation of -Math.pow(2,-1073), at 1076 characters.
  790. static const int bufSize = 1280;
  791. JavascriptString* JavascriptNumber::ToString(double value, ScriptContext* scriptContext)
  792. {
  793. char16 szBuffer[bufSize];
  794. int cchWritten = swprintf_s(szBuffer, _countof(szBuffer), _u("%g"), value);
  795. return JavascriptString::NewCopyBuffer(szBuffer, cchWritten, scriptContext);
  796. }
  797. JavascriptString* JavascriptNumber::ToStringNanOrInfiniteOrZero(double value, ScriptContext* scriptContext)
  798. {
  799. JavascriptString* nanF;
  800. if (nullptr != (nanF = ToStringNanOrInfinite(value, scriptContext)))
  801. {
  802. return nanF;
  803. }
  804. if (IsZero(value))
  805. {
  806. return scriptContext->GetLibrary()->GetCharStringCache().GetStringForCharA('0');
  807. }
  808. return nullptr;
  809. }
  810. JavascriptString* JavascriptNumber::ToStringRadix10(double value, ScriptContext* scriptContext)
  811. {
  812. JavascriptString* string = ToStringNanOrInfiniteOrZero(value, scriptContext);
  813. if (string != nullptr)
  814. {
  815. return string;
  816. }
  817. string = scriptContext->GetLastNumberToStringRadix10(value);
  818. if (string == nullptr)
  819. {
  820. char16 szBuffer[bufSize];
  821. if(!Js::NumberUtilities::FNonZeroFiniteDblToStr(value, szBuffer, bufSize))
  822. {
  823. Js::JavascriptError::ThrowOutOfMemoryError(scriptContext);
  824. }
  825. string = JavascriptString::NewCopySz(szBuffer, scriptContext);
  826. scriptContext->SetLastNumberToStringRadix10(value, string);
  827. }
  828. return string;
  829. }
  830. JavascriptString* JavascriptNumber::ToStringRadixHelper(double value, int radix, ScriptContext* scriptContext)
  831. {
  832. Assert(radix != 10);
  833. Assert(radix >= 2 && radix <= 36);
  834. JavascriptString* string = ToStringNanOrInfiniteOrZero(value, scriptContext);
  835. if (string != nullptr)
  836. {
  837. return string;
  838. }
  839. char16 szBuffer[bufSize];
  840. if (!Js::NumberUtilities::FNonZeroFiniteDblToStr(value, radix, szBuffer, _countof(szBuffer)))
  841. {
  842. Js::JavascriptError::ThrowOutOfMemoryError(scriptContext);
  843. }
  844. return JavascriptString::NewCopySz(szBuffer, scriptContext);
  845. }
  846. BOOL JavascriptNumber::GetThisValue(Var aValue, double* pDouble)
  847. {
  848. TypeId typeId = JavascriptOperators::GetTypeId(aValue);
  849. if (typeId <= TypeIds_UndefinedOrNull)
  850. {
  851. return FALSE;
  852. }
  853. if (typeId == TypeIds_Integer)
  854. {
  855. *pDouble = TaggedInt::ToDouble(aValue);
  856. return TRUE;
  857. }
  858. else if (typeId == TypeIds_Int64Number)
  859. {
  860. *pDouble = (double)VarTo<JavascriptInt64Number>(aValue)->GetValue();
  861. return TRUE;
  862. }
  863. else if (typeId == TypeIds_UInt64Number)
  864. {
  865. *pDouble = (double)VarTo<JavascriptUInt64Number>(aValue)->GetValue();
  866. return TRUE;
  867. }
  868. else if (JavascriptNumber::Is_NoTaggedIntCheck(aValue))
  869. {
  870. *pDouble = JavascriptNumber::GetValue(aValue);
  871. return TRUE;
  872. }
  873. else if (typeId == TypeIds_NumberObject)
  874. {
  875. JavascriptNumberObject* obj = VarTo<JavascriptNumberObject>(aValue);
  876. *pDouble = obj->GetValue();
  877. return TRUE;
  878. }
  879. else
  880. {
  881. return FALSE;
  882. }
  883. }
  884. JavascriptString* JavascriptNumber::ToLocaleStringNanOrInfinite(double value, ScriptContext* scriptContext)
  885. {
  886. if (!NumberUtilities::IsFinite(value))
  887. {
  888. if (IsNan(value))
  889. {
  890. return ToStringNan(scriptContext);
  891. }
  892. BSTR bstr = nullptr;
  893. if (IsPosInf(value))
  894. {
  895. bstr = BstrGetResourceString(IDS_INFINITY);
  896. }
  897. else
  898. {
  899. AssertMsg(IsNegInf(value), "bad handling of infinite number");
  900. bstr = BstrGetResourceString(IDS_MINUSINFINITY);
  901. }
  902. if (bstr == nullptr)
  903. {
  904. Js::JavascriptError::ThrowTypeError(scriptContext, VBSERR_InternalError);
  905. }
  906. JavascriptString* str = JavascriptString::NewCopyBuffer(bstr, SysStringLen(bstr), scriptContext);
  907. SysFreeString(bstr);
  908. return str;
  909. }
  910. return nullptr;
  911. }
  912. JavascriptString* JavascriptNumber::ToLocaleString(double value, ScriptContext* scriptContext)
  913. {
  914. WCHAR szRes[bufSize];
  915. WCHAR * pszRes = NULL;
  916. WCHAR * pszToBeFreed = NULL;
  917. size_t count;
  918. if (!Js::NumberUtilities::IsFinite(value))
  919. {
  920. //
  921. // +- Infinity : use the localized string
  922. // NaN would be returned as NaN
  923. //
  924. return ToLocaleStringNanOrInfinite(value, scriptContext);
  925. }
  926. JavascriptString *result = nullptr;
  927. JavascriptString *dblStr = VarTo<JavascriptString>(FormatDoubleToString(value, NumberUtilities::FormatFixed, -1, scriptContext));
  928. const char16* szValue = dblStr->GetSz();
  929. const size_t szLength = dblStr->GetLength();
  930. pszRes = szRes;
  931. count = Numbers::Utility::NumberToDefaultLocaleString(szValue, szLength, pszRes, bufSize);
  932. if( count == 0 )
  933. {
  934. return dblStr;
  935. }
  936. else
  937. {
  938. if( count > bufSize )
  939. {
  940. pszRes = pszToBeFreed = HeapNewArray(char16, count);
  941. count = Numbers::Utility::NumberToDefaultLocaleString(szValue, szLength, pszRes, count);
  942. if ( count == 0 )
  943. {
  944. AssertMsg(false, "GetNumberFormatEx failed");
  945. JavascriptError::ThrowError(scriptContext, VBSERR_InternalError);
  946. }
  947. }
  948. if ( count != 0 )
  949. {
  950. result = JavascriptString::NewCopySz(pszRes, scriptContext);
  951. }
  952. }
  953. if ( pszToBeFreed )
  954. {
  955. HeapDeleteArray(count, pszToBeFreed);
  956. }
  957. return result;
  958. }
  959. Var JavascriptNumber::CloneToScriptContext(Var aValue, ScriptContext* requestContext)
  960. {
  961. return JavascriptNumber::New(JavascriptNumber::GetValue(aValue), requestContext);
  962. }
  963. #if !FLOATVAR
  964. Var JavascriptNumber::BoxStackNumber(Var instance, ScriptContext* scriptContext)
  965. {
  966. if (ThreadContext::IsOnStack(instance) && JavascriptNumber::Is(instance))
  967. {
  968. return BoxStackInstance(JavascriptNumber::FromVar(instance), scriptContext);
  969. }
  970. else
  971. {
  972. return instance;
  973. }
  974. }
  975. Var JavascriptNumber::BoxStackInstance(Var instance, ScriptContext* scriptContext)
  976. {
  977. Assert(ThreadContext::IsOnStack(instance));
  978. double value = JavascriptNumber::FromVar(instance)->GetValue();
  979. return JavascriptNumber::New(value, scriptContext);
  980. }
  981. JavascriptNumber * JavascriptNumber::NewUninitialized(Recycler * recycler)
  982. {
  983. return RecyclerNew(recycler, JavascriptNumber, VirtualTableInfoCtorValue);
  984. }
  985. #endif
  986. }