• 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/mem/heap-inl.h"
18 #include "ecmascript/js_tagged_value-inl.h"
19 #include "ecmascript/interpreter/interpreter-inl.h"
20 
21 namespace panda::ecmascript::builtins {
GetFreeHeapSize(EcmaRuntimeCallInfo * info)22 JSTaggedValue BuiltinsGc::GetFreeHeapSize(EcmaRuntimeCallInfo *info)
23 {
24     auto *heap = info->GetThread()->GetEcmaVM()->GetHeap();
25     auto size = heap->GetHeapLimitSize() - heap->GetHeapObjectSize();
26     return JSTaggedValue(static_cast<int64_t>(size));
27 }
28 
GetReservedHeapSize(EcmaRuntimeCallInfo * info)29 JSTaggedValue BuiltinsGc::GetReservedHeapSize(EcmaRuntimeCallInfo *info)
30 {
31     auto *heap = info->GetThread()->GetEcmaVM()->GetHeap();
32     return JSTaggedValue(static_cast<int64_t>(heap->GetHeapLimitSize()));
33 }
34 
GetUsedHeapSize(EcmaRuntimeCallInfo * info)35 JSTaggedValue BuiltinsGc::GetUsedHeapSize(EcmaRuntimeCallInfo *info)
36 {
37     auto *heap = info->GetThread()->GetEcmaVM()->GetHeap();
38     return JSTaggedValue(static_cast<int64_t>(heap->GetHeapObjectSize()));
39 }
40 
GetObjectAddress(EcmaRuntimeCallInfo * info)41 JSTaggedValue BuiltinsGc::GetObjectAddress(EcmaRuntimeCallInfo *info)
42 {
43     JSHandle<JSTaggedValue> h = GetCallArg(info, 0);
44     return JSTaggedValue(reinterpret_cast<int64_t>(h->GetHeapObject()));
45 }
46 
GetObjectSpaceType(EcmaRuntimeCallInfo * info)47 JSTaggedValue BuiltinsGc::GetObjectSpaceType(EcmaRuntimeCallInfo *info)
48 {
49     JSHandle<JSTaggedValue> h = GetCallArg(info, 0);
50     auto *region = Region::ObjectAddressToRange(h->GetHeapObject());
51     return JSTaggedValue(region->GetSpaceType());
52 }
53 
RegisterNativeAllocation(EcmaRuntimeCallInfo * info)54 JSTaggedValue BuiltinsGc::RegisterNativeAllocation(EcmaRuntimeCallInfo *info)
55 {
56     JSHandle<JSTaggedValue> h = GetCallArg(info, 0);
57     auto *heap = const_cast<Heap *>(info->GetThread()->GetEcmaVM()->GetHeap());
58     auto size = h->GetInt();
59     if (size < 0) {
60         auto *thread = info->GetThread();
61         THROW_RANGE_ERROR_AND_RETURN(thread, "The value must be non negative", JSTaggedValue::Exception());
62     }
63     heap->IncreaseNativeBindingSize(size);
64     heap->TryTriggerFullMarkOrGCByNativeSize();
65     WaitAndHandleConcurrentMarkingFinished(heap);
66     return JSTaggedValue::Undefined();
67 }
68 
RegisterNativeFree(EcmaRuntimeCallInfo * info)69 JSTaggedValue BuiltinsGc::RegisterNativeFree(EcmaRuntimeCallInfo *info)
70 {
71     JSHandle<JSTaggedValue> h = GetCallArg(info, 0);
72     auto *heap = const_cast<Heap *>(info->GetThread()->GetEcmaVM()->GetHeap());
73     auto size = h->GetInt();
74     if (size < 0) {
75         auto *thread = info->GetThread();
76         THROW_RANGE_ERROR_AND_RETURN(thread, "The value must be non negative", JSTaggedValue::Exception());
77     }
78     auto allocated = heap->GetNativeBindingSize();
79     heap->DecreaseNativeBindingSize(std::min(allocated, static_cast<size_t>(size)));
80     return JSTaggedValue::Undefined();
81 }
82 
WaitForFinishGC(EcmaRuntimeCallInfo * info)83 JSTaggedValue BuiltinsGc::WaitForFinishGC(EcmaRuntimeCallInfo *info)
84 {
85     auto *heap = const_cast<Heap *>(info->GetThread()->GetEcmaVM()->GetHeap());
86     auto gcId = GetCallArg(info, 0)->GetInt();
87     if (gcId <= 0) {
88         return JSTaggedValue::Undefined();
89     }
90 
91     WaitAndHandleConcurrentMarkingFinished(heap);
92 
93 #ifndef NDEBUG
94     heap->EnableTriggerCollectionOnNewObject();
95 #endif
96     return JSTaggedValue::Undefined();
97 }
98 
StartGC(EcmaRuntimeCallInfo * info)99 JSTaggedValue BuiltinsGc::StartGC(EcmaRuntimeCallInfo *info)
100 {
101     static int counter = 0;
102     auto *thread = info->GetThread();
103     auto *heap = const_cast<Heap *>(info->GetThread()->GetEcmaVM()->GetHeap());
104     auto cause = StringToGcType(thread, GetCallArg(info, 0).GetTaggedValue());
105     auto runGcInPlace = GetCallArg(info, 2).GetTaggedValue().ToBoolean();
106 
107     if (cause == GC_TYPE_LAST) {
108         THROW_TYPE_ERROR_AND_RETURN(thread, "Invalid GC trigger type", JSTaggedValue::Exception());
109     }
110     switch (cause) {
111         case SHARED_GC:
112         case SHARED_PARTIAL_GC:
113             SharedHeap::GetInstance()->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::EXTERNAL_TRIGGER>(thread);
114             return JSTaggedValue(0);
115         case SHARED_FULL_GC:
116             SharedHeap::GetInstance()->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::EXTERNAL_TRIGGER>(
117                 thread);
118             return JSTaggedValue(0);
119         case APPSPAWN_SHARED_FULL_GC:
120             SharedHeap::GetInstance()
121                 ->CollectGarbage<TriggerGCType::APPSPAWN_SHARED_FULL_GC, GCReason::EXTERNAL_TRIGGER>(thread);
122             return JSTaggedValue(0);
123         default:
124             break;
125     }
126     if (cause != OLD_GC) {
127         // except OLD_GC all run in place implicitly
128         heap->CollectGarbage(cause, GCReason::EXTERNAL_TRIGGER);
129         return JSTaggedValue(0);
130     }
131 
132     heap->SetMarkType(MarkType::MARK_FULL);
133     heap->TriggerConcurrentMarking();
134 
135     if (heap->GetConcurrentMarker()->IsTriggeredConcurrentMark()) {
136         JSHandle<JSTaggedValue> hCallback = GetCallArg(info, 1);
137         if (!hCallback->IsUndefinedOrNull()) {
138             if (!hCallback->IsJSFunction()) {
139                 THROW_TYPE_ERROR_AND_RETURN(thread, "Invalid GC callback", JSTaggedValue::Exception());
140             }
141 
142             auto undefined = thread->GlobalConstants()->GetHandledUndefined();
143             auto *calleeInfo = EcmaInterpreter::NewRuntimeCallInfo(thread, hCallback, info->GetThis(), undefined, 0);
144             JSFunction::Call(calleeInfo);
145         }
146     }
147 
148     if (runGcInPlace) {
149         WaitAndHandleConcurrentMarkingFinished(heap);
150         return JSTaggedValue(0);
151     }
152 
153 #ifndef NDEBUG
154     heap->DisableTriggerCollectionOnNewObject();
155 #endif
156     return JSTaggedValue(++counter);
157 }
158 
WaitAndHandleConcurrentMarkingFinished(Heap * heap)159 void BuiltinsGc::WaitAndHandleConcurrentMarkingFinished(Heap *heap)
160 {
161     if (heap->GetConcurrentMarker()->IsTriggeredConcurrentMark()) {
162         heap->WaitConcurrentMarkingFinished();
163         heap->GetConcurrentMarker()->HandleMarkingFinished(GCReason::EXTERNAL_TRIGGER);
164     }
165 }
166 
AllocateArrayObject(EcmaRuntimeCallInfo * info)167 JSTaggedValue BuiltinsGc::AllocateArrayObject(EcmaRuntimeCallInfo *info)
168 {
169     auto *thread = info->GetThread();
170     auto *factory = thread->GetEcmaVM()->GetFactory();
171     ASSERT(thread != nullptr);
172     [[maybe_unused]] EcmaHandleScope handleScope(thread);
173 
174     int64_t sizeInBytes = 0;
175 
176     if (GetCallArg(info, 0)->IsInt()) {
177         sizeInBytes = GetCallArg(info, 0)->GetInt();
178     } else if (GetCallArg(info, 0)->IsDouble()) {
179         sizeInBytes = GetCallArg(info, 0)->GetDouble();
180     } else {
181         auto err = thread->GetEcmaVM()->GetFactory()->GetJSError(ErrorType::TYPE_ERROR, "The value must be an integer");
182         THROW_NEW_ERROR_AND_RETURN_VALUE(thread, err.GetTaggedValue(), JSTaggedValue::Exception());
183     }
184 
185     if (sizeInBytes < 0) {
186         auto err = thread->GetEcmaVM()->GetFactory()->GetJSError(ErrorType::TYPE_ERROR, "The value must be positive");
187         THROW_NEW_ERROR_AND_RETURN_VALUE(thread, err.GetTaggedValue(), JSTaggedValue::Exception());
188     }
189 
190     sizeInBytes = RoundUp(sizeInBytes, sizeof(TaggedType)) - JSArray::SIZE;
191     if (sizeInBytes < 0) {
192         sizeInBytes = 0;
193     }
194 
195     uint32_t numElements = static_cast<uint32_t>(sizeInBytes / sizeof(TaggedType));
196     auto array = factory->NewJSArray();
197 
198     if (numElements > 0) {
199         auto elements = factory->NewTaggedArray(numElements);
200         if (elements.IsEmpty()) {
201             return JSTaggedValue::Exception();
202         }
203 
204         array->SetElements(thread, elements);
205         array->SetArrayLength(thread, numElements);
206     }
207 
208     return array.GetTaggedValue();
209 }
210 
StringToGcType(JSThread * thread,JSTaggedValue cause)211 TriggerGCType BuiltinsGc::StringToGcType(JSThread *thread, JSTaggedValue cause)
212 {
213     static_assert(GC_TYPE_LAST == 8, "Update this method after TrigerGCType change");
214     if (JSTaggedValue::StrictEqual(thread->GlobalConstants()->GetYoungGcCause(), cause)) {
215         return YOUNG_GC;
216     }
217     if (JSTaggedValue::StrictEqual(thread->GlobalConstants()->GetOldGcCause(), cause)) {
218         return OLD_GC;
219     }
220     if (JSTaggedValue::StrictEqual(thread->GlobalConstants()->GetFullGcCause(), cause)) {
221         return FULL_GC;
222     }
223     if (JSTaggedValue::StrictEqual(thread->GlobalConstants()->GetAppSpawnFullGcCause(), cause)) {
224         return APPSPAWN_FULL_GC;
225     }
226     if (JSTaggedValue::StrictEqual(thread->GlobalConstants()->GetSharedGcCause(), cause)) {
227         return SHARED_GC;
228     }
229     if (JSTaggedValue::StrictEqual(thread->GlobalConstants()->GetSharedPartialGcCause(), cause)) {
230         return SHARED_PARTIAL_GC;
231     }
232     if (JSTaggedValue::StrictEqual(thread->GlobalConstants()->GetSharedFullGcCause(), cause)) {
233         return SHARED_FULL_GC;
234     }
235     if (JSTaggedValue::StrictEqual(thread->GlobalConstants()->GetAppSpawnSharedFullGcCause(), cause)) {
236         return APPSPAWN_SHARED_FULL_GC;
237     }
238     return GC_TYPE_LAST;
239 }
240 }  // namespace panda::ecmascript::builtins
241