1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/builtins/builtins_gc.h"
17 #include "ecmascript/builtins/builtins_ark_tools.h"
18 #include "ecmascript/mem/heap-inl.h"
19 #include "ecmascript/js_tagged_value-inl.h"
20 #include "ecmascript/interpreter/interpreter-inl.h"
21 #include "common_components/heap/heap.h"
22
23 namespace panda::ecmascript::builtins {
GetFreeHeapSize(EcmaRuntimeCallInfo * info)24 JSTaggedValue BuiltinsGc::GetFreeHeapSize(EcmaRuntimeCallInfo *info)
25 {
26 RETURN_IF_DISALLOW_ARKTOOLS(info->GetThread());
27 auto *heap = info->GetThread()->GetEcmaVM()->GetHeap();
28 auto size = heap->GetHeapLimitSize() - heap->GetHeapObjectSize();
29 return JSTaggedValue(static_cast<int64_t>(size));
30 }
31
GetReservedHeapSize(EcmaRuntimeCallInfo * info)32 JSTaggedValue BuiltinsGc::GetReservedHeapSize(EcmaRuntimeCallInfo *info)
33 {
34 RETURN_IF_DISALLOW_ARKTOOLS(info->GetThread());
35 auto *heap = info->GetThread()->GetEcmaVM()->GetHeap();
36 return JSTaggedValue(static_cast<int64_t>(heap->GetHeapLimitSize()));
37 }
38
GetUsedHeapSize(EcmaRuntimeCallInfo * info)39 JSTaggedValue BuiltinsGc::GetUsedHeapSize(EcmaRuntimeCallInfo *info)
40 {
41 RETURN_IF_DISALLOW_ARKTOOLS(info->GetThread());
42 auto *heap = info->GetThread()->GetEcmaVM()->GetHeap();
43 return JSTaggedValue(static_cast<int64_t>(heap->GetHeapObjectSize()));
44 }
45
GetObjectAddress(EcmaRuntimeCallInfo * info)46 JSTaggedValue BuiltinsGc::GetObjectAddress(EcmaRuntimeCallInfo *info)
47 {
48 RETURN_IF_DISALLOW_ARKTOOLS(info->GetThread());
49 JSHandle<JSTaggedValue> h = GetCallArg(info, 0);
50 return JSTaggedValue(reinterpret_cast<int64_t>(h->GetHeapObject()));
51 }
52
GetObjectSpaceType(EcmaRuntimeCallInfo * info)53 JSTaggedValue BuiltinsGc::GetObjectSpaceType(EcmaRuntimeCallInfo *info)
54 {
55 RETURN_IF_DISALLOW_ARKTOOLS(info->GetThread());
56 JSHandle<JSTaggedValue> h = GetCallArg(info, 0);
57 auto *region = Region::ObjectAddressToRange(h->GetHeapObject());
58 return JSTaggedValue(region->GetSpaceType());
59 }
60
RegisterNativeAllocation(EcmaRuntimeCallInfo * info)61 JSTaggedValue BuiltinsGc::RegisterNativeAllocation(EcmaRuntimeCallInfo *info)
62 {
63 JSHandle<JSTaggedValue> h = GetCallArg(info, 0);
64 auto *heap = const_cast<Heap *>(info->GetThread()->GetEcmaVM()->GetHeap());
65 auto size = h->GetInt();
66 if (size < 0) {
67 auto *thread = info->GetThread();
68 THROW_RANGE_ERROR_AND_RETURN(thread, "The value must be non negative", JSTaggedValue::Exception());
69 }
70 heap->IncreaseNativeBindingSize(size);
71 heap->TryTriggerFullMarkOrGCByNativeSize();
72 WaitAndHandleConcurrentMarkingFinished(heap);
73 return JSTaggedValue::Undefined();
74 }
75
RegisterNativeFree(EcmaRuntimeCallInfo * info)76 JSTaggedValue BuiltinsGc::RegisterNativeFree(EcmaRuntimeCallInfo *info)
77 {
78 RETURN_IF_DISALLOW_ARKTOOLS(info->GetThread());
79 JSHandle<JSTaggedValue> h = GetCallArg(info, 0);
80 auto *heap = const_cast<Heap *>(info->GetThread()->GetEcmaVM()->GetHeap());
81 auto size = h->GetInt();
82 if (size < 0) {
83 auto *thread = info->GetThread();
84 THROW_RANGE_ERROR_AND_RETURN(thread, "The value must be non negative", JSTaggedValue::Exception());
85 }
86 auto allocated = heap->GetNativeBindingSize();
87 heap->DecreaseNativeBindingSize(std::min(allocated, static_cast<size_t>(size)));
88 return JSTaggedValue::Undefined();
89 }
90
WaitForFinishGC(EcmaRuntimeCallInfo * info)91 JSTaggedValue BuiltinsGc::WaitForFinishGC(EcmaRuntimeCallInfo *info)
92 {
93 RETURN_IF_DISALLOW_ARKTOOLS(info->GetThread());
94 auto *heap = const_cast<Heap *>(info->GetThread()->GetEcmaVM()->GetHeap());
95 auto gcId = GetCallArg(info, 0)->GetInt();
96 if (gcId <= 0) {
97 return JSTaggedValue::Undefined();
98 }
99
100 WaitAndHandleConcurrentMarkingFinished(heap);
101
102 #ifndef NDEBUG
103 heap->EnableTriggerCollectionOnNewObject();
104 #endif
105 return JSTaggedValue::Undefined();
106 }
107
StartGC(EcmaRuntimeCallInfo * info)108 JSTaggedValue BuiltinsGc::StartGC(EcmaRuntimeCallInfo *info)
109 {
110 RETURN_IF_DISALLOW_ARKTOOLS(info->GetThread());
111 static int counter = 0;
112 auto *thread = info->GetThread();
113 auto *heap = const_cast<Heap *>(info->GetThread()->GetEcmaVM()->GetHeap());
114 auto cause = StringToGcType(thread, GetCallArg(info, 0).GetTaggedValue());
115 auto runGcInPlace = GetCallArg(info, 2).GetTaggedValue().ToBoolean();
116
117 if (cause == GC_TYPE_LAST) {
118 THROW_TYPE_ERROR_AND_RETURN(thread, "Invalid GC trigger type", JSTaggedValue::Exception());
119 }
120 switch (cause) {
121 case SHARED_GC:
122 case SHARED_PARTIAL_GC:
123 SharedHeap::GetInstance()->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::EXTERNAL_TRIGGER>(thread);
124 return JSTaggedValue(0);
125 case SHARED_FULL_GC:
126 SharedHeap::GetInstance()->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::EXTERNAL_TRIGGER>(
127 thread);
128 return JSTaggedValue(0);
129 case APPSPAWN_SHARED_FULL_GC:
130 SharedHeap::GetInstance()
131 ->CollectGarbage<TriggerGCType::APPSPAWN_SHARED_FULL_GC, GCReason::EXTERNAL_TRIGGER>(thread);
132 return JSTaggedValue(0);
133 default:
134 break;
135 }
136 if (cause != OLD_GC) {
137 // except OLD_GC all run in place implicitly
138 heap->CollectGarbage(cause, GCReason::EXTERNAL_TRIGGER);
139 return JSTaggedValue(0);
140 }
141
142 heap->SetMarkType(MarkType::MARK_FULL);
143 heap->TriggerConcurrentMarking(MarkReason::EXTERNAL_TRIGGER);
144
145 if (heap->GetConcurrentMarker()->IsTriggeredConcurrentMark()) {
146 JSHandle<JSTaggedValue> hCallback = GetCallArg(info, 1);
147 if (!hCallback->IsUndefinedOrNull()) {
148 if (!hCallback->IsJSFunction()) {
149 THROW_TYPE_ERROR_AND_RETURN(thread, "Invalid GC callback", JSTaggedValue::Exception());
150 }
151
152 auto undefined = thread->GlobalConstants()->GetHandledUndefined();
153 auto *calleeInfo = EcmaInterpreter::NewRuntimeCallInfo(thread, hCallback, info->GetThis(), undefined, 0);
154 JSFunction::Call(calleeInfo);
155 }
156 }
157
158 if (runGcInPlace) {
159 WaitAndHandleConcurrentMarkingFinished(heap);
160 return JSTaggedValue(0);
161 }
162
163 #ifndef NDEBUG
164 heap->DisableTriggerCollectionOnNewObject();
165 #endif
166 return JSTaggedValue(++counter);
167 }
168
WaitAndHandleConcurrentMarkingFinished(Heap * heap)169 void BuiltinsGc::WaitAndHandleConcurrentMarkingFinished(Heap *heap)
170 {
171 if (heap->GetConcurrentMarker()->IsTriggeredConcurrentMark()) {
172 heap->WaitConcurrentMarkingFinished();
173 heap->GetConcurrentMarker()->HandleMarkingFinished(GCReason::EXTERNAL_TRIGGER);
174 }
175 }
176
AllocateArrayObject(EcmaRuntimeCallInfo * info)177 JSTaggedValue BuiltinsGc::AllocateArrayObject(EcmaRuntimeCallInfo *info)
178 {
179 RETURN_IF_DISALLOW_ARKTOOLS(info->GetThread());
180 auto *thread = info->GetThread();
181 auto *factory = thread->GetEcmaVM()->GetFactory();
182 ASSERT(thread != nullptr);
183 [[maybe_unused]] EcmaHandleScope handleScope(thread);
184
185 int64_t sizeInBytes = 0;
186
187 if (GetCallArg(info, 0)->IsInt()) {
188 sizeInBytes = GetCallArg(info, 0)->GetInt();
189 } else if (GetCallArg(info, 0)->IsDouble()) {
190 sizeInBytes = GetCallArg(info, 0)->GetDouble();
191 } else {
192 auto err = thread->GetEcmaVM()->GetFactory()->GetJSError(ErrorType::TYPE_ERROR, "The value must be an integer");
193 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, err.GetTaggedValue(), JSTaggedValue::Exception());
194 }
195
196 if (sizeInBytes < 0) {
197 auto err = thread->GetEcmaVM()->GetFactory()->GetJSError(ErrorType::TYPE_ERROR, "The value must be positive");
198 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, err.GetTaggedValue(), JSTaggedValue::Exception());
199 }
200
201 sizeInBytes = RoundUp(sizeInBytes, sizeof(TaggedType)) - JSArray::SIZE;
202 if (sizeInBytes < 0) {
203 sizeInBytes = 0;
204 }
205
206 uint32_t numElements = static_cast<uint32_t>(sizeInBytes / sizeof(TaggedType));
207 auto array = factory->NewJSArray();
208
209 if (numElements > 0) {
210 auto elements = factory->NewTaggedArray(numElements);
211 if (elements.IsEmpty()) {
212 return JSTaggedValue::Exception();
213 }
214
215 array->SetElements(thread, elements);
216 array->SetArrayLength(thread, numElements);
217 }
218
219 return array.GetTaggedValue();
220 }
221
StringToGcType(JSThread * thread,JSTaggedValue cause)222 TriggerGCType BuiltinsGc::StringToGcType(JSThread *thread, JSTaggedValue cause)
223 {
224 static_assert(GC_TYPE_LAST == 9, "Update this method after TrigerGCType change");
225 if (JSTaggedValue::StrictEqual(thread, thread->GlobalConstants()->GetYoungGcCause(), cause)) {
226 return YOUNG_GC;
227 }
228 if (JSTaggedValue::StrictEqual(thread, thread->GlobalConstants()->GetOldGcCause(), cause)) {
229 return OLD_GC;
230 }
231 if (JSTaggedValue::StrictEqual(thread, thread->GlobalConstants()->GetFullGcCause(), cause)) {
232 return FULL_GC;
233 }
234 if (JSTaggedValue::StrictEqual(thread, thread->GlobalConstants()->GetAppSpawnFullGcCause(), cause)) {
235 return APPSPAWN_FULL_GC;
236 }
237 if (JSTaggedValue::StrictEqual(thread, thread->GlobalConstants()->GetSharedGcCause(), cause)) {
238 return SHARED_GC;
239 }
240 if (JSTaggedValue::StrictEqual(thread, thread->GlobalConstants()->GetSharedPartialGcCause(), cause)) {
241 return SHARED_PARTIAL_GC;
242 }
243 if (JSTaggedValue::StrictEqual(thread, thread->GlobalConstants()->GetSharedFullGcCause(), cause)) {
244 return SHARED_FULL_GC;
245 }
246 if (JSTaggedValue::StrictEqual(thread, thread->GlobalConstants()->GetAppSpawnSharedFullGcCause(), cause)) {
247 return APPSPAWN_SHARED_FULL_GC;
248 }
249 if (Runtime::GetInstance()->IsHybridVm() &&
250 JSTaggedValue::StrictEqual(thread, thread->GlobalConstants()->GetUnifiedGcCause(), cause)) {
251 return UNIFIED_GC;
252 }
253 return GC_TYPE_LAST;
254 }
255 } // namespace panda::ecmascript::builtins
256