JavascriptGenerator.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
  4. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  5. //-------------------------------------------------------------------------------------------------------
  6. #include "RuntimeLibraryPch.h"
  7. #include "Language/InterpreterStackFrame.h"
  8. using namespace Js;
  9. namespace
  10. {
  11. // RAII helper to set the state of the generator to completed if an exception is
  12. // thrown or if the save state InterpreterStackFrame is never created implying
  13. // the generator is JITed and returned without ever yielding
  14. struct GeneratorStateHelper
  15. {
  16. using GeneratorState = JavascriptGenerator::GeneratorState;
  17. JavascriptGenerator* generator;
  18. bool didThrow;
  19. GeneratorStateHelper(JavascriptGenerator* generator) :
  20. generator(generator),
  21. didThrow(true)
  22. {
  23. generator->SetState(GeneratorState::Executing);
  24. }
  25. ~GeneratorStateHelper()
  26. {
  27. generator->SetState(IsDone() ? GeneratorState::Completed : GeneratorState::Suspended);
  28. }
  29. bool IsDone()
  30. {
  31. // If the generator is jit'd, we set its interpreter frame to nullptr at the
  32. // end right before the epilogue to signal that the generator has completed
  33. auto* frame = generator->GetFrame();
  34. if (didThrow || frame == nullptr)
  35. return true;
  36. int nextOffset = frame->GetReader()->GetCurrentOffset();
  37. int endOffset = frame->GetFunctionBody()->GetByteCode()->GetLength();
  38. if (nextOffset == endOffset - 1)
  39. return true;
  40. return false;
  41. }
  42. };
  43. }
  44. JavascriptGenerator::JavascriptGenerator(
  45. DynamicType* type,
  46. Arguments& args,
  47. ScriptFunction* scriptFunction) :
  48. DynamicObject(type),
  49. args(args),
  50. frame(nullptr),
  51. state(GeneratorState::SuspendedStart),
  52. scriptFunction(scriptFunction),
  53. resumeYieldObject(nullptr) {}
  54. JavascriptGenerator* JavascriptGenerator::New(
  55. Recycler* recycler,
  56. DynamicType* generatorType,
  57. Arguments& args,
  58. ScriptFunction* scriptFunction)
  59. {
  60. // InterpreterStackFrame takes a pointer to the args, so copy them to the recycler
  61. // heap and use that buffer for the generator's InterpreterStackFrame
  62. Field(Var)* argValuesCopy = nullptr;
  63. if (args.Info.Count > 0)
  64. {
  65. argValuesCopy = RecyclerNewArray(recycler, Field(Var), args.Info.Count);
  66. CopyArray(argValuesCopy, args.Info.Count, args.Values, args.Info.Count);
  67. }
  68. Arguments heapArgs(args.Info, unsafe_write_barrier_cast<Var*>(argValuesCopy));
  69. #if GLOBAL_ENABLE_WRITE_BARRIER
  70. if (CONFIG_FLAG(ForceSoftwareWriteBarrier))
  71. {
  72. JavascriptGenerator* obj = RecyclerNewFinalized(
  73. recycler,
  74. JavascriptGenerator,
  75. generatorType,
  76. heapArgs,
  77. scriptFunction);
  78. if (obj->args.Values != nullptr)
  79. {
  80. recycler->RegisterPendingWriteBarrierBlock(
  81. obj->args.Values,
  82. obj->args.Info.Count * sizeof(Var));
  83. recycler->RegisterPendingWriteBarrierBlock(
  84. &obj->args.Values,
  85. sizeof(Var*));
  86. }
  87. return obj;
  88. }
  89. #endif
  90. return RecyclerNew(recycler, JavascriptGenerator, generatorType, heapArgs, scriptFunction);
  91. }
  92. template<>
  93. bool Js::VarIsImpl<JavascriptGenerator>(RecyclableObject* obj)
  94. {
  95. auto typeId = JavascriptOperators::GetTypeId(obj);
  96. return typeId == TypeIds_Generator || typeId == TypeIds_AsyncGenerator;
  97. }
  98. void JavascriptGenerator::SetFrame(InterpreterStackFrame* frame, size_t bytes)
  99. {
  100. Assert(this->frame == nullptr);
  101. this->frame = frame;
  102. #if GLOBAL_ENABLE_WRITE_BARRIER
  103. if (CONFIG_FLAG(ForceSoftwareWriteBarrier))
  104. {
  105. GetScriptContext()->GetRecycler()->RegisterPendingWriteBarrierBlock(frame, bytes);
  106. }
  107. #endif
  108. }
  109. void JavascriptGenerator::SetFrameSlots(Js::RegSlot slotCount, Field(Var)* frameSlotArray)
  110. {
  111. AssertMsg(this->frame->GetFunctionBody()->GetLocalsCount() == slotCount,
  112. "Unexpected mismatch in frame slot count for generated.");
  113. for (Js::RegSlot i = 0; i < slotCount; i++)
  114. GetFrame()->m_localSlots[i] = frameSlotArray[i];
  115. }
  116. #if GLOBAL_ENABLE_WRITE_BARRIER
  117. void JavascriptGenerator::Finalize(bool isShutdown)
  118. {
  119. if (CONFIG_FLAG(ForceSoftwareWriteBarrier) && !isShutdown)
  120. {
  121. auto* recycler = GetScriptContext()->GetRecycler();
  122. if (this->frame)
  123. recycler->UnRegisterPendingWriteBarrierBlock(this->frame);
  124. if (this->args.Values)
  125. recycler->UnRegisterPendingWriteBarrierBlock(this->args.Values);
  126. }
  127. }
  128. #endif
  129. void JavascriptGenerator::ThrowIfExecuting(const char16* apiName)
  130. {
  131. if (this->IsExecuting())
  132. {
  133. JavascriptError::ThrowTypeError(
  134. GetScriptContext(),
  135. JSERR_GeneratorAlreadyExecuting,
  136. apiName);
  137. }
  138. }
  139. Var JavascriptGenerator::CallGenerator(Var data, ResumeYieldKind resumeKind)
  140. {
  141. Assert(!IsExecuting() && !IsCompleted());
  142. ScriptContext* scriptContext = this->GetScriptContext();
  143. JavascriptLibrary* library = scriptContext->GetLibrary();
  144. Var result = nullptr;
  145. if (this->frame)
  146. {
  147. // if the function already has a state it may be going to resume in the jit
  148. // if so copy any innerScopes into registers jit can access
  149. uint32 innerScopeCount = this->scriptFunction->GetFunctionBody()->GetInnerScopeCount();
  150. for (uint32 i = 0; i < innerScopeCount; ++i)
  151. {
  152. Js::RegSlot reg = this->scriptFunction->GetFunctionBody()->GetFirstInnerScopeRegister() + i;
  153. this->frame->SetNonVarReg(reg, this->frame->InnerScopeFromIndex(i));
  154. }
  155. }
  156. SetResumeYieldProperties(data, resumeKind);
  157. {
  158. Var thunkArgs[] = {this, this->resumeYieldObject};
  159. Arguments arguments(_countof(thunkArgs), thunkArgs);
  160. GeneratorStateHelper helper(this);
  161. try
  162. {
  163. BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
  164. {
  165. result = JavascriptFunction::CallFunction<1>(
  166. this->scriptFunction,
  167. this->scriptFunction->GetEntryPoint(),
  168. arguments);
  169. }
  170. END_SAFE_REENTRANT_CALL
  171. helper.didThrow = false;
  172. }
  173. catch (const JavascriptException& err)
  174. {
  175. JavascriptExceptionOperators::DoThrowCheckClone(err.GetAndClear(), scriptContext);
  176. }
  177. }
  178. // Clear the value property of the resume yield object so that we don't
  179. // extend the lifetime of the value
  180. SetResumeYieldProperties(library->GetUndefined(), ResumeYieldKind::Normal);
  181. if (IsCompleted())
  182. return library->CreateIteratorResultObject(result, library->GetTrue());
  183. return result;
  184. }
  185. void JavascriptGenerator::SetResumeYieldProperties(Var value, ResumeYieldKind kind)
  186. {
  187. auto* library = GetScriptContext()->GetLibrary();
  188. DynamicType* type = library->GetResumeYieldObjectType();
  189. if (!this->resumeYieldObject)
  190. this->resumeYieldObject = DynamicObject::New(GetScriptContext()->GetRecycler(), type);
  191. else
  192. AssertOrFailFast(this->resumeYieldObject->GetDynamicType() == type);
  193. Var kindVar = TaggedInt::ToVarUnchecked((int)kind);
  194. this->resumeYieldObject->SetSlot(SetSlotArguments(Js::PropertyIds::value, 0, value));
  195. this->resumeYieldObject->SetSlot(SetSlotArguments(Js::PropertyIds::kind, 1, kindVar));
  196. }
  197. Var JavascriptGenerator::EntryNext(RecyclableObject* function, CallInfo callInfo, ...)
  198. {
  199. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  200. ARGUMENTS(args, callInfo);
  201. auto* scriptContext = function->GetScriptContext();
  202. auto* library = scriptContext->GetLibrary();
  203. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Generator.prototype.next"));
  204. if (!VarIs<DynamicObject>(args[0]) || JavascriptOperators::GetTypeId(args[0]) != TypeIds_Generator)
  205. {
  206. JavascriptError::ThrowTypeErrorVar(
  207. scriptContext,
  208. JSERR_NeedObjectOfType,
  209. _u("Generator.prototype.next"),
  210. _u("Generator"));
  211. }
  212. Var undefinedVar = library->GetUndefined();
  213. Var input = args.Info.Count > 1 ? args[1] : undefinedVar;
  214. auto* generator = UnsafeVarTo<JavascriptGenerator>(args[0]);
  215. if (generator->IsCompleted())
  216. return library->CreateIteratorResultObject(undefinedVar, library->GetTrue());
  217. generator->ThrowIfExecuting(_u("Generator.prototype.next"));
  218. return generator->CallGenerator(input, ResumeYieldKind::Normal);
  219. }
  220. Var JavascriptGenerator::EntryReturn(RecyclableObject* function, CallInfo callInfo, ...)
  221. {
  222. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  223. ARGUMENTS(args, callInfo);
  224. auto* scriptContext = function->GetScriptContext();
  225. auto* library = scriptContext->GetLibrary();
  226. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Generator.prototype.return"));
  227. if (!VarIs<DynamicObject>(args[0]) || JavascriptOperators::GetTypeId(args[0]) != TypeIds_Generator)
  228. {
  229. JavascriptError::ThrowTypeErrorVar(
  230. scriptContext,
  231. JSERR_NeedObjectOfType,
  232. _u("Generator.prototype.return"),
  233. _u("Generator"));
  234. }
  235. Var input = args.Info.Count > 1 ? args[1] : library->GetUndefined();
  236. auto* generator = UnsafeVarTo<JavascriptGenerator>(args[0]);
  237. if (generator->IsSuspendedStart())
  238. generator->SetCompleted();
  239. if (generator->IsCompleted())
  240. return library->CreateIteratorResultObject(input, library->GetTrue());
  241. generator->ThrowIfExecuting(_u("Generator.prototype.return"));
  242. return generator->CallGenerator(input, ResumeYieldKind::Return);
  243. }
  244. Var JavascriptGenerator::EntryThrow(RecyclableObject* function, CallInfo callInfo, ...)
  245. {
  246. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  247. ARGUMENTS(args, callInfo);
  248. auto* scriptContext = function->GetScriptContext();
  249. auto* library = scriptContext->GetLibrary();
  250. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Generator.prototype.throw"));
  251. if (!VarIs<DynamicObject>(args[0]) || JavascriptOperators::GetTypeId(args[0]) != TypeIds_Generator)
  252. {
  253. JavascriptError::ThrowTypeErrorVar(
  254. scriptContext,
  255. JSERR_NeedObjectOfType,
  256. _u("Generator.prototype.throw"),
  257. _u("Generator"));
  258. }
  259. Var input = args.Info.Count > 1 ? args[1] : library->GetUndefined();
  260. auto* generator = UnsafeVarTo<JavascriptGenerator>(args[0]);
  261. if (generator->IsSuspendedStart())
  262. generator->SetCompleted();
  263. if (generator->IsCompleted())
  264. JavascriptExceptionOperators::OP_Throw(input, scriptContext);
  265. generator->ThrowIfExecuting(_u("Generator.prototype.throw"));
  266. return generator->CallGenerator(input, ResumeYieldKind::Throw);
  267. }
  268. bool JavascriptGenerator::IsAsyncModule() const
  269. {
  270. FunctionProxy* proxy = this->scriptFunction->GetFunctionProxy();
  271. return proxy->IsModule() && proxy->IsAsync();
  272. }
  273. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  274. void JavascriptGenerator::OutputBailInTrace(JavascriptGenerator* generator)
  275. {
  276. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  277. FunctionBody* fnBody = generator->scriptFunction->GetFunctionBody();
  278. Output::Print(
  279. _u("BailIn: function: %s (%s) offset: #%04x\n"),
  280. fnBody->GetDisplayName(),
  281. fnBody->GetDebugNumberSet(debugStringBuffer),
  282. generator->frame->m_reader.GetCurrentOffset());
  283. if (generator->bailInSymbolsTraceArrayCount == 0)
  284. {
  285. Output::Print(
  286. _u("BailIn: No symbols reloaded\n"),
  287. fnBody->GetDisplayName(),
  288. fnBody->GetDebugNumberSet(debugStringBuffer));
  289. }
  290. else
  291. {
  292. for (int i = 0; i < generator->bailInSymbolsTraceArrayCount; i++)
  293. {
  294. const BailInSymbol& symbol = generator->bailInSymbolsTraceArray[i];
  295. Output::Print(_u("BailIn: Register #%4d, value: 0x%p\n"), symbol.id, symbol.value);
  296. }
  297. }
  298. }
  299. #endif
  300. #if ENABLE_TTD
  301. JavascriptGenerator *JavascriptGenerator::New(
  302. Recycler* recycler,
  303. DynamicType* generatorType,
  304. Arguments& args,
  305. JavascriptGenerator::GeneratorState state)
  306. {
  307. auto* obj = JavascriptGenerator::New(recycler, generatorType, args, nullptr);
  308. obj->SetState(state);
  309. return obj;
  310. }
  311. void JavascriptGenerator::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
  312. {
  313. if (this->scriptFunction != nullptr)
  314. {
  315. extractor->MarkVisitVar(this->scriptFunction);
  316. }
  317. // frame is null when generator has been completed
  318. if (this->frame != nullptr)
  319. {
  320. // mark slot variables for traversal
  321. Js::RegSlot slotCount = this->frame->GetFunctionBody()->GetLocalsCount();
  322. for (Js::RegSlot i = 0; i < slotCount; i++)
  323. {
  324. Js::Var curr = this->frame->m_localSlots[i];
  325. if (curr != nullptr)
  326. {
  327. extractor->MarkVisitVar(curr);
  328. }
  329. }
  330. }
  331. // args.Values is null when generator has been completed
  332. if (this->args.Values != nullptr)
  333. {
  334. // mark argument variables for traversal
  335. uint32 argCount = this->args.GetArgCountWithExtraArgs();
  336. for (uint32 i = 0; i < argCount; i++)
  337. {
  338. Js::Var curr = this->args[i];
  339. if (curr != nullptr)
  340. {
  341. extractor->MarkVisitVar(curr);
  342. }
  343. }
  344. }
  345. }
  346. TTD::NSSnapObjects::SnapObjectType JavascriptGenerator::GetSnapTag_TTD() const
  347. {
  348. return TTD::NSSnapObjects::SnapObjectType::SnapGenerator;
  349. }
  350. void JavascriptGenerator::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
  351. {
  352. TTD::NSSnapObjects::SnapGeneratorInfo* gi = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapGeneratorInfo>();
  353. // TODO: BUGBUG - figure out how to determine what the prototype was
  354. gi->generatorPrototype = 0;
  355. //if (this->GetPrototype() == this->GetScriptContext()->GetLibrary()->GetNull())
  356. //{
  357. // gi->generatorPrototype = 1;
  358. //}
  359. //else if (this->GetType() == this->GetScriptContext()->GetLibrary()->GetGeneratorConstructorPrototypeObjectType())
  360. //{
  361. // // check type here, not prototype, since type is static across generators
  362. // gi->generatorPrototype = 2;
  363. //}
  364. //else
  365. //{
  366. // //TTDAssert(false, "unexpected prototype found JavascriptGenerator");
  367. //}
  368. gi->scriptFunction = TTD_CONVERT_VAR_TO_PTR_ID(this->scriptFunction);
  369. gi->state = static_cast<uint32>(this->state);
  370. // grab slot info from InterpreterStackFrame
  371. gi->frame_slotCount = 0;
  372. gi->frame_slotArray = nullptr;
  373. if (this->frame != nullptr)
  374. {
  375. gi->frame_slotCount = this->frame->GetFunctionBody()->GetLocalsCount();
  376. if (gi->frame_slotCount > 0)
  377. {
  378. gi->frame_slotArray = alloc.SlabAllocateArray<TTD::TTDVar>(gi->frame_slotCount);
  379. }
  380. for (Js::RegSlot i = 0; i < gi->frame_slotCount; i++)
  381. {
  382. gi->frame_slotArray[i] = this->frame->m_localSlots[i];
  383. }
  384. }
  385. // grab arguments
  386. TTD_PTR_ID* depArray = nullptr;
  387. uint32 depCount = 0;
  388. if (this->args.Values == nullptr)
  389. {
  390. gi->arguments_count = 0;
  391. }
  392. else
  393. {
  394. gi->arguments_count = this->args.GetArgCountWithExtraArgs();
  395. }
  396. gi->arguments_values = nullptr;
  397. if (gi->arguments_count > 0)
  398. {
  399. gi->arguments_values = alloc.SlabAllocateArray<TTD::TTDVar>(gi->arguments_count);
  400. depArray = alloc.SlabReserveArraySpace<TTD_PTR_ID>(gi->arguments_count);
  401. }
  402. for (uint32 i = 0; i < gi->arguments_count; i++)
  403. {
  404. gi->arguments_values[i] = this->args[i];
  405. if (gi->arguments_values[i] != nullptr && TTD::JsSupport::IsVarComplexKind(gi->arguments_values[i]))
  406. {
  407. depArray[depCount] = TTD_CONVERT_VAR_TO_PTR_ID(gi->arguments_values[i]);
  408. depCount++;
  409. }
  410. }
  411. if (depCount > 0)
  412. {
  413. alloc.SlabCommitArraySpace<TTD_PTR_ID>(depCount, gi->arguments_count);
  414. }
  415. else if (gi->arguments_count > 0)
  416. {
  417. alloc.SlabAbortArraySpace<TTD_PTR_ID>(gi->arguments_count);
  418. }
  419. if (this->frame != nullptr)
  420. {
  421. gi->byteCodeReader_offset = this->frame->GetReader()->GetCurrentOffset();
  422. }
  423. else
  424. {
  425. gi->byteCodeReader_offset = 0;
  426. }
  427. // Copy the CallInfo data into the struct
  428. gi->arguments_callInfo_count = gi->arguments_count > 0 ? this->args.Info.Count : 0;
  429. gi->arguments_callInfo_flags = this->args.Info.Flags;
  430. // TODO: understand why there's a mis-match between args.Info.Count and GetArgCountWithExtraArgs
  431. // TTDAssert(this->args.Info.Count == gi->arguments_count, "mismatched count between args.Info and GetArgCountWithExtraArgs");
  432. if (depCount == 0)
  433. {
  434. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapGeneratorInfo*, TTD::NSSnapObjects::SnapObjectType::SnapGenerator>(objData, gi);
  435. }
  436. else
  437. {
  438. TTDAssert(depArray != nullptr, "depArray should be non-null if depCount is > 0");
  439. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapGeneratorInfo*, TTD::NSSnapObjects::SnapObjectType::SnapGenerator>(objData, gi, alloc, depCount, depArray);
  440. }
  441. }
  442. #endif