Skip to content

Commit b68f5c2

Browse files
authored
Generator misc (#6533)
* Generator method error fixes * Cache Await Object * Remove Dead code * Cache AsyncGenerator Continuations * Remove unnecessary prototype creation * TTD fixes for Await Object
1 parent 613fbf7 commit b68f5c2

28 files changed

+127
-246
lines changed

lib/Backend/Lower.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -2957,9 +2957,7 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
29572957

29582958
case Js::OpCode::NewAwaitObject:
29592959
{
2960-
IR::Opnd *src1Opnd = instr->UnlinkSrc1();
29612960
LoadScriptContext(instr);
2962-
m_lowererMD.LoadHelperArgument(instr, src1Opnd);
29632961
m_lowererMD.ChangeToHelperCall(instr, IR::HelperNewAwaitObject);
29642962
break;
29652963
}

lib/Backend/amd64/LinearScanMD.h

-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ class LinearScanMD : public LinearScanMDShared
3737
void GenerateBailOut(IR::Instr * instr,
3838
__in_ecount(registerSaveSymsCount) StackSym ** registerSaveSyms,
3939
uint registerSaveSymsCount);
40-
IR::Instr* GenerateBailInForGeneratorYield(IR::Instr* resumeLabelInstr, BailOutInfo* bailOutInfo);
4140

4241
private:
4342
static void SaveAllRegisters(BailOutRecord *const bailOutRecord);

lib/Backend/arm/LinearScanMD.cpp

-6
Original file line numberDiff line numberDiff line change
@@ -345,12 +345,6 @@ LinearScanMD::GenerateBailOut(
345345
instr->ReplaceSrc1(IR::RegOpnd::New(nullptr, RegLR, TyMachPtr, func));
346346
}
347347

348-
IR::Instr *
349-
LinearScanMD::GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo)
350-
{
351-
Js::Throw::NotImplemented();
352-
}
353-
354348
uint LinearScanMD::GetRegisterSaveIndex(RegNum reg)
355349
{
356350
if (RegTypes[reg] == TyFloat64)

lib/Backend/arm/LinearScanMD.h

-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ class LinearScanMD : public LinearScanMDShared
3939
void LegalizeConstantUse(IR::Instr * instr, IR::Opnd * opnd) { /* no op for arm */ }
4040

4141
void GenerateBailOut(IR::Instr * instr, __in_ecount(registerSaveSymsCount) StackSym ** registerSaveSyms, uint registerSaveSymsCount);
42-
IR::Instr *GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo);
4342
private:
4443
static void SaveAllRegisters(BailOutRecord *const bailOutRecord);
4544
public:

lib/Backend/arm64/LinearScanMD.cpp

-6
Original file line numberDiff line numberDiff line change
@@ -341,12 +341,6 @@ LinearScanMD::GenerateBailOut(
341341
instr->ReplaceSrc1(IR::RegOpnd::New(nullptr, RegLR, TyMachPtr, func));
342342
}
343343

344-
IR::Instr *
345-
LinearScanMD::GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo)
346-
{
347-
Js::Throw::NotImplemented();
348-
}
349-
350344
uint LinearScanMD::GetRegisterSaveIndex(RegNum reg)
351345
{
352346
return reg;

lib/Backend/arm64/LinearScanMD.h

-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ class LinearScanMD : public LinearScanMDShared
4646
void LegalizeConstantUse(IR::Instr * instr, IR::Opnd * opnd) { /* no op for arm */ }
4747

4848
void GenerateBailOut(IR::Instr * instr, __in_ecount(registerSaveSymsCount) StackSym ** registerSaveSyms, uint registerSaveSymsCount);
49-
IR::Instr *GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo);
5049
private:
5150
static void SaveAllRegisters(BailOutRecord *const bailOutRecord);
5251
public:

lib/Backend/i386/LinearScanMD.cpp

-181
Original file line numberDiff line numberDiff line change
@@ -180,187 +180,6 @@ LinearScanMD::GenerateBailOut(IR::Instr * instr, __in_ecount(registerSaveSymsCou
180180
}
181181
}
182182

