Add getter generation

This commit is contained in:
Alexander Klingenbeck (SHS DI SY R&D DEV4) 2023-05-08 18:05:03 +02:00
parent d9f10bed4f
commit fd97c55510
6 changed files with 91 additions and 22 deletions

View File

@ -150,7 +150,7 @@ export abstract class GenericCodeGenerator<T extends CodeGenerator> {
this.tokens.push(Token.UNINDENT) this.tokens.push(Token.UNINDENT)
} }
public function(name: string, returnType: string, args: FunctionArgument[], isStatic: boolean, func?: (gen: T) => void){ public function(name: string, returnType: string, args: FunctionArgument[], isStatic: boolean, func?: (gen: T) => void): T {
const sub = this.createGenerator(); const sub = this.createGenerator();
sub.setTag("_type", "function-body") sub.setTag("_type", "function-body")
sub.setTag("_name", name) sub.setTag("_name", name)

View File

@ -15,7 +15,7 @@ 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") texture_gen.addApiStructByName("Image", "UnloadImage", { properties: { width: { get: true } } })
texture_gen.writeTo("src/bindings/js_raylib_texture.h") texture_gen.writeTo("src/bindings/js_raylib_texture.h")
} }

View File

@ -55,7 +55,7 @@ export class QuickJsHeader {
} }
} }
export abstract class GenericQuickJsGenerator<T extends CodeGenerator> extends GenericCodeGenerator<T> { export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extends GenericCodeGenerator<T> {
jsBindingFunction(jsName: string){ jsBindingFunction(jsName: string){
const args = [ const args = [
@ -69,16 +69,27 @@ export abstract class GenericQuickJsGenerator<T extends CodeGenerator> extends G
return sub return sub
} }
jsReadParameter(type: string, name: string, index: number){ jsToC(type: string, name: string, src: string){
this.inline(`${type} ${name}`) this.inline(`${type} ${name}`)
switch (type) { switch (type) {
case "const char *": case "const char *":
this.statement(` = JS_ToCString(ctx, argv[${index}])`) this.statement(` = 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('')
this.statement(`JS_ToInt32(ctx, &${name}, argv[${index}])`) this.statement(`JS_ToInt32(ctx, &${name}, ${src})`)
break;
default:
throw new Error("Cannot handle parameter type: " + type)
}
}
jsToJs(type: string, name: string, src: string){
this.inline(`JSValue ${name}`)
switch (type) {
case "int":
this.statement(` = JS_NewInt32(ctx, ${src})`)
break; break;
default: default:
throw new Error("Cannot handle parameter type: " + type) throw new Error("Cannot handle parameter type: " + type)
@ -120,6 +131,10 @@ export abstract class GenericQuickJsGenerator<T extends CodeGenerator> extends G
jsPropStringDef(key: string, value: string){ jsPropStringDef(key: string, value: string){
this.line(`JS_PROP_STRING_DEF("${key}","${value}", JS_PROP_CONFIGURABLE),`) this.line(`JS_PROP_STRING_DEF("${key}","${value}", JS_PROP_CONFIGURABLE),`)
} }
jsGetSetDef(key: string, getFunc?: string, setFunc?: string){
this.line(`JS_CGETSET_DEF("${key}",${getFunc || "NULL"},${setFunc || "NULL"}),`)
}
jsStructFinalizer(classId: string, structName: string, onFinalize?: (gen: T, ptrName: string) => void){ jsStructFinalizer(classId: string, structName: string, onFinalize?: (gen: T, ptrName: string) => void){
const args = [{type: "JSRuntime *", name: "rt"},{type: "JSValue", name: "val"}] const args = [{type: "JSRuntime *", name: "rt"},{type: "JSValue", name: "val"}]
@ -154,8 +169,9 @@ export abstract class GenericQuickJsGenerator<T extends CodeGenerator> extends G
cond.returnExp("JS_EXCEPTION") cond.returnExp("JS_EXCEPTION")
}) })
fun.declare(field, type, false, "ptr->"+field) fun.declare(field, type, false, "ptr->"+field)
// TODO: to JS fun.jsToJs(type, "ret", field)
fun.returnExp("ret") fun.returnExp("ret")
return fun
} }
} }

View File

@ -1,9 +1,9 @@
import { ApiDescription, ApiFunction, ApiStruct } from "./api" import { ApiDescription, ApiFunction, ApiStruct } from "./api"
import { CodeGenerator } from "./generation"
import { QuickJsHeader } from "./quickjs" import { QuickJsHeader } from "./quickjs"
export interface StructBindingOptions { export interface StructBindingOptions {
getters?: string[] properties?: { [key:string]: { get?:boolean, set?:boolean } }
setters?: string[]
} }
@ -21,7 +21,7 @@ export class RayLibHeader extends QuickJsHeader {
// read parameters // read parameters
for (let i = 0; i < api.params.length; i++) { for (let i = 0; i < api.params.length; i++) {
const para = api.params[i] const para = api.params[i]
fun.jsReadParameter(para.type,para.name,i) fun.jsToC(para.type,para.name,"argv["+i+"]")
} }
// call c function // call c function
fun.call(api.name, api.params.map(x => x.name), api.returnType === "void" ? null : {type: api.returnType, name: "returnVal"}) fun.call(api.name, api.params.map(x => x.name), api.returnType === "void" ? null : {type: api.returnType, name: "returnVal"})
@ -33,8 +33,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 {
// TODO: Convert to JS fun.jsToJs(api.returnType, "ret", "returnVal")
fun.statement("return retVal") fun.returnExp("returnVal")
} }
// add binding to function declaration // add binding to function declaration
@ -51,8 +51,22 @@ export class RayLibHeader extends QuickJsHeader {
const classId = this.declarations.jsClassId(`js_${struct.name}_class_id`) const classId = this.declarations.jsClassId(`js_${struct.name}_class_id`)
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]))
//TODO: declareGetterSetter()
const propDeclarations = this.structs.createGenerator()
if(options && options.properties){
for (const field of Object.keys(options.properties)) {
const type = struct.fields.find(x => x.name === field)?.type
if(!type) throw new Error(`Struct ${struct.name} does not contain field ${field}`)
const el = options.properties[field]
let _get: CodeGenerator | undefined = undefined
let _set: CodeGenerator | undefined = undefined
if(el.get) _get = this.structs.jsStructGetter(struct.name, classId, field, type)
propDeclarations.jsGetSetDef(field, _get?.getTag("_name"), undefined)
}
}
const classFuncList = this.structs.jsFunctionList(`js_${struct.name}_proto_funcs`) const classFuncList = this.structs.jsFunctionList(`js_${struct.name}_proto_funcs`)
classFuncList.child(propDeclarations)
classFuncList.jsPropStringDef("[Symbol.toStringTag]", "Image") classFuncList.jsPropStringDef("[Symbol.toStringTag]", "Image")
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"))

View File

@ -332,16 +332,26 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
const sub = this.function("js_" + jsName, "JSValue", args, true); const sub = this.function("js_" + jsName, "JSValue", args, true);
return sub; return sub;
} }
jsReadParameter(type, name, index) { jsToC(type, name, src) {
this.inline(`${type} ${name}`); this.inline(`${type} ${name}`);
switch (type) { switch (type) {
case "const char *": case "const char *":
this.statement(` = JS_ToCString(ctx, argv[${index}])`); this.statement(` = 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('');
this.statement(`JS_ToInt32(ctx, &${name}, argv[${index}])`); this.statement(`JS_ToInt32(ctx, &${name}, ${src})`);
break;
default:
throw new Error("Cannot handle parameter type: " + type);
}
}
jsToJs(type, name, src) {
this.inline(`JSValue ${name}`);
switch (type) {
case "int":
this.statement(` = JS_NewInt32(ctx, ${src})`);
break; break;
default: default:
throw new Error("Cannot handle parameter type: " + type); throw new Error("Cannot handle parameter type: " + type);
@ -378,6 +388,9 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
jsPropStringDef(key, value) { jsPropStringDef(key, value) {
this.line(`JS_PROP_STRING_DEF("${key}","${value}", JS_PROP_CONFIGURABLE),`); this.line(`JS_PROP_STRING_DEF("${key}","${value}", JS_PROP_CONFIGURABLE),`);
} }
jsGetSetDef(key, getFunc, setFunc) {
this.line(`JS_CGETSET_DEF("${key}",${getFunc || "NULL"},${setFunc || "NULL"}),`);
}
jsStructFinalizer(classId, structName, onFinalize) { jsStructFinalizer(classId, structName, onFinalize) {
const args = [{ type: "JSRuntime *", name: "rt" }, { type: "JSValue", name: "val" }]; const args = [{ type: "JSRuntime *", name: "rt" }, { type: "JSValue", name: "val" }];
const body = this.function(`js_${structName}_finalizer`, "void", args, true); const body = this.function(`js_${structName}_finalizer`, "void", args, true);
@ -410,8 +423,9 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
cond.returnExp("JS_EXCEPTION"); cond.returnExp("JS_EXCEPTION");
}); });
fun.declare(field, type, false, "ptr->" + field); fun.declare(field, type, false, "ptr->" + field);
// TODO: to JS fun.jsToJs(type, "ret", field);
fun.returnExp("ret"); fun.returnExp("ret");
return fun;
} }
} }
exports.GenericQuickJsGenerator = GenericQuickJsGenerator; exports.GenericQuickJsGenerator = GenericQuickJsGenerator;
@ -447,7 +461,7 @@ class RayLibHeader extends quickjs_1.QuickJsHeader {
// read parameters // read parameters
for (let i = 0; i < api.params.length; i++) { for (let i = 0; i < api.params.length; i++) {
const para = api.params[i]; const para = api.params[i];
fun.jsReadParameter(para.type, para.name, i); fun.jsToC(para.type, para.name, "argv[" + i + "]");
} }
// call c function // call c function
fun.call(api.name, api.params.map(x => x.name), api.returnType === "void" ? null : { type: api.returnType, name: "returnVal" }); fun.call(api.name, api.params.map(x => x.name), api.returnType === "void" ? null : { type: api.returnType, name: "returnVal" });
@ -460,8 +474,8 @@ class RayLibHeader extends quickjs_1.QuickJsHeader {
fun.statement("return JS_UNDEFINED"); fun.statement("return JS_UNDEFINED");
} }
else { else {
// TODO: Convert to JS fun.jsToJs(api.returnType, "ret", "returnVal");
fun.statement("return retVal"); fun.returnExp("returnVal");
} }
// 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"));
@ -475,8 +489,22 @@ 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`);
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]));
//TODO: declareGetterSetter() const propDeclarations = this.structs.createGenerator();
if (options && options.properties) {
for (const field of Object.keys(options.properties)) {
const type = struct.fields.find(x => x.name === field)?.type;
if (!type)
throw new Error(`Struct ${struct.name} does not contain field ${field}`);
const el = options.properties[field];
let _get = undefined;
let _set = undefined;
if (el.get)
_get = this.structs.jsStructGetter(struct.name, classId, field, type);
propDeclarations.jsGetSetDef(field, _get?.getTag("_name"), undefined);
}
}
const classFuncList = this.structs.jsFunctionList(`js_${struct.name}_proto_funcs`); const classFuncList = this.structs.jsFunctionList(`js_${struct.name}_proto_funcs`);
classFuncList.child(propDeclarations);
classFuncList.jsPropStringDef("[Symbol.toStringTag]", "Image"); classFuncList.jsPropStringDef("[Symbol.toStringTag]", "Image");
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"]);
@ -559,7 +587,7 @@ function main() {
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"); texture_gen.addApiStructByName("Image", "UnloadImage", { properties: { width: { get: true } } });
texture_gen.writeTo("src/bindings/js_raylib_texture.h"); texture_gen.writeTo("src/bindings/js_raylib_texture.h");
} }
main(); main();

View File

@ -23,7 +23,18 @@ static void js_Image_finalizer(JSRuntime * rt, JSValue val) {
} }
} }
static JSValue js_Image_get_width(JSContext* ctx, JSValueConst this_val) {
Image* ptr = JS_GetOpaque2(ctx, this_val, js_Image_class_id);
if(!ptr) {
return JS_EXCEPTION;
}
int width = ptr->width;
JSValue ret = JS_NewInt32(ctx, width);
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_PROP_STRING_DEF("[Symbol.toStringTag]","Image", JS_PROP_CONFIGURABLE), JS_PROP_STRING_DEF("[Symbol.toStringTag]","Image", JS_PROP_CONFIGURABLE),
}; };