JavascriptBigInt.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  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. namespace Js
  7. {
  8. JavascriptBigInt * JavascriptBigInt::Create(const char16 * content, charcount_t cchUseLength, bool isNegative, ScriptContext * scriptContext)
  9. {
  10. return RecyclerNew(scriptContext->GetRecycler(), JavascriptBigInt, content, cchUseLength, isNegative, scriptContext->GetLibrary()->GetBigIntTypeStatic());
  11. }
  12. JavascriptBigInt * JavascriptBigInt::New(JavascriptBigInt * pbi, ScriptContext * scriptContext)
  13. {
  14. JavascriptBigInt * bigintNew = RecyclerNew(scriptContext->GetRecycler(), JavascriptBigInt, scriptContext->GetLibrary()->GetBigIntTypeStatic());
  15. bigintNew->m_length = pbi->m_length;
  16. bigintNew->m_maxLength = pbi->m_maxLength;
  17. bigintNew->m_isNegative = pbi->m_isNegative;
  18. bigintNew->m_digits = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), digit_t, pbi->m_length);
  19. js_memcpy_s(bigintNew->m_digits, bigintNew->m_length * sizeof(digit_t), pbi->m_digits, bigintNew->m_length * sizeof(digit_t));
  20. return bigintNew;
  21. }
  22. RecyclableObject * JavascriptBigInt::CloneToScriptContext(ScriptContext* requestContext)
  23. {
  24. return JavascriptBigInt::New(this, requestContext);
  25. }
  26. Var JavascriptBigInt::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  27. {
  28. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  29. ARGUMENTS(args, callInfo);
  30. ScriptContext* scriptContext = function->GetScriptContext();
  31. AssertMsg(args.HasArg(), "Should always have implicit 'this'");
  32. // SkipDefaultNewObject function flag should have prevented the default object from
  33. // being created, except when call true a host dispatch.
  34. Var newTarget = args.GetNewTarget();
  35. bool isCtorSuperCall = JavascriptOperators::GetAndAssertIsConstructorSuperCall(args);
  36. Var result = nullptr;
  37. if (args.Info.Count > 1)
  38. {
  39. result = JavascriptConversion::ToBigInt(args[1], scriptContext);
  40. }
  41. else
  42. {
  43. // TODO:
  44. // v8 throw: cannot convert from undefined to bigint
  45. // we can consider creating a Zero BigInt
  46. AssertOrFailFast(false);
  47. }
  48. if (callInfo.Flags & CallFlags_New)
  49. {
  50. // TODO: handle new constructor
  51. // v8 throw: bigint is not a constructor
  52. AssertOrFailFast(false);
  53. }
  54. return isCtorSuperCall ?
  55. JavascriptOperators::OrdinaryCreateFromConstructor(VarTo<RecyclableObject>(newTarget), UnsafeVarTo<RecyclableObject>(result), nullptr, scriptContext) :
  56. result;
  57. }
  58. BOOL JavascriptBigInt::Equals(Var other, BOOL* value, ScriptContext * requestContext)
  59. {
  60. return JavascriptBigInt::Equals(this, other, value, requestContext);
  61. }
  62. BOOL JavascriptBigInt::Equals(JavascriptBigInt* left, Var right, BOOL* value, ScriptContext * requestContext)
  63. {
  64. switch (JavascriptOperators::GetTypeId(right))
  65. {
  66. case TypeIds_BigInt:
  67. *value = JavascriptBigInt::Equals(left, right);
  68. break;
  69. default:
  70. AssertMsg(VarIs<JavascriptBigInt>(right), "do not support comparison with types other than BigInt");
  71. *value = FALSE;
  72. break;
  73. }
  74. return true;
  75. }
  76. bool JavascriptBigInt::Resize(digit_t length)
  77. {
  78. digit_t *digits;
  79. if (length <= m_maxLength)
  80. {
  81. return true;
  82. }
  83. length += length;// double size
  84. if (SIZE_MAX / sizeof(digit_t) < length) // overflow
  85. {
  86. return false;
  87. }
  88. digits = RecyclerNewArrayLeaf(this->GetScriptContext()->GetRecycler(), digit_t, length);
  89. if (NULL == digits)
  90. {
  91. return false;
  92. }
  93. if (0 < m_length) // in this case, we need to copy old data over
  94. {
  95. js_memcpy_s(digits, length * sizeof(digit_t), m_digits, m_length * sizeof(digit_t));
  96. }
  97. m_digits = digits;
  98. m_maxLength = length;
  99. return true;
  100. }
  101. template <typename EncodedChar>
  102. void JavascriptBigInt::InitFromCharDigits(const EncodedChar *pChar, uint32 charLength, bool isNegative)
  103. {
  104. Assert(charLength >= 0);
  105. Assert(pChar != 0);
  106. const EncodedChar *pCharLimit = pChar + charLength - 1;//'n' at the end
  107. m_length = 0;
  108. m_digits = RecyclerNewArrayLeaf(this->GetScriptContext()->GetRecycler(), digit_t, m_maxLength);
  109. m_isNegative = isNegative;
  110. digit_t digitMul = 1;
  111. digit_t digitAdd = 0;
  112. bool check = true;
  113. for (; pChar < pCharLimit; pChar++)
  114. {
  115. Assert(NumberUtilities::IsDigit(*pChar));
  116. if (digitMul == 1e9)
  117. {
  118. check = MulThenAdd(digitMul, digitAdd);
  119. Assert(check);
  120. digitMul = 1;
  121. digitAdd = 0;
  122. }
  123. digitMul *= 10;
  124. digitAdd = digitAdd * 10 + *pChar - '0';
  125. }
  126. Assert(1 < digitMul);
  127. check = MulThenAdd(digitMul, digitAdd);
  128. Assert(check);
  129. // make sure this is no negative zero
  130. if (m_length == 0)
  131. {
  132. m_isNegative = false;
  133. m_length = 1;
  134. m_digits[0] = 0;
  135. }
  136. }
  137. // return low(a+b) and out carry
  138. digit_t JavascriptBigInt::AddDigit(digit_t a, digit_t b, digit_t* carry)
  139. {
  140. digit_t result = a + b;
  141. if (result < a)
  142. {
  143. *carry += 1;
  144. }
  145. return result;
  146. }
  147. // return low(a+b) and out carry
  148. digit_t JavascriptBigInt::SubDigit(digit_t a, digit_t b, digit_t* borrow)
  149. {
  150. digit_t result = a - b;
  151. if (result > a)
  152. {
  153. *borrow += 1;
  154. }
  155. return result;
  156. }
  157. bool JavascriptBigInt::IsZero(JavascriptBigInt * pbi)
  158. {
  159. return (pbi->m_length == 1 && pbi->m_digits[0] == 0);
  160. }
  161. void JavascriptBigInt::AbsoluteIncrement(JavascriptBigInt * pbi)
  162. {
  163. JavascriptBigInt* result = pbi;
  164. digit_t carry = 1;
  165. for (digit_t i = 0; i < result->m_length && carry > 0; i++)
  166. {
  167. digit_t tempCarry = 0;
  168. result->m_digits[i] = JavascriptBigInt::AddDigit(result->m_digits[i], carry, &tempCarry);
  169. carry = tempCarry;
  170. }
  171. if (carry > 0) //increase length
  172. {
  173. if (result->m_length >= result->m_maxLength && !result->Resize(result->m_length + 1))
  174. {
  175. AssertOrFailFastMsg(false, "AbsoluteIncrement overflow");
  176. }
  177. result->m_digits[result->m_length++] = carry;
  178. }
  179. }
  180. void JavascriptBigInt::AbsoluteDecrement(JavascriptBigInt * pbi)
  181. {
  182. JavascriptBigInt* result = pbi;
  183. Assert(!JavascriptBigInt::IsZero(result));
  184. digit_t borrow = 1;
  185. for (digit_t i = 0; i < result->m_length && borrow > 0; i++)
  186. {
  187. digit_t tempBorrow = 0;
  188. result->m_digits[i] = JavascriptBigInt::SubDigit(result->m_digits[i], borrow, &tempBorrow);
  189. borrow = tempBorrow;
  190. }
  191. Assert(borrow == 0);
  192. // remove trailing zero
  193. if (result->m_digits[result->m_length-1] == 0)
  194. {
  195. result->m_length--;
  196. }
  197. }
  198. void JavascriptBigInt::Increment(JavascriptBigInt * pbi)
  199. {
  200. if (pbi->m_isNegative)
  201. {
  202. // return 0n for -1n
  203. if (pbi->m_length == 1 && pbi->m_digits[0] == 1)
  204. {
  205. JavascriptBigInt* result = pbi;
  206. result->m_digits[0] = 0;
  207. result->m_isNegative = false;
  208. return;
  209. }
  210. return JavascriptBigInt::AbsoluteDecrement(pbi);
  211. }
  212. return JavascriptBigInt::AbsoluteIncrement(pbi);
  213. }
  214. void JavascriptBigInt::Decrement(JavascriptBigInt * pbi)
  215. {
  216. if (pbi->m_isNegative)
  217. {
  218. return JavascriptBigInt::AbsoluteIncrement(pbi);
  219. }
  220. if (JavascriptBigInt::IsZero(pbi)) // return -1n for 0n
  221. {
  222. JavascriptBigInt* result = pbi;
  223. result->m_digits[0] = 1;
  224. result->m_isNegative = true;
  225. return;
  226. }
  227. return JavascriptBigInt::AbsoluteDecrement(pbi);
  228. }
  229. Var JavascriptBigInt::Increment(Var aRight)
  230. {
  231. JavascriptBigInt* rightBigInt = VarTo<JavascriptBigInt>(aRight);
  232. JavascriptBigInt* newBigInt = JavascriptBigInt::New(rightBigInt, rightBigInt->GetScriptContext());
  233. JavascriptBigInt::Increment(newBigInt);
  234. return newBigInt;
  235. }
  236. Var JavascriptBigInt::Decrement(Var aRight)
  237. {
  238. JavascriptBigInt* rightBigInt = VarTo<JavascriptBigInt>(aRight);
  239. JavascriptBigInt* newBigInt = JavascriptBigInt::New(rightBigInt, rightBigInt->GetScriptContext());
  240. JavascriptBigInt::Decrement(newBigInt);
  241. return newBigInt;
  242. }
  243. // return low(a*b) and out high
  244. digit_t JavascriptBigInt::MulDigit(digit_t a, digit_t b, digit_t* resultHigh)
  245. {
  246. // Multiply is performed in half chuck favor.
  247. // For inputs [AH AL]*[BH BL], the result is:
  248. //
  249. // [AL*BL] // rLow
  250. // + [AL*BH] // rMid1
  251. // + [AH*BL] // rMid2
  252. // + [AH*BH] // rHigh
  253. // = [R1 R2 R3 R4] // high = [R1 R2], low = [R3 R4]
  254. //
  255. digit_t kHalfDigitBits = sizeof(digit_t) * 4;
  256. digit_t kHalfDigitMask = ((digit_t)1 << kHalfDigitBits) - 1;
  257. digit_t aLow = a & kHalfDigitMask;
  258. digit_t aHigh = a >> kHalfDigitBits;
  259. digit_t bLow = b & kHalfDigitMask;
  260. digit_t bHigh = b >> kHalfDigitBits;
  261. digit_t rLow = aLow * bLow;
  262. digit_t rMid1 = aLow * bHigh;
  263. digit_t rMid2 = aHigh * bLow;
  264. digit_t rHigh = aHigh * bHigh;
  265. digit_t carry = 0;
  266. digit_t resultLow = JavascriptBigInt::AddDigit(rLow, rMid1 << kHalfDigitBits, &carry);
  267. resultLow = JavascriptBigInt::AddDigit(resultLow, rMid2 << kHalfDigitBits, &carry);
  268. *resultHigh = (rMid1 >> kHalfDigitBits) + (rMid2 >> kHalfDigitBits) + rHigh + carry;
  269. return resultLow;
  270. }
  271. bool JavascriptBigInt::MulThenAdd(digit_t digitMul, digit_t digitAdd)
  272. {
  273. Assert(digitMul != 0);
  274. digit_t carryDigit = 0;
  275. digit_t *pDigit = m_digits;
  276. digit_t *pDigitLimit = pDigit + m_length;
  277. for (; pDigit < pDigitLimit; pDigit++)
  278. {
  279. *pDigit = JavascriptBigInt::MulDigit(*pDigit, digitMul, &carryDigit);// return low Digit to digit, hight Digit to carry
  280. if (digitAdd > 0)
  281. {
  282. *pDigit = JavascriptBigInt::AddDigit(*pDigit, digitAdd, &carryDigit);// add carry to result
  283. }
  284. digitAdd = carryDigit;
  285. }
  286. if (0 < digitAdd) // length increase by 1
  287. {
  288. if (m_length >= m_maxLength && !Resize(m_length + 1))
  289. {
  290. return false;
  291. }
  292. m_digits[m_length++] = digitAdd;
  293. }
  294. return true;
  295. }
  296. int JavascriptBigInt::Compare(JavascriptBigInt *pbi)
  297. {
  298. if (m_isNegative != pbi->m_isNegative)
  299. {
  300. if (m_isNegative)
  301. {
  302. return -1;
  303. }
  304. else
  305. {
  306. return 1;
  307. }
  308. }
  309. digit_t index;
  310. int sign = m_isNegative ? -1 : 1;
  311. if (m_length > pbi->m_length)
  312. {
  313. return 1 * sign;
  314. }
  315. if (m_length < pbi->m_length)
  316. {
  317. return -1 * sign;
  318. }
  319. if (0 == m_length)
  320. {
  321. return 0;
  322. }
  323. #pragma prefast(suppress:__WARNING_LOOP_ONLY_EXECUTED_ONCE,"noise")
  324. for (index = m_length - 1; m_digits[index] == pbi->m_digits[index]; index--)
  325. {
  326. if (0 == index)
  327. return 0;
  328. }
  329. Assert(m_digits[index] != pbi->m_digits[index]);
  330. return sign*((m_digits[index] > pbi->m_digits[index]) ? 1 : -1);
  331. }
  332. bool JavascriptBigInt::LessThan(Var aLeft, Var aRight)
  333. {
  334. AssertMsg(VarIs<JavascriptBigInt>(aLeft) && VarIs<JavascriptBigInt>(aRight), "BigInt LessThan");
  335. JavascriptBigInt *leftBigInt = VarTo<JavascriptBigInt>(aLeft);
  336. JavascriptBigInt *rightBigInt = VarTo<JavascriptBigInt>(aRight);
  337. return (leftBigInt->Compare(rightBigInt) < 0);
  338. }
  339. bool JavascriptBigInt::Equals(Var aLeft, Var aRight)
  340. {
  341. AssertMsg(VarIs<JavascriptBigInt>(aLeft) && VarIs<JavascriptBigInt>(aRight), "BigInt Equals");
  342. JavascriptBigInt *leftBigInt = VarTo<JavascriptBigInt>(aLeft);
  343. JavascriptBigInt *rightBigInt = VarTo<JavascriptBigInt>(aRight);
  344. return (leftBigInt->Compare(rightBigInt) == 0);
  345. }
  346. } // namespace Js