JavascriptExceptionOperators.cpp 74 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeLanguagePch.h"
  6. #include "Language/InterpreterStackFrame.h"
  7. #ifdef _M_IX86
  8. #ifdef _CONTROL_FLOW_GUARD
  9. extern "C" PVOID __guard_check_icall_fptr;
  10. #endif
  11. #endif
  12. namespace Js
  13. {
  14. void JavascriptExceptionOperators::AutoCatchHandlerExists::FetchNonUserCodeStatus(ScriptContext * scriptContext)
  15. {
  16. Assert(scriptContext);
  17. bool fFound = false;
  18. // If the outer try catch was already in the user code, no need to go any further.
  19. if (!m_previousCatchHandlerToUserCodeStatus)
  20. {
  21. Js::JavascriptFunction* caller = nullptr;
  22. if (JavascriptStackWalker::GetCaller(&caller, scriptContext))
  23. {
  24. Js::FunctionBody *funcBody = NULL;
  25. if (caller != NULL && (funcBody = caller->GetFunctionBody()) != NULL)
  26. {
  27. m_threadContext->SetIsUserCode(funcBody->IsNonUserCode() == false);
  28. fFound = true;
  29. }
  30. }
  31. }
  32. if (!fFound)
  33. {
  34. // If not successfully able to find the caller, set this catch handler belongs to the user code.
  35. m_threadContext->SetIsUserCode(true);
  36. }
  37. }
  38. JavascriptExceptionOperators::AutoCatchHandlerExists::AutoCatchHandlerExists(ScriptContext* scriptContext, bool isPromiseHandled)
  39. {
  40. Assert(scriptContext);
  41. m_threadContext = scriptContext->GetThreadContext();
  42. Assert(m_threadContext);
  43. m_previousCatchHandlerExists = m_threadContext->HasCatchHandler();
  44. m_threadContext->SetHasCatchHandler(TRUE);
  45. if (!isPromiseHandled)
  46. {
  47. // If this is created from a promise-specific code path, and we don't have a rejection
  48. // handler on the promise, then we want SetCatchHandler to be false so we report any
  49. // unhandled exceptions to any detached debuggers.
  50. m_threadContext->SetHasCatchHandler(false);
  51. }
  52. m_previousCatchHandlerToUserCodeStatus = m_threadContext->IsUserCode();
  53. if (scriptContext->IsScriptContextInDebugMode())
  54. {
  55. FetchNonUserCodeStatus(scriptContext);
  56. }
  57. }
  58. JavascriptExceptionOperators::AutoCatchHandlerExists::~AutoCatchHandlerExists()
  59. {
  60. m_threadContext->SetHasCatchHandler(m_previousCatchHandlerExists);
  61. m_threadContext->SetIsUserCode(m_previousCatchHandlerToUserCodeStatus);
  62. }
  63. JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack::TryHandlerAddrOfReturnAddrStack(ScriptContext* scriptContext, void *addrOfReturnAddr)
  64. {
  65. m_threadContext = scriptContext->GetThreadContext();
  66. m_prevTryHandlerAddrOfReturnAddr = m_threadContext->GetTryHandlerAddrOfReturnAddr();
  67. scriptContext->GetThreadContext()->SetTryHandlerAddrOfReturnAddr(addrOfReturnAddr);
  68. }
  69. JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack::~TryHandlerAddrOfReturnAddrStack()
  70. {
  71. m_threadContext->SetTryHandlerAddrOfReturnAddr(m_prevTryHandlerAddrOfReturnAddr);
  72. }
  73. JavascriptExceptionOperators::HasBailedOutPtrStack::HasBailedOutPtrStack(ScriptContext* scriptContext, bool *hasBailedOutPtr)
  74. {
  75. m_threadContext = scriptContext->GetThreadContext();
  76. m_prevHasBailedOutPtr = m_threadContext->GetHasBailedOutBitPtr();
  77. scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(hasBailedOutPtr);
  78. }
  79. JavascriptExceptionOperators::HasBailedOutPtrStack::~HasBailedOutPtrStack()
  80. {
  81. m_threadContext->SetHasBailedOutBitPtr(m_prevHasBailedOutPtr);
  82. }
  83. JavascriptExceptionOperators::PendingFinallyExceptionStack::PendingFinallyExceptionStack(ScriptContext* scriptContext, Js::JavascriptExceptionObject *exceptionObj)
  84. {
  85. m_threadContext = scriptContext->GetThreadContext();
  86. m_threadContext->SetPendingFinallyException(exceptionObj);
  87. }
  88. JavascriptExceptionOperators::PendingFinallyExceptionStack::~PendingFinallyExceptionStack()
  89. {
  90. m_threadContext->SetPendingFinallyException(nullptr);
  91. }
  92. bool JavascriptExceptionOperators::CrawlStackForWER(Js::ScriptContext& scriptContext)
  93. {
  94. return Js::Configuration::Global.flags.WERExceptionSupport && !scriptContext.GetThreadContext()->HasCatchHandler();
  95. }
  96. uint64 JavascriptExceptionOperators::StackCrawlLimitOnThrow(Var thrownObject, ScriptContext& scriptContext)
  97. {
  98. return CrawlStackForWER(scriptContext) ? MaxStackTraceLimit : GetStackTraceLimit(thrownObject, &scriptContext);
  99. }
  100. #ifdef _M_X64
  101. void *JavascriptExceptionOperators::OP_TryCatch(void *tryAddr,
  102. void *catchAddr,
  103. void *frame,
  104. size_t spillSize,
  105. size_t argsSize,
  106. int hasBailedOutOffset,
  107. ScriptContext *scriptContext)
  108. {
  109. void *continuation = nullptr;
  110. JavascriptExceptionObject *exception = nullptr;
  111. void *tryHandlerAddrOfReturnAddr = nullptr;
  112. Js::JavascriptExceptionOperators::HasBailedOutPtrStack hasBailedOutPtrStack(scriptContext, (bool*)((char*)frame + hasBailedOutOffset));
  113. PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout + spillSize + argsSize);
  114. {
  115. void * addrOfReturnAddr = (void*)((char*)frame + sizeof(char*));
  116. Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr);
  117. try
  118. {
  119. Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext);
  120. continuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize);
  121. }
  122. catch (const Js::JavascriptException& err)
  123. {
  124. exception = err.GetAndClear();
  125. tryHandlerAddrOfReturnAddr = scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr();
  126. }
  127. }
  128. if (exception)
  129. {
  130. // We need to clear callinfo on inlinee virtual frames on an exception.
  131. // We now allow inlining of functions into callers that have try-catch/try-finally.
  132. // When there is an exception inside the inlinee with caller having a try-catch, clear the inlinee callinfo by walking the stack.
  133. // If not, we might have the try-catch inside a loop, and when we execute the loop next time in the interpreter on BailOnException,
  134. // we will see inlined frames as being present even though they are not, because we depend on FrameInfo's callinfo to tell if an inlinee is on the stack,
  135. // and we haven't cleared those bits due to the exception
  136. // When we start inlining functions with try, we have to track the try addresses of the inlined functions as well.
  137. #if ENABLE_NATIVE_CODEGEN
  138. if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction())
  139. {
  140. WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, tryHandlerAddrOfReturnAddr);
  141. }
  142. #endif
  143. exception = exception->CloneIfStaticExceptionObject(scriptContext);
  144. bool hasBailedOut = *(bool*)((char*)frame + hasBailedOutOffset); // stack offsets are negative
  145. // If an inlinee bailed out due to some reason, the execution of the current function enclosing the try catch will also continue in the interpreter
  146. // During execution in the interpreter, if we throw outside the region enclosed in try/catch, this catch ends up catching that exception because its present on the call stack
  147. if (hasBailedOut)
  148. {
  149. // If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
  150. // it so happens that this catch was on the stack and caught the exception.
  151. // Re-throw!
  152. JavascriptExceptionOperators::DoThrow(exception, scriptContext);
  153. }
  154. Var exceptionObject = exception->GetThrownObject(scriptContext);
  155. AssertMsg(exceptionObject, "Caught object is null.");
  156. continuation = amd64_CallWithFakeFrame(catchAddr, frame, spillSize, argsSize, exceptionObject);
  157. }
  158. return continuation;
  159. }
  160. void *JavascriptExceptionOperators::OP_TryFinally(void *tryAddr,
  161. void *finallyAddr,
  162. void *frame,
  163. size_t spillSize,
  164. size_t argsSize,
  165. int hasBailedOutOffset,
  166. ScriptContext *scriptContext)
  167. {
  168. void *tryContinuation = nullptr;
  169. JavascriptExceptionObject *exception = nullptr;
  170. Js::JavascriptExceptionOperators::HasBailedOutPtrStack hasBailedOutPtrStack(scriptContext, (bool*)((char*)frame + hasBailedOutOffset));
  171. PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout + spillSize + argsSize);
  172. try
  173. {
  174. tryContinuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize);
  175. }
  176. catch (const Js::JavascriptException& err)
  177. {
  178. exception = err.GetAndClear();
  179. }
  180. if (exception)
  181. {
  182. // Clone static exception object early in case finally block overwrites it
  183. exception = exception->CloneIfStaticExceptionObject(scriptContext);
  184. }
  185. if (exception)
  186. {
  187. #if ENABLE_NATIVE_CODEGEN
  188. if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr)
  189. {
  190. if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction())
  191. {
  192. WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr());
  193. }
  194. }
  195. else
  196. {
  197. if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction())
  198. {
  199. WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, frame);
  200. }
  201. }
  202. #endif
  203. bool hasBailedOut = *(bool*)((char*)frame + hasBailedOutOffset); // stack offsets are negative
  204. if (hasBailedOut)
  205. {
  206. // If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
  207. // it so happens that this catch was on the stack and caught the exception.
  208. // Re-throw!
  209. JavascriptExceptionOperators::DoThrow(exception, scriptContext);
  210. }
  211. {
  212. Js::JavascriptExceptionOperators::PendingFinallyExceptionStack pendingFinallyExceptionStack(scriptContext, exception);
  213. void *continuation = amd64_CallWithFakeFrame(finallyAddr, frame, spillSize, argsSize, exception);
  214. return continuation;
  215. }
  216. }
  217. return tryContinuation;
  218. }
  219. void * JavascriptExceptionOperators::OP_TryFinallyNoOpt(void * tryAddr, void * finallyAddr, void * frame, size_t spillSize, size_t argsSize, ScriptContext * scriptContext)
  220. {
  221. void *tryContinuation = nullptr;
  222. void *finallyContinuation = nullptr;
  223. JavascriptExceptionObject *exception = nullptr;
  224. PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout + spillSize + argsSize);
  225. try
  226. {
  227. tryContinuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize);
  228. }
  229. catch (const Js::JavascriptException& err)
  230. {
  231. exception = err.GetAndClear();
  232. }
  233. if (exception)
  234. {
  235. // Clone static exception object early in case finally block overwrites it
  236. exception = exception->CloneIfStaticExceptionObject(scriptContext);
  237. }
  238. finallyContinuation = amd64_CallWithFakeFrame(finallyAddr, frame, spillSize, argsSize);
  239. if (finallyContinuation)
  240. {
  241. return finallyContinuation;
  242. }
  243. if (exception)
  244. {
  245. JavascriptExceptionOperators::DoThrow(exception, scriptContext);
  246. }
  247. return tryContinuation;
  248. }
  249. #elif defined(_M_ARM32_OR_ARM64)
  250. void *JavascriptExceptionOperators::OP_TryCatch(
  251. void *tryAddr,
  252. void *catchAddr,
  253. void *framePtr,
  254. void *localsPtr,
  255. size_t argsSize,
  256. int hasBailedOutOffset,
  257. ScriptContext *scriptContext)
  258. {
  259. void *continuation = nullptr;
  260. JavascriptExceptionObject *exception = nullptr;
  261. void * tryHandlerAddrOfReturnAddr = nullptr;
  262. Js::JavascriptExceptionOperators::HasBailedOutPtrStack hasBailedOutPtrStack(scriptContext, (bool*)((char*)localsPtr + hasBailedOutOffset));
  263. PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout + argsSize);
  264. {
  265. void * addrOfReturnAddr = (void*)((char*)framePtr + sizeof(char*));
  266. Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr);
  267. try
  268. {
  269. Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext);
  270. #if defined(_M_ARM)
  271. continuation = arm_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize);
  272. #elif defined(_M_ARM64)
  273. continuation = arm64_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize);
  274. #endif
  275. }
  276. catch (const Js::JavascriptException& err)
  277. {
  278. exception = err.GetAndClear();
  279. tryHandlerAddrOfReturnAddr = scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr();
  280. }
  281. }
  282. if (exception)
  283. {
  284. // We need to clear callinfo on inlinee virtual frames on an exception.
  285. // We now allow inlining of functions into callers that have try-catch/try-finally.
  286. // When there is an exception inside the inlinee with caller having a try-catch, clear the inlinee callinfo by walking the stack.
  287. // If not, we might have the try-catch inside a loop, and when we execute the loop next time in the interpreter on BailOnException,
  288. // we will see inlined frames as being present even though they are not, because we depend on FrameInfo's callinfo to tell if an inlinee is on the stack,
  289. // and we haven't cleared those bits due to the exception
  290. // When we start inlining functions with try, we have to track the try addresses of the inlined functions as well.
  291. #if ENABLE_NATIVE_CODEGEN
  292. if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction())
  293. {
  294. WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, tryHandlerAddrOfReturnAddr);
  295. }
  296. #endif
  297. exception = exception->CloneIfStaticExceptionObject(scriptContext);
  298. bool hasBailedOut = *(bool*)((char*)localsPtr + hasBailedOutOffset); // stack offsets are sp relative
  299. if (hasBailedOut)
  300. {
  301. // If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
  302. // it so happens that this catch was on the stack and caught the exception.
  303. // Re-throw!
  304. JavascriptExceptionOperators::DoThrow(exception, scriptContext);
  305. }
  306. Var exceptionObject = exception->GetThrownObject(scriptContext);
  307. AssertMsg(exceptionObject, "Caught object is null.");
  308. #if defined(_M_ARM)
  309. continuation = arm_CallCatch(catchAddr, framePtr, localsPtr, argsSize, exceptionObject);
  310. #elif defined(_M_ARM64)
  311. continuation = arm64_CallCatch(catchAddr, framePtr, localsPtr, argsSize, exceptionObject);
  312. #endif
  313. }
  314. return continuation;
  315. }
  316. void *JavascriptExceptionOperators::OP_TryFinally(
  317. void *tryAddr,
  318. void *finallyAddr,
  319. void *framePtr,
  320. void *localsPtr,
  321. size_t argsSize,
  322. int hasBailedOutOffset,
  323. ScriptContext *scriptContext)
  324. {
  325. void *tryContinuation = nullptr;
  326. JavascriptExceptionObject *exception = nullptr;
  327. Js::JavascriptExceptionOperators::HasBailedOutPtrStack hasBailedOutPtrStack(scriptContext, (bool*)((char*)localsPtr + hasBailedOutOffset));
  328. PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout + argsSize);
  329. try
  330. {
  331. #if defined(_M_ARM)
  332. tryContinuation = arm_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize);
  333. #elif defined(_M_ARM64)
  334. tryContinuation = arm64_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize);
  335. #endif
  336. }
  337. catch (const Js::JavascriptException& err)
  338. {
  339. exception = err.GetAndClear();
  340. }
  341. if (exception)
  342. {
  343. #if ENABLE_NATIVE_CODEGEN
  344. if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr)
  345. {
  346. if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction())
  347. {
  348. WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr());
  349. }
  350. }
  351. else
  352. {
  353. if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction())
  354. {
  355. WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, framePtr);
  356. }
  357. }
  358. #endif
  359. // Clone static exception object early in case finally block overwrites it
  360. exception = exception->CloneIfStaticExceptionObject(scriptContext);
  361. bool hasBailedOut = *(bool*)((char*)localsPtr + hasBailedOutOffset); // stack offsets are sp relative
  362. if (hasBailedOut)
  363. {
  364. // If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
  365. // it so happens that this catch was on the stack and caught the exception.
  366. // Re-throw!
  367. JavascriptExceptionOperators::DoThrow(exception, scriptContext);
  368. }
  369. {
  370. Js::JavascriptExceptionOperators::PendingFinallyExceptionStack pendingFinallyExceptionStack(scriptContext, exception);
  371. #if defined(_M_ARM)
  372. void * finallyContinuation = arm_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize);
  373. #elif defined(_M_ARM64)
  374. void * finallyContinuation = arm64_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize);
  375. #endif
  376. return finallyContinuation;
  377. }
  378. }
  379. return tryContinuation;
  380. }
  381. void *JavascriptExceptionOperators::OP_TryFinallyNoOpt(
  382. void *tryAddr,
  383. void *finallyAddr,
  384. void *framePtr,
  385. void *localsPtr,
  386. size_t argsSize,
  387. ScriptContext *scriptContext)
  388. {
  389. void *tryContinuation = nullptr;
  390. void *finallyContinuation = nullptr;
  391. JavascriptExceptionObject *exception = nullptr;
  392. PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout + argsSize);
  393. try
  394. {
  395. #if defined(_M_ARM)
  396. tryContinuation = arm_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize);
  397. #elif defined(_M_ARM64)
  398. tryContinuation = arm64_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize);
  399. #endif
  400. }
  401. catch (const Js::JavascriptException& err)
  402. {
  403. exception = err.GetAndClear();
  404. }
  405. if (exception)
  406. {
  407. // Clone static exception object early in case finally block overwrites it
  408. exception = exception->CloneIfStaticExceptionObject(scriptContext);
  409. }
  410. #if defined(_M_ARM)
  411. finallyContinuation = arm_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize);
  412. #elif defined(_M_ARM64)
  413. finallyContinuation = arm64_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize);
  414. #endif
  415. if (finallyContinuation)
  416. {
  417. return finallyContinuation;
  418. }
  419. if (exception)
  420. {
  421. JavascriptExceptionOperators::DoThrow(exception, scriptContext);
  422. }
  423. return tryContinuation;
  424. }
  425. #else
  426. #pragma warning(push)
  427. #pragma warning(disable:4731) // frame pointer register 'ebp' modified by inline assembly code
  428. void* JavascriptExceptionOperators::OP_TryCatch(void* tryAddr, void* handlerAddr, void* framePtr, int hasBailedOutOffset, ScriptContext *scriptContext)
  429. {
  430. void* continuationAddr = NULL;
  431. Js::JavascriptExceptionObject* pExceptionObject = NULL;
  432. void *tryHandlerAddrOfReturnAddr = nullptr;
  433. Js::JavascriptExceptionOperators::HasBailedOutPtrStack hasBailedOutPtrStack(scriptContext, (bool*)((char*)framePtr + hasBailedOutOffset));
  434. PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout);
  435. {
  436. void * addrOfReturnAddr = (void*)((char*)framePtr + sizeof(char*));
  437. Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr);
  438. try
  439. {
  440. Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext);
  441. // Adjust the frame pointer and call into the try.
  442. // If the try completes without raising an exception, it will pass back the continuation address.
  443. // Bug in compiler optimizer: try-catch can be optimized away if the try block contains __asm calls into function
  444. // that may throw. The current workaround is to add the following dummy throw to prevent this optimization.
  445. if (!tryAddr)
  446. {
  447. Js::Throw::InternalError();
  448. }
  449. #ifdef _M_IX86
  450. void *savedEsp;
  451. __asm
  452. {
  453. // Save and restore the callee-saved registers around the call.
  454. // TODO: track register kills by region and generate per-region prologs and epilogs
  455. push esi
  456. push edi
  457. push ebx
  458. // 8-byte align frame to improve floating point perf of our JIT'd code.
  459. // Save ESP
  460. mov ecx, esp
  461. mov savedEsp, ecx
  462. and esp, -8
  463. // Set up the call target, save the current frame ptr, and adjust the frame to access
  464. // locals in native code.
  465. mov eax, tryAddr
  466. #if 0 && defined(_CONTROL_FLOW_GUARD)
  467. // verify that the call target is valid
  468. mov ebx, eax; save call target
  469. mov ecx, eax
  470. call[__guard_check_icall_fptr]
  471. mov eax, ebx; restore call target
  472. #endif
  473. push ebp
  474. mov ebp, framePtr
  475. call eax
  476. pop ebp
  477. // The native code gives us the address where execution should continue on exit
  478. // from the region.
  479. mov continuationAddr, eax
  480. // Restore ESP
  481. mov ecx, savedEsp
  482. mov esp, ecx
  483. pop ebx
  484. pop edi
  485. pop esi
  486. }
  487. #else
  488. AssertMsg(FALSE, "Unsupported native try-catch handler");
  489. #endif
  490. }
  491. catch (const Js::JavascriptException& err)
  492. {
  493. pExceptionObject = err.GetAndClear();
  494. tryHandlerAddrOfReturnAddr = scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr();
  495. }
  496. }
  497. // Let's run user catch handler code only after the stack has been unwound.
  498. if(pExceptionObject)
  499. {
  500. // We need to clear callinfo on inlinee virtual frames on an exception.
  501. // We now allow inlining of functions into callers that have try-catch/try-finally.
  502. // When there is an exception inside the inlinee with caller having a try-catch, clear the inlinee callinfo by walking the stack.
  503. // If not, we might have the try-catch inside a loop, and when we execute the loop next time in the interpreter on BailOnException,
  504. // we will see inlined frames as being present even though they are not, because we depend on FrameInfo's callinfo to tell if an inlinee is on the stack,
  505. // and we haven't cleared those bits due to the exception
  506. // When we start inlining functions with try, we have to track the try addresses of the inlined functions as well.
  507. #if ENABLE_NATIVE_CODEGEN
  508. if (pExceptionObject->GetExceptionContext() && pExceptionObject->GetExceptionContext()->ThrowingFunction())
  509. {
  510. WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, tryHandlerAddrOfReturnAddr);
  511. }
  512. #endif
  513. pExceptionObject = pExceptionObject->CloneIfStaticExceptionObject(scriptContext);
  514. bool hasBailedOut = *(bool*)((char*)framePtr + hasBailedOutOffset); // stack offsets are negative
  515. if (hasBailedOut)
  516. {
  517. // If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
  518. // it so happens that this catch was on the stack and caught the exception.
  519. // Re-throw!
  520. JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext);
  521. }
  522. Var catchObject = pExceptionObject->GetThrownObject(scriptContext);
  523. AssertMsg(catchObject, "Caught object is NULL");
  524. #ifdef _M_IX86
  525. void *savedEsp;
  526. __asm
  527. {
  528. // Save and restore the callee-saved registers around the call.
  529. // TODO: track register kills by region and generate per-region prologs and epilogs
  530. push esi
  531. push edi
  532. push ebx
  533. // 8-byte align frame to improve floating point perf of our JIT'd code.
  534. // Save ESP
  535. mov ecx, esp
  536. mov savedEsp, ecx
  537. and esp, -8
  538. // Set up the call target
  539. mov ecx, handlerAddr
  540. #if 0 && defined(_CONTROL_FLOW_GUARD)
  541. // verify that the call target is valid
  542. mov ebx, ecx ; save call target
  543. call [__guard_check_icall_fptr]
  544. mov ecx, ebx ; restore call target
  545. #endif
  546. // Set up catch object, save the current frame ptr, and adjust the frame to access
  547. // locals in native code.
  548. mov eax, catchObject
  549. push ebp
  550. mov ebp, framePtr
  551. call ecx
  552. pop ebp
  553. // The native code gives us the address where execution should continue on exit
  554. // from the region.
  555. mov continuationAddr, eax
  556. // Restore ESP
  557. mov ecx, savedEsp
  558. mov esp, ecx
  559. pop ebx
  560. pop edi
  561. pop esi
  562. }
  563. #else
  564. AssertMsg(FALSE, "Unsupported native try-catch handler");
  565. #endif
  566. }
  567. return continuationAddr;
  568. }
  569. void* JavascriptExceptionOperators::OP_TryFinally(void* tryAddr, void* handlerAddr, void* framePtr, int hasBailedOutOffset, ScriptContext *scriptContext)
  570. {
  571. Js::JavascriptExceptionObject* pExceptionObject = NULL;
  572. void* continuationAddr = NULL;
  573. Js::JavascriptExceptionOperators::HasBailedOutPtrStack hasBailedOutPtrStack(scriptContext, (bool*)((char*)framePtr + hasBailedOutOffset));
  574. PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout);
  575. try
  576. {
  577. // Bug in compiler optimizer: try-catch can be optimized away if the try block contains __asm calls into function
  578. // that may throw. The current workaround is to add the following dummy throw to prevent this optimization.
  579. // It seems like compiler got smart and still optimizes if the exception is not JavascriptExceptionObject (see catch handler below).
  580. // In order to circumvent that we are throwing OutOfMemory.
  581. if (!tryAddr)
  582. {
  583. Assert(false);
  584. ThrowOutOfMemory(scriptContext);
  585. }
  586. #ifdef _M_IX86
  587. void *savedEsp;
  588. __asm
  589. {
  590. // Save and restore the callee-saved registers around the call.
  591. // TODO: track register kills by region and generate per-region prologs and epilogs
  592. push esi
  593. push edi
  594. push ebx
  595. // 8-byte align frame to improve floating point perf of our JIT'd code.
  596. // Save ESP
  597. mov ecx, esp
  598. mov savedEsp, ecx
  599. and esp, -8
  600. // Set up the call target, save the current frame ptr, and adjust the frame to access
  601. // locals in native code.
  602. mov eax, tryAddr
  603. #if 0 && defined(_CONTROL_FLOW_GUARD)
  604. // verify that the call target is valid
  605. mov ebx, eax ; save call target
  606. mov ecx, eax
  607. call [__guard_check_icall_fptr]
  608. mov eax, ebx ; restore call target
  609. #endif
  610. push ebp
  611. mov ebp, framePtr
  612. call eax
  613. pop ebp
  614. // The native code gives us the address where execution should continue on exit
  615. // from the region.
  616. mov continuationAddr, eax
  617. // Restore ESP
  618. mov ecx, savedEsp
  619. mov esp, ecx
  620. pop ebx
  621. pop edi
  622. pop esi
  623. }
  624. #else
  625. AssertMsg(FALSE, "Unsupported native try-finally handler");
  626. #endif
  627. }
  628. catch(const Js::JavascriptException& err)
  629. {
  630. pExceptionObject = err.GetAndClear();
  631. }
  632. if (pExceptionObject)
  633. {
  634. #if ENABLE_NATIVE_CODEGEN
  635. if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr)
  636. {
  637. if (pExceptionObject->GetExceptionContext() && pExceptionObject->GetExceptionContext()->ThrowingFunction())
  638. {
  639. WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr());
  640. }
  641. }
  642. else
  643. {
  644. if (pExceptionObject->GetExceptionContext() && pExceptionObject->GetExceptionContext()->ThrowingFunction())
  645. {
  646. WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, framePtr);
  647. }
  648. }
  649. #endif
  650. // Clone static exception object early in case finally block overwrites it
  651. pExceptionObject = pExceptionObject->CloneIfStaticExceptionObject(scriptContext);
  652. bool hasBailedOut = *(bool*)((char*)framePtr + hasBailedOutOffset); // stack offsets are negative
  653. if (hasBailedOut)
  654. {
  655. // If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
  656. // it so happens that this catch was on the stack and caught the exception.
  657. // Re-throw!
  658. JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext);
  659. }
  660. {
  661. Js::JavascriptExceptionOperators::PendingFinallyExceptionStack pendingFinallyExceptionStack(scriptContext, pExceptionObject);
  662. if (!tryAddr)
  663. {
  664. // Bug in compiler optimizer: dtor is not called, it is a compiler bug
  665. // The compiler thinks the asm cannot throw, so add an explicit throw to generate dtor calls
  666. Js::Throw::InternalError();
  667. }
  668. void* newContinuationAddr = NULL;
  669. #ifdef _M_IX86
  670. void *savedEsp;
  671. __asm
  672. {
  673. // Save and restore the callee-saved registers around the call.
  674. // TODO: track register kills by region and generate per-region prologs and epilogs
  675. push esi
  676. push edi
  677. push ebx
  678. // 8-byte align frame to improve floating point perf of our JIT'd code.
  679. // Save ESP
  680. mov ecx, esp
  681. mov savedEsp, ecx
  682. and esp, -8
  683. // Set up the call target
  684. mov eax, handlerAddr
  685. #if 0 && defined(_CONTROL_FLOW_GUARD)
  686. // verify that the call target is valid
  687. mov ebx, eax; save call target
  688. mov ecx, eax
  689. call[__guard_check_icall_fptr]
  690. mov eax, ebx; restore call target
  691. #endif
  692. // save the current frame ptr, and adjust the frame to access
  693. // locals in native code.
  694. push ebp
  695. mov ebp, framePtr
  696. call eax
  697. pop ebp
  698. // The native code gives us the address where execution should continue on exit
  699. // from the finally, but only if flow leaves the finally before it completes.
  700. mov newContinuationAddr, eax
  701. // Restore ESP
  702. mov ecx, savedEsp
  703. mov esp, ecx
  704. pop ebx
  705. pop edi
  706. pop esi
  707. }
  708. #else
  709. AssertMsg(FALSE, "Unsupported native try-finally handler");
  710. #endif
  711. return newContinuationAddr;
  712. }
  713. }
  714. return continuationAddr;
  715. }
  716. void* JavascriptExceptionOperators::OP_TryFinallyNoOpt(void* tryAddr, void* handlerAddr, void* framePtr, ScriptContext *scriptContext)
  717. {
  718. Js::JavascriptExceptionObject* pExceptionObject = NULL;
  719. void* continuationAddr = NULL;
  720. PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout);
  721. try
  722. {
  723. // Bug in compiler optimizer: try-catch can be optimized away if the try block contains __asm calls into function
  724. // that may throw. The current workaround is to add the following dummy throw to prevent this optimization.
  725. // It seems like compiler got smart and still optimizes if the exception is not JavascriptExceptionObject (see catch handler below).
  726. // In order to circumvent that we are throwing OutOfMemory.
  727. if (!tryAddr)
  728. {
  729. Assert(false);
  730. ThrowOutOfMemory(scriptContext);
  731. }
  732. #ifdef _M_IX86
  733. void *savedEsp;
  734. __asm
  735. {
  736. // Save and restore the callee-saved registers around the call.
  737. // TODO: track register kills by region and generate per-region prologs and epilogs
  738. push esi
  739. push edi
  740. push ebx
  741. // 8-byte align frame to improve floating point perf of our JIT'd code.
  742. // Save ESP
  743. mov ecx, esp
  744. mov savedEsp, ecx
  745. and esp, -8
  746. // Set up the call target, save the current frame ptr, and adjust the frame to access
  747. // locals in native code.
  748. mov eax, tryAddr
  749. #if 0 && defined(_CONTROL_FLOW_GUARD)
  750. // verify that the call target is valid
  751. mov ebx, eax; save call target
  752. mov ecx, eax
  753. call[__guard_check_icall_fptr]
  754. mov eax, ebx; restore call target
  755. #endif
  756. push ebp
  757. mov ebp, framePtr
  758. call eax
  759. pop ebp
  760. // The native code gives us the address where execution should continue on exit
  761. // from the region.
  762. mov continuationAddr, eax
  763. // Restore ESP
  764. mov ecx, savedEsp
  765. mov esp, ecx
  766. pop ebx
  767. pop edi
  768. pop esi
  769. }
  770. #else
  771. AssertMsg(FALSE, "Unsupported native try-finally handler");
  772. #endif
  773. }
  774. catch (const Js::JavascriptException& err)
  775. {
  776. pExceptionObject = err.GetAndClear();
  777. }
  778. if (pExceptionObject)
  779. {
  780. // Clone static exception object early in case finally block overwrites it
  781. pExceptionObject = pExceptionObject->CloneIfStaticExceptionObject(scriptContext);
  782. }
  783. void* newContinuationAddr = NULL;
  784. #ifdef _M_IX86
  785. void *savedEsp;
  786. __asm
  787. {
  788. // Save and restore the callee-saved registers around the call.
  789. // TODO: track register kills by region and generate per-region prologs and epilogs
  790. push esi
  791. push edi
  792. push ebx
  793. // 8-byte align frame to improve floating point perf of our JIT'd code.
  794. // Save ESP
  795. mov ecx, esp
  796. mov savedEsp, ecx
  797. and esp, -8
  798. // Set up the call target
  799. mov eax, handlerAddr
  800. #if 0 && defined(_CONTROL_FLOW_GUARD)
  801. // verify that the call target is valid
  802. mov ecx, eax
  803. call[__guard_check_icall_fptr]
  804. mov eax, ecx; restore call target
  805. #endif
  806. // save the current frame ptr, and adjust the frame to access
  807. // locals in native code.
  808. push ebp
  809. mov ebp, framePtr
  810. call eax
  811. pop ebp
  812. // The native code gives us the address where execution should continue on exit
  813. // from the finally, but only if flow leaves the finally before it completes.
  814. mov newContinuationAddr, eax
  815. // Restore ESP
  816. mov ecx, savedEsp
  817. mov esp, ecx
  818. pop ebx
  819. pop edi
  820. pop esi
  821. }
  822. #else
  823. AssertMsg(FALSE, "Unsupported native try-finally handler");
  824. #endif
  825. if (newContinuationAddr != NULL)
  826. {
  827. // Non-null return value from the finally indicates that the finally seized the flow
  828. // with a jump/return out of the region. Continue at that address instead of handling
  829. // the exception.
  830. return newContinuationAddr;
  831. }
  832. if (pExceptionObject)
  833. {
  834. JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext);
  835. }
  836. return continuationAddr;
  837. }
  838. #endif
  839. void __declspec(noreturn) JavascriptExceptionOperators::OP_Throw(Var object, ScriptContext* scriptContext)
  840. {
  841. Throw(object, scriptContext);
  842. }
  843. #if defined(DBG) && defined(_M_IX86)
  844. extern "C" void * _except_handler4;
  845. void JavascriptExceptionOperators::DbgCheckEHChain()
  846. {
  847. #if 0
  848. // This debug check is disabled until we figure out how to trace a fs:0 chain if we throw from inside
  849. // a finally.
  850. void *currentFS0;
  851. ThreadContext * threadContext = ThreadContext::GetContextForCurrentThread();
  852. if (!threadContext->IsScriptActive())
  853. {
  854. return;
  855. }
  856. // Walk the FS:0 chain of exception handlers, until the FS:0 handler in CallRootFunction.
  857. // We should only see SEH frames on the way.
  858. // We do allow C++ EH frames as long as there is no active objects (state = -1).
  859. // That's because we may see frames that have calls to new(). This introduces an EH frame
  860. // to call delete if the constructor throws. Our constructors shouldn't throw, so we should be fine.
  861. currentFS0 = (void*)__readfsdword(0);
  862. while (currentFS0 != threadContext->callRootFS0)
  863. {
  864. // EH struct:
  865. // void * next;
  866. // void * handler;
  867. // int state;
  868. AssertMsg(*((void**)currentFS0 + 1) == &_except_handler4
  869. || *((int*)currentFS0 + 2) == -1, "Found a non SEH exception frame on stack");
  870. currentFS0 = *(void**)currentFS0;
  871. }
  872. #endif
  873. }
  874. #endif
  875. void JavascriptExceptionOperators::Throw(Var object, ScriptContext * scriptContext)
  876. {
  877. #if defined(DBG) && defined(_M_IX86)
  878. DbgCheckEHChain();
  879. #endif
  880. Assert(scriptContext != nullptr);
  881. // TODO: FastDOM Trampolines will throw JS Exceptions but are not isScriptActive
  882. //AssertMsg(scriptContext->GetThreadContext()->IsScriptActive() ||
  883. // (JavascriptError::Is(object) && (JavascriptError::FromVar(object))->IsExternalError()),
  884. // "Javascript exception raised when script is not active");
  885. AssertMsg(scriptContext->GetThreadContext()->IsInScript() ||
  886. (JavascriptError::Is(object) && (JavascriptError::FromVar(object))->IsExternalError()),
  887. "Javascript exception raised without being in CallRootFunction");
  888. JavascriptError *javascriptError = nullptr;
  889. if (JavascriptError::Is(object))
  890. {
  891. // We keep track of the JavascriptExceptionObject that was created when this error
  892. // was first thrown so that we can always get the correct metadata.
  893. javascriptError = JavascriptError::FromVar(object);
  894. JavascriptExceptionObject *exceptionObject = javascriptError->GetJavascriptExceptionObject();
  895. if (exceptionObject)
  896. {
  897. JavascriptExceptionOperators::ThrowExceptionObject(exceptionObject, scriptContext, true);
  898. }
  899. }
  900. JavascriptExceptionObject * exceptionObject =
  901. RecyclerNew(scriptContext->GetRecycler(), JavascriptExceptionObject, object, scriptContext, NULL);
  902. bool resetStack = false;
  903. if (javascriptError)
  904. {
  905. if (!javascriptError->IsStackPropertyRedefined())
  906. {
  907. /*
  908. Throwing an error object. Original stack property will be pointing to the stack created at time of Error constructor.
  909. Reset the stack property to match IE11 behavior
  910. */
  911. resetStack = true;
  912. }
  913. javascriptError->SetJavascriptExceptionObject(exceptionObject);
  914. }
  915. JavascriptExceptionOperators::ThrowExceptionObject(exceptionObject, scriptContext, /*considerPassingToDebugger=*/ true, /*returnAddress=*/ nullptr, resetStack);
  916. }
  917. #if ENABLE_NATIVE_CODEGEN
  918. // TODO: Add code address of throwing function on exception context, and use that for returnAddress instead of passing nullptr which starts stackwalk from the top
  919. void JavascriptExceptionOperators::WalkStackForCleaningUpInlineeInfo(ScriptContext *scriptContext, PVOID returnAddress, PVOID tryHandlerAddrOfReturnAddr)
  920. {
  921. Assert(tryHandlerAddrOfReturnAddr != nullptr);
  922. JavascriptStackWalker walker(scriptContext, /*useEERContext*/ true, returnAddress);
  923. // We have to walk the inlinee frames and clear callinfo count on them on an exception
  924. // At this point inlinedFrameWalker is closed, so we should build it again by calling InlinedFrameWalker::FromPhysicalFrame
  925. walker.WalkAndClearInlineeFrameCallInfoOnException(tryHandlerAddrOfReturnAddr);
  926. }
  927. #endif
  928. void
  929. JavascriptExceptionOperators::WalkStackForExceptionContext(ScriptContext& scriptContext, JavascriptExceptionContext& exceptionContext, Var thrownObject, uint64 stackCrawlLimit, PVOID returnAddress, bool isThrownException, bool resetSatck)
  930. {
  931. uint32 callerBytecodeOffset;
  932. JavascriptFunction * jsFunc = WalkStackForExceptionContextInternal(scriptContext, exceptionContext, thrownObject, callerBytecodeOffset, stackCrawlLimit, returnAddress, isThrownException, resetSatck);
  933. if (jsFunc)
  934. {
  935. // If found, the caller is a function, and we can retrieve the debugger info from there
  936. // otherwise it's probably just accessing property. While it is still possible to throw
  937. // from that context, we just won't be able to get the line number etc., which make sense.
  938. exceptionContext.SetThrowingFunction(jsFunc, callerBytecodeOffset, returnAddress);
  939. }
  940. }
  941. JavascriptFunction *
  942. JavascriptExceptionOperators::WalkStackForExceptionContextInternal(ScriptContext& scriptContext, JavascriptExceptionContext& exceptionContext, Var thrownObject,
  943. uint32& callerByteCodeOffset, uint64 stackCrawlLimit, PVOID returnAddress, bool isThrownException, bool resetStack)
  944. {
  945. JavascriptStackWalker walker(&scriptContext, true, returnAddress);
  946. JavascriptFunction* jsFunc = nullptr;
  947. if (!GetCaller(walker, jsFunc))
  948. {
  949. return nullptr;
  950. }
  951. // Skip to first non-Library code
  952. // Similar behavior to GetCaller returning false
  953. if(jsFunc->IsLibraryCode() && !walker.GetNonLibraryCodeCaller(&jsFunc))
  954. {
  955. return nullptr;
  956. }
  957. JavascriptFunction * caller = jsFunc;
  958. callerByteCodeOffset = walker.GetByteCodeOffset();
  959. Assert(!caller->IsLibraryCode());
  960. // NOTE Don't set the throwing exception here, because we might need to box it and will cause a nested stack walker
  961. // instead, return it to be set in WalkStackForExceptionContext
  962. JavascriptExceptionContext::StackTrace *stackTrace = nullptr;
  963. // If we take an OOM (JavascriptException for OOM if script is active), just bail early and return what we've got
  964. HRESULT hr;
  965. BEGIN_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_NESTED
  966. {
  967. stackTrace = RecyclerNew(scriptContext.GetRecycler(), JavascriptExceptionContext::StackTrace, scriptContext.GetRecycler());
  968. if (stackCrawlLimit > 0)
  969. {
  970. const bool crawlStackForWER = CrawlStackForWER(scriptContext);
  971. // In WER scenario, we should combine the original stack with latest throw stack as the final throw might be coming form
  972. // a different stack.
  973. uint64 i = 1;
  974. if (crawlStackForWER && thrownObject && Js::JavascriptError::Is(thrownObject))
  975. {
  976. Js::JavascriptError* errorObject = Js::JavascriptError::FromVar(thrownObject);
  977. Js::JavascriptExceptionContext::StackTrace *originalStackTrace = NULL;
  978. const Js::JavascriptExceptionObject* originalExceptionObject = errorObject->GetJavascriptExceptionObject();
  979. if (!resetStack && errorObject->GetInternalProperty(errorObject, InternalPropertyIds::StackTrace, (Js::Var*) &originalStackTrace, NULL, &scriptContext) &&
  980. (originalStackTrace != nullptr))
  981. {
  982. exceptionContext.SetOriginalStackTrace(originalStackTrace);
  983. }
  984. else
  985. {
  986. if (originalExceptionObject != nullptr)
  987. {
  988. exceptionContext.SetOriginalStackTrace(originalExceptionObject->GetExceptionContext()->GetStackTrace());
  989. }
  990. }
  991. }
  992. do
  993. {
  994. JavascriptExceptionContext::StackFrame stackFrame(jsFunc, walker, crawlStackForWER);
  995. stackTrace->Add(stackFrame);
  996. } while (walker.GetDisplayCaller(&jsFunc) && i++ < stackCrawlLimit);
  997. }
  998. }
  999. END_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_INSCRIPT(hr);
  1000. if (stackTrace != nullptr)
  1001. {
  1002. exceptionContext.SetStackTrace(stackTrace);
  1003. DumpStackTrace(exceptionContext, isThrownException);
  1004. }
  1005. return caller;
  1006. }
  1007. // We might be trying to raise a stack overflow exception from the interpreter before
  1008. // we've executed code in the current script stack frame. In that case the current byte
  1009. // code offset is 0. In such cases walk to the caller's caller.
  1010. BOOL JavascriptExceptionOperators::GetCaller(JavascriptStackWalker& walker, _Out_opt_ JavascriptFunction*& jsFunc)
  1011. {
  1012. if (! walker.GetCaller(&jsFunc))
  1013. {
  1014. return FALSE;
  1015. }
  1016. if (! walker.GetCurrentInterpreterFrame() ||
  1017. walker.GetCurrentInterpreterFrame()->GetReader()->GetCurrentOffset() > 0)
  1018. {
  1019. return TRUE;
  1020. }
  1021. return walker.GetCaller(&jsFunc);
  1022. }
  1023. void JavascriptExceptionOperators::DumpStackTrace(JavascriptExceptionContext& exceptionContext, bool isThrownException)
  1024. {
  1025. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  1026. if (! exceptionContext.GetStackTrace()
  1027. || ! Configuration::Global.flags.Dump.IsEnabled(ExceptionStackTracePhase)
  1028. || ! isThrownException)
  1029. {
  1030. return;
  1031. }
  1032. Output::Print(_u("\nStack trace for thrown exception\n"));
  1033. JavascriptExceptionContext::StackTrace *stackTrace = exceptionContext.GetStackTrace();
  1034. for (int i=0; i < stackTrace->Count(); i++)
  1035. {
  1036. Js::JavascriptExceptionContext::StackFrame& currFrame = stackTrace->Item(i);
  1037. ULONG lineNumber = 0;
  1038. LONG characterPosition = 0;
  1039. if (currFrame.IsScriptFunction() && !currFrame.GetFunctionBody()->GetUtf8SourceInfo()->GetIsLibraryCode())
  1040. {
  1041. currFrame.GetFunctionBody()->GetLineCharOffset(currFrame.GetByteCodeOffset(), &lineNumber, &characterPosition);
  1042. }
  1043. Output::Print(_u(" %3d: %s (%d, %d)\n"), i, currFrame.GetFunctionName(), lineNumber, characterPosition);
  1044. }
  1045. Output::Flush();
  1046. #endif
  1047. }
  1048. /// ---------------------------------------------------------------------------------------------------
  1049. /// When allocators throw out of memory exception - scriptContext is NULL
  1050. /// ---------------------------------------------------------------------------------------------------
  1051. JavascriptExceptionObject * JavascriptExceptionOperators::GetOutOfMemoryExceptionObject(ScriptContext *scriptContext)
  1052. {
  1053. ThreadContext *threadContext = scriptContext ?
  1054. scriptContext->GetThreadContext() :
  1055. ThreadContext::GetContextForCurrentThread();
  1056. JavascriptExceptionObject *oomExceptionObject = threadContext->GetPendingOOMErrorObject();
  1057. Assert(oomExceptionObject);
  1058. return oomExceptionObject;
  1059. }
  1060. void JavascriptExceptionOperators::ThrowOutOfMemory(ScriptContext *scriptContext)
  1061. {
  1062. ThreadContext *threadContext = scriptContext ?
  1063. scriptContext->GetThreadContext() :
  1064. ThreadContext::GetContextForCurrentThread();
  1065. if (CONFIG_FLAG(EnableFatalErrorOnOOM) && !threadContext->TestThreadContextFlag(ThreadContextFlagDisableFatalOnOOM))
  1066. {
  1067. OutOfMemory_unrecoverable_error();
  1068. }
  1069. else
  1070. {
  1071. threadContext->ClearDisableImplicitFlags();
  1072. #if DBG
  1073. if (scriptContext)
  1074. {
  1075. ++scriptContext->oomExceptionCount;
  1076. }
  1077. else
  1078. {
  1079. ScriptContext* ctx = threadContext->GetScriptContextList();
  1080. while (ctx)
  1081. {
  1082. ++ctx->oomExceptionCount;
  1083. ctx = ctx->next;
  1084. }
  1085. }
  1086. #endif
  1087. JavascriptExceptionObject *oom = JavascriptExceptionOperators::GetOutOfMemoryExceptionObject(scriptContext);
  1088. JavascriptExceptionOperators::ThrowExceptionObject(oom, scriptContext);
  1089. }
  1090. }
  1091. void JavascriptExceptionOperators::ThrowStackOverflow(ScriptContext *scriptContext, PVOID returnAddress)
  1092. {
  1093. Assert(scriptContext);
  1094. DebugOnly(++scriptContext->soExceptionCount);
  1095. ThreadContext *threadContext = scriptContext->GetThreadContext();
  1096. JavascriptExceptionObject *so = threadContext->GetPendingSOErrorObject();
  1097. Assert(so);
  1098. // Disable implicit call before calling into recycler (to prevent QueryContinue/dispose from leave script and stack overflow again)
  1099. threadContext->DisableImplicitCall();
  1100. Var thrownObject = scriptContext->GetLibrary()->CreateStackOverflowError();
  1101. so->SetThrownObject(thrownObject);
  1102. // NOTE: Do not ClearDisableImplicitFlags() here. We still need to allocate StackTrace, etc. Keep implicit call disabled till actual
  1103. // throw (ThrowExceptionObjectInternal will ClearDisableImplicitFlags before throw). If anything wrong happens in between which throws
  1104. // a new exception, the new throw will ClearDisableImplicitFlags.
  1105. JavascriptExceptionOperators::ThrowExceptionObject(so, scriptContext, false, returnAddress);
  1106. }
  1107. void JavascriptExceptionOperators::ThrowExceptionObjectInternal(Js::JavascriptExceptionObject * exceptionObject, ScriptContext* scriptContext, bool fillExceptionContext, bool considerPassingToDebugger, PVOID returnAddress, bool resetStack)
  1108. {
  1109. if (scriptContext)
  1110. {
  1111. ThreadContext *threadContext = scriptContext->GetThreadContext();
  1112. #if ENABLE_JS_REENTRANCY_CHECK
  1113. threadContext->SetNoJsReentrancy(false);
  1114. #endif
  1115. if (fillExceptionContext)
  1116. {
  1117. Assert(exceptionObject);
  1118. JavascriptExceptionContext exceptionContext;
  1119. Var thrownObject = exceptionObject->GetThrownObject(nullptr);
  1120. WalkStackForExceptionContext(*scriptContext, exceptionContext, thrownObject, StackCrawlLimitOnThrow(thrownObject, *scriptContext), returnAddress, /*isThrownException=*/ true, resetStack);
  1121. exceptionObject->FillError(exceptionContext, scriptContext);
  1122. AddStackTraceToObject(thrownObject, exceptionContext.GetStackTrace(), *scriptContext, /*isThrownException=*/ true, resetStack);
  1123. }
  1124. Assert(// If we disabled implicit calls and we did record an implicit call, do not throw.
  1125. // Check your helper to see if a call recorded an implicit call that might cause an invalid value
  1126. !(
  1127. threadContext->IsDisableImplicitCall() &&
  1128. threadContext->GetImplicitCallFlags() & (~ImplicitCall_None)
  1129. ) ||
  1130. // Make sure we didn't disable exceptions
  1131. !threadContext->IsDisableImplicitException()
  1132. );
  1133. threadContext->ClearDisableImplicitFlags();
  1134. if (fillExceptionContext && considerPassingToDebugger)
  1135. {
  1136. DispatchExceptionToDebugger(exceptionObject, scriptContext);
  1137. }
  1138. }
  1139. if (exceptionObject->IsPendingExceptionObject())
  1140. {
  1141. ThreadContext * threadContext = scriptContext? scriptContext->GetThreadContext() : ThreadContext::GetContextForCurrentThread();
  1142. threadContext->SetHasThrownPendingException();
  1143. }
  1144. DoThrow(exceptionObject, scriptContext);
  1145. }
  1146. void JavascriptExceptionOperators::DoThrow(JavascriptExceptionObject* exceptionObject, ScriptContext* scriptContext)
  1147. {
  1148. ThreadContext* threadContext = scriptContext? scriptContext->GetThreadContext() : ThreadContext::GetContextForCurrentThread();
  1149. // Throw a wrapper JavascriptException. catch handler must GetAndClear() the exception object.
  1150. throw JavascriptException(threadContext, exceptionObject);
  1151. }
  1152. void JavascriptExceptionOperators::DoThrowCheckClone(JavascriptExceptionObject* exceptionObject, ScriptContext* scriptContext)
  1153. {
  1154. DoThrow(exceptionObject->CloneIfStaticExceptionObject(scriptContext), scriptContext);
  1155. }
  1156. void JavascriptExceptionOperators::DispatchExceptionToDebugger(Js::JavascriptExceptionObject * exceptionObject, ScriptContext* scriptContext)
  1157. {
  1158. Assert(exceptionObject != NULL);
  1159. Assert(scriptContext != NULL);
  1160. #ifdef ENABLE_SCRIPT_DEBUGGING
  1161. if (scriptContext->IsScriptContextInDebugMode()
  1162. && scriptContext->GetDebugContext()->GetProbeContainer()->HasAllowedForException(exceptionObject))
  1163. {
  1164. InterpreterHaltState haltState(STOP_EXCEPTIONTHROW, /*executingFunction*/nullptr);
  1165. haltState.exceptionObject = exceptionObject;
  1166. scriptContext->GetDebugContext()->GetProbeContainer()->DispatchExceptionBreakpoint(&haltState);
  1167. }
  1168. #endif
  1169. }
  1170. void JavascriptExceptionOperators::ThrowExceptionObject(Js::JavascriptExceptionObject * exceptionObject, ScriptContext* scriptContext, bool considerPassingToDebugger, PVOID returnAddress, bool resetStack)
  1171. {
  1172. ThrowExceptionObjectInternal(exceptionObject, scriptContext, true, considerPassingToDebugger, returnAddress, resetStack);
  1173. }
  1174. // The purpose of RethrowExceptionObject is to determine if ThrowExceptionObjectInternal should fill in the exception context.
  1175. //
  1176. // We pretty much always want to fill in the exception context when we throw an exception. The only case where we don't want to do it
  1177. // is if we are rethrowing and have the JavascriptExceptionObject from the previous throw with its exception context intact. If
  1178. // RethrowExceptionObject is passed a JavascriptExceptionObject with the function already there, that implies we have existing
  1179. // exception context and shouldn't step on it on the throw.
  1180. //
  1181. // RethrowExceptionObject is called only for cross-host calls. When throwing across host calls, we stash our internal JavascriptExceptionObject
  1182. // in the TLS. When we are throwing on the same thread (e.g. a throw from one frame to another), we can retrieve that stashed JavascriptExceptionObject
  1183. // from the TLS and rethrow it with its exception context intact, so we don't want to step on it. In other cases, e.g. when we throw across threads,
  1184. // we cannot retrieve the internal JavascriptExceptionObject from the TLS and have to create a new one. In this case, we need to fill the exception context.
  1185. //
  1186. void JavascriptExceptionOperators::RethrowExceptionObject(Js::JavascriptExceptionObject * exceptionObject, ScriptContext* scriptContext, bool considerPassingToDebugger)
  1187. {
  1188. ThrowExceptionObjectInternal(exceptionObject, scriptContext, ! exceptionObject->GetFunction(), considerPassingToDebugger, /*returnAddress=*/ nullptr, /*resetStack=*/ false);
  1189. }
  1190. // Trim the stack trace down to the amount specified for Error.stackTraceLimit. This happens when we do a full crawl for WER, but we only want to store the specified amount in the error object for consistency.
  1191. JavascriptExceptionContext::StackTrace* JavascriptExceptionOperators::TrimStackTraceForThrownObject(JavascriptExceptionContext::StackTrace* stackTraceIn, Var thrownObject, ScriptContext& scriptContext)
  1192. {
  1193. Assert(CrawlStackForWER(scriptContext)); // Don't trim if crawl for Error.stack
  1194. Assert(stackTraceIn);
  1195. int stackTraceLimit = static_cast<int>(GetStackTraceLimit(thrownObject, &scriptContext));
  1196. Assert(stackTraceLimit == 0 || IsErrorInstance(thrownObject));
  1197. if (stackTraceIn->Count() <= stackTraceLimit)
  1198. {
  1199. return stackTraceIn;
  1200. }
  1201. JavascriptExceptionContext::StackTrace* stackTraceTrimmed = NULL;
  1202. if (stackTraceLimit > 0)
  1203. {
  1204. HRESULT hr;
  1205. BEGIN_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_NESTED
  1206. {
  1207. stackTraceTrimmed = RecyclerNew(scriptContext.GetRecycler(), JavascriptExceptionContext::StackTrace, scriptContext.GetRecycler());
  1208. for (int i = 0; i < stackTraceLimit; i++)
  1209. {
  1210. stackTraceTrimmed->Add(stackTraceIn->Item(i));
  1211. }
  1212. }
  1213. END_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_INSCRIPT(hr);
  1214. }
  1215. // ignore OOM and just return what we can get
  1216. return stackTraceTrimmed;
  1217. }
  1218. //
  1219. // Check if thrownObject is instanceof Error (but not an Error prototype).
  1220. //
  1221. bool JavascriptExceptionOperators::IsErrorInstance(Var thrownObject)
  1222. {
  1223. if (thrownObject && JavascriptError::Is(thrownObject))
  1224. {
  1225. return !JavascriptError::FromVar(thrownObject)->IsPrototype();
  1226. }
  1227. if (thrownObject && RecyclableObject::Is(thrownObject))
  1228. {
  1229. RecyclableObject* obj = RecyclableObject::FromVar(thrownObject);
  1230. while (true)
  1231. {
  1232. obj = JavascriptOperators::GetPrototype(obj);
  1233. if (JavascriptOperators::GetTypeId(obj) == TypeIds_Null)
  1234. {
  1235. break;
  1236. }
  1237. if (JavascriptError::Is(obj))
  1238. {
  1239. return true;
  1240. }
  1241. }
  1242. }
  1243. return false;
  1244. }
  1245. void JavascriptExceptionOperators::AddStackTraceToObject(Var targetObject, JavascriptExceptionContext::StackTrace* stackTrace, ScriptContext& scriptContext, bool isThrownException, bool resetStack)
  1246. {
  1247. if (!stackTrace || !scriptContext.GetConfig()->IsErrorStackTraceEnabled())
  1248. {
  1249. return;
  1250. }
  1251. if (stackTrace->Count() == 0 && !IsErrorInstance(targetObject))
  1252. {
  1253. return;
  1254. }
  1255. if (isThrownException && CrawlStackForWER(scriptContext)) // Trim stack trace for WER
  1256. {
  1257. stackTrace = TrimStackTraceForThrownObject(stackTrace, targetObject, scriptContext);
  1258. if (!stackTrace)
  1259. {
  1260. return;
  1261. }
  1262. }
  1263. // If we still have stack trace to store and obj is a thrown exception object, obj must be an Error instance.
  1264. Assert(!isThrownException || IsErrorInstance(targetObject));
  1265. RecyclableObject* obj = RecyclableObject::FromVar(targetObject);
  1266. if (!resetStack && obj->HasProperty(PropertyIds::stack))
  1267. {
  1268. return; // we don't want to overwrite an existing "stack" property
  1269. }
  1270. JavascriptFunction* accessor = scriptContext.GetLibrary()->GetStackTraceAccessorFunction();
  1271. PropertyDescriptor stackPropertyDescriptor;
  1272. stackPropertyDescriptor.SetSetter(accessor);
  1273. stackPropertyDescriptor.SetGetter(accessor);
  1274. stackPropertyDescriptor.SetConfigurable(true);
  1275. stackPropertyDescriptor.SetEnumerable(false);
  1276. HRESULT hr;
  1277. BEGIN_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_NESTED
  1278. {
  1279. if (JavascriptOperators::DefineOwnPropertyDescriptor(obj, PropertyIds::stack, stackPropertyDescriptor, false, &scriptContext))
  1280. {
  1281. obj->SetInternalProperty(InternalPropertyIds::StackTrace, stackTrace, PropertyOperationFlags::PropertyOperation_None, NULL);
  1282. obj->SetInternalProperty(InternalPropertyIds::StackTraceCache, NULL, PropertyOperationFlags::PropertyOperation_None, NULL);
  1283. }
  1284. }
  1285. END_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_INSCRIPT(hr)
  1286. }
  1287. Var JavascriptExceptionOperators::OP_RuntimeTypeError(MessageId messageId, ScriptContext *scriptContext)
  1288. {
  1289. JavascriptError::ThrowTypeError(scriptContext, MAKE_HR(messageId));
  1290. }
  1291. Var JavascriptExceptionOperators::OP_RuntimeRangeError(MessageId messageId, ScriptContext *scriptContext)
  1292. {
  1293. JavascriptError::ThrowRangeError(scriptContext, MAKE_HR(messageId));
  1294. }
  1295. Var JavascriptExceptionOperators::OP_WebAssemblyRuntimeError(MessageId messageId, ScriptContext *scriptContext)
  1296. {
  1297. JavascriptError::ThrowWebAssemblyRuntimeError(scriptContext, MAKE_HR(messageId));
  1298. }
  1299. Var JavascriptExceptionOperators::OP_RuntimeReferenceError(MessageId messageId, ScriptContext *scriptContext)
  1300. {
  1301. JavascriptError::ThrowReferenceError(scriptContext, MAKE_HR(messageId));
  1302. }
  1303. // Throw type error on access 'arguments', 'callee' or 'caller' when in a restricted context
  1304. Var JavascriptExceptionOperators::ThrowTypeErrorRestrictedPropertyAccessor(RecyclableObject* function, CallInfo callInfo, ...)
  1305. {
  1306. JavascriptError::ThrowTypeError(function->GetScriptContext(), JSERR_AccessRestrictedProperty);
  1307. }
  1308. Var JavascriptExceptionOperators::StackTraceAccessor(RecyclableObject* function, CallInfo callInfo, ...)
  1309. {
  1310. ARGUMENTS(args, callInfo);
  1311. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  1312. ScriptContext *scriptContext = function->GetScriptContext();
  1313. AnalysisAssert(scriptContext);
  1314. // If the first argument to the accessor is not a recyclable object, return undefined
  1315. // for compat with other browsers
  1316. if (!RecyclableObject::Is(args[0]))
  1317. {
  1318. return scriptContext->GetLibrary()->GetUndefined();
  1319. }
  1320. RecyclableObject *obj = RecyclableObject::FromVar(args[0]);
  1321. // If an argument was passed to the accessor, it is being called as a setter.
  1322. // Set the internal StackTraceCache property accordingly.
  1323. if (args.Info.Count > 1)
  1324. {
  1325. obj->SetInternalProperty(InternalPropertyIds::StackTraceCache, args[1], PropertyOperationFlags::PropertyOperation_None, NULL);
  1326. if (JavascriptError::Is(obj))
  1327. {
  1328. ((JavascriptError *)obj)->SetStackPropertyRedefined(true);
  1329. }
  1330. return scriptContext->GetLibrary()->GetEmptyString();
  1331. }
  1332. // Otherwise, the accessor is being called as a getter.
  1333. // Return existing cached value, or obtain the string representation of the StackTrace to return.
  1334. Var cache = NULL;
  1335. if (obj->GetInternalProperty(obj,InternalPropertyIds::StackTraceCache, (Var*)&cache, NULL, scriptContext) && cache)
  1336. {
  1337. return cache;
  1338. }
  1339. JavascriptString* stringMessage = scriptContext->GetLibrary()->GetEmptyString();
  1340. HRESULT hr;
  1341. BEGIN_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_NESTED
  1342. {
  1343. Js::JavascriptExceptionContext::StackTrace *stackTrace = NULL;
  1344. if (!obj->GetInternalProperty(obj,InternalPropertyIds::StackTrace, (Js::Var*) &stackTrace, NULL, scriptContext) ||
  1345. stackTrace == nullptr)
  1346. {
  1347. obj->SetInternalProperty(InternalPropertyIds::StackTraceCache, stringMessage, PropertyOperationFlags::PropertyOperation_None, NULL);
  1348. return stringMessage;
  1349. }
  1350. if (IsErrorInstance(obj))
  1351. {
  1352. stringMessage = JavascriptConversion::ToString(obj, scriptContext);
  1353. }
  1354. CompoundString *const stringBuilder = CompoundString::NewWithCharCapacity(40, scriptContext->GetLibrary());
  1355. stringBuilder->AppendChars(stringMessage);
  1356. for (int i = 0; i < stackTrace->Count(); i++)
  1357. {
  1358. Js::JavascriptExceptionContext::StackFrame& currentFrame = stackTrace->Item(i);
  1359. // Defend in depth. Discard cross domain frames if somehow they creped in.
  1360. if (currentFrame.IsScriptFunction())
  1361. {
  1362. ScriptContext* funcScriptContext = currentFrame.GetFunctionBody()->GetScriptContext();
  1363. AnalysisAssert(funcScriptContext);
  1364. if (scriptContext != funcScriptContext && FAILED(scriptContext->GetHostScriptContext()->CheckCrossDomainScriptContext(funcScriptContext)))
  1365. {
  1366. continue; // Ignore this frame
  1367. }
  1368. }
  1369. FunctionBody* functionBody = currentFrame.GetFunctionBody();
  1370. const bool isLibraryCode = !functionBody || functionBody->GetUtf8SourceInfo()->GetIsLibraryCode();
  1371. if (isLibraryCode)
  1372. {
  1373. AppendLibraryFrameToStackTrace(stringBuilder, currentFrame.GetFunctionName());
  1374. }
  1375. else
  1376. {
  1377. LPCWSTR pUrl = NULL;
  1378. ULONG lineNumber = 0;
  1379. LONG characterPosition = 0;
  1380. functionBody->GetLineCharOffset(currentFrame.GetByteCodeOffset(), &lineNumber, &characterPosition);
  1381. pUrl = functionBody->GetSourceName();
  1382. LPCWSTR functionName = nullptr;
  1383. if (CONFIG_FLAG(ExtendedErrorStackForTestHost))
  1384. {
  1385. BEGIN_LEAVE_SCRIPT_INTERNAL(scriptContext)
  1386. {
  1387. if (currentFrame.GetFunctionNameWithArguments(&functionName) != S_OK)
  1388. {
  1389. functionName = functionBody->GetExternalDisplayName();
  1390. }
  1391. }
  1392. END_LEAVE_SCRIPT_INTERNAL(scriptContext)
  1393. }
  1394. else
  1395. {
  1396. functionName = functionBody->GetExternalDisplayName();
  1397. }
  1398. AppendExternalFrameToStackTrace(stringBuilder, functionName, pUrl ? pUrl : _u(""), lineNumber + 1, characterPosition + 1);
  1399. }
  1400. }
  1401. // Try to create the string object even if we did OOM, but if can't, just return what we've got. We catch and ignore OOM so it doesn't propagate up.
  1402. // With all the stack trace functionality, we do best effort to produce the stack trace in the case of OOM, but don't want it to trigger an OOM. Idea is if do take
  1403. // an OOM, have some chance of producing a stack trace to see where it happened.
  1404. stringMessage = stringBuilder;
  1405. }
  1406. END_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_INSCRIPT(hr);
  1407. obj->SetInternalProperty(InternalPropertyIds::StackTraceCache, stringMessage, PropertyOperationFlags::PropertyOperation_None, NULL);
  1408. return stringMessage;
  1409. }
  1410. uint64 JavascriptExceptionOperators::GetStackTraceLimit(Var thrownObject, ScriptContext* scriptContext)
  1411. {
  1412. uint64 limit = 0;
  1413. if (scriptContext->GetConfig()->IsErrorStackTraceEnabled()
  1414. && IsErrorInstance(thrownObject))
  1415. {
  1416. HRESULT hr = JavascriptError::GetRuntimeError(RecyclableObject::FromVar(thrownObject), NULL);
  1417. JavascriptFunction* error = scriptContext->GetLibrary()->GetErrorConstructor();
  1418. // If we are throwing StackOverflow and Error.stackTraceLimit is a custom getter, we can't make the getter
  1419. // call as we don't have stack space. Just bail out without stack trace in such case. Only proceed to get
  1420. // Error.stackTraceLimit property if we are not throwing StackOverflow, or there is no implicitCall (in getter case).
  1421. DisableImplicitFlags disableImplicitFlags = scriptContext->GetThreadContext()->GetDisableImplicitFlags();
  1422. if (hr == VBSERR_OutOfStack)
  1423. {
  1424. scriptContext->GetThreadContext()->SetDisableImplicitFlags(DisableImplicitCallAndExceptionFlag);
  1425. }
  1426. Var var = nullptr;
  1427. if (JavascriptOperators::GetPropertyNoCache(error, PropertyIds::stackTraceLimit, &var, scriptContext))
  1428. {
  1429. // Only accept the value if it is a "Number". Avoid potential valueOf() call.
  1430. switch (JavascriptOperators::GetTypeId(var))
  1431. {
  1432. case TypeIds_Integer:
  1433. case TypeIds_Number:
  1434. case TypeIds_Int64Number:
  1435. case TypeIds_UInt64Number:
  1436. double value = JavascriptConversion::ToNumber(var, scriptContext);
  1437. limit = JavascriptNumber::IsNan(value) ? 0 :
  1438. (NumberUtilities::IsFinite(value) ? JavascriptConversion::ToUInt32(var, scriptContext) : MaxStackTraceLimit);
  1439. break;
  1440. }
  1441. }
  1442. if (hr == VBSERR_OutOfStack)
  1443. {
  1444. scriptContext->GetThreadContext()->SetDisableImplicitFlags(disableImplicitFlags);
  1445. }
  1446. }
  1447. return limit;
  1448. }
  1449. void JavascriptExceptionOperators::AppendExternalFrameToStackTrace(CompoundString* bs, LPCWSTR functionName, LPCWSTR fileName, ULONG lineNumber, LONG characterPosition)
  1450. {
  1451. // format is equivalent to wprintf("\n at %s (%s:%d:%d)", functionName, filename, lineNumber, characterPosition);
  1452. const CharCount maxULongStringLength = 10; // excluding null terminator
  1453. const auto ConvertULongToString = [](const ULONG value, char16 *const buffer, const CharCount charCapacity)
  1454. {
  1455. const errno_t err = _ultow_s(value, buffer, charCapacity, 10);
  1456. Assert(err == 0);
  1457. };
  1458. if (CONFIG_FLAG(ExtendedErrorStackForTestHost))
  1459. {
  1460. bs->AppendChars(_u("\n\tat "));
  1461. }
  1462. else
  1463. {
  1464. bs->AppendChars(_u("\n at "));
  1465. }
  1466. bs->AppendCharsSz(functionName);
  1467. bs->AppendChars(_u(" ("));
  1468. if (CONFIG_FLAG(ExtendedErrorStackForTestHost) && *fileName != _u('\0'))
  1469. {
  1470. char16 shortfilename[_MAX_FNAME];
  1471. char16 ext[_MAX_EXT];
  1472. errno_t err = _wsplitpath_s(fileName, NULL, 0, NULL, 0, shortfilename, _MAX_FNAME, ext, _MAX_EXT);
  1473. if (err != 0)
  1474. {
  1475. bs->AppendCharsSz(fileName);
  1476. }
  1477. else
  1478. {
  1479. bs->AppendCharsSz(shortfilename);
  1480. bs->AppendCharsSz(ext);
  1481. }
  1482. }
  1483. else
  1484. {
  1485. bs->AppendCharsSz(fileName);
  1486. }
  1487. bs->AppendChars(_u(':'));
  1488. bs->AppendChars(lineNumber, maxULongStringLength, ConvertULongToString);
  1489. bs->AppendChars(_u(':'));
  1490. bs->AppendChars(characterPosition, maxULongStringLength, ConvertULongToString);
  1491. bs->AppendChars(_u(')'));
  1492. }
  1493. void JavascriptExceptionOperators::AppendLibraryFrameToStackTrace(CompoundString* bs, LPCWSTR functionName)
  1494. {
  1495. // format is equivalent to wprintf("\n at %s (native code)", functionName);
  1496. bs->AppendChars(_u("\n at "));
  1497. bs->AppendCharsSz(functionName);
  1498. bs->AppendChars(_u(" (native code)"));
  1499. }
  1500. } // namespace Js