update binding generator

This commit is contained in:
Alexander Klingenbeck 2023-05-08 23:37:58 +02:00
parent fd97c55510
commit bdba35dda4
9 changed files with 287 additions and 46 deletions

View File

@ -3,6 +3,7 @@
"sdl.h": "c", "sdl.h": "c",
"vs.sc.mtl.bin.h": "c", "vs.sc.mtl.bin.h": "c",
"embedded_shader.h": "c", "embedded_shader.h": "c",
"common.h": "c" "common.h": "c",
"raylib.h": "c"
} }
} }

View File

@ -8,6 +8,14 @@ function main(){
const apiDesc = new ApiDescription(api) const apiDesc = new ApiDescription(api)
const core_gen = new RayLibHeader("raylib_core", apiDesc) const core_gen = new RayLibHeader("raylib_core", apiDesc)
core_gen.addApiStructByName("Color", {
properties: {
r: { get: true, set: true },
g: { get: true, set: true },
b: { get: true, set: true },
a: { get: true, set: true },
}
})
core_gen.addApiFunctionByName("SetWindowTitle") core_gen.addApiFunctionByName("SetWindowTitle")
core_gen.addApiFunctionByName("SetWindowPosition") core_gen.addApiFunctionByName("SetWindowPosition")
core_gen.addApiFunctionByName("BeginDrawing") core_gen.addApiFunctionByName("BeginDrawing")
@ -15,7 +23,14 @@ function main(){
core_gen.writeTo("src/bindings/js_raylib_core.h") core_gen.writeTo("src/bindings/js_raylib_core.h")
const texture_gen = new RayLibHeader("raylib_texture", apiDesc) const texture_gen = new RayLibHeader("raylib_texture", apiDesc)
texture_gen.addApiStructByName("Image", "UnloadImage", { properties: { width: { get: true } } }) texture_gen.addApiStructByName("Image", {
properties: {
width: { get: true },
height: { get: true }
},
destructor: "UnloadImage"
})
texture_gen.addApiFunctionByName("LoadImage")
texture_gen.writeTo("src/bindings/js_raylib_texture.h") texture_gen.writeTo("src/bindings/js_raylib_texture.h")
} }

View File

