Skip to content

Commit 94dff80

Browse files
authored
feat: memory management model for custom props (#1)
* feat: memory management model for custom props * fix warnings * refactor closures code, fix NSMethodSignature crash, add webgpu example, add Symbols framework to metadata generator script, interop.handleof implementation
1 parent f12902f commit 94dff80

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+7465
-5066
lines changed

examples/appkit.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export class Window extends NSWindow {
5555
NSWindowStyleMask.Miniaturizable |
5656
NSWindowStyleMask.Resizable,
5757
2,
58-
false
58+
false,
5959
);
6060

6161
this.title = "NativeScript for macOS";
@@ -70,7 +70,7 @@ export class Window extends NSWindow {
7070
118 / 255,
7171
171 / 255,
7272
235 / 255,
73-
1
73+
1,
7474
);
7575

7676
const label = NSTextField.alloc().initWithFrame({
@@ -90,7 +90,7 @@ export class Window extends NSWindow {
9090

9191
label.font = NSFontManager.sharedFontManager.convertFontToHaveTrait(
9292
NSFont.fontWithNameSize(label.font.fontName, 45),
93-
NSFontTraitMask.Bold
93+
NSFontTraitMask.Bold,
9494
);
9595

9696
label.sizeToFit();
@@ -107,7 +107,7 @@ export class Window extends NSWindow {
107107
vstack.translatesAutoresizingMaskIntoConstraints = false;
108108

109109
const image = NSImage.alloc().initWithContentsOfFile(
110-
new URL("../assets/NativeScript.png", import.meta.url).pathname
110+
new URL("../assets/NativeScript.png", import.meta.url).pathname,
111111
);
112112

113113
image.size = { width: 128, height: 128 };
@@ -120,10 +120,10 @@ export class Window extends NSWindow {
120120
this.contentView.addSubview(vstack);
121121

122122
vstack.centerXAnchor.constraintEqualToAnchor(
123-
this.contentView.centerXAnchor
123+
this.contentView.centerXAnchor,
124124
).isActive = true;
125125
vstack.centerYAnchor.constraintEqualToAnchor(
126-
this.contentView.centerYAnchor
126+
this.contentView.centerYAnchor,
127127
).isActive = true;
128128

129129
return this;
@@ -160,7 +160,7 @@ interop.addMethod(
160160
NSEventMask.Any,
161161
null,
162162
"kCFRunLoopDefaultMode",
163-
true
163+
true,
164164
);
165165

166166
if (event != null) {
@@ -169,6 +169,7 @@ interop.addMethod(
169169

170170
if (this.running) {
171171
setTimeout(loop, 10);
172+
// queueMicrotask(loop);
172173
}
173174
};
174175

@@ -177,7 +178,7 @@ interop.addMethod(
177178
setTimeout(() => {
178179
console.log("[setTimeout] after 2 seconds");
179180
}, 2000);
180-
}
181+
},
181182
);
182183

183184
NSApp.delegate = delegate;

examples/memory_management.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import "@nativescript/macos-node-api";
2+
3+
let array, weakRef;
4+
5+
const finalizationRegistry = new FinalizationRegistry((value) => {
6+
console.log("finalized", value);
7+
8+
const obj = array[0];
9+
10+
console.log("got object from array", obj);
11+
console.log("custom property after gc", obj.helloWorld);
12+
});
13+
14+
(() => {
15+
const obj = NSObject.new();
16+
17+
obj.helloWorld = "Hello, world!";
18+
19+
console.log("created object", obj);
20+
console.log("custom property before gc", obj.helloWorld);
21+
22+
finalizationRegistry.register(obj, "NativeObject");
23+
24+
// weakRef = new WeakRef(obj);
25+
26+
array = NSMutableArray.arrayWithCapacity(1);
27+
array.addObject(obj);
28+
29+
console.log("added object to array", array);
30+
})();
31+
32+
console.log("out of scope");
33+
34+
gc();
35+
36+
console.log("gc called");
37+
38+
// console.log("weakRef", weakRef.deref());

examples/webgpu.ts

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import "@nativescript/macos-node-api";
2+
3+
const adapter = await navigator.gpu.requestAdapter();
4+
const device = await adapter!.requestDevice();
5+
6+
@NativeClass
7+
export class ApplicationDelegate
8+
extends NSObject
9+
implements NSApplicationDelegate
10+
{
11+
static ObjCProtocols = [NSApplicationDelegate];
12+
13+
running = true;
14+
window: Window | null = null;
15+
16+
applicationDidFinishLaunching(_notification: NSNotification) {
17+
this.window = Window.new();
18+
}
19+
20+
applicationWillTerminate(_notification: NSNotification) {
21+
this.running = false;
22+
}
23+
}
24+
25+
@NativeClass
26+
export class Window extends NSWindow implements NSWindowDelegate {
27+
static ObjCProtocols = [NSWindowDelegate];
28+
29+
surface!: Deno.UnsafeWindowSurface;
30+
context!: GPUCanvasContext;
31+
renderPipeline!: GPURenderPipeline;
32+
33+
init() {
34+
const menu = NSMenu.new();
35+
NSApp.mainMenu = menu;
36+
37+
const appMenuItem = NSMenuItem.new();
38+
menu.addItem(appMenuItem);
39+
40+
const appMenu = NSMenu.new();
41+
appMenuItem.submenu = appMenu;
42+
43+
appMenu.addItemWithTitleActionKeyEquivalent("Quit", "terminate:", "q");
44+
45+
super.initWithContentRectStyleMaskBackingDefer(
46+
{ origin: { x: 0, y: 0 }, size: { width: 500, height: 500 } },
47+
NSWindowStyleMask.Titled |
48+
NSWindowStyleMask.Closable |
49+
NSWindowStyleMask.Miniaturizable |
50+
NSWindowStyleMask.Resizable,
51+
2,
52+
false
53+
);
54+
55+
this.title = "NativeScript for macOS";
56+
this.delegate = this;
57+
58+
this.isReleasedWhenClosed = false;
59+
60+
this.surface = new Deno.UnsafeWindowSurface(
61+
"cocoa",
62+
Deno.UnsafePointer.create(BigInt(interop.handleof(this).toNumber())),
63+
Deno.UnsafePointer.create(
64+
BigInt(interop.handleof(this.contentView).toNumber())
65+
)
66+
);
67+
68+
const { width, height } = this.convertRectToBacking(this.frame).size;
69+
70+
this.context = this.surface.getContext("webgpu");
71+
72+
this.context.configure({
73+
device,
74+
format: "bgra8unorm",
75+
width,
76+
height,
77+
});
78+
79+
const shaderCode = `
80+
@vertex
81+
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
82+
let x = f32(i32(in_vertex_index) - 1);
83+
let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
84+
return vec4<f32>(x, y, 0.0, 1.0);
85+
}
86+
87+
@fragment
88+
fn fs_main() -> @___location(0) vec4<f32> {
89+
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
90+
}
91+
`;
92+
93+
const shaderModule = device.createShaderModule({
94+
code: shaderCode,
95+
});
96+
97+
const pipelineLayout = device.createPipelineLayout({
98+
bindGroupLayouts: [],
99+
});
100+
101+
this.renderPipeline = device.createRenderPipeline({
102+
layout: pipelineLayout,
103+
vertex: {
104+
module: shaderModule,
105+
entryPoint: "vs_main",
106+
},
107+
fragment: {
108+
module: shaderModule,
109+
entryPoint: "fs_main",
110+
targets: [
111+
{
112+
format: "bgra8unorm",
113+
},
114+
],
115+
},
116+
});
117+
118+
NSApp.activateIgnoringOtherApps(true);
119+
120+
this.makeKeyAndOrderFront(NSApp);
121+
122+
this.center();
123+
124+
return this;
125+
}
126+
127+
render() {
128+
const encoder = device.createCommandEncoder();
129+
const texture = this.context.getCurrentTexture().createView();
130+
const renderPass = encoder.beginRenderPass({
131+
colorAttachments: [
132+
{
133+
view: texture,
134+
storeOp: "store",
135+
loadOp: "clear",
136+
clearValue: { r: 0, g: 1, b: 0, a: 1.0 },
137+
},
138+
],
139+
});
140+
renderPass.setPipeline(this.renderPipeline);
141+
renderPass.draw(3, 1);
142+
renderPass.end();
143+
device.queue.submit([encoder.finish()]);
144+
this.surface.present();
145+
}
146+
147+
windowWillClose(_notification: NSNotification) {
148+
NSApp.terminate(this);
149+
}
150+
}
151+
152+
const NSApp = NSApplication.sharedApplication;
153+
154+
NSApp.setActivationPolicy(NSApplicationActivationPolicy.Regular);
155+
156+
const delegate = ApplicationDelegate.new();
157+
158+
NSApp.delegate = delegate;
159+
160+
NSApp.finishLaunching();
161+
162+
while (true) {
163+
const event = NSApp.nextEventMatchingMaskUntilDateInModeDequeue(
164+
NSEventMask.Any,
165+
null,
166+
"kCFRunLoopDefaultMode",
167+
true
168+
);
169+
170+
if (event != null) {
171+
NSApp.sendEvent(event);
172+
}
173+
174+
if (!delegate.running) {
175+
break;
176+
}
177+
178+
delegate.window?.render();
179+
}

include/ObjCBridge.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ using namespace metagen;
2626

2727
namespace objc_bridge {
2828

29+
void finalize_objc_object(napi_env /*env*/, void *data, void *hint);
30+
2931
// Determines how retain/release should be called when an Objective-C
3032
// object is exposed to JavaScript land.
3133
typedef enum ObjectOwnership {
@@ -68,7 +70,7 @@ class ObjCBridgeState {
6870
MethodCif *getMethodCif(napi_env env, MDSectionOffset offset);
6971

7072
napi_value proxyNativeObject(napi_env env, napi_value object,
71-
bool isArray = false);
73+
id nativeObject);
7274

7375
napi_value getObject(napi_env env, id object, napi_value constructor,
7476
ObjectOwnership ownership = kUnownedObject);
@@ -112,6 +114,7 @@ class ObjCBridgeState {
112114
napi_ref referenceClass;
113115
napi_ref createNativeProxy;
114116
napi_ref createFastEnumeratorIterator;
117+
napi_ref transferOwnershipToNative;
115118

116119
std::unordered_map<MDSectionOffset, ObjCClass *> classes;
117120
std::unordered_map<MDSectionOffset, ObjCProtocol *> protocols;

metadata/metadata.macos.nsmd

34.2 KB
Binary file not shown.

packages/macos/types/AVFAudio.d.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -221,16 +221,6 @@ declare class AVAudioTime extends NSObject {
221221
readonly audioTimeStamp: AudioTimeStamp;
222222
}
223223

224-
declare class AVAudioEnvironmentReverbParameters extends NSObject {
225-
enable: boolean;
226-
227-
level: number;
228-
229-
readonly filterParameters: AVAudioUnitEQFilterParameters;
230-
231-
loadFactoryReverbPreset(preset: interop.Enum<typeof AVAudioUnitReverbPreset>): void;
232-
}
233-
234224
declare class AVAudioPCMBuffer extends AVAudioBuffer {
235225
initWithPCMFormatFrameCapacity(format: AVAudioFormat, frameCapacity: number): this;
236226

@@ -452,6 +442,16 @@ declare class AVAudioFormat extends NSObject implements NSSecureCoding {
452442
initWithCoder(coder: NSCoder): this;
453443
}
454444

445+
declare class AVAudioEnvironmentReverbParameters extends NSObject {
446+
enable: boolean;
447+
448+
level: number;
449+
450+
readonly filterParameters: AVAudioUnitEQFilterParameters;
451+
452+
loadFactoryReverbPreset(preset: interop.Enum<typeof AVAudioUnitReverbPreset>): void;
453+
}
454+
455455
declare class AVAudioIONode extends AVAudioNode {
456456
readonly presentationLatency: number;
457457

@@ -668,6 +668,8 @@ declare class AVAudioEnvironmentNode extends AVAudioNode implements AVAudioMixin
668668

669669
readonly applicableRenderingAlgorithms: NSArray;
670670

671+
isListenerHeadTrackingEnabled: boolean;
672+
671673
destinationForMixerBus(mixer: AVAudioNode, bus: number): AVAudioMixingDestination;
672674

673675
volume: number;

0 commit comments

Comments
 (0)