StackScriptFunction.cpp 34 KB

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