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