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)
}
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();
sub.setTag("_type", "function-body")
sub.setTag("_name", name)

View File

@ -15,7 +15,7 @@ function main(){
core_gen.writeTo("src/bindings/js_raylib_core.h")
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")
}

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){
const args = [
@ -69,16 +69,27 @@ export abstract class GenericQuickJsGenerator<T extends CodeGenerator> extends G
return sub
}
jsReadParameter(type: string, name: string, index: number){
jsToC(type: string, name: string, src: string){
this.inline(`${type} ${name}`)
switch (type) {
case "const char *":
this.statement(` = JS_ToCString(ctx, argv[${index}])`)
this.statement(` = JS_ToCString(ctx, ${src})`)
this.statement(`if(${name} == NULL) return JS_EXCEPTION`)
break;
case "int":
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;
default:
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){
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){
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")
})
fun.declare(field, type, false, "ptr->"+field)
// TODO: to JS
fun.jsToJs(type, "ret", field)
fun.returnExp("ret")
return fun
}
}

View File

@ -1,9 +1,9 @@
import { ApiDescription, ApiFunction, ApiStruct } from "./api"
import { CodeGenerator } from "./generation"
import { QuickJsHeader } from "./quickjs"
export interface StructBindingOptions {
getters?: string[]
setters?: string[]
properties?: { [key:string]: { get?:boolean, set?:boolean } }
}
@ -21,7 +21,7 @@ export class RayLibHeader extends QuickJsHeader {
// read parameters
for (let i = 0; i < api.params.length; i++) {
const para = api.params[i]
fun.jsReadParameter(para.type,para.name,i)
fun.jsToC(para.type,para.name,"argv["+i+"]")
}
// call c function
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"){
fun.statement("return JS_UNDEFINED")
} else {
// TODO: Convert to JS
fun.statement("return retVal")
fun.jsToJs(api.returnType, "ret", "returnVal")
fun.returnExp("returnVal")
}
// 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 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`)
classFuncList.child(propDeclarations)
classFuncList.jsPropStringDef("[Symbol.toStringTag]", "Image")
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);
return sub;
}
jsReadParameter(type, name, index) {
jsToC(type, name, src) {
this.inline(`${type} ${name}`);
switch (type) {
case "const char *":
this.statement(` = JS_ToCString(ctx, argv[${index}])`);
this.statement(` = JS_ToCString(ctx, ${src})`);
this.statement(`if(${name} == NULL) return JS_EXCEPTION`);
break;
case "int":
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;
default:
throw new Error("Cannot handle parameter type: " + type);
@ -378,6 +388,9 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
jsPropStringDef(key, value) {
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) {
const args = [{ type: "JSRuntime *", name: "rt" }, { type: "JSValue", name: "val" }];
const body = this.function(`js_${structName}_finalizer`, "void", args, true);
@ -410,8 +423,9 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
cond.returnExp("JS_EXCEPTION");
});
fun.declare(field, type, false, "ptr->" + field);
// TODO: to JS
fun.jsToJs(type, "ret", field);
fun.returnExp("ret");
return fun;
}
}
exports.GenericQuickJsGenerator = GenericQuickJsGenerator;
@ -447,7 +461,7 @@ class RayLibHeader extends quickjs_1.QuickJsHeader {
// read parameters
for (let i = 0; i < api.params.length; i++) {
const para = api.params[i];
fun.jsReadParameter(para.type, para.name, i);
fun.jsToC(para.type, para.name, "argv[" + i + "]");
}
// call c function
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");
}
else {
// TODO: Convert to JS
fun.statement("return retVal");
fun.jsToJs(api.returnType, "ret", "returnVal");
fun.returnExp("returnVal");
}
// add binding to function declaration
this.moduleFunctionList.jsFuncDef(jName, api.argc, fun.getTag("_name"));
@ -475,8 +489,22 @@ class RayLibHeader extends quickjs_1.QuickJsHeader {
addApiStruct(struct, destructor, options) {
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]));
//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`);
classFuncList.child(propDeclarations);
classFuncList.jsPropStringDef("[Symbol.toStringTag]", "Image");
const classDecl = this.structs.jsClassDeclaration(struct.name, classId, finalizer.getTag("_name"), classFuncList.getTag("_name"));
this.moduleInit.call(classDecl.getTag("_name"), ["ctx", "m"]);
@ -559,7 +587,7 @@ function main() {
core_gen.addApiFunctionByName("EndDrawing");
core_gen.writeTo("src/bindings/js_raylib_core.h");
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");
}
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[] = {
JS_CGETSET_DEF("width",js_Image_get_width,NULL),
JS_PROP_STRING_DEF("[Symbol.toStringTag]","Image", JS_PROP_CONFIGURABLE),
};