mirror of https://github.com/mode777/rayjs.git
add binding generator
This commit is contained in:
parent
63836f34d0
commit
e2d140e25b
Binary file not shown.
After Width: | Height: | Size: 280 KiB |
|
@ -1,15 +1,17 @@
|
|||
[
|
||||
{
|
||||
"name": "raylib_core",
|
||||
"functions": [
|
||||
{ "name": "SetWindowTitle" },
|
||||
{ "name": "SetWindowPosition" },
|
||||
{ "name": "BeginDrawing" },
|
||||
{ "name": "EndDrawing" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "raylib_texture",
|
||||
"functions": []
|
||||
}
|
||||
]
|
||||
{
|
||||
"headers": [
|
||||
{
|
||||
"name": "raylib_core",
|
||||
"functions": [
|
||||
{ "name": "SetWindowTitle" },
|
||||
{ "name": "SetWindowPosition" },
|
||||
{ "name": "BeginDrawing" },
|
||||
{ "name": "EndDrawing" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "raylib_texture",
|
||||
"functions": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
node_modules/
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "bindings",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"watch": "webpack --watch --config webpack.config.js --mode development"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"ts-loader": "^9.4.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.0.4",
|
||||
"webpack": "^5.82.0",
|
||||
"webpack-cli": "^5.0.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
import { RayLibApi, RayLibFunction, RayLibStruct } from "./interfaces"
|
||||
|
||||
export class ApiFunction{
|
||||
constructor(private api: RayLibFunction){
|
||||
api.params = api.params || []
|
||||
}
|
||||
|
||||
get name() { return this.api.name }
|
||||
get argc() { return this.api.params?.length || 0 }
|
||||
get params() { return this.api.params || [] }
|
||||
get returnType() { return this.api.returnType }
|
||||
}
|
||||
|
||||
export class ApiStruct{
|
||||
constructor(private api: RayLibStruct){
|
||||
|
||||
}
|
||||
|
||||
get name() { return this.api.name }
|
||||
get fields() { return this.api.fields || [] }
|
||||
}
|
||||
|
||||
export class ApiDescription{
|
||||
constructor(private api: RayLibApi){
|
||||
|
||||
}
|
||||
|
||||
getFunction(name: string){
|
||||
const f = this.api.functions.find(x => x.name === name)
|
||||
if(!f) return null
|
||||
return new ApiFunction(f)
|
||||
}
|
||||
|
||||
getStruct(name: string){
|
||||
const s = this.api.structs.find(x => x.name === name)
|
||||
if(!s) return null
|
||||
return new ApiStruct(s)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
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),`)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
export class StringWriter {
|
||||
private buffer = ''
|
||||
|
||||
write(value: string): void {
|
||||
this.buffer += value;
|
||||
}
|
||||
|
||||
writeLine(value: string = ''): void {
|
||||
this.buffer += value + '\n';
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this.buffer;
|
||||
}
|
||||
}
|
||||
|
||||
export class CodeWriter extends StringWriter {
|
||||
private indent = 0
|
||||
private needsIndent = true
|
||||
|
||||
writeGenerator(generator: CodeGenerator){
|
||||
const tokens = generator.iterateTokens()
|
||||
const text = generator.iterateText()
|
||||
const children = generator.iterateChildren()
|
||||
let result = tokens.next();
|
||||
while (!result.done) {
|
||||
switch (result.value) {
|
||||
case Token.STRING:
|
||||
const str = text.next().value
|
||||
if(this.needsIndent){
|
||||
this.write(" ".repeat(this.indent))
|
||||
this.needsIndent = false
|
||||
}
|
||||
this.write(str)
|
||||
break
|
||||
case Token.GOSUB:
|
||||
const sub = children.next().value
|
||||
this.writeGenerator(sub)
|
||||
break
|
||||
case Token.INDENT:
|
||||
this.indent++
|
||||
break
|
||||
case Token.UNINDENT:
|
||||
this.indent = this.indent > 0 ? this.indent-1 : 0
|
||||
break
|
||||
case Token.NEWLINE:
|
||||
this.write("\n")
|
||||
this.needsIndent = true
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
result = tokens.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Token {
|
||||
STRING = 0,
|
||||
NEWLINE = 1,
|
||||
INDENT = 2,
|
||||
UNINDENT = 3,
|
||||
GOSUB = 4
|
||||
}
|
||||
|
||||
export class CodeGenerator {
|
||||
private children: CodeGenerator[] = []
|
||||
private text: string[] = []
|
||||
private tokens: Token[] = []
|
||||
|
||||
iterateTokens(){
|
||||
return this.tokens[Symbol.iterator]()
|
||||
}
|
||||
|
||||
iterateText(){
|
||||
return this.text[Symbol.iterator]()
|
||||
}
|
||||
|
||||
iterateChildren(){
|
||||
return this.children[Symbol.iterator]()
|
||||
}
|
||||
|
||||
line(text: string){
|
||||
this.tokens.push(Token.STRING, Token.NEWLINE)
|
||||
this.text.push(text)
|
||||
}
|
||||
|
||||
comment(text: string){
|
||||
this.line("// " + text)
|
||||
}
|
||||
|
||||
call(name: string, params: string[], returnVal: FunctionArgument | null = null){
|
||||
if(returnVal) this.inline(`${returnVal.type} ${returnVal.name} = `)
|
||||
this.inline(name + "(")
|
||||
this.inline(params.join(", "))
|
||||
this.statement(")")
|
||||
}
|
||||
|
||||
declare(name: string, type: string, isStatic = false, initValue: string | null = null){
|
||||
if(isStatic) this.inline("static ")
|
||||
this.inline(type + " " + name)
|
||||
if(initValue) this.inline(" = " + initValue)
|
||||
this.statement("")
|
||||
}
|
||||
|
||||
child(sub: CodeGenerator){
|
||||
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)
|
||||
}
|
||||
|
||||
inline(str: string){
|
||||
this.tokens.push(Token.STRING)
|
||||
this.text.push(str)
|
||||
}
|
||||
|
||||
statement(str: string){
|
||||
this.line(str+";")
|
||||
}
|
||||
|
||||
breakLine(){
|
||||
this.tokens.push(Token.NEWLINE)
|
||||
}
|
||||
|
||||
public indent(){
|
||||
this.tokens.push(Token.INDENT)
|
||||
}
|
||||
|
||||
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<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 ")
|
||||
this.inline(returnType + " " + name + "(")
|
||||
this.inline(args.map(x => x.type + " " + x.name).join(", "))
|
||||
this.inline(") {")
|
||||
this.breakLine()
|
||||
this.indent()
|
||||
this.child(body)
|
||||
this.unindent()
|
||||
this.line("}")
|
||||
}
|
||||
}
|
||||
|
||||
export class FunctionGenerator extends CustomFunctionGenerator<CodeGenerator> {
|
||||
constructor(name: string, returnType: string, args: FunctionArgument[], isStatic = false, body: CodeGenerator = new CodeGenerator()){
|
||||
super(name, returnType, args, body, isStatic)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
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 = <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(){
|
||||
const api = <RayLibApi>JSON.parse(readFileSync("thirdparty/raylib/parser/output/raylib_api.json", 'utf8'))
|
||||
const apiDesc = new ApiDescription(api)
|
||||
|
||||
const core_gen = new RayLibHeaderGenerator("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 RayLibHeaderGenerator("raylib_texture", apiDesc)
|
||||
texture_gen.addApiStructByName("Image", "UnloadImage")
|
||||
writeHeader(texture_gen, "src/bindings/js_raylib_texture.h")
|
||||
}
|
||||
|
||||
main()
|
|
@ -0,0 +1,64 @@
|
|||
export type RayLibType = "void" | "const char *" | "bool" | "float" | "unsigned char" | "void *" | "int" | "usigned int" | "Texture" | "Rectangle" | "Image" | "Rectangle *" | "GylphInfo *" | "Texture2D" | "Vector3" | "Vector2" | "float *" | "unsigned char *" | "unsigned short *" | "unsigned int *" | "Shader" | "MaterialMap *" | "float[4]" | "Vector3"
|
||||
|
||||
export interface RayLibDefine {
|
||||
name: string,
|
||||
type: "GUARD" | "INT" | "STRING" | "MACRO" | "UNKNOWN" | "FLOAT" | "FLOAT_MATH" | "COLOR",
|
||||
value: string,
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface RayLibFieldDescription {
|
||||
type: RayLibType,
|
||||
name: string,
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface RayLibStruct {
|
||||
name: string,
|
||||
description: string,
|
||||
fields: RayLibFieldDescription[]
|
||||
}
|
||||
|
||||
export interface RayLibEnumValue {
|
||||
name: string,
|
||||
value: number,
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface RayLibEnum {
|
||||
name: string,
|
||||
description: string,
|
||||
values: RayLibEnumValue[]
|
||||
}
|
||||
|
||||
export interface RayLibParamDescription {
|
||||
type: RayLibType,
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface RayLibFunction {
|
||||
name: string,
|
||||
description: string,
|
||||
returnType: RayLibType,
|
||||
params?: RayLibParamDescription[]
|
||||
}
|
||||
|
||||
export interface RayLibApi {
|
||||
defines: RayLibDefine[],
|
||||
structs: RayLibStruct[],
|
||||
enums: RayLibEnum[],
|
||||
functions: RayLibFunction[]
|
||||
}
|
||||
|
||||
export interface BindingFunction {
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface BindingHeader {
|
||||
name: string,
|
||||
functions: BindingFunction[],
|
||||
}
|
||||
|
||||
export interface Bindings {
|
||||
headers: BindingHeader
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
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")
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"module": "commonjs",
|
||||
"outDir": "dist",
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: './src/index.ts',
|
||||
devtool: false,
|
||||
target: "node",
|
||||
mode: 'production',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js'],
|
||||
},
|
||||
output: {
|
||||
filename: 'generate-bindings.js',
|
||||
path: path.resolve(__dirname, '..'),
|
||||
},
|
||||
};
|
|
@ -0,0 +1,608 @@
|
|||
/******/ (() => { // webpackBootstrap
|
||||
/******/ "use strict";
|
||||
/******/ var __webpack_modules__ = ({
|
||||
|
||||
/***/ "./src/api.ts":
|
||||
/*!********************!*\
|
||||
!*** ./src/api.ts ***!
|
||||
\********************/
|
||||
/***/ ((__unused_webpack_module, exports) => {
|
||||
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.ApiDescription = exports.ApiStruct = exports.ApiFunction = void 0;
|
||||
class ApiFunction {
|
||||
constructor(api) {
|
||||
this.api = api;
|
||||
api.params = api.params || [];
|
||||
}
|
||||
get name() { return this.api.name; }
|
||||
get argc() { return this.api.params?.length || 0; }
|
||||
get params() { return this.api.params || []; }
|
||||
get returnType() { return this.api.returnType; }
|
||||
}
|
||||
exports.ApiFunction = ApiFunction;
|
||||
class ApiStruct {
|
||||
constructor(api) {
|
||||
this.api = api;
|
||||
}
|
||||
get name() { return this.api.name; }
|
||||
get fields() { return this.api.fields || []; }
|
||||
}
|
||||
exports.ApiStruct = ApiStruct;
|
||||
class ApiDescription {
|
||||
constructor(api) {
|
||||
this.api = api;
|
||||
}
|
||||
getFunction(name) {
|
||||
const f = this.api.functions.find(x => x.name === name);
|
||||
if (!f)
|
||||
return null;
|
||||
return new ApiFunction(f);
|
||||
}
|
||||
getStruct(name) {
|
||||
const s = this.api.structs.find(x => x.name === name);
|
||||
if (!s)
|
||||
return null;
|
||||
return new ApiStruct(s);
|
||||
}
|
||||
}
|
||||
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 ***!
|
||||
\***************************/
|
||||
/***/ ((__unused_webpack_module, exports) => {
|
||||
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.FunctionGenerator = exports.CustomFunctionGenerator = exports.HeaderGenerator = exports.ConditionGenerator = exports.CodeGenerator = exports.CodeWriter = exports.StringWriter = void 0;
|
||||
class StringWriter {
|
||||
constructor() {
|
||||
this.buffer = '';
|
||||
}
|
||||
write(value) {
|
||||
this.buffer += value;
|
||||
}
|
||||
writeLine(value = '') {
|
||||
this.buffer += value + '\n';
|
||||
}
|
||||
toString() {
|
||||
return this.buffer;
|
||||
}
|
||||
}
|
||||
exports.StringWriter = StringWriter;
|
||||
class CodeWriter extends StringWriter {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.indent = 0;
|
||||
this.needsIndent = true;
|
||||
}
|
||||
writeGenerator(generator) {
|
||||
const tokens = generator.iterateTokens();
|
||||
const text = generator.iterateText();
|
||||
const children = generator.iterateChildren();
|
||||
let result = tokens.next();
|
||||
while (!result.done) {
|
||||
switch (result.value) {
|
||||
case Token.STRING:
|
||||
const str = text.next().value;
|
||||
if (this.needsIndent) {
|
||||
this.write(" ".repeat(this.indent));
|
||||
this.needsIndent = false;
|
||||
}
|
||||
this.write(str);
|
||||
break;
|
||||
case Token.GOSUB:
|
||||
const sub = children.next().value;
|
||||
this.writeGenerator(sub);
|
||||
break;
|
||||
case Token.INDENT:
|
||||
this.indent++;
|
||||
break;
|
||||
case Token.UNINDENT:
|
||||
this.indent = this.indent > 0 ? this.indent - 1 : 0;
|
||||
break;
|
||||
case Token.NEWLINE:
|
||||
this.write("\n");
|
||||
this.needsIndent = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
result = tokens.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.CodeWriter = CodeWriter;
|
||||
var Token;
|
||||
(function (Token) {
|
||||
Token[Token["STRING"] = 0] = "STRING";
|
||||
Token[Token["NEWLINE"] = 1] = "NEWLINE";
|
||||
Token[Token["INDENT"] = 2] = "INDENT";
|
||||
Token[Token["UNINDENT"] = 3] = "UNINDENT";
|
||||
Token[Token["GOSUB"] = 4] = "GOSUB";
|
||||
})(Token || (Token = {}));
|
||||
class CodeGenerator {
|
||||
constructor() {
|
||||
this.children = [];
|
||||
this.text = [];
|
||||
this.tokens = [];
|
||||
}
|
||||
iterateTokens() {
|
||||
return this.tokens[Symbol.iterator]();
|
||||
}
|
||||
iterateText() {
|
||||
return this.text[Symbol.iterator]();
|
||||
}
|
||||
iterateChildren() {
|
||||
return this.children[Symbol.iterator]();
|
||||
}
|
||||
line(text) {
|
||||
this.tokens.push(Token.STRING, Token.NEWLINE);
|
||||
this.text.push(text);
|
||||
}
|
||||
comment(text) {
|
||||
this.line("// " + text);
|
||||
}
|
||||
call(name, params, returnVal = null) {
|
||||
if (returnVal)
|
||||
this.inline(`${returnVal.type} ${returnVal.name} = `);
|
||||
this.inline(name + "(");
|
||||
this.inline(params.join(", "));
|
||||
this.statement(")");
|
||||
}
|
||||
declare(name, type, isStatic = false, initValue = null) {
|
||||
if (isStatic)
|
||||
this.inline("static ");
|
||||
this.inline(type + " " + name);
|
||||
if (initValue)
|
||||
this.inline(" = " + initValue);
|
||||
this.statement("");
|
||||
}
|
||||
child(sub) {
|
||||
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);
|
||||
}
|
||||
inline(str) {
|
||||
this.tokens.push(Token.STRING);
|
||||
this.text.push(str);
|
||||
}
|
||||
statement(str) {
|
||||
this.line(str + ";");
|
||||
}
|
||||
breakLine() {
|
||||
this.tokens.push(Token.NEWLINE);
|
||||
}
|
||||
indent() {
|
||||
this.tokens.push(Token.INDENT);
|
||||
}
|
||||
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;
|
||||
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.unindent();
|
||||
this.line("}");
|
||||
}
|
||||
}
|
||||
exports.CustomFunctionGenerator = CustomFunctionGenerator;
|
||||
class FunctionGenerator extends CustomFunctionGenerator {
|
||||
constructor(name, returnType, args, isStatic = false, body = new CodeGenerator()) {
|
||||
super(name, returnType, args, body, isStatic);
|
||||
}
|
||||
}
|
||||
exports.FunctionGenerator = FunctionGenerator;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/header.ts":
|
||||
/*!***********************!*\
|
||||
!*** ./src/header.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");
|
||||
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();
|
||||
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();
|
||||
}
|
||||
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);
|
||||
}
|
||||
addApiFunctionByName(name, jsName = null) {
|
||||
const func = this.api.getFunction(name);
|
||||
if (func === null)
|
||||
throw new Error("Function not in API: " + name);
|
||||
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"]);
|
||||
// OPT: 7. expose class and constructor
|
||||
}
|
||||
addApiStructByName(structName, destructorName = null, options) {
|
||||
const struct = this.api.getStruct(structName);
|
||||
if (!struct)
|
||||
throw new Error("Struct not in API: " + structName);
|
||||
let destructor = 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);
|
||||
}
|
||||
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;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "fs":
|
||||
/*!*********************!*\
|
||||
!*** external "fs" ***!
|
||||
\*********************/
|
||||
/***/ ((module) => {
|
||||
|
||||
module.exports = require("fs");
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
||||
/************************************************************************/
|
||||
/******/ // The module cache
|
||||
/******/ var __webpack_module_cache__ = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/ // Check if module is in cache
|
||||
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
||||
/******/ if (cachedModule !== undefined) {
|
||||
/******/ return cachedModule.exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = __webpack_module_cache__[moduleId] = {
|
||||
/******/ // no module.id needed
|
||||
/******/ // no module.loaded needed
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/************************************************************************/
|
||||
var __webpack_exports__ = {};
|
||||
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
|
||||
(() => {
|
||||
var exports = __webpack_exports__;
|
||||
/*!**********************!*\
|
||||
!*** ./src/index.ts ***!
|
||||
\**********************/
|
||||
|
||||
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());
|
||||
}
|
||||
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);
|
||||
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);
|
||||
texture_gen.addApiStructByName("Image", "UnloadImage");
|
||||
writeHeader(texture_gen, "src/bindings/js_raylib_texture.h");
|
||||
}
|
||||
main();
|
||||
|
||||
})();
|
||||
|
||||
/******/ })()
|
||||
;
|
11
main.js
11
main.js
|
@ -1,6 +1,15 @@
|
|||
import { setWindowTitle, setWindowPosition } from "raylib.core"
|
||||
import { loadImage, Image } from "raylib.texture"
|
||||
import { gc } from "std"
|
||||
|
||||
console.log(loadImage("assets/planet00.png"))
|
||||
|
||||
const img = new Image("assets/planet00.png")
|
||||
|
||||
gc()
|
||||
console.log(img.width)
|
||||
|
||||
setWindowTitle("My Window")
|
||||
setWindowPosition(50,50)
|
||||
setWindowPosition(1920,50)
|
||||
|
||||
|
||||
|
|
|
@ -12,66 +12,48 @@
|
|||
#define countof(x) (sizeof(x) / sizeof((x)[0]))
|
||||
#endif
|
||||
|
||||
// 1. class id
|
||||
static JSClassID js_image_class_id;
|
||||
|
||||
typedef struct {
|
||||
Image *image;
|
||||
JSContext *ctx;
|
||||
JSValue this_obj;
|
||||
} JSImage;
|
||||
|
||||
// 2. finalize
|
||||
static void js_image_finalizer(JSRuntime *rt, JSValue val) {
|
||||
JSImage *js_image = JS_GetOpaque(val, js_image_class_id);
|
||||
if (js_image) {
|
||||
UnloadImage(*js_image->image);
|
||||
js_free(js_image->ctx, js_image);
|
||||
Image *image = JS_GetOpaque(val, js_image_class_id);
|
||||
if (image) {
|
||||
puts("Finalize image");
|
||||
UnloadImage(*image);
|
||||
js_free_rt(rt, image);
|
||||
}
|
||||
}
|
||||
|
||||
static JSValue js_image_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) {
|
||||
const char *filename = JS_ToCString(ctx, argv[0]);
|
||||
Image image = LoadImage(filename);
|
||||
JS_FreeCString(ctx, filename);
|
||||
JSImage *js_image = js_malloc(ctx, sizeof(JSImage));
|
||||
js_image->ctx = ctx;
|
||||
js_image->image = js_malloc(ctx, sizeof(Image));
|
||||
*js_image->image = image;
|
||||
js_image->this_obj = JS_UNDEFINED;
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_image_class_id);
|
||||
js_image->this_obj = obj;
|
||||
JS_SetOpaque(obj, js_image);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 3. getter/setter
|
||||
static JSValue js_image_get_width(JSContext *ctx, JSValueConst this_val) {
|
||||
JSImage *js_image = JS_GetOpaque2(ctx, this_val, js_image_class_id);
|
||||
if (!js_image) {
|
||||
Image *image = JS_GetOpaque2(ctx, this_val, js_image_class_id);
|
||||
if (!image) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
int width = js_image->image->width;
|
||||
int width = image->width;
|
||||
return JS_NewInt32(ctx, width);
|
||||
}
|
||||
|
||||
static JSValue js_image_get_height(JSContext *ctx, JSValueConst this_val) {
|
||||
JSImage *js_image = JS_GetOpaque2(ctx, this_val, js_image_class_id);
|
||||
if (!js_image) {
|
||||
Image *image = JS_GetOpaque2(ctx, this_val, js_image_class_id);
|
||||
if (!image) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
int height = js_image->image->height;
|
||||
int height = image->height;
|
||||
return JS_NewInt32(ctx, height);
|
||||
}
|
||||
|
||||
// 4. class members
|
||||
static const JSCFunctionListEntry js_image_proto_funcs[] = {
|
||||
JS_CGETSET_DEF("width", js_image_get_width, NULL),
|
||||
JS_CGETSET_DEF("height", js_image_get_height, NULL),
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Image", JS_PROP_CONFIGURABLE),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_raylib_texture_funcs[] = {
|
||||
JS_CFUNC_DEF("open", 2, js_std_open )
|
||||
};
|
||||
|
||||
static int js_raylib_texture_init(JSContext *ctx, JSModuleDef *m) {
|
||||
// 5. class declaration
|
||||
static int js_declare_image(JSContext *ctx, JSModuleDef *m){
|
||||
// Define image struct
|
||||
JS_NewClassID(&js_image_class_id);
|
||||
JSClassDef js_image_class_def = {
|
||||
|
@ -82,9 +64,36 @@ static int js_raylib_texture_init(JSContext *ctx, JSModuleDef *m) {
|
|||
JSValue proto = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, proto, js_image_proto_funcs, countof(js_image_proto_funcs));
|
||||
JS_SetClassProto(ctx, js_image_class_id, proto);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static JSValue js_LoadImage(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) {
|
||||
const char *filename = JS_ToCString(ctx, argv[0]);
|
||||
|
||||
Image _struct = LoadImage(filename);
|
||||
Image* ptr = (Image*)js_malloc(ctx, sizeof(Image));
|
||||
*ptr = _struct;
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_image_class_id);
|
||||
JS_SetOpaque(obj, ptr);
|
||||
|
||||
JS_FreeCString(ctx, filename);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_raylib_texture_funcs[] = {
|
||||
JS_CFUNC_DEF("loadImage", 1, js_LoadImage)
|
||||
};
|
||||
|
||||
static int js_raylib_texture_init(JSContext *ctx, JSModuleDef *m) {
|
||||
// 6. call declaration
|
||||
js_declare_image(ctx, m);
|
||||
|
||||
JS_SetModuleExportList(ctx, m, js_raylib_texture_funcs, countof(js_raylib_texture_funcs));
|
||||
|
||||
// Implement constructor
|
||||
JSValue constr = JS_NewCFunction2(ctx, js_LoadImage, "Image", 4, JS_CFUNC_constructor_or_func, 0);
|
||||
JS_SetModuleExport(ctx, m, "Image", constr);
|
||||
|
||||
// TODO export module constants
|
||||
//JS_SetModuleExport(ctx, m, "in", js_new_std_file(ctx, stdin, FALSE, FALSE));
|
||||
|
||||
|
@ -101,7 +110,7 @@ JSModuleDef *js_init_module_raylib_texture(JSContext *ctx, const char *module_na
|
|||
JS_AddModuleExportList(ctx, m, js_raylib_texture_funcs,countof(js_raylib_texture_funcs));
|
||||
|
||||
//TODO export module contants
|
||||
//JS_AddModuleExport(ctx, m, "in");
|
||||
JS_AddModuleExport(ctx, m, "Image");
|
||||
|
||||
return m;
|
||||
}
|
||||
|
|
|
@ -1,75 +1,63 @@
|
|||
|
||||
#ifndef JS_raylib_core
|
||||
#define JS_raylib_core
|
||||
#ifndef JS_raylib_core_GUARD
|
||||
#define JS_raylib_core_GUARD
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <quickjs.h>
|
||||
#include <raylib.h>
|
||||
|
||||
#ifndef countof
|
||||
#define countof(x) (sizeof(x) / sizeof((x)[0]))
|
||||
#endif
|
||||
|
||||
static JSValue js_setWindowTitle(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv){
|
||||
if(!JS_IsString(argv[0])) return JS_ThrowReferenceError(ctx, "SetWindowTitle argument title (0) needs to be a string");
|
||||
const char * title = JS_ToCString(ctx, argv[0]);
|
||||
SetWindowTitle(title);
|
||||
JS_FreeCString(ctx, title);
|
||||
|
||||
static JSValue js_setWindowTitle(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) {
|
||||
const char * title = JS_ToCString(ctx, argv[0]);
|
||||
if(title == NULL) return JS_EXCEPTION;
|
||||
SetWindowTitle(title);
|
||||
JS_FreeCString(ctx, title);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue js_setWindowPosition(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv){
|
||||
if(!JS_IsNumber(argv[0])) return JS_ThrowReferenceError(ctx, "SetWindowPosition argument x (0) needs to be a number");
|
||||
if(!JS_IsNumber(argv[1])) return JS_ThrowReferenceError(ctx, "SetWindowPosition argument y (1) needs to be a number");
|
||||
int x; JS_ToInt32(ctx, &x, argv[0]);
|
||||
int y; JS_ToInt32(ctx, &y, argv[1]);
|
||||
SetWindowPosition(x, y);
|
||||
|
||||
static JSValue js_setWindowPosition(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) {
|
||||
int x;
|
||||
JS_ToInt32(ctx, &x, argv[0]);
|
||||
int y;
|
||||
JS_ToInt32(ctx, &y, argv[1]);
|
||||
SetWindowPosition(x, y);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue js_beginDrawing(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv){
|
||||
|
||||
|
||||
BeginDrawing();
|
||||
|
||||
static JSValue js_beginDrawing(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) {
|
||||
BeginDrawing();
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue js_endDrawing(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv){
|
||||
|
||||
|
||||
EndDrawing();
|
||||
|
||||
static JSValue js_endDrawing(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) {
|
||||
EndDrawing();
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_raylib_core_funcs[] = {
|
||||
JS_CFUNC_DEF("setWindowTitle", 1, js_setWindowTitle),
|
||||
JS_CFUNC_DEF("setWindowPosition", 2, js_setWindowPosition),
|
||||
JS_CFUNC_DEF("beginDrawing", 0, js_beginDrawing),
|
||||
JS_CFUNC_DEF("endDrawing", 0, js_endDrawing)
|
||||
JS_CFUNC_DEF("setWindowTitle",1,js_setWindowTitle),
|
||||
JS_CFUNC_DEF("setWindowPosition",2,js_setWindowPosition),
|
||||
JS_CFUNC_DEF("beginDrawing",0,js_beginDrawing),
|
||||
JS_CFUNC_DEF("endDrawing",0,js_endDrawing),
|
||||
};
|
||||
|
||||
static int js_raylib_core_init(JSContext *ctx, JSModuleDef *m){
|
||||
JS_SetModuleExportList(ctx, m, js_raylib_core_funcs,
|
||||
countof(js_raylib_core_funcs));
|
||||
int js_raylib_core_init(JSContext * ctx, JSModuleDef * m) {
|
||||
JS_SetModuleExportList(ctx, m,js_raylib_core_funcs,countof(js_raylib_core_funcs));
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSModuleDef *js_init_module_raylib_core(JSContext *ctx, const char *module_name)
|
||||
{
|
||||
JSModuleDef * js_init_module_raylib_core(JSContext * ctx, const char * module_name) {
|
||||
JSModuleDef *m;
|
||||
m = JS_NewCModule(ctx, module_name, js_raylib_core_init);
|
||||
if (!m)
|
||||
return NULL;
|
||||
|
||||
JS_AddModuleExportList(ctx, m, js_raylib_core_funcs,
|
||||
countof(js_raylib_core_funcs));
|
||||
|
||||
if(!m) return NULL;
|
||||
JS_AddModuleExportList(ctx, m, js_raylib_core_funcs, countof(js_raylib_core_funcs));
|
||||
return m;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // JS_raylib_core_GUARD
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
#ifndef JS_raylib_texture_GUARD
|
||||
#define JS_raylib_texture_GUARD
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <quickjs.h>
|
||||
#include <raylib.h>
|
||||
|
||||
#ifndef countof
|
||||
#define countof(x) (sizeof(x) / sizeof((x)[0]))
|
||||
#endif
|
||||
|
||||
static JSClassID js_Image_class_id;
|
||||
|
||||
static void js_Image_finalizer(JSRuntime * rt, JSValue val) {
|
||||
Image* ptr = JS_GetOpaque(val, js_Image_class_id);
|
||||
if(ptr) {
|
||||
puts("Finalize Image");
|
||||
UnloadImage(*ptr);
|
||||
js_free_rt(rt, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_Image_proto_funcs[] = {
|
||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]","Image", JS_PROP_CONFIGURABLE),
|
||||
};
|
||||
|
||||
static int js_declare_Image(JSContext * ctx, JSModuleDef * m) {
|
||||
JS_NewClassID(&js_Image_class_id);
|
||||
JSClassDef js_Image_def = { .class_name = "Image", .finalizer = js_Image_finalizer };
|
||||
JS_NewClass(JS_GetRuntime(ctx), js_Image_class_id, &js_Image_def);
|
||||
JSValue proto = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, proto, js_Image_proto_funcs, countof(js_Image_proto_funcs));
|
||||
JS_SetClassProto(ctx, js_Image_class_id, proto);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_raylib_texture_funcs[] = {
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
JSModuleDef * js_init_module_raylib_texture(JSContext * ctx, const char * module_name) {
|
||||
JSModuleDef *m;
|
||||
m = JS_NewCModule(ctx, module_name, js_raylib_texture_init);
|
||||
if(!m) return NULL;
|
||||
JS_AddModuleExportList(ctx, m, js_raylib_texture_funcs, countof(js_raylib_texture_funcs));
|
||||
return m;
|
||||
}
|
||||
|
||||
#endif // JS_raylib_texture_GUARD
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include "common.h"
|
||||
#include "bindings/js_raylib_core.h"
|
||||
#include "bindings/_js_raylib_texture.h"
|
||||
#include "bindings/js_raylib_texture.h"
|
||||
|
||||
static JSContext *JS_NewCustomContext(JSRuntime *rt);
|
||||
static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
|
||||
|
@ -90,6 +90,7 @@ static JSContext *JS_NewCustomContext(JSRuntime *rt)
|
|||
js_init_module_std(ctx, "std");
|
||||
//js_init_module_os(ctx, "os");
|
||||
js_init_module_raylib_core(ctx, "raylib.core");
|
||||
js_init_module_raylib_texture(ctx, "raylib.texture");
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit a48bb6e1ed7b33190e486ba65b7875f0dff73701
|
||||
Subproject commit 5573f0f1c7b29bfe46d0b70487e4adb4d01cba62
|
Loading…
Reference in New Issue