ScopeInfo.cpp 14 KB

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