blob: 00b93184867e49b9e6ab13bba70217ece0e9cf1c [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeLibraryPch.h"
#include "Language/JavascriptFunctionArgIndex.h"
#include "Language/InterpreterStackFrame.h"
#include "Library/StackScriptFunction.h"
namespace Js
{
JavascriptFunction *
StackScriptFunction::EnsureBoxed(BOX_PARAM(JavascriptFunction * function, void * returnAddress, char16 const * reason))
{
#if ENABLE_DEBUG_CONFIG_OPTIONS
char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
#endif
if (!ThreadContext::IsOnStack(function))
{
return function;
}
// Only script function can be on the stack
StackScriptFunction * stackScriptFunction = StackScriptFunction::FromVar(function);
ScriptFunction * boxedFunction = stackScriptFunction->boxedScriptFunction;
if (boxedFunction != nullptr)
{
// We have already boxed this stack function before, and the function
// wasn't on any slot or not a caller that we can replace.
// Just give out the function we boxed before
return boxedFunction;
}
PHASE_PRINT_TESTTRACE(Js::StackFuncPhase, function->GetFunctionProxy(),
_u("StackScriptFunction (%s): box and disable stack function: %s (function %s)\n"),
reason, function->GetFunctionProxy()->IsDeferredDeserializeFunction()?
_u("<DeferDeserialize>") : function->GetParseableFunctionInfo()->GetDisplayName(),
function->GetFunctionProxy()->GetDebugNumberSet(debugStringBuffer));
// During the box workflow we reset all the parents of all nested functions and up. If a fault occurs when the stack function
// is created this will cause further issues when trying to use the function object again. So failing faster seems to make more sense
try
{
boxedFunction = StackScriptFunction::Box(stackScriptFunction, returnAddress);
}
catch (Js::OutOfMemoryException)
{
FailedToBox_OOM_fatal_error((ULONG_PTR)stackScriptFunction);
}
return boxedFunction;
}
JavascriptFunction *
StackScriptFunction::GetCurrentFunctionObject(JavascriptFunction * function)
{
if (!ThreadContext::IsOnStack(function))
{
return function;
}
ScriptFunction * boxed = StackScriptFunction::FromVar(function)->boxedScriptFunction;
return boxed ? boxed : function;
}
StackScriptFunction *
StackScriptFunction::FromVar(Var var)
{
Assert(ScriptFunction::Is(var));
Assert(ThreadContext::IsOnStack(var));
return static_cast<StackScriptFunction *>(var);
}
ScriptFunction *
StackScriptFunction::Box(StackScriptFunction *stackScriptFunction, void * returnAddress)
{
Assert(ThreadContext::IsOnStack(stackScriptFunction));
Assert(stackScriptFunction->boxedScriptFunction == nullptr);
FunctionBody * functionParent = stackScriptFunction->GetFunctionBody()->GetStackNestedFuncParentStrongRef();
Assert(functionParent != nullptr);
ScriptContext * scriptContext = stackScriptFunction->GetScriptContext();
ScriptFunction * boxedFunction;
BEGIN_TEMP_ALLOCATOR(tempAllocator, scriptContext, _u("BoxStackFunction"));
{
BoxState state(tempAllocator, functionParent, scriptContext, returnAddress);
state.Box();
boxedFunction = stackScriptFunction->boxedScriptFunction;
Assert(boxedFunction != nullptr);
}
END_TEMP_ALLOCATOR(tempAllocator, scriptContext);
return boxedFunction;
}
void StackScriptFunction::Box(Js::FunctionBody * parent, ScriptFunction ** functionRef)
{
ScriptContext * scriptContext = parent->GetScriptContext();
BEGIN_TEMP_ALLOCATOR(tempAllocator, scriptContext, _u("BoxStackFunction"));
{
BoxState state(tempAllocator, parent, scriptContext);
state.Box();
if (functionRef != nullptr && ThreadContext::IsOnStack(*functionRef))
{
ScriptFunction * boxedScriptFunction = StackScriptFunction::FromVar(*functionRef)->boxedScriptFunction;
if (boxedScriptFunction != nullptr)
{
*functionRef = boxedScriptFunction;
}
}
}
END_TEMP_ALLOCATOR(tempAllocator, scriptContext);
}
StackScriptFunction::BoxState::BoxState(ArenaAllocator * alloc, FunctionBody * functionBody, ScriptContext * scriptContext, void * returnAddress) :
frameToBox(alloc), functionObjectToBox(alloc), boxedValues(alloc), scriptContext(scriptContext), returnAddress(returnAddress)
{
Assert(functionBody->DoStackNestedFunc() && functionBody->GetNestedCount() != 0);
FunctionBody * current = functionBody;
do
{
frameToBox.Add(current);
for (uint i = 0; i < current->GetNestedCount(); i++)
{
FunctionProxy * nested = current->GetNestedFunc(i);
functionObjectToBox.Add(nested->GetFunctionProxy());
if (nested->IsFunctionBody())
{
nested->GetFunctionBody()->ClearStackNestedFuncParent();
}
}
current = current->GetAndClearStackNestedFuncParent();
}
while (current && current->DoStackNestedFunc());
}
bool StackScriptFunction::BoxState::NeedBoxFrame(FunctionBody * functionBody)
{
return frameToBox.Contains(functionBody);
}
bool StackScriptFunction::BoxState::NeedBoxScriptFunction(ScriptFunction * scriptFunction)
{
return functionObjectToBox.Contains(scriptFunction->GetFunctionProxy()->GetFunctionProxy());
}
void StackScriptFunction::BoxState::Box()
{
JavascriptStackWalker walker(scriptContext, true, returnAddress);
JavascriptFunction * caller;
bool hasInlineeToBox = false;
while (walker.GetCaller(&caller))
{
if (!caller->IsScriptFunction())
{
continue;
}
ScriptFunction * callerScriptFunction = ScriptFunction::FromVar(caller);
FunctionBody * callerFunctionBody = callerScriptFunction->GetFunctionBody();
if (hasInlineeToBox || this->NeedBoxFrame(callerFunctionBody))
{
// Box the frame display, but don't need to box the function unless we see them
// in the slots.
// If the frame display has any stack nested function, then it must have given to one of the
// stack functions. If it doesn't appear in on eof the stack function , the frame display
// doesn't contain any stack function
InterpreterStackFrame * interpreterFrame = walker.GetCurrentInterpreterFrame();
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
if (this->NeedBoxFrame(callerFunctionBody) || (hasInlineeToBox && !walker.IsInlineFrame()))
{
char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
char16 const * frameKind;
if (interpreterFrame)
{
Assert(!hasInlineeToBox);
frameKind = walker.IsBailedOutFromInlinee()? _u("Interpreted from Inlined Bailout (Pending)") :
walker.IsBailedOutFromFunction()? _u("Interpreted from Bailout") : _u("Interpreted");
}
else if (walker.IsInlineFrame())
{
Assert(this->NeedBoxFrame(callerFunctionBody));
frameKind = _u("Native Inlined (Pending)");
}
else if (this->NeedBoxFrame(callerFunctionBody))
{
frameKind = (hasInlineeToBox? _u("Native and Inlinee") : _u("Native"));
}
else
{
frameKind = _u("Native for Inlinee");
}
PHASE_PRINT_TESTTRACE(Js::StackFuncPhase, callerFunctionBody,
_u("Boxing Frame [%s]: %s %s\n"), frameKind,
callerFunctionBody->GetDisplayName(), callerFunctionBody->GetDebugNumberSet(debugStringBuffer));
}
#endif
if (interpreterFrame)
{
Assert(!hasInlineeToBox);
Assert(StackScriptFunction::GetCurrentFunctionObject(interpreterFrame->GetJavascriptFunction()) == caller);
if (callerFunctionBody->DoStackFrameDisplay())
{
Js::FrameDisplay *stackFrameDisplay = interpreterFrame->GetLocalFrameDisplay();
// Local frame display may be null if bailout didn't restore it, which means we don't need it.
if (stackFrameDisplay)
{
Js::FrameDisplay *boxedFrameDisplay = this->BoxFrameDisplay(stackFrameDisplay);
interpreterFrame->SetLocalFrameDisplay(boxedFrameDisplay);
}
}
if (callerFunctionBody->DoStackScopeSlots())
{
Var* stackScopeSlots = (Var*)interpreterFrame->GetLocalClosure();
if (stackScopeSlots)
{
// Scope slot pointer may be null if bailout didn't restore it, which means we don't need it.
Var* boxedScopeSlots = this->BoxScopeSlots(stackScopeSlots, ScopeSlots(stackScopeSlots).GetCount());
interpreterFrame->SetLocalClosure((Var)boxedScopeSlots);
}
}
uint nestedCount = callerFunctionBody->GetNestedCount();
for (uint i = 0; i < nestedCount; i++)
{
// Box the stack function, even if they might not be "created" in the byte code yet.
// Some of them will not be captured in slots, so we just need to box them and record it with the
// stack func so that when we can just use the boxed value when we need it.
StackScriptFunction * stackFunction = interpreterFrame->GetStackNestedFunction(i);
ScriptFunction * boxedFunction = this->BoxStackFunction(stackFunction);
Assert(stackFunction->boxedScriptFunction == boxedFunction);
this->UpdateFrameDisplay(stackFunction);
}
if (walker.IsBailedOutFromInlinee())
{
if (!walker.IsCurrentPhysicalFrameForLoopBody())
{
// this is the interpret frame from bailing out of inline frame
// Just mark we have inlinee to box so we will walk the native frame's list when we get there.
hasInlineeToBox = true;
}
}
else if (walker.IsBailedOutFromFunction())
{
// The current interpret frame is from bailing out of a native frame.
// Walk native frame that was bailed out as well.
// The stack walker is pointing to the native frame already.
this->BoxNativeFrame(walker, callerFunctionBody);
// We don't need to box this frame, but we may still need to box the scope slot references
// within nested frame displays if the slots they refer to have been boxed.
if (callerFunctionBody->GetNestedCount() != 0)
{
this->ForEachStackNestedFunctionNative(walker, callerFunctionBody, [&](ScriptFunction *nestedFunc)
{
this->UpdateFrameDisplay(nestedFunc);
});
}
}
}
else
{
if (walker.IsInlineFrame())
{
if (!walker.IsCurrentPhysicalFrameForLoopBody())
{
// We may have function that are not in slots. So we have to walk the stack function list of the inliner
// to box all the needed function to catch those
hasInlineeToBox = true;
}
}
else
{
hasInlineeToBox = false;
if (callerFunctionBody->DoStackFrameDisplay())
{
Js::FrameDisplay *stackFrameDisplay =
this->GetFrameDisplayFromNativeFrame(walker, callerFunctionBody);
// Local frame display may be null if bailout didn't restore it, which means we don't need it.
if (stackFrameDisplay)
{
this->BoxFrameDisplay(stackFrameDisplay);
}
}
if (callerFunctionBody->DoStackScopeSlots())
{
Var* stackScopeSlots = this->GetScopeSlotsFromNativeFrame(walker, callerFunctionBody);
if (stackScopeSlots)
{
// Scope slot pointer may be null if bailout didn't restore it, which means we don't need it.
this->BoxScopeSlots(stackScopeSlots, ScopeSlots(stackScopeSlots).GetCount());
}
}
// walk native frame
this->BoxNativeFrame(walker, callerFunctionBody);
// We don't need to box this frame, but we may still need to box the scope slot references
// within nested frame displays if the slots they refer to have been boxed.
if (callerFunctionBody->GetNestedCount() != 0)
{
this->ForEachStackNestedFunctionNative(walker, callerFunctionBody, [&](ScriptFunction *nestedFunc)
{
this->UpdateFrameDisplay(nestedFunc);
});
}
}
}
}
else if (callerFunctionBody->DoStackFrameDisplay() && !walker.IsInlineFrame())
{
// The case here is that a frame need not be boxed, but the closure environment in that frame
// refers to an outer boxed frame.
// Find the FD and walk it looking for a slot array that refers to a FB that must be boxed.
// Everything from that point outward must be boxed.
FrameDisplay *frameDisplay;
InterpreterStackFrame *interpreterFrame = walker.GetCurrentInterpreterFrame();
if (interpreterFrame)
{
frameDisplay = interpreterFrame->GetLocalFrameDisplay();
}
else
{
frameDisplay = (Js::FrameDisplay*)walker.GetCurrentArgv()[
#if _M_IX86 || _M_AMD64
callerFunctionBody->GetInParamsCount() == 0 ?
JavascriptFunctionArgIndex_StackFrameDisplayNoArg :
#endif
JavascriptFunctionArgIndex_StackFrameDisplay];
}
if (ThreadContext::IsOnStack(frameDisplay))
{
int i;
for (i = 0; i < frameDisplay->GetLength(); i++)
{
Var *slotArray = (Var*)frameDisplay->GetItem(i);
ScopeSlots slots(slotArray);
if (slots.IsFunctionScopeSlotArray())
{
FunctionBody *functionBody = slots.GetFunctionBody();
if (this->NeedBoxFrame(functionBody))
{
break;
}
}
}
for (; i < frameDisplay->GetLength(); i++)
{
Var *scopeSlots = (Var*)frameDisplay->GetItem(i);
size_t count = ScopeSlots(scopeSlots).GetCount();
if (count < ScopeSlots::MaxEncodedSlotCount)
{
Var *boxedSlots = this->BoxScopeSlots(scopeSlots, static_cast<uint>(count));
frameDisplay->SetItem(i, boxedSlots);
}
}
}
}
ScriptFunction * boxedCaller = nullptr;
if (this->NeedBoxScriptFunction(callerScriptFunction))
{
// TODO-STACK-NESTED-FUNC: Can't assert this yet, JIT might not do stack func allocation
// if the function hasn't been parsed or deserialized yet.
// Assert(ThreadContext::IsOnStack(callerScriptFunction));
if (ThreadContext::IsOnStack(callerScriptFunction))
{
boxedCaller = this->BoxStackFunction(StackScriptFunction::FromVar(callerScriptFunction));
walker.SetCurrentFunction(boxedCaller);
InterpreterStackFrame * interpreterFrame = walker.GetCurrentInterpreterFrame();
if (interpreterFrame)
{
interpreterFrame->SetExecutingStackFunction(boxedCaller);
}
// We don't need to box this frame, but we may still need to box the scope slot references
// within nested frame displays if the slots they refer to have been boxed.
if (callerFunctionBody->GetNestedCount() != 0)
{
this->ForEachStackNestedFunction(walker, callerFunctionBody, [&](ScriptFunction *nestedFunc)
{
this->UpdateFrameDisplay(nestedFunc);
});
}
}
}
}
Assert(!hasInlineeToBox);
// We have to find one nested function
this->Finish();
}
void StackScriptFunction::BoxState::UpdateFrameDisplay(ScriptFunction *nestedFunc)
{
// In some cases a function's frame display need not be boxed, but it may include outer scopes that
// have been boxed. If that's the case, make sure that those scopes are updated.
FrameDisplay *frameDisplay = nestedFunc->GetEnvironment();
if (ThreadContext::IsOnStack(frameDisplay))
{
// The case here is a frame that doesn't define any captured locals, so it blindly grabs the parent
// function's environment, which may have been boxed.
FrameDisplay *boxedFrameDisplay;
if (boxedValues.TryGetValue(frameDisplay, (void **)&boxedFrameDisplay))
{
nestedFunc->SetEnvironment(boxedFrameDisplay);
return;
}
}
for (uint i = 0; i < frameDisplay->GetLength(); i++)
{
Var* stackScopeSlots = (Var*)frameDisplay->GetItem(i);
Var* boxedScopeSlots;
if (boxedValues.TryGetValue(stackScopeSlots, (void**)&boxedScopeSlots))
{
frameDisplay->SetItem(i, boxedScopeSlots);
}
}
}
uintptr_t StackScriptFunction::BoxState::GetNativeFrameDisplayIndex(FunctionBody * functionBody)
{
#if _M_IX86 || _M_AMD64
if (functionBody->GetInParamsCount() == 0)
{
return (uintptr_t)JavascriptFunctionArgIndex_StackFrameDisplayNoArg;
}
else
#endif
{
return (uintptr_t)JavascriptFunctionArgIndex_StackFrameDisplay;
}
}
uintptr_t StackScriptFunction::BoxState::GetNativeScopeSlotsIndex(FunctionBody * functionBody)
{
#if _M_IX86 || _M_AMD64
if (functionBody->GetInParamsCount() == 0)
{
return (uintptr_t)JavascriptFunctionArgIndex_StackScopeSlotsNoArg;
}
else
#endif
{
return (uintptr_t)JavascriptFunctionArgIndex_StackScopeSlots;
}
}
FrameDisplay * StackScriptFunction::BoxState::GetFrameDisplayFromNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody)
{
uintptr_t frameDisplayIndex = GetNativeFrameDisplayIndex(callerFunctionBody);
void **argv = walker.GetCurrentArgv();
return (Js::FrameDisplay*)argv[frameDisplayIndex];
}
Var * StackScriptFunction::BoxState::GetScopeSlotsFromNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody)
{
uintptr_t scopeSlotsIndex = GetNativeScopeSlotsIndex(callerFunctionBody);
void **argv = walker.GetCurrentArgv();
return (Var*)argv[scopeSlotsIndex];
}
void StackScriptFunction::BoxState::SetFrameDisplayFromNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody, FrameDisplay * frameDisplay)
{
uintptr_t frameDisplayIndex = GetNativeFrameDisplayIndex(callerFunctionBody);
void **argv = walker.GetCurrentArgv();
((FrameDisplay**)argv)[frameDisplayIndex] = frameDisplay;
}
void StackScriptFunction::BoxState::SetScopeSlotsFromNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody, Var * scopeSlots)
{
uintptr_t scopeSlotsIndex = GetNativeScopeSlotsIndex(callerFunctionBody);
void **argv = walker.GetCurrentArgv();
((Var**)argv)[scopeSlotsIndex] = scopeSlots;
}
void StackScriptFunction::BoxState::BoxNativeFrame(JavascriptStackWalker const& walker, FunctionBody * callerFunctionBody)
{
this->ForEachStackNestedFunctionNative(walker, callerFunctionBody, [&](ScriptFunction *curr)
{
StackScriptFunction * func = StackScriptFunction::FromVar(curr);
// Need to check if we need the script function as the list of script function
// include inlinee stack function that doesn't necessary need to be boxed
if (this->NeedBoxScriptFunction(func))
{
// Box the stack function, even if they might not be "created" in the byte code yet.
// Some of them will not be captured in slots, so we just need to box them and record it with the
// stack func so that when we can just use the boxed value when we need it.
this->BoxStackFunction(func);
}
});
// Write back the boxed stack closure pointers at the designated stack locations.
Js::FrameDisplay *stackFrameDisplay = this->GetFrameDisplayFromNativeFrame(walker, callerFunctionBody);
if (ThreadContext::IsOnStack(stackFrameDisplay))
{
Js::FrameDisplay *boxedFrameDisplay;
if (boxedValues.TryGetValue(stackFrameDisplay, (void**)&boxedFrameDisplay))
{
this->SetFrameDisplayFromNativeFrame(walker, callerFunctionBody, boxedFrameDisplay);
callerFunctionBody->GetScriptContext()->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_Accessor);
}
}
Var *stackScopeSlots = this->GetScopeSlotsFromNativeFrame(walker, callerFunctionBody);
if (ThreadContext::IsOnStack(stackScopeSlots))
{
Var *boxedScopeSlots;
if (boxedValues.TryGetValue(stackScopeSlots, (void**)&boxedScopeSlots))
{
this->SetScopeSlotsFromNativeFrame(walker, callerFunctionBody, boxedScopeSlots);
callerFunctionBody->GetScriptContext()->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_Accessor);
}
}
}
template<class Fn>
void StackScriptFunction::BoxState::ForEachStackNestedFunction(
JavascriptStackWalker const& walker,
FunctionBody *callerFunctionBody,
Fn fn)
{
if (!callerFunctionBody->DoStackNestedFunc())
{
return;
}
InterpreterStackFrame *interpreterFrame = walker.GetCurrentInterpreterFrame();
if (interpreterFrame)
{
this->ForEachStackNestedFunctionInterpreted(interpreterFrame, callerFunctionBody, fn);
}
else
{
this->ForEachStackNestedFunctionNative(walker, callerFunctionBody, fn);
}
}
template<class Fn>
void StackScriptFunction::BoxState::ForEachStackNestedFunctionInterpreted(
InterpreterStackFrame *interpreterFrame,
FunctionBody *callerFunctionBody,
Fn fn)
{
uint nestedCount = callerFunctionBody->GetNestedCount();
for (uint i = 0; i < nestedCount; i++)
{
ScriptFunction *scriptFunction = interpreterFrame->GetStackNestedFunction(i);
fn(scriptFunction);
}
}
template<class Fn>
void StackScriptFunction::BoxState::ForEachStackNestedFunctionNative(
JavascriptStackWalker const& walker,
FunctionBody *callerFunctionBody,
Fn fn)
{
if (walker.IsInlineFrame())
{
return;
}
void **argv = walker.GetCurrentArgv();
// On arm, we always have an argument slot fo frames that has stack nested func
Js::Var curr =
#if _M_IX86 || _M_AMD64
callerFunctionBody->GetInParamsCount() == 0?
argv[JavascriptFunctionArgIndex_StackNestedFuncListWithNoArg]:
#endif
argv[JavascriptFunctionArgIndex_StackNestedFuncList];
// TODO: It is possible to have a function that is marked as doing stack function
// and we end up not JIT'ing any of them because they are deferred or don't
// have the default type allocated already. We can turn this into an assert
// when we start support JIT'ing that.
if (curr != nullptr)
{
do
{
StackScriptFunction *func = StackScriptFunction::FromVar(curr);
fn(func);
curr = *(Js::Var *)(func + 1);
}
while (curr != nullptr);
}
}
void StackScriptFunction::BoxState::Finish()
{
frameToBox.Map([](FunctionBody * body)
{
body->SetStackNestedFunc(false);
});
}
FrameDisplay * StackScriptFunction::BoxState::BoxFrameDisplay(FrameDisplay * frameDisplay)
{
Assert(frameDisplay != nullptr);
if (frameDisplay == &Js::NullFrameDisplay)
{
return frameDisplay;
}
FrameDisplay * boxedFrameDisplay;
if (boxedValues.TryGetValue(frameDisplay, (void **)&boxedFrameDisplay))
{
return boxedFrameDisplay;
}
// Create new frame display when we allocate the frame display on the stack
uint16 length = frameDisplay->GetLength();
if (!ThreadContext::IsOnStack(frameDisplay))
{
boxedFrameDisplay = frameDisplay;
}
else
{
boxedFrameDisplay = RecyclerNewPlus(scriptContext->GetRecycler(), length * sizeof(Var), FrameDisplay, length);
}
boxedValues.Add(frameDisplay, boxedFrameDisplay);
for (uint16 i = 0; i < length; i++)
{
// TODO: Once we allocate the slots on the stack, we can only look those slots
Var * scopeSlots = (Var *)frameDisplay->GetItem(i);
size_t scopeSlotcount = ScopeSlots(scopeSlots).GetCount(); // (size_t)scopeSlots[Js::ScopeSlots::EncodedSlotCountSlotIndex];
// We don't do stack slots if we exceed max encoded slot count
if (scopeSlotcount < ScopeSlots::MaxEncodedSlotCount)
{
scopeSlots = BoxScopeSlots(scopeSlots, static_cast<uint>(scopeSlotcount));
}
boxedFrameDisplay->SetItem(i, scopeSlots);
frameDisplay->SetItem(i, scopeSlots);
}
return boxedFrameDisplay;
}
Var * StackScriptFunction::BoxState::BoxScopeSlots(Var * slotArray, uint count)
{
Assert(slotArray != nullptr);
Assert(count != 0);
Var * boxedSlotArray;
if (boxedValues.TryGetValue(slotArray, (void **)&boxedSlotArray))
{
return boxedSlotArray;
}
if (!ThreadContext::IsOnStack(slotArray))
{
boxedSlotArray = slotArray;
}
else
{
// Create new scope slots when we allocate them on the stack
boxedSlotArray = RecyclerNewArray(scriptContext->GetRecycler(), Var, count + ScopeSlots::FirstSlotIndex);
}
boxedValues.Add(slotArray, boxedSlotArray);
ScopeSlots scopeSlots(slotArray);
ScopeSlots boxedScopeSlots(boxedSlotArray);
boxedScopeSlots.SetCount(count);
boxedScopeSlots.SetScopeMetadata(scopeSlots.GetScopeMetadataRaw());
// Box all the stack function in the parent's scope slot as well
for (uint i = 0; i < count; i++)
{
Js::Var slotValue = scopeSlots.Get(i);
if (ScriptFunction::Is(slotValue))
{
ScriptFunction * stackFunction = ScriptFunction::FromVar(slotValue);
slotValue = BoxStackFunction(stackFunction);
}
boxedScopeSlots.Set(i, slotValue);
}
return boxedSlotArray;
}
ScriptFunction * StackScriptFunction::BoxState::BoxStackFunction(ScriptFunction * scriptFunction)
{
// Box the frame display first, which may in turn box the function
FrameDisplay * frameDisplay = scriptFunction->GetEnvironment();
FrameDisplay * boxedFrameDisplay = BoxFrameDisplay(frameDisplay);
if (!ThreadContext::IsOnStack(scriptFunction))
{
return scriptFunction;
}
StackScriptFunction * stackFunction = StackScriptFunction::FromVar(scriptFunction);
ScriptFunction * boxedFunction = stackFunction->boxedScriptFunction;
if (boxedFunction != nullptr)
{
return boxedFunction;
}
if (PHASE_TESTTRACE(Js::StackFuncPhase, stackFunction->GetFunctionProxy()))
{
char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
Output::Print(_u("Boxing StackScriptFunction Object: %s (function Id: %s)"),
stackFunction->GetFunctionProxy()->IsDeferredDeserializeFunction()?
_u("<DeferDeserialize>") : stackFunction->GetParseableFunctionInfo()->GetDisplayName(),
stackFunction->GetFunctionProxy()->GetDebugNumberSet(debugStringBuffer));
if (PHASE_VERBOSE_TESTTRACE(Js::StackFuncPhase, stackFunction->GetFunctionProxy()))
{
Output::Print(_u(" %p\n"), stackFunction);
}
else
{
Output::Print(_u("\n"));
}
Output::Flush();
}
// Make sure we use the latest function proxy (if it is parsed or deserialized)
FunctionProxy * functionBody = stackFunction->GetFunctionProxy()->GetFunctionProxy();
boxedFunction = ScriptFunction::OP_NewScFunc(boxedFrameDisplay, &functionBody);
stackFunction->boxedScriptFunction = boxedFunction;
stackFunction->SetEnvironment(boxedFrameDisplay);
return boxedFunction;
}
ScriptFunction * StackScriptFunction::OP_NewStackScFunc(FrameDisplay *environment, FunctionProxy** proxyRef, ScriptFunction * stackFunction)
{
if (stackFunction)
{
#if ENABLE_DEBUG_CONFIG_OPTIONS
char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
#endif
FunctionProxy* functionProxy = (*proxyRef);
AssertMsg(functionProxy != nullptr, "BYTE-CODE VERIFY: Must specify a valid function to create");
Assert(stackFunction->GetFunctionInfo()->GetFunctionProxy() == functionProxy);
Assert(!functionProxy->IsFunctionBody() || functionProxy->GetFunctionBody()->GetStackNestedFuncParentStrongRef() != nullptr);
stackFunction->SetEnvironment(environment);
PHASE_PRINT_VERBOSE_TRACE(Js::StackFuncPhase, functionProxy,
_u("Stack alloc nested function: %s %s (address: %p)\n"),
functionProxy->IsFunctionBody()?
functionProxy->GetFunctionBody()->GetDisplayName() : _u("<deferred>"),
functionProxy->GetDebugNumberSet(debugStringBuffer), stackFunction);
return stackFunction;
}
return ScriptFunction::OP_NewScFunc(environment, proxyRef);
}
#if ENABLE_TTD
TTD::NSSnapObjects::SnapObjectType StackScriptFunction::GetSnapTag_TTD() const
{
//Make sure this isn't accidentally handled by parent class
return TTD::NSSnapObjects::SnapObjectType::Invalid;
}
void StackScriptFunction::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
{
TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<void*, TTD::NSSnapObjects::SnapObjectType::Invalid>(objData, nullptr);
}
#endif
}