JavascriptExceptionOperators.cpp 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272
  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 "shlwapi.h"
  7. #include "Language\InterpreterStackFrame.h"
  8. #ifdef _M_IX86
  9. #ifdef _CONTROL_FLOW_GUARD
  10. extern "C" PVOID __guard_check_icall_fptr;
  11. #endif
  12. #endif
  13. namespace Js
  14. {
  15. void JavascriptExceptionOperators::AutoCatchHandlerExists::FetchNonUserCodeStatus(ScriptContext * scriptContext)
  16. {
  17. Assert(scriptContext);
  18. bool fFound = false;
  19. // If the outer try catch was already in the user code, no need to go any further.
  20. if (!m_previousCatchHandlerToUserCodeStatus)
  21. {
  22. Js::JavascriptFunction* caller;
  23. if (JavascriptStackWalker::GetCaller(&caller, scriptContext))
  24. {
  25. Js::FunctionBody *funcBody = NULL;
  26. if (caller != NULL && (funcBody = caller->GetFunctionBody()) != NULL)
  27. {
  28. m_threadContext->SetIsUserCode(funcBody->IsNonUserCode() == false);
  29. fFound = true;
  30. }
  31. }
  32. }
  33. if (!fFound)
  34. {
  35. // If not successfully able to find the caller, set this catch handler belongs to the user code.
  36. m_threadContext->SetIsUserCode(true);
  37. }
  38. }
  39. JavascriptExceptionOperators::AutoCatchHandlerExists::AutoCatchHandlerExists(ScriptContext* scriptContext)
  40. {
  41. Assert(scriptContext);
  42. m_threadContext = scriptContext->GetThreadContext();
  43. Assert(m_threadContext);
  44. m_previousCatchHandlerExists = m_threadContext->HasCatchHandler();
  45. m_threadContext->SetHasCatchHandler(TRUE);
  46. m_previousCatchHandlerToUserCodeStatus = m_threadContext->IsUserCode();
  47. if (scriptContext->IsInDebugMode())
  48. {
  49. FetchNonUserCodeStatus(scriptContext);
  50. }
  51. }
  52. JavascriptExceptionOperators::AutoCatchHandlerExists::~AutoCatchHandlerExists()
  53. {
  54. m_threadContext->SetHasCatchHandler(m_previousCatchHandlerExists);
  55. m_threadContext->SetIsUserCode(m_previousCatchHandlerToUserCodeStatus);
  56. }
  57. bool JavascriptExceptionOperators::CrawlStackForWER(Js::ScriptContext& scriptContext)
  58. {
  59. return Js::Configuration::Global.flags.WERExceptionSupport && !scriptContext.GetThreadContext()->HasCatchHandler();
  60. }
  61. uint64 JavascriptExceptionOperators::StackCrawlLimitOnThrow(Var thrownObject, ScriptContext& scriptContext)
  62. {
  63. return CrawlStackForWER(scriptContext) ? MaxStackTraceLimit : GetStackTraceLimit(thrownObject, &scriptContext);
  64. }
  65. #ifdef _M_X64
  66. void *JavascriptExceptionOperators::OP_TryCatch(void *tryAddr,
  67. void *catchAddr,
  68. void *frame,
  69. size_t spillSize,
  70. size_t argsSize,
  71. int hasBailedOutOffset,
  72. ScriptContext *scriptContext)
  73. {
  74. void *continuation = nullptr;
  75. JavascriptExceptionObject *exception = nullptr;
  76. PROBE_STACK(scriptContext, Constants::MinStackDefault + spillSize + argsSize);
  77. try
  78. {
  79. Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext);
  80. continuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize);
  81. }
  82. catch (JavascriptExceptionObject *caughtException)
  83. {
  84. exception = caughtException;
  85. }
  86. if (exception)
  87. {
  88. exception = exception->CloneIfStaticExceptionObject(scriptContext);
  89. bool hasBailedOut = *(bool*)((char*)frame + hasBailedOutOffset); // stack offsets are negative
  90. if (hasBailedOut)
  91. {
  92. // If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
  93. // it so happens that this catch was on the stack and caught the exception.
  94. // Re-throw!
  95. throw exception;
  96. }
  97. Var exceptionObject = exception->GetThrownObject(scriptContext);
  98. AssertMsg(exceptionObject, "Caught object is null.");
  99. continuation = amd64_CallWithFakeFrame(catchAddr, frame, spillSize, argsSize, exceptionObject);
  100. }
  101. return continuation;
  102. }
  103. void *JavascriptExceptionOperators::OP_TryFinally(void *tryAddr,
  104. void *finallyAddr,
  105. void *frame,
  106. size_t spillSize,
  107. size_t argsSize,
  108. ScriptContext *scriptContext)
  109. {
  110. void *tryContinuation = nullptr;
  111. void *finallyContinuation = nullptr;
  112. JavascriptExceptionObject *exception = nullptr;
  113. PROBE_STACK(scriptContext, Constants::MinStackDefault + spillSize + argsSize);
  114. try
  115. {
  116. tryContinuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize);
  117. }
  118. catch (JavascriptExceptionObject *caughtException)
  119. {
  120. exception = caughtException;
  121. }
  122. if (exception)
  123. {
  124. // Clone static exception object early in case finally block overwrites it
  125. exception = exception->CloneIfStaticExceptionObject(scriptContext);
  126. }
  127. finallyContinuation = amd64_CallWithFakeFrame(finallyAddr, frame, spillSize, argsSize);
  128. if (finallyContinuation)
  129. {
  130. return finallyContinuation;
  131. }
  132. if (exception)
  133. {
  134. throw exception;
  135. }
  136. return tryContinuation;
  137. }
  138. #elif defined(_M_ARM32_OR_ARM64)
  139. void *JavascriptExceptionOperators::OP_TryCatch(
  140. void *tryAddr,
  141. void *catchAddr,
  142. void *framePtr,
  143. void *localsPtr,
  144. size_t argsSize,
  145. int hasBailedOutOffset,
  146. ScriptContext *scriptContext)
  147. {
  148. void *continuation = nullptr;
  149. JavascriptExceptionObject *exception = nullptr;
  150. PROBE_STACK(scriptContext, Constants::MinStackDefault + argsSize);
  151. try
  152. {
  153. Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext);
  154. #if defined(_M_ARM)
  155. continuation = arm_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize);
  156. #elif defined(_M_ARM64)
  157. continuation = arm64_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize);
  158. #endif
  159. }
  160. catch (JavascriptExceptionObject *caughtException)
  161. {
  162. exception = caughtException;
  163. }
  164. if (exception)
  165. {
  166. exception = exception->CloneIfStaticExceptionObject(scriptContext);
  167. bool hasBailedOut = *(bool*)((char*)localsPtr + hasBailedOutOffset); // stack offsets are sp relative
  168. if (hasBailedOut)
  169. {
  170. // If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
  171. // it so happens that this catch was on the stack and caught the exception.
  172. // Re-throw!
  173. throw exception;
  174. }
  175. Var exceptionObject = exception->GetThrownObject(scriptContext);
  176. AssertMsg(exceptionObject, "Caught object is null.");
  177. #if defined(_M_ARM)
  178. continuation = arm_CallCatch(catchAddr, framePtr, localsPtr, argsSize, exceptionObject);
  179. #elif defined(_M_ARM64)
  180. continuation = arm64_CallCatch(catchAddr, framePtr, localsPtr, argsSize, exceptionObject);
  181. #endif
  182. }
  183. return continuation;
  184. }
  185. void *JavascriptExceptionOperators::OP_TryFinally(
  186. void *tryAddr,
  187. void *finallyAddr,
  188. void *framePtr,
  189. void *localsPtr,
  190. size_t argsSize,
  191. ScriptContext *scriptContext)
  192. {
  193. void *tryContinuation = nullptr;
  194. void *finallyContinuation = nullptr;
  195. JavascriptExceptionObject *exception = nullptr;
  196. PROBE_STACK(scriptContext, Constants::MinStackDefault + argsSize);
  197. try
  198. {
  199. #if defined(_M_ARM)
  200. tryContinuation = arm_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize);
  201. #elif defined(_M_ARM64)
  202. tryContinuation = arm64_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize);
  203. #endif
  204. }
  205. catch (JavascriptExceptionObject *caughtException)
  206. {
  207. exception = caughtException;
  208. }
  209. if (exception)
  210. {
  211. // Clone static exception object early in case finally block overwrites it
  212. exception = exception->CloneIfStaticExceptionObject(scriptContext);
  213. }
  214. #if defined(_M_ARM)
  215. finallyContinuation = arm_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize);
  216. #elif defined(_M_ARM64)
  217. finallyContinuation = arm64_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize);
  218. #endif
  219. if (finallyContinuation)
  220. {
  221. return finallyContinuation;
  222. }
  223. if (exception)
  224. {
  225. throw exception;
  226. }
  227. return tryContinuation;
  228. }
  229. #else
  230. #pragma warning(push)
  231. #pragma warning(disable:4731) // frame pointer register 'ebp' modified by inline assembly code
  232. void* JavascriptExceptionOperators::OP_TryCatch(void* tryAddr, void* handlerAddr, void* framePtr, int hasBailedOutOffset, ScriptContext *scriptContext)
  233. {
  234. void* continuationAddr = NULL;
  235. Js::JavascriptExceptionObject* pExceptionObject = NULL;
  236. PROBE_STACK(scriptContext, Constants::MinStackDefault);
  237. try
  238. {
  239. Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext);
  240. // Adjust the frame pointer and call into the try.
  241. // If the try completes without raising an exception, it will pass back the continuation address.
  242. // Bug in compiler optimizer: try-catch can be optimized away if the try block contains __asm calls into function
  243. // that may throw. The current workaround is to add the following dummy throw to prevent this optimization.
  244. if (!tryAddr)
  245. {
  246. Js::Throw::InternalError();
  247. }
  248. #ifdef _M_IX86
  249. void *savedEsp;
  250. __asm
  251. {
  252. // Save and restore the callee-saved registers around the call.
  253. // TODO: track register kills by region and generate per-region prologs and epilogs
  254. push esi
  255. push edi
  256. push ebx
  257. // 8-byte align frame to improve floating point perf of our JIT'd code.
  258. // Save ESP
  259. mov ecx, esp
  260. mov savedEsp, ecx
  261. and esp, -8
  262. // Set up the call target, save the current frame ptr, and adjust the frame to access
  263. // locals in native code.
  264. mov eax, tryAddr
  265. #if 0 && defined(_CONTROL_FLOW_GUARD)
  266. // verify that the call target is valid
  267. mov ebx, eax ; save call target
  268. mov ecx, eax
  269. call [__guard_check_icall_fptr]
  270. mov eax, ebx ; restore call target
  271. #endif
  272. push ebp
  273. mov ebp, framePtr
  274. call eax
  275. pop ebp
  276. // The native code gives us the address where execution should continue on exit
  277. // from the region.
  278. mov continuationAddr, eax
  279. // Restore ESP
  280. mov ecx, savedEsp
  281. mov esp, ecx
  282. pop ebx
  283. pop edi
  284. pop esi
  285. }
  286. #else
  287. AssertMsg(FALSE, "Unsupported native try-catch handler");
  288. #endif
  289. }
  290. catch(Js::JavascriptExceptionObject * exceptionObject)
  291. {
  292. pExceptionObject = exceptionObject;
  293. }
  294. // Let's run user catch handler code only after the stack has been unwound.
  295. if(pExceptionObject)
  296. {
  297. pExceptionObject = pExceptionObject->CloneIfStaticExceptionObject(scriptContext);
  298. bool hasBailedOut = *(bool*)((char*)framePtr + hasBailedOutOffset); // stack offsets are negative
  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. throw pExceptionObject;
  305. }
  306. Var catchObject = pExceptionObject->GetThrownObject(scriptContext);
  307. AssertMsg(catchObject, "Caught object is NULL");
  308. #ifdef _M_IX86
  309. void *savedEsp;
  310. __asm
  311. {
  312. // Save and restore the callee-saved registers around the call.
  313. // TODO: track register kills by region and generate per-region prologs and epilogs
  314. push esi
  315. push edi
  316. push ebx
  317. // 8-byte align frame to improve floating point perf of our JIT'd code.
  318. // Save ESP
  319. mov ecx, esp
  320. mov savedEsp, ecx
  321. and esp, -8
  322. // Set up the call target
  323. mov ecx, handlerAddr
  324. #if 0 && defined(_CONTROL_FLOW_GUARD)
  325. // verify that the call target is valid
  326. mov ebx, ecx ; save call target
  327. call [__guard_check_icall_fptr]
  328. mov ecx, ebx ; restore call target
  329. #endif
  330. // Set up catch object, save the current frame ptr, and adjust the frame to access
  331. // locals in native code.
  332. mov eax, catchObject
  333. push ebp
  334. mov ebp, framePtr
  335. call ecx
  336. pop ebp
  337. // The native code gives us the address where execution should continue on exit
  338. // from the region.
  339. mov continuationAddr, eax
  340. // Restore ESP
  341. mov ecx, savedEsp
  342. mov esp, ecx
  343. pop ebx
  344. pop edi
  345. pop esi
  346. }
  347. #else
  348. AssertMsg(FALSE, "Unsupported native try-catch handler");
  349. #endif
  350. }
  351. return continuationAddr;
  352. }
  353. void* JavascriptExceptionOperators::OP_TryFinally(void* tryAddr, void* handlerAddr, void* framePtr, ScriptContext *scriptContext)
  354. {
  355. Js::JavascriptExceptionObject* pExceptionObject = NULL;
  356. void* continuationAddr = NULL;
  357. PROBE_STACK(scriptContext, Constants::MinStackDefault);
  358. try
  359. {
  360. // Bug in compiler optimizer: try-catch can be optimized away if the try block contains __asm calls into function
  361. // that may throw. The current workaround is to add the following dummy throw to prevent this optimization.
  362. // It seems like compiler got smart and still optimizes if the exception is not JavascriptExceptionObject (see catch handler below).
  363. // In order to circumvent that we are throwing OutOfMemory.
  364. if (!tryAddr)
  365. {
  366. Assert(false);
  367. ThrowOutOfMemory(scriptContext);
  368. }
  369. #ifdef _M_IX86
  370. void *savedEsp;
  371. __asm
  372. {
  373. // Save and restore the callee-saved registers around the call.
  374. // TODO: track register kills by region and generate per-region prologs and epilogs
  375. push esi
  376. push edi
  377. push ebx
  378. // 8-byte align frame to improve floating point perf of our JIT'd code.
  379. // Save ESP
  380. mov ecx, esp
  381. mov savedEsp, ecx
  382. and esp, -8
  383. // Set up the call target, save the current frame ptr, and adjust the frame to access
  384. // locals in native code.
  385. mov eax, tryAddr
  386. #if 0 && defined(_CONTROL_FLOW_GUARD)
  387. // verify that the call target is valid
  388. mov ebx, eax ; save call target
  389. mov ecx, eax
  390. call [__guard_check_icall_fptr]
  391. mov eax, ebx ; restore call target
  392. #endif
  393. push ebp
  394. mov ebp, framePtr
  395. call eax
  396. pop ebp
  397. // The native code gives us the address where execution should continue on exit
  398. // from the region.
  399. mov continuationAddr, eax
  400. // Restore ESP
  401. mov ecx, savedEsp
  402. mov esp, ecx
  403. pop ebx
  404. pop edi
  405. pop esi
  406. }
  407. #else
  408. AssertMsg(FALSE, "Unsupported native try-finally handler");
  409. #endif
  410. }
  411. catch(Js::JavascriptExceptionObject* e)
  412. {
  413. pExceptionObject = e;
  414. }
  415. if (pExceptionObject)
  416. {
  417. // Clone static exception object early in case finally block overwrites it
  418. pExceptionObject = pExceptionObject->CloneIfStaticExceptionObject(scriptContext);
  419. }
  420. void* newContinuationAddr = NULL;
  421. #ifdef _M_IX86
  422. void *savedEsp;
  423. __asm
  424. {
  425. // Save and restore the callee-saved registers around the call.
  426. // TODO: track register kills by region and generate per-region prologs and epilogs
  427. push esi
  428. push edi
  429. push ebx
  430. // 8-byte align frame to improve floating point perf of our JIT'd code.
  431. // Save ESP
  432. mov ecx, esp
  433. mov savedEsp, ecx
  434. and esp, -8
  435. // Set up the call target
  436. mov eax, handlerAddr
  437. #if 0 && defined(_CONTROL_FLOW_GUARD)
  438. // verify that the call target is valid
  439. mov ebx, eax ; save call target
  440. mov ecx, eax
  441. call [__guard_check_icall_fptr]
  442. mov eax, ebx ; restore call target
  443. #endif
  444. // save the current frame ptr, and adjust the frame to access
  445. // locals in native code.
  446. push ebp
  447. mov ebp, framePtr
  448. call eax
  449. pop ebp
  450. // The native code gives us the address where execution should continue on exit
  451. // from the finally, but only if flow leaves the finally before it completes.
  452. mov newContinuationAddr, eax
  453. // Restore ESP
  454. mov ecx, savedEsp
  455. mov esp, ecx
  456. pop ebx
  457. pop edi
  458. pop esi
  459. }
  460. #else
  461. AssertMsg(FALSE, "Unsupported native try-finally handler");
  462. #endif
  463. if (newContinuationAddr != NULL)
  464. {
  465. // Non-null return value from the finally indicates that the finally seized the flow
  466. // with a jump/return out of the region. Continue at that address instead of handling
  467. // the exception.
  468. return newContinuationAddr;
  469. }
  470. if (pExceptionObject)
  471. {
  472. throw pExceptionObject;
  473. }
  474. return continuationAddr;
  475. }
  476. #endif
  477. void __declspec(noreturn) JavascriptExceptionOperators::OP_Throw(Var object, ScriptContext* scriptContext)
  478. {
  479. Throw(object, scriptContext);
  480. }
  481. #if defined(DBG) && defined(_M_IX86)
  482. extern "C" void * _except_handler4;
  483. void JavascriptExceptionOperators::DbgCheckEHChain()
  484. {
  485. #if 0
  486. // This debug check is disabled until we figure out how to trace a fs:0 chain if we throw from inside
  487. // a finally.
  488. void *currentFS0;
  489. ThreadContext * threadContext = ThreadContext::GetContextForCurrentThread();
  490. if (!threadContext->IsScriptActive())
  491. {
  492. return;
  493. }
  494. // Walk the FS:0 chain of exception handlers, until the FS:0 handler in CallRootFunction.
  495. // We should only see SEH frames on the way.
  496. // We do allow C++ EH frames as long as there is no active objects (state = -1).
  497. // That's because we may see frames that have calls to new(). This introduces an EH frame
  498. // to call delete if the constructor throws. Our constructors shouldn't throw, so we should be fine.
  499. currentFS0 = (void*)__readfsdword(0);
  500. while (currentFS0 != threadContext->callRootFS0)
  501. {
  502. // EH struct:
  503. // void * next;
  504. // void * handler;
  505. // int state;
  506. AssertMsg(*((void**)currentFS0 + 1) == &_except_handler4
  507. || *((int*)currentFS0 + 2) == -1, "Found a non SEH exception frame on stack");
  508. currentFS0 = *(void**)currentFS0;
  509. }
  510. #endif
  511. }
  512. #endif
  513. void JavascriptExceptionOperators::Throw(Var object, ScriptContext * scriptContext)
  514. {
  515. #if defined(DBG) && defined(_M_IX86)
  516. DbgCheckEHChain();
  517. #endif
  518. Assert(scriptContext != nullptr);
  519. // TODO: FastDOM Trampolines will throw JS Exceptions but are not isScriptActive
  520. //AssertMsg(scriptContext->GetThreadContext()->IsScriptActive() ||
  521. // (JavascriptError::Is(object) && (JavascriptError::FromVar(object))->IsExternalError()),
  522. // "Javascript exception raised when script is not active");
  523. AssertMsg(scriptContext->GetThreadContext()->IsInScript() ||
  524. (JavascriptError::Is(object) && (JavascriptError::FromVar(object))->IsExternalError()),
  525. "Javascript exception raised without being in CallRootFunction");
  526. JavascriptError *javascriptError = nullptr;
  527. if (JavascriptError::Is(object))
  528. {
  529. // We keep track of the JavascriptExceptionObject that was created when this error
  530. // was first thrown so that we can always get the correct metadata.
  531. javascriptError = JavascriptError::FromVar(object);
  532. JavascriptExceptionObject *exceptionObject = javascriptError->GetJavascriptExceptionObject();
  533. if (exceptionObject)
  534. {
  535. JavascriptExceptionOperators::ThrowExceptionObject(exceptionObject, scriptContext, true);
  536. }
  537. }
  538. JavascriptExceptionObject * exceptionObject =
  539. RecyclerNew(scriptContext->GetRecycler(), JavascriptExceptionObject, object, scriptContext, NULL);
  540. bool resetStack = false;
  541. if (javascriptError)
  542. {
  543. if (!javascriptError->IsStackPropertyRedefined())
  544. {
  545. /*
  546. Throwing an error object. Original stack property will be pointing to the stack created at time of Error constructor.
  547. Reset the stack property to match IE11 behavior
  548. */
  549. resetStack = true;
  550. }
  551. javascriptError->SetJavascriptExceptionObject(exceptionObject);
  552. }
  553. JavascriptExceptionOperators::ThrowExceptionObject(exceptionObject, scriptContext, /*considerPassingToDebugger=*/ true, /*returnAddress=*/ nullptr, resetStack);
  554. }
  555. void
  556. JavascriptExceptionOperators::WalkStackForExceptionContext(ScriptContext& scriptContext, JavascriptExceptionContext& exceptionContext, Var thrownObject, uint64 stackCrawlLimit, PVOID returnAddress, bool isThrownException, bool resetSatck)
  557. {
  558. uint32 callerBytecodeOffset;
  559. JavascriptFunction * jsFunc = WalkStackForExceptionContextInternal(scriptContext, exceptionContext, thrownObject, callerBytecodeOffset, stackCrawlLimit, returnAddress, isThrownException, resetSatck);
  560. if (jsFunc)
  561. {
  562. // If found, the caller is a function, and we can retrieve the debugger info from there
  563. // otherwise it's probably just accessing property. While it is still possible to throw
  564. // from that context, we just won't be able to get the line number etc., which make sense.
  565. exceptionContext.SetThrowingFunction(jsFunc, callerBytecodeOffset, returnAddress);
  566. }
  567. }
  568. JavascriptFunction *
  569. JavascriptExceptionOperators::WalkStackForExceptionContextInternal(ScriptContext& scriptContext, JavascriptExceptionContext& exceptionContext, Var thrownObject,
  570. uint32& callerByteCodeOffset, uint64 stackCrawlLimit, PVOID returnAddress, bool isThrownException, bool resetStack)
  571. {
  572. JavascriptStackWalker walker(&scriptContext, true, returnAddress);
  573. JavascriptFunction* jsFunc = nullptr;
  574. if (!GetCaller(walker, jsFunc))
  575. {
  576. return nullptr;
  577. }
  578. // Skip to first non-Library code
  579. // Similar behavior to GetCaller returning false
  580. if(jsFunc->IsLibraryCode() && !walker.GetNonLibraryCodeCaller(&jsFunc))
  581. {
  582. return nullptr;
  583. }
  584. JavascriptFunction * caller = jsFunc;
  585. callerByteCodeOffset = walker.GetByteCodeOffset();
  586. Assert(!caller->IsLibraryCode());
  587. // NOTE Don't set the throwing exception here, because we might need to box it and will cause a nested stack walker
  588. // instead, return it to be set in WalkStackForExceptionContext
  589. if (stackCrawlLimit == 0)
  590. {
  591. return caller;
  592. }
  593. const bool crawlStackForWER = CrawlStackForWER(scriptContext);
  594. // If we take an OOM (JavascriptException for OOM if script is active), just bail early and return what we've got
  595. HRESULT hr;
  596. JavascriptExceptionContext::StackTrace *stackTrace = NULL;
  597. BEGIN_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_NESTED
  598. {
  599. // In WER scenario, we should combine the original stack with latest throw stack as the final throw might be coming form
  600. // a different stack.
  601. uint64 i = 1;
  602. if (crawlStackForWER && thrownObject && Js::JavascriptError::Is(thrownObject))
  603. {
  604. Js::JavascriptError* errorObject = Js::JavascriptError::FromVar(thrownObject);
  605. Js::JavascriptExceptionContext::StackTrace *originalStackTrace = NULL;
  606. const Js::JavascriptExceptionObject* originalExceptionObject = errorObject->GetJavascriptExceptionObject();
  607. if (!resetStack && errorObject->GetInternalProperty(errorObject, InternalPropertyIds::StackTrace, (Js::Var*) &originalStackTrace, NULL, &scriptContext) &&
  608. (originalStackTrace != nullptr))
  609. {
  610. exceptionContext.SetOriginalStackTrace(originalStackTrace);
  611. }
  612. else
  613. {
  614. if (originalExceptionObject != nullptr)
  615. {
  616. exceptionContext.SetOriginalStackTrace(originalExceptionObject->GetExceptionContext()->GetStackTrace());
  617. }
  618. }
  619. }
  620. stackTrace = RecyclerNew(scriptContext.GetRecycler(), JavascriptExceptionContext::StackTrace, scriptContext.GetRecycler());
  621. do
  622. {
  623. JavascriptExceptionContext::StackFrame stackFrame(jsFunc, walker, crawlStackForWER);
  624. stackTrace->Add(stackFrame);
  625. } while (walker.GetDisplayCaller(&jsFunc) && i++ < stackCrawlLimit);
  626. }
  627. END_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_INSCRIPT(hr);
  628. exceptionContext.SetStackTrace(stackTrace);
  629. DumpStackTrace(exceptionContext, isThrownException);
  630. return caller;
  631. }
  632. // We might be trying to raise a stack overflow exception from the interpreter before
  633. // we've executed code in the current script stack frame. In that case the current byte
  634. // code offset is 0. In such cases walk to the caller's caller.
  635. BOOL JavascriptExceptionOperators::GetCaller(JavascriptStackWalker& walker, JavascriptFunction*& jsFunc)
  636. {
  637. if (! walker.GetCaller(&jsFunc))
  638. {
  639. return FALSE;
  640. }
  641. if (! walker.GetCurrentInterpreterFrame() ||
  642. walker.GetCurrentInterpreterFrame()->GetReader()->GetCurrentOffset() > 0)
  643. {
  644. return TRUE;
  645. }
  646. return walker.GetCaller(&jsFunc);
  647. }
  648. void JavascriptExceptionOperators::DumpStackTrace(JavascriptExceptionContext& exceptionContext, bool isThrownException)
  649. {
  650. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  651. if (! exceptionContext.GetStackTrace()
  652. || ! Configuration::Global.flags.Dump.IsEnabled(ExceptionStackTracePhase)
  653. || ! isThrownException)
  654. {
  655. return;
  656. }
  657. Output::Print(L"\nStack trace for thrown exception\n");
  658. JavascriptExceptionContext::StackTrace *stackTrace = exceptionContext.GetStackTrace();
  659. for (int i=0; i < stackTrace->Count(); i++)
  660. {
  661. Js::JavascriptExceptionContext::StackFrame currFrame = stackTrace->Item(i);
  662. ULONG lineNumber = 0;
  663. LONG characterPosition = 0;
  664. if (currFrame.IsScriptFunction() && !currFrame.GetFunctionBody()->GetUtf8SourceInfo()->GetIsLibraryCode())
  665. {
  666. currFrame.GetFunctionBody()->GetLineCharOffset(currFrame.GetByteCodeOffset(), &lineNumber, &characterPosition);
  667. }
  668. Output::Print(L" %3d: %s (%d, %d)\n", i, currFrame.GetFunctionName(), lineNumber, characterPosition);
  669. }
  670. Output::Flush();
  671. #endif
  672. }
  673. /// ---------------------------------------------------------------------------------------------------
  674. /// When allocators throw out of memory exception - scriptContext is NULL
  675. /// ---------------------------------------------------------------------------------------------------
  676. JavascriptExceptionObject * JavascriptExceptionOperators::GetOutOfMemoryExceptionObject(ScriptContext *scriptContext)
  677. {
  678. ThreadContext *threadContext = scriptContext ?
  679. scriptContext->GetThreadContext() :
  680. ThreadContext::GetContextForCurrentThread();
  681. JavascriptExceptionObject *oomExceptionObject = threadContext->GetPendingOOMErrorObject();
  682. Assert(oomExceptionObject);
  683. return oomExceptionObject;
  684. }
  685. void JavascriptExceptionOperators::ThrowOutOfMemory(ScriptContext *scriptContext)
  686. {
  687. ThreadContext *threadContext = scriptContext ?
  688. scriptContext->GetThreadContext() :
  689. ThreadContext::GetContextForCurrentThread();
  690. threadContext->ClearDisableImplicitFlags();
  691. JavascriptExceptionObject *oom = JavascriptExceptionOperators::GetOutOfMemoryExceptionObject(scriptContext);
  692. JavascriptExceptionOperators::ThrowExceptionObject(oom, scriptContext);
  693. }
  694. void JavascriptExceptionOperators::ThrowStackOverflow(ScriptContext *scriptContext, PVOID returnAddress)
  695. {
  696. Assert(scriptContext);
  697. ThreadContext *threadContext = scriptContext->GetThreadContext();
  698. JavascriptExceptionObject *so = threadContext->GetPendingSOErrorObject();
  699. Assert(so);
  700. // Disable implicit call before calling into recycler (to prevent QueryContinue/dispose from leave script and stack overflow again)
  701. threadContext->DisableImplicitCall();
  702. Var thrownObject = scriptContext->GetLibrary()->CreateStackOverflowError();
  703. so->SetThrownObject(thrownObject);
  704. // NOTE: Do not ClearDisableImplicitFlags() here. We still need to allocate StackTrace, etc. Keep implicit call disabled till actual
  705. // throw (ThrowExceptionObjectInternal will ClearDisableImplicitFlags before throw). If anything wrong happens in between which throws
  706. // a new exception, the new throw will ClearDisableImplicitFlags.
  707. JavascriptExceptionOperators::ThrowExceptionObject(so, scriptContext, false, returnAddress);
  708. }
  709. void JavascriptExceptionOperators::ThrowExceptionObjectInternal(Js::JavascriptExceptionObject * exceptionObject, ScriptContext* scriptContext, bool fillExceptionContext, bool considerPassingToDebugger, PVOID returnAddress, bool resetStack)
  710. {
  711. if (scriptContext)
  712. {
  713. if (fillExceptionContext)
  714. {
  715. Assert(exceptionObject);
  716. JavascriptExceptionContext exceptionContext;
  717. Var thrownObject = exceptionObject->GetThrownObject(nullptr);
  718. WalkStackForExceptionContext(*scriptContext, exceptionContext, thrownObject, StackCrawlLimitOnThrow(thrownObject, *scriptContext), returnAddress, /*isThrownException=*/ true, resetStack);
  719. exceptionObject->FillError(exceptionContext, scriptContext);
  720. AddStackTraceToObject(thrownObject, exceptionContext.GetStackTrace(), *scriptContext, /*isThrownException=*/ true, resetStack);
  721. if (considerPassingToDebugger)
  722. {
  723. DispatchExceptionToDebugger(exceptionObject, scriptContext);
  724. }
  725. }
  726. Assert(!scriptContext ||
  727. // If we disabled implicit calls and we did record an implicit call, do not throw.
  728. // Check your helper to see if a call recorded an implicit call that might cause an invalid value
  729. !(
  730. scriptContext->GetThreadContext()->IsDisableImplicitCall() &&
  731. scriptContext->GetThreadContext()->GetImplicitCallFlags() & (~ImplicitCall_None)
  732. ) ||
  733. // Make sure we didn't disable exceptions
  734. !scriptContext->GetThreadContext()->IsDisableImplicitException()
  735. );
  736. scriptContext->GetThreadContext()->ClearDisableImplicitFlags();
  737. }
  738. if (exceptionObject->IsPendingExceptionObject())
  739. {
  740. ThreadContext * threadContext = scriptContext? scriptContext->GetThreadContext() : ThreadContext::GetContextForCurrentThread();
  741. threadContext->SetHasThrownPendingException();
  742. }
  743. throw exceptionObject;
  744. }
  745. void JavascriptExceptionOperators::DispatchExceptionToDebugger(Js::JavascriptExceptionObject * exceptionObject, ScriptContext* scriptContext)
  746. {
  747. Assert(exceptionObject != NULL);
  748. Assert(scriptContext != NULL);
  749. if (scriptContext->IsInDebugMode()
  750. && scriptContext->GetDebugContext()->GetProbeContainer()->HasAllowedForException(exceptionObject))
  751. {
  752. InterpreterHaltState haltState(STOP_EXCEPTIONTHROW, /*executingFunction*/nullptr);
  753. haltState.exceptionObject = exceptionObject;
  754. scriptContext->GetDebugContext()->GetProbeContainer()->DispatchExceptionBreakpoint(&haltState);
  755. }
  756. }
  757. void JavascriptExceptionOperators::ThrowExceptionObject(Js::JavascriptExceptionObject * exceptionObject, ScriptContext* scriptContext, bool considerPassingToDebugger, PVOID returnAddress, bool resetStack)
  758. {
  759. ThrowExceptionObjectInternal(exceptionObject, scriptContext, true, considerPassingToDebugger, returnAddress, resetStack);
  760. }
  761. // The purpose of RethrowExceptionObject is to determine if ThrowExceptionObjectInternal should fill in the exception context.
  762. //
  763. // 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
  764. // is if we are rethrowing and have the JavascriptExceptionObject from the previous throw with its exception context intact. If
  765. // RethrowExceptionObject is passed a JavascriptExceptionObject with the function already there, that implies we have existing
  766. // exception context and shouldn't step on it on the throw.
  767. //
  768. // RethrowExceptionObject is called only for cross-host calls. When throwing across host calls, we stash our internal JavascriptExceptionObject
  769. // 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
  770. // 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,
  771. // 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.
  772. //
  773. void JavascriptExceptionOperators::RethrowExceptionObject(Js::JavascriptExceptionObject * exceptionObject, ScriptContext* scriptContext, bool considerPassingToDebugger)
  774. {
  775. ThrowExceptionObjectInternal(exceptionObject, scriptContext, ! exceptionObject->GetFunction(), considerPassingToDebugger, /*returnAddress=*/ nullptr, /*resetStack=*/ false);
  776. }
  777. // 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.
  778. JavascriptExceptionContext::StackTrace* JavascriptExceptionOperators::TrimStackTraceForThrownObject(JavascriptExceptionContext::StackTrace* stackTraceIn, Var thrownObject, ScriptContext& scriptContext)
  779. {
  780. Assert(CrawlStackForWER(scriptContext)); // Don't trim if crawl for Error.stack
  781. Assert(stackTraceIn);
  782. int stackTraceLimit = static_cast<int>(GetStackTraceLimit(thrownObject, &scriptContext));
  783. Assert(stackTraceLimit == 0 || IsErrorInstance(thrownObject));
  784. if (stackTraceIn->Count() <= stackTraceLimit)
  785. {
  786. return stackTraceIn;
  787. }
  788. JavascriptExceptionContext::StackTrace* stackTraceTrimmed = NULL;
  789. if (stackTraceLimit > 0)
  790. {
  791. HRESULT hr;
  792. BEGIN_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_NESTED
  793. {
  794. stackTraceTrimmed = RecyclerNew(scriptContext.GetRecycler(), JavascriptExceptionContext::StackTrace, scriptContext.GetRecycler());
  795. for (int i = 0; i < stackTraceLimit; i++)
  796. {
  797. stackTraceTrimmed->Add(stackTraceIn->Item(i));
  798. }
  799. }
  800. END_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_INSCRIPT(hr);
  801. }
  802. // ignore OOM and just return what we can get
  803. return stackTraceTrimmed;
  804. }
  805. //
  806. // Check if thrownObject is instanceof Error (but not an Error prototype).
  807. //
  808. bool JavascriptExceptionOperators::IsErrorInstance(Var thrownObject)
  809. {
  810. if (thrownObject && JavascriptError::Is(thrownObject))
  811. {
  812. return !JavascriptError::FromVar(thrownObject)->IsPrototype();
  813. }
  814. if (thrownObject && RecyclableObject::Is(thrownObject))
  815. {
  816. RecyclableObject* obj = RecyclableObject::FromVar(thrownObject);
  817. while (true)
  818. {
  819. obj = JavascriptOperators::GetPrototype(obj);
  820. if (JavascriptOperators::GetTypeId(obj) == TypeIds_Null)
  821. {
  822. break;
  823. }
  824. if (JavascriptError::Is(obj))
  825. {
  826. return true;
  827. }
  828. }
  829. }
  830. return false;
  831. }
  832. void JavascriptExceptionOperators::AddStackTraceToObject(Var targetObject, JavascriptExceptionContext::StackTrace* stackTrace, ScriptContext& scriptContext, bool isThrownException, bool resetStack)
  833. {
  834. if (!stackTrace || stackTrace->Count() == 0 || !scriptContext.GetConfig()->IsErrorStackTraceEnabled())
  835. {
  836. return;
  837. }
  838. if (isThrownException && CrawlStackForWER(scriptContext)) // Trim stack trace for WER
  839. {
  840. stackTrace = TrimStackTraceForThrownObject(stackTrace, targetObject, scriptContext);
  841. if (!stackTrace)
  842. {
  843. return;
  844. }
  845. }
  846. // If we still have stack trace to store and obj is a thrown exception object, obj must be an Error instance.
  847. Assert(!isThrownException || IsErrorInstance(targetObject));
  848. RecyclableObject* obj = RecyclableObject::FromVar(targetObject);
  849. if (!resetStack && obj->HasProperty(PropertyIds::stack))
  850. {
  851. return; // we don't want to overwrite an existing "stack" property
  852. }
  853. JavascriptFunction* accessor = scriptContext.GetLibrary()->GetStackTraceAccessorFunction();
  854. PropertyDescriptor stackPropertyDescriptor;
  855. stackPropertyDescriptor.SetSetter(accessor);
  856. stackPropertyDescriptor.SetGetter(accessor);
  857. stackPropertyDescriptor.SetConfigurable(true);
  858. stackPropertyDescriptor.SetEnumerable(false);
  859. HRESULT hr;
  860. BEGIN_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_NESTED
  861. {
  862. if (JavascriptOperators::DefineOwnPropertyDescriptor(obj, PropertyIds::stack, stackPropertyDescriptor, false, &scriptContext))
  863. {
  864. obj->SetInternalProperty(InternalPropertyIds::StackTrace, stackTrace, PropertyOperationFlags::PropertyOperation_None, NULL);
  865. obj->SetInternalProperty(InternalPropertyIds::StackTraceCache, NULL, PropertyOperationFlags::PropertyOperation_None, NULL);
  866. }
  867. }
  868. END_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_INSCRIPT(hr)
  869. }
  870. Var JavascriptExceptionOperators::OP_RuntimeTypeError(MessageId messageId, ScriptContext *scriptContext)
  871. {
  872. JavascriptError::ThrowTypeError(scriptContext, MAKE_HR(messageId));
  873. }
  874. Var JavascriptExceptionOperators::OP_RuntimeRangeError(MessageId messageId, ScriptContext *scriptContext)
  875. {
  876. JavascriptError::ThrowRangeError(scriptContext, MAKE_HR(messageId));
  877. }
  878. Var JavascriptExceptionOperators::OP_RuntimeReferenceError(MessageId messageId, ScriptContext *scriptContext)
  879. {
  880. JavascriptError::ThrowReferenceError(scriptContext, MAKE_HR(messageId));
  881. }
  882. Var JavascriptExceptionOperators::ThrowTypeErrorAccessor(RecyclableObject* function, CallInfo callInfo, ...)
  883. {
  884. JavascriptError::ThrowTypeError(function->GetScriptContext(), VBSERR_ActionNotSupported);
  885. }
  886. // Throw type error on access caller when in a restricted context
  887. Var JavascriptExceptionOperators::ThrowTypeErrorCallerAccessor(RecyclableObject* function, CallInfo callInfo, ...)
  888. {
  889. JavascriptError::ThrowTypeError(function->GetScriptContext(), JSERR_AccessCallerRestricted);
  890. }
  891. // Throw type error on access on callee when strict mode
  892. Var JavascriptExceptionOperators::ThrowTypeErrorCalleeAccessor(RecyclableObject* function, CallInfo callInfo, ...)
  893. {
  894. JavascriptError::ThrowTypeError(function->GetScriptContext(), JSERR_AccessCallee);
  895. }
  896. // Throw type error on access arguments when in a restricted context
  897. Var JavascriptExceptionOperators::ThrowTypeErrorArgumentsAccessor(RecyclableObject* function, CallInfo callInfo, ...)
  898. {
  899. JavascriptError::ThrowTypeError(function->GetScriptContext(), JSERR_AccessArgumentsRestricted);
  900. }
  901. Var JavascriptExceptionOperators::StackTraceAccessor(RecyclableObject* function, CallInfo callInfo, ...)
  902. {
  903. ARGUMENTS(args, callInfo);
  904. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  905. ScriptContext *scriptContext = function->GetScriptContext();
  906. AnalysisAssert(scriptContext);
  907. // If the first argument to the accessor is not a recyclable object, return undefined
  908. // for compat with other browsers
  909. if (!RecyclableObject::Is(args[0]))
  910. {
  911. return scriptContext->GetLibrary()->GetUndefined();
  912. }
  913. RecyclableObject *obj = RecyclableObject::FromVar(args[0]);
  914. // If an argument was passed to the accessor, it is being called as a setter.
  915. // Set the internal StackTraceCache property accordingly.
  916. if (args.Info.Count > 1)
  917. {
  918. obj->SetInternalProperty(InternalPropertyIds::StackTraceCache, args[1], PropertyOperationFlags::PropertyOperation_None, NULL);
  919. if (JavascriptError::Is(obj))
  920. {
  921. ((JavascriptError *)obj)->SetStackPropertyRedefined(true);
  922. }
  923. return scriptContext->GetLibrary()->GetEmptyString();
  924. }
  925. // Otherwise, the accessor is being called as a getter.
  926. // Return existing cached value, or obtain the string representation of the StackTrace to return.
  927. Var cache = NULL;
  928. if (obj->GetInternalProperty(obj,InternalPropertyIds::StackTraceCache, (Var*)&cache, NULL, scriptContext) && cache)
  929. {
  930. return cache;
  931. }
  932. JavascriptString* stringMessage = scriptContext->GetLibrary()->GetEmptyString();
  933. HRESULT hr;
  934. BEGIN_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_NESTED
  935. {
  936. Js::JavascriptExceptionContext::StackTrace *stackTrace = NULL;
  937. if (!obj->GetInternalProperty(obj,InternalPropertyIds::StackTrace, (Js::Var*) &stackTrace, NULL, scriptContext) ||
  938. stackTrace == nullptr)
  939. {
  940. obj->SetInternalProperty(InternalPropertyIds::StackTraceCache, stringMessage, PropertyOperationFlags::PropertyOperation_None, NULL);
  941. return stringMessage;
  942. }
  943. if (IsErrorInstance(obj))
  944. {
  945. stringMessage = JavascriptConversion::ToString(obj, scriptContext);
  946. }
  947. CompoundString *const stringBuilder = CompoundString::NewWithCharCapacity(40, scriptContext->GetLibrary());
  948. stringBuilder->AppendChars(stringMessage);
  949. for (int i = 0; i < stackTrace->Count(); i++)
  950. {
  951. Js::JavascriptExceptionContext::StackFrame currentFrame = stackTrace->Item(i);
  952. // Defend in depth. Discard cross domain frames if somehow they creped in.
  953. if (currentFrame.IsScriptFunction())
  954. {
  955. ScriptContext* funcScriptContext = currentFrame.GetFunctionBody()->GetScriptContext();
  956. AnalysisAssert(funcScriptContext);
  957. if (scriptContext != funcScriptContext && FAILED(scriptContext->GetHostScriptContext()->CheckCrossDomainScriptContext(funcScriptContext)))
  958. {
  959. continue; // Ignore this frame
  960. }
  961. }
  962. FunctionBody* functionBody = currentFrame.GetFunctionBody();
  963. const bool isLibraryCode = !functionBody || functionBody->GetUtf8SourceInfo()->GetIsLibraryCode();
  964. if (isLibraryCode)
  965. {
  966. AppendLibraryFrameToStackTrace(stringBuilder, currentFrame.GetFunctionName());
  967. }
  968. else
  969. {
  970. LPCWSTR pUrl = NULL;
  971. ULONG lineNumber = 0;
  972. LONG characterPosition = 0;
  973. functionBody->GetLineCharOffset(currentFrame.GetByteCodeOffset(), &lineNumber, &characterPosition);
  974. pUrl = functionBody->GetSourceName();
  975. LPCWSTR functionName = nullptr;
  976. if (CONFIG_FLAG(ExtendedErrorStackForTestHost))
  977. {
  978. BEGIN_LEAVE_SCRIPT_INTERNAL(scriptContext)
  979. {
  980. if (currentFrame.GetFunctionNameWithArguments(&functionName) != S_OK)
  981. {
  982. functionName = functionBody->GetExternalDisplayName();
  983. }
  984. }
  985. END_LEAVE_SCRIPT_INTERNAL(scriptContext)
  986. }
  987. else
  988. {
  989. functionName = functionBody->GetExternalDisplayName();
  990. }
  991. AppendExternalFrameToStackTrace(stringBuilder, functionName, pUrl ? pUrl : L"", lineNumber + 1, characterPosition + 1);
  992. }
  993. }
  994. // 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.
  995. // 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
  996. // an OOM, have some chance of producing a stack trace to see where it happened.
  997. stringMessage = stringBuilder;
  998. }
  999. END_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_INSCRIPT(hr);
  1000. obj->SetInternalProperty(InternalPropertyIds::StackTraceCache, stringMessage, PropertyOperationFlags::PropertyOperation_None, NULL);
  1001. return stringMessage;
  1002. }
  1003. uint64 JavascriptExceptionOperators::GetStackTraceLimit(Var thrownObject, ScriptContext* scriptContext)
  1004. {
  1005. uint64 limit = 0;
  1006. if (scriptContext->GetConfig()->IsErrorStackTraceEnabled()
  1007. && IsErrorInstance(thrownObject))
  1008. {
  1009. HRESULT hr = JavascriptError::GetRuntimeError(RecyclableObject::FromVar(thrownObject), NULL);
  1010. JavascriptFunction* error = scriptContext->GetLibrary()->GetErrorConstructor();
  1011. // If we are throwing StackOverflow and Error.stackTraceLimit is a custom getter, we can't make the getter
  1012. // call as we don't have stack space. Just bail out without stack trace in such case. Only proceed to get
  1013. // Error.stackTraceLimit property if we are not throwing StackOverflow, or there is no implicitCall (in getter case).
  1014. DisableImplicitFlags disableImplicitFlags = scriptContext->GetThreadContext()->GetDisableImplicitFlags();
  1015. if (hr == VBSERR_OutOfStack)
  1016. {
  1017. scriptContext->GetThreadContext()->SetDisableImplicitFlags(DisableImplicitCallAndExceptionFlag);
  1018. }
  1019. Var var;
  1020. if (JavascriptOperators::GetProperty(error, PropertyIds::stackTraceLimit, &var, scriptContext))
  1021. {
  1022. // Only accept the value if it is a "Number". Avoid potential valueOf() call.
  1023. switch (JavascriptOperators::GetTypeId(var))
  1024. {
  1025. case TypeIds_Integer:
  1026. case TypeIds_Number:
  1027. case TypeIds_Int64Number:
  1028. case TypeIds_UInt64Number:
  1029. double value = JavascriptConversion::ToNumber(var, scriptContext);
  1030. limit = JavascriptNumber::IsNan(value) ? 0 :
  1031. (NumberUtilities::IsFinite(value) ? JavascriptConversion::ToUInt32(var, scriptContext) : MaxStackTraceLimit);
  1032. break;
  1033. }
  1034. }
  1035. if (hr == VBSERR_OutOfStack)
  1036. {
  1037. scriptContext->GetThreadContext()->SetDisableImplicitFlags(disableImplicitFlags);
  1038. }
  1039. }
  1040. return limit;
  1041. }
  1042. void JavascriptExceptionOperators::AppendExternalFrameToStackTrace(CompoundString* bs, LPCWSTR functionName, LPCWSTR fileName, ULONG lineNumber, LONG characterPosition)
  1043. {
  1044. // format is equivalent to printf("\n at %s (%s:%d:%d)", functionName, filename, lineNumber, characterPosition);
  1045. const CharCount maxULongStringLength = 10; // excluding null terminator
  1046. const auto ConvertULongToString = [](const ULONG value, wchar_t *const buffer, const CharCount charCapacity)
  1047. {
  1048. const errno_t err = _ultow_s(value, buffer, charCapacity, 10);
  1049. Assert(err == 0);
  1050. };
  1051. if (CONFIG_FLAG(ExtendedErrorStackForTestHost))
  1052. {
  1053. bs->AppendChars(L"\n\tat ");
  1054. }
  1055. else
  1056. {
  1057. bs->AppendChars(L"\n at ");
  1058. }
  1059. bs->AppendCharsSz(functionName);
  1060. bs->AppendChars(L" (");
  1061. if (CONFIG_FLAG(ExtendedErrorStackForTestHost) && *fileName != L'\0')
  1062. {
  1063. wchar_t shortfilename[_MAX_FNAME];
  1064. wchar_t ext[_MAX_EXT];
  1065. errno_t err = _wsplitpath_s(fileName, NULL, 0, NULL, 0, shortfilename, _MAX_FNAME, ext, _MAX_EXT);
  1066. if (err != 0)
  1067. {
  1068. bs->AppendCharsSz(fileName);
  1069. }
  1070. else
  1071. {
  1072. bs->AppendCharsSz(shortfilename);
  1073. bs->AppendCharsSz(ext);
  1074. }
  1075. }
  1076. else
  1077. {
  1078. bs->AppendCharsSz(fileName);
  1079. }
  1080. bs->AppendChars(L':');
  1081. bs->AppendChars(lineNumber, maxULongStringLength, ConvertULongToString);
  1082. bs->AppendChars(L':');
  1083. bs->AppendChars(characterPosition, maxULongStringLength, ConvertULongToString);
  1084. bs->AppendChars(L')');
  1085. }
  1086. void JavascriptExceptionOperators::AppendLibraryFrameToStackTrace(CompoundString* bs, LPCWSTR functionName)
  1087. {
  1088. // format is equivalent to printf("\n at %s (native code)", functionName);
  1089. bs->AppendChars(L"\n at ");
  1090. bs->AppendCharsSz(functionName);
  1091. bs->AppendChars(L" (native code)");
  1092. }
  1093. } // namespace Js