add binding generator

This commit is contained in:
Alexander Klingenbeck (SHS DI SY R&D DEV4) 2023-05-06 23:43:40 +02:00
parent 63836f34d0
commit e2d140e25b
22 changed files with 4323 additions and 96 deletions

BIN
assets/planet00.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

View File

@ -1,4 +1,5 @@
[ {
"headers": [
{ {
"name": "raylib_core", "name": "raylib_core",
"functions": [ "functions": [
@ -13,3 +14,4 @@
"functions": [] "functions": []
} }
] ]
}

1
bindings/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules/

2912
bindings/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

20
bindings/package.json Normal file
View File

@ -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"
}
}

39
bindings/src/api.ts Normal file
View File

@ -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)
}
}

69
bindings/src/function.ts Normal file
View File

@ -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;
}
}
}

View File

@ -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),`)
}
}

193
bindings/src/generation.ts Normal file
View File

@ -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)
}
}

102
bindings/src/header.ts Normal file
View File

@ -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)
}
}

31
bindings/src/index.ts Normal file
View File

@ -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()

View File

@ -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
}

63
bindings/src/struct.ts Normal file
View File

@ -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")
})
}
}

11
bindings/tsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"outDir": "dist",
"esModuleInterop": true,
"resolveJsonModule": true,
"strict": true
}
}

View File

@ -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, '..'),
},
};

608
generate-bindings.js Normal file
View File

@ -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
View File

@ -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)

View File

@ -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;
} }

View File

@ -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

View File

@ -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

View File

@ -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;
} }

2
thirdparty/raylib vendored

@ -1 +1 @@
Subproject commit a48bb6e1ed7b33190e486ba65b7875f0dff73701 Subproject commit 5573f0f1c7b29bfe46d0b70487e4adb4d01cba62