ScopeInfo.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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. funcInfo->byteCodeFunction->SetScopeInfo(scopeInfo);
  167. }
  168. }
  169. //
  170. // Load persisted scope info.
  171. //
  172. void ScopeInfo::ExtractScopeInfo(Parser *parser, /*ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo,*/ Scope* scope)
  173. {
  174. ScriptContext* scriptContext;
  175. ArenaAllocator* alloc;
  176. // Load scope attributes and push onto scope stack.
  177. scope->SetMustInstantiate(this->mustInstantiate);
  178. scope->SetHasOwnLocalInClosure(this->hasLocalInClosure);
  179. scope->SetIsDynamic(this->isDynamic);
  180. if (this->isObject)
  181. {
  182. scope->SetIsObject();
  183. }
  184. Assert(parser);
  185. scriptContext = parser->GetScriptContext();
  186. alloc = parser->GetAllocator();
  187. // Load scope symbols
  188. // On first access to the scopeinfo, replace the ID's with PropertyRecord*'s to save the dictionary lookup
  189. // on later accesses. Replace them all before allocating Symbol's to prevent inconsistency on OOM.
  190. if (!this->areNamesCached && !PHASE_OFF1(Js::CacheScopeInfoNamesPhase))
  191. {
  192. for (int i = 0; i < symbolCount; i++)
  193. {
  194. PropertyId propertyId = GetSymbolId(i);
  195. if (propertyId != 0) // There may be empty slots, e.g. "arguments" may have no slot
  196. {
  197. PropertyRecord const* name = scriptContext->GetPropertyName(propertyId);
  198. this->SetPropertyName(i, name);
  199. }
  200. }
  201. this->areNamesCached = true;
  202. }
  203. for (int i = 0; i < symbolCount; i++)
  204. {
  205. PropertyRecord const* name = nullptr;
  206. if (this->areNamesCached)
  207. {
  208. name = this->GetPropertyName(i);
  209. }
  210. else
  211. {
  212. PropertyId propertyId = GetSymbolId(i);
  213. if (propertyId != 0) // There may be empty slots, e.g. "arguments" may have no slot
  214. {
  215. name = scriptContext->GetPropertyName(propertyId);
  216. }
  217. }
  218. if (name != nullptr)
  219. {
  220. SymbolType symbolType = GetSymbolType(i);
  221. SymbolName symName(name->GetBuffer(), name->GetLength());
  222. Symbol *sym = Anew(alloc, Symbol, symName, nullptr, symbolType);
  223. sym->SetScopeSlot(static_cast<PropertyId>(i));
  224. sym->SetIsBlockVar(GetIsBlockVariable(i));
  225. sym->SetIsConst(GetIsConst(i));
  226. sym->SetIsFuncExpr(GetIsFuncExpr(i));
  227. sym->SetIsModuleExportStorage(GetIsModuleExportStorage(i));
  228. sym->SetIsModuleImport(GetIsModuleImport(i));
  229. if (GetHasFuncAssignment(i))
  230. {
  231. sym->RestoreHasFuncAssignment();
  232. }
  233. scope->AddNewSymbol(sym);
  234. sym->SetHasNonLocalReference();
  235. Assert(parser);
  236. parser->RestorePidRefForSym(sym);
  237. TRACE_BYTECODE(_u("%12s %d\n"), sym->GetName().GetBuffer(), sym->GetScopeSlot());
  238. }
  239. }
  240. this->scope = scope;
  241. DebugOnly(scope->isRestored = true);
  242. }
  243. ScopeInfo::AutoCapturesAllScope::AutoCapturesAllScope(Scope* scope, bool turnOn)
  244. : scope(scope)
  245. {
  246. oldCapturesAll = scope->GetCapturesAll();
  247. if (turnOn)
  248. {
  249. scope->SetCapturesAll(true);
  250. }
  251. }
  252. ScopeInfo::AutoCapturesAllScope::~AutoCapturesAllScope()
  253. {
  254. scope->SetCapturesAll(oldCapturesAll);
  255. }
  256. } // namespace Js