• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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