StackScriptFunction.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835
  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_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 = 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. }
  224. if (walker.IsBailedOutFromInlinee())
  225. {
  226. if (!walker.IsCurrentPhysicalFrameForLoopBody())
  227. {
  228. // this is the interpret frame from bailing out of inline frame
  229. // Just mark we have inlinee to box so we will walk the native frame's list when we get there.
  230. hasInlineeToBox = true;
  231. }
  232. }
  233. else if (walker.IsBailedOutFromFunction())
  234. {
  235. // The current interpret frame is from bailing out of a native frame.
  236. // Walk native frame that was bailed out as well.
  237. // The stack walker is pointing to the native frame already.
  238. this->BoxNativeFrame(walker, callerFunctionBody);
  239. }
  240. }
  241. else
  242. {
  243. if (walker.IsInlineFrame())
  244. {
  245. if (!walker.IsCurrentPhysicalFrameForLoopBody())
  246. {
  247. // We may have function that are not in slots. So we have to walk the stack function list of the inliner
  248. // to box all the needed function to catch those
  249. hasInlineeToBox = true;
  250. }
  251. }
  252. else
  253. {
  254. hasInlineeToBox = false;
  255. if (callerFunctionBody->DoStackFrameDisplay())
  256. {
  257. Js::FrameDisplay *stackFrameDisplay =
  258. this->GetFrameDisplayFromNativeFrame(walker, callerFunctionBody);
  259. // Local frame display may be null if bailout didn't restore it, which means we don't need it.
  260. if (stackFrameDisplay)
  261. {
  262. this->BoxFrameDisplay(stackFrameDisplay);
  263. }
  264. }
  265. if (callerFunctionBody->DoStackScopeSlots())
  266. {
  267. Field(Var)* stackScopeSlots = (Field(Var)*)this->GetScopeSlotsFromNativeFrame(walker, callerFunctionBody);
  268. if (stackScopeSlots)
  269. {
  270. // Scope slot pointer may be null if bailout didn't restore it, which means we don't need it.
  271. this->BoxScopeSlots(stackScopeSlots, static_cast<uint>(ScopeSlots(stackScopeSlots).GetCount()));
  272. }
  273. }
  274. // walk native frame
  275. this->BoxNativeFrame(walker, callerFunctionBody);
  276. }
  277. }
  278. }
  279. else if (callerFunctionBody->DoStackFrameDisplay() && !walker.IsInlineFrame())
  280. {
  281. // The case here is that a frame need not be boxed, but the closure environment in that frame
  282. // refers to an outer boxed frame.
  283. // Find the FD and walk it looking for a slot array that refers to a FB that must be boxed.
  284. // Everything from that point outward must be boxed.
  285. FrameDisplay *frameDisplay;
  286. InterpreterStackFrame *interpreterFrame = walker.GetCurrentInterpreterFrame();
  287. 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.
  288. if (interpreterFrame)
  289. {
  290. frameDisplay = interpreterFrame->GetLocalFrameDisplay();
  291. closureInitDone = interpreterFrame->IsClosureInitDone();
  292. }
  293. else
  294. {
  295. frameDisplay = (Js::FrameDisplay*)walker.GetCurrentArgv()[
  296. #if _M_IX86 || _M_AMD64
  297. callerFunctionBody->GetInParamsCount() == 0 ?
  298. JavascriptFunctionArgIndex_StackFrameDisplayNoArg :
  299. #endif
  300. JavascriptFunctionArgIndex_StackFrameDisplay];
  301. }
  302. if (ThreadContext::IsOnStack(frameDisplay) && closureInitDone)
  303. {
  304. int i;
  305. for (i = 0; i < frameDisplay->GetLength(); i++)
  306. {
  307. Field(Var) *slotArray = (Field(Var)*)frameDisplay->GetItem(i);
  308. if (ScopeSlots::Is(slotArray))
  309. {
  310. ScopeSlots slots(slotArray);
  311. if (!slots.IsDebuggerScopeSlotArray())
  312. {
  313. FunctionProxy *functionProxy = slots.GetFunctionInfo()->GetFunctionProxy();
  314. if (functionProxy->IsFunctionBody() && this->NeedBoxFrame(functionProxy->GetFunctionBody()))
  315. {
  316. break;
  317. }
  318. }
  319. }
  320. }
  321. for (; i < frameDisplay->GetLength(); i++)
  322. {
  323. Field(Var) *pScope = (Field(Var)*)frameDisplay->GetItem(i);
  324. if (ScopeSlots::Is(pScope))
  325. {
  326. Field(Var) *boxedSlots = this->BoxScopeSlots(pScope, static_cast<uint>(ScopeSlots(pScope).GetCount()));
  327. frameDisplay->SetItem(i, boxedSlots);
  328. }
  329. }
  330. }
  331. }
  332. ScriptFunction * boxedCaller = nullptr;
  333. if (this->NeedBoxScriptFunction(callerScriptFunction))
  334. {
  335. // TODO-STACK-NESTED-FUNC: Can't assert this yet, JIT might not do stack func allocation
  336. // if the function hasn't been parsed or deserialized yet.
  337. // Assert(ThreadContext::IsOnStack(callerScriptFunction));
  338. if (ThreadContext::IsOnStack(callerScriptFunction))
  339. {
  340. boxedCaller = this->BoxStackFunction(StackScriptFunction::FromVar(callerScriptFunction));
  341. walker.SetCurrentFunction(boxedCaller);
  342. InterpreterStackFrame * interpreterFrame = walker.GetCurrentInterpreterFrame();
  343. if (interpreterFrame)
  344. {
  345. interpreterFrame->SetExecutingStackFunction(boxedCaller);
  346. }
  347. }
  348. }
  349. // We don't need to box this frame, but we may still need to box the scope slot references
  350. // within nested frame displays if the slots they refer to have been boxed.
  351. if (callerFunctionBody->GetNestedCount() != 0)
  352. {
  353. this->ForEachStackNestedFunction(walker, callerFunctionBody, [&](ScriptFunction *nestedFunc)
  354. {
  355. this->UpdateFrameDisplay(nestedFunc);
  356. });
  357. }
  358. }
  359. Assert(!hasInlineeToBox);
  360. // We have to find one nested function
  361. this->Finish();
  362. }
  363. void StackScriptFunction::BoxState::UpdateFrameDisplay(ScriptFunction *nestedFunc)
  364. {
  365. // In some cases a function's frame display need not be boxed, but it may include outer scopes that
  366. // have been boxed. If that's the case, make sure that those scopes are updated.
  367. FrameDisplay *frameDisplay = nestedFunc->GetEnvironment();
  368. if (ThreadContext::IsOnStack(frameDisplay))
  369. {
  370. // The case here is a frame that doesn't define any captured locals, so it blindly grabs the parent
  371. // function's environment, which may have been boxed.
  372. FrameDisplay *boxedFrameDisplay = nullptr;
  373. if (boxedValues.TryGetValue(frameDisplay, (void **)&boxedFrameDisplay))
  374. {
  375. nestedFunc->SetEnvironment(boxedFrameDisplay);
  376. return;
  377. }
  378. }
  379. UpdateFrameDisplay(frameDisplay);
  380. }
  381. void StackScriptFunction::BoxState::UpdateFrameDisplay(FrameDisplay *frameDisplay)
  382. {
  383. for (uint i = 0; i < frameDisplay->GetLength(); i++)
  384. {
  385. Var* stackScopeSlots = (Var*)frameDisplay->GetItem(i);
  386. Var* boxedScopeSlots = nullptr;
  387. if (boxedValues.TryGetValue(stackScopeSlots, (void**)&boxedScopeSlots))
  388. {
  389. frameDisplay->SetItem(i, boxedScopeSlots);
  390. }
  391. }
  392. }
  393. uintptr_t StackScriptFunction::BoxState::GetNativeFrameDisplayIndex(FunctionBody * functionBody)
  394. {
  395. #if _M_IX86 || _M_AMD64
  396. if (functionBody->GetInParamsCount() == 0)
  397. {
  398. return (uintptr_t)JavascriptFunctionArgIndex_StackFrameDisplayNoArg;
  399. }
  400. else
  401. #endif
  402. {
  403. return (uintptr_t)JavascriptFunctionArgIndex_StackFrameDisplay;
  404. }
  405. }
  406. uintptr_t StackScriptFunction::BoxState::GetNativeScopeSlotsIndex(FunctionBody * functionBody)
  407. {
  408. #if _M_IX86 || _M_AMD64
  409. if (functionBody->GetInParamsCount() == 0)
  410. {
  411. return (uintptr_t)JavascriptFunctionArgIndex_StackScopeSlotsNoArg;
  412. }
  413. else
  414. #endif
  415. {
  416. return (uintptr_t)JavascriptFunctionArgIndex_StackScopeSlots;
  417. }
  418. }
  419. uintptr_t StackScriptFunction::BoxState::GetInlineeFrameDisplaysIndex(FunctionBody * functionBody)
  420. {
  421. #if _M_IX86 || _M_AMD64
  422. if (functionBody->GetInParamsCount() == 0)
  423. {
  424. return (uintptr_t)JavascriptFunctionArgIndex_InlineeFrameDisplaysNoArg;
  425. }
  426. else
  427. #endif
  428. {
  429. return (uintptr_t)JavascriptFunctionArgIndex_InlineeFrameDisplays;
  430. }
  431. }
  432. FrameDisplay * StackScriptFunction::BoxState::GetFrameDisplayFromNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody)
  433. {
  434. uintptr_t frameDisplayIndex = GetNativeFrameDisplayIndex(callerFunctionBody);
  435. void **argv = walker.GetCurrentArgv();
  436. return (Js::FrameDisplay*)argv[frameDisplayIndex];
  437. }
  438. Var * StackScriptFunction::BoxState::GetScopeSlotsFromNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody)
  439. {
  440. uintptr_t scopeSlotsIndex = GetNativeScopeSlotsIndex(callerFunctionBody);
  441. void **argv = walker.GetCurrentArgv();
  442. return (Var*)argv[scopeSlotsIndex];
  443. }
  444. FrameDisplay * StackScriptFunction::BoxState::GetInlineeFrameDisplaysFromNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody)
  445. {
  446. uintptr_t inlineeFrameDisplaysIndex = GetInlineeFrameDisplaysIndex(callerFunctionBody);
  447. void **argv = walker.GetCurrentArgv();
  448. return (FrameDisplay*)argv[inlineeFrameDisplaysIndex];
  449. }
  450. void StackScriptFunction::BoxState::SetFrameDisplayFromNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody, FrameDisplay * frameDisplay)
  451. {
  452. uintptr_t frameDisplayIndex = GetNativeFrameDisplayIndex(callerFunctionBody);
  453. void **argv = walker.GetCurrentArgv();
  454. ((FrameDisplay**)argv)[frameDisplayIndex] = frameDisplay;
  455. }
  456. void StackScriptFunction::BoxState::SetScopeSlotsFromNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody, Var * scopeSlots)
  457. {
  458. uintptr_t scopeSlotsIndex = GetNativeScopeSlotsIndex(callerFunctionBody);
  459. void **argv = walker.GetCurrentArgv();
  460. ((Var**)argv)[scopeSlotsIndex] = scopeSlots;
  461. }
  462. void StackScriptFunction::BoxState::BoxNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody)
  463. {
  464. this->ForEachStackNestedFunctionNative(walker, callerFunctionBody, [&](ScriptFunction *curr)
  465. {
  466. StackScriptFunction * func = StackScriptFunction::FromVar(curr);
  467. // Need to check if we need the script function as the list of script function
  468. // include inlinee stack function that doesn't necessary need to be boxed
  469. if (this->NeedBoxScriptFunction(func))
  470. {
  471. // Box the stack function, even if they might not be "created" in the byte code yet.
  472. // Some of them will not be captured in slots, so we just need to box them and record it with the
  473. // stack func so that when we can just use the boxed value when we need it.
  474. this->BoxStackFunction(func);
  475. }
  476. });
  477. // Write back the boxed stack closure pointers at the designated stack locations.
  478. Js::FrameDisplay *stackFrameDisplay = this->GetFrameDisplayFromNativeFrame(walker, callerFunctionBody);
  479. if (ThreadContext::IsOnStack(stackFrameDisplay))
  480. {
  481. Js::FrameDisplay *boxedFrameDisplay = nullptr;
  482. if (boxedValues.TryGetValue(stackFrameDisplay, (void**)&boxedFrameDisplay))
  483. {
  484. this->SetFrameDisplayFromNativeFrame(walker, callerFunctionBody, boxedFrameDisplay);
  485. callerFunctionBody->GetScriptContext()->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_Accessor);
  486. }
  487. }
  488. Var *stackScopeSlots = this->GetScopeSlotsFromNativeFrame(walker, callerFunctionBody);
  489. if (ThreadContext::IsOnStack(stackScopeSlots))
  490. {
  491. Var *boxedScopeSlots = nullptr;
  492. if (boxedValues.TryGetValue(stackScopeSlots, (void**)&boxedScopeSlots))
  493. {
  494. this->SetScopeSlotsFromNativeFrame(walker, callerFunctionBody, boxedScopeSlots);
  495. callerFunctionBody->GetScriptContext()->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_Accessor);
  496. }
  497. }
  498. this->ForEachInlineeFrameDisplay(walker, callerFunctionBody, [&](FrameDisplay *frameDisplay)
  499. {
  500. // Update all the inlinee frame displays, which are not stack-allocated but may refer to scopes on the stack.
  501. // This is only necessary in a native frame that does stack frame displays
  502. this->UpdateFrameDisplay(frameDisplay);
  503. });
  504. }
  505. template<class Fn>
  506. void StackScriptFunction::BoxState::ForEachStackNestedFunction(
  507. JavascriptStackWalker const& walker,
  508. FunctionBody *callerFunctionBody,
  509. Fn fn)
  510. {
  511. if (!callerFunctionBody->DoStackNestedFunc())
  512. {
  513. return;
  514. }
  515. InterpreterStackFrame *interpreterFrame = walker.GetCurrentInterpreterFrame();
  516. if (interpreterFrame)
  517. {
  518. this->ForEachStackNestedFunctionInterpreted(interpreterFrame, callerFunctionBody, fn);
  519. }
  520. else
  521. {
  522. this->ForEachStackNestedFunctionNative(walker, callerFunctionBody, fn);
  523. }
  524. }
  525. template<class Fn>
  526. void StackScriptFunction::BoxState::ForEachStackNestedFunctionInterpreted(
  527. InterpreterStackFrame *interpreterFrame,
  528. FunctionBody *callerFunctionBody,
  529. Fn fn)
  530. {
  531. uint nestedCount = callerFunctionBody->GetNestedCount();
  532. for (uint i = 0; i < nestedCount; i++)
  533. {
  534. ScriptFunction *scriptFunction = interpreterFrame->GetStackNestedFunction(i);
  535. fn(scriptFunction);
  536. }
  537. }
  538. template<class Fn>
  539. void StackScriptFunction::BoxState::ForEachStackNestedFunctionNative(
  540. JavascriptStackWalker const& walker,
  541. FunctionBody *callerFunctionBody,
  542. Fn fn)
  543. {
  544. if (walker.IsInlineFrame())
  545. {
  546. return;
  547. }
  548. void **argv = walker.GetCurrentArgv();
  549. // On arm, we always have an argument slot fo frames that has stack nested func
  550. Js::Var curr =
  551. #if _M_IX86 || _M_AMD64
  552. callerFunctionBody->GetInParamsCount() == 0?
  553. argv[JavascriptFunctionArgIndex_StackNestedFuncListWithNoArg]:
  554. #endif
  555. argv[JavascriptFunctionArgIndex_StackNestedFuncList];
  556. // TODO: It is possible to have a function that is marked as doing stack function
  557. // and we end up not JIT'ing any of them because they are deferred or don't
  558. // have the default type allocated already. We can turn this into an assert
  559. // when we start support JIT'ing that.
  560. if (curr != nullptr)
  561. {
  562. do
  563. {
  564. StackScriptFunction *func = StackScriptFunction::FromVar(curr);
  565. fn(func);
  566. curr = *(Js::Var *)(func + 1);
  567. }
  568. while (curr != nullptr);
  569. }
  570. }
  571. template<class Fn>
  572. void StackScriptFunction::BoxState::ForEachInlineeFrameDisplay(
  573. JavascriptStackWalker const& walker,
  574. FunctionBody *callerFunctionBody,
  575. Fn fn)
  576. {
  577. if (!callerFunctionBody->DoStackFrameDisplay() || walker.GetCurrentInterpreterFrame() != nullptr || walker.IsInlineFrame())
  578. {
  579. return;
  580. }
  581. #ifdef MD_GROW_LOCALS_AREA_UP
  582. // Stack closures not supported for layouts like ARM. We shouldn't get here.
  583. AssertOrFailFast(0);
  584. #endif
  585. void **argv = walker.GetCurrentArgv();
  586. FrameDisplay ** curr = (FrameDisplay**)(
  587. #if _M_IX86 || _M_AMD64
  588. callerFunctionBody->GetInParamsCount() == 0?
  589. &argv[JavascriptFunctionArgIndex_InlineeFrameDisplaysNoArg]:
  590. #endif
  591. &argv[JavascriptFunctionArgIndex_InlineeFrameDisplays]);
  592. while (*curr != nullptr)
  593. {
  594. fn(*curr);
  595. curr--;
  596. }
  597. }
  598. void StackScriptFunction::BoxState::Finish()
  599. {
  600. frameToBox.Map([](FunctionBody * body)
  601. {
  602. body->SetStackNestedFunc(false);
  603. });
  604. }
  605. FrameDisplay * StackScriptFunction::BoxState::BoxFrameDisplay(FrameDisplay * frameDisplay)
  606. {
  607. Assert(frameDisplay != nullptr);
  608. if (frameDisplay == &Js::NullFrameDisplay)
  609. {
  610. return frameDisplay;
  611. }
  612. FrameDisplay * boxedFrameDisplay = nullptr;
  613. if (boxedValues.TryGetValue(frameDisplay, (void **)&boxedFrameDisplay))
  614. {
  615. return boxedFrameDisplay;
  616. }
  617. // Create new frame display when we allocate the frame display on the stack
  618. uint16 length = frameDisplay->GetLength();
  619. if (!ThreadContext::IsOnStack(frameDisplay))
  620. {
  621. boxedFrameDisplay = frameDisplay;
  622. }
  623. else
  624. {
  625. boxedFrameDisplay = RecyclerNewPlus(scriptContext->GetRecycler(), length * sizeof(Var), FrameDisplay, length);
  626. }
  627. boxedValues.Add(frameDisplay, boxedFrameDisplay);
  628. for (uint16 i = 0; i < length; i++)
  629. {
  630. // TODO: Once we allocate the slots on the stack, we can only look those slots
  631. Field(Var) * pScope = (Field(Var) *)frameDisplay->GetItem(i);
  632. // We don't do stack slots if we exceed max encoded slot count
  633. if (ScopeSlots::Is(pScope))
  634. {
  635. pScope = BoxScopeSlots(pScope, static_cast<uint>(ScopeSlots(pScope).GetCount()));
  636. }
  637. boxedFrameDisplay->SetItem(i, pScope);
  638. frameDisplay->SetItem(i, pScope);
  639. }
  640. return boxedFrameDisplay;
  641. }
  642. Field(Var) * StackScriptFunction::BoxState::BoxScopeSlots(Field(Var) * slotArray, uint count)
  643. {
  644. Assert(slotArray != nullptr);
  645. Assert(count != 0);
  646. void * tmp = nullptr;
  647. if (boxedValues.TryGetValue(slotArray, &tmp))
  648. {
  649. return (Field(Var) *)tmp;
  650. }
  651. Field(Var) * boxedSlotArray = nullptr;
  652. if (!ThreadContext::IsOnStack(slotArray))
  653. {
  654. boxedSlotArray = slotArray;
  655. }
  656. else
  657. {
  658. // Create new scope slots when we allocate them on the stack
  659. boxedSlotArray = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), count + ScopeSlots::FirstSlotIndex);
  660. }
  661. boxedValues.Add(slotArray, boxedSlotArray);
  662. ScopeSlots scopeSlots(slotArray);
  663. ScopeSlots boxedScopeSlots(boxedSlotArray);
  664. boxedScopeSlots.SetCount(count);
  665. boxedScopeSlots.SetScopeMetadata(scopeSlots.GetScopeMetadataRaw());
  666. // Box all the stack function in the parent's scope slot as well
  667. for (uint i = 0; i < count; i++)
  668. {
  669. Js::Var slotValue = scopeSlots.Get(i);
  670. if (ScriptFunction::Is(slotValue))
  671. {
  672. ScriptFunction * stackFunction = ScriptFunction::FromVar(slotValue);
  673. slotValue = BoxStackFunction(stackFunction);
  674. }
  675. boxedScopeSlots.Set(i, slotValue);
  676. }
  677. return boxedSlotArray;
  678. }
  679. ScriptFunction * StackScriptFunction::BoxState::BoxStackFunction(ScriptFunction * scriptFunction)
  680. {
  681. // Box the frame display first, which may in turn box the function
  682. FrameDisplay * frameDisplay = scriptFunction->GetEnvironment();
  683. FrameDisplay * boxedFrameDisplay = BoxFrameDisplay(frameDisplay);
  684. if (!ThreadContext::IsOnStack(scriptFunction))
  685. {
  686. return scriptFunction;
  687. }
  688. StackScriptFunction * stackFunction = StackScriptFunction::FromVar(scriptFunction);
  689. ScriptFunction * boxedFunction = stackFunction->boxedScriptFunction;
  690. if (boxedFunction != nullptr)
  691. {
  692. return boxedFunction;
  693. }
  694. if (PHASE_TESTTRACE(Js::StackFuncPhase, stackFunction->GetFunctionProxy()))
  695. {
  696. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  697. Output::Print(_u("Boxing StackScriptFunction Object: %s (function Id: %s)"),
  698. stackFunction->GetFunctionProxy()->IsDeferredDeserializeFunction()?
  699. _u("<DeferDeserialize>") : stackFunction->GetParseableFunctionInfo()->GetDisplayName(),
  700. stackFunction->GetFunctionProxy()->GetDebugNumberSet(debugStringBuffer));
  701. if (PHASE_VERBOSE_TESTTRACE(Js::StackFuncPhase, stackFunction->GetFunctionProxy()))
  702. {
  703. Output::Print(_u(" %p\n"), stackFunction);
  704. }
  705. else
  706. {
  707. Output::Print(_u("\n"));
  708. }
  709. Output::Flush();
  710. }
  711. FunctionInfo * functionInfo = stackFunction->GetFunctionInfo();
  712. boxedFunction = ScriptFunction::OP_NewScFunc(boxedFrameDisplay,
  713. reinterpret_cast<FunctionInfoPtrPtr>(&functionInfo));
  714. stackFunction->boxedScriptFunction = boxedFunction;
  715. stackFunction->SetEnvironment(boxedFrameDisplay);
  716. return boxedFunction;
  717. }
  718. ScriptFunction * StackScriptFunction::OP_NewStackScFunc(FrameDisplay *environment, FunctionInfoPtrPtr infoRef, ScriptFunction * stackFunction)
  719. {
  720. if (stackFunction)
  721. {
  722. #if ENABLE_DEBUG_CONFIG_OPTIONS
  723. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  724. #endif
  725. FunctionProxy* functionProxy = (*infoRef)->GetFunctionProxy();
  726. AssertMsg(functionProxy != nullptr, "BYTE-CODE VERIFY: Must specify a valid function to create");
  727. Assert(stackFunction->GetFunctionInfo()->GetFunctionProxy() == functionProxy);
  728. Assert(!functionProxy->IsFunctionBody() || functionProxy->GetFunctionBody()->GetStackNestedFuncParentStrongRef() != nullptr);
  729. stackFunction->SetEnvironment(environment);
  730. PHASE_PRINT_VERBOSE_TRACE(Js::StackFuncPhase, functionProxy,
  731. _u("Stack alloc nested function: %s %s (address: %p)\n"),
  732. functionProxy->IsFunctionBody()?
  733. functionProxy->GetFunctionBody()->GetDisplayName() : _u("<deferred>"),
  734. functionProxy->GetDebugNumberSet(debugStringBuffer), stackFunction);
  735. return stackFunction;
  736. }
  737. return ScriptFunction::OP_NewScFunc(environment, infoRef);
  738. }
  739. #if ENABLE_TTD
  740. TTD::NSSnapObjects::SnapObjectType StackScriptFunction::GetSnapTag_TTD() const
  741. {
  742. //Make sure this isn't accidentally handled by parent class
  743. return TTD::NSSnapObjects::SnapObjectType::Invalid;
  744. }
  745. void StackScriptFunction::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
  746. {
  747. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<void*, TTD::NSSnapObjects::SnapObjectType::Invalid>(objData, nullptr);
  748. }
  749. #endif
  750. }