From d9f10bed4ff91f33c3fb0e91eb0ac098dde9a39f Mon Sep 17 00:00:00 2001 From: "Alexander Klingenbeck (SHS DI SY R&D DEV4)" Date: Mon, 8 May 2023 16:43:50 +0200 Subject: [PATCH] Refactor binding generator --- bindings/src/function.ts | 69 ---- bindings/src/functionList.ts | 24 -- bindings/src/generation.ts | 135 ++++--- bindings/src/header.ts | 102 ------ bindings/src/index.ts | 19 +- bindings/src/quickjs.ts | 167 +++++++++ bindings/src/raylib-header.ts | 73 ++++ bindings/src/struct.ts | 63 ---- generate-bindings.js | 582 +++++++++++++++---------------- src/bindings/js_raylib_core.h | 2 +- src/bindings/js_raylib_texture.h | 2 +- 11 files changed, 602 insertions(+), 636 deletions(-) delete mode 100644 bindings/src/function.ts delete mode 100644 bindings/src/functionList.ts delete mode 100644 bindings/src/header.ts create mode 100644 bindings/src/quickjs.ts create mode 100644 bindings/src/raylib-header.ts delete mode 100644 bindings/src/struct.ts diff --git a/bindings/src/function.ts b/bindings/src/function.ts deleted file mode 100644 index 81dbda9..0000000 --- a/bindings/src/function.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { ApiFunction } from "./api" -import { FunctionGenerator } from "./generation" -import { RayLibParamDescription } from "./interfaces" - -export class RayLibFunctionGenerator extends FunctionGenerator { - constructor(public readonly jsName: string, public readonly func: ApiFunction){ - super("js_"+jsName, "JSValue", [ - {type: "JSContext *", name: "ctx"}, - {type: "JSValueConst", name: "this_val"}, - {type: "int", name: "argc"}, - {type: "JSValueConst *", name: "argv"}, - ], true) - this.readParameters() - this.callFunction() - this.cleanUp() - this.returnValue() - } - - readParameters() { - for (let i = 0; i < this.func.params.length; i++) { - const para = this.func.params[i] - this.readParameter(para,i) - } - } - - callFunction() { - this.body.call(this.func.name, this.func.params.map(x => x.name), this.func.returnType === "void" ? null : {type: this.func.returnType, name: "returnVal"}) - } - - cleanUp() { - for (const param of this.func.params) { - this.cleanUpParameter(param) - } - } - - returnValue() { - if(this.func.returnType === "void"){ - this.body.statement("return JS_UNDEFINED") - } else { - this.body.statement("return retVal") - } - } - - private readParameter(para: RayLibParamDescription, index: number){ - this.body.inline(`${para.type} ${para.name}`) - switch (para.type) { - case "const char *": - this.body.statement(` = JS_ToCString(ctx, argv[${index}])`) - this.body.statement(`if(${para.name} == NULL) return JS_EXCEPTION`) - break; - case "int": - this.body.statement('') - this.body.statement(`JS_ToInt32(ctx, &${para.name}, argv[${index}])`) - break; - default: - throw new Error("Cannot handle parameter type: " + para.type) - } - } - - private cleanUpParameter(param: RayLibParamDescription) { - switch (param.type) { - case "const char *": - this.body.statement(`JS_FreeCString(ctx, ${param.name})`) - break; - default: - break; - } - } -} \ No newline at end of file diff --git a/bindings/src/functionList.ts b/bindings/src/functionList.ts deleted file mode 100644 index 0abb3c5..0000000 --- a/bindings/src/functionList.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { CodeGenerator } from "./generation" - -export class RayLibFunctionListGenerator extends CodeGenerator { - - - private entries = new CodeGenerator() - - constructor(public readonly name: string){ - super() - this.line("static const JSCFunctionListEntry "+ name + "[] = {") - this.indent() - this.child(this.entries) - this.unindent() - this.statement("}") - } - - addFunction(jsName: string, numArgs: number, cName: string){ - this.entries.line(`JS_CFUNC_DEF("${jsName}",${numArgs},${cName}),`) - } - - addPropertyString(key: string, value: string) { - this.entries.line(`JS_PROP_STRING_DEF("${key}","${value}", JS_PROP_CONFIGURABLE),`) - } -} \ No newline at end of file diff --git a/bindings/src/generation.ts b/bindings/src/generation.ts index 176af61..da2a2f6 100644 --- a/bindings/src/generation.ts +++ b/bindings/src/generation.ts @@ -63,11 +63,28 @@ enum Token { GOSUB = 4 } -export class CodeGenerator { +export interface FunctionArgument { + type: string, + name: string +} + + + +export abstract class GenericCodeGenerator { private children: CodeGenerator[] = [] private text: string[] = [] private tokens: Token[] = [] + private tags: {[key:string]:any} = {} + + getTag(key: string){ + return this.tags[key] + } + + setTag(key: string, value: any){ + this.tags[key] = value + } + iterateTokens(){ return this.tokens[Symbol.iterator]() } @@ -79,6 +96,8 @@ export class CodeGenerator { iterateChildren(){ return this.children[Symbol.iterator]() } + + abstract createGenerator(): T; line(text: string){ this.tokens.push(Token.STRING, Token.NEWLINE) @@ -100,22 +119,14 @@ export class CodeGenerator { if(isStatic) this.inline("static ") this.inline(type + " " + name) if(initValue) this.inline(" = " + initValue) - this.statement("") + this.statement("") } - child(sub: CodeGenerator){ + child(sub?: T){ + if(!sub) sub = this.createGenerator() this.tokens.push(Token.GOSUB) this.children.push(sub) - } - - childFunc(sub: CodeGenerator, func: (gen: CodeGenerator) => void){ - this.child(sub) - func(sub) - } - - childFuncBody(sub: FunctionGenerator | ConditionGenerator, func: (gen: CodeGenerator) => void): void { - this.child(sub) - func(sub.body) + return sub } inline(str: string){ @@ -138,56 +149,76 @@ export class CodeGenerator { public unindent(){ this.tokens.push(Token.UNINDENT) } -} -export class ConditionGenerator extends CodeGenerator { - body = new CodeGenerator() - - constructor(condition: string){ - super() - this.line("if("+condition+") {") - this.indent() - this.child(this.body) - this.unindent() - this.line("}") - } -} - -export class HeaderGenerator extends CodeGenerator { - guardStart(name: string){ - this.line("#ifndef " + name) - this.line("#define " + name) - } - guardEnd(name: string){ - this.line("#endif // " + name) - } - include(name: string){ - this.line("#include <" + name + ">") - } -} - -export interface FunctionArgument { - type: string, - name: string -} - -export class CustomFunctionGenerator extends CodeGenerator { - constructor(public readonly name: string, returnType: string, args: FunctionArgument[], public readonly body: T, isStatic = false){ - super() + public function(name: string, returnType: string, args: FunctionArgument[], isStatic: boolean, func?: (gen: T) => void){ + const sub = this.createGenerator(); + sub.setTag("_type", "function-body") + sub.setTag("_name", name) + sub.setTag("_isStatic", isStatic) + sub.setTag("_returnType", returnType) if(isStatic) this.inline("static ") this.inline(returnType + " " + name + "(") this.inline(args.map(x => x.type + " " + x.name).join(", ")) this.inline(") {") this.breakLine() this.indent() - this.child(body) + this.child(sub) this.unindent() this.line("}") + this.breakLine() + if(func) func(sub) + return sub } + + public if(condition: string, funIf?: (gen: T) => void){ + this.line("if("+condition+") {") + this.indent() + const sub = this.createGenerator() + sub.setTag("_type", "if-body") + sub.setTag("_condition", condition) + this.child(sub) + this.unindent() + this.line("}") + if(funIf) funIf(sub) + return sub + } + + public else(funElse?: (gen: T) => void){ + this.line("else {") + this.indent() + const sub = this.createGenerator() + sub.setTag("_type", "else-body") + this.child(sub) + this.unindent() + this.line("}") + if(funElse) funElse(sub) + return sub + } + + public returnExp(exp: string){ + this.statement("return "+exp) + } + + public include(name: string){ + this.line("#include <" + name + ">") + } + + public header(guard: string, fun?: (gen: T) => void){ + this.line("#ifndef " + guard) + this.line("#define " + guard) + this.breakLine() + const sub = this.child() + sub.setTag("_type", "header-body") + sub.setTag("_guardName", guard) + this.line("#endif // " + guard) + if(fun) fun(sub) + return sub + } } -export class FunctionGenerator extends CustomFunctionGenerator { - constructor(name: string, returnType: string, args: FunctionArgument[], isStatic = false, body: CodeGenerator = new CodeGenerator()){ - super(name, returnType, args, body, isStatic) +export class CodeGenerator extends GenericCodeGenerator{ + createGenerator(): CodeGenerator { + return new CodeGenerator() } + } \ No newline at end of file diff --git a/bindings/src/header.ts b/bindings/src/header.ts deleted file mode 100644 index 154b6ad..0000000 --- a/bindings/src/header.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { ApiDescription, ApiFunction, ApiStruct } from "./api" -import { RayLibFunctionGenerator } from "./function" -import { RayLibFunctionListGenerator } from "./functionList" -import { CodeGenerator, FunctionGenerator, HeaderGenerator } from "./generation" -import { RayLibStructGenerator, StructBindingOptions } from "./struct" - -export class RayLibHeaderGenerator extends HeaderGenerator { - - public readonly moduleFunctionList: RayLibFunctionListGenerator - public readonly moduleInit = new CodeGenerator() - public readonly moduleEntry = new CodeGenerator() - public readonly declarations = new CodeGenerator() - public readonly body = new CodeGenerator() - - constructor(public readonly name: string, private api: ApiDescription){ - super() - this.moduleFunctionList = new RayLibFunctionListGenerator("js_"+name+"_funcs") - this.init() - } - - addApiFunction(func: ApiFunction, jsName: string | null = null){ - const jName = jsName || func.name.charAt(0).toLowerCase() + func.name.slice(1) - const gen = new RayLibFunctionGenerator(jName, func) - this.body.child(gen) - this.body.breakLine() - this.moduleFunctionList.addFunction(jName, func.argc, gen.name) - } - - addApiFunctionByName(name: string, jsName: string | null = null){ - const func = this.api.getFunction(name) - if(func === null) throw new Error("Function not in API: " + name) - this.addApiFunction(func, jsName) - } - - addApiStruct(struct: ApiStruct, destructor: ApiFunction | null, options?: StructBindingOptions){ - const classIdName = `js_${struct.name}_class_id` - this.declarations.declare(classIdName, "JSClassID", true) - - const gen = new RayLibStructGenerator(classIdName, struct, destructor, options) - this.body.child(gen) - - this.moduleInit.call(gen.classDeclarationName, ["ctx", "m"]) - // OPT: 7. expose class and constructor - } - - addApiStructByName(structName: string, destructorName: string | null = null, options?: StructBindingOptions){ - const struct = this.api.getStruct(structName) - if(!struct) throw new Error("Struct not in API: "+ structName) - let destructor: ApiFunction | null = null - if(destructorName !== null){ - destructor = this.api.getFunction(destructorName) - if(!destructor) throw new Error("Destructor func not in API: "+ destructorName) - } - this.addApiStruct(struct, destructor, options) - } - - private init(){ - const guardName = "JS_"+this.name+"_GUARD"; - this.guardStart(guardName) - this.breakLine() - this.include("stdio.h") - this.include("stdlib.h") - this.include("string.h") - this.breakLine() - this.include("quickjs.h") - this.include("raylib.h") - this.breakLine() - this.line("#ifndef countof") - this.line("#define countof(x) (sizeof(x) / sizeof((x)[0]))") - this.line("#endif") - this.breakLine() - this.child(this.declarations) - this.breakLine() - this.child(this.body) - this.child(this.moduleFunctionList) - this.breakLine() - - const moduleInitFunc = new FunctionGenerator("js_"+this.name+"_init", "int", [ - {type: "JSContext *", name: "ctx"}, - {type: "JSModuleDef *", name: "m"}]) - moduleInitFunc.body.statement(`JS_SetModuleExportList(ctx, m,${this.moduleFunctionList.name},countof(${this.moduleFunctionList.name}))`) - moduleInitFunc.body.child(this.moduleInit) - moduleInitFunc.body.statement("return 0") - this.child(moduleInitFunc) - this.breakLine() - - const moduleEntryFunc = new FunctionGenerator("js_init_module_"+this.name, "JSModuleDef *", [ - {type: "JSContext *", name: "ctx"}, - {type: "const char *", name: "module_name"} - ]) - moduleEntryFunc.body.statement("JSModuleDef *m") - moduleEntryFunc.body.statement(`m = JS_NewCModule(ctx, module_name, ${moduleInitFunc.name})`) - moduleEntryFunc.body.statement("if(!m) return NULL") - moduleEntryFunc.body.statement(`JS_AddModuleExportList(ctx, m, ${this.moduleFunctionList.name}, countof(${this.moduleFunctionList.name}))`) - moduleEntryFunc.body.child(this.moduleEntry) - moduleEntryFunc.body.statement("return m") - this.child(moduleEntryFunc) - this.breakLine() - - this.guardEnd(guardName) - } -} \ No newline at end of file diff --git a/bindings/src/index.ts b/bindings/src/index.ts index dcc2d3b..ea2cfe7 100644 --- a/bindings/src/index.ts +++ b/bindings/src/index.ts @@ -1,31 +1,22 @@ import { readFileSync, writeFileSync } from "fs"; import { Bindings, RayLibApi } from "./interfaces"; -import { CodeWriter, HeaderGenerator } from "./generation"; import { ApiDescription } from "./api"; -import { RayLibHeaderGenerator } from "./header"; - -const bindings = JSON.parse(readFileSync("bindings.json", 'utf8')) - -function writeHeader(header: HeaderGenerator, filename: string){ - const writer = new CodeWriter() - writer.writeGenerator(header) - writeFileSync(filename, writer.toString()) -} +import { RayLibHeader } from "./raylib-header"; function main(){ const api = JSON.parse(readFileSync("thirdparty/raylib/parser/output/raylib_api.json", 'utf8')) const apiDesc = new ApiDescription(api) - const core_gen = new RayLibHeaderGenerator("raylib_core", apiDesc) + const core_gen = new RayLibHeader("raylib_core", apiDesc) core_gen.addApiFunctionByName("SetWindowTitle") core_gen.addApiFunctionByName("SetWindowPosition") core_gen.addApiFunctionByName("BeginDrawing") core_gen.addApiFunctionByName("EndDrawing") - writeHeader(core_gen, "src/bindings/js_raylib_core.h") + core_gen.writeTo("src/bindings/js_raylib_core.h") - const texture_gen = new RayLibHeaderGenerator("raylib_texture", apiDesc) + const texture_gen = new RayLibHeader("raylib_texture", apiDesc) texture_gen.addApiStructByName("Image", "UnloadImage") - writeHeader(texture_gen, "src/bindings/js_raylib_texture.h") + texture_gen.writeTo("src/bindings/js_raylib_texture.h") } main() diff --git a/bindings/src/quickjs.ts b/bindings/src/quickjs.ts new file mode 100644 index 0000000..b723b1d --- /dev/null +++ b/bindings/src/quickjs.ts @@ -0,0 +1,167 @@ +import { writeFileSync } from "fs"; +import { ApiFunction } from "./api" +import { CodeGenerator, CodeWriter, GenericCodeGenerator } from "./generation" + +export class QuickJsHeader { + + public readonly moduleFunctionList: QuickJsGenerator + public readonly structs: QuickJsGenerator + public readonly functions: QuickJsGenerator + public readonly moduleInit: QuickJsGenerator + public readonly moduleEntry: QuickJsGenerator + public readonly declarations: QuickJsGenerator + public readonly body: QuickJsGenerator + public readonly includes: QuickJsGenerator + private readonly root: QuickJsGenerator + + constructor(private name: string){ + const root = this.root = new QuickJsGenerator() + const body = this.body = root.header("JS_"+this.name+"_GUARD") + const includes = this.includes = body.child() + includes.include("stdio.h") + includes.include("stdlib.h") + includes.include("string.h") + includes.breakLine() + includes.include("quickjs.h") + body.breakLine() + body.line("#ifndef countof") + body.line("#define countof(x) (sizeof(x) / sizeof((x)[0]))") + body.line("#endif") + body.breakLine() + this.declarations = body.child() + body.breakLine() + this.structs = body.child() + this.functions = body.child() + this.moduleFunctionList = body.jsFunctionList("js_"+name+"_funcs") + + const moduleInitFunc = body.function("js_"+this.name+"_init", "int", [{type: "JSContext *", name: "ctx"},{type: "JSModuleDef *", name: "m"}], true) + const moduleInit = this.moduleInit = moduleInitFunc.child() + moduleInit.statement(`JS_SetModuleExportList(ctx, m,${this.moduleFunctionList.getTag("_name")},countof(${this.moduleFunctionList.getTag("_name")}))`) + moduleInitFunc.returnExp("0") + + const moduleEntryFunc = body.function("js_init_module_"+this.name, "JSModuleDef *", [{type: "JSContext *", name: "ctx"},{type: "const char *", name: "module_name"}], false) + const moduleEntry = this.moduleEntry = moduleEntryFunc.child() + moduleEntry.statement("JSModuleDef *m") + moduleEntry.statement(`m = JS_NewCModule(ctx, module_name, ${moduleInitFunc.getTag("_name")})`) + moduleEntry.statement("if(!m) return NULL") + moduleEntry.statement(`JS_AddModuleExportList(ctx, m, ${this.moduleFunctionList.getTag("_name")}, countof(${this.moduleFunctionList.getTag("_name")}))`) + moduleEntryFunc.statement("return m") + } + + public writeTo(filename: string){ + const writer = new CodeWriter() + writer.writeGenerator(this.root) + writeFileSync(filename, writer.toString()) + } +} + +export abstract class GenericQuickJsGenerator extends GenericCodeGenerator { + + jsBindingFunction(jsName: string){ + const args = [ + {type: "JSContext *", name: "ctx"}, + {type: "JSValueConst", name: "this_val"}, + {type: "int", name: "argc"}, + {type: "JSValueConst *", name: "argv"}, + ] + const sub = this.function("js_"+jsName, "JSValue", args, true) + + return sub + } + + jsReadParameter(type: string, name: string, index: number){ + this.inline(`${type} ${name}`) + switch (type) { + case "const char *": + this.statement(` = JS_ToCString(ctx, argv[${index}])`) + this.statement(`if(${name} == NULL) return JS_EXCEPTION`) + break; + case "int": + this.statement('') + this.statement(`JS_ToInt32(ctx, &${name}, argv[${index}])`) + break; + default: + throw new Error("Cannot handle parameter type: " + type) + } + } + + jsCleanUpParameter(type: string, name: string) { + switch (type) { + case "const char *": + this.statement(`JS_FreeCString(ctx, ${name})`) + break; + default: + break; + } + } + + jsFunctionList(name: string){ + this.line("static const JSCFunctionListEntry "+ name + "[] = {") + this.indent() + const sub = this.createGenerator() + sub.setTag("_type", "js-function-list") + sub.setTag("_name", name) + this.child(sub) + this.unindent() + this.statement("}") + this.breakLine() + return sub + } + + jsFuncDef(jsName: string, numArgs: number, cName: string){ + this.line(`JS_CFUNC_DEF("${jsName}",${numArgs},${cName}),`) + } + + jsClassId(id: string){ + this.declare(id, "JSClassID", true) + return id + } + + jsPropStringDef(key: string, value: string){ + this.line(`JS_PROP_STRING_DEF("${key}","${value}", JS_PROP_CONFIGURABLE),`) + } + + jsStructFinalizer(classId: string, structName: string, onFinalize?: (gen: T, ptrName: string) => void){ + const args = [{type: "JSRuntime *", name: "rt"},{type: "JSValue", name: "val"}] + const body = this.function(`js_${structName}_finalizer`, "void", args, true) + body.statement(`${structName}* ptr = JS_GetOpaque(val, ${classId})`) + body.if("ptr", cond => { + cond.call("puts", ["\"Finalize "+structName+"\""]) + if(onFinalize) onFinalize(cond, "ptr") + cond.call("js_free_rt", ["rt","ptr"]) + }) + return body + } + + jsClassDeclaration(structName: string, classId: string, finalizerName: string, funcListName: string){ + const body = this.function("js_declare_" + structName, "int", [{type: "JSContext *", name: "ctx"},{type: "JSModuleDef *", name: "m"}],true) + body.call("JS_NewClassID", ["&"+classId]) + const classDefName = `js_${structName}_def` + body.declare(classDefName, "JSClassDef", false, `{ .class_name = "${structName}", .finalizer = ${finalizerName} }`) + body.call("JS_NewClass", ["JS_GetRuntime(ctx)",classId,"&"+classDefName]) + body.declare("proto", "JSValue", false, "JS_NewObject(ctx)") + body.call("JS_SetPropertyFunctionList", ["ctx", "proto", funcListName, `countof(${funcListName})`]) + body.call("JS_SetClassProto", ["ctx",classId,"proto"]) + body.statement("return 0") + return body + } + + jsStructGetter(structName: string, classId: string, field: string, type: string){ + const args = [{type: "JSContext*", name: "ctx" }, {type: "JSValueConst", name: "this_val"}] + const fun = this.function(`js_${structName}_get_${field}`,"JSValue",args,true) + fun.declare("ptr", structName+"*", false, `JS_GetOpaque2(ctx, this_val, ${classId})`) + fun.if("!ptr", cond => { + cond.returnExp("JS_EXCEPTION") + }) + fun.declare(field, type, false, "ptr->"+field) + // TODO: to JS + fun.returnExp("ret") + } +} + +export class QuickJsGenerator extends GenericQuickJsGenerator { + createGenerator(): QuickJsGenerator { + return new QuickJsGenerator() + } +} + diff --git a/bindings/src/raylib-header.ts b/bindings/src/raylib-header.ts new file mode 100644 index 0000000..e95e64e --- /dev/null +++ b/bindings/src/raylib-header.ts @@ -0,0 +1,73 @@ +import { ApiDescription, ApiFunction, ApiStruct } from "./api" +import { QuickJsHeader } from "./quickjs" + +export interface StructBindingOptions { + getters?: string[] + setters?: string[] +} + + +export class RayLibHeader extends QuickJsHeader { + + constructor(name: string, private api: ApiDescription){ + super(name) + this.includes.include("raylib.h") + } + + addApiFunction(api: ApiFunction, jsName: string | null = null){ + const jName = jsName || api.name.charAt(0).toLowerCase() + api.name.slice(1) + + const fun = this.functions.jsBindingFunction(jName) + // read parameters + for (let i = 0; i < api.params.length; i++) { + const para = api.params[i] + fun.jsReadParameter(para.type,para.name,i) + } + // call c function + fun.call(api.name, api.params.map(x => x.name), api.returnType === "void" ? null : {type: api.returnType, name: "returnVal"}) + // clean up parameters + for (const param of api.params) { + fun.jsCleanUpParameter(param.type, param.name) + } + // return result + if(api.returnType === "void"){ + fun.statement("return JS_UNDEFINED") + } else { + // TODO: Convert to JS + fun.statement("return retVal") + } + + // add binding to function declaration + this.moduleFunctionList.jsFuncDef(jName, api.argc, fun.getTag("_name")) + } + + addApiFunctionByName(name: string, jsName: string | null = null){ + const func = this.api.getFunction(name) + if(func === null) throw new Error("Function not in API: " + name) + this.addApiFunction(func, jsName) + } + + addApiStruct(struct: ApiStruct, destructor: ApiFunction | null, options?: StructBindingOptions){ + 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 classFuncList = this.structs.jsFunctionList(`js_${struct.name}_proto_funcs`) + 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"]) + // OPT: 7. expose class and constructor + } + + addApiStructByName(structName: string, destructorName: string | null = null, options?: StructBindingOptions){ + const struct = this.api.getStruct(structName) + if(!struct) throw new Error("Struct not in API: "+ structName) + let destructor: ApiFunction | null = null + if(destructorName !== null){ + destructor = this.api.getFunction(destructorName) + if(!destructor) throw new Error("Destructor func not in API: "+ destructorName) + } + this.addApiStruct(struct, destructor, options) + } +} \ No newline at end of file diff --git a/bindings/src/struct.ts b/bindings/src/struct.ts deleted file mode 100644 index 50be1c9..0000000 --- a/bindings/src/struct.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { ApiStruct, ApiFunction } from "./api"; -import { RayLibFunctionListGenerator } from "./functionList"; -import { CodeGenerator, ConditionGenerator, FunctionGenerator } from "./generation"; - -export interface StructBindingOptions { - getters?: string[] - setters?: string[] -} - -export class RayLibStructGenerator extends CodeGenerator { - - private readonly options: StructBindingOptions; - private readonly funcList: RayLibFunctionListGenerator - public finalizerName = "" - public classDeclarationName = "" - - constructor(public readonly classId: string, private struct: ApiStruct, private destructor: ApiFunction | null, options?: StructBindingOptions){ - super() - this.options = options || {} - this.funcList = new RayLibFunctionListGenerator(`js_${struct.name}_proto_funcs`) - - this.declareFinalizer() - this.breakLine() - this.declareGetterSetter() - this.funcList.addPropertyString("[Symbol.toStringTag]", "Image") - this.child(this.funcList) - this.breakLine() - this.buildClassDeclaration() - this.breakLine() - } - - private declareFinalizer(){ - this.finalizerName = `js_${this.struct.name}_finalizer` - this.childFuncBody(new FunctionGenerator(this.finalizerName, "void", [ - {type: "JSRuntime *", name: "rt"}, - {type: "JSValue", name: "val"}], true), body => { - body.statement(`${this.struct.name}* ptr = JS_GetOpaque(val, ${this.classId})`) - body.childFuncBody(new ConditionGenerator("ptr"), cond => { - cond.call("puts", ["\"Finalize "+this.struct.name+"\""]) - if(this.destructor) cond.call(this.destructor.name, ["*ptr"]) - cond.call("js_free_rt", ["rt","ptr"]) - }) - }) - } - - private declareGetterSetter(){ - // Add to funList - } - - private buildClassDeclaration(){ - this.classDeclarationName = "js_declare_" + this.struct.name - this.childFuncBody(new FunctionGenerator(this.classDeclarationName, "int", [{type: "JSContext *", name: "ctx"},{type: "JSModuleDef *", name: "m"}],true), body => { - body.call("JS_NewClassID", ["&"+this.classId]) - const classDefName = `js_${this.struct.name}_def` - body.declare(classDefName, "JSClassDef", false, `{ .class_name = "${this.struct.name}", .finalizer = ${this.finalizerName} }`) - body.call("JS_NewClass", ["JS_GetRuntime(ctx)",this.classId,"&"+classDefName]) - body.declare("proto", "JSValue", false, "JS_NewObject(ctx)") - body.call("JS_SetPropertyFunctionList", ["ctx", "proto", this.funcList.name, `countof(${this.funcList.name})`]) - body.call("JS_SetClassProto", ["ctx",this.classId,"proto"]) - body.statement("return 0") - }) - } -} \ No newline at end of file diff --git a/generate-bindings.js b/generate-bindings.js index 623084f..7664aea 100644 --- a/generate-bindings.js +++ b/generate-bindings.js @@ -50,116 +50,6 @@ class ApiDescription { exports.ApiDescription = ApiDescription; -/***/ }), - -/***/ "./src/function.ts": -/*!*************************!*\ - !*** ./src/function.ts ***! - \*************************/ -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.RayLibFunctionGenerator = void 0; -const generation_1 = __webpack_require__(/*! ./generation */ "./src/generation.ts"); -class RayLibFunctionGenerator extends generation_1.FunctionGenerator { - constructor(jsName, func) { - super("js_" + jsName, "JSValue", [ - { type: "JSContext *", name: "ctx" }, - { type: "JSValueConst", name: "this_val" }, - { type: "int", name: "argc" }, - { type: "JSValueConst *", name: "argv" }, - ], true); - this.jsName = jsName; - this.func = func; - this.readParameters(); - this.callFunction(); - this.cleanUp(); - this.returnValue(); - } - readParameters() { - for (let i = 0; i < this.func.params.length; i++) { - const para = this.func.params[i]; - this.readParameter(para, i); - } - } - callFunction() { - this.body.call(this.func.name, this.func.params.map(x => x.name), this.func.returnType === "void" ? null : { type: this.func.returnType, name: "returnVal" }); - } - cleanUp() { - for (const param of this.func.params) { - this.cleanUpParameter(param); - } - } - returnValue() { - if (this.func.returnType === "void") { - this.body.statement("return JS_UNDEFINED"); - } - else { - this.body.statement("return retVal"); - } - } - readParameter(para, index) { - this.body.inline(`${para.type} ${para.name}`); - switch (para.type) { - case "const char *": - this.body.statement(` = JS_ToCString(ctx, argv[${index}])`); - this.body.statement(`if(${para.name} == NULL) return JS_EXCEPTION`); - break; - case "int": - this.body.statement(''); - this.body.statement(`JS_ToInt32(ctx, &${para.name}, argv[${index}])`); - break; - default: - throw new Error("Cannot handle parameter type: " + para.type); - } - } - cleanUpParameter(param) { - switch (param.type) { - case "const char *": - this.body.statement(`JS_FreeCString(ctx, ${param.name})`); - break; - default: - break; - } - } -} -exports.RayLibFunctionGenerator = RayLibFunctionGenerator; - - -/***/ }), - -/***/ "./src/functionList.ts": -/*!*****************************!*\ - !*** ./src/functionList.ts ***! - \*****************************/ -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.RayLibFunctionListGenerator = void 0; -const generation_1 = __webpack_require__(/*! ./generation */ "./src/generation.ts"); -class RayLibFunctionListGenerator extends generation_1.CodeGenerator { - constructor(name) { - super(); - this.name = name; - this.entries = new generation_1.CodeGenerator(); - this.line("static const JSCFunctionListEntry " + name + "[] = {"); - this.indent(); - this.child(this.entries); - this.unindent(); - this.statement("}"); - } - addFunction(jsName, numArgs, cName) { - this.entries.line(`JS_CFUNC_DEF("${jsName}",${numArgs},${cName}),`); - } - addPropertyString(key, value) { - this.entries.line(`JS_PROP_STRING_DEF("${key}","${value}", JS_PROP_CONFIGURABLE),`); - } -} -exports.RayLibFunctionListGenerator = RayLibFunctionListGenerator; - - /***/ }), /***/ "./src/generation.ts": @@ -170,7 +60,7 @@ exports.RayLibFunctionListGenerator = RayLibFunctionListGenerator; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.FunctionGenerator = exports.CustomFunctionGenerator = exports.HeaderGenerator = exports.ConditionGenerator = exports.CodeGenerator = exports.CodeWriter = exports.StringWriter = void 0; +exports.CodeGenerator = exports.GenericCodeGenerator = exports.CodeWriter = exports.StringWriter = void 0; class StringWriter { constructor() { this.buffer = ''; @@ -237,11 +127,18 @@ var Token; Token[Token["UNINDENT"] = 3] = "UNINDENT"; Token[Token["GOSUB"] = 4] = "GOSUB"; })(Token || (Token = {})); -class CodeGenerator { +class GenericCodeGenerator { constructor() { this.children = []; this.text = []; this.tokens = []; + this.tags = {}; + } + getTag(key) { + return this.tags[key]; + } + setTag(key, value) { + this.tags[key] = value; } iterateTokens() { return this.tokens[Symbol.iterator](); @@ -275,16 +172,11 @@ class CodeGenerator { this.statement(""); } child(sub) { + if (!sub) + sub = this.createGenerator(); this.tokens.push(Token.GOSUB); this.children.push(sub); - } - childFunc(sub, func) { - this.child(sub); - func(sub); - } - childFuncBody(sub, func) { - this.child(sub); - func(sub.body); + return sub; } inline(str) { this.tokens.push(Token.STRING); @@ -302,38 +194,12 @@ class CodeGenerator { unindent() { this.tokens.push(Token.UNINDENT); } -} -exports.CodeGenerator = CodeGenerator; -class ConditionGenerator extends CodeGenerator { - constructor(condition) { - super(); - this.body = new CodeGenerator(); - this.line("if(" + condition + ") {"); - this.indent(); - this.child(this.body); - this.unindent(); - this.line("}"); - } -} -exports.ConditionGenerator = ConditionGenerator; -class HeaderGenerator extends CodeGenerator { - guardStart(name) { - this.line("#ifndef " + name); - this.line("#define " + name); - } - guardEnd(name) { - this.line("#endif // " + name); - } - include(name) { - this.line("#include <" + name + ">"); - } -} -exports.HeaderGenerator = HeaderGenerator; -class CustomFunctionGenerator extends CodeGenerator { - constructor(name, returnType, args, body, isStatic = false) { - super(); - this.name = name; - this.body = body; + function(name, returnType, args, isStatic, func) { + const sub = this.createGenerator(); + sub.setTag("_type", "function-body"); + sub.setTag("_name", name); + sub.setTag("_isStatic", isStatic); + sub.setTag("_returnType", returnType); if (isStatic) this.inline("static "); this.inline(returnType + " " + name + "("); @@ -341,53 +207,264 @@ class CustomFunctionGenerator extends CodeGenerator { this.inline(") {"); this.breakLine(); this.indent(); - this.child(body); + this.child(sub); this.unindent(); this.line("}"); + this.breakLine(); + if (func) + func(sub); + return sub; + } + if(condition, funIf) { + this.line("if(" + condition + ") {"); + this.indent(); + const sub = this.createGenerator(); + sub.setTag("_type", "if-body"); + sub.setTag("_condition", condition); + this.child(sub); + this.unindent(); + this.line("}"); + if (funIf) + funIf(sub); + return sub; + } + else(funElse) { + this.line("else {"); + this.indent(); + const sub = this.createGenerator(); + sub.setTag("_type", "else-body"); + this.child(sub); + this.unindent(); + this.line("}"); + if (funElse) + funElse(sub); + return sub; + } + returnExp(exp) { + this.statement("return " + exp); + } + include(name) { + this.line("#include <" + name + ">"); + } + header(guard, fun) { + this.line("#ifndef " + guard); + this.line("#define " + guard); + this.breakLine(); + const sub = this.child(); + sub.setTag("_type", "header-body"); + sub.setTag("_guardName", guard); + this.line("#endif // " + guard); + if (fun) + fun(sub); + return sub; } } -exports.CustomFunctionGenerator = CustomFunctionGenerator; -class FunctionGenerator extends CustomFunctionGenerator { - constructor(name, returnType, args, isStatic = false, body = new CodeGenerator()) { - super(name, returnType, args, body, isStatic); +exports.GenericCodeGenerator = GenericCodeGenerator; +class CodeGenerator extends GenericCodeGenerator { + createGenerator() { + return new CodeGenerator(); } } -exports.FunctionGenerator = FunctionGenerator; +exports.CodeGenerator = CodeGenerator; /***/ }), -/***/ "./src/header.ts": -/*!***********************!*\ - !*** ./src/header.ts ***! - \***********************/ +/***/ "./src/quickjs.ts": +/*!************************!*\ + !*** ./src/quickjs.ts ***! + \************************/ /***/ ((__unused_webpack_module, exports, __webpack_require__) => { Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.RayLibHeaderGenerator = void 0; -const function_1 = __webpack_require__(/*! ./function */ "./src/function.ts"); -const functionList_1 = __webpack_require__(/*! ./functionList */ "./src/functionList.ts"); +exports.QuickJsGenerator = exports.GenericQuickJsGenerator = exports.QuickJsHeader = void 0; +const fs_1 = __webpack_require__(/*! fs */ "fs"); const generation_1 = __webpack_require__(/*! ./generation */ "./src/generation.ts"); -const struct_1 = __webpack_require__(/*! ./struct */ "./src/struct.ts"); -class RayLibHeaderGenerator extends generation_1.HeaderGenerator { - constructor(name, api) { - super(); +class QuickJsHeader { + constructor(name) { this.name = name; - this.api = api; - this.moduleInit = new generation_1.CodeGenerator(); - this.moduleEntry = new generation_1.CodeGenerator(); - this.declarations = new generation_1.CodeGenerator(); - this.body = new generation_1.CodeGenerator(); - this.moduleFunctionList = new functionList_1.RayLibFunctionListGenerator("js_" + name + "_funcs"); - this.init(); + const root = this.root = new QuickJsGenerator(); + const body = this.body = root.header("JS_" + this.name + "_GUARD"); + const includes = this.includes = body.child(); + includes.include("stdio.h"); + includes.include("stdlib.h"); + includes.include("string.h"); + includes.breakLine(); + includes.include("quickjs.h"); + body.breakLine(); + body.line("#ifndef countof"); + body.line("#define countof(x) (sizeof(x) / sizeof((x)[0]))"); + body.line("#endif"); + body.breakLine(); + this.declarations = body.child(); + body.breakLine(); + this.structs = body.child(); + this.functions = body.child(); + this.moduleFunctionList = body.jsFunctionList("js_" + name + "_funcs"); + const moduleInitFunc = body.function("js_" + this.name + "_init", "int", [{ type: "JSContext *", name: "ctx" }, { type: "JSModuleDef *", name: "m" }], true); + const moduleInit = this.moduleInit = moduleInitFunc.child(); + moduleInit.statement(`JS_SetModuleExportList(ctx, m,${this.moduleFunctionList.getTag("_name")},countof(${this.moduleFunctionList.getTag("_name")}))`); + moduleInitFunc.returnExp("0"); + const moduleEntryFunc = body.function("js_init_module_" + this.name, "JSModuleDef *", [{ type: "JSContext *", name: "ctx" }, { type: "const char *", name: "module_name" }], false); + const moduleEntry = this.moduleEntry = moduleEntryFunc.child(); + moduleEntry.statement("JSModuleDef *m"); + moduleEntry.statement(`m = JS_NewCModule(ctx, module_name, ${moduleInitFunc.getTag("_name")})`); + moduleEntry.statement("if(!m) return NULL"); + moduleEntry.statement(`JS_AddModuleExportList(ctx, m, ${this.moduleFunctionList.getTag("_name")}, countof(${this.moduleFunctionList.getTag("_name")}))`); + moduleEntryFunc.statement("return m"); } - addApiFunction(func, jsName = null) { - const jName = jsName || func.name.charAt(0).toLowerCase() + func.name.slice(1); - const gen = new function_1.RayLibFunctionGenerator(jName, func); - this.body.child(gen); - this.body.breakLine(); - this.moduleFunctionList.addFunction(jName, func.argc, gen.name); + writeTo(filename) { + const writer = new generation_1.CodeWriter(); + writer.writeGenerator(this.root); + (0, fs_1.writeFileSync)(filename, writer.toString()); + } +} +exports.QuickJsHeader = QuickJsHeader; +class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator { + jsBindingFunction(jsName) { + const args = [ + { type: "JSContext *", name: "ctx" }, + { type: "JSValueConst", name: "this_val" }, + { type: "int", name: "argc" }, + { type: "JSValueConst *", name: "argv" }, + ]; + const sub = this.function("js_" + jsName, "JSValue", args, true); + return sub; + } + jsReadParameter(type, name, index) { + this.inline(`${type} ${name}`); + switch (type) { + case "const char *": + this.statement(` = JS_ToCString(ctx, argv[${index}])`); + this.statement(`if(${name} == NULL) return JS_EXCEPTION`); + break; + case "int": + this.statement(''); + this.statement(`JS_ToInt32(ctx, &${name}, argv[${index}])`); + break; + default: + throw new Error("Cannot handle parameter type: " + type); + } + } + jsCleanUpParameter(type, name) { + switch (type) { + case "const char *": + this.statement(`JS_FreeCString(ctx, ${name})`); + break; + default: + break; + } + } + jsFunctionList(name) { + this.line("static const JSCFunctionListEntry " + name + "[] = {"); + this.indent(); + const sub = this.createGenerator(); + sub.setTag("_type", "js-function-list"); + sub.setTag("_name", name); + this.child(sub); + this.unindent(); + this.statement("}"); + this.breakLine(); + return sub; + } + jsFuncDef(jsName, numArgs, cName) { + this.line(`JS_CFUNC_DEF("${jsName}",${numArgs},${cName}),`); + } + jsClassId(id) { + this.declare(id, "JSClassID", true); + return id; + } + jsPropStringDef(key, value) { + this.line(`JS_PROP_STRING_DEF("${key}","${value}", JS_PROP_CONFIGURABLE),`); + } + jsStructFinalizer(classId, structName, onFinalize) { + const args = [{ type: "JSRuntime *", name: "rt" }, { type: "JSValue", name: "val" }]; + const body = this.function(`js_${structName}_finalizer`, "void", args, true); + body.statement(`${structName}* ptr = JS_GetOpaque(val, ${classId})`); + body.if("ptr", cond => { + cond.call("puts", ["\"Finalize " + structName + "\""]); + if (onFinalize) + onFinalize(cond, "ptr"); + cond.call("js_free_rt", ["rt", "ptr"]); + }); + return body; + } + jsClassDeclaration(structName, classId, finalizerName, funcListName) { + const body = this.function("js_declare_" + structName, "int", [{ type: "JSContext *", name: "ctx" }, { type: "JSModuleDef *", name: "m" }], true); + body.call("JS_NewClassID", ["&" + classId]); + const classDefName = `js_${structName}_def`; + body.declare(classDefName, "JSClassDef", false, `{ .class_name = "${structName}", .finalizer = ${finalizerName} }`); + body.call("JS_NewClass", ["JS_GetRuntime(ctx)", classId, "&" + classDefName]); + body.declare("proto", "JSValue", false, "JS_NewObject(ctx)"); + body.call("JS_SetPropertyFunctionList", ["ctx", "proto", funcListName, `countof(${funcListName})`]); + body.call("JS_SetClassProto", ["ctx", classId, "proto"]); + body.statement("return 0"); + return body; + } + jsStructGetter(structName, classId, field, type) { + const args = [{ type: "JSContext*", name: "ctx" }, { type: "JSValueConst", name: "this_val" }]; + const fun = this.function(`js_${structName}_get_${field}`, "JSValue", args, true); + fun.declare("ptr", structName + "*", false, `JS_GetOpaque2(ctx, this_val, ${classId})`); + fun.if("!ptr", cond => { + cond.returnExp("JS_EXCEPTION"); + }); + fun.declare(field, type, false, "ptr->" + field); + // TODO: to JS + fun.returnExp("ret"); + } +} +exports.GenericQuickJsGenerator = GenericQuickJsGenerator; +class QuickJsGenerator extends GenericQuickJsGenerator { + createGenerator() { + return new QuickJsGenerator(); + } +} +exports.QuickJsGenerator = QuickJsGenerator; + + +/***/ }), + +/***/ "./src/raylib-header.ts": +/*!******************************!*\ + !*** ./src/raylib-header.ts ***! + \******************************/ +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.RayLibHeader = void 0; +const quickjs_1 = __webpack_require__(/*! ./quickjs */ "./src/quickjs.ts"); +class RayLibHeader extends quickjs_1.QuickJsHeader { + constructor(name, api) { + super(name); + this.api = api; + this.includes.include("raylib.h"); + } + addApiFunction(api, jsName = null) { + const jName = jsName || api.name.charAt(0).toLowerCase() + api.name.slice(1); + const fun = this.functions.jsBindingFunction(jName); + // read parameters + for (let i = 0; i < api.params.length; i++) { + const para = api.params[i]; + fun.jsReadParameter(para.type, para.name, i); + } + // call c function + fun.call(api.name, api.params.map(x => x.name), api.returnType === "void" ? null : { type: api.returnType, name: "returnVal" }); + // clean up parameters + for (const param of api.params) { + fun.jsCleanUpParameter(param.type, param.name); + } + // return result + if (api.returnType === "void") { + fun.statement("return JS_UNDEFINED"); + } + else { + // TODO: Convert to JS + fun.statement("return retVal"); + } + // add binding to function declaration + this.moduleFunctionList.jsFuncDef(jName, api.argc, fun.getTag("_name")); } addApiFunctionByName(name, jsName = null) { const func = this.api.getFunction(name); @@ -396,11 +473,13 @@ class RayLibHeaderGenerator extends generation_1.HeaderGenerator { this.addApiFunction(func, jsName); } addApiStruct(struct, destructor, options) { - const classIdName = `js_${struct.name}_class_id`; - this.declarations.declare(classIdName, "JSClassID", true); - const gen = new struct_1.RayLibStructGenerator(classIdName, struct, destructor, options); - this.body.child(gen); - this.moduleInit.call(gen.classDeclarationName, ["ctx", "m"]); + 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 classFuncList = this.structs.jsFunctionList(`js_${struct.name}_proto_funcs`); + 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"]); // OPT: 7. expose class and constructor } addApiStructByName(structName, destructorName = null, options) { @@ -415,118 +494,8 @@ class RayLibHeaderGenerator extends generation_1.HeaderGenerator { } this.addApiStruct(struct, destructor, options); } - init() { - const guardName = "JS_" + this.name + "_GUARD"; - this.guardStart(guardName); - this.breakLine(); - this.include("stdio.h"); - this.include("stdlib.h"); - this.include("string.h"); - this.breakLine(); - this.include("quickjs.h"); - this.include("raylib.h"); - this.breakLine(); - this.line("#ifndef countof"); - this.line("#define countof(x) (sizeof(x) / sizeof((x)[0]))"); - this.line("#endif"); - this.breakLine(); - this.child(this.declarations); - this.breakLine(); - this.child(this.body); - this.child(this.moduleFunctionList); - this.breakLine(); - const moduleInitFunc = new generation_1.FunctionGenerator("js_" + this.name + "_init", "int", [ - { type: "JSContext *", name: "ctx" }, - { type: "JSModuleDef *", name: "m" } - ]); - moduleInitFunc.body.statement(`JS_SetModuleExportList(ctx, m,${this.moduleFunctionList.name},countof(${this.moduleFunctionList.name}))`); - moduleInitFunc.body.child(this.moduleInit); - moduleInitFunc.body.statement("return 0"); - this.child(moduleInitFunc); - this.breakLine(); - const moduleEntryFunc = new generation_1.FunctionGenerator("js_init_module_" + this.name, "JSModuleDef *", [ - { type: "JSContext *", name: "ctx" }, - { type: "const char *", name: "module_name" } - ]); - moduleEntryFunc.body.statement("JSModuleDef *m"); - moduleEntryFunc.body.statement(`m = JS_NewCModule(ctx, module_name, ${moduleInitFunc.name})`); - moduleEntryFunc.body.statement("if(!m) return NULL"); - moduleEntryFunc.body.statement(`JS_AddModuleExportList(ctx, m, ${this.moduleFunctionList.name}, countof(${this.moduleFunctionList.name}))`); - moduleEntryFunc.body.child(this.moduleEntry); - moduleEntryFunc.body.statement("return m"); - this.child(moduleEntryFunc); - this.breakLine(); - this.guardEnd(guardName); - } } -exports.RayLibHeaderGenerator = RayLibHeaderGenerator; - - -/***/ }), - -/***/ "./src/struct.ts": -/*!***********************!*\ - !*** ./src/struct.ts ***! - \***********************/ -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.RayLibStructGenerator = void 0; -const functionList_1 = __webpack_require__(/*! ./functionList */ "./src/functionList.ts"); -const generation_1 = __webpack_require__(/*! ./generation */ "./src/generation.ts"); -class RayLibStructGenerator extends generation_1.CodeGenerator { - constructor(classId, struct, destructor, options) { - super(); - this.classId = classId; - this.struct = struct; - this.destructor = destructor; - this.finalizerName = ""; - this.classDeclarationName = ""; - this.options = options || {}; - this.funcList = new functionList_1.RayLibFunctionListGenerator(`js_${struct.name}_proto_funcs`); - this.declareFinalizer(); - this.breakLine(); - this.declareGetterSetter(); - this.funcList.addPropertyString("[Symbol.toStringTag]", "Image"); - this.child(this.funcList); - this.breakLine(); - this.buildClassDeclaration(); - this.breakLine(); - } - declareFinalizer() { - this.finalizerName = `js_${this.struct.name}_finalizer`; - this.childFuncBody(new generation_1.FunctionGenerator(this.finalizerName, "void", [ - { type: "JSRuntime *", name: "rt" }, - { type: "JSValue", name: "val" } - ], true), body => { - body.statement(`${this.struct.name}* ptr = JS_GetOpaque(val, ${this.classId})`); - body.childFuncBody(new generation_1.ConditionGenerator("ptr"), cond => { - cond.call("puts", ["\"Finalize " + this.struct.name + "\""]); - if (this.destructor) - cond.call(this.destructor.name, ["*ptr"]); - cond.call("js_free_rt", ["rt", "ptr"]); - }); - }); - } - declareGetterSetter() { - // Add to funList - } - buildClassDeclaration() { - this.classDeclarationName = "js_declare_" + this.struct.name; - this.childFuncBody(new generation_1.FunctionGenerator(this.classDeclarationName, "int", [{ type: "JSContext *", name: "ctx" }, { type: "JSModuleDef *", name: "m" }], true), body => { - body.call("JS_NewClassID", ["&" + this.classId]); - const classDefName = `js_${this.struct.name}_def`; - body.declare(classDefName, "JSClassDef", false, `{ .class_name = "${this.struct.name}", .finalizer = ${this.finalizerName} }`); - body.call("JS_NewClass", ["JS_GetRuntime(ctx)", this.classId, "&" + classDefName]); - body.declare("proto", "JSValue", false, "JS_NewObject(ctx)"); - body.call("JS_SetPropertyFunctionList", ["ctx", "proto", this.funcList.name, `countof(${this.funcList.name})`]); - body.call("JS_SetClassProto", ["ctx", this.classId, "proto"]); - body.statement("return 0"); - }); - } -} -exports.RayLibStructGenerator = RayLibStructGenerator; +exports.RayLibHeader = RayLibHeader; /***/ }), @@ -578,27 +547,20 @@ var exports = __webpack_exports__; Object.defineProperty(exports, "__esModule", ({ value: true })); const fs_1 = __webpack_require__(/*! fs */ "fs"); -const generation_1 = __webpack_require__(/*! ./generation */ "./src/generation.ts"); const api_1 = __webpack_require__(/*! ./api */ "./src/api.ts"); -const header_1 = __webpack_require__(/*! ./header */ "./src/header.ts"); -const bindings = JSON.parse((0, fs_1.readFileSync)("bindings.json", 'utf8')); -function writeHeader(header, filename) { - const writer = new generation_1.CodeWriter(); - writer.writeGenerator(header); - (0, fs_1.writeFileSync)(filename, writer.toString()); -} +const raylib_header_1 = __webpack_require__(/*! ./raylib-header */ "./src/raylib-header.ts"); function main() { const api = JSON.parse((0, fs_1.readFileSync)("thirdparty/raylib/parser/output/raylib_api.json", 'utf8')); const apiDesc = new api_1.ApiDescription(api); - const core_gen = new header_1.RayLibHeaderGenerator("raylib_core", apiDesc); + const core_gen = new raylib_header_1.RayLibHeader("raylib_core", apiDesc); core_gen.addApiFunctionByName("SetWindowTitle"); core_gen.addApiFunctionByName("SetWindowPosition"); core_gen.addApiFunctionByName("BeginDrawing"); core_gen.addApiFunctionByName("EndDrawing"); - writeHeader(core_gen, "src/bindings/js_raylib_core.h"); - const texture_gen = new header_1.RayLibHeaderGenerator("raylib_texture", apiDesc); + 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"); - writeHeader(texture_gen, "src/bindings/js_raylib_texture.h"); + texture_gen.writeTo("src/bindings/js_raylib_texture.h"); } main(); diff --git a/src/bindings/js_raylib_core.h b/src/bindings/js_raylib_core.h index 81ddbb6..4178152 100644 --- a/src/bindings/js_raylib_core.h +++ b/src/bindings/js_raylib_core.h @@ -47,7 +47,7 @@ static const JSCFunctionListEntry js_raylib_core_funcs[] = { JS_CFUNC_DEF("endDrawing",0,js_endDrawing), }; -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)); return 0; } diff --git a/src/bindings/js_raylib_texture.h b/src/bindings/js_raylib_texture.h index c2ba750..a7f7602 100644 --- a/src/bindings/js_raylib_texture.h +++ b/src/bindings/js_raylib_texture.h @@ -40,7 +40,7 @@ static int js_declare_Image(JSContext * ctx, JSModuleDef * m) { static const JSCFunctionListEntry js_raylib_texture_funcs[] = { }; -int js_raylib_texture_init(JSContext * ctx, JSModuleDef * m) { +static int js_raylib_texture_init(JSContext * ctx, JSModuleDef * m) { JS_SetModuleExportList(ctx, m,js_raylib_texture_funcs,countof(js_raylib_texture_funcs)); js_declare_Image(ctx, m); return 0;