JavascriptMath.cpp 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402
  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. using namespace Js;
  6. Var JavascriptMath::Negate_Full(Var aRight, ScriptContext* scriptContext)
  7. {
  8. JIT_HELPER_REENTRANT_HEADER(Op_Negate_Full);
  9. // Special case for zero. Must return -0
  10. if( aRight == TaggedInt::ToVarUnchecked(0) )
  11. {
  12. return scriptContext->GetLibrary()->GetNegativeZero();
  13. }
  14. if (JavascriptOperators::GetTypeId(aRight) == TypeIds_BigInt)
  15. {
  16. return JavascriptBigInt::Negate(aRight);
  17. }
  18. double value = Negate_Helper(aRight, scriptContext);
  19. return JavascriptNumber::ToVarIntCheck(value, scriptContext);
  20. JIT_HELPER_END(Op_Negate_Full);
  21. }
  22. JIT_HELPER_TEMPLATE(Op_Negate_Full, Op_Negate)
  23. Var JavascriptMath::Negate_InPlace(Var aRight, ScriptContext* scriptContext, JavascriptNumber* result)
  24. {
  25. JIT_HELPER_REENTRANT_HEADER(Op_NegateInPlace);
  26. // Special case for zero. Must return -0
  27. if( aRight == TaggedInt::ToVarUnchecked(0) )
  28. {
  29. return scriptContext->GetLibrary()->GetNegativeZero();
  30. }
  31. if (JavascriptOperators::GetTypeId(aRight) == TypeIds_BigInt)
  32. {
  33. return JavascriptBigInt::Negate(aRight);
  34. }
  35. double value = Negate_Helper(aRight, scriptContext);
  36. return JavascriptNumber::InPlaceNew(value, scriptContext, result);
  37. JIT_HELPER_END(Op_NegateInPlace);
  38. }
  39. Var JavascriptMath::Not_Full(Var aRight, ScriptContext* scriptContext)
  40. {
  41. JIT_HELPER_REENTRANT_HEADER(Op_Not_Full);
  42. #if _M_IX86
  43. AssertMsg(!TaggedInt::Is(aRight), "Should be detected");
  44. #endif
  45. if (JavascriptOperators::GetTypeId(aRight) == TypeIds_BigInt)
  46. {
  47. return JavascriptBigInt::Not(aRight);
  48. }
  49. int nValue = JavascriptConversion::ToInt32(aRight, scriptContext);
  50. return JavascriptNumber::ToVar(~nValue, scriptContext);
  51. JIT_HELPER_END(Op_Not_Full);
  52. }
  53. JIT_HELPER_TEMPLATE(Op_Not_Full, Op_Not)
  54. Var JavascriptMath::Not_InPlace(Var aRight, ScriptContext* scriptContext, JavascriptNumber* result)
  55. {
  56. JIT_HELPER_REENTRANT_HEADER(Op_NotInPlace);
  57. AssertMsg(!TaggedInt::Is(aRight), "Should be detected");
  58. if (JavascriptOperators::GetTypeId(aRight) == TypeIds_BigInt)
  59. {
  60. return JavascriptBigInt::Not(aRight);
  61. }
  62. int nValue = JavascriptConversion::ToInt32(aRight, scriptContext);
  63. return JavascriptNumber::ToVarInPlace(~nValue, scriptContext, result);
  64. JIT_HELPER_END(Op_NotInPlace);
  65. }
  66. Var JavascriptMath::Increment_InPlace(Var aRight, ScriptContext* scriptContext, JavascriptNumber* result)
  67. {
  68. JIT_HELPER_REENTRANT_HEADER(Op_IncrementInPlace);
  69. if (TaggedInt::Is(aRight))
  70. {
  71. return TaggedInt::Increment(aRight, scriptContext);
  72. }
  73. if (VarIs<JavascriptBigInt>(aRight))
  74. {
  75. return JavascriptBigInt::Increment(aRight);
  76. }
  77. double inc = Increment_Helper(aRight, scriptContext);
  78. return JavascriptNumber::InPlaceNew(inc, scriptContext, result);
  79. JIT_HELPER_END(Op_IncrementInPlace);
  80. }
  81. Var JavascriptMath::Increment_Full(Var aRight, ScriptContext* scriptContext)
  82. {
  83. JIT_HELPER_REENTRANT_HEADER(Op_Increment_Full);
  84. if (TaggedInt::Is(aRight))
  85. {
  86. return TaggedInt::Increment(aRight, scriptContext);
  87. }
  88. if (VarIs<JavascriptBigInt>(aRight))
  89. {
  90. return JavascriptBigInt::Increment(aRight);
  91. }
  92. double inc = Increment_Helper(aRight, scriptContext);
  93. return JavascriptNumber::ToVarIntCheck(inc, scriptContext);
  94. JIT_HELPER_END(Op_Increment_Full);
  95. }
  96. JIT_HELPER_TEMPLATE(Op_Increment_Full, Op_Increment)
  97. Var JavascriptMath::Decrement_InPlace(Var aRight, ScriptContext* scriptContext, JavascriptNumber* result)
  98. {
  99. JIT_HELPER_REENTRANT_HEADER(Op_DecrementInPlace);
  100. if (TaggedInt::Is(aRight))
  101. {
  102. return TaggedInt::Decrement(aRight, scriptContext);
  103. }
  104. if (VarIs<JavascriptBigInt>(aRight))
  105. {
  106. return JavascriptBigInt::Decrement(aRight);
  107. }
  108. double dec = Decrement_Helper(aRight,scriptContext);
  109. return JavascriptNumber::InPlaceNew(dec, scriptContext, result);
  110. JIT_HELPER_END(Op_DecrementInPlace);
  111. }
  112. Var JavascriptMath::Decrement_Full(Var aRight, ScriptContext* scriptContext)
  113. {
  114. JIT_HELPER_REENTRANT_HEADER(Op_Decrement_Full);
  115. if (TaggedInt::Is(aRight))
  116. {
  117. return TaggedInt::Decrement(aRight, scriptContext);
  118. }
  119. if (VarIs<JavascriptBigInt>(aRight))
  120. {
  121. return JavascriptBigInt::Decrement(aRight);
  122. }
  123. double dec = Decrement_Helper(aRight,scriptContext);
  124. return JavascriptNumber::ToVarIntCheck(dec, scriptContext);
  125. JIT_HELPER_END(Op_Decrement_Full);
  126. }
  127. JIT_HELPER_TEMPLATE(Op_Decrement_Full, Op_Decrement)
  128. Var JavascriptMath::Increment_Numeric(Var aRight, ScriptContext* scriptContext)
  129. {
  130. if (VarIs<JavascriptBigInt>(aRight))
  131. {
  132. return JavascriptBigInt::Increment(aRight);
  133. }
  134. return JavascriptMath::Add(aRight, TaggedInt::ToVarUnchecked(1), scriptContext);
  135. }
  136. Var JavascriptMath::Decrement_Numeric(Var aRight, ScriptContext* scriptContext)
  137. {
  138. if (VarIs<JavascriptBigInt>(aRight))
  139. {
  140. return JavascriptBigInt::Decrement(aRight);
  141. }
  142. return JavascriptMath::Subtract(aRight, TaggedInt::ToVarUnchecked(1), scriptContext);
  143. }
  144. Var JavascriptMath::And_Full(Var aLeft, Var aRight, ScriptContext* scriptContext)
  145. {
  146. JIT_HELPER_REENTRANT_HEADER(Op_And_Full);
  147. int32 value = And_Helper(aLeft, aRight, scriptContext);
  148. return JavascriptNumber::ToVar(value, scriptContext);
  149. JIT_HELPER_END(Op_And_Full);
  150. }
  151. JIT_HELPER_TEMPLATE(Op_And_Full, Op_And)
  152. Var JavascriptMath::And_InPlace(Var aLeft, Var aRight, ScriptContext* scriptContext, JavascriptNumber* result)
  153. {
  154. JIT_HELPER_REENTRANT_HEADER(Op_AndInPlace);
  155. int32 value = And_Helper(aLeft, aRight, scriptContext);
  156. return JavascriptNumber::ToVarInPlace(value, scriptContext, result);
  157. JIT_HELPER_END(Op_AndInPlace);
  158. }
  159. Var JavascriptMath::Or_Full(Var aLeft, Var aRight, ScriptContext* scriptContext)
  160. {
  161. JIT_HELPER_REENTRANT_HEADER(Op_Or_Full);
  162. int32 value = Or_Helper(aLeft, aRight, scriptContext);
  163. return JavascriptNumber::ToVar(value, scriptContext);
  164. JIT_HELPER_END(Op_Or_Full);
  165. }
  166. JIT_HELPER_TEMPLATE(Op_Or_Full, Op_Or)
  167. Var JavascriptMath::Or_InPlace(Var aLeft, Var aRight, ScriptContext* scriptContext, JavascriptNumber* result)
  168. {
  169. JIT_HELPER_REENTRANT_HEADER(Op_OrInPlace);
  170. int32 value = Or_Helper(aLeft, aRight, scriptContext);
  171. return JavascriptNumber::ToVarInPlace(value, scriptContext, result);
  172. JIT_HELPER_END(Op_OrInPlace);
  173. }
  174. Var JavascriptMath::Xor_Full(Var aLeft, Var aRight, ScriptContext* scriptContext)
  175. {
  176. JIT_HELPER_REENTRANT_HEADER(Op_Xor_Full);
  177. int32 nLeft = TaggedInt::Is(aLeft) ? TaggedInt::ToInt32(aLeft) : JavascriptConversion::ToInt32(aLeft, scriptContext);
  178. int32 nRight = TaggedInt::Is(aRight) ? TaggedInt::ToInt32(aRight) : JavascriptConversion::ToInt32(aRight, scriptContext);
  179. return JavascriptNumber::ToVar(nLeft ^ nRight,scriptContext);
  180. JIT_HELPER_END(Op_Xor_Full);
  181. }
  182. JIT_HELPER_TEMPLATE(Op_Xor_Full, Op_Xor)
  183. Var JavascriptMath::Xor_InPlace(Var aLeft, Var aRight, ScriptContext* scriptContext, JavascriptNumber* result)
  184. {
  185. JIT_HELPER_REENTRANT_HEADER(Op_XorInPlace);
  186. int32 nLeft = TaggedInt::Is(aLeft) ? TaggedInt::ToInt32(aLeft) : JavascriptConversion::ToInt32(aLeft, scriptContext);
  187. int32 nRight = TaggedInt::Is(aRight) ? TaggedInt::ToInt32(aRight) : JavascriptConversion::ToInt32(aRight, scriptContext);
  188. return JavascriptNumber::ToVarInPlace(nLeft ^ nRight, scriptContext, result);
  189. JIT_HELPER_END(Op_XorInPlace);
  190. }
  191. Var JavascriptMath::ShiftLeft_Full(Var aLeft, Var aRight, ScriptContext* scriptContext)
  192. {
  193. JIT_HELPER_REENTRANT_HEADER(Op_ShiftLeft_Full);
  194. int32 nValue = JavascriptConversion::ToInt32(aLeft, scriptContext);
  195. uint32 nShift = JavascriptConversion::ToUInt32(aRight, scriptContext);
  196. int32 nResult = nValue << (nShift & 0x1F);
  197. return JavascriptNumber::ToVar(nResult,scriptContext);
  198. JIT_HELPER_END(Op_ShiftLeft_Full);
  199. }
  200. JIT_HELPER_TEMPLATE(Op_ShiftLeft_Full, Op_ShiftLeft)
  201. Var JavascriptMath::ShiftRight_Full(Var aLeft, Var aRight, ScriptContext* scriptContext)
  202. {
  203. JIT_HELPER_REENTRANT_HEADER(Op_ShiftRight_Full);
  204. int32 nValue = JavascriptConversion::ToInt32(aLeft, scriptContext);
  205. uint32 nShift = JavascriptConversion::ToUInt32(aRight, scriptContext);
  206. int32 nResult = nValue >> (nShift & 0x1F);
  207. return JavascriptNumber::ToVar(nResult,scriptContext);
  208. JIT_HELPER_END(Op_ShiftRight_Full);
  209. }
  210. Var JavascriptMath::ShiftRightU_Full(Var aLeft, Var aRight, ScriptContext* scriptContext)
  211. {
  212. JIT_HELPER_REENTRANT_HEADER(Op_ShiftRightU_Full);
  213. uint32 nValue = JavascriptConversion::ToUInt32(aLeft, scriptContext);
  214. uint32 nShift = JavascriptConversion::ToUInt32(aRight, scriptContext);
  215. uint32 nResult = nValue >> (nShift & 0x1F);
  216. return JavascriptNumber::ToVar(nResult,scriptContext);
  217. JIT_HELPER_END(Op_ShiftRightU_Full);
  218. }
  219. #if FLOATVAR
  220. Var JavascriptMath::Add_Full(Var aLeft, Var aRight, ScriptContext* scriptContext)
  221. {
  222. JIT_HELPER_REENTRANT_HEADER(Op_Add_Full);
  223. Assert(aLeft != nullptr);
  224. Assert(aRight != nullptr);
  225. Assert(scriptContext != nullptr);
  226. Js::TypeId typeLeft = JavascriptOperators::GetTypeId(aLeft);
  227. Js::TypeId typeRight = JavascriptOperators::GetTypeId(aRight);
  228. if (typeRight == typeLeft)
  229. {
  230. // If both sides are numbers/string, then we can do the addition directly
  231. if(typeLeft == TypeIds_Number)
  232. {
  233. double sum = JavascriptNumber::GetValue(aLeft) + JavascriptNumber::GetValue(aRight);
  234. return JavascriptNumber::ToVarNoCheck(sum, scriptContext);
  235. }
  236. else if (typeLeft == TypeIds_Integer)
  237. {
  238. __int64 sum = TaggedInt::ToInt64(aLeft) + TaggedInt::ToInt64(aRight);
  239. return JavascriptNumber::ToVar(sum, scriptContext);
  240. }
  241. else if (typeLeft == TypeIds_String)
  242. {
  243. return JavascriptString::Concat(UnsafeVarTo<JavascriptString>(aLeft), UnsafeVarTo<JavascriptString>(aRight));
  244. }
  245. }
  246. else if(typeLeft == TypeIds_Number && typeRight == TypeIds_Integer)
  247. {
  248. double sum = JavascriptNumber::GetValue(aLeft) + TaggedInt::ToDouble(aRight);
  249. return JavascriptNumber::ToVarNoCheck(sum, scriptContext);
  250. }
  251. else if(typeLeft == TypeIds_Integer && typeRight == TypeIds_Number)
  252. {
  253. double sum = TaggedInt::ToDouble(aLeft) + JavascriptNumber::GetValue(aRight);
  254. return JavascriptNumber::ToVarNoCheck(sum, scriptContext);
  255. }
  256. return Add_FullHelper_Wrapper(aLeft, aRight, scriptContext, nullptr, false);
  257. JIT_HELPER_END(Op_Add_Full);
  258. }
  259. #else
  260. Var JavascriptMath::Add_Full(Var aLeft, Var aRight, ScriptContext* scriptContext)
  261. {
  262. JIT_HELPER_REENTRANT_HEADER(Op_Add_Full);
  263. Assert(aLeft != nullptr);
  264. Assert(aRight != nullptr);
  265. Assert(scriptContext != nullptr);
  266. Js::TypeId typeLeft = JavascriptOperators::GetTypeId(aLeft);
  267. Js::TypeId typeRight = JavascriptOperators::GetTypeId(aRight);
  268. // Handle combinations of TaggedInt and Number or String pairs directly,
  269. // otherwise call the helper.
  270. switch( typeLeft )
  271. {
  272. case TypeIds_Integer:
  273. {
  274. switch( typeRight )
  275. {
  276. case TypeIds_Integer:
  277. {
  278. // Compute the sum using integer addition, then convert to double.
  279. // That way there's only one int->float conversion.
  280. #if INT32VAR
  281. int64 sum = TaggedInt::ToInt64(aLeft) + TaggedInt::ToInt64(aRight);
  282. #else
  283. int32 sum = TaggedInt::ToInt32(aLeft) + TaggedInt::ToInt32(aRight);
  284. #endif
  285. return JavascriptNumber::ToVar(sum, scriptContext );
  286. }
  287. case TypeIds_Number:
  288. {
  289. double sum = TaggedInt::ToDouble(aLeft) + JavascriptNumber::GetValue(aRight);
  290. return JavascriptNumber::NewInlined( sum, scriptContext );
  291. }
  292. }
  293. break;
  294. }
  295. case TypeIds_Number:
  296. {
  297. switch( typeRight )
  298. {
  299. case TypeIds_Integer:
  300. {
  301. double sum = JavascriptNumber::GetValue(aLeft) + TaggedInt::ToDouble(aRight);
  302. return JavascriptNumber::NewInlined( sum, scriptContext );
  303. }
  304. case TypeIds_Number:
  305. {
  306. double sum = JavascriptNumber::GetValue(aLeft) + JavascriptNumber::GetValue(aRight);
  307. return JavascriptNumber::NewInlined( sum, scriptContext );
  308. }
  309. }
  310. break;
  311. }
  312. case TypeIds_String:
  313. {
  314. if( typeRight == TypeIds_String )
  315. {
  316. JavascriptString* leftString = UnsafeVarTo<JavascriptString>(aLeft);
  317. JavascriptString* rightString = UnsafeVarTo<JavascriptString>(aRight);
  318. return JavascriptString::Concat(leftString, rightString);
  319. }
  320. break;
  321. }
  322. }
  323. return Add_FullHelper_Wrapper(aLeft, aRight, scriptContext, nullptr, false);
  324. JIT_HELPER_END(Op_Add_Full);
  325. }
  326. #endif
  327. JIT_HELPER_TEMPLATE(Op_Add_Full, Op_Add)
  328. Var JavascriptMath::Add_InPlace(Var aLeft, Var aRight, ScriptContext* scriptContext, JavascriptNumber* result)
  329. {
  330. JIT_HELPER_REENTRANT_HEADER(Op_AddInPlace);
  331. Assert(aLeft != nullptr);
  332. Assert(aRight != nullptr);
  333. Assert(scriptContext != nullptr);
  334. Assert(result != nullptr);
  335. // If both sides are numbers, then we can do the addition directly, otherwise
  336. // we need to call the helper.
  337. if( TaggedInt::Is(aLeft) )
  338. {
  339. if( TaggedInt::Is(aRight) )
  340. {
  341. // Compute the sum using integer addition, then convert to double.
  342. // That way there's only one int->float conversion.
  343. #if INT32VAR
  344. int64 sum = TaggedInt::ToInt64(aLeft) + TaggedInt::ToInt64(aRight);
  345. #else
  346. int32 sum = TaggedInt::ToInt32(aLeft) + TaggedInt::ToInt32(aRight);
  347. #endif
  348. return JavascriptNumber::ToVarInPlace(sum, scriptContext, result);
  349. }
  350. else if( JavascriptNumber::Is_NoTaggedIntCheck(aRight) )
  351. {
  352. double sum = TaggedInt::ToDouble(aLeft) + JavascriptNumber::GetValue(aRight);
  353. return JavascriptNumber::InPlaceNew( sum, scriptContext, result );
  354. }
  355. }
  356. else if( TaggedInt::Is(aRight) )
  357. {
  358. if( JavascriptNumber::Is_NoTaggedIntCheck(aLeft) )
  359. {
  360. double sum = JavascriptNumber::GetValue(aLeft) + TaggedInt::ToDouble(aRight);
  361. return JavascriptNumber::InPlaceNew( sum, scriptContext, result );
  362. }
  363. }
  364. else if( JavascriptNumber::Is_NoTaggedIntCheck(aLeft) && JavascriptNumber::Is_NoTaggedIntCheck(aRight) )
  365. {
  366. double sum = JavascriptNumber::GetValue(aLeft) + JavascriptNumber::GetValue(aRight);
  367. return JavascriptNumber::InPlaceNew( sum, scriptContext, result );
  368. }
  369. return Add_FullHelper_Wrapper(aLeft, aRight, scriptContext, result, false);
  370. JIT_HELPER_END(Op_AddInPlace);
  371. }
  372. Var JavascriptMath::AddLeftDead(Var aLeft, Var aRight, ScriptContext* scriptContext, JavascriptNumber *result)
  373. {
  374. // Conservatively assume src1 is not dead until proven otherwise.
  375. bool leftIsDead = false;
  376. JIT_HELPER_REENTRANT_HEADER(Op_AddLeftDead);
  377. if (JavascriptOperators::GetTypeId(aLeft) == TypeIds_String)
  378. {
  379. leftIsDead = true;
  380. JavascriptString* leftString = UnsafeVarTo<JavascriptString>(aLeft);
  381. JavascriptString* rightString;
  382. TypeId rightType = JavascriptOperators::GetTypeId(aRight);
  383. switch(rightType)
  384. {
  385. case TypeIds_String:
  386. rightString = UnsafeVarTo<JavascriptString>(aRight);
  387. StringCommon:
  388. return leftString->ConcatDestructive(rightString);
  389. case TypeIds_Integer:
  390. rightString = scriptContext->GetIntegerString(aRight);
  391. goto StringCommon;
  392. case TypeIds_Number:
  393. rightString = JavascriptNumber::ToStringRadix10(JavascriptNumber::GetValue(aRight), scriptContext);
  394. goto StringCommon;
  395. }
  396. }
  397. if (TaggedInt::Is(aLeft))
  398. {
  399. if (TaggedInt::Is(aRight))
  400. {
  401. return TaggedInt::Add(aLeft, aRight, scriptContext);
  402. }
  403. else if (JavascriptNumber::Is_NoTaggedIntCheck(aRight))
  404. {
  405. return JavascriptNumber::ToVarMaybeInPlace(TaggedInt::ToDouble(aLeft) + JavascriptNumber::GetValue(aRight), scriptContext, result);
  406. }
  407. }
  408. else if (TaggedInt::Is(aRight))
  409. {
  410. if (JavascriptNumber::Is_NoTaggedIntCheck(aLeft))
  411. {
  412. return JavascriptNumber::ToVarMaybeInPlace(JavascriptNumber::GetValue(aLeft) + TaggedInt::ToDouble(aRight), scriptContext, result);
  413. }
  414. }
  415. else if (JavascriptNumber::Is_NoTaggedIntCheck(aLeft) && JavascriptNumber::Is_NoTaggedIntCheck(aRight))
  416. {
  417. return JavascriptNumber::ToVarMaybeInPlace(JavascriptNumber::GetValue(aLeft) + JavascriptNumber::GetValue(aRight), scriptContext, result);
  418. }
  419. return Add_FullHelper_Wrapper(aLeft, aRight, scriptContext, result, leftIsDead);
  420. JIT_HELPER_END(Op_AddLeftDead);
  421. }
  422. Var JavascriptMath::Add_FullHelper_Wrapper(Var aLeft, Var aRight, ScriptContext* scriptContext, JavascriptNumber* result, bool leftIsDead)
  423. {
  424. Var aLeftToPrim = JavascriptConversion::ToPrimitive<JavascriptHint::None>(aLeft, scriptContext);
  425. Var aRightToPrim = JavascriptConversion::ToPrimitive<JavascriptHint::None>(aRight, scriptContext);
  426. return Add_FullHelper(aLeftToPrim, aRightToPrim, scriptContext, result, leftIsDead);
  427. }
  428. Var JavascriptMath::Add_FullHelper(Var primLeft, Var primRight, ScriptContext* scriptContext, JavascriptNumber *result, bool leftIsDead)
  429. {
  430. TypeId typeLeft = JavascriptOperators::GetTypeId(primLeft);
  431. TypeId typeRight = JavascriptOperators::GetTypeId(primRight);
  432. if (typeLeft == TypeIds_BigInt || typeRight == TypeIds_BigInt)
  433. {
  434. if (typeRight != typeLeft)
  435. {
  436. JavascriptError::ThrowTypeError(scriptContext, VBSERR_TypeMismatch, _u("Add BigInt"));
  437. }
  438. return JavascriptBigInt::Add(primLeft, primRight);
  439. }
  440. // If either side is a string, then the result is also a string
  441. if (typeLeft == TypeIds_String)
  442. {
  443. JavascriptString* stringLeft = UnsafeVarTo<JavascriptString>(primLeft);
  444. JavascriptString* stringRight = nullptr;
  445. if (typeRight == TypeIds_String)
  446. {
  447. stringRight = UnsafeVarTo<JavascriptString>(primRight);
  448. }
  449. else
  450. {
  451. stringRight = JavascriptConversion::ToString(primRight, scriptContext);
  452. }
  453. if(leftIsDead)
  454. {
  455. return stringLeft->ConcatDestructive(stringRight);
  456. }
  457. return JavascriptString::Concat(stringLeft, stringRight);
  458. }
  459. if (typeRight == TypeIds_String)
  460. {
  461. JavascriptString* stringLeft = JavascriptConversion::ToString(primLeft, scriptContext);
  462. JavascriptString* stringRight = UnsafeVarTo<JavascriptString>(primRight);
  463. if(leftIsDead)
  464. {
  465. return stringLeft->ConcatDestructive(stringRight);
  466. }
  467. return JavascriptString::Concat(stringLeft, stringRight);
  468. }
  469. double sum = Add_Helper(primLeft, primRight, scriptContext);
  470. return JavascriptNumber::ToVarMaybeInPlace(sum, scriptContext, result);
  471. }
  472. Var JavascriptMath::MulAddLeft(Var mulLeft, Var mulRight, Var addLeft, ScriptContext* scriptContext, JavascriptNumber* result)
  473. {
  474. JIT_HELPER_REENTRANT_HEADER(Op_MulAddLeft);
  475. if(TaggedInt::Is(mulLeft))
  476. {
  477. if(TaggedInt::Is(mulRight))
  478. {
  479. // Compute the sum using integer addition, then convert to double.
  480. // That way there's only one int->float conversion.
  481. JavascriptNumber mulTemp(0, scriptContext->GetLibrary()->GetNumberTypeStatic());
  482. Var mulResult = TaggedInt::MultiplyInPlace(mulLeft, mulRight, scriptContext, &mulTemp);
  483. if (result)
  484. {
  485. return JavascriptMath::Add_InPlace(addLeft, mulResult, scriptContext, result);
  486. }
  487. else
  488. {
  489. return JavascriptMath::Add_Full(addLeft, mulResult, scriptContext);
  490. }
  491. }
  492. else if(JavascriptNumber::Is_NoTaggedIntCheck(mulRight))
  493. {
  494. double mulResult = TaggedInt::ToDouble(mulLeft) * JavascriptNumber::GetValue(mulRight);
  495. return JavascriptMath::Add_DoubleHelper(addLeft, mulResult, scriptContext, result);
  496. }
  497. }
  498. else if(TaggedInt::Is(mulRight))
  499. {
  500. if(JavascriptNumber::Is_NoTaggedIntCheck(mulLeft))
  501. {
  502. double mulResult = JavascriptNumber::GetValue(mulLeft) * TaggedInt::ToDouble(mulRight);
  503. return JavascriptMath::Add_DoubleHelper(addLeft, mulResult, scriptContext, result);
  504. }
  505. }
  506. else if(JavascriptNumber::Is_NoTaggedIntCheck(mulLeft) && JavascriptNumber::Is_NoTaggedIntCheck(mulRight))
  507. {
  508. double mulResult = JavascriptNumber::GetValue(mulLeft) * JavascriptNumber::GetValue(mulRight);
  509. return JavascriptMath::Add_DoubleHelper(addLeft, mulResult, scriptContext, result);
  510. }
  511. Var aMul;
  512. JavascriptNumber mulTemp(0, scriptContext->GetLibrary()->GetNumberTypeStatic());
  513. aMul = JavascriptMath::Multiply_InPlace(mulLeft, mulRight, scriptContext, &mulTemp);
  514. if (result)
  515. {
  516. return JavascriptMath::Add_InPlace(addLeft, aMul, scriptContext, result);
  517. }
  518. else
  519. {
  520. return JavascriptMath::Add_Full(addLeft, aMul, scriptContext);
  521. }
  522. JIT_HELPER_END(Op_MulAddLeft);
  523. }
  524. Var JavascriptMath::MulAddRight(Var mulLeft, Var mulRight, Var addRight, ScriptContext* scriptContext, JavascriptNumber* result)
  525. {
  526. JIT_HELPER_REENTRANT_HEADER(Op_MulAddRight);
  527. if (TaggedInt::Is(mulLeft))
  528. {
  529. if(TaggedInt::Is(mulRight))
  530. {
  531. // Compute the sum using integer addition, then convert to double.
  532. // That way there's only one int->float conversion.
  533. JavascriptNumber mulTemp(0, scriptContext->GetLibrary()->GetNumberTypeStatic());
  534. Var mulResult = TaggedInt::MultiplyInPlace(mulLeft, mulRight, scriptContext, &mulTemp);
  535. if (result)
  536. {
  537. return JavascriptMath::Add_InPlace(mulResult, addRight, scriptContext, result);
  538. }
  539. else
  540. {
  541. return JavascriptMath::Add_Full(mulResult, addRight, scriptContext);
  542. }
  543. }
  544. else if(JavascriptNumber::Is_NoTaggedIntCheck(mulRight))
  545. {
  546. double mulResult = TaggedInt::ToDouble(mulLeft) * JavascriptNumber::GetValue(mulRight);
  547. return JavascriptMath::Add_DoubleHelper(mulResult, addRight, scriptContext, result);
  548. }
  549. }
  550. else if(TaggedInt::Is(mulRight))
  551. {
  552. if(JavascriptNumber::Is_NoTaggedIntCheck(mulLeft))
  553. {
  554. double mulResult = JavascriptNumber::GetValue(mulLeft) * TaggedInt::ToDouble(mulRight);
  555. return JavascriptMath::Add_DoubleHelper(mulResult, addRight, scriptContext, result);
  556. }
  557. }
  558. else if(JavascriptNumber::Is_NoTaggedIntCheck(mulLeft) && JavascriptNumber::Is_NoTaggedIntCheck(mulRight))
  559. {
  560. double mulResult = JavascriptNumber::GetValue(mulLeft) * JavascriptNumber::GetValue(mulRight);
  561. return JavascriptMath::Add_DoubleHelper(mulResult, addRight, scriptContext, result);
  562. }
  563. Var aMul;
  564. JavascriptNumber mulTemp(0, scriptContext->GetLibrary()->GetNumberTypeStatic());
  565. aMul = JavascriptMath::Multiply_InPlace(mulLeft, mulRight, scriptContext, &mulTemp);
  566. if (result)
  567. {
  568. return JavascriptMath::Add_InPlace(aMul, addRight, scriptContext, result);
  569. }
  570. else
  571. {
  572. return JavascriptMath::Add_Full(aMul, addRight, scriptContext);
  573. }
  574. JIT_HELPER_END(Op_MulAddRight);
  575. }
  576. Var JavascriptMath::MulSubLeft(Var mulLeft, Var mulRight, Var subLeft, ScriptContext* scriptContext, JavascriptNumber* result)
  577. {
  578. JIT_HELPER_REENTRANT_HEADER(Op_MulSubLeft);
  579. if (TaggedInt::Is(mulLeft))
  580. {
  581. if(TaggedInt::Is(mulRight))
  582. {
  583. // Compute the sum using integer addition, then convert to double.
  584. // That way there's only one int->float conversion.
  585. JavascriptNumber mulTemp(0, scriptContext->GetLibrary()->GetNumberTypeStatic());
  586. Var mulResult = TaggedInt::MultiplyInPlace(mulLeft, mulRight, scriptContext, &mulTemp);
  587. if (result)
  588. {
  589. return JavascriptMath::Subtract_InPlace(subLeft, mulResult, scriptContext, result);
  590. }
  591. else
  592. {
  593. return JavascriptMath::Subtract_Full(subLeft, mulResult, scriptContext);
  594. }
  595. }
  596. else if(JavascriptNumber::Is_NoTaggedIntCheck(mulRight))
  597. {
  598. double mulResult = TaggedInt::ToDouble(mulLeft) * JavascriptNumber::GetValue(mulRight);
  599. return JavascriptMath::Subtract_DoubleHelper(subLeft, mulResult, scriptContext, result);
  600. }
  601. }
  602. else if(TaggedInt::Is(mulRight))
  603. {
  604. if(JavascriptNumber::Is_NoTaggedIntCheck(mulLeft))
  605. {
  606. double mulResult = JavascriptNumber::GetValue(mulLeft) * TaggedInt::ToDouble(mulRight);
  607. return JavascriptMath::Subtract_DoubleHelper(subLeft, mulResult, scriptContext, result);
  608. }
  609. }
  610. else if(JavascriptNumber::Is_NoTaggedIntCheck(mulLeft) && JavascriptNumber::Is_NoTaggedIntCheck(mulRight))
  611. {
  612. double mulResult = JavascriptNumber::GetValue(mulLeft) * JavascriptNumber::GetValue(mulRight);
  613. return JavascriptMath::Subtract_DoubleHelper(subLeft, mulResult, scriptContext, result);
  614. }
  615. Var aMul;
  616. JavascriptNumber mulTemp(0, scriptContext->GetLibrary()->GetNumberTypeStatic());
  617. aMul = JavascriptMath::Multiply_InPlace(mulLeft, mulRight, scriptContext, &mulTemp);
  618. if (result)
  619. {
  620. return JavascriptMath::Subtract_InPlace(subLeft, aMul, scriptContext, result);
  621. }
  622. else
  623. {
  624. return JavascriptMath::Subtract_Full(subLeft, aMul, scriptContext);
  625. }
  626. JIT_HELPER_END(Op_MulSubLeft);
  627. }
  628. Var JavascriptMath::MulSubRight(Var mulLeft, Var mulRight, Var subRight, ScriptContext* scriptContext, JavascriptNumber* result)
  629. {
  630. JIT_HELPER_REENTRANT_HEADER(Op_MulSubRight);
  631. if(TaggedInt::Is(mulLeft))
  632. {
  633. if(TaggedInt::Is(mulRight))
  634. {
  635. // Compute the sum using integer addition, then convert to double.
  636. // That way there's only one int->float conversion.
  637. JavascriptNumber mulTemp(0, scriptContext->GetLibrary()->GetNumberTypeStatic());
  638. Var mulResult = TaggedInt::MultiplyInPlace(mulLeft, mulRight, scriptContext, &mulTemp);
  639. if (result)
  640. {
  641. return JavascriptMath::Subtract_InPlace(mulResult, subRight, scriptContext, result);
  642. }
  643. else
  644. {
  645. return JavascriptMath::Subtract_Full(mulResult, subRight, scriptContext);
  646. }
  647. }
  648. else if(JavascriptNumber::Is_NoTaggedIntCheck(mulRight))
  649. {
  650. double mulResult = TaggedInt::ToDouble(mulLeft) * JavascriptNumber::GetValue(mulRight);
  651. return JavascriptMath::Subtract_DoubleHelper(mulResult, subRight, scriptContext, result);
  652. }
  653. }
  654. else if(TaggedInt::Is(mulRight))
  655. {
  656. if(JavascriptNumber::Is_NoTaggedIntCheck(mulLeft))
  657. {
  658. double mulResult = JavascriptNumber::GetValue(mulLeft) * TaggedInt::ToDouble(mulRight);
  659. return JavascriptMath::Subtract_DoubleHelper(mulResult, subRight, scriptContext, result);
  660. }
  661. }
  662. else if(JavascriptNumber::Is_NoTaggedIntCheck(mulLeft) && JavascriptNumber::Is_NoTaggedIntCheck(mulRight))
  663. {
  664. double mulResult = JavascriptNumber::GetValue(mulLeft) * JavascriptNumber::GetValue(mulRight);
  665. return JavascriptMath::Subtract_DoubleHelper(mulResult, subRight, scriptContext, result);
  666. }
  667. Var aMul;
  668. JavascriptNumber mulTemp(0, scriptContext->GetLibrary()->GetNumberTypeStatic());
  669. aMul = JavascriptMath::Multiply_InPlace(mulLeft, mulRight, scriptContext, &mulTemp);
  670. if (result)
  671. {
  672. return JavascriptMath::Subtract_InPlace(aMul, subRight, scriptContext, result);
  673. }
  674. else
  675. {
  676. return JavascriptMath::Subtract_Full(aMul, subRight, scriptContext);
  677. }
  678. JIT_HELPER_END(Op_MulSubRight);
  679. }
  680. Var inline JavascriptMath::Add_DoubleHelper(double dblLeft, Var addRight, ScriptContext* scriptContext, JavascriptNumber*result)
  681. {
  682. if (TaggedInt::Is(addRight))
  683. {
  684. double sum = dblLeft + TaggedInt::ToDouble(addRight);
  685. return JavascriptNumber::ToVarMaybeInPlace(sum, scriptContext, result);
  686. }
  687. else if (JavascriptNumber::Is_NoTaggedIntCheck(addRight))
  688. {
  689. double sum = dblLeft + JavascriptNumber::GetValue(addRight);
  690. return JavascriptNumber::ToVarMaybeInPlace(sum, scriptContext, result);
  691. }
  692. else
  693. {
  694. Var aLeft = JavascriptNumber::ToVarMaybeInPlace(dblLeft, scriptContext, result);
  695. return Add_Full(aLeft, addRight, scriptContext);
  696. }
  697. }
  698. Var inline JavascriptMath::Add_DoubleHelper(Var addLeft, double dblRight, ScriptContext* scriptContext, JavascriptNumber*result)
  699. {
  700. if (TaggedInt::Is(addLeft))
  701. {
  702. double sum = TaggedInt::ToDouble(addLeft) + dblRight;
  703. return JavascriptNumber::ToVarMaybeInPlace(sum, scriptContext, result);
  704. }
  705. else if (JavascriptNumber::Is_NoTaggedIntCheck(addLeft))
  706. {
  707. double sum = JavascriptNumber::GetValue(addLeft) + dblRight;
  708. return JavascriptNumber::ToVarMaybeInPlace(sum, scriptContext, result);
  709. }
  710. else
  711. {
  712. Var aRight = JavascriptNumber::ToVarMaybeInPlace(dblRight, scriptContext, result);
  713. return Add_Full(addLeft, aRight, scriptContext);
  714. }
  715. }
  716. Var inline JavascriptMath::Subtract_DoubleHelper(double dblLeft, Var subRight, ScriptContext* scriptContext, JavascriptNumber* result)
  717. {
  718. if (TaggedInt::Is(subRight))
  719. {
  720. double sum = dblLeft - TaggedInt::ToDouble(subRight);
  721. return JavascriptNumber::ToVarMaybeInPlace(sum, scriptContext, result);
  722. }
  723. else if (JavascriptNumber::Is_NoTaggedIntCheck(subRight))
  724. {
  725. double sum = dblLeft - JavascriptNumber::GetValue(subRight);
  726. return JavascriptNumber::ToVarMaybeInPlace(sum, scriptContext, result);
  727. }
  728. else
  729. {
  730. Var aLeft = JavascriptNumber::ToVarMaybeInPlace(dblLeft, scriptContext, result);
  731. return Subtract_Full(aLeft, subRight, scriptContext);
  732. }
  733. }
  734. Var inline JavascriptMath::Subtract_DoubleHelper(Var subLeft, double dblRight, ScriptContext* scriptContext, JavascriptNumber*result)
  735. {
  736. if (TaggedInt::Is(subLeft))
  737. {
  738. double sum = TaggedInt::ToDouble(subLeft) - dblRight;
  739. return JavascriptNumber::ToVarMaybeInPlace(sum, scriptContext, result);
  740. }
  741. else if (JavascriptNumber::Is_NoTaggedIntCheck(subLeft))
  742. {
  743. double sum = JavascriptNumber::GetValue(subLeft) - dblRight;
  744. return JavascriptNumber::ToVarMaybeInPlace(sum, scriptContext, result);
  745. }
  746. else
  747. {
  748. Var aRight = JavascriptNumber::ToVarMaybeInPlace(dblRight, scriptContext, result);
  749. return Subtract_Full(subLeft, aRight, scriptContext);
  750. }
  751. }
  752. Var JavascriptMath::Subtract_Full(Var aLeft, Var aRight, ScriptContext* scriptContext)
  753. {
  754. JIT_HELPER_REENTRANT_HEADER(Op_Subtract_Full);
  755. Var aLeftToPrim = JavascriptConversion::ToPrimitive<JavascriptHint::HintNumber>(aLeft, scriptContext);
  756. Var aRightToPrim = JavascriptConversion::ToPrimitive<JavascriptHint::HintNumber>(aRight, scriptContext);
  757. Js::TypeId typeLeft = JavascriptOperators::GetTypeId(aLeftToPrim);
  758. Js::TypeId typeRight = JavascriptOperators::GetTypeId(aRightToPrim);
  759. if (typeLeft == TypeIds_BigInt || typeRight == TypeIds_BigInt)
  760. {
  761. if (typeRight != typeLeft)
  762. {
  763. JavascriptError::ThrowTypeError(scriptContext, VBSERR_TypeMismatch, _u("Subtract BigInt"));
  764. }
  765. return JavascriptBigInt::Sub(aLeftToPrim, aRightToPrim);
  766. }
  767. double difference = Subtract_Helper(aLeftToPrim, aRightToPrim, scriptContext);
  768. return JavascriptNumber::ToVarIntCheck(difference, scriptContext);
  769. JIT_HELPER_END(Op_Subtract_Full);
  770. }
  771. JIT_HELPER_TEMPLATE(Op_Subtract_Full, Op_Subtract)
  772. Var JavascriptMath::Subtract_InPlace(Var aLeft, Var aRight, ScriptContext* scriptContext, JavascriptNumber* result)
  773. {
  774. JIT_HELPER_REENTRANT_HEADER(Op_SubtractInPlace);
  775. Var aLeftToPrim = JavascriptConversion::ToPrimitive<JavascriptHint::HintNumber>(aLeft, scriptContext);
  776. Var aRightToPrim = JavascriptConversion::ToPrimitive<JavascriptHint::HintNumber>(aRight, scriptContext);
  777. Js::TypeId typeLeft = JavascriptOperators::GetTypeId(aLeftToPrim);
  778. Js::TypeId typeRight = JavascriptOperators::GetTypeId(aRightToPrim);
  779. if (typeLeft == TypeIds_BigInt || typeRight == TypeIds_BigInt)
  780. {
  781. if (typeRight != typeLeft)
  782. {
  783. JavascriptError::ThrowTypeError(scriptContext, VBSERR_TypeMismatch, _u("Subtract BigInt"));
  784. }
  785. return JavascriptBigInt::Sub(aLeftToPrim, aRightToPrim);
  786. }
  787. double difference = Subtract_Helper(aLeftToPrim, aRightToPrim, scriptContext);
  788. return JavascriptNumber::InPlaceNew(difference, scriptContext, result);
  789. JIT_HELPER_END(Op_SubtractInPlace);
  790. }
  791. Var JavascriptMath::Divide_Full(Var aLeft,Var aRight, ScriptContext* scriptContext)
  792. {
  793. JIT_HELPER_REENTRANT_HEADER(Op_Divide_Full);
  794. // If both arguments are TaggedInt, then try to do integer division
  795. // This case is not handled by the lowerer.
  796. if (TaggedInt::IsPair(aLeft, aRight))
  797. {
  798. return TaggedInt::Divide(aLeft, aRight, scriptContext);
  799. }
  800. return JavascriptNumber::NewInlined( Divide_Helper(aLeft, aRight, scriptContext), scriptContext );
  801. JIT_HELPER_END(Op_Divide_Full);
  802. }
  803. JIT_HELPER_TEMPLATE(Op_Divide_Full, Op_Divide)
  804. Var JavascriptMath::Exponentiation_Full(Var aLeft, Var aRight, ScriptContext *scriptContext)
  805. {
  806. JIT_HELPER_REENTRANT_HEADER(Op_Exponentiation_Full);
  807. double x = JavascriptConversion::ToNumber(aLeft, scriptContext);
  808. double y = JavascriptConversion::ToNumber(aRight, scriptContext);
  809. return JavascriptNumber::ToVarIntCheck(Math::Pow(x, y), scriptContext);
  810. JIT_HELPER_END(Op_Exponentiation_Full);
  811. }
  812. JIT_HELPER_TEMPLATE(Op_Exponentiation_Full, Op_Exponentiation)
  813. Var JavascriptMath::Exponentiation_InPlace(Var aLeft, Var aRight, ScriptContext* scriptContext, JavascriptNumber* result)
  814. {
  815. JIT_HELPER_REENTRANT_HEADER(Op_ExponentiationInPlace);
  816. // The IEEE 754 floating point spec ensures that NaNs are preserved in all operations
  817. double dblLeft = JavascriptConversion::ToNumber(aLeft, scriptContext);
  818. double dblRight = JavascriptConversion::ToNumber(aRight, scriptContext);
  819. return JavascriptNumber::InPlaceNew(Math::Pow(dblLeft, dblRight), scriptContext, result);
  820. JIT_HELPER_END(Op_ExponentiationInPlace);
  821. }
  822. Var JavascriptMath::Multiply_Full(Var aLeft, Var aRight, ScriptContext* scriptContext)
  823. {
  824. JIT_HELPER_REENTRANT_HEADER(Op_Multiply_Full);
  825. Assert(aLeft != nullptr);
  826. Assert(aRight != nullptr);
  827. Assert(scriptContext != nullptr);
  828. Js::TypeId typeLeft = JavascriptOperators::GetTypeId(aLeft);
  829. Js::TypeId typeRight = JavascriptOperators::GetTypeId(aRight);
  830. if (typeLeft == TypeIds_BigInt || typeRight == TypeIds_BigInt)
  831. {
  832. if (typeRight != typeLeft)
  833. {
  834. JavascriptError::ThrowTypeError(scriptContext, VBSERR_TypeMismatch, _u("Multiply BigInt"));
  835. }
  836. return JavascriptBigInt::Mul(aLeft, aRight);
  837. }
  838. if(JavascriptNumber::Is(aLeft))
  839. {
  840. if(JavascriptNumber::Is(aRight))
  841. {
  842. double product = JavascriptNumber::GetValue(aLeft) * JavascriptNumber::GetValue(aRight);
  843. return JavascriptNumber::ToVarNoCheck(product, scriptContext);
  844. }
  845. else if(TaggedInt::Is(aRight))
  846. {
  847. double product = TaggedInt::ToDouble(aRight) * JavascriptNumber::GetValue(aLeft);
  848. return JavascriptNumber::ToVarNoCheck(product, scriptContext);
  849. }
  850. }
  851. else if(JavascriptNumber::Is(aRight))
  852. {
  853. if(TaggedInt::Is(aLeft))
  854. {
  855. double product = TaggedInt::ToDouble(aLeft) * JavascriptNumber::GetValue(aRight);
  856. return JavascriptNumber::ToVarNoCheck(product, scriptContext);
  857. }
  858. }
  859. else if(TaggedInt::IsPair(aLeft, aRight))
  860. {
  861. return TaggedInt::Multiply(aLeft, aRight, scriptContext);
  862. }
  863. double product = Multiply_Helper(aLeft, aRight, scriptContext);
  864. return JavascriptNumber::ToVarIntCheck(product, scriptContext);
  865. JIT_HELPER_END(Op_Multiply_Full);
  866. }
  867. JIT_HELPER_TEMPLATE(Op_Multiply_Full, Op_Multiply)
  868. Var JavascriptMath::Multiply_InPlace(Var aLeft, Var aRight, ScriptContext* scriptContext, JavascriptNumber* result)
  869. {
  870. JIT_HELPER_REENTRANT_HEADER(Op_MultiplyInPlace);
  871. Js::TypeId typeLeft = JavascriptOperators::GetTypeId(aLeft);
  872. Js::TypeId typeRight = JavascriptOperators::GetTypeId(aRight);
  873. if (typeLeft == TypeIds_BigInt || typeRight == TypeIds_BigInt)
  874. {
  875. if (typeRight != typeLeft)
  876. {
  877. JavascriptError::ThrowTypeError(scriptContext, VBSERR_TypeMismatch, _u("Multiply BigInt"));
  878. }
  879. return JavascriptBigInt::Mul(aLeft, aRight);
  880. }
  881. if(JavascriptNumber::Is(aLeft))
  882. {
  883. if(JavascriptNumber::Is(aRight))
  884. {
  885. return JavascriptNumber::ToVarInPlace(
  886. JavascriptNumber::GetValue(aLeft) * JavascriptNumber::GetValue(aRight), scriptContext, result);
  887. }
  888. else if (TaggedInt::Is(aRight))
  889. {
  890. return JavascriptNumber::ToVarInPlace(
  891. JavascriptNumber::GetValue(aLeft) * TaggedInt::ToDouble(aRight), scriptContext, result);
  892. }
  893. }
  894. else if(JavascriptNumber::Is(aRight))
  895. {
  896. if(TaggedInt::Is(aLeft))
  897. {
  898. return JavascriptNumber::ToVarInPlace(
  899. TaggedInt::ToDouble(aLeft) * JavascriptNumber::GetValue(aRight), scriptContext, result);
  900. }
  901. }
  902. else if(TaggedInt::IsPair(aLeft, aRight))
  903. {
  904. return TaggedInt::MultiplyInPlace(aLeft, aRight, scriptContext, result);
  905. }
  906. double product = Multiply_Helper(aLeft, aRight, scriptContext);
  907. return JavascriptNumber::InPlaceNew(product, scriptContext, result);
  908. JIT_HELPER_END(Op_MultiplyInPlace);
  909. }
  910. Var JavascriptMath::Divide_InPlace(Var aLeft, Var aRight, ScriptContext* scriptContext, JavascriptNumber* result)
  911. {
  912. JIT_HELPER_REENTRANT_HEADER(Op_DivideInPlace);
  913. // If both arguments are TaggedInt, then try to do integer division
  914. // This case is not handled by the lowerer.
  915. if (TaggedInt::IsPair(aLeft, aRight))
  916. {
  917. return TaggedInt::DivideInPlace(aLeft, aRight, scriptContext, result);
  918. }
  919. double quotient = Divide_Helper(aLeft, aRight, scriptContext);
  920. return JavascriptNumber::InPlaceNew(quotient, scriptContext, result);
  921. JIT_HELPER_END(Op_DivideInPlace);
  922. }
  923. Var JavascriptMath::Modulus_Full(Var aLeft, Var aRight, ScriptContext* scriptContext)
  924. {
  925. JIT_HELPER_REENTRANT_HEADER(Op_Modulus_Full);
  926. // If both arguments are TaggedInt, then try to do integer modulus.
  927. // This case is not handled by the lowerer.
  928. if (TaggedInt::IsPair(aLeft, aRight))
  929. {
  930. return TaggedInt::Modulus(aLeft, aRight, scriptContext);
  931. }
  932. double remainder = Modulus_Helper(aLeft, aRight, scriptContext);
  933. return JavascriptNumber::ToVarIntCheck(remainder, scriptContext);
  934. JIT_HELPER_END(Op_Modulus_Full);
  935. }
  936. JIT_HELPER_TEMPLATE(Op_Modulus_Full, Op_Modulus)
  937. Var JavascriptMath::Modulus_InPlace(Var aLeft, Var aRight, ScriptContext* scriptContext, JavascriptNumber* result)
  938. {
  939. JIT_HELPER_REENTRANT_HEADER(Op_ModulusInPlace);
  940. Assert(aLeft != nullptr);
  941. Assert(aRight != nullptr);
  942. Assert(scriptContext != nullptr);
  943. // If both arguments are TaggedInt, then try to do integer division
  944. // This case is not handled by the lowerer.
  945. if (TaggedInt::IsPair(aLeft, aRight))
  946. {
  947. return TaggedInt::Modulus(aLeft, aRight, scriptContext);
  948. }
  949. double remainder = Modulus_Helper(aLeft, aRight, scriptContext);
  950. return JavascriptNumber::InPlaceNew(remainder, scriptContext, result);
  951. JIT_HELPER_END(Op_ModulusInPlace);
  952. }
  953. Var JavascriptMath::FinishOddDivByPow2(int32 value, ScriptContext *scriptContext)
  954. {
  955. JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_FinishOddDivByPow2);
  956. return JavascriptNumber::New((double)(value + 0.5), scriptContext);
  957. JIT_HELPER_END(Op_FinishOddDivByPow2);
  958. }
  959. Var JavascriptMath::FinishOddDivByPow2_InPlace(int32 value, ScriptContext *scriptContext, JavascriptNumber* result)
  960. {
  961. JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_FinishOddDivByPow2InPlace);
  962. return JavascriptNumber::InPlaceNew((double)(value + 0.5), scriptContext, result);
  963. JIT_HELPER_END(Op_FinishOddDivByPow2InPlace);
  964. }
  965. Var JavascriptMath::MaxInAnArray(RecyclableObject * function, CallInfo callInfo, ...)
  966. {
  967. JIT_HELPER_REENTRANT_HEADER(Op_MaxInAnArray);
  968. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  969. ARGUMENTS(args, callInfo);
  970. Assert(args.Info.Count == 2);
  971. Var thisArg = args[0];
  972. Var arrayArg = args[1];
  973. ScriptContext * scriptContext = function->GetScriptContext();
  974. TypeId typeId = JavascriptOperators::GetTypeId(arrayArg);
  975. if (!JavascriptNativeArray::Is(typeId) && !(TypedArrayBase::Is(typeId) && typeId != TypeIds_CharArray && typeId != TypeIds_BoolArray))
  976. {
  977. if (JavascriptArray::IsVarArray(typeId) && UnsafeVarTo<JavascriptArray>(arrayArg)->GetLength() == 0)
  978. {
  979. return scriptContext->GetLibrary()->GetNegativeInfinite();
  980. }
  981. BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
  982. {
  983. return JavascriptFunction::CalloutHelper<false>(function, thisArg, /* overridingNewTarget = */nullptr, arrayArg, scriptContext);
  984. }
  985. END_SAFE_REENTRANT_CALL
  986. }
  987. if (JavascriptNativeArray::Is(typeId))
  988. {
  989. #if ENABLE_COPYONACCESS_ARRAY
  990. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(arrayArg);
  991. #endif
  992. JavascriptNativeArray * argsArray = UnsafeVarTo<JavascriptNativeArray>(arrayArg);
  993. uint len = argsArray->GetLength();
  994. if (len == 0)
  995. {
  996. return scriptContext->GetLibrary()->GetNegativeInfinite();
  997. }
  998. if (argsArray->GetHead()->next != nullptr || !argsArray->HasNoMissingValues() ||
  999. argsArray->GetHead()->length != len)
  1000. {
  1001. BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
  1002. {
  1003. return JavascriptFunction::CalloutHelper<false>(function, thisArg, /* overridingNewTarget = */nullptr, arrayArg, scriptContext);
  1004. }
  1005. END_SAFE_REENTRANT_CALL
  1006. }
  1007. return argsArray->FindMinOrMax(scriptContext, true /*findMax*/);
  1008. }
  1009. else
  1010. {
  1011. TypedArrayBase * argsArray = UnsafeVarTo<TypedArrayBase>(arrayArg);
  1012. uint len = argsArray->GetLength();
  1013. if (len == 0)
  1014. {
  1015. return scriptContext->GetLibrary()->GetNegativeInfinite();
  1016. }
  1017. Var max = argsArray->FindMinOrMax(scriptContext, typeId, true /*findMax*/);
  1018. if (max == nullptr)
  1019. {
  1020. BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
  1021. {
  1022. return JavascriptFunction::CalloutHelper<false>(function, thisArg, /* overridingNewTarget = */nullptr, arrayArg, scriptContext);
  1023. }
  1024. END_SAFE_REENTRANT_CALL
  1025. }
  1026. return max;
  1027. }
  1028. JIT_HELPER_END(Op_MaxInAnArray);
  1029. }
  1030. Var JavascriptMath::MinInAnArray(RecyclableObject * function, CallInfo callInfo, ...)
  1031. {
  1032. JIT_HELPER_REENTRANT_HEADER(Op_MinInAnArray);
  1033. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  1034. ARGUMENTS(args, callInfo);
  1035. Assert(args.Info.Count == 2);
  1036. Var thisArg = args[0];
  1037. Var arrayArg = args[1];
  1038. ScriptContext * scriptContext = function->GetScriptContext();
  1039. TypeId typeId = JavascriptOperators::GetTypeId(arrayArg);
  1040. if (!JavascriptNativeArray::Is(typeId) && !(TypedArrayBase::Is(typeId) && typeId != TypeIds_CharArray && typeId != TypeIds_BoolArray))
  1041. {
  1042. if (JavascriptArray::IsNonES5Array(typeId) && UnsafeVarTo<JavascriptArray>(arrayArg)->GetLength() == 0)
  1043. {
  1044. return scriptContext->GetLibrary()->GetPositiveInfinite();
  1045. }
  1046. BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
  1047. {
  1048. return JavascriptFunction::CalloutHelper<false>(function, thisArg, /* overridingNewTarget = */nullptr, arrayArg, scriptContext);
  1049. }
  1050. END_SAFE_REENTRANT_CALL
  1051. }
  1052. if (JavascriptNativeArray::Is(typeId))
  1053. {
  1054. #if ENABLE_COPYONACCESS_ARRAY
  1055. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(arrayArg);
  1056. #endif
  1057. JavascriptNativeArray * argsArray = UnsafeVarTo<JavascriptNativeArray>(arrayArg);
  1058. uint len = argsArray->GetLength();
  1059. if (len == 0)
  1060. {
  1061. return scriptContext->GetLibrary()->GetPositiveInfinite();
  1062. }
  1063. if (argsArray->GetHead()->next != nullptr || !argsArray->HasNoMissingValues() ||
  1064. argsArray->GetHead()->length != len)
  1065. {
  1066. BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
  1067. {
  1068. return JavascriptFunction::CalloutHelper<false>(function, thisArg, /* overridingNewTarget = */nullptr, arrayArg, scriptContext);
  1069. }
  1070. END_SAFE_REENTRANT_CALL
  1071. }
  1072. return argsArray->FindMinOrMax(scriptContext, false /*findMax*/);
  1073. }
  1074. else
  1075. {
  1076. TypedArrayBase * argsArray = UnsafeVarTo<TypedArrayBase>(arrayArg);
  1077. uint len = argsArray->GetLength();
  1078. if (len == 0)
  1079. {
  1080. return scriptContext->GetLibrary()->GetPositiveInfinite();
  1081. }
  1082. Var min = argsArray->FindMinOrMax(scriptContext, typeId, false /*findMax*/);
  1083. if (min == nullptr)
  1084. {
  1085. BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
  1086. {
  1087. return JavascriptFunction::CalloutHelper<false>(function, thisArg, /* overridingNewTarget = */nullptr, arrayArg, scriptContext);
  1088. }
  1089. END_SAFE_REENTRANT_CALL
  1090. }
  1091. return min;
  1092. }
  1093. JIT_HELPER_END(Op_MinInAnArray);
  1094. }
  1095. void InitializeRandomSeeds(uint64 *seed0, uint64 *seed1, ScriptContext *scriptContext)
  1096. {
  1097. #if DBG
  1098. if (CONFIG_FLAG(PRNGSeed0) && CONFIG_FLAG(PRNGSeed1))
  1099. {
  1100. *seed0 = CONFIG_FLAG(PRNGSeed0);
  1101. *seed1 = CONFIG_FLAG(PRNGSeed1);
  1102. }
  1103. else
  1104. #endif
  1105. {
  1106. LARGE_INTEGER s0;
  1107. LARGE_INTEGER s1;
  1108. if (!rand_s(reinterpret_cast<unsigned int*>(&s0.LowPart)) &&
  1109. !rand_s(reinterpret_cast<unsigned int*>(&s0.HighPart)) &&
  1110. !rand_s(reinterpret_cast<unsigned int*>(&s1.LowPart)) &&
  1111. !rand_s(reinterpret_cast<unsigned int*>(&s1.HighPart)))
  1112. {
  1113. *seed0 = s0.QuadPart;
  1114. *seed1 = s1.QuadPart;
  1115. }
  1116. else
  1117. {
  1118. AssertMsg(false, "Unable to initialize PRNG seeds with rand_s. Revert to using entropy.");
  1119. #ifdef ENABLE_CUSTOM_ENTROPY
  1120. ThreadContext *threadContext = scriptContext->GetThreadContext();
  1121. threadContext->GetEntropy().AddThreadCycleTime();
  1122. threadContext->GetEntropy().AddIoCounters();
  1123. *seed0 = threadContext->GetEntropy().GetRand();
  1124. threadContext->GetEntropy().AddThreadCycleTime();
  1125. threadContext->GetEntropy().AddIoCounters();
  1126. *seed1 = threadContext->GetEntropy().GetRand();
  1127. #endif
  1128. }
  1129. }
  1130. }
  1131. double ConvertRandomSeedsToDouble(const uint64 seed0, const uint64 seed1)
  1132. {
  1133. const uint64 mExp = 0x3FF0000000000000;
  1134. const uint64 mMant = 0x000FFFFFFFFFFFFF;
  1135. // Take lower 52 bits of the sum of two seeds to make a double
  1136. // Subtract 1.0 to negate the implicit integer bit of 1. Final range: [0.0, 1.0)
  1137. // See IEEE754 Double-precision floating-point format for details
  1138. // https://en.wikipedia.org/wiki/Double-precision_floating-point_format
  1139. uint64 resplusone_ui64 = ((seed0 + seed1) & mMant) | mExp;
  1140. double res = *(reinterpret_cast<double*>(&resplusone_ui64)) - 1.0;
  1141. return res;
  1142. }
  1143. void Xorshift128plus(uint64 *seed0, uint64 *seed1)
  1144. {
  1145. uint64 s1 = *seed0;
  1146. uint64 s0 = *seed1;
  1147. *seed0 = s0;
  1148. s1 ^= s1 << 23;
  1149. s1 ^= s1 >> 17;
  1150. s1 ^= s0;
  1151. s1 ^= s0 >> 26;
  1152. *seed1 = s1;
  1153. }
  1154. double JavascriptMath::Random(ScriptContext *scriptContext)
  1155. {
  1156. JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(DirectMath_Random);
  1157. uint64 seed0;
  1158. uint64 seed1;
  1159. if (!scriptContext->GetLibrary()->IsPRNGSeeded())
  1160. {
  1161. InitializeRandomSeeds(&seed0, &seed1, scriptContext);
  1162. #if DBG_DUMP
  1163. OUTPUT_TRACE(Js::PRNGPhase, _u("[PRNG:%x] INIT %I64x %I64x\n"), scriptContext, seed0, seed1);
  1164. #endif
  1165. scriptContext->GetLibrary()->SetIsPRNGSeeded(true);
  1166. #if ENABLE_TTD
  1167. if(scriptContext->ShouldPerformReplayAction())
  1168. {
  1169. scriptContext->GetThreadContext()->TTDLog->ReplayExternalEntropyRandomEvent(&seed0, &seed1);
  1170. }
  1171. else if(scriptContext->ShouldPerformRecordAction())
  1172. {
  1173. scriptContext->GetThreadContext()->TTDLog->RecordExternalEntropyRandomEvent(seed0, seed1);
  1174. }
  1175. else
  1176. {
  1177. ;
  1178. }
  1179. #endif
  1180. }
  1181. else
  1182. {
  1183. seed0 = scriptContext->GetLibrary()->GetRandSeed0();
  1184. seed1 = scriptContext->GetLibrary()->GetRandSeed1();
  1185. }
  1186. #if DBG_DUMP
  1187. OUTPUT_TRACE(Js::PRNGPhase, _u("[PRNG:%x] SEED %I64x %I64x\n"), scriptContext, seed0, seed1);
  1188. #endif
  1189. Xorshift128plus(&seed0, &seed1);
  1190. //update the seeds in script context
  1191. scriptContext->GetLibrary()->SetRandSeed0(seed0);
  1192. scriptContext->GetLibrary()->SetRandSeed1(seed1);
  1193. double res = ConvertRandomSeedsToDouble(seed0, seed1);
  1194. #if DBG_DUMP
  1195. OUTPUT_TRACE(Js::PRNGPhase, _u("[PRNG:%x] RAND %I64x\n"), scriptContext, *((uint64 *)&res));
  1196. #endif
  1197. return res;
  1198. JIT_HELPER_END(DirectMath_Random);
  1199. }
  1200. uint32 JavascriptMath::ToUInt32(double T1)
  1201. {
  1202. // Same as doing ToInt32 and reinterpret the bits as uint32
  1203. return (uint32)ToInt32Core(T1);
  1204. }
  1205. int32 JavascriptMath::ToInt32(double T1)
  1206. {
  1207. return JavascriptMath::ToInt32Core(T1);
  1208. }
  1209. int32 JavascriptMath::ToInt32_Full(Var aValue, ScriptContext* scriptContext)
  1210. {
  1211. AssertMsg(!TaggedInt::Is(aValue), "Should be detected");
  1212. // This is used when TaggedInt's overflow but remain under int32
  1213. // so Number is our most critical case:
  1214. TypeId typeId = JavascriptOperators::GetTypeId(aValue);
  1215. if (typeId == TypeIds_Number)
  1216. {
  1217. return JavascriptMath::ToInt32Core(JavascriptNumber::GetValue(aValue));
  1218. }
  1219. return JavascriptConversion::ToInt32_Full(aValue, scriptContext);
  1220. }
  1221. // Unable to put JIT_HELPER macro in .inl file, do instantiation here
  1222. JIT_HELPER_TEMPLATE(Conv_ToInt32Core, Conv_ToInt32Core)
  1223. JIT_HELPER_TEMPLATE(Conv_ToInt32_NoObjects, Conv_ToInt32_NoObjects)
  1224. JIT_HELPER_TEMPLATE(Conv_ToInt32, Conv_ToInt32)
  1225. JIT_HELPER_TEMPLATE(Op_ShiftRight, Op_ShiftRight)
  1226. JIT_HELPER_TEMPLATE(Op_ShiftRightU, Op_ShiftRightU)