rayjs/generate-bindings.js

608 lines
21 KiB
JavaScript

/******/ (() => { // 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();
})();
/******/ })()
;