blob: 32102f32bdf6217ed28aa5693d8be4ebebf4432d [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
enum ScopeType: int
{
ScopeType_Unknown,
ScopeType_Global,
ScopeType_GlobalEvalBlock,
ScopeType_FunctionBody,
ScopeType_FuncExpr,
ScopeType_Block,
ScopeType_Catch,
ScopeType_CatchParamPattern,
ScopeType_With,
ScopeType_Parameter
};
class Scope
{
private:
Scope *enclosingScope;
Js::ScopeInfo *scopeInfo;
Js::RegSlot location;
FuncInfo *func;
Symbol *m_symList;
int m_count;
ArenaAllocator *alloc;
uint scopeSlotCount; // count of slots in the local scope
uint innerScopeIndex;
ScopeType const scopeType;
BYTE isDynamic : 1;
BYTE isObject : 1;
BYTE canMerge : 1;
BYTE capturesAll : 1;
BYTE mustInstantiate : 1;
BYTE hasCrossScopeFuncAssignment : 1;
BYTE hasDuplicateFormals : 1;
BYTE canMergeWithBodyScope : 1;
BYTE hasLocalInClosure : 1;
BYTE isBlockInLoop : 1;
BYTE containsWith : 1;
public:
#if DBG
BYTE isRestored : 1;
#endif
Scope(ArenaAllocator *alloc, ScopeType scopeType, int capacity = 0) :
alloc(alloc),
func(nullptr),
enclosingScope(nullptr),
scopeInfo(nullptr),
isDynamic(false),
isObject(false),
canMerge(true),
capturesAll(false),
mustInstantiate(false),
hasCrossScopeFuncAssignment(false),
hasDuplicateFormals(false),
canMergeWithBodyScope(true),
hasLocalInClosure(false),
isBlockInLoop(false),
containsWith(false),
location(Js::Constants::NoRegister),
m_symList(nullptr),
m_count(0),
scopeSlotCount(0),
innerScopeIndex((uint)-1),
scopeType(scopeType)
#if DBG
, isRestored(false)
#endif
{
}
Symbol *FindLocalSymbol(SymbolName const& key)
{
Symbol *sym;
for (sym = m_symList; sym; sym = sym->GetNext())
{
if (sym->GetName() == key)
{
break;
}
}
return sym;
}
template<class Fn>
void ForEachSymbol(Fn fn)
{
for (Symbol *sym = m_symList; sym;)
{
Symbol *next = sym->GetNext();
fn(sym);
sym = next;
}
}
template<class Fn>
void ForEachSymbolUntil(Fn fn)
{
for (Symbol *sym = m_symList; sym;)
{
Symbol *next = sym->GetNext();
if (fn(sym))
{
return;
}
sym = next;
}
}
// For JScript, this should not return NULL because
// there is always an enclosing global scope.
Symbol *FindSymbol(SymbolName const& name, SymbolType symbolType, bool fCreate = true)
{
Symbol *sym = FindLocalSymbol(name);
if (sym == nullptr)
{
if (enclosingScope != nullptr)
{
sym = enclosingScope->FindSymbol(name, symbolType);
}
else if (fCreate)
{
sym = Anew(alloc, Symbol, name, nullptr, symbolType);
AddNewSymbol(sym);
}
}
return sym;
}
void AddSymbol(Symbol *sym)
{
if (enclosingScope == nullptr)
{
sym->SetIsGlobal(true);
}
sym->SetScope(this);
for (Symbol *symInList = m_symList; symInList; symInList = symInList->GetNext())
{
if (symInList->GetName() == sym->GetName())
{
return;
}
}
sym->SetNext(m_symList);
m_symList = sym;
m_count++;
}
void AddNewSymbol(Symbol *sym)
{
if (scopeType == ScopeType_Global)
{
sym->SetIsGlobal(true);
}
sym->SetScope(this);
sym->SetNext(m_symList);
m_symList = sym;
m_count++;
}
bool HasStaticPathToAncestor(Scope const * target) const
{
return target == this || (!isDynamic && enclosingScope != nullptr && enclosingScope->HasStaticPathToAncestor(target));
}
void SetEnclosingScope(Scope *enclosingScope)
{
// Check for scope cycles
Assert(enclosingScope != this);
Assert(enclosingScope == nullptr || this != enclosingScope->GetEnclosingScope());
this->enclosingScope = enclosingScope;
}
Scope *GetEnclosingScope() const
{
return enclosingScope;
}
void SetScopeInfo(Js::ScopeInfo * scopeInfo)
{
this->scopeInfo = scopeInfo;
}
Js::ScopeInfo * GetScopeInfo() const
{
return this->scopeInfo;
}
ScopeType GetScopeType() const
{
return this->scopeType;
}
bool IsInnerScope() const
{
return scopeType == ScopeType_Block
|| scopeType == ScopeType_Catch
|| scopeType == ScopeType_CatchParamPattern
|| scopeType == ScopeType_GlobalEvalBlock;
}
int Count() const
{
return m_count;
}
void SetFunc(FuncInfo *func)
{
this->func = func;
}
FuncInfo *GetFunc() const
{
return func;
}
FuncInfo *GetEnclosingFunc()
{
Scope *scope = this;
while (scope && scope->func == nullptr)
{
scope = scope->GetEnclosingScope();
}
AnalysisAssert(scope);
return scope->func;
}
void SetLocation(Js::RegSlot loc) { location = loc; }
Js::RegSlot GetLocation() const { return location; }
void SetIsDynamic(bool is) { isDynamic = is; }
bool GetIsDynamic() const { return isDynamic; }
bool IsEmpty() const;
bool IsBlockScope(FuncInfo *funcInfo);
void SetIsObject();
bool GetIsObject() const { return isObject; }
void SetCapturesAll(bool does) { capturesAll = does; }
bool GetCapturesAll() const { return capturesAll; }
void SetMustInstantiate(bool must) { mustInstantiate = must; }
bool GetMustInstantiate() const { return mustInstantiate; }
void SetCanMerge(bool can) { canMerge = can; }
bool GetCanMerge() const { return canMerge && !mustInstantiate && !isObject; }
void SetScopeSlotCount(uint i) { scopeSlotCount = i; }
uint GetScopeSlotCount() const { return scopeSlotCount; }
void SetHasDuplicateFormals() { hasDuplicateFormals = true; }
bool GetHasDuplicateFormals() { return hasDuplicateFormals; }
void SetHasOwnLocalInClosure(bool has) { hasLocalInClosure = has; }
bool GetHasOwnLocalInClosure() const { return hasLocalInClosure; }
void SetIsBlockInLoop(bool is = true) { isBlockInLoop = is; }
bool IsBlockInLoop() const { return isBlockInLoop; }
void SetContainsWith(bool does = true) { containsWith = does; }
bool ContainsWith() const { return containsWith; }
bool HasInnerScopeIndex() const { return innerScopeIndex != (uint)-1; }
uint GetInnerScopeIndex() const { return innerScopeIndex; }
void SetInnerScopeIndex(uint index) { innerScopeIndex = index; }
int AddScopeSlot();
void SetHasCrossScopeFuncAssignment() { hasCrossScopeFuncAssignment = true; }
bool HasCrossScopeFuncAssignment() const { return hasCrossScopeFuncAssignment; }
void ForceAllSymbolNonLocalReference(ByteCodeGenerator *byteCodeGenerator);
bool IsGlobalEvalBlockScope() const;
static void MergeParamAndBodyScopes(ParseNode *pnodeScope);
static void RemoveParamScope(ParseNode *pnodeScope);
};