183-
IR::Instr *
184-
LinearScanMD::GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo)
185-
{
186-
BailOutRecord * bailOutRecord = bailOutInfo->bailOutRecord;
187-
IR::Instr * instrAfter = resumeLabelInstr->m_next;
188-
IR::Instr * newInstr;
189-
190-
// if (argOuts) {
191-
// sub esp, numArgOutsTimesMachSizeMachAligned
192-
// }
193-
// mov eax, prm1
194-
// mov eax, [eax + offset of JavascriptGenerator::frame]
195-
// <restore live stack syms, out params, and registers directly from InterpreterStackFrame registers (use ecx as temporary for mov IndirOpnd, IndirOpnd before restoring registers)>
196-
197-
IntConstType stackAdjustSize = 0;
198-
bailOutRecord->MapStartCallParamCounts([&stackAdjustSize](uint startCallParamCount) {
199-
IntConstType sizeValue = startCallParamCount;
200-
int32 stackAlignment = Math::Align<int32>(sizeValue*MachPtr, MachStackAlignment) - sizeValue*MachPtr;
201-
if (stackAlignment != 0)
202-
{
203-
sizeValue += 1;
204-
}
205-
sizeValue *= MachPtr;
206-
stackAdjustSize += sizeValue;
207-
});
208-
209-
if (stackAdjustSize != 0)
210-
{
211-
if ((uint32)stackAdjustSize > AutoSystemInfo::PageSize)
212-
{
213-
// mov eax, sizeOpnd->m_value
214-
// call _chkstk
215-
IR::RegOpnd *eaxOpnd = IR::RegOpnd::New(nullptr, RegEAX, TyMachReg, this->func);
216-
newInstr = IR::Instr::New(Js::OpCode::MOV, eaxOpnd, IR::IntConstOpnd::New(stackAdjustSize, TyInt32, this->func), this->func);
217-
instrAfter->InsertBefore(newInstr);
218-
219-
newInstr = IR::Instr::New(Js::OpCode::CALL, this->func);
220-
newInstr->SetSrc1(IR::HelperCallOpnd::New(IR::HelperCRT_chkstk, this->func));
221-
instrAfter->InsertBefore(newInstr);
222-
this->func->SetHasCallsOnSelfAndParents();
223-
}
224-
else
225-
{
226-
// lea esp, [esp - sizeValue]
227-
IR::RegOpnd * espOpnd = IR::RegOpnd::New(nullptr, LowererMD::GetRegStackPointer(), TyMachReg, this->func);
228-
newInstr = IR::Instr::New(Js::OpCode::LEA, espOpnd, IR::IndirOpnd::New(espOpnd, -stackAdjustSize, TyMachReg, this->func), this->func);
229-
instrAfter->InsertBefore(newInstr);
230-
}
231-
}
232-
233-
IR::RegOpnd * eaxRegOpnd = IR::RegOpnd::New(nullptr, RegEAX, TyMachPtr, this->func);
234-
IR::RegOpnd * ecxRegOpnd = IR::RegOpnd::New(nullptr, RegECX, TyVar, this->func);
235-
236-
StackSym * sym = StackSym::NewParamSlotSym(1, this->func);
237-
this->func->SetArgOffset(sym, LowererMD::GetFormalParamOffset() * MachPtr);
238-
IR::SymOpnd * symOpnd = IR::SymOpnd::New(sym, TyMachPtr, this->func);
239-
newInstr = IR::Instr::New(Js::OpCode::MOV, eaxRegOpnd, symOpnd, this->func);
240-
instrAfter->InsertBefore(newInstr);
241-
242-
IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(eaxRegOpnd, Js::JavascriptGenerator::GetFrameOffset(), TyMachPtr, this->func);
243-
newInstr = IR::Instr::New(Js::OpCode::MOV, eaxRegOpnd, indirOpnd, this->func);
244-
instrAfter->InsertBefore(newInstr);
245-
246-
247-
// eax points to the frame, restore stack syms and registers except eax, restore eax last
248-
249-
IR::Instr * eaxRestoreInstr = nullptr;
250-
IR::Instr * instrInsertStackSym = instrAfter;
251-
IR::Instr * instrInsertRegSym = instrAfter;
252-
253-
Assert(bailOutInfo->capturedValues->constantValues.Empty());
254-
Assert(bailOutInfo->capturedValues->copyPropSyms.Empty());
255-
256-
auto restoreSymFn = [this, &eaxRegOpnd, &ecxRegOpnd, &eaxRestoreInstr, &instrInsertStackSym, &instrInsertRegSym](SymID symId)
257-
{
258-
StackSym * stackSym = this->func->m_symTable->FindStackSym(symId);
259-
Assert(stackSym->IsVar());
260-
261-
Assert(stackSym->HasByteCodeRegSlot());
262-
Js::RegSlot byteCodeReg = stackSym->GetByteCodeRegSlot();
263-
int32 offset = byteCodeReg * sizeof(Js::Var) + Js::InterpreterStackFrame::GetOffsetOfLocals();
264-
265-
IR::Opnd * srcOpnd = IR::IndirOpnd::New(eaxRegOpnd, offset, stackSym->GetType(), this->func);
266-
Lifetime * lifetime = stackSym->scratch.linearScan.lifetime;
267-
268-
if (lifetime->isSpilled)
269-
{
270-
// stack restores require an extra register since we can't move an indir directly to an indir on x86
271-
IR::Instr * instr = IR::Instr::New(Js::OpCode::MOV, ecxRegOpnd, srcOpnd, this->func);
272-
instrInsertStackSym->InsertBefore(instr);
273-
274-
IR::SymOpnd * dstOpnd = IR::SymOpnd::New(stackSym, stackSym->GetType(), this->func);
275-
instr = IR::Instr::New(Js::OpCode::MOV, dstOpnd, ecxRegOpnd, this->func);
276-
instrInsertStackSym->InsertBefore(instr);
277-
}
278-
else
279-
{
280-
// register restores must come after stack restores so that we have EAX and ECX free to
281-
// use for stack restores and further EAX must be restored last since it holds the
282-
// pointer to the InterpreterStackFrame from which we are restoring values.
283-
// We must also track these restores using RecordDef in case the symbols are spilled.
284-
285-
IR::RegOpnd * dstRegOpnd = IR::RegOpnd::New(stackSym, stackSym->GetType(), this->func);
286-
dstRegOpnd->SetReg(lifetime->reg);
287-
288-
IR::Instr * instr = IR::Instr::New(Js::OpCode::MOV, dstRegOpnd, srcOpnd, this->func);
289-
instrInsertRegSym->InsertBefore(instr);
290-
291-
if (instrInsertRegSym == instrInsertStackSym)
292-
{
293-
// this is the first register sym, make sure we don't insert stack stores
294-
// after this instruction so we can ensure eax and ecx remain free to use
295-
// for restoring spilled stack syms.
296-
instrInsertStackSym = instr;
297-
}
298-
299-
if (lifetime->reg == RegEAX)
300-
{
301-
// ensure eax is restored last
302-
Assert(instrInsertRegSym != instrInsertStackSym);
303-
304-
instrInsertRegSym = instr;
305-
306-
if (eaxRestoreInstr != nullptr)
307-
{
308-
AssertMsg(false, "this is unexpected until copy prop is enabled");
309-
// eax was mapped to multiple bytecode registers. Obviously only the first
310-
// restore we do will work so change all following stores to `mov eax, eax`.
311-
// We still need to keep them around for RecordDef in case the corresponding
312-
// dst sym is spilled later on.
313-
eaxRestoreInstr->FreeSrc1();
314-
eaxRestoreInstr->SetSrc1(eaxRegOpnd);
315-
}
316-
317-
eaxRestoreInstr = instr;
318-
}
319-
320-
this->linearScan->RecordDef(lifetime, instr, 0);
321-
}
322-
};
323-
324-
FOREACH_BITSET_IN_SPARSEBV(symId, bailOutInfo->byteCodeUpwardExposedUsed)
325-
{
326-
restoreSymFn(symId);
327-
}
328-
NEXT_BITSET_IN_SPARSEBV;
329-
330-
if (bailOutInfo->capturedValues->argObjSyms)
331-
{
332-
FOREACH_BITSET_IN_SPARSEBV(symId, bailOutInfo->capturedValues->argObjSyms)
333-
{
334-
restoreSymFn(symId);
335-
}
336-
NEXT_BITSET_IN_SPARSEBV;
337-
}
338-
339-
// Restore out params.
340-
// Would be nice to use restoreSymFn on a walk of the SymIds where the walk matches
341-
// the logic in LinearScan::FillBailOutRecord, but the walk is very complicated and
342-
// requires state to enumerate the exact syms that are actually mapped in the bail
343-
// out record. So instead, since we have disabled most of GlobOpt for the time
344-
// being, just enumerate the argouts from the BailOutRecord and ignore the syms.
345-
// This may need to be improved to use the syms when the optimizations are brought
346-
// online.
347-
bailOutRecord->MapArgOutOffsets([this, &eaxRegOpnd, &ecxRegOpnd, &instrInsertStackSym](Js::RegSlot regSlot, int32 stackOffset) {
348-
// mov ecx, [eax + bytecode reg offset]
349-
// mov [ebp + native stack offset], ecx
350-
int32 regSlotOffset = Js::InterpreterStackFrame::GetOffsetOfLocals() + (this->func->GetJITFunctionBody()->GetLocalsCount() + regSlot) * sizeof(Js::Var);
351-
IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(eaxRegOpnd, regSlotOffset, TyVar, this->func);
352-
IR::Instr * instr = IR::Instr::New(Js::OpCode::MOV, ecxRegOpnd, indirOpnd, this->func);
353-
instrInsertStackSym->InsertBefore(instr);
354-
355-
IR::RegOpnd* ebpRegOpnd = IR::RegOpnd::New(nullptr, RegEBP, TyMachPtr, this->func);
356-
indirOpnd = IR::IndirOpnd::New(ebpRegOpnd, stackOffset, TyVar, this->func);
357-
instr = IR::Instr::New(Js::OpCode::RestoreOutParam, indirOpnd, ecxRegOpnd, this->func);
358-
instrInsertStackSym->InsertBefore(instr);
359-
});
360-
361-
return instrAfter;
362-
}
363-
364183
__declspec(naked) void LinearScanMD::SaveAllRegisters(BailOutRecord *const bailOutRecord)
365184
{
366185
__asm

lib/Backend/i386/LinearScanMD.h

-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ class LinearScanMD : public LinearScanMDShared
3131
void LegalizeUse(IR::Instr * instr, IR::Opnd * opnd) { /* A nop for x86 */ }
3232
void LegalizeConstantUse(IR::Instr * instr, IR::Opnd * opnd) { /* A nop for x86 */ }
3333
void GenerateBailOut(IR::Instr * instr, __in_ecount(registerSaveSymsCount) StackSym ** registerSaveSyms, uint registerSaveSymsCount);
34-
IR::Instr *GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo);
3534
void InsertOpHelperSpillAndRestores(SList<OpHelperBlock> *opHelperBlockList);
3635
void EndOfHelperBlock(uint32 helperSpilledLiveranges) { /* NOP */ }
3736

