ScopeInfo.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  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. // We don't need to create slot for or save "arguments"
  14. bool needScopeSlot = !sym->GetIsArguments() && sym->GetHasNonLocalReference();
  15. Js::PropertyId scopeSlot = Constants::NoSlot;
  16. if (sym->GetIsModuleExportStorage())
  17. {
  18. // Export symbols aren't in slots but we need to persist the fact that they are export storage
  19. scopeSlot = sym->GetScope()->GetScopeSlotCount() + mapSymbolData->nonScopeSymbolCount++;
  20. }
  21. else if (needScopeSlot)
  22. {
  23. // Any symbol may have non-local ref from deferred child. Allocate slot for it.
  24. scopeSlot = sym->EnsureScopeSlot(mapSymbolData->func);
  25. }
  26. if (needScopeSlot || sym->GetIsModuleExportStorage())
  27. {
  28. Js::PropertyId propertyId = sym->EnsurePosition(mapSymbolData->func);
  29. this->SetSymbolId(scopeSlot, propertyId);
  30. this->SetSymbolType(scopeSlot, sym->GetSymbolType());
  31. this->SetHasFuncAssignment(scopeSlot, sym->GetHasFuncAssignment());
  32. this->SetIsBlockVariable(scopeSlot, sym->GetIsBlockVar());
  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. //
  40. // Create scope info for a deferred child to refer to its parent ParseableFunctionInfo.
  41. //
  42. ScopeInfo* ScopeInfo::FromParent(FunctionBody* parent)
  43. {
  44. return RecyclerNew(parent->GetScriptContext()->GetRecycler(), // Alloc with ParseableFunctionInfo
  45. ScopeInfo, parent->GetFunctionInfo(), 0);
  46. }
  47. inline void AddSlotCount(int& count, int addCount)
  48. {
  49. if (addCount != 0 && Int32Math::Add(count, addCount, &count))
  50. {
  51. ::Math::DefaultOverflowPolicy();
  52. }
  53. }
  54. //
  55. // Create scope info for an outer scope.
  56. //
  57. ScopeInfo* ScopeInfo::FromScope(ByteCodeGenerator* byteCodeGenerator, ParseableFunctionInfo* parent, Scope* scope, ScriptContext *scriptContext)
  58. {
  59. int count = scope->Count();
  60. // Add argsPlaceHolder which includes same name args and destructuring patterns on parameters
  61. AddSlotCount(count, scope->GetFunc()->argsPlaceHolderSlotCount);
  62. AddSlotCount(count, scope->GetFunc()->thisScopeSlot != Js::Constants::NoRegister ? 1 : 0);
  63. AddSlotCount(count, scope->GetFunc()->superScopeSlot != Js::Constants::NoRegister ? 1 : 0);
  64. AddSlotCount(count, scope->GetFunc()->newTargetScopeSlot != Js::Constants::NoRegister ? 1 : 0);
  65. ScopeInfo* scopeInfo = RecyclerNewPlusZ(scriptContext->GetRecycler(),
  66. count * sizeof(SymbolInfo),
  67. ScopeInfo, parent ? parent->GetFunctionInfo() : nullptr, count);
  68. scopeInfo->isDynamic = scope->GetIsDynamic();
  69. scopeInfo->isObject = scope->GetIsObject();
  70. scopeInfo->mustInstantiate = scope->GetMustInstantiate();
  71. scopeInfo->isCached = (scope->GetFunc()->GetBodyScope() == scope) && scope->GetFunc()->GetHasCachedScope();
  72. scopeInfo->isGlobalEval = scope->GetScopeType() == ScopeType_GlobalEvalBlock;
  73. scopeInfo->hasLocalInClosure = scope->GetHasOwnLocalInClosure();
  74. TRACE_BYTECODE(_u("\nSave ScopeInfo: %s parent: %s #symbols: %d %s\n"),
  75. scope->GetFunc()->name, parent ? parent->GetDisplayName() : _u(""), count,
  76. scopeInfo->isObject ? _u("isObject") : _u(""));
  77. MapSymbolData mapSymbolData = { byteCodeGenerator, scope->GetFunc(), 0 };
  78. scope->ForEachSymbol([&mapSymbolData, scopeInfo, scope](Symbol * sym)
  79. {
  80. Assert(scope == sym->GetScope());
  81. scopeInfo->SaveSymbolInfo(sym, &mapSymbolData);
  82. });
  83. return scopeInfo;
  84. }
  85. //
  86. // Ensure the pids referenced by this scope are tracked.
  87. //
  88. void ScopeInfo::EnsurePidTracking(ScriptContext* scriptContext)
  89. {
  90. for (int i = 0; i < symbolCount; i++)
  91. {
  92. auto propertyName = scriptContext->GetPropertyName(symbols[i].propertyId);
  93. scriptContext->TrackPid(propertyName);
  94. }
  95. if (funcExprScopeInfo)
  96. {
  97. funcExprScopeInfo->EnsurePidTracking(scriptContext);
  98. }
  99. if (paramScopeInfo)
  100. {
  101. paramScopeInfo->EnsurePidTracking(scriptContext);
  102. }
  103. }
  104. //
  105. // Save needed scope info for a deferred child func. The scope info is empty and only links to parent.
  106. //
  107. void ScopeInfo::SaveParentScopeInfo(FuncInfo* parentFunc, FuncInfo* func)
  108. {
  109. Assert(func->IsDeferred() || func->byteCodeFunction->CanBeDeferred());
  110. // Parent must be parsed
  111. FunctionBody* parent = parentFunc->byteCodeFunction->GetFunctionBody();
  112. ParseableFunctionInfo* funcBody = func->byteCodeFunction;
  113. TRACE_BYTECODE(_u("\nSave ScopeInfo: %s parent: %s\n\n"),
  114. funcBody->GetDisplayName(), parent->GetDisplayName());
  115. ScopeInfo *info = FromParent(parent);
  116. info->parentOnly = true;
  117. funcBody->SetScopeInfo(info);
  118. }
  119. //
  120. // Save scope info for an outer func which has deferred child.
  121. //
  122. void ScopeInfo::SaveScopeInfo(ByteCodeGenerator* byteCodeGenerator, FuncInfo* parentFunc, FuncInfo* func)
  123. {
  124. ParseableFunctionInfo* funcBody = func->byteCodeFunction;
  125. Assert((!func->IsGlobalFunction() || byteCodeGenerator->GetFlags() & fscrEvalCode) &&
  126. (func->HasDeferredChild() || func->HasRedeferrableChild() || funcBody->IsReparsed()));
  127. // If we are reparsing a deferred function, we already have correct "parent" info in
  128. // funcBody->scopeInfo. parentFunc is the knopProg shell and should not be used in this
  129. // case. We should use existing parent if available.
  130. ParseableFunctionInfo * parent = funcBody->GetScopeInfo() ?
  131. funcBody->GetScopeInfo()->GetParent() :
  132. parentFunc ? parentFunc->byteCodeFunction : nullptr;
  133. ScopeInfo* funcExprScopeInfo = nullptr;
  134. Scope* funcExprScope = func->GetFuncExprScope();
  135. if (funcExprScope && funcExprScope->GetMustInstantiate())
  136. {
  137. funcExprScopeInfo = FromScope(byteCodeGenerator, parent, funcExprScope, funcBody->GetScriptContext());
  138. }
  139. Scope* bodyScope = func->IsGlobalFunction() ? func->GetGlobalEvalBlockScope() : func->GetBodyScope();
  140. ScopeInfo* paramScopeInfo = nullptr;
  141. Scope* paramScope = func->GetParamScope();
  142. if (!func->IsBodyAndParamScopeMerged())
  143. {
  144. paramScopeInfo = FromScope(byteCodeGenerator, parent, paramScope, funcBody->GetScriptContext());
  145. }
  146. ScopeInfo* scopeInfo = FromScope(byteCodeGenerator, parent, bodyScope, funcBody->GetScriptContext());
  147. scopeInfo->SetFuncExprScopeInfo(funcExprScopeInfo);
  148. scopeInfo->SetParamScopeInfo(paramScopeInfo);
  149. funcBody->SetScopeInfo(scopeInfo);
  150. }
  151. void ScopeInfo::SaveScopeInfoForDeferParse(ByteCodeGenerator* byteCodeGenerator, FuncInfo* parentFunc, FuncInfo* funcInfo)
  152. {
  153. // TODO: Not technically necessary, as we always do scope look up on eval if it is
  154. // not found in the scope chain, and block scopes are always objects in eval.
  155. // But if we save the global eval block scope for deferred child so that we can look up
  156. // let/const in that scope with slot index instead of doing a scope lookup.
  157. // We will have to implement encoding block scope info to enable, which will also
  158. // enable defer parsing function that are in block scopes.
  159. if (funcInfo->byteCodeFunction &&
  160. funcInfo->byteCodeFunction->GetScopeInfo() != nullptr &&
  161. !funcInfo->byteCodeFunction->GetScopeInfo()->IsParentInfoOnly())
  162. {
  163. // No need to regenerate scope info if we re-compile an enclosing function
  164. return;
  165. }
  166. Scope* currentScope = byteCodeGenerator->GetCurrentScope();
  167. Assert(currentScope == funcInfo->GetBodyScope());
  168. if (funcInfo->HasDeferredChild() ||
  169. funcInfo->HasRedeferrableChild() ||
  170. (!funcInfo->IsGlobalFunction() &&
  171. funcInfo->byteCodeFunction &&
  172. funcInfo->byteCodeFunction->IsReparsed() &&
  173. funcInfo->byteCodeFunction->GetFunctionBody()->HasAllNonLocalReferenced()))
  174. {
  175. // When we reparse due to attach, we would need to capture all of them, since they were captured before going to debug mode.
  176. Js::ScopeInfo::SaveScopeInfo(byteCodeGenerator, parentFunc, funcInfo);
  177. }
  178. else if (funcInfo->IsDeferred() || funcInfo->IsRedeferrable())
  179. {
  180. // Don't need to remember the parent function if we have a global function
  181. if (!parentFunc->IsGlobalFunction() ||
  182. ((byteCodeGenerator->GetFlags() & fscrEvalCode) && (parentFunc->HasDeferredChild() || parentFunc->HasRedeferrableChild())))
  183. {
  184. // TODO: currently we only support defer nested function that is in function scope (no block scope, no with scope, etc.)
  185. #if DBG
  186. if (funcInfo->GetFuncExprScope() && funcInfo->GetFuncExprScope()->GetIsObject())
  187. {
  188. if (!funcInfo->IsBodyAndParamScopeMerged())
  189. {
  190. Assert(currentScope->GetEnclosingScope()->GetEnclosingScope() == funcInfo->GetFuncExprScope());
  191. }
  192. else
  193. {
  194. Assert(currentScope->GetEnclosingScope() == funcInfo->GetFuncExprScope() &&
  195. currentScope->GetEnclosingScope()->GetEnclosingScope() ==
  196. (parentFunc->IsGlobalFunction() && parentFunc->GetGlobalEvalBlockScope()->GetMustInstantiate() ?
  197. parentFunc->GetGlobalEvalBlockScope() : parentFunc->GetBodyScope()));
  198. }
  199. }
  200. else
  201. {
  202. if (currentScope->GetEnclosingScope() == parentFunc->GetParamScope())
  203. {
  204. Assert(!parentFunc->IsBodyAndParamScopeMerged());
  205. Assert(funcInfo->IsBodyAndParamScopeMerged());
  206. }
  207. else if (currentScope->GetEnclosingScope() == funcInfo->GetParamScope())
  208. {
  209. Assert(!funcInfo->IsBodyAndParamScopeMerged());
  210. }
  211. #if 0
  212. else
  213. {
  214. Assert(currentScope->GetEnclosingScope() ==
  215. (parentFunc->IsGlobalFunction() && parentFunc->GetGlobalEvalBlockScope() && parentFunc->GetGlobalEvalBlockScope()->GetMustInstantiate() ? parentFunc->GetGlobalEvalBlockScope() : parentFunc->GetBodyScope()));
  216. }
  217. #endif
  218. }
  219. #endif
  220. Js::ScopeInfo::SaveParentScopeInfo(parentFunc, funcInfo);
  221. }
  222. }
  223. }
  224. //
  225. // Load persisted scope info.
  226. //
  227. void ScopeInfo::GetScopeInfo(Parser *parser, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, Scope* scope)
  228. {
  229. ScriptContext* scriptContext;
  230. ArenaAllocator* alloc;
  231. // Load scope attributes and push onto scope stack.
  232. scope->SetIsDynamic(this->isDynamic);
  233. if (this->isObject)
  234. {
  235. scope->SetIsObject();
  236. }
  237. scope->SetMustInstantiate(this->mustInstantiate);
  238. scope->SetHasOwnLocalInClosure(this->hasLocalInClosure);
  239. if (parser)
  240. {
  241. scriptContext = parser->GetScriptContext();
  242. alloc = parser->GetAllocator();
  243. }
  244. else
  245. {
  246. TRACE_BYTECODE(_u("\nRestore ScopeInfo: %s #symbols: %d %s\n"),
  247. funcInfo->name, symbolCount, isObject ? _u("isObject") : _u(""));
  248. Assert(!this->isCached || scope == funcInfo->GetBodyScope());
  249. funcInfo->SetHasCachedScope(this->isCached);
  250. byteCodeGenerator->PushScope(scope);
  251. // this->scope was created/saved during parsing and used by
  252. // ByteCodeGenerator::RestoreScopeInfo. We no longer need it by now.
  253. // Clear it to avoid GC false positive (arena memory later used by GC).
  254. Assert(this->scope == scope);
  255. this->scope = nullptr;
  256. // The scope is already populated, so we're done.
  257. return;
  258. }
  259. // Load scope symbols
  260. // On first access to the scopeinfo, replace the ID's with PropertyRecord*'s to save the dictionary lookup
  261. // on later accesses. Replace them all before allocating Symbol's to prevent inconsistency on OOM.
  262. if (!this->areNamesCached && !PHASE_OFF1(Js::CacheScopeInfoNamesPhase))
  263. {
  264. for (int i = 0; i < symbolCount; i++)
  265. {
  266. PropertyId propertyId = GetSymbolId(i);
  267. if (propertyId != 0) // There may be empty slots, e.g. "arguments" may have no slot
  268. {
  269. PropertyRecord const* name = scriptContext->GetPropertyName(propertyId);
  270. this->SetPropertyName(i, name);
  271. }
  272. }
  273. this->areNamesCached = true;
  274. }
  275. for (int i = 0; i < symbolCount; i++)
  276. {
  277. PropertyRecord const* name = nullptr;
  278. if (this->areNamesCached)
  279. {
  280. name = this->GetPropertyName(i);
  281. }
  282. else
  283. {
  284. PropertyId propertyId = GetSymbolId(i);
  285. if (propertyId != 0) // There may be empty slots, e.g. "arguments" may have no slot
  286. {
  287. name = scriptContext->GetPropertyName(propertyId);
  288. }
  289. }
  290. if (name != nullptr)
  291. {
  292. SymbolType symbolType = GetSymbolType(i);
  293. SymbolName symName(name->GetBuffer(), name->GetLength());
  294. Symbol *sym = Anew(alloc, Symbol, symName, nullptr, symbolType);
  295. sym->SetScopeSlot(static_cast<PropertyId>(i));
  296. sym->SetIsBlockVar(GetIsBlockVariable(i));
  297. sym->SetIsFuncExpr(GetIsFuncExpr(i));
  298. sym->SetIsModuleExportStorage(GetIsModuleExportStorage(i));
  299. sym->SetIsModuleImport(GetIsModuleImport(i));
  300. if (GetHasFuncAssignment(i))
  301. {
  302. sym->RestoreHasFuncAssignment();
  303. }
  304. scope->AddNewSymbol(sym);
  305. sym->SetHasNonLocalReference();
  306. Assert(parser);
  307. parser->RestorePidRefForSym(sym);
  308. TRACE_BYTECODE(_u("%12s %d\n"), sym->GetName().GetBuffer(), sym->GetScopeSlot());
  309. }
  310. }
  311. this->scope = scope;
  312. DebugOnly(scope->isRestored = true);
  313. }
  314. ScopeInfo::AutoCapturesAllScope::AutoCapturesAllScope(Scope* scope, bool turnOn)
  315. : scope(scope)
  316. {
  317. oldCapturesAll = scope->GetCapturesAll();
  318. if (turnOn)
  319. {
  320. scope->SetCapturesAll(true);
  321. }
  322. }
  323. ScopeInfo::AutoCapturesAllScope::~AutoCapturesAllScope()
  324. {
  325. scope->SetCapturesAll(oldCapturesAll);
  326. }
  327. } // namespace Js