// // Create scope info for an outer scope. // ScopeInfo* ScopeInfo::FromScope(ByteCodeGenerator* byteCodeGenerator, FunctionBody* parent, Scope* scope, ScriptContext *scriptContext) { 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()->newTargetScopeSlot != Js::Constants::NoRegister ? 1 : 0); ScopeInfo* scopeInfo = RecyclerNewPlusZ(scriptContext->GetRecycler(), count * sizeof(SymbolInfo), ScopeInfo, parent, count); scopeInfo->isDynamic = scope->GetIsDynamic(); scopeInfo->isObject = scope->GetIsObject(); scopeInfo->mustInstantiate = scope->GetMustInstantiate(); scopeInfo->isCached = (scope->GetFunc()->GetBodyScope() == scope) && scope->GetFunc()->GetHasCachedScope(); scopeInfo->isGlobalEval = scope->GetScopeType() == ScopeType_GlobalEvalBlock; scopeInfo->canMergeWithBodyScope = scope->GetCanMergeWithBodyScope(); scopeInfo->hasLocalInClosure = scope->GetHasOwnLocalInClosure(); TRACE_BYTECODE(_u("\nSave ScopeInfo: %s parent: %s #symbols: %d %s\n"), scope->GetFunc()->name, parent->GetDisplayName(), count, scopeInfo->isObject ? _u("isObject") : _u("")); MapSymbolData mapSymbolData = { byteCodeGenerator, scope->GetFunc() }; scope->ForEachSymbol([&mapSymbolData, scopeInfo, scope](Symbol * sym) { Assert(scope == sym->GetScope()); scopeInfo->SaveSymbolInfo(sym, &mapSymbolData); }); return scopeInfo; }
// // 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() && (!mapSymbolData->func->IsInnerArgumentsSymbol(sym) || mapSymbolData->func->GetHasArguments()); 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()); }
// // Save needed scope info for a deferred child func. The scope info is empty and only links to parent. // void ScopeInfo::SaveParentScopeInfo(FuncInfo* parentFunc, FuncInfo* func) { Assert(func->IsDeferred()); // Parent must be parsed FunctionBody* parent = parentFunc->byteCodeFunction->GetFunctionBody(); ParseableFunctionInfo* funcBody = func->byteCodeFunction; TRACE_BYTECODE(_u("\nSave ScopeInfo: %s parent: %s\n\n"), funcBody->GetDisplayName(), parent->GetDisplayName()); funcBody->SetScopeInfo(FromParent(parent)); }
// // Persist one symbol info into ScopeInfo. // void ScopeInfo::SaveSymbolInfo(Symbol* sym, MapSymbolData* mapSymbolData) { // We don't need to create slot for or save "arguments" if (!sym->GetIsArguments() && sym->GetHasNonLocalReference()) { // Any symbol may have non-local ref from deferred child. Allocate slot for it. Js::PropertyId scopeSlot = sym->EnsureScopeSlot(mapSymbolData->func); 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()); } TRACE_BYTECODE(_u("%12s %d\n"), sym->GetName().GetBuffer(), sym->GetScopeSlot()); }
// // Load persisted scope info. // void ScopeInfo::GetScopeInfo(Parser *parser, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, Scope* scope) { ScriptContext* scriptContext; ArenaAllocator* alloc; // Load scope attributes and push onto scope stack. scope->SetIsDynamic(this->isDynamic); if (this->isObject) { scope->SetIsObject(); } scope->SetMustInstantiate(this->mustInstantiate); if (!this->GetCanMergeWithBodyScope()) { scope->SetCannotMergeWithBodyScope(); } scope->SetHasOwnLocalInClosure(this->hasLocalInClosure); if (parser) { scriptContext = parser->GetScriptContext(); alloc = parser->GetAllocator(); } else { TRACE_BYTECODE(_u("\nRestore ScopeInfo: %s #symbols: %d %s\n"), funcInfo->name, symbolCount, isObject ? _u("isObject") : _u("")); Assert(!this->isCached || scope == funcInfo->GetBodyScope()); funcInfo->SetHasCachedScope(this->isCached); byteCodeGenerator->PushScope(scope); // The scope is already populated, so we're done. return; } // 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)); if (GetHasFuncAssignment(i)) { sym->RestoreHasFuncAssignment(); } scope->AddNewSymbol(sym); if (parser) { parser->RestorePidRefForSym(sym); } TRACE_BYTECODE(_u("%12s %d\n"), sym->GetName().GetBuffer(), sym->GetScopeSlot()); } } this->scope = scope; DebugOnly(scope->isRestored = true); }