lib/Runtime/ByteCode/ByteCodeEmitter.cpp

+16-6
Original file line numberDiff line numberDiff line change
@@ -1794,6 +1794,10 @@ void ByteCodeGenerator::FinalizeRegisters(FuncInfo* funcInfo, Js::FunctionBody*
17941794
// NOTE: The FB expects the yield reg to be the final non-temp.
17951795
if (byteCodeFunction->IsCoroutine())
17961796
{
1797+
if (funcInfo->root->IsAsync())
1798+
{
1799+
funcInfo->AssignAwaitRegister();
1800+
}
17971801
funcInfo->AssignYieldRegister();
17981802
}
17991803

@@ -2903,6 +2907,11 @@ void ByteCodeGenerator::EmitOneFunction(ParseNodeFnc *pnodeFnc)
29032907
LoadAllConstants(funcInfo);
29042908
HomeArguments(funcInfo);
29052909

2910+
if (funcInfo->root->IsAsync())
2911+
{
2912+
Writer()->Reg1(Js::OpCode::NewAwaitObject, funcInfo->awaitRegister);
2913+
}
2914+
29062915
if (!funcInfo->IsBodyAndParamScopeMerged())
29072916
{
29082917
byteCodeFunction->SetParamAndBodyScopeNotMerged();
@@ -10547,17 +10556,18 @@ void EmitAwait(
1054710556
ByteCodeGenerator* byteCodeGenerator,
1054810557
FuncInfo* funcInfo)
1054910558
{
10550-
// OPTIMIZE: We should only have to allocate this object once before any awaits.
10551-
// Awaiting can merely set the value property of that object.
10552-
10553-
auto* writer = byteCodeGenerator->Writer();
10554-
writer->Reg2(Js::OpCode::NewAwaitObject, funcInfo->yieldRegister, inputReg);
1055510559

10560+
auto writer = byteCodeGenerator->Writer();
10561+
writer->PatchableProperty(
10562+
Js::OpCode::StFld,
10563+
inputReg,
10564+
funcInfo->awaitRegister,
10565+
funcInfo->FindOrAddInlineCacheId(funcInfo->awaitRegister, Js::PropertyIds::value, false, true));
1055610566
Js::ByteCodeLabel resumeNormal = writer->DefineLabel();
1055710567

1055810568
EmitYieldAndResume(
1055910569
resultReg,
10560-
funcInfo->yieldRegister,
10570+
funcInfo->awaitRegister,
1056110571
resumeNormal,
1056210572
Js::Constants::NoByteCodeLabel,
1056310573
byteCodeGenerator,

lib/Runtime/ByteCode/FuncInfo.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ FuncInfo::FuncInfo(
4949
frameDisplayRegister(Js::Constants::NoRegister),
5050
funcObjRegister(Js::Constants::NoRegister),
5151
localClosureReg(Js::Constants::NoRegister),
52+
awaitRegister(Js::Constants::NoRegister),
5253
yieldRegister(Js::Constants::NoRegister),
5354
firstTmpReg(Js::Constants::NoRegister),
5455
curTmpReg(Js::Constants::NoRegister),

lib/Runtime/ByteCode/FuncInfo.h

+8
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ class FuncInfo
114114
Js::RegSlot frameDisplayRegister; // location, if any, of the display of nested frames
115115
Js::RegSlot funcObjRegister;
116116
Js::RegSlot localClosureReg;
117+
Js::RegSlot awaitRegister;
117118
Js::RegSlot yieldRegister;
118119
Js::RegSlot firstTmpReg;
119120
Js::RegSlot curTmpReg;
@@ -568,6 +569,13 @@ class FuncInfo
568569
return this->falseConstantRegister;
569570
}
570571

572+
Js::RegSlot AssignAwaitRegister()
573+
{
574+
AssertMsg(this->awaitRegister == Js::Constants::NoRegister, "await register should only be assigned once by FinalizeRegisters()");
575+
this->awaitRegister = NextVarRegister();
576+
return this->awaitRegister;
577+
}
578+
571579
Js::RegSlot AssignYieldRegister()
572580
{
573581
AssertMsg(this->yieldRegister == Js::Constants::NoRegister, "yield register should only be assigned once by FinalizeRegisters()");

lib/Runtime/ByteCode/OpCodes.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,7 @@ MACRO_EXTEND_WMS( NewScGenFuncHomeObj, ElementSlot, OpSideEffec
625625
MACRO_EXTEND_WMS( NewInnerScFuncHomeObj, ElementSlotI3, OpSideEffect) // Create new ScriptFunction instance that has home object
626626
MACRO_EXTEND_WMS( NewInnerScGenFuncHomeObj, ElementSlotI3, OpSideEffect) // Create new JavascriptGeneratorFunction instance that has home object
627627
MACRO_EXTEND_WMS( NewAsyncFromSyncIterator, Reg2, OpSideEffect) // Create new JavascriptAsyncFromSyncOperator instance
628-
MACRO_EXTEND_WMS( NewAwaitObject, Reg2, OpSideEffect) // Create new internal await object instance
628+
MACRO_EXTEND_WMS( NewAwaitObject, Reg1, OpSideEffect) // Create new internal await object instance
629629
MACRO_BACKEND_ONLY( NewScopeObject, Reg1, None) // Create new NewScopeObject
630630
MACRO_BACKEND_ONLY( InitCachedScope, Reg2Aux, None) // Retrieve cached scope; create if not cached
631631
MACRO_BACKEND_ONLY( InitLetCachedScope, Reg2Aux, OpSideEffect) // Retrieve cached scope; create if not cached (formals are let-like instead of var-like)

lib/Runtime/Debug/TTSnapObjects.cpp

+20-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace TTD
1818
TTDAssert(!obj->IsExternal(), "We are not prepared for custom external objects yet");
1919

2020
sobj->ObjectPtrId = TTD_CONVERT_VAR_TO_PTR_ID(obj);
21-
sobj->SnapObjectTag = obj->GetSnapTag_TTD();
21+
sobj->SnapObjectTag = obj->GetTypeId() == Js::TypeId::TypeIds_AwaitObject ? SnapObjectType::SnapAwaitObject : obj->GetSnapTag_TTD();
2222

2323
TTD_WELLKNOWN_TOKEN lookupToken = isWellKnown ? obj->GetScriptContext()->TTDWellKnownInfo->ResolvePathForKnownObject(obj) : TTD_INVALID_WELLKNOWN_TOKEN;
2424
sobj->OptWellKnownToken = alloc.CopyRawNullTerminatedStringInto(lookupToken);
@@ -39,7 +39,10 @@ namespace TTD
3939
NSSnapObjects::StdPropertyExtract_DynamicType(sobj, static_cast<Js::DynamicObject*>(obj), alloc);
4040
}
4141

42-
obj->ExtractSnapObjectDataInto(sobj, alloc);
42+
if (sobj->SnapObjectTag != SnapObjectType::SnapAwaitObject)
43+
{
44+
obj->ExtractSnapObjectDataInto(sobj, alloc);
45+
}
4346
}
4447

4548
void StdPropertyExtract_StaticType(SnapObject* snpObject, Js::RecyclableObject* obj)
@@ -681,6 +684,21 @@ namespace TTD
681684
}
682685
#endif
683686

687+
Js::RecyclableObject* DoObjectInflation_SnapAwaitObject(const SnapObject* snpObject, InflateMap* inflator)
688+
{
689+
Js::DynamicObject* rcObj = ReuseObjectCheckAndReset(snpObject, inflator);
690+
if(rcObj != nullptr)
691+
{
692+
return rcObj;
693+
}
694+
else
695+
{
696+
Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
697+
698+
return Js::DynamicObject::New(ctx->GetRecycler(), ctx->GetLibrary()->GetAwaitObjectType());
699+
}
700+
}
701+
684702
Js::RecyclableObject* DoObjectInflation_SnapDynamicObject(const SnapObject* snpObject, InflateMap* inflator)
685703
{
686704
Js::DynamicObject* rcObj = ReuseObjectCheckAndReset(snpObject, inflator);

lib/Runtime/Debug/TTSnapObjects.h

+2
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ namespace TTD
155155
//ParseAddtlInfo is a nop
156156
//AssertSnapEquiv is a nop
157157

158+
Js::RecyclableObject* DoObjectInflation_SnapAwaitObject(const SnapObject* snpObject, InflateMap* inflator);
159+
158160
//////////////////
159161

160162
//A struct that represents a script function object

0 commit comments

Comments
 (0)