Skip to content

Commit a8c25b3

Browse files
committed
feat: more work on the runtime
1 parent ec2ba41 commit a8c25b3

File tree

11 files changed

+257
-53
lines changed

11 files changed

+257
-53
lines changed

packages/macos/lib/native.js

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,38 @@ if (typeof Deno == "object") {
66
parts.pop(); // remove executable name
77
const containerDir = parts.pop();
88
if (containerDir === "MacOS" && parts.pop() === "Contents") {
9-
const resourcesDir = parts.join("/") + "/Contents/Resources";
10-
const newPath = resourcesDir + "/NativeScript.node";
9+
const resourcesDir = parts.join("/") + "/Contents/Frameworks";
10+
const newPath = resourcesDir + "/libNativeScript.dylib";
1111
if (Deno.statSync(newPath).isFile) {
1212
path = newPath;
1313
}
1414
}
1515
}
1616

17-
let metaURL = import.meta.url;
18-
if (!metaURL.includes("://")) {
19-
metaURL = "file://" + metaURL;
20-
}
17+
if (typeof interop === "undefined") {
18+
let metaURL = import.meta.url;
19+
if (!metaURL.includes("://")) {
20+
metaURL = "file://" + metaURL;
21+
}
2122

22-
let functions;
23-
if (typeof Deno === "object") {
24-
const { dlopen } = await import("node:process");
25-
functions = dlopen(
26-
{ exports: {} },
27-
new URL(path, metaURL).pathname,
28-
).exports;
29-
} else {
30-
functions = {};
31-
process.dlopen(
32-
{ exports: functions },
33-
new URL(path, metaURL).pathname,
23+
let functions;
24+
if (typeof Deno === "object") {
25+
const { dlopen } = await import("node:process");
26+
functions = dlopen(
27+
{ exports: {} },
28+
new URL(path, metaURL).pathname,
29+
).exports;
30+
} else {
31+
functions = {};
32+
process.dlopen(
33+
{ exports: functions },
34+
new URL(path, metaURL).pathname,
35+
);
36+
}
37+
38+
functions.init(
39+
typeof Deno === "object"
40+
? Deno.env.get("METADATA_PATH")
41+
: process.env.METADATA_PATH,
3442
);
3543
}
36-
37-
functions.init(
38-
typeof Deno === "object"
39-
? Deno.env.get("METADATA_PATH")
40-
: process.env.METADATA_PATH,
41-
);

runtime/CMakeLists.txt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ set(RT_SOURCE_FILES
8181
src/Runtime.cpp
8282
src/Require.cpp
8383
src/Performance.cpp
84+
src/Bundle.mm
85+
src/Timers.mm
8486
)
8587

8688
# Define targets
@@ -122,16 +124,19 @@ set_target_properties(
122124
target_link_options(
123125
NativeScriptRuntime
124126
PRIVATE
125-
-Wl,-F${NS_BRIDGE_PATH},-F${CMAKE_CURRENT_SOURCE_DIR}/Frameworks/hermes.xcframework/${TARGET_PLATFORM_SPEC},-rpath,${NS_BRIDGE_PATH},-rpath,${CMAKE_CURRENT_SOURCE_DIR}/Frameworks/hermes.xcframework/${TARGET_PLATFORM_SPEC}
127+
-Wl,-rpath,@executable_path/../Frameworks
128+
-Wl,-rpath,${CMAKE_CURRENT_SOURCE_DIR}/Frameworks/hermes.xcframework/${TARGET_PLATFORM_SPEC}
129+
-Wl,-rpath,${NS_BRIDGE_PATH}
130+
-Wl,-F${NS_BRIDGE_PATH},-F${CMAKE_CURRENT_SOURCE_DIR}/Frameworks/hermes.xcframework/${TARGET_PLATFORM_SPEC}
126131
)
127132

128133
if (TARGET_PLATFORM_MACOS)
129-
set(BRIDGE_BIN "NativeScript")
134+
set(BRIDGE_BIN "-lNativeScript")
130135

131-
target_link_directories(
136+
target_link_options(
132137
NativeScriptRuntime
133138
PRIVATE
134-
${NS_BRIDGE_PATH}
139+
-L${NS_BRIDGE_PATH}
135140
)
136141
else()
137142
set(BRIDGE_BIN "-framework NativeScript")
@@ -158,12 +163,15 @@ if(TARGET_PLATFORM_MACOS)
158163
target_link_options(
159164
charon
160165
PRIVATE
166+
-Wl,-rpath,@executable_path/../Frameworks
167+
-Wl,-rpath,${CMAKE_CURRENT_SOURCE_DIR}/Frameworks/hermes.xcframework/${TARGET_PLATFORM_SPEC}
168+
-Wl,-rpath,${NS_BRIDGE_PATH}
161169
-undefined dynamic_lookup
162170
)
163171

164172
target_link_libraries(
165173
charon
166174
PRIVATE
167-
NativeScriptRuntime
175+
"NativeScriptRuntime"
168176
)
169177
endif()

runtime/include/Bundle.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#ifndef BUNDLE_H
2+
#define BUNDLE_H
3+
4+
#include <string>
5+
6+
std::string getBundlePath();
7+
8+
std::string getBytecodePathFromBundle();
9+
10+
#endif // BUNDLE_H

runtime/include/Timers.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#ifndef TIMERS_H
2+
#define TIMERS_H
3+
4+
#include "js_native_api.h"
5+
6+
namespace charon {
7+
8+
class Timers {
9+
public:
10+
static void init(napi_env env);
11+
12+
static napi_value setTimeout(napi_env env, napi_callback_info cbinfo);
13+
};
14+
15+
} // namespace charon
16+
17+
#endif // TIMERS_H

runtime/src/Bundle.mm

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#ifdef __APPLE__
2+
3+
#include "Bundle.h"
4+
5+
#include <Foundation/Foundation.h>
6+
7+
std::string getBundlePath() {
8+
NSBundle *mainBundle = [NSBundle mainBundle];
9+
NSString *bundlePath = [mainBundle bundlePath];
10+
return [bundlePath UTF8String];
11+
}
12+
13+
std::string getBytecodePathFromBundle() {
14+
NSBundle *mainBundle = [NSBundle mainBundle];
15+
if ([mainBundle objectForInfoDictionaryKey:@"NativeScriptApplication"] == nil) {
16+
return "";
17+
}
18+
NSString *bytecodePath = [mainBundle pathForResource:@"app" ofType:@"hbc"];
19+
if (bytecodePath == nil) {
20+
return "";
21+
}
22+
return [bytecodePath UTF8String];
23+
}
24+
25+
#endif // __APPLE__

runtime/src/Require.cpp

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "js_native_api.h"
44
#include <iostream>
55
#include <string>
6+
#include <dlfcn.h>
67

78
napi_value Require::createRequire(napi_env env, std::string &path,
89
std::string &tilde, Require **pRequire) {
@@ -64,16 +65,52 @@ std::string Require::resolve(std::string &spec) {
6465

6566
size_t pos = result.rfind("/");
6667
size_t jspos = result.find(".js");
67-
if (jspos < pos || jspos == std::string::npos) {
68-
result += result.ends_with("/") ? "index.js" : "/index.js";
68+
size_t dylibpos = result.rfind(".dylib");
69+
if ((jspos < pos || jspos == std::string::npos) && !(dylibpos < pos)) {
70+
// result += result.ends_with("/") ? "index.js" : "/index.js";
6971
}
7072

7173
return result;
7274
}
7375

76+
void finalize_dlobject(napi_env env, void *finalize_data, void *finalize_hint) {
77+
void *handle = finalize_data;
78+
dlclose(handle);
79+
}
80+
81+
typedef napi_value napi_module_register_fn(napi_env env, napi_value exports);
82+
7483
napi_value Require::require(napi_env env, std::string &spec) {
7584
std::string path = resolve(spec);
7685

86+
if (path.ends_with(".node") || path.ends_with(".dylib") || path.ends_with(".so")) {
87+
void *handle = dlopen(path.c_str(), RTLD_GLOBAL | RTLD_LAZY);
88+
if (!handle) {
89+
std::cerr << "error in dlopen: " << dlerror() << std::endl;
90+
return nullptr;
91+
}
92+
93+
void *sym = dlsym(handle, "napi_register_module_v1");
94+
if (!sym) {
95+
std::cerr << "error in dlsym: " << dlerror() << std::endl;
96+
return nullptr;
97+
}
98+
99+
napi_value module, exports;
100+
napi_create_object(env, &module);
101+
napi_create_object(env, &exports);
102+
napi_set_named_property(env, module, "exports", exports);
103+
104+
napi_module_register_fn *register_fn = (napi_module_register_fn *)sym;
105+
106+
exports = register_fn(env, exports);
107+
108+
napi_ref ref;
109+
napi_add_finalizer(env, module, handle, finalize_dlobject, nullptr, &ref);
110+
111+
return module;
112+
}
113+
77114
// std::cout << "================\nrequire: " << path << std::endl;
78115

79116
char *source = nullptr;

runtime/src/Runtime.cpp

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
#include "Console.h"
33
#include "Performance.h"
44
#include "Require.h"
5+
#include "Timers.h"
56
#include "js_native_api.h"
6-
#include <iostream>
77
#include <CoreFoundation/CFRunLoop.h>
8+
#include <iostream>
89

910
// #include <NativeScript/NativeScript.h>
1011

1112
extern "C" {
12-
void objc_bridge_init(napi_env env, const char *metadata_path);
13+
void objc_bridge_init(napi_env env, const char *metadata_path);
1314
}
1415

1516
namespace charon {
@@ -28,9 +29,11 @@ class BytecodeBuffer : public facebook::jsi::Buffer {
2829
};
2930

3031
Runtime::Runtime(std::string &mainPath) : mainPath(mainPath) {
31-
hermes::vm::RuntimeConfig config = hermes::vm::RuntimeConfig::Builder().withMicrotaskQueue(true).build();
32+
hermes::vm::RuntimeConfig config =
33+
hermes::vm::RuntimeConfig::Builder().withMicrotaskQueue(true).build();
3234
threadSafeRuntime = facebook::hermes::makeThreadSafeHermesRuntime(config);
33-
runtime = (facebook::hermes::HermesRuntime *) &threadSafeRuntime->getUnsafeRuntime();
35+
runtime =
36+
(facebook::hermes::HermesRuntime *)&threadSafeRuntime->getUnsafeRuntime();
3437

3538
runtime->createNapiEnv(&env);
3639

@@ -40,6 +43,9 @@ Runtime::Runtime(std::string &mainPath) : mainPath(mainPath) {
4043

4144
Console::init(env);
4245
Performance::init(env);
46+
#ifdef __APPLE__
47+
Timers::init(env);
48+
#endif // __APPLE__
4349

4450
require = Require::init(env, mainPath, mainPath);
4551

@@ -84,31 +90,32 @@ int Runtime::executeBytecode(const uint8_t *data, size_t size) {
8490
return 0;
8591
}
8692

87-
bool Runtime::eventLoopStep() {
88-
return !runtime->drainMicrotasks();
89-
}
93+
bool Runtime::eventLoopStep() { return !runtime->drainMicrotasks(); }
9094

9195
void Runtime::addEventLoopToRunLoop(bool exitOnEmpty) {
92-
auto handler = ^void (CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
93-
if (activity == kCFRunLoopBeforeWaiting){
94-
bool moreWork = this->eventLoopStep();
95-
if (moreWork) {
96-
CFRunLoopWakeUp(CFRunLoopGetMain());
97-
} else if (exitOnEmpty) {
98-
CFRunLoopStop(CFRunLoopGetMain());
99-
}
100-
}
101-
};
102-
103-
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, true, 0, handler);
96+
auto handler =
97+
^void(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
98+
if (activity == kCFRunLoopBeforeWaiting) {
99+
bool moreWork = this->eventLoopStep();
100+
if (moreWork) {
101+
CFRunLoopWakeUp(CFRunLoopGetMain());
102+
} else if (exitOnEmpty) {
103+
CFRunLoopStop(CFRunLoopGetMain());
104+
}
105+
}
106+
};
107+
108+
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(
109+
kCFAllocatorDefault, kCFRunLoopAllActivities, true, 0, handler);
104110
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);
105111
}
106112

107113
void Runtime::runRunLoop() {
108114
// Why does this not stop?
109115
// while (true) {
110-
// CFRunLoopRunResult result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true);
111-
// if (result == kCFRunLoopRunFinished || result == kCFRunLoopRunStopped) {
116+
// CFRunLoopRunResult result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0,
117+
// true); if (result == kCFRunLoopRunFinished || result ==
118+
// kCFRunLoopRunStopped) {
112119
// break;
113120
// }
114121
// }

runtime/src/Timers.mm

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#include "js_native_api.h"
2+
#ifdef __APPLE__
3+
4+
#include "Timers.h"
5+
#import <Foundation/Foundation.h>
6+
7+
namespace charon {
8+
9+
void Timers::init(napi_env env) {
10+
napi_value global, Performance, performance;
11+
12+
napi_get_global(env, &global);
13+
14+
const napi_property_descriptor properties[] = {
15+
{
16+
.utf8name = "setTimeout",
17+
.name = nullptr,
18+
.method = setTimeout,
19+
.getter = nullptr,
20+
.setter = nullptr,
21+
.value = nullptr,
22+
.attributes = napi_default,
23+
.data = nullptr,
24+
},
25+
};
26+
27+
napi_define_properties(env, global, 1, properties);
28+
}
29+
30+
napi_value Timers::setTimeout(napi_env env, napi_callback_info cbinfo) {
31+
size_t argc = 2;
32+
napi_value argv[2];
33+
napi_get_cb_info(env, cbinfo, &argc, argv, nullptr, nullptr);
34+
35+
double ms;
36+
napi_get_value_double(env, argv[1], &ms);
37+
38+
NSTimeInterval interval = ms / 1000;
39+
40+
napi_ref callback;
41+
napi_create_reference(env, argv[0], 1, &callback);
42+
43+
NSTimer *timer = [NSTimer
44+
timerWithTimeInterval:interval
45+
repeats:false
46+
block:^(NSTimer *timer) {
47+
napi_value global, callbackValue;
48+
napi_get_global(env, &global);
49+
napi_get_reference_value(env, callback, &callbackValue);
50+
napi_call_function(env, global, callbackValue, 0,
51+
nullptr, nullptr);
52+
napi_delete_reference(env, callback);
53+
}];
54+
55+
napi_value result;
56+
napi_create_int64(env, (int64_t)timer, &result);
57+
58+
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
59+
60+
return result;
61+
}
62+
63+
} // namespace charon
64+
65+
#endif // __APPLE__

0 commit comments

Comments
 (0)