@ -2,8 +2,11 @@ import { writeFileSync } from "fs";
import { ApiFunction } from "./api" import { ApiFunction } from "./api"
import { CodeGenerator, CodeWriter, GenericCodeGenerator } from "./generation" import { CodeGenerator, CodeWriter, GenericCodeGenerator } from "./generation"
export type StructLookup = { [struct: string]: string }
export class QuickJsHeader { export class QuickJsHeader {
public readonly structLookup: StructLookup = {}
public readonly moduleFunctionList: QuickJsGenerator public readonly moduleFunctionList: QuickJsGenerator
public readonly structs: QuickJsGenerator public readonly structs: QuickJsGenerator
public readonly functions: QuickJsGenerator public readonly functions: QuickJsGenerator
@ -48,6 +51,10 @@ export class QuickJsHeader {
moduleEntryFunc.statement("return m") moduleEntryFunc.statement("return m")
} }
registerStruct(struct: string, classId: string){
this.structLookup[struct] = classId;
}
public writeTo(filename: string){ public writeTo(filename: string){
const writer = new CodeWriter() const writer = new CodeWriter()
writer.writeGenerator(this.root) writer.writeGenerator(this.root)
@ -70,32 +77,45 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
} }
jsToC(type: string, name: string, src: string){ jsToC(type: string, name: string, src: string){
this.inline(`${type} ${name}`)
switch (type) { switch (type) {
case "const char *": case "const char *":
this.statement(` = JS_ToCString(ctx, ${src})`) this.statement(`${type} ${name} = JS_ToCString(ctx, ${src})`)
this.statement(`if(${name} == NULL) return JS_EXCEPTION`) this.statement(`if(${name} == NULL) return JS_EXCEPTION`)
break; break;
case "int": case "int":
this.statement('') this.statement(`${type} ${name}`)
this.statement(`JS_ToInt32(ctx, &${name}, ${src})`) this.statement(`JS_ToInt32(ctx, &${name}, ${src})`)
break; break;
case "unsigned char":
this.statement(`int _tmp`)
this.statement(`JS_ToInt32(ctx, &_tmp, ${src})`)
this.statement(`${type} ${name} = (${type})_tmp`)
break;
default: default:
throw new Error("Cannot handle parameter type: " + type) throw new Error("Cannot handle parameter type: " + type)
} }
} }
jsToJs(type: string, name: string, src: string){ jsToJs(type: string, name: string, src: string, classIds: StructLookup = {}){
this.inline(`JSValue ${name}`)
switch (type) { switch (type) {
case "int": case "int":
this.statement(` = JS_NewInt32(ctx, ${src})`) case "unsigned char":
this.declare(name,'JSValue', false, `JS_NewInt32(ctx, ${src})`)
break; break;
default: default:
throw new Error("Cannot handle parameter type: " + type) const classId = classIds[type]
if(!classId) throw new Error("Cannot handle parameter type: " + type)
this.jsStructToOpq(type, name, src, classId)
} }
} }
jsStructToOpq(structType: string, jsVar: string, srcVar: string, classId: string){
this.declare("ptr", structType+"*", false, `(${structType}*)js_malloc(ctx, sizeof(${structType}))`)
this.statement("*ptr = " + srcVar)
this.declare(jsVar, "JSValue", false, `JS_NewObjectClass(ctx, ${classId})`)
this.call("JS_SetOpaque", [jsVar, "ptr"])
}
jsCleanUpParameter(type: string, name: string) { jsCleanUpParameter(type: string, name: string) {
switch (type) { switch (type) {
case "const char *": case "const char *":
@ -141,7 +161,7 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
const body = this.function(`js_${structName}_finalizer`, "void", args, true) const body = this.function(`js_${structName}_finalizer`, "void", args, true)
body.statement(`${structName}* ptr = JS_GetOpaque(val, ${classId})`) body.statement(`${structName}* ptr = JS_GetOpaque(val, ${classId})`)
body.if("ptr", cond => { body.if("ptr", cond => {
cond.call("puts", ["\"Finalize "+structName+"\""]) cond.call("TraceLog", ["LOG_INFO",`"Finalize ${structName}"`])
if(onFinalize) onFinalize(<T>cond, "ptr") if(onFinalize) onFinalize(<T>cond, "ptr")
cond.call("js_free_rt", ["rt","ptr"]) cond.call("js_free_rt", ["rt","ptr"])
}) })
@ -173,6 +193,19 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
fun.returnExp("ret") fun.returnExp("ret")
return fun return fun
} }
jsStructSetter(structName: string, classId: string, field: string, type: string){
const args = [{type: "JSContext*", name: "ctx" }, {type: "JSValueConst", name: "this_val"},{type: "JSValueConst", name: "v"}]
const fun = this.function(`js_${structName}_set_${field}`,"JSValue",args,true)
fun.declare("ptr", structName+"*", false, `JS_GetOpaque2(ctx, this_val, ${classId})`)
fun.if("!ptr", cond => {
cond.returnExp("JS_EXCEPTION")
})
fun.jsToC(type, "value", "v");
fun.statement("ptr->"+field+" = value")
fun.returnExp("JS_UNDEFINED")
return fun
}
} }
export class QuickJsGenerator extends GenericQuickJsGenerator<QuickJsGenerator> { export class QuickJsGenerator extends GenericQuickJsGenerator<QuickJsGenerator> {

View File

@ -3,12 +3,16 @@ import { CodeGenerator } from "./generation"
import { QuickJsHeader } from "./quickjs" import { QuickJsHeader } from "./quickjs"
export interface StructBindingOptions { export interface StructBindingOptions {
properties?: { [key:string]: { get?:boolean, set?:boolean } } properties?: { [key:string]: { get?:boolean, set?:boolean } },
destructor?: string,
construct?: string,
} }
export class RayLibHeader extends QuickJsHeader { export class RayLibHeader extends QuickJsHeader {
constructor(name: string, private api: ApiDescription){ constructor(name: string, private api: ApiDescription){
super(name) super(name)
this.includes.include("raylib.h") this.includes.include("raylib.h")
@ -33,8 +37,8 @@ export class RayLibHeader extends QuickJsHeader {
if(api.returnType === "void"){ if(api.returnType === "void"){
fun.statement("return JS_UNDEFINED") fun.statement("return JS_UNDEFINED")
} else { } else {
fun.jsToJs(api.returnType, "ret", "returnVal") fun.jsToJs(api.returnType, "ret", "returnVal", this.structLookup)
fun.returnExp("returnVal") fun.returnExp("ret")
} }
// add binding to function declaration // add binding to function declaration
@ -49,7 +53,7 @@ export class RayLibHeader extends QuickJsHeader {
addApiStruct(struct: ApiStruct, destructor: ApiFunction | null, options?: StructBindingOptions){ addApiStruct(struct: ApiStruct, destructor: ApiFunction | null, options?: StructBindingOptions){
const classId = this.declarations.jsClassId(`js_${struct.name}_class_id`) const classId = this.declarations.jsClassId(`js_${struct.name}_class_id`)
this.registerStruct(struct.name, classId)
const finalizer = this.structs.jsStructFinalizer(classId, struct.name, (gen,ptr) => destructor && gen.call(destructor.name, ["*"+ptr])) const finalizer = this.structs.jsStructFinalizer(classId, struct.name, (gen,ptr) => destructor && gen.call(destructor.name, ["*"+ptr]))
const propDeclarations = this.structs.createGenerator() const propDeclarations = this.structs.createGenerator()
@ -61,26 +65,27 @@ export class RayLibHeader extends QuickJsHeader {
let _get: CodeGenerator | undefined = undefined let _get: CodeGenerator | undefined = undefined
let _set: CodeGenerator | undefined = undefined let _set: CodeGenerator | undefined = undefined
if(el.get) _get = this.structs.jsStructGetter(struct.name, classId, field, type) if(el.get) _get = this.structs.jsStructGetter(struct.name, classId, field, type)
propDeclarations.jsGetSetDef(field, _get?.getTag("_name"), undefined) if(el.set) _set = this.structs.jsStructSetter(struct.name, classId, field, type)
propDeclarations.jsGetSetDef(field, _get?.getTag("_name"), _set?.getTag("_name"))
} }
} }
const classFuncList = this.structs.jsFunctionList(`js_${struct.name}_proto_funcs`) const classFuncList = this.structs.jsFunctionList(`js_${struct.name}_proto_funcs`)
classFuncList.child(propDeclarations) classFuncList.child(propDeclarations)
classFuncList.jsPropStringDef("[Symbol.toStringTag]", "Image") classFuncList.jsPropStringDef("[Symbol.toStringTag]", struct.name)
const classDecl = this.structs.jsClassDeclaration(struct.name, classId, finalizer.getTag("_name"), classFuncList.getTag("_name")) const classDecl = this.structs.jsClassDeclaration(struct.name, classId, finalizer.getTag("_name"), classFuncList.getTag("_name"))
this.moduleInit.call(classDecl.getTag("_name"), ["ctx", "m"]) this.moduleInit.call(classDecl.getTag("_name"), ["ctx", "m"])
// OPT: 7. expose class and constructor // OPT: 7. expose class and constructor
} }
addApiStructByName(structName: string, destructorName: string | null = null, options?: StructBindingOptions){ addApiStructByName(structName: string, options?: StructBindingOptions){
const struct = this.api.getStruct(structName) const struct = this.api.getStruct(structName)
if(!struct) throw new Error("Struct not in API: "+ structName) if(!struct) throw new Error("Struct not in API: "+ structName)
let destructor: ApiFunction | null = null let destructor: ApiFunction | null = null
if(destructorName !== null){ if(options?.destructor){
destructor = this.api.getFunction(destructorName) destructor = this.api.getFunction(options.destructor)
if(!destructor) throw new Error("Destructor func not in API: "+ destructorName) if(!destructor) throw new Error("Destructor func not in API: "+ options.destructor)
} }
this.addApiStruct(struct, destructor, options) this.addApiStruct(struct, destructor, options)
} }

View File

@ -284,6 +284,7 @@ const generation_1 = __webpack_require__(/*! ./generation */ "./src/generation.t
class QuickJsHeader { class QuickJsHeader {
constructor(name) { constructor(name) {
this.name = name; this.name = name;
this.structLookup = {};
const root = this.root = new QuickJsGenerator(); const root = this.root = new QuickJsGenerator();
const body = this.body = root.header("JS_" + this.name + "_GUARD"); const body = this.body = root.header("JS_" + this.name + "_GUARD");
const includes = this.includes = body.child(); const includes = this.includes = body.child();
@ -314,6 +315,9 @@ class QuickJsHeader {
moduleEntry.statement(`JS_AddModuleExportList(ctx, m, ${this.moduleFunctionList.getTag("_name")}, countof(${this.moduleFunctionList.getTag("_name")}))`); moduleEntry.statement(`JS_AddModuleExportList(ctx, m, ${this.moduleFunctionList.getTag("_name")}, countof(${this.moduleFunctionList.getTag("_name")}))`);
moduleEntryFunc.statement("return m"); moduleEntryFunc.statement("return m");
} }
registerStruct(struct, classId) {
this.structLookup[struct] = classId;
}
writeTo(filename) { writeTo(filename) {
const writer = new generation_1.CodeWriter(); const writer = new generation_1.CodeWriter();
writer.writeGenerator(this.root); writer.writeGenerator(this.root);
@ -333,30 +337,43 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
return sub; return sub;
} }
jsToC(type, name, src) { jsToC(type, name, src) {
this.inline(`${type} ${name}`);
switch (type) { switch (type) {
case "const char *": case "const char *":
this.statement(` = JS_ToCString(ctx, ${src})`); this.statement(`${type} ${name} = JS_ToCString(ctx, ${src})`);
this.statement(`if(${name} == NULL) return JS_EXCEPTION`); this.statement(`if(${name} == NULL) return JS_EXCEPTION`);
break; break;
case "int": case "int":
this.statement(''); this.statement(`${type} ${name}`);
this.statement(`JS_ToInt32(ctx, &${name}, ${src})`); this.statement(`JS_ToInt32(ctx, &${name}, ${src})`);
break; break;
case "unsigned char":
this.statement(`int _tmp`);
this.statement(`JS_ToInt32(ctx, &_tmp, ${src})`);
this.statement(`${type} ${name} = (${type})_tmp`);
break;
default: default:
throw new Error("Cannot handle parameter type: " + type); throw new Error("Cannot handle parameter type: " + type);
} }
} }
jsToJs(type, name, src) { jsToJs(type, name, src, classIds = {}) {
this.inline(`JSValue ${name}`);
switch (type) { switch (type) {
case "int": case "int":
this.statement(` = JS_NewInt32(ctx, ${src})`); case "unsigned char":
this.declare(name, 'JSValue', false, `JS_NewInt32(ctx, ${src})`);
break; break;
default: default:
throw new Error("Cannot handle parameter type: " + type); const classId = classIds[type];
if (!classId)
throw new Error("Cannot handle parameter type: " + type);
this.jsStructToOpq(type, name, src, classId);
} }
} }
jsStructToOpq(structType, jsVar, srcVar, classId) {
this.declare("ptr", structType + "*", false, `(${structType}*)js_malloc(ctx, sizeof(${structType}))`);
this.statement("*ptr = " + srcVar);
this.declare(jsVar, "JSValue", false, `JS_NewObjectClass(ctx, ${classId})`);
this.call("JS_SetOpaque", [jsVar, "ptr"]);
}
jsCleanUpParameter(type, name) { jsCleanUpParameter(type, name) {
switch (type) { switch (type) {
case "const char *": case "const char *":
@ -396,7 +413,7 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
const body = this.function(`js_${structName}_finalizer`, "void", args, true); const body = this.function(`js_${structName}_finalizer`, "void", args, true);
body.statement(`${structName}* ptr = JS_GetOpaque(val, ${classId})`); body.statement(`${structName}* ptr = JS_GetOpaque(val, ${classId})`);
body.if("ptr", cond => { body.if("ptr", cond => {
cond.call("puts", ["\"Finalize " + structName + "\""]); cond.call("TraceLog", ["LOG_INFO", `"Finalize ${structName}"`]);
if (onFinalize) if (onFinalize)
onFinalize(cond, "ptr"); onFinalize(cond, "ptr");
cond.call("js_free_rt", ["rt", "ptr"]); cond.call("js_free_rt", ["rt", "ptr"]);
@ -427,6 +444,18 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
fun.returnExp("ret"); fun.returnExp("ret");
return fun; return fun;
} }
jsStructSetter(structName, classId, field, type) {
const args = [{ type: "JSContext*", name: "ctx" }, { type: "JSValueConst", name: "this_val" }, { type: "JSValueConst", name: "v" }];
const fun = this.function(`js_${structName}_set_${field}`, "JSValue", args, true);
fun.declare("ptr", structName + "*", false, `JS_GetOpaque2(ctx, this_val, ${classId})`);
fun.if("!ptr", cond => {
cond.returnExp("JS_EXCEPTION");
});
fun.jsToC(type, "value", "v");
fun.statement("ptr->" + field + " = value");
fun.returnExp("JS_UNDEFINED");
return fun;
}
} }
exports.GenericQuickJsGenerator = GenericQuickJsGenerator; exports.GenericQuickJsGenerator = GenericQuickJsGenerator;
class QuickJsGenerator extends GenericQuickJsGenerator { class QuickJsGenerator extends GenericQuickJsGenerator {
@ -474,8 +503,8 @@ class RayLibHeader extends quickjs_1.QuickJsHeader {
fun.statement("return JS_UNDEFINED"); fun.statement("return JS_UNDEFINED");
} }
else { else {
fun.jsToJs(api.returnType, "ret", "returnVal"); fun.jsToJs(api.returnType, "ret", "returnVal", this.structLookup);
fun.returnExp("returnVal"); fun.returnExp("ret");
} }
// add binding to function declaration // add binding to function declaration
this.moduleFunctionList.jsFuncDef(jName, api.argc, fun.getTag("_name")); this.moduleFunctionList.jsFuncDef(jName, api.argc, fun.getTag("_name"));
@ -488,6 +517,7 @@ class RayLibHeader extends quickjs_1.QuickJsHeader {
} }
addApiStruct(struct, destructor, options) { addApiStruct(struct, destructor, options) {
const classId = this.declarations.jsClassId(`js_${struct.name}_class_id`); const classId = this.declarations.jsClassId(`js_${struct.name}_class_id`);
this.registerStruct(struct.name, classId);
const finalizer = this.structs.jsStructFinalizer(classId, struct.name, (gen, ptr) => destructor && gen.call(destructor.name, ["*" + ptr])); const finalizer = this.structs.jsStructFinalizer(classId, struct.name, (gen, ptr) => destructor && gen.call(destructor.name, ["*" + ptr]));
const propDeclarations = this.structs.createGenerator(); const propDeclarations = this.structs.createGenerator();
if (options && options.properties) { if (options && options.properties) {
@ -500,25 +530,27 @@ class RayLibHeader extends quickjs_1.QuickJsHeader {
let _set = undefined; let _set = undefined;
if (el.get) if (el.get)
_get = this.structs.jsStructGetter(struct.name, classId, field, type); _get = this.structs.jsStructGetter(struct.name, classId, field, type);
propDeclarations.jsGetSetDef(field, _get?.getTag("_name"), undefined); if (el.set)
_set = this.structs.jsStructSetter(struct.name, classId, field, type);
propDeclarations.jsGetSetDef(field, _get?.getTag("_name"), _set?.getTag("_name"));
} }
} }
const classFuncList = this.structs.jsFunctionList(`js_${struct.name}_proto_funcs`); const classFuncList = this.structs.jsFunctionList(`js_${struct.name}_proto_funcs`);
classFuncList.child(propDeclarations); classFuncList.child(propDeclarations);
classFuncList.jsPropStringDef("[Symbol.toStringTag]", "Image"); classFuncList.jsPropStringDef("[Symbol.toStringTag]", struct.name);
const classDecl = this.structs.jsClassDeclaration(struct.name, classId, finalizer.getTag("_name"), classFuncList.getTag("_name")); const classDecl = this.structs.jsClassDeclaration(struct.name, classId, finalizer.getTag("_name"), classFuncList.getTag("_name"));
this.moduleInit.call(classDecl.getTag("_name"), ["ctx", "m"]); this.moduleInit.call(classDecl.getTag("_name"), ["ctx", "m"]);
// OPT: 7. expose class and constructor // OPT: 7. expose class and constructor
} }
addApiStructByName(structName, destructorName = null, options) { addApiStructByName(structName, options) {
const struct = this.api.getStruct(structName); const struct = this.api.getStruct(structName);
if (!struct) if (!struct)
throw new Error("Struct not in API: " + structName); throw new Error("Struct not in API: " + structName);
let destructor = null; let destructor = null;
if (destructorName !== null) { if (options?.destructor) {
destructor = this.api.getFunction(destructorName); destructor = this.api.getFunction(options.destructor);
if (!destructor) if (!destructor)
throw new Error("Destructor func not in API: " + destructorName); throw new Error("Destructor func not in API: " + options.destructor);
} }
this.addApiStruct(struct, destructor, options); this.addApiStruct(struct, destructor, options);
} }
@ -581,13 +613,28 @@ function main() {
const api = JSON.parse((0, fs_1.readFileSync)("thirdparty/raylib/parser/output/raylib_api.json", 'utf8')); const api = JSON.parse((0, fs_1.readFileSync)("thirdparty/raylib/parser/output/raylib_api.json", 'utf8'));
const apiDesc = new api_1.ApiDescription(api); const apiDesc = new api_1.ApiDescription(api);
const core_gen = new raylib_header_1.RayLibHeader("raylib_core", apiDesc); const core_gen = new raylib_header_1.RayLibHeader("raylib_core", apiDesc);
core_gen.addApiStructByName("Color", {
properties: {
r: { get: true, set: true },
g: { get: true, set: true },
b: { get: true, set: true },
a: { get: true, set: true },
}
});
core_gen.addApiFunctionByName("SetWindowTitle"); core_gen.addApiFunctionByName("SetWindowTitle");
core_gen.addApiFunctionByName("SetWindowPosition"); core_gen.addApiFunctionByName("SetWindowPosition");
core_gen.addApiFunctionByName("BeginDrawing"); core_gen.addApiFunctionByName("BeginDrawing");
core_gen.addApiFunctionByName("EndDrawing"); core_gen.addApiFunctionByName("EndDrawing");
core_gen.writeTo("src/bindings/js_raylib_core.h"); core_gen.writeTo("src/bindings/js_raylib_core.h");
const texture_gen = new raylib_header_1.RayLibHeader("raylib_texture", apiDesc); const texture_gen = new raylib_header_1.RayLibHeader("raylib_texture", apiDesc);
texture_gen.addApiStructByName("Image", "UnloadImage", { properties: { width: { get: true } } }); texture_gen.addApiStructByName("Image", {
properties: {
width: { get: true },
height: { get: true }
},
destructor: "UnloadImage"
});
texture_gen.addApiFunctionByName("LoadImage");
texture_gen.writeTo("src/bindings/js_raylib_texture.h"); texture_gen.writeTo("src/bindings/js_raylib_texture.h");
} }
main(); main();

14
main.js
View File

@ -1,15 +1,15 @@
import { setWindowTitle, setWindowPosition } from "raylib.core" import { setWindowTitle, setWindowPosition } from "raylib.core"
import { loadImage, Image } from "raylib.texture" import { loadImage } from "raylib.texture"
import { gc } from "std" import { gc } from "std"
console.log(loadImage("assets/planet00.png")) const img = loadImage("assets/planet00.png")
const img = new Image("assets/planet00.png")
gc()
console.log(img.width) console.log(img.width)
//const img = new Image("assets/planet00.png")
gc()
setWindowTitle("My Window") setWindowTitle("My Window")
setWindowPosition(1920,50) setWindowPosition(20,50)

View File

@ -12,6 +12,121 @@
#define countof(x) (sizeof(x) / sizeof((x)[0])) #define countof(x) (sizeof(x) / sizeof((x)[0]))
#endif #endif
static JSClassID js_Color_class_id;
static void js_Color_finalizer(JSRuntime * rt, JSValue val) {
Color* ptr = JS_GetOpaque(val, js_Color_class_id);
if(ptr) {
TraceLog(LOG_INFO, "Finalize Color");
js_free_rt(rt, ptr);
}
}
static JSValue js_Color_get_r(JSContext* ctx, JSValueConst this_val) {
Color* ptr = JS_GetOpaque2(ctx, this_val, js_Color_class_id);
if(!ptr) {
return JS_EXCEPTION;
}
unsigned char r = ptr->r;
JSValue ret = JS_NewInt32(ctx, r);
return ret;
}
static JSValue js_Color_set_r(JSContext* ctx, JSValueConst this_val, JSValueConst v) {
Color* ptr = JS_GetOpaque2(ctx, this_val, js_Color_class_id);
if(!ptr) {
return JS_EXCEPTION;
}
int _tmp;
JS_ToInt32(ctx, &_tmp, v);
unsigned char value = (unsigned char)_tmp;
ptr->r = value;
return JS_UNDEFINED;
}
static JSValue js_Color_get_g(JSContext* ctx, JSValueConst this_val) {
Color* ptr = JS_GetOpaque2(ctx, this_val, js_Color_class_id);
if(!ptr) {
return JS_EXCEPTION;
}
unsigned char g = ptr->g;
JSValue ret = JS_NewInt32(ctx, g);
return ret;
}
static JSValue js_Color_set_g(JSContext* ctx, JSValueConst this_val, JSValueConst v) {
Color* ptr = JS_GetOpaque2(ctx, this_val, js_Color_class_id);
if(!ptr) {
return JS_EXCEPTION;
}
int _tmp;
JS_ToInt32(ctx, &_tmp, v);
unsigned char value = (unsigned char)_tmp;
ptr->g = value;
return JS_UNDEFINED;
}
static JSValue js_Color_get_b(JSContext* ctx, JSValueConst this_val) {
Color* ptr = JS_GetOpaque2(ctx, this_val, js_Color_class_id);
if(!ptr) {
return JS_EXCEPTION;
}
unsigned char b = ptr->b;
JSValue ret = JS_NewInt32(ctx, b);
return ret;
}
static JSValue js_Color_set_b(JSContext* ctx, JSValueConst this_val, JSValueConst v) {
Color* ptr = JS_GetOpaque2(ctx, this_val, js_Color_class_id);
if(!ptr) {
return JS_EXCEPTION;
}
int _tmp;
JS_ToInt32(ctx, &_tmp, v);
unsigned char value = (unsigned char)_tmp;
ptr->b = value;
return JS_UNDEFINED;
}
static JSValue js_Color_get_a(JSContext* ctx, JSValueConst this_val) {
Color* ptr = JS_GetOpaque2(ctx, this_val, js_Color_class_id);
if(!ptr) {
return JS_EXCEPTION;
}
unsigned char a = ptr->a;
JSValue ret = JS_NewInt32(ctx, a);
return ret;
}
static JSValue js_Color_set_a(JSContext* ctx, JSValueConst this_val, JSValueConst v) {
Color* ptr = JS_GetOpaque2(ctx, this_val, js_Color_class_id);
if(!ptr) {
return JS_EXCEPTION;
}
int _tmp;
JS_ToInt32(ctx, &_tmp, v);
unsigned char value = (unsigned char)_tmp;
ptr->a = value;
return JS_UNDEFINED;
}
static const JSCFunctionListEntry js_Color_proto_funcs[] = {
JS_CGETSET_DEF("r",js_Color_get_r,js_Color_set_r),
JS_CGETSET_DEF("g",js_Color_get_g,js_Color_set_g),
JS_CGETSET_DEF("b",js_Color_get_b,js_Color_set_b),
JS_CGETSET_DEF("a",js_Color_get_a,js_Color_set_a),
JS_PROP_STRING_DEF("[Symbol.toStringTag]","Color", JS_PROP_CONFIGURABLE),
};
static int js_declare_Color(JSContext * ctx, JSModuleDef * m) {
JS_NewClassID(&js_Color_class_id);
JSClassDef js_Color_def = { .class_name = "Color", .finalizer = js_Color_finalizer };
JS_NewClass(JS_GetRuntime(ctx), js_Color_class_id, &js_Color_def);
JSValue proto = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, proto, js_Color_proto_funcs, countof(js_Color_proto_funcs));
JS_SetClassProto(ctx, js_Color_class_id, proto);
return 0;
}
static JSValue js_setWindowTitle(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { static JSValue js_setWindowTitle(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) {
const char * title = JS_ToCString(ctx, argv[0]); const char * title = JS_ToCString(ctx, argv[0]);
@ -49,6 +164,7 @@ static const JSCFunctionListEntry js_raylib_core_funcs[] = {
static int js_raylib_core_init(JSContext * ctx, JSModuleDef * m) { static int js_raylib_core_init(JSContext * ctx, JSModuleDef * m) {
JS_SetModuleExportList(ctx, m,js_raylib_core_funcs,countof(js_raylib_core_funcs)); JS_SetModuleExportList(ctx, m,js_raylib_core_funcs,countof(js_raylib_core_funcs));
js_declare_Color(ctx, m);
return 0; return 0;
} }

View File

@ -17,7 +17,7 @@ static JSClassID js_Image_class_id;
static void js_Image_finalizer(JSRuntime * rt, JSValue val) { static void js_Image_finalizer(JSRuntime * rt, JSValue val) {
Image* ptr = JS_GetOpaque(val, js_Image_class_id); Image* ptr = JS_GetOpaque(val, js_Image_class_id);
if(ptr) { if(ptr) {
puts("Finalize Image"); TraceLog(LOG_INFO, "Finalize Image");
UnloadImage(*ptr); UnloadImage(*ptr);
js_free_rt(rt, ptr); js_free_rt(rt, ptr);
} }
@ -33,8 +33,19 @@ static JSValue js_Image_get_width(JSContext* ctx, JSValueConst this_val) {
return ret; return ret;
} }
static JSValue js_Image_get_height(JSContext* ctx, JSValueConst this_val) {
Image* ptr = JS_GetOpaque2(ctx, this_val, js_Image_class_id);
if(!ptr) {
return JS_EXCEPTION;
}
int height = ptr->height;
JSValue ret = JS_NewInt32(ctx, height);
return ret;
}
static const JSCFunctionListEntry js_Image_proto_funcs[] = { static const JSCFunctionListEntry js_Image_proto_funcs[] = {
JS_CGETSET_DEF("width",js_Image_get_width,NULL), JS_CGETSET_DEF("width",js_Image_get_width,NULL),
JS_CGETSET_DEF("height",js_Image_get_height,NULL),
JS_PROP_STRING_DEF("[Symbol.toStringTag]","Image", JS_PROP_CONFIGURABLE), JS_PROP_STRING_DEF("[Symbol.toStringTag]","Image", JS_PROP_CONFIGURABLE),
}; };
@ -48,7 +59,20 @@ static int js_declare_Image(JSContext * ctx, JSModuleDef * m) {
return 0; return 0;
} }
static JSValue js_loadImage(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) {
const char * fileName = JS_ToCString(ctx, argv[0]);
if(fileName == NULL) return JS_EXCEPTION;
Image returnVal = LoadImage(fileName);
JS_FreeCString(ctx, fileName);
Image* ptr = (Image*)js_malloc(ctx, sizeof(Image));
*ptr = returnVal;
JSValue ret = JS_NewObjectClass(ctx, js_Image_class_id);
JS_SetOpaque(ret, ptr);
return ret;
}
static const JSCFunctionListEntry js_raylib_texture_funcs[] = { static const JSCFunctionListEntry js_raylib_texture_funcs[] = {
JS_CFUNC_DEF("loadImage",1,js_loadImage),
}; };
static int js_raylib_texture_init(JSContext * ctx, JSModuleDef * m) { static int js_raylib_texture_init(JSContext * ctx, JSModuleDef * m) {

2
thirdparty/raylib vendored

@ -1 +1 @@
Subproject commit 5573f0f1c7b29bfe46d0b70487e4adb4d01cba62 Subproject commit a48bb6e1ed7b33190e486ba65b7875f0dff73701