1/* 2 * Copyright (c) 2025 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 16import { finalizerRegister, finalizerUnregister, Thunk } from "@koalaui/common" 17import { InteropNativeModule } from "./InteropNativeModule" 18import { pointer, nullptr } from "./InteropTypes" 19 20class NativeThunk implements Thunk { 21 finalizer: pointer 22 obj: pointer 23 name: string|undefined 24 25 constructor(obj: pointer, finalizer: pointer, name?: string) { 26 this.finalizer = finalizer 27 this.obj = obj 28 this.name = name 29 } 30 31 clean() { 32 if (this.obj != nullptr) { 33 this.destroyNative(this.obj, this.finalizer) 34 } 35 this.obj = nullptr 36 } 37 38 destroyNative(ptr: pointer, finalizer: pointer): void { 39 InteropNativeModule._InvokeFinalizer(ptr, finalizer) 40 } 41} 42 43/** 44 * Class with the custom finalizer, usually used to release a native peer. 45 * Do not use directly, only via subclasses. 46 */ 47export class Finalizable { 48 ptr: pointer 49 finalizer: pointer 50 cleaner: NativeThunk|undefined = undefined 51 managed: boolean 52 constructor(ptr: pointer, finalizer: pointer, managed: boolean = true) { 53 this.ptr = ptr 54 this.finalizer = finalizer 55 this.managed = managed 56 const handle = undefined 57 58 if (this.managed) { 59 if (this.ptr == nullptr) throw new Error("Can't have nullptr ptr ${}") 60 if (this.finalizer == nullptr) throw new Error("Managed finalizer is 0") 61 62 const thunk = new NativeThunk(ptr, finalizer, handle) 63 finalizerRegister(this, thunk) 64 this.cleaner = thunk 65 } 66 } 67 68 close() { 69 if (this.ptr == nullptr) { 70 throw new Error(`Closing a closed object: ` + this.toString()) 71 } else if (this.cleaner == undefined) { 72 throw new Error(`No thunk assigned to ` + this.toString()) 73 } else { 74 finalizerUnregister(this) 75 this.cleaner!.clean() 76 this.cleaner = undefined 77 this.ptr = nullptr 78 } 79 } 80 81 release(): pointer { 82 finalizerUnregister(this) 83 if (this.cleaner) 84 this.cleaner!.obj = nullptr 85 let result = this.ptr 86 this.ptr = nullptr 87 return result 88 } 89 90 resetPeer(pointer: pointer) { 91 if (this.managed) throw new Error("Can only reset peer for an unmanaged object") 92 this.ptr = pointer 93 } 94 95 use<R>(body: (value: Finalizable) => R): R { 96 let result = body(this) 97 this.close() 98 return result 99 } 100} 101