ScopeInfo.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  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 "RuntimeByteCodePch.h"
  6. namespace Js
  7. {
  8. //
  9. // Persist one symbol info into ScopeInfo.
  10. //
  11. void ScopeInfo::SaveSymbolInfo(Symbol* sym, MapSymbolData* mapSymbolData)
  12. {
  13. bool needScopeSlot = sym->GetHasNonLocalReference();
  14. Js::PropertyId scopeSlot = Constants::NoSlot;
  15. if (sym->GetIsModuleExportStorage())
  16. {
  17. // Export symbols aren't in slots but we need to persist the fact that they are export storage
  18. scopeSlot = sym->GetScope()->GetScopeSlotCount() + mapSymbolData->nonScopeSymbolCount++;
  19. }
  20. else if (needScopeSlot)
  21. {
  22. // Any symbol may have non-local ref from deferred child. Allocate slot for it.
  23. scopeSlot = sym->EnsureScopeSlot(mapSymbolData->byteCodeGenerator, mapSymbolData->func);
  24. }
  25. if (needScopeSlot || sym->GetIsModuleExportStorage())
  26. {
  27. Js::PropertyId propertyId = sym->EnsurePosition(mapSymbolData->func);
  28. this->SetSymbolId(scopeSlot, propertyId);
  29. this->SetSymbolType(scopeSlot, sym->GetSymbolType());
  30. this->SetHasFuncAssignment(scopeSlot, sym->GetHasFuncAssignment());
  31. this->SetIsBlockVariable(scopeSlot, sym->GetIsBlockVar());
  32. this->SetIsConst(scopeSlot, sym->GetIsConst());
  33. this->SetIsFuncExpr(scopeSlot, sym->GetIsFuncExpr());
  34. this->SetIsModuleExportStorage(scopeSlot, sym->GetIsModuleExportStorage());
  35. this->SetIsModuleImport(scopeSlot, sym->GetIsModuleImport());
  36. }
  37. TRACE_BYTECODE(_u("%12s %d\n"), sym->GetName().GetBuffer(), sym->GetScopeSlot());
  38. }
  39. inline void AddSlotCount(int& count, int addCount)
  40. {
  41. if (addCount != 0 && Int32Math::Add(count, addCount, &count))
  42. {
  43. ::Math::DefaultOverflowPolicy();
  44. }
  45. }
  46. //
  47. // Create scope info for a single scope.
  48. //
  49. ScopeInfo* ScopeInfo::SaveOneScopeInfo(ByteCodeGenerator* byteCodeGenerator, Scope* scope, ScriptContext *scriptContext)
  50. {
  51. Assert(scope->GetScopeInfo() == nullptr);
  52. Assert(scope->GetScopeType() != ScopeType_Global);
  53. int count = scope->Count();
  54. // Add argsPlaceHolder which includes same name args and destructuring patterns on parameters
  55. AddSlotCount(count, scope->GetFunc()->argsPlaceHolderSlotCount);
  56. ScopeInfo* scopeInfo = RecyclerNewPlusZ(scriptContext->GetRecycler(),
  57. count * sizeof(SymbolInfo),
  58. ScopeInfo, scope->GetFunc()->byteCodeFunction->GetFunctionInfo(),/*parent ? parent->GetFunctionInfo() : nullptr,*/ count);
  59. scopeInfo->SetScopeType(scope->GetScopeType());
  60. scopeInfo->isDynamic = scope->GetIsDynamic();
  61. scopeInfo->isObject = scope->GetIsObject();
  62. scopeInfo->mustInstantiate = scope->GetMustInstantiate();
  63. scopeInfo->isCached = (scope->GetFunc()->GetBodyScope() == scope) && scope->GetFunc()->GetHasCachedScope();
  64. scopeInfo->hasLocalInClosure = scope->GetHasOwnLocalInClosure();
  65. if (scope->GetScopeType() == ScopeType_FunctionBody)
  66. {
  67. scopeInfo->isGeneratorFunctionBody = scope->GetFunc()->byteCodeFunction->GetFunctionInfo()->IsGenerator();
  68. scopeInfo->isAsyncFunctionBody = scope->GetFunc()->byteCodeFunction->GetFunctionInfo()->IsAsync();
  69. }
  70. TRACE_BYTECODE(_u("\nSave ScopeInfo: %s #symbols: %d %s\n"),
  71. scope->GetFunc()->name, count,
  72. scopeInfo->isObject ? _u("isObject") : _u(""));
  73. MapSymbolData mapSymbolData = { byteCodeGenerator, scope->GetFunc(), 0 };
  74. scope->ForEachSymbol([&mapSymbolData, scopeInfo, scope](Symbol * sym)
  75. {
  76. Assert(scope == sym->GetScope());
  77. scopeInfo->SaveSymbolInfo(sym, &mapSymbolData);
  78. });
  79. scope->SetScopeInfo(scopeInfo);
  80. return scopeInfo;
  81. }
  82. //
  83. // Ensure the pids referenced by this scope are tracked.
  84. //
  85. void ScopeInfo::EnsurePidTracking(ScriptContext* scriptContext)
  86. {
  87. for (int i = 0; i < symbolCount; i++)
  88. {
  89. auto propertyName = scriptContext->GetPropertyName(symbols[i].propertyId);
  90. scriptContext->TrackPid(propertyName);
  91. }
  92. }
  93. //
  94. // Save scope info for an individual scope and link it to its enclosing scope.
  95. //
  96. ScopeInfo * ScopeInfo::SaveScopeInfo(ByteCodeGenerator* byteCodeGenerator, Scope * scope, ScriptContext * scriptContext)
  97. {
  98. // Advance past scopes that will be excluded from the closure environment. (But note that we always want the body scope.)
  99. while (scope)
  100. {
  101. FuncInfo* func = scope->GetFunc();
  102. if (scope->GetMustInstantiate() ||
  103. func->GetBodyScope() == scope ||
  104. (func->GetParamScope() == scope && func->IsBodyAndParamScopeMerged() && scope->GetHasNestedParamFunc()))
  105. {
  106. break;
  107. }
  108. scope = scope->GetEnclosingScope();
  109. }
  110. // If we've exhausted the scope chain, we're done.
  111. if (scope == nullptr || scope->GetScopeType() == ScopeType_Global)
  112. {
  113. return nullptr;
  114. }
  115. // If we've already collected info for this scope, we're done.
  116. ScopeInfo * scopeInfo = scope->GetScopeInfo();
  117. if (scopeInfo != nullptr)
  118. {
  119. return scopeInfo;
  120. }
  121. // Do the work for this scope.
  122. scopeInfo = ScopeInfo::SaveOneScopeInfo(byteCodeGenerator, scope, scriptContext);
  123. // Link to the parent (if any).
  124. scope = scope->GetEnclosingScope();
  125. if (scope)
  126. {
  127. scopeInfo->SetParentScopeInfo(ScopeInfo::SaveScopeInfo(byteCodeGenerator, scope, scriptContext));
  128. }
  129. return scopeInfo;
  130. }
  131. void ScopeInfo::SaveEnclosingScopeInfo(ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo)
  132. {
  133. // TODO: Not technically necessary, as we always do scope look up on eval if it is
  134. // not found in the scope chain, and block scopes are always objects in eval.
  135. // But if we save the global eval block scope for deferred child so that we can look up
  136. // let/const in that scope with slot index instead of doing a scope lookup.
  137. // We will have to implement encoding block scope info to enable, which will also
  138. // enable defer parsing function that are in block scopes.
  139. if (funcInfo->byteCodeFunction &&
  140. funcInfo->byteCodeFunction->GetScopeInfo() != nullptr)
  141. {
  142. // No need to regenerate scope info if we re-compile an enclosing function
  143. return;
  144. }
  145. Scope* currentScope = byteCodeGenerator->GetCurrentScope();
  146. Assert(currentScope->GetFunc() == funcInfo);
  147. if (funcInfo->root->IsDeclaredInParamScope())
  148. {
  149. Assert(currentScope->GetScopeType() == ScopeType_FunctionBody);
  150. Assert(currentScope->GetEnclosingScope());
  151. FuncInfo* func = byteCodeGenerator->GetEnclosingFuncInfo();
  152. Assert(func);
  153. if (func->IsBodyAndParamScopeMerged())
  154. {
  155. currentScope = func->GetParamScope();
  156. Assert(currentScope->GetScopeType() == ScopeType_Parameter);
  157. }
  158. }
  159. while (currentScope->GetFunc() == funcInfo)
  160. {
  161. currentScope = currentScope->GetEnclosingScope();
  162. }
  163. ScopeInfo * scopeInfo = ScopeInfo::SaveScopeInfo(byteCodeGenerator, currentScope, byteCodeGenerator->GetScriptContext());
  164. if (scopeInfo != nullptr)
  165. {
  166. if (funcInfo->root->IsDeclaredInParamScope())
  167. {
  168. FuncInfo* func = byteCodeGenerator->GetEnclosingFuncInfo();
  169. Assert(func);
  170. if (func->IsBodyAndParamScopeMerged())
  171. {
  172. Assert(currentScope == func->GetParamScope() && currentScope->GetScopeType() == ScopeType_Parameter);
  173. Assert(scopeInfo->GetScopeType() == ScopeType_Parameter);
  174. Assert(func->GetBodyScope());
  175. // If the current function is nested in the param scope of it's enclosing function we may have
  176. // skipped the body scope and in may not be the scope stack but the body scope might still be
  177. // in the frame display and we will need to account for it. See ByteCodeGenerateor::FindScopeForSym.
  178. scopeInfo->mustInstantiate = func->GetBodyScope()->GetMustInstantiate();
  179. }
  180. }
  181. funcInfo->byteCodeFunction->SetScopeInfo(scopeInfo);
  182. }
  183. }
  184. //
  185. // Load persisted scope info.
  186. //
  187. void ScopeInfo::ExtractScopeInfo(Parser *parser, /*ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo,*/ Scope* scope)
  188. {
  189. ScriptContext* scriptContext;
  190. ArenaAllocator* alloc;
  191. // Load scope attributes and push onto scope stack.
  192. scope->SetMustInstantiate(this->mustInstantiate);
  193. scope->SetHasOwnLocalInClosure(this->hasLocalInClosure);
  194. scope->SetIsDynamic(this->isDynamic);
  195. if (this->isObject)
  196. {
  197. scope->SetIsObject();
  198. }
  199. Assert(parser);
  200. scriptContext = parser->GetScriptContext();
  201. alloc = parser->GetAllocator();
  202. // Load scope symbols
  203. // On first access to the scopeinfo, replace the ID's with PropertyRecord*'s to save the dictionary lookup
  204. // on later accesses. Replace them all before allocating Symbol's to prevent inconsistency on OOM.
  205. if (!this->areNamesCached && !PHASE_OFF1(Js::CacheScopeInfoNamesPhase))
  206. {
  207. for (int i = 0; i < symbolCount; i++)
  208. {
  209. PropertyId propertyId = GetSymbolId(i);
  210. if (propertyId != 0) // There may be empty slots, e.g. "arguments" may have no slot
  211. {
  212. PropertyRecord const* name = scriptContext->GetPropertyName(propertyId);
  213. this->SetPropertyName(i, name);
  214. }
  215. }
  216. this->areNamesCached = true;
  217. }
  218. for (int i = 0; i < symbolCount; i++)
  219. {
  220. PropertyRecord const* name = nullptr;
  221. if (this->areNamesCached)
  222. {
  223. name = this->GetPropertyName(i);
  224. }
  225. else
  226. {
  227. PropertyId propertyId = GetSymbolId(i);
  228. if (propertyId != 0) // There may be empty slots, e.g. "arguments" may have no slot
  229. {
  230. name = scriptContext->GetPropertyName(propertyId);
  231. }
  232. }
  233. if (name != nullptr)
  234. {
  235. SymbolType symbolType = GetSymbolType(i);
  236. SymbolName symName(name->GetBuffer(), name->GetLength());
  237. Symbol *sym = Anew(alloc, Symbol, symName, nullptr, symbolType);
  238. sym->SetScopeSlot(static_cast<PropertyId>(i));
  239. sym->SetIsBlockVar(GetIsBlockVariable(i));
  240. sym->SetIsConst(GetIsConst(i));
  241. sym->SetIsFuncExpr(GetIsFuncExpr(i));
  242. sym->SetIsModuleExportStorage(GetIsModuleExportStorage(i));
  243. sym->SetIsModuleImport(GetIsModuleImport(i));
  244. if (GetHasFuncAssignment(i))
  245. {
  246. sym->RestoreHasFuncAssignment();
  247. }
  248. scope->AddNewSymbol(sym);
  249. sym->SetHasNonLocalReference();
  250. Assert(parser);
  251. parser->RestorePidRefForSym(sym);
  252. TRACE_BYTECODE(_u("%12s %d\n"), sym->GetName().GetBuffer(), sym->GetScopeSlot());
  253. }
  254. }
  255. this->scope = scope;
  256. DebugOnly(scope->isRestored = true);
  257. }
  258. ScopeInfo::AutoCapturesAllScope::AutoCapturesAllScope(Scope* scope, bool turnOn)
  259. : scope(scope)
  260. {
  261. oldCapturesAll = scope->GetCapturesAll();
  262. if (turnOn)
  263. {
  264. scope->SetCapturesAll(true);
  265. }
  266. }
  267. ScopeInfo::AutoCapturesAllScope::~AutoCapturesAllScope()
  268. {
  269. scope->SetCapturesAll(oldCapturesAll);
  270. }
  271. } // namespace Js