ScopeInfo.cpp 13 KB

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