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,4 +1,5 @@
|
||||||
[
|
{
|
||||||
|
"headers": [
|
||||||
{
|
{
|
||||||
"name": "raylib_core",
|
"name": "raylib_core",
|
||||||
"functions": [
|
"functions": [
|
||||||
|
@ -13,3 +14,4 @@
|
||||||
"functions": []
|
"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 { 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")
|
setWindowTitle("My Window")
|
||||||
setWindowPosition(50,50)
|
setWindowPosition(1920,50)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,66 +12,48 @@
|
||||||
#define countof(x) (sizeof(x) / sizeof((x)[0]))
|
#define countof(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// 1. class id
|
||||||
static JSClassID js_image_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) {
|
static void js_image_finalizer(JSRuntime *rt, JSValue val) {
|
||||||
JSImage *js_image = JS_GetOpaque(val, js_image_class_id);
|
Image *image = JS_GetOpaque(val, js_image_class_id);
|
||||||
if (js_image) {
|
if (image) {
|
||||||
UnloadImage(*js_image->image);
|
puts("Finalize image");
|
||||||
js_free(js_image->ctx, js_image);
|
UnloadImage(*image);
|
||||||
|
js_free_rt(rt, image);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSValue js_image_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) {
|
// 3. getter/setter
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSValue js_image_get_width(JSContext *ctx, JSValueConst this_val) {
|
static JSValue js_image_get_width(JSContext *ctx, JSValueConst this_val) {
|
||||||
JSImage *js_image = JS_GetOpaque2(ctx, this_val, js_image_class_id);
|
Image *image = JS_GetOpaque2(ctx, this_val, js_image_class_id);
|
||||||
if (!js_image) {
|
if (!image) {
|
||||||
return JS_EXCEPTION;
|
return JS_EXCEPTION;
|
||||||
}
|
}
|
||||||
int width = js_image->image->width;
|
int width = image->width;
|
||||||
return JS_NewInt32(ctx, width);
|
return JS_NewInt32(ctx, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSValue js_image_get_height(JSContext *ctx, JSValueConst this_val) {
|
static JSValue js_image_get_height(JSContext *ctx, JSValueConst this_val) {
|
||||||
JSImage *js_image = JS_GetOpaque2(ctx, this_val, js_image_class_id);
|
Image *image = JS_GetOpaque2(ctx, this_val, js_image_class_id);
|
||||||
if (!js_image) {
|
if (!image) {
|
||||||
return JS_EXCEPTION;
|
return JS_EXCEPTION;
|
||||||
}
|
}
|
||||||
int height = js_image->image->height;
|
int height = image->height;
|
||||||
return JS_NewInt32(ctx, height);
|
return JS_NewInt32(ctx, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. class members
|
||||||
static const JSCFunctionListEntry js_image_proto_funcs[] = {
|
static const JSCFunctionListEntry js_image_proto_funcs[] = {
|
||||||
JS_CGETSET_DEF("width", js_image_get_width, NULL),
|
JS_CGETSET_DEF("width", js_image_get_width, NULL),
|
||||||
JS_CGETSET_DEF("height", js_image_get_height, NULL),
|
JS_CGETSET_DEF("height", js_image_get_height, NULL),
|
||||||
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Image", JS_PROP_CONFIGURABLE),
|
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Image", JS_PROP_CONFIGURABLE),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const JSCFunctionListEntry js_raylib_texture_funcs[] = {
|
// 5. class declaration
|
||||||
JS_CFUNC_DEF("open", 2, js_std_open )
|
static int js_declare_image(JSContext *ctx, JSModuleDef *m){
|
||||||
};
|
|
||||||
|
|
||||||
static int js_raylib_texture_init(JSContext *ctx, JSModuleDef *m) {
|
|
||||||
// Define image struct
|
// Define image struct
|
||||||
JS_NewClassID(&js_image_class_id);
|
JS_NewClassID(&js_image_class_id);
|
||||||
JSClassDef js_image_class_def = {
|
JSClassDef js_image_class_def = {
|
||||||
|
@ -82,9 +64,36 @@ static int js_raylib_texture_init(JSContext *ctx, JSModuleDef *m) {
|
||||||
JSValue proto = JS_NewObject(ctx);
|
JSValue proto = JS_NewObject(ctx);
|
||||||
JS_SetPropertyFunctionList(ctx, proto, js_image_proto_funcs, countof(js_image_proto_funcs));
|
JS_SetPropertyFunctionList(ctx, proto, js_image_proto_funcs, countof(js_image_proto_funcs));
|
||||||
JS_SetClassProto(ctx, js_image_class_id, proto);
|
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));
|
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
|
// TODO export module constants
|
||||||
//JS_SetModuleExport(ctx, m, "in", js_new_std_file(ctx, stdin, FALSE, FALSE));
|
//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));
|
JS_AddModuleExportList(ctx, m, js_raylib_texture_funcs,countof(js_raylib_texture_funcs));
|
||||||
|
|
||||||
//TODO export module contants
|
//TODO export module contants
|
||||||
//JS_AddModuleExport(ctx, m, "in");
|
JS_AddModuleExport(ctx, m, "Image");
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +1,42 @@
|
||||||
|
#ifndef JS_raylib_core_GUARD
|
||||||
#ifndef JS_raylib_core
|
#define JS_raylib_core_GUARD
|
||||||
#define JS_raylib_core
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <quickjs.h>
|
#include <quickjs.h>
|
||||||
|
#include <raylib.h>
|
||||||
|
|
||||||
#ifndef countof
|
#ifndef countof
|
||||||
#define countof(x) (sizeof(x) / sizeof((x)[0]))
|
#define countof(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static JSValue js_setWindowTitle(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) {
|
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]);
|
const char * title = JS_ToCString(ctx, argv[0]);
|
||||||
|
if(title == NULL) return JS_EXCEPTION;
|
||||||
SetWindowTitle(title);
|
SetWindowTitle(title);
|
||||||
JS_FreeCString(ctx, title);
|
JS_FreeCString(ctx, title);
|
||||||
return JS_UNDEFINED;
|
return JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSValue js_setWindowPosition(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) {
|
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");
|
int x;
|
||||||
if(!JS_IsNumber(argv[1])) return JS_ThrowReferenceError(ctx, "SetWindowPosition argument y (1) needs to be a number");
|
JS_ToInt32(ctx, &x, argv[0]);
|
||||||
int x; JS_ToInt32(ctx, &x, argv[0]);
|
int y;
|
||||||
int y; JS_ToInt32(ctx, &y, argv[1]);
|
JS_ToInt32(ctx, &y, argv[1]);
|
||||||
SetWindowPosition(x, y);
|
SetWindowPosition(x, y);
|
||||||
|
|
||||||
return JS_UNDEFINED;
|
return JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSValue js_beginDrawing(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) {
|
static JSValue js_beginDrawing(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) {
|
||||||
|
|
||||||
|
|
||||||
BeginDrawing();
|
BeginDrawing();
|
||||||
|
|
||||||
return JS_UNDEFINED;
|
return JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSValue js_endDrawing(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) {
|
static JSValue js_endDrawing(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) {
|
||||||
|
|
||||||
|
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
|
|
||||||
return JS_UNDEFINED;
|
return JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,26 +44,20 @@ static const JSCFunctionListEntry js_raylib_core_funcs[] = {
|
||||||
JS_CFUNC_DEF("setWindowTitle",1,js_setWindowTitle),
|
JS_CFUNC_DEF("setWindowTitle",1,js_setWindowTitle),
|
||||||
JS_CFUNC_DEF("setWindowPosition",2,js_setWindowPosition),
|
JS_CFUNC_DEF("setWindowPosition",2,js_setWindowPosition),
|
||||||
JS_CFUNC_DEF("beginDrawing",0,js_beginDrawing),
|
JS_CFUNC_DEF("beginDrawing",0,js_beginDrawing),
|
||||||
JS_CFUNC_DEF("endDrawing", 0, js_endDrawing)
|
JS_CFUNC_DEF("endDrawing",0,js_endDrawing),
|
||||||
};
|
};
|
||||||
|
|
||||||
static int js_raylib_core_init(JSContext *ctx, JSModuleDef *m){
|
int js_raylib_core_init(JSContext * ctx, JSModuleDef * m) {
|
||||||
JS_SetModuleExportList(ctx, m, js_raylib_core_funcs,
|
JS_SetModuleExportList(ctx, m,js_raylib_core_funcs,countof(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;
|
JSModuleDef *m;
|
||||||
m = JS_NewCModule(ctx, module_name, js_raylib_core_init);
|
m = JS_NewCModule(ctx, module_name, js_raylib_core_init);
|
||||||
if (!m)
|
if(!m) return NULL;
|
||||||
return NULL;
|
JS_AddModuleExportList(ctx, m, js_raylib_core_funcs, countof(js_raylib_core_funcs));
|
||||||
|
|
||||||
JS_AddModuleExportList(ctx, m, js_raylib_core_funcs,
|
|
||||||
countof(js_raylib_core_funcs));
|
|
||||||
|
|
||||||
return m;
|
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 "common.h"
|
||||||
#include "bindings/js_raylib_core.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 JSContext *JS_NewCustomContext(JSRuntime *rt);
|
||||||
static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
|
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_std(ctx, "std");
|
||||||
//js_init_module_os(ctx, "os");
|
//js_init_module_os(ctx, "os");
|
||||||
js_init_module_raylib_core(ctx, "raylib.core");
|
js_init_module_raylib_core(ctx, "raylib.core");
|
||||||
|
js_init_module_raylib_texture(ctx, "raylib.texture");
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit a48bb6e1ed7b33190e486ba65b7875f0dff73701
|
Subproject commit 5573f0f1c7b29bfe46d0b70487e4adb4d01cba62
|
Loading…
Reference in New Issue