StackScriptFunction.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeLibraryPch.h"
  6. #include "Language/JavascriptFunctionArgIndex.h"
  7. #include "Language/InterpreterStackFrame.h"
  8. #include "Library/StackScriptFunction.h"
  9. namespace Js
  10. {
  11. JavascriptFunction *
  12. StackScriptFunction::EnsureBoxed(BOX_PARAM(JavascriptFunction * function, void * returnAddress, char16 const * reason))
  13. {
  14. #if ENABLE_DEBUG_CONFIG_OPTIONS
  15. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  16. #endif
  17. if (!ThreadContext::IsOnStack(function))
  18. {
  19. return function;
  20. }
  21. // Only script function can be on the stack
  22. StackScriptFunction * stackScriptFunction = StackScriptFunction::FromVar(function);
  23. ScriptFunction * boxedFunction = stackScriptFunction->boxedScriptFunction;
  24. if (boxedFunction != nullptr)
  25. {
  26. // We have already boxed this stack function before, and the function
  27. // wasn't on any slot or not a caller that we can replace.
  28. // Just give out the function we boxed before
  29. return boxedFunction;
  30. }
  31. PHASE_PRINT_TESTTRACE(Js::StackFuncPhase, function->GetFunctionProxy(),
  32. _u("StackScriptFunction (%s): box and disable stack function: %s (function %s)\n"),
  33. reason, function->GetFunctionProxy()->IsDeferredDeserializeFunction()?
  34. _u("<DeferDeserialize>") : function->GetParseableFunctionInfo()->GetDisplayName(),
  35. function->GetFunctionProxy()->GetDebugNumberSet(debugStringBuffer));
  36. // During the box workflow we reset all the parents of all nested functions and up. If a fault occurs when the stack function
  37. // is created this will cause further issues when trying to use the function object again. So failing faster seems to make more sense
  38. try
  39. {
  40. boxedFunction = StackScriptFunction::Box(stackScriptFunction, returnAddress);
  41. }
  42. catch (Js::OutOfMemoryException)
  43. {
  44. FailedToBox_OOM_fatal_error((ULONG_PTR)stackScriptFunction);
  45. }
  46. return boxedFunction;
  47. }
  48. JavascriptFunction *
  49. StackScriptFunction::GetCurrentFunctionObject(JavascriptFunction * function)
  50. {
  51. if (!ThreadContext::IsOnStack(function))
  52. {
  53. return function;
  54. }
  55. ScriptFunction * boxed = StackScriptFunction::FromVar(function)->boxedScriptFunction;
  56. return boxed ? boxed : function;
  57. }
  58. StackScriptFunction *
  59. StackScriptFunction::FromVar(Var var)
  60. {
  61. AssertOrFailFast(ScriptFunction::Is(var));
  62. Assert(ThreadContext::IsOnStack(var));
  63. return static_cast<StackScriptFunction *>(var);
  64. }
  65. StackScriptFunction *
  66. StackScriptFunction::UnsafeFromVar(Var var)
  67. {
  68. Assert(ScriptFunction::Is(var));
  69. Assert(ThreadContext::IsOnStack(var));
  70. return static_cast<StackScriptFunction *>(var);
  71. }
  72. ScriptFunction *
  73. StackScriptFunction::Box(StackScriptFunction *stackScriptFunction, void * returnAddress)
  74. {
  75. Assert(ThreadContext::IsOnStack(stackScriptFunction));
  76. Assert(stackScriptFunction->boxedScriptFunction == nullptr);
  77. FunctionInfo * functionInfoParent = stackScriptFunction->GetFunctionBody()->GetStackNestedFuncParentStrongRef();
  78. Assert(functionInfoParent != nullptr);
  79. FunctionBody * functionParent = functionInfoParent->GetFunctionBody();
  80. ScriptContext * scriptContext = stackScriptFunction->GetScriptContext();
  81. ScriptFunction * boxedFunction;
  82. BEGIN_TEMP_ALLOCATOR(tempAllocator, scriptContext, _u("BoxStackFunction"));
  83. {
  84. BoxState state(tempAllocator, functionParent, scriptContext, returnAddress);
  85. state.Box();
  86. boxedFunction = stackScriptFunction->boxedScriptFunction;
  87. Assert(boxedFunction != nullptr);
  88. }
  89. END_TEMP_ALLOCATOR(tempAllocator, scriptContext);
  90. return boxedFunction;
  91. }
  92. void StackScriptFunction::Box(Js::FunctionBody * parent, ScriptFunction ** functionRef)
  93. {
  94. ScriptContext * scriptContext = parent->GetScriptContext();
  95. BEGIN_TEMP_ALLOCATOR(tempAllocator, scriptContext, _u("BoxStackFunction"));
  96. {
  97. BoxState state(tempAllocator, parent, scriptContext);
  98. state.Box();
  99. if (functionRef != nullptr && ThreadContext::IsOnStack(*functionRef))
  100. {
  101. ScriptFunction * boxedScriptFunction = StackScriptFunction::FromVar(*functionRef)->boxedScriptFunction;
  102. if (boxedScriptFunction != nullptr)
  103. {
  104. *functionRef = boxedScriptFunction;
  105. }
  106. }
  107. }
  108. END_TEMP_ALLOCATOR(tempAllocator, scriptContext);
  109. }
  110. StackScriptFunction::BoxState::BoxState(ArenaAllocator * alloc, FunctionBody * functionBody, ScriptContext * scriptContext, void * returnAddress) :
  111. frameToBox(alloc), functionObjectToBox(alloc), boxedValues(alloc), scriptContext(scriptContext), returnAddress(returnAddress)
  112. {
  113. Assert(functionBody->DoStackNestedFunc() && functionBody->GetNestedCount() != 0);
  114. FunctionBody * current = functionBody;
  115. do
  116. {
  117. frameToBox.Add(current);
  118. for (uint i = 0; i < current->GetNestedCount(); i++)
  119. {
  120. FunctionProxy * nested = current->GetNestedFunctionProxy(i);
  121. functionObjectToBox.Add(nested);
  122. if (nested->IsFunctionBody())
  123. {
  124. nested->GetFunctionBody()->ClearStackNestedFuncParent();
  125. }
  126. }
  127. FunctionInfo * functionInfo = current->GetAndClearStackNestedFuncParent();
  128. current = functionInfo ? functionInfo->GetFunctionBody() : nullptr;
  129. }
  130. while (current && current->DoStackNestedFunc());
  131. }
  132. bool StackScriptFunction::BoxState::NeedBoxFrame(FunctionBody * functionBody)
  133. {
  134. return frameToBox.Contains(functionBody);
  135. }
  136. bool StackScriptFunction::BoxState::NeedBoxScriptFunction(ScriptFunction * scriptFunction)
  137. {
  138. return functionObjectToBox.Contains(scriptFunction->GetFunctionProxy());
  139. }
  140. void StackScriptFunction::BoxState::Box()
  141. {
  142. JavascriptStackWalker walker(scriptContext, true, returnAddress);
  143. JavascriptFunction * caller;
  144. bool hasInlineeToBox = false;
  145. while (walker.GetCaller(&caller))
  146. {
  147. if (!caller->IsScriptFunction())
  148. {
  149. continue;
  150. }
  151. ScriptFunction * callerScriptFunction = ScriptFunction::FromVar(caller);
  152. FunctionBody * callerFunctionBody = callerScriptFunction->GetFunctionBody();
  153. if (hasInlineeToBox || this->NeedBoxFrame(callerFunctionBody))
  154. {
  155. // Box the frame display, but don't need to box the function unless we see them
  156. // in the slots.
  157. // If the frame display has any stack nested function, then it must have given to one of the
  158. // stack functions. If it doesn't appear in on eof the stack function , the frame display
  159. // doesn't contain any stack function
  160. InterpreterStackFrame * interpreterFrame = walker.GetCurrentInterpreterFrame();
  161. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  162. if (this->NeedBoxFrame(callerFunctionBody) || (hasInlineeToBox && !walker.IsInlineFrame()))
  163. {
  164. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  165. char16 const * frameKind;
  166. if (interpreterFrame)
  167. {
  168. Assert(!hasInlineeToBox);
  169. frameKind = walker.IsBailedOutFromInlinee()? _u("Interpreted from Inlined Bailout (Pending)") :
  170. walker.IsBailedOutFromFunction()? _u("Interpreted from Bailout") : _u("Interpreted");
  171. }
  172. else if (walker.IsInlineFrame())
  173. {
  174. Assert(this->NeedBoxFrame(callerFunctionBody));
  175. frameKind = _u("Native Inlined (Pending)");
  176. }
  177. else if (this->NeedBoxFrame(callerFunctionBody))
  178. {
  179. frameKind = (hasInlineeToBox? _u("Native and Inlinee") : _u("Native"));
  180. }
  181. else
  182. {
  183. frameKind = _u("Native for Inlinee");
  184. }
  185. PHASE_PRINT_TESTTRACE(Js::StackFuncPhase, callerFunctionBody,
  186. _u("Boxing Frame [%s]: %s %s\n"), frameKind,
  187. callerFunctionBody->GetDisplayName(), callerFunctionBody->GetDebugNumberSet(debugStringBuffer));
  188. }
  189. #endif
  190. if (interpreterFrame)
  191. {
  192. Assert(!hasInlineeToBox);
  193. Assert(StackScriptFunction::GetCurrentFunctionObject(interpreterFrame->GetJavascriptFunction()) == caller);
  194. if (callerFunctionBody->DoStackFrameDisplay() && interpreterFrame->IsClosureInitDone())
  195. {
  196. Js::FrameDisplay *stackFrameDisplay = interpreterFrame->GetLocalFrameDisplay();
  197. // Local frame display may be null if bailout didn't restore it, which means we don't need it.
  198. if (stackFrameDisplay)
  199. {
  200. Js::FrameDisplay *boxedFrameDisplay = this->BoxFrameDisplay(stackFrameDisplay);
  201. interpreterFrame->SetLocalFrameDisplay(boxedFrameDisplay);
  202. }
  203. }
  204. if (callerFunctionBody->DoStackScopeSlots() && interpreterFrame->IsClosureInitDone())
  205. {
  206. Field(Var)* stackScopeSlots = (Field(Var)*)interpreterFrame->GetLocalClosure();
  207. if (stackScopeSlots)
  208. {
  209. // Scope slot pointer may be null if bailout didn't restore it, which means we don't need it.
  210. Field(Var)* boxedScopeSlots = this->BoxScopeSlots(stackScopeSlots, static_cast<uint>(ScopeSlots(stackScopeSlots).GetCount()));
  211. interpreterFrame->SetLocalClosure(boxedScopeSlots);
  212. }
  213. }
  214. uint nestedCount = callerFunctionBody->GetNestedCount();
  215. for (uint i = 0; i < nestedCount; i++)
  216. {
  217. // Box the stack function, even if they might not be "created" in the byte code yet.
  218. // Some of them will not be captured in slots, so we just need to box them and record it with the
  219. // stack func so that when we can just use the boxed value when we need it.
  220. StackScriptFunction * stackFunction = interpreterFrame->GetStackNestedFunction(i);
  221. ScriptFunction * boxedFunction = this->BoxStackFunction(stackFunction);
  222. Assert(stackFunction->boxedScriptFunction == boxedFunction);
  223. this->UpdateFrameDisplay(stackFunction);
  224. }
  225. if (walker.IsBailedOutFromInlinee())
  226. {
  227. if (!walker.IsCurrentPhysicalFrameForLoopBody())
  228. {
  229. // this is the interpret frame from bailing out of inline frame
  230. // Just mark we have inlinee to box so we will walk the native frame's list when we get there.
  231. hasInlineeToBox = true;
  232. }
  233. }
  234. else if (walker.IsBailedOutFromFunction())
  235. {
  236. // The current interpret frame is from bailing out of a native frame.
  237. // Walk native frame that was bailed out as well.
  238. // The stack walker is pointing to the native frame already.
  239. this->BoxNativeFrame(walker, callerFunctionBody);
  240. // We don't need to box this frame, but we may still need to box the scope slot references
  241. // within nested frame displays if the slots they refer to have been boxed.
  242. if (callerFunctionBody->GetNestedCount() != 0)
  243. {
  244. this->ForEachStackNestedFunctionNative(walker, callerFunctionBody, [&](ScriptFunction *nestedFunc)
  245. {
  246. this->UpdateFrameDisplay(nestedFunc);
  247. });
  248. }
  249. }
  250. }
  251. else
  252. {
  253. if (walker.IsInlineFrame())
  254. {
  255. if (!walker.IsCurrentPhysicalFrameForLoopBody())
  256. {
  257. // We may have function that are not in slots. So we have to walk the stack function list of the inliner
  258. // to box all the needed function to catch those
  259. hasInlineeToBox = true;
  260. }
  261. }
  262. else
  263. {
  264. hasInlineeToBox = false;
  265. if (callerFunctionBody->DoStackFrameDisplay())
  266. {
  267. Js::FrameDisplay *stackFrameDisplay =
  268. this->GetFrameDisplayFromNativeFrame(walker, callerFunctionBody);
  269. // Local frame display may be null if bailout didn't restore it, which means we don't need it.
  270. if (stackFrameDisplay)
  271. {
  272. this->BoxFrameDisplay(stackFrameDisplay);
  273. }
  274. }
  275. if (callerFunctionBody->DoStackScopeSlots())
  276. {
  277. Field(Var)* stackScopeSlots = (Field(Var)*)this->GetScopeSlotsFromNativeFrame(walker, callerFunctionBody);
  278. if (stackScopeSlots)
  279. {
  280. // Scope slot pointer may be null if bailout didn't restore it, which means we don't need it.
  281. this->BoxScopeSlots(stackScopeSlots, static_cast<uint>(ScopeSlots(stackScopeSlots).GetCount()));
  282. }
  283. }
  284. // walk native frame
  285. this->BoxNativeFrame(walker, callerFunctionBody);
  286. // We don't need to box this frame, but we may still need to box the scope slot references
  287. // within nested frame displays if the slots they refer to have been boxed.
  288. if (callerFunctionBody->GetNestedCount() != 0)
  289. {
  290. this->ForEachStackNestedFunctionNative(walker, callerFunctionBody, [&](ScriptFunction *nestedFunc)
  291. {
  292. this->UpdateFrameDisplay(nestedFunc);
  293. });
  294. }
  295. }
  296. }
  297. }
  298. else if (callerFunctionBody->DoStackFrameDisplay() && !walker.IsInlineFrame())
  299. {
  300. // The case here is that a frame need not be boxed, but the closure environment in that frame
  301. // refers to an outer boxed frame.
  302. // Find the FD and walk it looking for a slot array that refers to a FB that must be boxed.
  303. // Everything from that point outward must be boxed.
  304. FrameDisplay *frameDisplay;
  305. InterpreterStackFrame *interpreterFrame = walker.GetCurrentInterpreterFrame();
  306. bool closureInitDone = true; // Set to true as for native frame bailout will always restore the frameDisplay but for interpreter frame if PROBE_STACK fails closureInitDone won't have completed.
  307. if (interpreterFrame)
  308. {
  309. frameDisplay = interpreterFrame->GetLocalFrameDisplay();
  310. closureInitDone = interpreterFrame->IsClosureInitDone();
  311. }
  312. else
  313. {
  314. frameDisplay = (Js::FrameDisplay*)walker.GetCurrentArgv()[
  315. #if _M_IX86 || _M_AMD64
  316. callerFunctionBody->GetInParamsCount() == 0 ?
  317. JavascriptFunctionArgIndex_StackFrameDisplayNoArg :
  318. #endif
  319. JavascriptFunctionArgIndex_StackFrameDisplay];
  320. }
  321. if (ThreadContext::IsOnStack(frameDisplay) && closureInitDone)
  322. {
  323. int i;
  324. for (i = 0; i < frameDisplay->GetLength(); i++)
  325. {
  326. Field(Var) *slotArray = (Field(Var)*)frameDisplay->GetItem(i);
  327. if (ScopeSlots::Is(slotArray))
  328. {
  329. ScopeSlots slots(slotArray);
  330. if (!slots.IsDebuggerScopeSlotArray())
  331. {
  332. FunctionProxy *functionProxy = slots.GetFunctionInfo()->GetFunctionProxy();
  333. if (functionProxy->IsFunctionBody() && this->NeedBoxFrame(functionProxy->GetFunctionBody()))
  334. {
  335. break;
  336. }
  337. }
  338. }
  339. }
  340. for (; i < frameDisplay->GetLength(); i++)
  341. {
  342. Field(Var) *pScope = (Field(Var)*)frameDisplay->GetItem(i);
  343. if (ScopeSlots::Is(pScope))
  344. {
  345. Field(Var) *boxedSlots = this->BoxScopeSlots(pScope, static_cast<uint>(ScopeSlots(pScope).GetCount()));
  346. frameDisplay->SetItem(i, boxedSlots);
  347. }
  348. }
  349. }
  350. }
  351. ScriptFunction * boxedCaller = nullptr;
  352. if (this->NeedBoxScriptFunction(callerScriptFunction))
  353. {
  354. // TODO-STACK-NESTED-FUNC: Can't assert this yet, JIT might not do stack func allocation
  355. // if the function hasn't been parsed or deserialized yet.
  356. // Assert(ThreadContext::IsOnStack(callerScriptFunction));
  357. if (ThreadContext::IsOnStack(callerScriptFunction))
  358. {
  359. boxedCaller = this->BoxStackFunction(StackScriptFunction::FromVar(callerScriptFunction));
  360. walker.SetCurrentFunction(boxedCaller);
  361. InterpreterStackFrame * interpreterFrame = walker.GetCurrentInterpreterFrame();
  362. if (interpreterFrame)
  363. {
  364. interpreterFrame->SetExecutingStackFunction(boxedCaller);
  365. }
  366. // We don't need to box this frame, but we may still need to box the scope slot references
  367. // within nested frame displays if the slots they refer to have been boxed.
  368. if (callerFunctionBody->GetNestedCount() != 0)
  369. {
  370. this->ForEachStackNestedFunction(walker, callerFunctionBody, [&](ScriptFunction *nestedFunc)
  371. {
  372. this->UpdateFrameDisplay(nestedFunc);
  373. });
  374. }
  375. }
  376. }
  377. }
  378. Assert(!hasInlineeToBox);
  379. // We have to find one nested function
  380. this->Finish();
  381. }
  382. void StackScriptFunction::BoxState::UpdateFrameDisplay(ScriptFunction *nestedFunc)
  383. {
  384. // In some cases a function's frame display need not be boxed, but it may include outer scopes that
  385. // have been boxed. If that's the case, make sure that those scopes are updated.
  386. FrameDisplay *frameDisplay = nestedFunc->GetEnvironment();
  387. if (ThreadContext::IsOnStack(frameDisplay))
  388. {
  389. // The case here is a frame that doesn't define any captured locals, so it blindly grabs the parent
  390. // function's environment, which may have been boxed.
  391. FrameDisplay *boxedFrameDisplay = nullptr;
  392. if (boxedValues.TryGetValue(frameDisplay, (void **)&boxedFrameDisplay))
  393. {
  394. nestedFunc->SetEnvironment(boxedFrameDisplay);
  395. return;
  396. }
  397. }
  398. for (uint i = 0; i < frameDisplay->GetLength(); i++)
  399. {
  400. Var* stackScopeSlots = (Var*)frameDisplay->GetItem(i);
  401. Var* boxedScopeSlots = nullptr;
  402. if (boxedValues.TryGetValue(stackScopeSlots, (void**)&boxedScopeSlots))
  403. {
  404. frameDisplay->SetItem(i, boxedScopeSlots);
  405. }
  406. }
  407. }
  408. uintptr_t StackScriptFunction::BoxState::GetNativeFrameDisplayIndex(FunctionBody * functionBody)
  409. {
  410. #if _M_IX86 || _M_AMD64
  411. if (functionBody->GetInParamsCount() == 0)
  412. {
  413. return (uintptr_t)JavascriptFunctionArgIndex_StackFrameDisplayNoArg;
  414. }
  415. else
  416. #endif
  417. {
  418. return (uintptr_t)JavascriptFunctionArgIndex_StackFrameDisplay;
  419. }
  420. }
  421. uintptr_t StackScriptFunction::BoxState::GetNativeScopeSlotsIndex(FunctionBody * functionBody)
  422. {
  423. #if _M_IX86 || _M_AMD64
  424. if (functionBody->GetInParamsCount() == 0)
  425. {
  426. return (uintptr_t)JavascriptFunctionArgIndex_StackScopeSlotsNoArg;
  427. }
  428. else
  429. #endif
  430. {
  431. return (uintptr_t)JavascriptFunctionArgIndex_StackScopeSlots;
  432. }
  433. }
  434. FrameDisplay * StackScriptFunction::BoxState::GetFrameDisplayFromNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody)
  435. {
  436. uintptr_t frameDisplayIndex = GetNativeFrameDisplayIndex(callerFunctionBody);
  437. void **argv = walker.GetCurrentArgv();
  438. return (Js::FrameDisplay*)argv[frameDisplayIndex];
  439. }
  440. Var * StackScriptFunction::BoxState::GetScopeSlotsFromNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody)
  441. {
  442. uintptr_t scopeSlotsIndex = GetNativeScopeSlotsIndex(callerFunctionBody);
  443. void **argv = walker.GetCurrentArgv();
  444. return (Var*)argv[scopeSlotsIndex];
  445. }
  446. void StackScriptFunction::BoxState::SetFrameDisplayFromNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody, FrameDisplay * frameDisplay)
  447. {
  448. uintptr_t frameDisplayIndex = GetNativeFrameDisplayIndex(callerFunctionBody);
  449. void **argv = walker.GetCurrentArgv();
  450. ((FrameDisplay**)argv)[frameDisplayIndex] = frameDisplay;
  451. }
  452. void StackScriptFunction::BoxState::SetScopeSlotsFromNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody, Var * scopeSlots)
  453. {
  454. uintptr_t scopeSlotsIndex = GetNativeScopeSlotsIndex(callerFunctionBody);
  455. void **argv = walker.GetCurrentArgv();
  456. ((Var**)argv)[scopeSlotsIndex] = scopeSlots;
  457. }
  458. void StackScriptFunction::BoxState::BoxNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody)
  459. {
  460. this->ForEachStackNestedFunctionNative(walker, callerFunctionBody, [&](ScriptFunction *curr)
  461. {
  462. StackScriptFunction * func = StackScriptFunction::FromVar(curr);
  463. // Need to check if we need the script function as the list of script function
  464. // include inlinee stack function that doesn't necessary need to be boxed
  465. if (this->NeedBoxScriptFunction(func))
  466. {
  467. // Box the stack function, even if they might not be "created" in the byte code yet.
  468. // Some of them will not be captured in slots, so we just need to box them and record it with the
  469. // stack func so that when we can just use the boxed value when we need it.
  470. this->BoxStackFunction(func);
  471. }
  472. });
  473. // Write back the boxed stack closure pointers at the designated stack locations.
  474. Js::FrameDisplay *stackFrameDisplay = this->GetFrameDisplayFromNativeFrame(walker, callerFunctionBody);
  475. if (ThreadContext::IsOnStack(stackFrameDisplay))
  476. {
  477. Js::FrameDisplay *boxedFrameDisplay = nullptr;
  478. if (boxedValues.TryGetValue(stackFrameDisplay, (void**)&boxedFrameDisplay))
  479. {
  480. this->SetFrameDisplayFromNativeFrame(walker, callerFunctionBody, boxedFrameDisplay);
  481. callerFunctionBody->GetScriptContext()->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_Accessor);
  482. }
  483. }
  484. Var *stackScopeSlots = this->GetScopeSlotsFromNativeFrame(walker, callerFunctionBody);
  485. if (ThreadContext::IsOnStack(stackScopeSlots))
  486. {
  487. Var *boxedScopeSlots = nullptr;
  488. if (boxedValues.TryGetValue(stackScopeSlots, (void**)&boxedScopeSlots))
  489. {
  490. this->SetScopeSlotsFromNativeFrame(walker, callerFunctionBody, boxedScopeSlots);
  491. callerFunctionBody->GetScriptContext()->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_Accessor);
  492. }
  493. }
  494. }
  495. template<class Fn>
  496. void StackScriptFunction::BoxState::ForEachStackNestedFunction(
  497. JavascriptStackWalker const& walker,
  498. FunctionBody *callerFunctionBody,
  499. Fn fn)
  500. {
  501. if (!callerFunctionBody->DoStackNestedFunc())
  502. {
  503. return;
  504. }
  505. InterpreterStackFrame *interpreterFrame = walker.GetCurrentInterpreterFrame();
  506. if (interpreterFrame)
  507. {
  508. this->ForEachStackNestedFunctionInterpreted(interpreterFrame, callerFunctionBody, fn);
  509. }
  510. else
  511. {
  512. this->ForEachStackNestedFunctionNative(walker, callerFunctionBody, fn);
  513. }
  514. }
  515. template<class Fn>
  516. void StackScriptFunction::BoxState::ForEachStackNestedFunctionInterpreted(
  517. InterpreterStackFrame *interpreterFrame,
  518. FunctionBody *callerFunctionBody,
  519. Fn fn)
  520. {
  521. uint nestedCount = callerFunctionBody->GetNestedCount();
  522. for (uint i = 0; i < nestedCount; i++)
  523. {
  524. ScriptFunction *scriptFunction = interpreterFrame->GetStackNestedFunction(i);
  525. fn(scriptFunction);
  526. }
  527. }
  528. template<class Fn>
  529. void StackScriptFunction::BoxState::ForEachStackNestedFunctionNative(
  530. JavascriptStackWalker const& walker,
  531. FunctionBody *callerFunctionBody,
  532. Fn fn)
  533. {
  534. if (walker.IsInlineFrame())
  535. {
  536. return;
  537. }
  538. void **argv = walker.GetCurrentArgv();
  539. // On arm, we always have an argument slot fo frames that has stack nested func
  540. Js::Var curr =
  541. #if _M_IX86 || _M_AMD64
  542. callerFunctionBody->GetInParamsCount() == 0?
  543. argv[JavascriptFunctionArgIndex_StackNestedFuncListWithNoArg]:
  544. #endif
  545. argv[JavascriptFunctionArgIndex_StackNestedFuncList];
  546. // TODO: It is possible to have a function that is marked as doing stack function
  547. // and we end up not JIT'ing any of them because they are deferred or don't
  548. // have the default type allocated already. We can turn this into an assert
  549. // when we start support JIT'ing that.
  550. if (curr != nullptr)
  551. {
  552. do
  553. {
  554. StackScriptFunction *func = StackScriptFunction::FromVar(curr);
  555. fn(func);
  556. curr = *(Js::Var *)(func + 1);
  557. }
  558. while (curr != nullptr);
  559. }
  560. }
  561. void StackScriptFunction::BoxState::Finish()
  562. {
  563. frameToBox.Map([](FunctionBody * body)
  564. {
  565. body->SetStackNestedFunc(false);
  566. });
  567. }
  568. FrameDisplay * StackScriptFunction::BoxState::BoxFrameDisplay(FrameDisplay * frameDisplay)
  569. {
  570. Assert(frameDisplay != nullptr);
  571. if (frameDisplay == &Js::NullFrameDisplay)
  572. {
  573. return frameDisplay;
  574. }
  575. FrameDisplay * boxedFrameDisplay = nullptr;
  576. if (boxedValues.TryGetValue(frameDisplay, (void **)&boxedFrameDisplay))
  577. {
  578. return boxedFrameDisplay;
  579. }
  580. // Create new frame display when we allocate the frame display on the stack
  581. uint16 length = frameDisplay->GetLength();
  582. if (!ThreadContext::IsOnStack(frameDisplay))
  583. {
  584. boxedFrameDisplay = frameDisplay;
  585. }
  586. else
  587. {
  588. boxedFrameDisplay = RecyclerNewPlus(scriptContext->GetRecycler(), length * sizeof(Var), FrameDisplay, length);
  589. }
  590. boxedValues.Add(frameDisplay, boxedFrameDisplay);
  591. for (uint16 i = 0; i < length; i++)
  592. {
  593. // TODO: Once we allocate the slots on the stack, we can only look those slots
  594. Field(Var) * pScope = (Field(Var) *)frameDisplay->GetItem(i);
  595. // We don't do stack slots if we exceed max encoded slot count
  596. if (ScopeSlots::Is(pScope))
  597. {
  598. pScope = BoxScopeSlots(pScope, static_cast<uint>(ScopeSlots(pScope).GetCount()));
  599. }
  600. boxedFrameDisplay->SetItem(i, pScope);
  601. frameDisplay->SetItem(i, pScope);
  602. }
  603. return boxedFrameDisplay;
  604. }
  605. Field(Var) * StackScriptFunction::BoxState::BoxScopeSlots(Field(Var) * slotArray, uint count)
  606. {
  607. Assert(slotArray != nullptr);
  608. Assert(count != 0);
  609. void * tmp = nullptr;
  610. if (boxedValues.TryGetValue(slotArray, &tmp))
  611. {
  612. return (Field(Var) *)tmp;
  613. }
  614. Field(Var) * boxedSlotArray = nullptr;
  615. if (!ThreadContext::IsOnStack(slotArray))
  616. {
  617. boxedSlotArray = slotArray;
  618. }
  619. else
  620. {
  621. // Create new scope slots when we allocate them on the stack
  622. boxedSlotArray = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), count + ScopeSlots::FirstSlotIndex);
  623. }
  624. boxedValues.Add(slotArray, boxedSlotArray);
  625. ScopeSlots scopeSlots(slotArray);
  626. ScopeSlots boxedScopeSlots(boxedSlotArray);
  627. boxedScopeSlots.SetCount(count);
  628. boxedScopeSlots.SetScopeMetadata(scopeSlots.GetScopeMetadataRaw());
  629. // Box all the stack function in the parent's scope slot as well
  630. for (uint i = 0; i < count; i++)
  631. {
  632. Js::Var slotValue = scopeSlots.Get(i);
  633. if (ScriptFunction::Is(slotValue))
  634. {
  635. ScriptFunction * stackFunction = ScriptFunction::FromVar(slotValue);
  636. slotValue = BoxStackFunction(stackFunction);
  637. }
  638. boxedScopeSlots.Set(i, slotValue);
  639. }
  640. return boxedSlotArray;
  641. }
  642. ScriptFunction * StackScriptFunction::BoxState::BoxStackFunction(ScriptFunction * scriptFunction)
  643. {
  644. // Box the frame display first, which may in turn box the function
  645. FrameDisplay * frameDisplay = scriptFunction->GetEnvironment();
  646. FrameDisplay * boxedFrameDisplay = BoxFrameDisplay(frameDisplay);
  647. if (!ThreadContext::IsOnStack(scriptFunction))
  648. {
  649. return scriptFunction;
  650. }
  651. StackScriptFunction * stackFunction = StackScriptFunction::FromVar(scriptFunction);
  652. ScriptFunction * boxedFunction = stackFunction->boxedScriptFunction;
  653. if (boxedFunction != nullptr)
  654. {
  655. return boxedFunction;
  656. }
  657. if (PHASE_TESTTRACE(Js::StackFuncPhase, stackFunction->GetFunctionProxy()))
  658. {
  659. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  660. Output::Print(_u("Boxing StackScriptFunction Object: %s (function Id: %s)"),
  661. stackFunction->GetFunctionProxy()->IsDeferredDeserializeFunction()?
  662. _u("<DeferDeserialize>") : stackFunction->GetParseableFunctionInfo()->GetDisplayName(),
  663. stackFunction->GetFunctionProxy()->GetDebugNumberSet(debugStringBuffer));
  664. if (PHASE_VERBOSE_TESTTRACE(Js::StackFuncPhase, stackFunction->GetFunctionProxy()))
  665. {
  666. Output::Print(_u(" %p\n"), stackFunction);
  667. }
  668. else
  669. {
  670. Output::Print(_u("\n"));
  671. }
  672. Output::Flush();
  673. }
  674. FunctionInfo * functionInfo = stackFunction->GetFunctionInfo();
  675. boxedFunction = ScriptFunction::OP_NewScFunc(boxedFrameDisplay,
  676. reinterpret_cast<FunctionInfoPtrPtr>(&functionInfo));
  677. stackFunction->boxedScriptFunction = boxedFunction;
  678. stackFunction->SetEnvironment(boxedFrameDisplay);
  679. return boxedFunction;
  680. }
  681. ScriptFunction * StackScriptFunction::OP_NewStackScFunc(FrameDisplay *environment, FunctionInfoPtrPtr infoRef, ScriptFunction * stackFunction)
  682. {
  683. if (stackFunction)
  684. {
  685. #if ENABLE_DEBUG_CONFIG_OPTIONS
  686. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  687. #endif
  688. FunctionProxy* functionProxy = (*infoRef)->GetFunctionProxy();
  689. AssertMsg(functionProxy != nullptr, "BYTE-CODE VERIFY: Must specify a valid function to create");
  690. Assert(stackFunction->GetFunctionInfo()->GetFunctionProxy() == functionProxy);
  691. Assert(!functionProxy->IsFunctionBody() || functionProxy->GetFunctionBody()->GetStackNestedFuncParentStrongRef() != nullptr);
  692. stackFunction->SetEnvironment(environment);
  693. PHASE_PRINT_VERBOSE_TRACE(Js::StackFuncPhase, functionProxy,
  694. _u("Stack alloc nested function: %s %s (address: %p)\n"),
  695. functionProxy->IsFunctionBody()?
  696. functionProxy->GetFunctionBody()->GetDisplayName() : _u("<deferred>"),
  697. functionProxy->GetDebugNumberSet(debugStringBuffer), stackFunction);
  698. return stackFunction;
  699. }
  700. return ScriptFunction::OP_NewScFunc(environment, infoRef);
  701. }
  702. #if ENABLE_TTD
  703. TTD::NSSnapObjects::SnapObjectType StackScriptFunction::GetSnapTag_TTD() const
  704. {
  705. //Make sure this isn't accidentally handled by parent class
  706. return TTD::NSSnapObjects::SnapObjectType::Invalid;
  707. }
  708. void StackScriptFunction::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
  709. {
  710. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<void*, TTD::NSSnapObjects::SnapObjectType::Invalid>(objData, nullptr);
  711. }
  712. #endif
  713. }