mirror of https://github.com/mode777/rayjs.git
Refactor binding generator
This commit is contained in:
parent
e2d140e25b
commit
d9f10bed4f
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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),`)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -63,11 +63,28 @@ enum Token {
|
||||||
GOSUB = 4
|
GOSUB = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CodeGenerator {
|
export interface FunctionArgument {
|
||||||
|
type: string,
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export abstract class GenericCodeGenerator<T extends CodeGenerator> {
|
||||||
private children: CodeGenerator[] = []
|
private children: CodeGenerator[] = []
|
||||||
private text: string[] = []
|
private text: string[] = []
|
||||||
private tokens: Token[] = []
|
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(){
|
iterateTokens(){
|
||||||
return this.tokens[Symbol.iterator]()
|
return this.tokens[Symbol.iterator]()
|
||||||
}
|
}
|
||||||
|
@ -79,6 +96,8 @@ export class CodeGenerator {
|
||||||
iterateChildren(){
|
iterateChildren(){
|
||||||
return this.children[Symbol.iterator]()
|
return this.children[Symbol.iterator]()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract createGenerator(): T;
|
||||||
|
|
||||||
line(text: string){
|
line(text: string){
|
||||||
this.tokens.push(Token.STRING, Token.NEWLINE)
|
this.tokens.push(Token.STRING, Token.NEWLINE)
|
||||||
|
@ -100,22 +119,14 @@ export class CodeGenerator {
|
||||||
if(isStatic) this.inline("static ")
|
if(isStatic) this.inline("static ")
|
||||||
this.inline(type + " " + name)
|
this.inline(type + " " + name)
|
||||||
if(initValue) this.inline(" = " + initValue)
|
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.tokens.push(Token.GOSUB)
|
||||||
this.children.push(sub)
|
this.children.push(sub)
|
||||||
}
|
return 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline(str: string){
|
inline(str: string){
|
||||||
|
@ -138,56 +149,76 @@ export class CodeGenerator {
|
||||||
public unindent(){
|
public unindent(){
|
||||||
this.tokens.push(Token.UNINDENT)
|
this.tokens.push(Token.UNINDENT)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export class ConditionGenerator extends CodeGenerator {
|
public function(name: string, returnType: string, args: FunctionArgument[], isStatic: boolean, func?: (gen: T) => void){
|
||||||
body = new CodeGenerator()
|
const sub = this.createGenerator();
|
||||||
|
sub.setTag("_type", "function-body")
|
||||||
constructor(condition: string){
|
sub.setTag("_name", name)
|
||||||
super()
|
sub.setTag("_isStatic", isStatic)
|
||||||
this.line("if("+condition+") {")
|
sub.setTag("_returnType", returnType)
|
||||||
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<T extends CodeGenerator> extends CodeGenerator {
|
|
||||||
constructor(public readonly name: string, returnType: string, args: FunctionArgument[], public readonly body: T, isStatic = false){
|
|
||||||
super()
|
|
||||||
if(isStatic) this.inline("static ")
|
if(isStatic) this.inline("static ")
|
||||||
this.inline(returnType + " " + name + "(")
|
this.inline(returnType + " " + name + "(")
|
||||||
this.inline(args.map(x => x.type + " " + x.name).join(", "))
|
this.inline(args.map(x => x.type + " " + x.name).join(", "))
|
||||||
this.inline(") {")
|
this.inline(") {")
|
||||||
this.breakLine()
|
this.breakLine()
|
||||||
this.indent()
|
this.indent()
|
||||||
this.child(body)
|
this.child(sub)
|
||||||
this.unindent()
|
this.unindent()
|
||||||
this.line("}")
|
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<CodeGenerator> {
|
export class CodeGenerator extends GenericCodeGenerator<CodeGenerator>{
|
||||||
constructor(name: string, returnType: string, args: FunctionArgument[], isStatic = false, body: CodeGenerator = new CodeGenerator()){
|
createGenerator(): CodeGenerator {
|
||||||
super(name, returnType, args, body, isStatic)
|
return new CodeGenerator()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +1,22 @@
|
||||||
import { readFileSync, writeFileSync } from "fs";
|
import { readFileSync, writeFileSync } from "fs";
|
||||||
import { Bindings, RayLibApi } from "./interfaces";
|
import { Bindings, RayLibApi } from "./interfaces";
|
||||||
import { CodeWriter, HeaderGenerator } from "./generation";
|
|
||||||
import { ApiDescription } from "./api";
|
import { ApiDescription } from "./api";
|
||||||
import { RayLibHeaderGenerator } from "./header";
|
import { RayLibHeader } from "./raylib-header";
|
||||||
|
|
||||||
const bindings = <Bindings>JSON.parse(readFileSync("bindings.json", 'utf8'))
|
|
||||||
|
|
||||||
function writeHeader(header: HeaderGenerator, filename: string){
|
|
||||||
const writer = new CodeWriter()
|
|
||||||
writer.writeGenerator(header)
|
|
||||||
writeFileSync(filename, writer.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
function main(){
|
function main(){
|
||||||
const api = <RayLibApi>JSON.parse(readFileSync("thirdparty/raylib/parser/output/raylib_api.json", 'utf8'))
|
const api = <RayLibApi>JSON.parse(readFileSync("thirdparty/raylib/parser/output/raylib_api.json", 'utf8'))
|
||||||
const apiDesc = new ApiDescription(api)
|
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("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")
|
||||||
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")
|
texture_gen.addApiStructByName("Image", "UnloadImage")
|
||||||
writeHeader(texture_gen, "src/bindings/js_raylib_texture.h")
|
texture_gen.writeTo("src/bindings/js_raylib_texture.h")
|
||||||
}
|
}
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -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<T extends CodeGenerator> extends GenericCodeGenerator<T> {
|
||||||
|
|
||||||
|
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(<T>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<QuickJsGenerator> {
|
||||||
|
createGenerator(): QuickJsGenerator {
|
||||||
|
return new QuickJsGenerator()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -50,116 +50,6 @@ class ApiDescription {
|
||||||
exports.ApiDescription = 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":
|
/***/ "./src/generation.ts":
|
||||||
|
@ -170,7 +60,7 @@ exports.RayLibFunctionListGenerator = RayLibFunctionListGenerator;
|
||||||
|
|
||||||
|
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
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 {
|
class StringWriter {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.buffer = '';
|
this.buffer = '';
|
||||||
|
@ -237,11 +127,18 @@ var Token;
|
||||||
Token[Token["UNINDENT"] = 3] = "UNINDENT";
|
Token[Token["UNINDENT"] = 3] = "UNINDENT";
|
||||||
Token[Token["GOSUB"] = 4] = "GOSUB";
|
Token[Token["GOSUB"] = 4] = "GOSUB";
|
||||||
})(Token || (Token = {}));
|
})(Token || (Token = {}));
|
||||||
class CodeGenerator {
|
class GenericCodeGenerator {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.children = [];
|
this.children = [];
|
||||||
this.text = [];
|
this.text = [];
|
||||||
this.tokens = [];
|
this.tokens = [];
|
||||||
|
this.tags = {};
|
||||||
|
}
|
||||||
|
getTag(key) {
|
||||||
|
return this.tags[key];
|
||||||
|
}
|
||||||
|
setTag(key, value) {
|
||||||
|
this.tags[key] = value;
|
||||||
}
|
}
|
||||||
iterateTokens() {
|
iterateTokens() {
|
||||||
return this.tokens[Symbol.iterator]();
|
return this.tokens[Symbol.iterator]();
|
||||||
|
@ -275,16 +172,11 @@ class CodeGenerator {
|
||||||
this.statement("");
|
this.statement("");
|
||||||
}
|
}
|
||||||
child(sub) {
|
child(sub) {
|
||||||
|
if (!sub)
|
||||||
|
sub = this.createGenerator();
|
||||||
this.tokens.push(Token.GOSUB);
|
this.tokens.push(Token.GOSUB);
|
||||||
this.children.push(sub);
|
this.children.push(sub);
|
||||||
}
|
return sub;
|
||||||
childFunc(sub, func) {
|
|
||||||
this.child(sub);
|
|
||||||
func(sub);
|
|
||||||
}
|
|
||||||
childFuncBody(sub, func) {
|
|
||||||
this.child(sub);
|
|
||||||
func(sub.body);
|
|
||||||
}
|
}
|
||||||
inline(str) {
|
inline(str) {
|
||||||
this.tokens.push(Token.STRING);
|
this.tokens.push(Token.STRING);
|
||||||
|
@ -302,38 +194,12 @@ class CodeGenerator {
|
||||||
unindent() {
|
unindent() {
|
||||||
this.tokens.push(Token.UNINDENT);
|
this.tokens.push(Token.UNINDENT);
|
||||||
}
|
}
|
||||||
}
|
function(name, returnType, args, isStatic, func) {
|
||||||
exports.CodeGenerator = CodeGenerator;
|
const sub = this.createGenerator();
|
||||||
class ConditionGenerator extends CodeGenerator {
|
sub.setTag("_type", "function-body");
|
||||||
constructor(condition) {
|
sub.setTag("_name", name);
|
||||||
super();
|
sub.setTag("_isStatic", isStatic);
|
||||||
this.body = new CodeGenerator();
|
sub.setTag("_returnType", returnType);
|
||||||
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;
|
|
||||||
if (isStatic)
|
if (isStatic)
|
||||||
this.inline("static ");
|
this.inline("static ");
|
||||||
this.inline(returnType + " " + name + "(");
|
this.inline(returnType + " " + name + "(");
|
||||||
|
@ -341,53 +207,264 @@ class CustomFunctionGenerator extends CodeGenerator {
|
||||||
this.inline(") {");
|
this.inline(") {");
|
||||||
this.breakLine();
|
this.breakLine();
|
||||||
this.indent();
|
this.indent();
|
||||||
this.child(body);
|
this.child(sub);
|
||||||
this.unindent();
|
this.unindent();
|
||||||
this.line("}");
|
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;
|
exports.GenericCodeGenerator = GenericCodeGenerator;
|
||||||
class FunctionGenerator extends CustomFunctionGenerator {
|
class CodeGenerator extends GenericCodeGenerator {
|
||||||
constructor(name, returnType, args, isStatic = false, body = new CodeGenerator()) {
|
createGenerator() {
|
||||||
super(name, returnType, args, body, isStatic);
|
return new CodeGenerator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.FunctionGenerator = FunctionGenerator;
|
exports.CodeGenerator = CodeGenerator;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ "./src/header.ts":
|
/***/ "./src/quickjs.ts":
|
||||||
/*!***********************!*\
|
/*!************************!*\
|
||||||
!*** ./src/header.ts ***!
|
!*** ./src/quickjs.ts ***!
|
||||||
\***********************/
|
\************************/
|
||||||
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
||||||
|
|
||||||
|
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.RayLibHeaderGenerator = void 0;
|
exports.QuickJsGenerator = exports.GenericQuickJsGenerator = exports.QuickJsHeader = void 0;
|
||||||
const function_1 = __webpack_require__(/*! ./function */ "./src/function.ts");
|
const fs_1 = __webpack_require__(/*! fs */ "fs");
|
||||||
const functionList_1 = __webpack_require__(/*! ./functionList */ "./src/functionList.ts");
|
|
||||||
const generation_1 = __webpack_require__(/*! ./generation */ "./src/generation.ts");
|
const generation_1 = __webpack_require__(/*! ./generation */ "./src/generation.ts");
|
||||||
const struct_1 = __webpack_require__(/*! ./struct */ "./src/struct.ts");
|
class QuickJsHeader {
|
||||||
class RayLibHeaderGenerator extends generation_1.HeaderGenerator {
|
constructor(name) {
|
||||||
constructor(name, api) {
|
|
||||||
super();
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.api = api;
|
const root = this.root = new QuickJsGenerator();
|
||||||
this.moduleInit = new generation_1.CodeGenerator();
|
const body = this.body = root.header("JS_" + this.name + "_GUARD");
|
||||||
this.moduleEntry = new generation_1.CodeGenerator();
|
const includes = this.includes = body.child();
|
||||||
this.declarations = new generation_1.CodeGenerator();
|
includes.include("stdio.h");
|
||||||
this.body = new generation_1.CodeGenerator();
|
includes.include("stdlib.h");
|
||||||
this.moduleFunctionList = new functionList_1.RayLibFunctionListGenerator("js_" + name + "_funcs");
|
includes.include("string.h");
|
||||||
this.init();
|
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) {
|
writeTo(filename) {
|
||||||
const jName = jsName || func.name.charAt(0).toLowerCase() + func.name.slice(1);
|
const writer = new generation_1.CodeWriter();
|
||||||
const gen = new function_1.RayLibFunctionGenerator(jName, func);
|
writer.writeGenerator(this.root);
|
||||||
this.body.child(gen);
|
(0, fs_1.writeFileSync)(filename, writer.toString());
|
||||||
this.body.breakLine();
|
}
|
||||||
this.moduleFunctionList.addFunction(jName, func.argc, gen.name);
|
}
|
||||||
|
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) {
|
addApiFunctionByName(name, jsName = null) {
|
||||||
const func = this.api.getFunction(name);
|
const func = this.api.getFunction(name);
|
||||||
|
@ -396,11 +473,13 @@ class RayLibHeaderGenerator extends generation_1.HeaderGenerator {
|
||||||
this.addApiFunction(func, jsName);
|
this.addApiFunction(func, jsName);
|
||||||
}
|
}
|
||||||
addApiStruct(struct, destructor, options) {
|
addApiStruct(struct, destructor, options) {
|
||||||
const classIdName = `js_${struct.name}_class_id`;
|
const classId = this.declarations.jsClassId(`js_${struct.name}_class_id`);
|
||||||
this.declarations.declare(classIdName, "JSClassID", true);
|
const finalizer = this.structs.jsStructFinalizer(classId, struct.name, (gen, ptr) => destructor && gen.call(destructor.name, ["*" + ptr]));
|
||||||
const gen = new struct_1.RayLibStructGenerator(classIdName, struct, destructor, options);
|
//TODO: declareGetterSetter()
|
||||||
this.body.child(gen);
|
const classFuncList = this.structs.jsFunctionList(`js_${struct.name}_proto_funcs`);
|
||||||
this.moduleInit.call(gen.classDeclarationName, ["ctx", "m"]);
|
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
|
// OPT: 7. expose class and constructor
|
||||||
}
|
}
|
||||||
addApiStructByName(structName, destructorName = null, options) {
|
addApiStructByName(structName, destructorName = null, options) {
|
||||||
|
@ -415,118 +494,8 @@ class RayLibHeaderGenerator extends generation_1.HeaderGenerator {
|
||||||
}
|
}
|
||||||
this.addApiStruct(struct, destructor, options);
|
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;
|
exports.RayLibHeader = RayLibHeader;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ "./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;
|
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
@ -578,27 +547,20 @@ var exports = __webpack_exports__;
|
||||||
|
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
const fs_1 = __webpack_require__(/*! fs */ "fs");
|
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 api_1 = __webpack_require__(/*! ./api */ "./src/api.ts");
|
||||||
const header_1 = __webpack_require__(/*! ./header */ "./src/header.ts");
|
const raylib_header_1 = __webpack_require__(/*! ./raylib-header */ "./src/raylib-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());
|
|
||||||
}
|
|
||||||
function main() {
|
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 header_1.RayLibHeaderGenerator("raylib_core", apiDesc);
|
const core_gen = new raylib_header_1.RayLibHeader("raylib_core", apiDesc);
|
||||||
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");
|
||||||
writeHeader(core_gen, "src/bindings/js_raylib_core.h");
|
core_gen.writeTo("src/bindings/js_raylib_core.h");
|
||||||
const texture_gen = new header_1.RayLibHeaderGenerator("raylib_texture", apiDesc);
|
const texture_gen = new raylib_header_1.RayLibHeader("raylib_texture", apiDesc);
|
||||||
texture_gen.addApiStructByName("Image", "UnloadImage");
|
texture_gen.addApiStructByName("Image", "UnloadImage");
|
||||||
writeHeader(texture_gen, "src/bindings/js_raylib_texture.h");
|
texture_gen.writeTo("src/bindings/js_raylib_texture.h");
|
||||||
}
|
}
|
||||||
main();
|
main();
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ static const JSCFunctionListEntry js_raylib_core_funcs[] = {
|
||||||
JS_CFUNC_DEF("endDrawing",0,js_endDrawing),
|
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));
|
JS_SetModuleExportList(ctx, m,js_raylib_core_funcs,countof(js_raylib_core_funcs));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ static int js_declare_Image(JSContext * ctx, JSModuleDef * m) {
|
||||||
static const JSCFunctionListEntry js_raylib_texture_funcs[] = {
|
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_SetModuleExportList(ctx, m,js_raylib_texture_funcs,countof(js_raylib_texture_funcs));
|
||||||
js_declare_Image(ctx, m);
|
js_declare_Image(ctx, m);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue