Skip to content

Commit fe21d05

Browse files
committed
start working on separating runtime into a framework, implement interop.sizeof
1 parent 4e48199 commit fe21d05

File tree

9 files changed

+290
-9
lines changed

9 files changed

+290
-9
lines changed

examples/foundation.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,10 @@ console.log(`${dict}`);
3636
for (const key of dict) {
3737
console.log(key);
3838
}
39+
40+
console.log(
41+
interop.sizeof(dict),
42+
interop.sizeof(interop.types.uint8),
43+
interop.sizeof(NSMutableArray),
44+
interop.sizeof(AudioBuffer),
45+
);

runtime/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ set(CMAKE_CXX_FLAGS_DEBUG "-g")
99
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
1010

1111
set(CMAKE_CXX_STANDARD 20)
12+
set(CMAKE_OBJCXX_STANDARD 20)
1213

1314
include_directories(
1415
${CMAKE_CURRENT_SOURCE_DIR}/include
@@ -20,6 +21,8 @@ set(EXEC_SOURCE_FILES
2021
src/Runtime.cpp
2122
src/segappend.cpp
2223
src/Compiler.cpp
24+
src/Require.mm
25+
src/Performance.cpp
2326
)
2427

2528
add_executable(
@@ -36,7 +39,7 @@ target_link_options(
3639
target_link_directories(
3740
${NAME}
3841
PRIVATE
39-
${CMAKE_CURRENT_SOURCE_DIR}/../build/macos/Release
42+
${CMAKE_CURRENT_SOURCE_DIR}/../packages/macos/build/macos/Release
4043
)
4144

4245
target_link_libraries(

runtime/include/NapiUtil.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#ifndef NAPIUTIL_H
2+
#define NAPIUTIL_H
3+
4+
#include <string>
5+
#include "js_native_api.h"
6+
7+
inline std::string getStringValue(napi_env env, napi_value str) {
8+
size_t length = 0;
9+
napi_get_value_string_utf8(env, str, nullptr, 0, &length);
10+
char* strbuf = (char*) malloc(length + 1);
11+
napi_get_value_string_utf8(env, str, strbuf, length + 1, &length);
12+
std::string result = strbuf;
13+
free(strbuf);
14+
return result;
15+
}
16+
17+
#endif // NAPIUTIL_H

runtime/src/Compiler.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "segappend.h"
33
#include <filesystem>
44
#include <iostream>
5+
#include <array>
56

67
namespace charon {
78

runtime/src/Require.mm

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
#include <string>
2+
#include "js_native_api.h"
3+
#include "NapiUtil.h"
4+
5+
#import <Foundation/Foundation.h>
6+
7+
class Require {
8+
public:
9+
Require(std::string path) : path(path) {}
10+
11+
static napi_value createRequire(napi_env env, std::string &path) {
12+
Require *require = new Require(path);
13+
napi_value result;
14+
napi_create_function(env, "require", NAPI_AUTO_LENGTH, Require::requireCallback, require, &result);
15+
napi_ref ref;
16+
napi_wrap(env, result, require, Require::finalize, nullptr, &ref);
17+
return result;
18+
}
19+
20+
static void finalize(napi_env env, void *data, void *hint) {
21+
Require *require = (Require *) data;
22+
delete require;
23+
}
24+
25+
std::string resolve(std::string &spec) {
26+
if (spec.find("/") == 0) {
27+
return spec;
28+
}
29+
30+
size_t dotpos = spec.find(".");
31+
size_t tildepos = spec.find("~");
32+
std::string result;
33+
if (tildepos == 0) {
34+
result = [NSBundle.mainBundle.resourcePath UTF8String];
35+
result += "/app";
36+
} else {
37+
result = path;
38+
}
39+
40+
if (dotpos == 0 || tildepos == 0) {
41+
std::string relativeSpec = spec.substr(spec.find("./") == 0 || spec.find("~/") == 0 ? 2 : 1);
42+
if (relativeSpec.empty()) {
43+
result += "/index.js";
44+
} else {
45+
result += "/" + relativeSpec;
46+
}
47+
} else {
48+
result += "/" + spec;
49+
}
50+
51+
size_t pos = result.rfind("/");
52+
size_t jspos = result.find(".js");
53+
if (jspos < pos || jspos == std::string::npos) {
54+
result += result.ends_with("/") ? "index.js" : "/index.js";
55+
}
56+
57+
return result;
58+
}
59+
60+
napi_value require(napi_env env, std::string &spec) {
61+
std::string path = resolve(spec);
62+
NSError *error = nil;
63+
64+
NSString *source = [NSString stringWithContentsOfFile:[NSString stringWithUTF8String:path.c_str()] encoding:NSUTF8StringEncoding error:&error];
65+
if (error != nil) {
66+
NSLog(@"error: %@", error);
67+
napi_throw_error(env, nullptr, [[error localizedDescription] UTF8String]);
68+
return nullptr;
69+
}
70+
71+
if (spec.ends_with(".json")) {
72+
napi_value script, result;
73+
napi_create_string_utf8(env, [source UTF8String], NAPI_AUTO_LENGTH, &script);
74+
napi_run_script(env, script, &result);
75+
return result;
76+
}
77+
78+
std::string bootstrap;
79+
bootstrap = "let cjsModule; try { cjsModule = function cjsModule(exports, require, module, __filename, __dirname) { try { ";
80+
bootstrap += [source UTF8String];
81+
bootstrap += "\n } catch(e) { console.log('evaluate module failed:', e.stack); } }; Object.defineProperty(cjsModule, \"name\", { value: `" + path + "` }); } catch (e) { console.log(e.stack) }\n cjsModule";
82+
83+
napi_status status;
84+
napi_value func, script, module, exports, require, __filename, __dirname, global, result;
85+
86+
napi_create_string_utf8(env, bootstrap.c_str(), NAPI_AUTO_LENGTH, &script);
87+
88+
status = napi_run_script(env, script, &func);
89+
if (status != napi_ok) {
90+
const napi_extended_error_info *info;
91+
napi_get_last_error_info(env, &info);
92+
NSLog(@"error in run script: %d, %s", status, info->error_message);
93+
return nullptr;
94+
}
95+
96+
napi_create_object(env, &module);
97+
napi_create_object(env, &exports);
98+
99+
napi_set_named_property(env, module, "exports", exports);
100+
101+
napi_get_global(env, &global);
102+
103+
napi_create_string_utf8(env, path.c_str(), NAPI_AUTO_LENGTH, &__filename);
104+
105+
std::string dirname = path.substr(0, path.rfind("/"));
106+
107+
require = Require::createRequire(env, dirname);
108+
109+
napi_create_string_utf8(env, dirname.c_str(), NAPI_AUTO_LENGTH, &__dirname);
110+
111+
napi_value argv[5] = {exports, require, module, __filename, __dirname};
112+
113+
status = napi_call_function(env, global, func, 5, argv, &result);
114+
if (status != napi_ok) {
115+
const napi_extended_error_info *info;
116+
napi_get_last_error_info(env, &info);
117+
NSLog(@"error in evaluate module: %d, %s", status, info->error_message);
118+
return nullptr;
119+
}
120+
121+
napi_get_named_property(env, module, "exports", &exports);
122+
return exports;
123+
}
124+
125+
static napi_value requireCallback(napi_env env, napi_callback_info cbinfo) {
126+
napi_value arg;
127+
Require *require;
128+
size_t argc = 1;
129+
napi_get_cb_info(env, cbinfo, &argc, &arg, nullptr, (void**) &require);
130+
std::string spec = getStringValue(env, arg);
131+
return require->require(env, spec);
132+
}
133+
134+
std::string path;
135+
};

src/Class.mm

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@
216216
return nullptr;
217217
}
218218

219+
// Implemented in JS to minimize calls into native. Fast enumeration
220+
// makes use of buffers, so we only fill up the buffer once via
221+
// native call and only do it again if needed via _fillStack.
219222
static const char *FastEnumerationIteratorFactorySource = R"(
220223
(function () {
221224
return {
@@ -465,6 +468,9 @@ napi_value toJS(napi_env env) {
465468
napi_valuetype type;
466469
napi_typeof(env, SymbolDispose, &type);
467470

471+
napi_value sizeofValue;
472+
napi_create_int32(env, sizeof(id), &sizeofValue);
473+
468474
napi_property_descriptor properties[] = {
469475
{
470476
.utf8name = nil,
@@ -486,6 +492,16 @@ napi_value toJS(napi_env env) {
486492
.attributes = napi_enumerable,
487493
.data = nil,
488494
},
495+
{
496+
.utf8name = nil,
497+
.name = jsSymbolFor(env, "sizeof"),
498+
.method = nil,
499+
.getter = nil,
500+
.setter = nil,
501+
.value = sizeofValue,
502+
.attributes = napi_enumerable,
503+
.data = nil,
504+
},
489505
{
490506
.utf8name = nil,
491507
.name = SymbolIterator,
@@ -497,7 +513,9 @@ napi_value toJS(napi_env env) {
497513
.data = nil,
498514
}};
499515

500-
napi_define_properties(env, prototype, 3, properties);
516+
napi_define_properties(env, prototype, 4, properties);
517+
518+
napi_define_properties(env, constructor, 1, &properties[2]);
501519

502520
if (type == napi_symbol) {
503521
properties[0].name = SymbolDispose;

src/Interop.mm

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,10 +340,92 @@ napi_value interop_free(napi_env env, napi_callback_info info) {
340340
return nullptr;
341341
}
342342

343+
size_t jsSizeof(napi_env env, napi_value arg) {
344+
napi_valuetype type;
345+
napi_typeof(env, arg, &type);
346+
347+
switch (type) {
348+
case napi_number: {
349+
int32_t ival;
350+
napi_get_value_int32(env, arg, &ival);
351+
352+
switch (ival) {
353+
case mdTypeVoid:
354+
return 0;
355+
356+
case mdTypeBool:
357+
case mdTypeChar:
358+
case mdTypeUInt8:
359+
return sizeof(uint8_t);
360+
361+
case mdTypeSShort:
362+
case mdTypeUShort:
363+
return sizeof(uint16_t);
364+
365+
case mdTypeSInt:
366+
case mdTypeUInt:
367+
return sizeof(uint32_t);
368+
369+
case mdTypeSInt64:
370+
case mdTypeUInt64:
371+
return sizeof(uint64_t);
372+
373+
case mdTypeFloat:
374+
return sizeof(float);
375+
376+
case mdTypeDouble:
377+
return sizeof(double);
378+
379+
case mdTypeString:
380+
return sizeof(char *);
381+
382+
case mdTypeAnyObject:
383+
return sizeof(id);
384+
385+
case mdTypePointer:
386+
return sizeof(void *);
387+
388+
case mdTypeSelector:
389+
return sizeof(SEL);
390+
391+
default:
392+
napi_throw_type_error(env, "TypeError", "Invalid type number for sizeof. Use interop.types.*");
393+
}
394+
}
395+
396+
case napi_object:
397+
case napi_function: {
398+
napi_value symbolSizeof = jsSymbolFor(env, "sizeof");
399+
napi_value result;
400+
napi_get_property(env, arg, symbolSizeof, &result);
401+
napi_valuetype resultType;
402+
napi_typeof(env, result, &resultType);
403+
if (resultType == napi_number) {
404+
int32_t size;
405+
napi_get_value_int32(env, result, &size);
406+
return size;
407+
} else {
408+
napi_throw_type_error(env, "TypeError", "Invalid type for sizeof");
409+
}
410+
}
411+
412+
default:
413+
napi_throw_type_error(env, "TypeError", "Invalid type for sizeof");
414+
}
415+
416+
return -1;
417+
}
418+
343419
napi_value interop_sizeof(napi_env env, napi_callback_info info) {
344-
// TODO
420+
napi_valuetype type;
421+
napi_value arg;
422+
size_t argc = 1;
423+
napi_get_cb_info(env, info, &argc, &arg, nullptr, nullptr);
424+
425+
size_t size = jsSizeof(env, arg);
345426
napi_value result;
346-
napi_create_int32(env, 0, &result);
427+
napi_create_int32(env, size, &result);
428+
347429
return result;
348430
}
349431

src/Struct.mm

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ void StructObject_finalize(napi_env env, void *data, void *hint) {
316316

317317
napi_value StructObject::defineJSClass(napi_env env, StructInfo *info) {
318318
auto properties = (napi_property_descriptor *)malloc(
319-
(info->fields.size() + 1) * sizeof(napi_property_descriptor));
319+
(info->fields.size() + 2) * sizeof(napi_property_descriptor));
320320

321321
for (int i = 0; i < info->fields.size(); i++) {
322322
auto field = info->fields[i];
@@ -342,9 +342,24 @@ void StructObject_finalize(napi_env env, void *data, void *hint) {
342342
prop->attributes = napi_default;
343343
prop->data = nullptr;
344344

345+
napi_value size;
346+
napi_create_int32(env, info->size, &size);
347+
348+
auto sizeofProp = &properties[info->fields.size() + 1];
349+
sizeofProp->utf8name = nullptr;
350+
sizeofProp->name = jsSymbolFor(env, "sizeof");
351+
sizeofProp->method = nullptr;
352+
sizeofProp->getter = nullptr;
353+
sizeofProp->setter = nullptr;
354+
sizeofProp->value = size;
355+
sizeofProp->attributes = napi_enumerable;
356+
sizeofProp->data = nullptr;
357+
345358
napi_value result;
346359
napi_define_class(env, info->name, NAPI_AUTO_LENGTH, JS_StructConstructor,
347-
(void *)info, info->fields.size() + 1, properties, &result);
360+
(void *)info, info->fields.size() + 2, properties, &result);
361+
362+
napi_define_properties(env, result, 1, sizeofProp);
348363

349364
free(properties);
350365

src/TypeConv.mm

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,10 +1299,13 @@ napi_value toJS(napi_env env, void *value, uint32_t flags) override {
12991299

13001300
void toNative(napi_env env, napi_value value, void *result, bool *shouldFree,
13011301
bool *shouldFreeAny) override {
1302-
// TODO?
13031302
NAPI_PREAMBLE
1304-
1305-
NSLog(@"ArrayTypeConv toNative: TODO");
1303+
1304+
if (Pointer::isInstance(env, value)) {
1305+
Pointer *ptr = Pointer::unwrap(env, value);
1306+
memcpy(result, ptr->data, type->size);
1307+
return;
1308+
}
13061309

13071310
void *data;
13081311
size_t length = 0;

0 commit comments

Comments
 (0)