StackScriptFunction.cpp 34 KB

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