Skip to content

Commit 8178637

Browse files
committed
added Point example
1 parent 19fe714 commit 8178637

File tree

7 files changed

+204
-40
lines changed

7 files changed

+204
-40
lines changed

examples/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,10 @@ output
77
```
88
Hello World
99
fib(10)= 55
10+
```
11+
12+
#### Run point example
13+
```shell
14+
nim c --app:lib point.nim
15+
nim c run_test.nim test_point.js
1016
```

examples/fib.nim

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ proc fib(n: int32): int32 =
88
else:
99
result = fib(n - 1) + fib(n - 2);
1010

11-
12-
1311
proc js_fib(ctx: JSContext, this_val: JSValue, argc: cint, argv: ptr UncheckedArray[JSValue]): JSValue {.cdecl.} =
1412
var n: int32
1513

examples/hello.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import os, quickjs
1+
import quickjs
22

33
var hello = [byte(0x02), 0x04, 0x0e, 0x63, 0x6f, 0x6e, 0x73, 0x6f,
44
0x6c, 0x65, 0x06, 0x6c, 0x6f, 0x67, 0x16, 0x48,

examples/point.nim

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import quickjs, math
2+
3+
type
4+
JSPointData = object
5+
x: int32
6+
y: int32
7+
8+
var js_point_class_id: JSClassID
9+
10+
template fail() =
11+
js_free(ctx, s);
12+
JS_FreeValue(ctx, result);
13+
return JS_EXCEPTION;
14+
15+
proc js_point_ctor(ctx: JSContext, new_target: JSValueConst, argc: int32, argv: ptr UncheckedArray[JSValueConst]): JSValue {.cdecl.} =
16+
var
17+
s: ptr JSPointData
18+
proto: JSValue
19+
20+
result = JS_UNDEFINED
21+
22+
s = cast[ptr JSPointData](js_mallocz(ctx, sizeof(s[]).csize_t))
23+
if s == nil:
24+
return JS_EXCEPTION;
25+
if JS_ToInt32(ctx, addr s.x, argv[0]) != 0:
26+
fail()
27+
28+
if JS_ToInt32(ctx, addr s.y, argv[1]) != 0:
29+
fail()
30+
31+
proto = JS_GetPropertyStr(ctx, new_target, "prototype")
32+
if JS_IsException(proto):
33+
fail()
34+
result = JS_NewObjectProtoClass(ctx, proto, js_point_class_id)
35+
JS_FreeValue(ctx, proto)
36+
if JS_IsException(result):
37+
fail()
38+
JS_SetOpaque(result, s)
39+
40+
proc js_point_get_xy(ctx: JSContext, this: JSValueConst, magic: int32): JSValue {.cdecl.} =
41+
let s = cast[ptr JSPointData](JS_GetOpaque2(ctx, this, js_point_class_id))
42+
if s == nil:
43+
return JS_EXCEPTION
44+
if magic == 0:
45+
return JS_NewInt32(ctx, s.x)
46+
else:
47+
return JS_NewInt32(ctx, s.y)
48+
49+
proc js_point_set_xy(ctx: JSContext, this: JSValueConst, val: JSValue, magic: int32): JSValue {.cdecl.} =
50+
var s = cast[ptr JSPointData](JS_GetOpaque2(ctx, this, js_point_class_id))
51+
var v: int32
52+
if s == nil:
53+
return JS_EXCEPTION
54+
if JS_ToInt32(ctx, addr v, val) != 0:
55+
return JS_EXCEPTION
56+
if magic == 0:
57+
s.x = v
58+
else:
59+
s.y = v
60+
return JS_UNDEFINED
61+
62+
proc js_point_norm(ctx: JSContext, this: JSValueConst, argc: int32, argv: ptr UncheckedArray[JSValueConst]): JSValue {.cdecl.} =
63+
let s = cast[ptr JSPointData](JS_GetOpaque2(ctx, this, js_point_class_id))
64+
if s == nil:
65+
return JS_EXCEPTION
66+
return JS_NewFloat64(ctx, sqrt(s.x.float64 * s.x.float64 + s.y.float64 * s.y.float64))
67+
68+
var js_point_proto_funcs = [
69+
JS_CGETSET_MAGIC_DEF("x", js_point_get_xy, js_point_set_xy, 0),
70+
JS_CGETSET_MAGIC_DEF("y", js_point_get_xy, js_point_set_xy, 1),
71+
JS_CFUNC_DEF("norm", 0, js_point_norm),
72+
]
73+
74+
75+
proc js_point_finalizer(rt: JSRuntime, val: JSValue) {.cdecl.} =
76+
let s = cast[ptr JSPointData](JS_GetOpaque(val, js_point_class_id))
77+
if s != nil:
78+
js_free_rt(rt, s)
79+
80+
proc point_class_init(ctx: JSContext): JSValue =
81+
var
82+
js_point_class = JSClassDef(
83+
class_name: "Point",
84+
finalizer: js_point_finalizer
85+
)
86+
point_proto = JS_NewObject(ctx)
87+
88+
discard JS_NewClassID(addr js_point_class_id)
89+
discard JS_NewClass(JS_GetRuntime(ctx), js_point_class_id, addr js_point_class)
90+
JS_SetPropertyFunctionList(ctx, point_proto, addr js_point_proto_funcs[0], js_point_proto_funcs.len.int32);
91+
result = JS_NewCFunction2(ctx, js_point_ctor, "Point", 2, JS_CFUNC_constructor, 0)
92+
JS_SetConstructor(ctx, result, point_proto)
93+
JS_SetClassProto(ctx, js_point_class_id, point_proto)
94+
95+
when appType == "lib":
96+
proc js_point_init(ctx: JSContext, m: JSModuleDef): int32 {.cdecl.} =
97+
var point_class = point_class_init(ctx)
98+
discard JS_SetModuleExport(ctx, m, "Point", point_class)
99+
proc js_init_module(ctx: JSContext, moduleName: cstring): JSModuleDef {.exportc, dynlib.} =
100+
result = JS_NewCModule(ctx, moduleName, js_point_init)
101+
if result != nil:
102+
discard JS_AddModuleExport(ctx, result, "Point")
103+
else:
104+
let e = newEngine()
105+
e.registerValue("Point", point_class_init(e.ctx))
106+
let ret = e.evalString("""
107+
function assert(b, str) {
108+
if (b) {
109+
return;
110+
} else {
111+
throw Error("assertion failed: " + str);
112+
}
113+
}
114+
var pt = new Point(2, 3);
115+
assert(pt.x === 2);
116+
assert(pt.y === 3);
117+
pt.x = 4;
118+
assert(pt.x === 4);
119+
assert(pt.norm() == 5);
120+
""")
121+
quit(ret)

examples/test_point.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/* example of JS module importing a C module */
2+
import { Point } from "./libpoint.so";
3+
4+
function assert(b, str)
5+
{
6+
if (b) {
7+
return;
8+
} else {
9+
throw Error("assertion failed: " + str);
10+
}
11+
}
12+
13+
class ColorPoint extends Point {
14+
constructor(x, y, color) {
15+
super(x, y);
16+
this.color = color;
17+
}
18+
get_color() {
19+
return this.color;
20+
}
21+
};
22+
23+
function main()
24+
{
25+
var pt, pt2;
26+
27+
pt = new Point(2, 3);
28+
assert(pt.x === 2);
29+
assert(pt.y === 3);
30+
pt.x = 4;
31+
assert(pt.x === 4);
32+
assert(pt.norm() == 5);
33+
34+
pt2 = new ColorPoint(2, 3, 0xffffff);
35+
assert(pt2.x === 2);
36+
assert(pt2.color === 0xffffff);
37+
assert(pt2.get_color() === 0xffffff);
38+
}
39+
40+
main();

src/quickjs.nim

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ export core, helpers, libc
33

44
type
55
Engine* = object
6-
ctx: JSContext
7-
rt: JSRuntime
6+
ctx*: JSContext
7+
rt*: JSRuntime
88

99
proc `=destroy`*(e: var Engine) =
1010
JS_FreeContext(e.ctx)
@@ -126,3 +126,4 @@ proc registerFunction*(e: Engine, name: string, paramCount: int, fn: JSCFunction
126126
let global_obj = JS_GetGlobalObject(e.ctx)
127127
e.registerFunction(global_obj, name, paramCount, fn)
128128
JS_FreeValue(e.ctx, global_obj)
129+

src/quickjs/core.nim

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -308,9 +308,9 @@ proc JS_NewRuntime2*(mf: ptr JSMallocFunctions; opaque: pointer): JSRuntime {.
308308
importc, header: headerquickjs.}
309309
proc JS_FreeRuntime*(rt: JSRuntime) {.importc, header: headerquickjs.}
310310
type
311-
JS_MarkFunc* = proc (rt: JSRuntime; val: JSValue): void
311+
JS_MarkFunc* = proc (rt: JSRuntime; val: JSValue) {.cdecl.}
312312

313-
proc JS_MarkValue*(rt: JSRuntime; val: JSValue; mark_func: ptr JS_MarkFunc) {.
313+
proc JS_MarkValue*(rt: JSRuntime; val: JSValue; mark_func: JS_MarkFunc) {.
314314
importc, header: headerquickjs.}
315315
proc JS_RunGC*(rt: JSRuntime) {.importc, header: headerquickjs.}
316316
proc JS_IsLiveObject*(rt: JSRuntime; obj: JSValue): int32 {.
@@ -437,47 +437,46 @@ type
437437
getter* {.importc: "getter".}: JSValue
438438
setter* {.importc: "setter".}: JSValue
439439

440-
JSClassExoticMethods* {.importc, header: headerquickjs,
441-
bycopy.} = object
442-
get_own_property* {.importc: "get_own_property".}: proc (ctx: JSContext;
440+
JSClassExoticMethods* {.bycopy.} = object
441+
get_own_property*: proc (ctx: JSContext;
443442
desc: ptr JSPropertyDescriptor; obj: JSValue; prop: JSAtom): int32 ## Return -1 if exception (can only happen in case of Proxy object),
444443
## FALSE if the property does not exists, TRUE if it exists. If 1 is
445444
## returned, the property descriptor 'desc' is filled if != NULL.
446445
## '*ptab' should hold the '*plen' property keys. Return 0 if OK,
447446
## -1 if exception. The 'is_enumerable' field is ignored.
448447
##
449-
get_own_property_names* {.importc: "get_own_property_names".}: proc (
448+
get_own_property_names*: proc (
450449
ctx: JSContext; ptab: ptr ptr JSPropertyEnum; plen: ptr uint32; obj: JSValue): int32 ## return < 0 if exception, or TRUE/FALSE
451-
delete_property* {.importc: "delete_property".}: proc (ctx: JSContext;
450+
delete_property*: proc (ctx: JSContext;
452451
obj: JSValue; prop: JSAtom): int32 ## return < 0 if exception or TRUE/FALSE
453-
define_own_property* {.importc: "define_own_property".}: proc (
452+
define_own_property*: proc (
454453
ctx: JSContext; this_obj: JSValue; prop: JSAtom; val: JSValue;
455454
getter: JSValue; setter: JSValue; flags: int32): int32 ## The following methods can be emulated with the previous ones,
456455
## so they are usually not needed
457456
## return < 0 if exception or TRUE/FALSE
458-
has_property* {.importc: "has_property".}: proc (ctx: JSContext; obj: JSValue;
457+
has_property*: proc (ctx: JSContext; obj: JSValue;
459458
atom: JSAtom): int32
460-
get_property* {.importc: "get_property".}: proc (ctx: JSContext; obj: JSValue;
459+
get_property*: proc (ctx: JSContext; obj: JSValue;
461460
atom: JSAtom; receiver: JSValue): JSValue ## return < 0 if exception or TRUE/FALSE
462-
set_property* {.importc: "set_property".}: proc (ctx: JSContext; obj: JSValue;
461+
set_property*: proc (ctx: JSContext; obj: JSValue;
463462
atom: JSAtom; value: JSValue; receiver: JSValue; flags: int32): int32
464463

465-
JSClassFinalizer* = proc (rt: JSRuntime; val: JSValue): void {.cdecl.}
466-
JSClassGCMark* = proc (rt: JSRuntime; val: JSValue; mark_func: ptr JS_MarkFunc): void {.cdecl.}
464+
JSClassFinalizer* = proc (rt: JSRuntime; val: JSValue) {.cdecl.}
465+
JSClassGCMark* = proc (rt: JSRuntime; val: JSValue; mark_func: JS_MarkFunc) {.cdecl.}
467466
JSClassCall* = proc (ctx: JSContext; func_obj: JSValue; this_val: JSValue;
468467
argc: int32; argv: ptr UncheckedArray[JSValue]): JSValue {.cdecl.}
469-
JSClassDef* {.importc, header: headerquickjs, bycopy.} = object
470-
class_name* {.importc: "class_name".}: cstring
471-
finalizer* {.importc: "finalizer".}: JSClassFinalizer
472-
gc_mark* {.importc: "gc_mark".}: JSClassGCMark
473-
call* {.importc: "call".}: JSClassCall ## XXX: suppress this indirection ? It is here only to save memory
468+
JSClassDef* {.bycopy.} = object
469+
class_name*: cstring
470+
finalizer*: JSClassFinalizer
471+
gc_mark*: JSClassGCMark
472+
call*: JSClassCall ## XXX: suppress this indirection ? It is here only to save memory
474473
## because only a few classes need these methods
475-
exotic* {.importc: "exotic".}: JSClassExoticMethods
474+
exotic*: JSClassExoticMethods
476475

477-
proc JS_NewClassID*(pclass_id: JSClassID): JSClassID {.importc, header: headerquickjs.}
478-
proc JS_NewClass*(rt: JSRuntime; class_id: JSClassID; class_def: JSClassDef): int32 {.
476+
proc JS_NewClassID*(pclass_id: ptr JSClassID): JSClassID {.importc, header: headerquickjs.}
477+
proc JS_NewClass*(rt: JSRuntime; class_id: JSClassID; class_def: ptr JSClassDef): int32 {.
479478
importc, header: headerquickjs.}
480-
proc JS_IsRegisteredClass*(rt: JSRuntime; class_id: JSClassID): int32 {.
479+
proc JS_IsRegisteredClass*(rt: JSRuntime; class_id: JSClassID): bool {.
481480
importc, header: headerquickjs.}
482481
## value handling
483482

@@ -561,7 +560,7 @@ proc JS_ToCString*(ctx: JSContext; val1: JSValue): cstring {.
561560
proc JS_FreeCString*(ctx: JSContext; `ptr`: cstring) {.importc, header: headerquickjs.}
562561
proc JS_NewObjectProtoClass*(ctx: JSContext; proto: JSValue; class_id: JSClassID): JSValue {.
563562
importc, header: headerquickjs.}
564-
proc JS_NewObjectClass*(ctx: JSContext; class_id: int32): JSValue {.
563+
proc JS_NewObjectClass*(ctx: JSContext; class_id: JSClassID): JSValue {.
565564
importc, header: headerquickjs.}
566565
proc JS_NewObjectProto*(ctx: JSContext; proto: JSValue): JSValue {.
567566
importc, header: headerquickjs.}
@@ -718,16 +717,16 @@ proc JS_EvalFunction*(ctx: JSContext; fun_obj: JSValue): JSValue {.
718717
importc, header: headerquickjs.}
719718

720719
## C function definition
721-
proc JS_NewCFunction2*(ctx: JSContext; `func`: JSCFunction; name: cstring;
720+
proc JS_NewCFunction2*(ctx: JSContext; fn: JSCFunction; name: cstring;
722721
length: int32; cproto: JSCFunctionEnum; magic: int32): JSValue {.
723722
importc, header: headerquickjs.}
724-
proc JS_NewCFunctionData*(ctx: JSContext; `func`: ptr JSCFunctionData;
723+
proc JS_NewCFunctionData*(ctx: JSContext; fn: ptr JSCFunctionData;
725724
length: int32; magic: int32; data_len: int32; data: ptr JSValue): JSValue {.
726725
importc, header: headerquickjs.}
727-
proc JS_NewCFunction*(ctx: JSContext; `func`: JSCFunction; name: cstring;
726+
proc JS_NewCFunction*(ctx: JSContext; fn: JSCFunction; name: cstring;
728727
length: int32): JSValue {.importc, header: headerquickjs.}
729728

730-
proc JS_NewCFunctionMagic*(ctx: JSContext; `func`: ptr JSCFunctionMagic;
729+
proc JS_NewCFunctionMagic*(ctx: JSContext; fn: ptr JSCFunctionMagic;
731730
name: cstring; length: int32; cproto: JSCFunctionEnum;
732731
magic: int32): JSValue {.importc, header: headerquickjs.}
733732

@@ -741,19 +740,18 @@ type
741740
JSModuleInitFunc* = proc (ctx: JSContext; m: JSModuleDef): int32 {.cdecl.}
742741

743742
proc JS_NewCModule*(ctx: JSContext; name_str: cstring;
744-
fn: JSModuleInitFunc): JSModuleDef {.
745-
importc, header: headerquickjs.}
743+
fn: JSModuleInitFunc): JSModuleDef {.importc, header: headerquickjs.}
746744
## can only be called before the module is instantiated
747745

748-
proc JS_AddModuleExport*(ctx: JSContext; m: JSModuleDef; name_str: cstring): int32 {.
749-
importc, header: headerquickjs.}
746+
proc JS_AddModuleExport*(ctx: JSContext; m: JSModuleDef; name_str: cstring): int32 {.importc, header: headerquickjs.}
750747
proc JS_AddModuleExportList*(ctx: JSContext; m: JSModuleDef;
751-
tab: ptr JSCFunctionListEntry; len: int32): int32 {.
752-
importc, header: headerquickjs.}
748+
tab: ptr JSCFunctionListEntry; len: int32): int32 {.importc, header: headerquickjs.}
753749
## can only be called after the module is instantiated
754750

755751
proc JS_SetModuleExport*(ctx: JSContext; m: JSModuleDef; export_name: cstring;
756752
val: JSValue): int32 {.importc, header: headerquickjs.}
757753
proc JS_SetModuleExportList*(ctx: JSContext; m: JSModuleDef;
758-
tab: ptr JSCFunctionListEntry; len: int32): int32 {.
759-
importc, header: headerquickjs.}
754+
tab: ptr JSCFunctionListEntry; len: int32): int32 {.importc, header: headerquickjs.}
755+
756+
757+
proc JS_SetConstructor*(ctx: JSContext, func_obj: JSValueConst, proto: JSValueConst) {.importc, header: headerquickjs.}

0 commit comments

Comments
 (0)