1 /*
2 * QuickJS: Example of C module with a class
3 *
4 * Copyright (c) 2019 Fabrice Bellard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 #include "../quickjs.h"
25 #include <math.h>
26
27 #define countof(x) (sizeof(x) / sizeof((x)[0]))
28
29 /* Point Class */
30
31 typedef struct {
32 int x;
33 int y;
34 } JSPointData;
35
36 static JSClassID js_point_class_id;
37
js_point_finalizer(JSRuntime * rt,JSValue val)38 static void js_point_finalizer(JSRuntime *rt, JSValue val)
39 {
40 JSPointData *s = JS_GetOpaque(val, js_point_class_id);
41 /* Note: 's' can be NULL in case JS_SetOpaque() was not called */
42 js_free_rt(rt, s);
43 }
44
js_point_ctor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)45 static JSValue js_point_ctor(JSContext *ctx,
46 JSValueConst new_target,
47 int argc, JSValueConst *argv)
48 {
49 JSPointData *s;
50 JSValue obj = JS_UNDEFINED;
51 JSValue proto;
52
53 s = js_mallocz(ctx, sizeof(*s));
54 if (!s)
55 return JS_EXCEPTION;
56 if (JS_ToInt32(ctx, &s->x, argv[0]))
57 goto fail;
58 if (JS_ToInt32(ctx, &s->y, argv[1]))
59 goto fail;
60 /* using new_target to get the prototype is necessary when the
61 class is extended. */
62 proto = JS_GetPropertyStr(ctx, new_target, "prototype");
63 if (JS_IsException(proto))
64 goto fail;
65 obj = JS_NewObjectProtoClass(ctx, proto, js_point_class_id);
66 JS_FreeValue(ctx, proto);
67 if (JS_IsException(obj))
68 goto fail;
69 JS_SetOpaque(obj, s);
70 return obj;
71 fail:
72 js_free(ctx, s);
73 JS_FreeValue(ctx, obj);
74 return JS_EXCEPTION;
75 }
76
js_point_get_xy(JSContext * ctx,JSValueConst this_val,int magic)77 static JSValue js_point_get_xy(JSContext *ctx, JSValueConst this_val, int magic)
78 {
79 JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id);
80 if (!s)
81 return JS_EXCEPTION;
82 if (magic == 0)
83 return JS_NewInt32(ctx, s->x);
84 else
85 return JS_NewInt32(ctx, s->y);
86 }
87
js_point_set_xy(JSContext * ctx,JSValueConst this_val,JSValue val,int magic)88 static JSValue js_point_set_xy(JSContext *ctx, JSValueConst this_val, JSValue val, int magic)
89 {
90 JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id);
91 int v;
92 if (!s)
93 return JS_EXCEPTION;
94 if (JS_ToInt32(ctx, &v, val))
95 return JS_EXCEPTION;
96 if (magic == 0)
97 s->x = v;
98 else
99 s->y = v;
100 return JS_UNDEFINED;
101 }
102
js_point_norm(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)103 static JSValue js_point_norm(JSContext *ctx, JSValueConst this_val,
104 int argc, JSValueConst *argv)
105 {
106 JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id);
107 if (!s)
108 return JS_EXCEPTION;
109 return JS_NewFloat64(ctx, sqrt((double)s->x * s->x + (double)s->y * s->y));
110 }
111
112 static JSClassDef js_point_class = {
113 "Point",
114 .finalizer = js_point_finalizer,
115 };
116
117 static const JSCFunctionListEntry js_point_proto_funcs[] = {
118 JS_CGETSET_MAGIC_DEF("x", js_point_get_xy, js_point_set_xy, 0),
119 JS_CGETSET_MAGIC_DEF("y", js_point_get_xy, js_point_set_xy, 1),
120 JS_CFUNC_DEF("norm", 0, js_point_norm),
121 };
122
js_point_init(JSContext * ctx,JSModuleDef * m)123 static int js_point_init(JSContext *ctx, JSModuleDef *m)
124 {
125 JSValue point_proto, point_class;
126
127 /* create the Point class */
128 JS_NewClassID(&js_point_class_id);
129 JS_NewClass(JS_GetRuntime(ctx), js_point_class_id, &js_point_class);
130
131 point_proto = JS_NewObject(ctx);
132 JS_SetPropertyFunctionList(ctx, point_proto, js_point_proto_funcs, countof(js_point_proto_funcs));
133
134 point_class = JS_NewCFunction2(ctx, js_point_ctor, "Point", 2, JS_CFUNC_constructor, 0);
135 /* set proto.constructor and ctor.prototype */
136 JS_SetConstructor(ctx, point_class, point_proto);
137 JS_SetClassProto(ctx, js_point_class_id, point_proto);
138
139 JS_SetModuleExport(ctx, m, "Point", point_class);
140 return 0;
141 }
142
js_init_module(JSContext * ctx,const char * module_name)143 JSModuleDef *js_init_module(JSContext *ctx, const char *module_name)
144 {
145 JSModuleDef *m;
146 m = JS_NewCModule(ctx, module_name, js_point_init);
147 if (!m)
148 return NULL;
149 JS_AddModuleExport(ctx, m, "Point");
150 return m;
151 }
152