point.c (4781B)
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 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 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 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 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 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 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 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 }