blob: 20c73c1ebb7587256e8058e98fe8e2dcfee23d03 [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeByteCodePch.h"
namespace Js
{
//
// Persist one symbol info into ScopeInfo.
//
void ScopeInfo::SaveSymbolInfo(Symbol* sym, MapSymbolData* mapSymbolData)
{
// We don't need to create slot for or save "arguments"
bool needScopeSlot = !sym->GetIsArguments() && sym->GetHasNonLocalReference();
Js::PropertyId scopeSlot = Constants::NoSlot;
if (sym->GetIsModuleExportStorage())
{
// Export symbols aren't in slots but we need to persist the fact that they are export storage
scopeSlot = sym->GetScope()->GetScopeSlotCount() + mapSymbolData->nonScopeSymbolCount++;
}
else if (needScopeSlot)
{
// Any symbol may have non-local ref from deferred child. Allocate slot for it.
scopeSlot = sym->EnsureScopeSlot(mapSymbolData->func);
}
if (needScopeSlot || sym->GetIsModuleExportStorage())
{
Js::PropertyId propertyId = sym->EnsurePosition(mapSymbolData->func);
this->SetSymbolId(scopeSlot, propertyId);
this->SetSymbolType(scopeSlot, sym->GetSymbolType());
this->SetHasFuncAssignment(scopeSlot, sym->GetHasFuncAssignment());
this->SetIsBlockVariable(scopeSlot, sym->GetIsBlockVar());
this->SetIsFuncExpr(scopeSlot, sym->GetIsFuncExpr());
this->SetIsModuleExportStorage(scopeSlot, sym->GetIsModuleExportStorage());
this->SetIsModuleImport(scopeSlot, sym->GetIsModuleImport());
}
TRACE_BYTECODE(_u("%12s %d\n"), sym->GetName().GetBuffer(), sym->GetScopeSlot());
}
inline void AddSlotCount(int& count, int addCount)
{
if (addCount != 0 && Int32Math::Add(count, addCount, &count))
{
::Math::DefaultOverflowPolicy();
}
}
//
// Create scope info for a single scope.
//
ScopeInfo* ScopeInfo::SaveOneScopeInfo(/*ByteCodeGenerator* byteCodeGenerator, ParseableFunctionInfo* parent,*/ Scope* scope, ScriptContext *scriptContext)
{
Assert(scope->GetScopeInfo() == nullptr);
Assert(scope->GetScopeType() != ScopeType_Global);
int count = scope->Count();
// Add argsPlaceHolder which includes same name args and destructuring patterns on parameters
AddSlotCount(count, scope->GetFunc()->argsPlaceHolderSlotCount);
AddSlotCount(count, scope->GetFunc()->thisScopeSlot != Js::Constants::NoRegister ? 1 : 0);
AddSlotCount(count, scope->GetFunc()->superScopeSlot != Js::Constants::NoRegister ? 1 : 0);
AddSlotCount(count, scope->GetFunc()->newTargetScopeSlot != Js::Constants::NoRegister ? 1 : 0);
ScopeInfo* scopeInfo = RecyclerNewPlusZ(scriptContext->GetRecycler(),
count * sizeof(SymbolInfo),
ScopeInfo, scope->GetFunc()->byteCodeFunction->GetFunctionInfo(),/*parent ? parent->GetFunctionInfo() : nullptr,*/ count);
scopeInfo->SetScopeType(scope->GetScopeType());
scopeInfo->isDynamic = scope->GetIsDynamic();
scopeInfo->isObject = scope->GetIsObject();
scopeInfo->mustInstantiate = scope->GetMustInstantiate();
scopeInfo->isCached = (scope->GetFunc()->GetBodyScope() == scope) && scope->GetFunc()->GetHasCachedScope();
scopeInfo->hasLocalInClosure = scope->GetHasOwnLocalInClosure();
TRACE_BYTECODE(_u("\nSave ScopeInfo: %s #symbols: %d %s\n"),
scope->GetFunc()->name, count,
scopeInfo->isObject ? _u("isObject") : _u(""));
MapSymbolData mapSymbolData = { scope->GetFunc(), 0 };
scope->ForEachSymbol([&mapSymbolData, scopeInfo, scope](Symbol * sym)
{
Assert(scope == sym->GetScope());
scopeInfo->SaveSymbolInfo(sym, &mapSymbolData);
});
scope->SetScopeInfo(scopeInfo);
return scopeInfo;
}
//
// Ensure the pids referenced by this scope are tracked.
//
void ScopeInfo::EnsurePidTracking(ScriptContext* scriptContext)
{
for (int i = 0; i < symbolCount; i++)
{
auto propertyName = scriptContext->GetPropertyName(symbols[i].propertyId);
scriptContext->TrackPid(propertyName);
}
}
//
// Save scope info for an individual scope and link it to its enclosing scope.
//
ScopeInfo * ScopeInfo::SaveScopeInfo(Scope * scope/*ByteCodeGenerator* byteCodeGenerator, FuncInfo* parentFunc, FuncInfo* func*/, ScriptContext * scriptContext)
{
// Advance past scopes that will be excluded from the closure environment. (But note that we always want the body scope.)
while (scope && (!scope->GetMustInstantiate() && scope != scope->GetFunc()->GetBodyScope()))
{
scope = scope->GetEnclosingScope();
}
// If we've exhausted the scope chain, we're done.
if (scope == nullptr || scope->GetScopeType() == ScopeType_Global)
{
return nullptr;
}
// If we've already collected info for this scope, we're done.
ScopeInfo * scopeInfo = scope->GetScopeInfo();
if (scopeInfo != nullptr)
{
return scopeInfo;
}
// Do the work for this scope.
scopeInfo = ScopeInfo::SaveOneScopeInfo(scope, scriptContext);
// Link to the parent (if any).
scope = scope->GetEnclosingScope();
if (scope)
{
scopeInfo->SetParentScopeInfo(ScopeInfo::SaveScopeInfo(scope, scriptContext));
}
return scopeInfo;
}
void ScopeInfo::SaveEnclosingScopeInfo(ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo)
{
// TODO: Not technically necessary, as we always do scope look up on eval if it is
// not found in the scope chain, and block scopes are always objects in eval.
// But if we save the global eval block scope for deferred child so that we can look up
// let/const in that scope with slot index instead of doing a scope lookup.
// We will have to implement encoding block scope info to enable, which will also
// enable defer parsing function that are in block scopes.
if (funcInfo->byteCodeFunction &&
funcInfo->byteCodeFunction->GetScopeInfo() != nullptr)
{
// No need to regenerate scope info if we re-compile an enclosing function
return;
}
Scope* currentScope = byteCodeGenerator->GetCurrentScope();
Assert(currentScope->GetFunc() == funcInfo);
while (currentScope->GetFunc() == funcInfo)
{
currentScope = currentScope->GetEnclosingScope();
}
ScopeInfo * scopeInfo = ScopeInfo::SaveScopeInfo(currentScope, byteCodeGenerator->GetScriptContext());
if (scopeInfo != nullptr)
{
funcInfo->byteCodeFunction->SetScopeInfo(scopeInfo);
}
}
//
// Load persisted scope info.
//
void ScopeInfo::ExtractScopeInfo(Parser *parser, /*ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo,*/ Scope* scope)
{
ScriptContext* scriptContext;
ArenaAllocator* alloc;
// Load scope attributes and push onto scope stack.
scope->SetMustInstantiate(this->mustInstantiate);
scope->SetHasOwnLocalInClosure(this->hasLocalInClosure);
scope->SetIsDynamic(this->isDynamic);
if (this->isObject)
{
scope->SetIsObject();
}
Assert(parser);
scriptContext = parser->GetScriptContext();
alloc = parser->GetAllocator();
// Load scope symbols
// On first access to the scopeinfo, replace the ID's with PropertyRecord*'s to save the dictionary lookup
// on later accesses. Replace them all before allocating Symbol's to prevent inconsistency on OOM.
if (!this->areNamesCached && !PHASE_OFF1(Js::CacheScopeInfoNamesPhase))
{
for (int i = 0; i < symbolCount; i++)
{
PropertyId propertyId = GetSymbolId(i);
if (propertyId != 0) // There may be empty slots, e.g. "arguments" may have no slot
{
PropertyRecord const* name = scriptContext->GetPropertyName(propertyId);
this->SetPropertyName(i, name);
}
}
this->areNamesCached = true;
}
for (int i = 0; i < symbolCount; i++)
{
PropertyRecord const* name = nullptr;
if (this->areNamesCached)
{
name = this->GetPropertyName(i);
}
else
{
PropertyId propertyId = GetSymbolId(i);
if (propertyId != 0) // There may be empty slots, e.g. "arguments" may have no slot
{
name = scriptContext->GetPropertyName(propertyId);
}
}
if (name != nullptr)
{
SymbolType symbolType = GetSymbolType(i);
SymbolName symName(name->GetBuffer(), name->GetLength());
Symbol *sym = Anew(alloc, Symbol, symName, nullptr, symbolType);
sym->SetScopeSlot(static_cast<PropertyId>(i));
sym->SetIsBlockVar(GetIsBlockVariable(i));
sym->SetIsFuncExpr(GetIsFuncExpr(i));
sym->SetIsModuleExportStorage(GetIsModuleExportStorage(i));
sym->SetIsModuleImport(GetIsModuleImport(i));
if (GetHasFuncAssignment(i))
{
sym->RestoreHasFuncAssignment();
}
scope->AddNewSymbol(sym);
sym->SetHasNonLocalReference();
Assert(parser);
parser->RestorePidRefForSym(sym);
TRACE_BYTECODE(_u("%12s %d\n"), sym->GetName().GetBuffer(), sym->GetScopeSlot());
}
}
this->scope = scope;
DebugOnly(scope->isRestored = true);
}
ScopeInfo::AutoCapturesAllScope::AutoCapturesAllScope(Scope* scope, bool turnOn)
: scope(scope)
{
oldCapturesAll = scope->GetCapturesAll();
if (turnOn)
{
scope->SetCapturesAll(true);
}
}
ScopeInfo::AutoCapturesAllScope::~AutoCapturesAllScope()
{
scope->SetCapturesAll(oldCapturesAll);
}
} // namespace Js