-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathThreadContext.h
2023 lines (1665 loc) · 76.3 KB
/
ThreadContext.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
namespace Js
{
class ScriptContext;
struct InlineCache;
class CodeGenRecyclableData;
#ifdef ENABLE_SCRIPT_DEBUGGING
class DebugManager;
struct ReturnedValue;
typedef JsUtil::List<ReturnedValue*> ReturnedValueList;
#endif
class DelayedFreeArrayBuffer;
}
typedef BVSparse<ArenaAllocator> ActiveFunctionSet;
using namespace PlatformAgnostic;
struct IAuthorFileContext;
class HostScriptContext;
class ScriptSite;
class ThreadServiceWrapper;
struct IActiveScriptProfilerHeapEnum;
class DynamicProfileMutator;
class StackProber;
enum DisableImplicitFlags : BYTE
{
DisableImplicitNoFlag = 0x00,
DisableImplicitCallFlag = 0x01,
DisableImplicitExceptionFlag = 0x02,
DisableImplicitCallAndExceptionFlag = DisableImplicitCallFlag | DisableImplicitExceptionFlag
};
enum ThreadContextFlags
{
ThreadContextFlagNoFlag = 0x00000000,
ThreadContextFlagCanDisableExecution = 0x00000001,
ThreadContextFlagEvalDisabled = 0x00000002,
ThreadContextFlagNoJIT = 0x00000004,
ThreadContextFlagDisableFatalOnOOM = 0x00000008,
ThreadContextFlagNoDynamicThunks = 0x00000010,
};
const int LS_MAX_STACK_SIZE_KB = 300;
class ThreadContext;
class InterruptPoller
{
// Interface with a polling object located in the hosting layer.
public:
InterruptPoller(ThreadContext *tc);
virtual ~InterruptPoller() { }
void CheckInterruptPoll();
void GetStatementCount(ULONG *pluHi, ULONG *pluLo);
void ResetStatementCount() { lastResetTick = lastPollTick; }
void StartScript() { lastResetTick = lastPollTick = ::GetTickCount(); }
void EndScript() { lastResetTick = lastPollTick = 0;}
bool IsDisabled() const { return isDisabled; }
void SetDisabled(bool disable) { isDisabled = disable; }
virtual void TryInterruptPoll(Js::ScriptContext *scriptContext) = 0;
// Default: throw up QC dialog after 5M statements == 2 minutes
static const DWORD TicksToStatements = (5000000 / 120000);
protected:
ThreadContext *threadContext;
DWORD lastPollTick;
DWORD lastResetTick;
bool isDisabled;
};
#define PROBE_STACK(scriptContext, size) ((scriptContext)->GetThreadContext()->ProbeStack(size, scriptContext))
#define PROBE_STACK_NO_DISPOSE(scriptContext, size) ((scriptContext)->GetThreadContext()->ProbeStackNoDispose(size, scriptContext))
#define PROBE_STACK_PARTIAL_INITIALIZED_INTERPRETER_FRAME(scriptContext, size) ((scriptContext)->GetThreadContext()->ProbeStack(size, scriptContext, _ReturnAddress()))
#define PROBE_STACK_PARTIAL_INITIALIZED_BAILOUT_FRAME(scriptContext, size, returnAddress) ((scriptContext)->GetThreadContext()->ProbeStack(size, scriptContext, returnAddress))
#define PROBE_STACK_CALL(scriptContext, obj, size) ((scriptContext)->GetThreadContext()->ProbeStack(size, obj, scriptContext))
#define AssertInScript() Assert(ThreadContext::GetContextForCurrentThread()->IsScriptActive());
#define AssertNotInScript() Assert(!ThreadContext::GetContextForCurrentThread()->IsScriptActive());
#define LEAVE_SCRIPT_START_EX(scriptContext, stackProbe, leaveForHost, isFPUControlRestoreNeeded) \
{ \
void * __frameAddr = nullptr; \
GET_CURRENT_FRAME_ID(__frameAddr); \
Js::LeaveScriptObject<stackProbe, leaveForHost, isFPUControlRestoreNeeded> __leaveScriptObject(scriptContext, __frameAddr); \
AutoReentrancyHandler autoReentrancyHandler(scriptContext->GetThreadContext());
#define LEAVE_SCRIPT_END_EX(scriptContext) \
if (scriptContext != nullptr) \
{ \
scriptContext->GetThreadContext()->DisposeOnLeaveScript(); \
}\
}
#define LEAVE_SCRIPT_IF_ACTIVE(scriptContext, externalCall) \
if (scriptContext->GetThreadContext()->IsScriptActive()) \
{ \
BEGIN_LEAVE_SCRIPT(scriptContext); \
externalCall \
END_LEAVE_SCRIPT(scriptContext); \
} \
else \
{ \
DECLARE_EXCEPTION_CHECK_DATA \
SAVE_EXCEPTION_CHECK \
externalCall \
RESTORE_EXCEPTION_CHECK \
}
#define ENTER_SCRIPT_IF(scriptContext, doCleanup, isCallRoot, hasCaller, condition, block) \
if (condition) \
{ \
BEGIN_ENTER_SCRIPT(scriptContext, doCleanup, isCallRoot, hasCaller); \
block \
END_ENTER_SCRIPT(scriptContext, doCleanup, isCallRoot, hasCaller); \
} \
else \
{ \
block \
}
#define BEGIN_LEAVE_SCRIPT(scriptContext) \
LEAVE_SCRIPT_START_EX(scriptContext, /* stackProbe */ true, /* leaveForHost */ true, /* isFPUControlRestoreNeeded */ false)
#define BEGIN_LEAVE_SCRIPT_SAVE_FPU_CONTROL(scriptContext) \
LEAVE_SCRIPT_START_EX(scriptContext, /* stackProbe */ true, /* leaveForHost */ true, /* isFPUControlRestoreNeeded */ true)
// BEGIN_LEAVE_SCRIPT_INTERNAL is used when there are no explicit external call after leave script,
// but we might have external call when allocation memory doing QC or GC Dispose, which may enter script again.
// This will record the reentry as an implicit call (ImplicitCall_AsyncHostOperation)
#define BEGIN_LEAVE_SCRIPT_INTERNAL(scriptContext) \
LEAVE_SCRIPT_START_EX(scriptContext, /* stackProbe */ true, /* leaveForHost */ false, /* isFPUControlRestoreNeeded */ false)
#define BEGIN_LEAVE_SCRIPT_NO_STACK_PROBE(scriptContext) \
LEAVE_SCRIPT_START_EX(scriptContext, /* stackProbe */ false, /* leaveForHost */ true, /* isFPUControlRestoreNeeded */ false)
#define END_LEAVE_SCRIPT(scriptContext) \
LEAVE_SCRIPT_END_EX(scriptContext)
#define END_LEAVE_SCRIPT_RESTORE_FPU_CONTROL(scriptContext) \
LEAVE_SCRIPT_END_EX(scriptContext)
#define END_LEAVE_SCRIPT_INTERNAL(scriptContext) \
LEAVE_SCRIPT_END_EX(scriptContext)
#define END_LEAVE_SCRIPT_NO_STACK_PROBE(scriptContext) \
LEAVE_SCRIPT_END_EX(scriptContext)
#define BEGIN_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext) \
BEGIN_LEAVE_SCRIPT(scriptContext)
#define END_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext) \
Assert(!scriptContext->HasRecordedException()); \
END_LEAVE_SCRIPT(scriptContext)
#define BEGIN_SAFE_REENTRANT_CALL(threadContext) \
{ \
AutoReentrancyHandler autoReentrancyHandler(threadContext);
#define END_SAFE_REENTRANT_CALL }
#define BEGIN_SAFE_REENTRANT_REGION(threadContext) \
{ \
AutoReentrancySafeRegion autoReentrancySafeRegion(threadContext);
#define END_SAFE_REENTRANT_REGION }
// Keep in sync with CollectGarbageCallBackFlags in scriptdirect.idl
enum RecyclerCollectCallBackFlags
{
Collect_Begin = 0x01,
Collect_Begin_Concurrent = 0x11,
Collect_Begin_Partial = 0x21,
Collect_Begin_Concurrent_Partial = Collect_Begin_Concurrent | Collect_Begin_Partial,
Collect_End = 0x02,
Collect_Wait = 0x04, // callback can be from another thread
Collect_Begin_Sweep = 0x08
};
typedef void (__cdecl *RecyclerCollectCallBackFunction)(void * context, RecyclerCollectCallBackFlags flags);
#ifdef NTBUILD
struct ThreadContextWatsonTelemetryBlock
{
FILETIME lastScriptStartTime;
FILETIME lastScriptEndTime;
};
#endif
class NativeLibraryEntryRecord
{
public:
struct Entry
{
Js::RecyclableObject* function;
Js::CallInfo callInfo;
PCWSTR name;
PVOID addr;
Entry* next;
};
private:
Entry* head;
public:
NativeLibraryEntryRecord() : head(nullptr)
{
}
const Entry* Peek() const
{
return head;
}
void Push(_In_ Entry* e)
{
e->next = head;
head = e;
}
void Pop()
{
head = head->next;
}
};
class AutoTagNativeLibraryEntry
{
private:
NativeLibraryEntryRecord::Entry entry;
public:
AutoTagNativeLibraryEntry(Js::RecyclableObject* function, Js::CallInfo callInfo, PCWSTR name, void* addr);
~AutoTagNativeLibraryEntry();
};
#define AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, name) \
AutoTagNativeLibraryEntry __tag(function, callInfo, name, _AddressOfReturnAddress())
class ThreadConfiguration
{
public:
ThreadConfiguration(bool enableExperimentalFeatures)
{
CopyGlobalFlags();
if (enableExperimentalFeatures)
{
EnableExperimentalFeatures();
ResetExperimentalFeaturesFromConfig();
}
}
#define DEFINE_FLAG(threadFlag, globalFlag) \
public: \
inline bool threadFlag() const { return m_##globalFlag##; } \
\
private: \
bool m_##globalFlag##;
#define FLAG(threadFlag, globalFlag) DEFINE_FLAG(threadFlag, globalFlag)
#define FLAG_RELEASE(threadFlag, globalFlag) DEFINE_FLAG(threadFlag, globalFlag)
#include "ThreadConfigFlagsList.h"
#undef FLAG_RELEASE
#undef FLAG
#undef DEFINE_FLAG
private:
void CopyGlobalFlags()
{
AutoCriticalSection autocs(&Js::Configuration::Global.flags.csExperimentalFlags);
#define FLAG(threadFlag, globalFlag) m_##globalFlag## = CONFIG_FLAG(globalFlag);
#define FLAG_RELEASE(threadFlag, globalFlag) m_##globalFlag## = CONFIG_FLAG_RELEASE(globalFlag);
#include "ThreadConfigFlagsList.h"
#undef FLAG_RELEASE
#undef FLAG
}
void EnableExperimentalFeatures()
{
// If a ES6 flag is disabled using compile flag don't enable it
#define FLAG_REGOVR_EXP(type, name, ...) m_##name## = COMPILE_DISABLE_##name## ? false : true;
#include "ConfigFlagsList.h"
#undef FLAG_REGOVR_EXP
}
void ResetExperimentalFeaturesFromConfig()
{
// If a flag was overridden using config/command line it should take precedence
#define FLAG_REGOVR_EXP(type, name, ...) if(CONFIG_ISENABLED(Js::Flag::##name##Flag)) { m_##name## = CONFIG_FLAG_RELEASE(##name##); }
#include "ConfigFlagsList.h"
#undef FLAG_REGOVR_EXP
}
};
class AutoReentrancyHandler;
class ThreadContext sealed :
public DefaultRecyclerCollectionWrapper,
public JsUtil::DoublyLinkedListElement<ThreadContext>,
public ThreadContextInfo
{
public:
static void GlobalInitialize();
static const DWORD NoThread = 0xFFFFFFFF;
struct CollectCallBack
{
RecyclerCollectCallBackFunction callback;
void * context;
};
struct WorkerThread
{
// Abstract notion to hold onto threadHandle of worker thread
HANDLE threadHandle;
WorkerThread(HANDLE handle = nullptr) :threadHandle(handle){};
};
struct AutoRestoreImplicitFlags
{
ThreadContext * threadContext;
Js::ImplicitCallFlags savedImplicitCallFlags;
DisableImplicitFlags savedDisableImplicitFlags;
AutoRestoreImplicitFlags(ThreadContext *threadContext, Js::ImplicitCallFlags implicitCallFlags, DisableImplicitFlags disableImplicitFlags) :
threadContext(threadContext),
savedImplicitCallFlags(implicitCallFlags),
savedDisableImplicitFlags(disableImplicitFlags)
{
}
~AutoRestoreImplicitFlags()
{
threadContext->SetImplicitCallFlags((Js::ImplicitCallFlags)(savedImplicitCallFlags));
threadContext->SetDisableImplicitFlags((DisableImplicitFlags)savedDisableImplicitFlags);
}
};
void SetCurrentThreadId(DWORD threadId) { this->currentThreadId = threadId; }
DWORD GetCurrentThreadId() const { return this->currentThreadId; }
void SetIsThreadBound()
{
if (this->recycler)
{
this->recycler->SetIsThreadBound();
}
this->isThreadBound = true;
}
bool IsJSRT() const { return !this->isThreadBound; }
virtual bool IsThreadBound() const override { return this->isThreadBound; }
void SetStackProber(StackProber * stackProber);
static DWORD GetStackLimitForCurrentThreadOffset() { return offsetof(ThreadContext, stackLimitForCurrentThread); }
template <class Fn>
Js::ImplicitCallFlags TryWithDisabledImplicitCall(Fn fn)
{
DisableImplicitFlags prevDisableImplicitFlags = this->GetDisableImplicitFlags();
Js::ImplicitCallFlags savedImplicitCallFlags = this->GetImplicitCallFlags();
this->DisableImplicitCall();
this->SetImplicitCallFlags(Js::ImplicitCallFlags::ImplicitCall_None);
fn();
Js::ImplicitCallFlags curImplicitCallFlags = this->GetImplicitCallFlags();
this->SetDisableImplicitFlags(prevDisableImplicitFlags);
this->SetImplicitCallFlags(savedImplicitCallFlags);
return curImplicitCallFlags;
}
void * GetAddressOfStackLimitForCurrentThread() const
{
FAULTINJECT_SCRIPT_TERMINATION
return &this->stackLimitForCurrentThread;
}
void InitAvailableCommit();
// This is always on for JSRT APIs.
bool IsRentalThreadingEnabledInJSRT() const { return true; }
IActiveScriptProfilerHeapEnum* GetHeapEnum();
void SetHeapEnum(IActiveScriptProfilerHeapEnum* newHeapEnum);
void ClearHeapEnum();
Js::PropertyRecord const * GetPropertyRecord(Js::PropertyId propertyId);
virtual bool IsNumericProperty(Js::PropertyId propertyId) override;
#ifdef ENABLE_WASM_SIMD
#if _M_IX86 || _M_AMD64
// auxiliary SIMD values in memory to help JIT'ed code. E.g. used for Int8x16 shuffle.
_x86_SIMDValue X86_TEMP_SIMD[SIMD_TEMP_SIZE];
_x86_SIMDValue * GetSimdTempArea() { return X86_TEMP_SIMD; }
#endif
#endif
public:
Js::PropertyRecord const * GetEmptyStringPropertyRecord()
{
if (!emptyStringPropertyRecord)
{
emptyStringPropertyRecord = propertyMap->LookupWithKey(Js::HashedCharacterBuffer<char16>(_u(""), 0));
if (emptyStringPropertyRecord == nullptr)
{
emptyStringPropertyRecord = this->UncheckedAddPropertyId(_u(""), 0, true);
}
}
return emptyStringPropertyRecord;
}
Js::PropertyId GetEmptyStringPropertyId()
{
return GetEmptyStringPropertyRecord()->GetPropertyId();
}
private:
const Js::PropertyRecord * emptyStringPropertyRecord;
bool noScriptScope;
#ifdef ENABLE_SCRIPT_DEBUGGING
Js::DebugManager * debugManager;
#endif
static uint const MaxTemporaryArenaAllocators = 5;
static CriticalSection s_csThreadContext;
StackProber * GetStackProber() const { return this->stackProber; }
size_t GetStackLimitForCurrentThread() const;
void SetStackLimitForCurrentThread(size_t limit);
// The current heap enumeration object being used during enumeration.
IActiveScriptProfilerHeapEnum* heapEnum;
struct PropertyGuardEntry
{
public:
typedef JsUtil::BaseHashSet<RecyclerWeakReference<Js::PropertyGuard>*, Recycler, PowerOf2SizePolicy> PropertyGuardHashSet;
// we do not have WeaklyReferencedKeyHashSet - hence use BYTE as a dummy value.
typedef JsUtil::WeaklyReferencedKeyDictionary<Js::EntryPointInfo, BYTE> EntryPointDictionary;
// The sharedGuard is strongly referenced and will be kept alive by ThreadContext::propertyGuards until it's invalidated or
// the property record itself is collected. If the code using the guard needs access to it after it's been invalidated, it
// (the code) is responsible for keeping it alive.
// Each unique guard, is weakly referenced, such that it can be reclaimed if not referenced elsewhere even without being
// invalidated. The entry of a unique guard is removed from the table once the corresponding cache is invalidated.
Field(Js::PropertyGuard*) sharedGuard;
Field(PropertyGuardHashSet) uniqueGuards;
Field(EntryPointDictionary*) entryPoints;
PropertyGuardEntry(Recycler* recycler) : sharedGuard(nullptr), uniqueGuards(recycler), entryPoints(nullptr) {}
};
public:
typedef JsUtil::BaseHashSet<const Js::PropertyRecord *, HeapAllocator, PowerOf2SizePolicy, const Js::PropertyRecord *,
Js::PropertyRecordStringHashComparer, JsUtil::SimpleHashedEntry, JsUtil::AsymetricResizeLock> PropertyMap;
PropertyMap * propertyMap;
typedef JsUtil::BaseHashSet<Js::CaseInvariantPropertyListWithHashCode*, Recycler, PowerOf2SizePolicy, Js::CaseInvariantPropertyListWithHashCode*, JsUtil::NoCaseComparer, JsUtil::SimpleDictionaryEntry>
PropertyNoCaseSetType;
typedef JsUtil::WeaklyReferencedKeyDictionary<Js::Type, bool> TypeHashSet;
typedef JsUtil::BaseDictionary<Js::PropertyId, TypeHashSet *, Recycler, PowerOf2SizePolicy> PropertyIdToTypeHashSetDictionary;
typedef JsUtil::WeaklyReferencedKeyDictionary<const Js::PropertyRecord, PropertyGuardEntry*, Js::PropertyRecordPointerComparer> PropertyGuardDictionary;
private:
PTHREADCONTEXT_HANDLE m_remoteThreadContextInfo;
intptr_t m_prereservedRegionAddr;
intptr_t m_jitThunkStartAddr;
#if ENABLE_NATIVE_CODEGEN
BVSparse<HeapAllocator> * m_jitNumericProperties;
bool m_jitNeedsPropertyUpdate;
public:
intptr_t GetPreReservedRegionAddr()
{
return m_prereservedRegionAddr;
}
intptr_t GetJITThunkStartAddr()
{
return m_jitThunkStartAddr;
}
BVSparse<HeapAllocator> * GetJITNumericProperties() const
{
return m_jitNumericProperties;
}
bool JITNeedsPropUpdate() const
{
return m_jitNeedsPropertyUpdate;
}
void ResetJITNeedsPropUpdate()
{
m_jitNeedsPropertyUpdate = false;
}
static void SetJITConnectionInfo(HANDLE processHandle, void* serverSecurityDescriptor, UUID connectionId);
bool EnsureJITThreadContext(bool allowPrereserveAlloc);
PTHREADCONTEXT_HANDLE GetRemoteThreadContextAddr()
{
Assert(m_remoteThreadContextInfo);
return m_remoteThreadContextInfo;
}
#endif
private:
typedef JsUtil::BaseDictionary<uint, Js::SourceDynamicProfileManager*, Recycler, PowerOf2SizePolicy> SourceDynamicProfileManagerMap;
typedef JsUtil::BaseDictionary<Js::HashedCharacterBuffer<char16>*, const Js::PropertyRecord*, Recycler, PowerOf2SizePolicy, Js::PropertyRecordStringHashComparer> SymbolRegistrationMap;
class SourceDynamicProfileManagerCache
{
public:
SourceDynamicProfileManagerCache() : refCount(0), sourceProfileManagerMap(nullptr) {}
Field(SourceDynamicProfileManagerMap*) sourceProfileManagerMap;
void AddRef() { refCount++; }
uint Release() { Assert(refCount > 0); return --refCount; }
private:
Field(uint) refCount; // For every script context using this cache, there is a ref count added.
};
typedef JsUtil::BaseDictionary<const WCHAR*, SourceDynamicProfileManagerCache*, Recycler, PowerOf2SizePolicy> SourceProfileManagersByUrlMap;
struct RecyclableData
{
RecyclableData(Recycler *const recycler);
Field(Js::TempArenaAllocatorObject *) temporaryArenaAllocators[MaxTemporaryArenaAllocators];
Field(Js::TempGuestArenaAllocatorObject *) temporaryGuestArenaAllocators[MaxTemporaryArenaAllocators];
Field(Js::JavascriptExceptionObject *) pendingFinallyException;
Field(Js::JavascriptExceptionObject *) exceptionObject;
Field(bool) propagateException;
// We throw a JS catchable SO exception if we detect we might overflow the stack. Allocating this (JS)
// object though might really overflow the stack. So use this thread global to identify them from the throw point
// to where they are caught; where the stack has been unwound and it is safer to allocate the real exception
// object and throw.
Field(Js::JavascriptExceptionObject) soErrorObject;
// We can't allocate an out of memory object... So use this static as a way to identify
// them from the throw point to where they are caught.
Field(Js::JavascriptExceptionObject) oomErrorObject;
// This is for JsRT scenario where a runtime is not usable after a suspend request, before a resume runtime call is made
Field(Js::JavascriptExceptionObject) terminatedErrorObject;
Field(Js::JavascriptExceptionObject*) unhandledExceptionObject;
// Used to temporarily keep throwing exception object alive (thrown but not yet caught)
Field(Js::JavascriptExceptionObject*) tempUncaughtException;
// Contains types that have property caches that need to be tracked, as the caches may need to be cleared. Types that
// contain a property cache for a property that is on a prototype object will be tracked in this map since those caches
// need to be cleared if for instance, the property is deleted from the prototype object.
//
// It is expected that over time, types that are deleted will eventually be removed by the weak reference hash sets when
// they're searching through a bucket while registering a type or enumerating types to invalidate, or when a property ID
// is reclaimed. If none of those happen, then this collection may contain weak reference handles to deleted objects
// that would not get removed, but it would also not get any bigger.
Field(PropertyIdToTypeHashSetDictionary) typesWithProtoPropertyCache;
#if ENABLE_NATIVE_CODEGEN
// The property guard dictionary contains property guards which need to be invalidated in response to properties changing
// from writable to read-only and vice versa, properties being shadowed or unshadowed on prototypes, etc. The dictionary
// holds only weak references to property guards and their lifetimes are controlled by their creators (typically entry points).
// When a guard is no longer needed it is garbage collected, but the weak references and dictionary entries remain, until
// the guards for a given property get invalidated.
// TODO: Create and use a self-cleaning weak reference dictionary, which would periodically remove any unused weak references.
Field(PropertyGuardDictionary) propertyGuards;
#endif
Field(PropertyNoCaseSetType *) caseInvariantPropertySet;
Field(JsUtil::List<Js::PropertyRecord const*>*) boundPropertyStrings; // Recycler allocated list of property strings that we need to strongly reference so that they're not reclaimed
Field(SourceProfileManagersByUrlMap*) sourceProfileManagersByUrl;
// Used to register recyclable data that needs to be kept alive while jitting
typedef JsUtil::DoublyLinkedList<Js::CodeGenRecyclableData, Recycler> CodeGenRecyclableDataList;
Field(CodeGenRecyclableDataList) codeGenRecyclableDatas;
// Used to root old entry points so that they're not prematurely collected
Field(Js::FunctionEntryPointInfo*) oldEntryPointInfo;
// Used to store a mapping of string to Symbol for cross-realm Symbol registration
// See ES6 (draft 22) 19.4.2.2
Field(SymbolRegistrationMap*) symbolRegistrationMap;
#ifdef ENABLE_SCRIPT_DEBUGGING
// Just holding the reference to the returnedValueList of the stepController. This way that list will not get recycled prematurely.
Field(Js::ReturnedValueList *) returnedValueList;
#endif
Field(uint) constructorCacheInvalidationCount;
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
// use for autoProxy called from Debug.setAutoProxyName. we need to keep the buffer from GetSz() alive.
Field(LPCWSTR) autoProxyName;
#endif
};
static ThreadContext * globalListLast;
ThreadContextFlags threadContextFlags;
DWORD currentThreadId;
mutable size_t stackLimitForCurrentThread;
StackProber * stackProber;
bool isThreadBound;
bool hasThrownPendingException;
bool * hasBailedOutBitPtr;
#if ENABLE_JS_REENTRANCY_CHECK
bool noJsReentrancy;
#endif
private:
bool reentrancySafeOrHandled;
bool isInReentrancySafeRegion;
AllocationPolicyManager * allocationPolicyManager;
JsUtil::ThreadService threadService;
#if ENABLE_NATIVE_CODEGEN
PreReservedVirtualAllocWrapper preReservedVirtualAllocator;
#endif
uint callRootLevel;
#if ENABLE_BACKGROUND_PAGE_FREEING
// The thread page allocator is used by the recycler and need the background page queue
PageAllocator::BackgroundPageQueue backgroundPageQueue;
#endif
IdleDecommitPageAllocator pageAllocator;
Recycler* recycler;
// This instance holds list of delay-free array buffer - this will be used in
// scanning the stack in order to release any delay-free buffer.
Js::DelayedFreeArrayBuffer delayFreeCallback;
// Fake RecyclerWeakReference for built-in properties
class StaticPropertyRecordReference : public RecyclerWeakReference<const Js::PropertyRecord>
{
public:
StaticPropertyRecordReference(const Js::PropertyRecord* propertyRecord)
{
strongRef = (char*)propertyRecord;
strongRefHeapBlock = &CollectedRecyclerWeakRefHeapBlock::Instance;
}
};
static const Js::PropertyRecord * const builtInPropertyRecords[];
PropertyNoCaseSetType * caseInvariantPropertySet;
Js::ScriptContext * rootPendingClose;
Js::ScriptEntryExitRecord * entryExitRecord;
Js::InterpreterStackFrame* leafInterpreterFrame;
const Js::PropertyRecord * propertyNamesDirect[128];
ArenaAllocator threadAlloc;
ThreadServiceWrapper* threadServiceWrapper;
uint functionCount;
uint sourceInfoCount;
void * tryHandlerAddrOfReturnAddr;
enum RedeferralState
{
InitialRedeferralState,
StartupRedeferralState,
MainRedeferralState
};
RedeferralState redeferralState;
uint gcSinceLastRedeferral;
uint gcSinceCallCountsCollected;
static const uint InitialRedeferralDelay = 5;
static const uint StartupRedeferralCheckInterval = 10;
static const uint StartupRedeferralInactiveThreshold = 5;
static const uint MainRedeferralCheckInterval = 20;
static const uint MainRedeferralInactiveThreshold = 10;
Js::TypeId nextTypeId;
uint32 polymorphicCacheState;
#if ENABLE_NATIVE_CODEGEN
JsUtil::JobProcessor *jobProcessor;
Js::Var * bailOutRegisterSaveSpace;
#if !FLOATVAR
CodeGenNumberThreadAllocator * codeGenNumberThreadAllocator;
XProcNumberPageSegmentManager * xProcNumberPageSegmentManager;
#endif
#if DYNAMIC_INTERPRETER_THUNK || defined(ASMJS_PLAT)
CustomHeap::InProcCodePageAllocators thunkPageAllocators;
#endif
CustomHeap::InProcCodePageAllocators codePageAllocators;
#if defined(_CONTROL_FLOW_GUARD) && !defined(_M_ARM)
InProcJITThunkEmitter jitThunkEmitter;
#endif
#endif
RecyclerRootPtr<RecyclableData> recyclableData;
uint temporaryArenaAllocatorCount;
uint temporaryGuestArenaAllocatorCount;
#if DBG_DUMP || defined(PROFILE_EXEC)
ScriptSite* topLevelScriptSite;
#endif
Js::ScriptContext *scriptContextList;
bool scriptContextEverRegistered;
static size_t processNativeCodeSize;
size_t nativeCodeSize;
size_t sourceCodeSize;
DateTime::HiResTimer hTimer;
int stackProbeCount;
// Count stack probes and poll for continuation every n probes
static const int StackProbePollThreshold = 1000;
EXCEPTION_POINTERS exceptionInfo;
uint32 exceptionCode;
ArenaAllocator inlineCacheThreadInfoAllocator;
ArenaAllocator isInstInlineCacheThreadInfoAllocator;
ArenaAllocator equivalentTypeCacheInfoAllocator;
DListBase<Js::EntryPointInfo *> equivalentTypeCacheEntryPoints;
typedef SList<Js::InlineCache*> InlineCacheList;
typedef JsUtil::BaseDictionary<Js::PropertyId, InlineCacheList*, ArenaAllocator, PrimeSizePolicy> InlineCacheListMapByPropertyId;
InlineCacheListMapByPropertyId protoInlineCacheByPropId;
InlineCacheListMapByPropertyId storeFieldInlineCacheByPropId;
uint registeredInlineCacheCount;
uint unregisteredInlineCacheCount;
#if DBG
uint totalUnregisteredCacheCount;
uint arrayMutationSeed; // This is mostly to aid in debugging.
#endif
typedef JsUtil::BaseDictionary<Js::Var, Js::IsInstInlineCache*, ArenaAllocator, PrimeSizePolicy> IsInstInlineCacheListMapByFunction;
IsInstInlineCacheListMapByFunction isInstInlineCacheByFunction;
Js::IsConcatSpreadableCache isConcatSpreadableCache;
Js::NoSpecialPropertyThreadRegistry noSpecialPropertyRegistry;
Js::OnlyWritablePropertyThreadRegistry onlyWritablePropertyRegistry;
DListBase<CollectCallBack> collectCallBackList;
CriticalSection csCollectionCallBack;
bool hasCollectionCallBack;
bool isOptimizedForManyInstances;
bool bgJit;
// We report library code to profiler only if called directly by user code. Not if called by library implementation.
bool isProfilingUserCode;
void* jsrtRuntime;
bool hasUnhandledException;
bool hasCatchHandler;
DisableImplicitFlags disableImplicitFlags;
// Used for identifying that any particular time, the caller chain has try/catch blocks belong to the user code.
// If all try/catch blocks in the current stack marked as non-user code then this member will remain false.
bool hasCatchHandlerToUserCode;
#ifdef ENABLE_GLOBALIZATION
Js::DelayLoadWinRtString delayLoadWinRtString;
#if defined(ENABLE_INTL_OBJECT) || defined(ENABLE_ES6_CHAR_CLASSIFIER)
#ifdef INTL_WINGLOB
Js::DelayLoadWindowsGlobalization delayLoadWindowsGlobalizationLibrary;
Js::WindowsGlobalizationAdapter windowsGlobalizationAdapter;
#endif
#endif
#ifdef ENABLE_FOUNDATION_OBJECT
Js::DelayLoadWinRtFoundation delayLoadWinRtFoundationLibrary;
Js::WindowsFoundationAdapter windowsFoundationAdapter;
#endif
#endif
// Number of script context attached with probe manager.
// This counter will be used as addref when the script context is created, this way we maintain the life of diagnostic object.
// Once no script context available , diagnostic will go away.
LONG crefSContextForDiag;
Entropy entropy;
JsUtil::Stack<HostScriptContext*>* hostScriptContextStack;
//
// Regex globals
//
UnifiedRegex::StandardChars<uint8>* standardUTF8Chars;
UnifiedRegex::StandardChars<char16>* standardUnicodeChars;
Js::ImplicitCallFlags implicitCallFlags;
THREAD_LOCAL static uint activeScriptSiteCount;
bool isScriptActive;
// When ETW rundown in background thread which needs to walk scriptContext/functionBody/entryPoint lists,
// or when JIT thread is getting auxPtrs from function body, we should not be modifying the list of
// functionBody/entrypoints, or expanding the auxPtrs
CriticalSection csFunctionBody;
#ifdef _M_X64
friend class Js::Amd64StackFrame;
Js::Amd64ContextsManager amd64ContextsManager;
Js::Amd64ContextsManager* GetAmd64ContextsManager() { return &amd64ContextsManager; }
#endif
typedef JsUtil::BaseDictionary<Js::DynamicType const *, void *, HeapAllocator, PowerOf2SizePolicy> DynamicObjectEnumeratorCacheMap;
DynamicObjectEnumeratorCacheMap dynamicObjectEnumeratorCacheMap;
#ifdef NTBUILD
ThreadContextWatsonTelemetryBlock localTelemetryBlock;
ThreadContextWatsonTelemetryBlock * telemetryBlock;
#endif
NativeLibraryEntryRecord nativeLibraryEntry;
UCrtC99MathApis ucrtC99MathApis;
// Indicates the current loop depth as observed by the interpreter. The interpreter causes this value to be updated upon
// entering and leaving a loop.
uint8 loopDepth;
const ThreadConfiguration configuration;
public:
static ThreadContext * globalListFirst;
static uint GetScriptSiteHolderCount() { return activeScriptSiteCount; }
static uint IncrementActiveScriptSiteCount() { return ++activeScriptSiteCount; }
static uint DecrementActiveScriptSiteCount() { return --activeScriptSiteCount; }
static ThreadContext * GetThreadContextList() { return globalListFirst; }
void ValidateThreadContext();
bool IsInScript() const { return callRootLevel != 0; }
uint GetCallRootLevel() const { return callRootLevel; }
PageAllocator * GetPageAllocator() { return &pageAllocator; }
AllocationPolicyManager * GetAllocationPolicyManager() { return allocationPolicyManager; }
// used for diagnosing abnormally high number of closed, but still formally reachable script contexts
// at the time of failfast due to allocation limits.
// high number may indicate that context leaks have occured.
uint closedScriptContextCount;
enum VisibilityState : BYTE
{
Undefined = 0,
Visible = 1,
NotVisible = 2
};
// indicates the visibility state of the hosting application/window/tab if known.
VisibilityState visibilityState;
#if ENABLE_NATIVE_CODEGEN
PreReservedVirtualAllocWrapper * GetPreReservedVirtualAllocator() { return &preReservedVirtualAllocator; }
#if DYNAMIC_INTERPRETER_THUNK || defined(ASMJS_PLAT)
CustomHeap::InProcCodePageAllocators * GetThunkPageAllocators() { return &thunkPageAllocators; }
#endif
CustomHeap::InProcCodePageAllocators * GetCodePageAllocators() { return &codePageAllocators; }
#if defined(_CONTROL_FLOW_GUARD) && !defined(_M_ARM)
InProcJITThunkEmitter * GetJITThunkEmitter() { return &jitThunkEmitter; }
#endif
#endif // ENABLE_NATIVE_CODEGEN
CriticalSection* GetFunctionBodyLock() { return &csFunctionBody; }
UCrtC99MathApis* GetUCrtC99MathApis() { return &ucrtC99MathApis; }
Js::IsConcatSpreadableCache* GetIsConcatSpreadableCache() { return &isConcatSpreadableCache; }
#ifdef ENABLE_GLOBALIZATION
Js::DelayLoadWinRtString *GetWinRTStringLibrary();
#if defined(ENABLE_INTL_OBJECT) || defined(ENABLE_ES6_CHAR_CLASSIFIER)
#ifdef INTL_WINGLOB
Js::DelayLoadWindowsGlobalization *GetWindowsGlobalizationLibrary();
Js::WindowsGlobalizationAdapter *GetWindowsGlobalizationAdapter();
#endif
#endif
#ifdef ENABLE_FOUNDATION_OBJECT
Js::DelayLoadWinRtFoundation *GetWinRtFoundationLibrary();
Js::WindowsFoundationAdapter *GetWindowsFoundationAdapter();
#endif
#endif
void SetAbnormalExceptionRecord(EXCEPTION_POINTERS *exceptionInfo) { this->exceptionInfo = *exceptionInfo; }
void SetAbnormalExceptionCode(uint32 exceptionInfo) { this->exceptionCode = exceptionInfo; }
uint32 GetAbnormalExceptionCode() const { return this->exceptionCode; }
#ifdef ENABLE_BASIC_TELEMETRY
GUID activityId;
LPFILETIME GetLastScriptExecutionEndTime() const;
#endif
void *tridentLoadAddress;
void* GetTridentLoadAddress() const { return tridentLoadAddress; }
void SetTridentLoadAddress(void *loadAddress) { tridentLoadAddress = loadAddress; }
Js::NoSpecialPropertyThreadRegistry* GetNoSpecialPropertyRegistry() { return &this->noSpecialPropertyRegistry; }
Js::OnlyWritablePropertyThreadRegistry* GetOnlyWritablePropertyRegistry() { return &this->onlyWritablePropertyRegistry; }
Js::DelayedFreeArrayBuffer * GetScanStackCallback()
{
return &this->delayFreeCallback;
}
#ifdef ENABLE_DIRECTCALL_TELEMETRY
DirectCallTelemetry directCallTelemetry;
#endif
BOOL HasPreviousHostScriptContext();
HostScriptContext* GetPreviousHostScriptContext() ;
void PushHostScriptContext(HostScriptContext* topProvider);
HostScriptContext* PopHostScriptContext();
void SetInterruptPoller(InterruptPoller *poller) { interruptPoller = poller; }
InterruptPoller *GetInterruptPoller() const { return interruptPoller; }
BOOL HasInterruptPoller() const { return interruptPoller != nullptr; }
void CheckScriptInterrupt();
void CheckInterruptPoll();
bool DoInterruptProbe(Js::FunctionBody *const func) const
{
return
(this->TestThreadContextFlag(ThreadContextFlagCanDisableExecution) &&
!PHASE_OFF(Js::InterruptProbePhase, func)) ||
PHASE_ON(Js::InterruptProbePhase, func);
}
bool DoInterruptProbe() const
{
return
(this->TestThreadContextFlag(ThreadContextFlagCanDisableExecution) &&
!PHASE_OFF1(Js::InterruptProbePhase)) ||
PHASE_ON1(Js::InterruptProbePhase);
}
bool EvalDisabled() const
{
return this->TestThreadContextFlag(ThreadContextFlagEvalDisabled);
}
bool NoJIT() const
{
return this->TestThreadContextFlag(ThreadContextFlagNoJIT);
}
bool NoDynamicThunks() const
{
return this->TestThreadContextFlag(ThreadContextFlagNoDynamicThunks);
}
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
Js::Var GetMemoryStat(Js::ScriptContext* scriptContext);
void SetAutoProxyName(LPCWSTR objectName);
LPCWSTR GetAutoProxyName() const { return recyclableData->autoProxyName; }
Js::PropertyId handlerPropertyId = Js::Constants::NoProperty;
#endif
#ifdef ENABLE_SCRIPT_DEBUGGING
void SetReturnedValueList(Js::ReturnedValueList *returnedValueList)
{
Assert(this->recyclableData != nullptr);
this->recyclableData->returnedValueList = returnedValueList;
}
#if DBG
void EnsureNoReturnedValueList()
{
Assert(this->recyclableData == nullptr || this->recyclableData->returnedValueList == nullptr);
}
#endif
#endif
#if DBG || defined(RUNTIME_DATA_COLLECTION)
uint GetScriptContextCount() const { return this->scriptContextCount; }
#endif
Js::ScriptContext* GetScriptContextList() const { return this->scriptContextList; }
bool WasAnyScriptContextEverRegistered() const { return this->scriptContextEverRegistered; }
#if DBG_DUMP || defined(PROFILE_EXEC)
void SetTopLevelScriptSite(ScriptSite* topScriptSite) { this->topLevelScriptSite = topScriptSite; }
ScriptSite* GetTopLevelScriptSite () { return this->topLevelScriptSite; }
#endif
#if DBG || defined(PROFILE_EXEC)
virtual bool AsyncHostOperationStart(void *) override;