rayjs/generate-bindings.js

1481 lines
58 KiB
JavaScript
Raw Normal View History

2023-05-06 21:43:40 +00:00
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ "./src/generation.ts":
/*!***************************!*\
!*** ./src/generation.ts ***!
\***************************/
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({ value: true }));
2023-05-08 14:43:50 +00:00
exports.CodeGenerator = exports.GenericCodeGenerator = exports.CodeWriter = exports.StringWriter = void 0;
2023-05-06 21:43:40 +00:00
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 = {}));
2023-05-08 14:43:50 +00:00
class GenericCodeGenerator {
2023-05-06 21:43:40 +00:00
constructor() {
this.children = [];
this.text = [];
this.tokens = [];
2023-05-08 14:43:50 +00:00
this.tags = {};
}
getTag(key) {
return this.tags[key];
}
setTag(key, value) {
this.tags[key] = value;
2023-05-06 21:43:40 +00:00
}
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) {
2023-05-08 14:43:50 +00:00
if (!sub)
sub = this.createGenerator();
2023-05-06 21:43:40 +00:00
this.tokens.push(Token.GOSUB);
this.children.push(sub);
2023-05-08 14:43:50 +00:00
return sub;
2023-05-06 21:43:40 +00:00
}
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);
}
2023-05-08 14:43:50 +00:00
function(name, returnType, args, isStatic, func) {
const sub = this.createGenerator();
sub.setTag("_type", "function-body");
sub.setTag("_name", name);
sub.setTag("_isStatic", isStatic);
sub.setTag("_returnType", returnType);
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(sub);
this.unindent();
this.line("}");
this.breakLine();
if (func)
func(sub);
return sub;
}
if(condition, funIf) {
2023-05-06 21:43:40 +00:00
this.line("if(" + condition + ") {");
this.indent();
2023-05-08 14:43:50 +00:00
const sub = this.createGenerator();
sub.setTag("_type", "if-body");
sub.setTag("_condition", condition);
this.child(sub);
2023-05-06 21:43:40 +00:00
this.unindent();
this.line("}");
2023-05-08 14:43:50 +00:00
if (funIf)
funIf(sub);
return sub;
2023-05-06 21:43:40 +00:00
}
2023-05-08 14:43:50 +00:00
else(funElse) {
this.line("else {");
this.indent();
const sub = this.createGenerator();
sub.setTag("_type", "else-body");
this.child(sub);
this.unindent();
this.line("}");
if (funElse)
funElse(sub);
return sub;
2023-05-06 21:43:40 +00:00
}
2023-05-08 14:43:50 +00:00
returnExp(exp) {
this.statement("return " + exp);
2023-05-06 21:43:40 +00:00
}
include(name) {
this.line("#include <" + name + ">");
}
2023-06-02 06:03:36 +00:00
for(indexVar, lengthVar) {
this.line(`for(int ${indexVar}; i < ${lengthVar}; i++){`);
this.indent();
const child = this.child();
this.unindent();
this.line("}");
return child;
}
2023-05-08 14:43:50 +00:00
header(guard, fun) {
this.line("#ifndef " + guard);
this.line("#define " + guard);
this.breakLine();
const sub = this.child();
sub.setTag("_type", "header-body");
sub.setTag("_guardName", guard);
this.line("#endif // " + guard);
if (fun)
fun(sub);
return sub;
}
2023-05-10 21:26:53 +00:00
declareStruct(structName, varName, values, isStatic = false) {
if (isStatic)
this.inline("static ");
this.statement(`${structName} ${varName} = { ${values.join(', ')} }`);
}
2023-05-16 06:28:30 +00:00
switch(switchVar) {
this.line(`switch(${switchVar}) {`);
this.indent();
const body = this.child();
this.unindent();
this.line("}");
return body;
}
case(value) {
this.line(`case ${value}:`);
}
defaultBreak() {
this.line("default:");
this.line("{");
this.indent();
const body = this.child();
this.statement("break");
this.unindent();
this.line("}");
return body;
}
caseBreak(value) {
this.case(value);
this.line("{");
this.indent();
const body = this.child();
this.statement("break");
this.unindent();
this.line("}");
return body;
}
2023-05-08 14:43:50 +00:00
}
exports.GenericCodeGenerator = GenericCodeGenerator;
class CodeGenerator extends GenericCodeGenerator {
createGenerator() {
return new CodeGenerator();
}
2023-05-06 21:43:40 +00:00
}
2023-05-08 14:43:50 +00:00
exports.CodeGenerator = CodeGenerator;
2023-05-26 20:09:53 +00:00
/***/ }),
/***/ "./src/header-parser.ts":
/*!******************************!*\
!*** ./src/header-parser.ts ***!
\******************************/
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.HeaderParser = void 0;
class HeaderParser {
parseEnums(input) {
const matches = [...input.matchAll(/((?:\/\/.+\n)*)typedef enum {\n([^}]+)} ([^;]+)/gm)];
return matches.map(groups => {
return {
description: this.parseComments(groups[1]),
values: this.parseEnumValues(groups[2]),
name: groups[3],
};
});
}
parseEnumValues(input) {
let lastNumber = 0;
return input.split('\n')
.map(line => line.trim().match(/([^ ,]+)(?: = ([0-9]+))?,?(?: *)(?:\/\/ (.+))?/))
.filter(x => x !== null && !x[0].startsWith("/"))
.map(groups => {
let val = lastNumber = groups[2] ? parseInt(groups[2]) : lastNumber;
lastNumber++;
return {
name: groups[1],
description: groups[3] || "",
value: val
};
});
}
parseComments(input) {
return input.split('\n').map(x => x.replace("// ", "")).join('\n').trim();
}
parseFunctionDefinitions(input) {
2023-05-26 22:47:44 +00:00
const matches = [...input.matchAll(/^[A-Z]+ (.+?)(\w+)\(([^\)]+)\);(?: +\/\/ (.+))?$/gm)];
2023-05-26 20:09:53 +00:00
return matches.map(groups => ({
returnType: groups[1].trim(),
name: groups[2],
params: this.parseFunctionArgs(groups[3]),
description: groups[4] || ""
}));
}
2023-05-26 22:47:44 +00:00
parseFunctions(input, noPrefix = false) {
const matches = noPrefix
? [...input.matchAll(/((?:\/\/.+\n)+)^(.+?)(\w+)\(([^\)]+)\)/gm)]
: [...input.matchAll(/((?:\/\/.+\n)+)^[A-Z]+ (.+?)(\w+)\(([^\)]+)\)/gm)];
2023-05-26 20:09:53 +00:00
return matches.map(groups => ({
2023-05-26 22:47:44 +00:00
returnType: groups[2].trim(),
name: groups[3],
params: this.parseFunctionArgs(groups[4]),
description: groups[1] ? this.parseComments(groups[1]) : ""
2023-05-26 20:09:53 +00:00
}));
}
parseFunctionArgs(input) {
return input.split(',').filter(x => x !== 'void').map(arg => {
arg = arg.trim().replace(" *", "* ");
const frags = arg.split(' ');
const name = frags.pop();
const type = frags.join(' ').replace("*", " *");
return { name: name || "", type: type.trim() };
});
}
}
exports.HeaderParser = HeaderParser;
2023-05-08 14:43:50 +00:00
/***/ }),
/***/ "./src/quickjs.ts":
/*!************************!*\
!*** ./src/quickjs.ts ***!
\************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.QuickJsGenerator = exports.GenericQuickJsGenerator = exports.QuickJsHeader = void 0;
const fs_1 = __webpack_require__(/*! fs */ "fs");
const generation_1 = __webpack_require__(/*! ./generation */ "./src/generation.ts");
class QuickJsHeader {
constructor(name) {
2023-05-06 21:43:40 +00:00
this.name = name;
2023-05-08 21:37:58 +00:00
this.structLookup = {};
2023-05-08 14:43:50 +00:00
const root = this.root = new QuickJsGenerator();
const body = this.body = root.header("JS_" + this.name + "_GUARD");
const includes = this.includes = body.child();
includes.include("stdio.h");
includes.include("stdlib.h");
includes.include("string.h");
includes.breakLine();
includes.include("quickjs.h");
body.breakLine();
body.line("#ifndef countof");
body.line("#define countof(x) (sizeof(x) / sizeof((x)[0]))");
body.line("#endif");
body.breakLine();
2023-05-14 20:19:47 +00:00
this.definitions = body.child();
2023-05-08 14:43:50 +00:00
body.breakLine();
this.structs = body.child();
this.functions = body.child();
this.moduleFunctionList = body.jsFunctionList("js_" + name + "_funcs");
const moduleInitFunc = body.function("js_" + this.name + "_init", "int", [{ type: "JSContext *", name: "ctx" }, { type: "JSModuleDef *", name: "m" }], true);
const moduleInit = this.moduleInit = moduleInitFunc.child();
moduleInit.statement(`JS_SetModuleExportList(ctx, m,${this.moduleFunctionList.getTag("_name")},countof(${this.moduleFunctionList.getTag("_name")}))`);
moduleInitFunc.returnExp("0");
const moduleEntryFunc = body.function("js_init_module_" + this.name, "JSModuleDef *", [{ type: "JSContext *", name: "ctx" }, { type: "const char *", name: "module_name" }], false);
const moduleEntry = this.moduleEntry = moduleEntryFunc.child();
moduleEntry.statement("JSModuleDef *m");
moduleEntry.statement(`m = JS_NewCModule(ctx, module_name, ${moduleInitFunc.getTag("_name")})`);
moduleEntry.statement("if(!m) return NULL");
moduleEntry.statement(`JS_AddModuleExportList(ctx, m, ${this.moduleFunctionList.getTag("_name")}, countof(${this.moduleFunctionList.getTag("_name")}))`);
moduleEntryFunc.statement("return m");
}
2023-05-08 21:37:58 +00:00
registerStruct(struct, classId) {
this.structLookup[struct] = classId;
}
2023-05-08 14:43:50 +00:00
writeTo(filename) {
const writer = new generation_1.CodeWriter();
writer.writeGenerator(this.root);
(0, fs_1.writeFileSync)(filename, writer.toString());
}
}
exports.QuickJsHeader = QuickJsHeader;
class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
jsBindingFunction(jsName) {
const args = [
{ type: "JSContext *", name: "ctx" },
{ type: "JSValueConst", name: "this_val" },
{ type: "int", name: "argc" },
{ type: "JSValueConst *", name: "argv" },
];
const sub = this.function("js_" + jsName, "JSValue", args, true);
return sub;
}
2023-06-03 07:15:38 +00:00
jsToC(type, name, src, classIds = {}, supressDeclaration = false, typeAlias) {
switch (typeAlias ?? type) {
// Array Buffer
case "const void *":
case "void *":
case "float *":
case "unsigned short *":
case "unsigned char *":
2023-05-24 20:47:17 +00:00
case "const unsigned char *":
this.declare(name + "_size", "size_t");
this.declare(name + "_js", "void *", false, `(void *)JS_GetArrayBuffer(ctx, &${name}_size, ${src})`);
this.if(name + "_js == NULL").returnExp("JS_EXCEPTION");
this.declare(name, type, false, "malloc(" + name + "_size)");
this.call("memcpy", ["(void *)" + name, "(const void *)" + name + "_js", name + "_size"]);
break;
// String
2023-05-08 14:43:50 +00:00
case "const char *":
2023-06-03 07:15:38 +00:00
//case "char *":
2023-05-16 09:59:29 +00:00
if (!supressDeclaration)
this.statement(`${type} ${name} = JS_IsNull(${src}) ? NULL : (${type})JS_ToCString(ctx, ${src})`);
2023-05-16 09:59:29 +00:00
else
this.statement(`${name} = JS_IsNull(${src}) ? NULL : (${type})JS_ToCString(ctx, ${src})`);
2023-05-08 14:43:50 +00:00
break;
2023-05-14 20:19:47 +00:00
case "double":
2023-05-16 09:59:29 +00:00
if (!supressDeclaration)
this.statement(`${type} ${name}`);
2023-05-14 20:19:47 +00:00
this.statement(`JS_ToFloat64(ctx, &${name}, ${src})`);
break;
2023-05-10 21:26:53 +00:00
case "float":
this.statement("double _double_" + name);
this.statement(`JS_ToFloat64(ctx, &_double_${name}, ${src})`);
2023-05-16 09:59:29 +00:00
if (!supressDeclaration)
this.statement(`${type} ${name} = (${type})_double_${name}`);
else
this.statement(`${name} = (${type})_double_${name}`);
2023-05-10 21:26:53 +00:00
break;
2023-05-08 14:43:50 +00:00
case "int":
2023-05-16 09:59:29 +00:00
if (!supressDeclaration)
this.statement(`${type} ${name}`);
2023-05-14 20:19:47 +00:00
this.statement(`JS_ToInt32(ctx, &${name}, ${src})`);
break;
2023-05-13 12:49:05 +00:00
case "unsigned int":
2023-05-16 09:59:29 +00:00
if (!supressDeclaration)
this.statement(`${type} ${name}`);
2023-05-14 20:19:47 +00:00
this.statement(`JS_ToUint32(ctx, &${name}, ${src})`);
2023-05-08 16:05:03 +00:00
break;
2023-05-08 21:37:58 +00:00
case "unsigned char":
2023-05-14 20:19:47 +00:00
this.statement("unsigned int _int_" + name);
this.statement(`JS_ToUint32(ctx, &_int_${name}, ${src})`);
2023-05-16 09:59:29 +00:00
if (!supressDeclaration)
this.statement(`${type} ${name} = (${type})_int_${name}`);
else
this.statement(`${name} = (${type})_int_${name}`);
2023-05-08 21:37:58 +00:00
break;
2023-05-14 20:19:47 +00:00
case "bool":
2023-05-16 09:59:29 +00:00
if (!supressDeclaration)
this.statement(`${type} ${name} = JS_ToBool(ctx, ${src})`);
else
this.statement(`${name} = JS_ToBool(ctx, ${src})`);
2023-05-14 20:19:47 +00:00
break;
2023-05-08 16:05:03 +00:00
default:
2023-05-14 20:19:47 +00:00
const isConst = type.startsWith('const');
2023-05-15 21:02:41 +00:00
const isPointer = type.endsWith(' *');
const classId = classIds[type.replace("const ", "").replace(" *", "")];
2023-05-10 21:26:53 +00:00
if (!classId)
throw new Error("Cannot convert into parameter type: " + type);
2023-05-15 21:02:41 +00:00
const suffix = isPointer ? "" : "_ptr";
this.jsOpqToStructPtr(type.replace(" *", ""), name + suffix, src, classId);
this.statement(`if(${name + suffix} == NULL) return JS_EXCEPTION`);
if (!isPointer)
this.declare(name, type, false, `*${name}_ptr`);
2023-05-08 16:05:03 +00:00
}
}
2023-05-08 21:37:58 +00:00
jsToJs(type, name, src, classIds = {}) {
2023-05-08 16:05:03 +00:00
switch (type) {
case "int":
2023-05-13 12:49:05 +00:00
case "long":
2023-05-08 21:37:58 +00:00
this.declare(name, 'JSValue', false, `JS_NewInt32(ctx, ${src})`);
2023-05-08 14:43:50 +00:00
break;
2023-05-14 20:19:47 +00:00
case "long":
this.declare(name, 'JSValue', false, `JS_NewInt64(ctx, ${src})`);
break;
case "unsigned int":
case "unsigned char":
this.declare(name, 'JSValue', false, `JS_NewUint32(ctx, ${src})`);
break;
2023-05-10 21:26:53 +00:00
case "bool":
this.declare(name, 'JSValue', false, `JS_NewBool(ctx, ${src})`);
break;
case "float":
2023-05-13 12:49:05 +00:00
case "double":
2023-05-10 21:26:53 +00:00
this.declare(name, 'JSValue', false, `JS_NewFloat64(ctx, ${src})`);
break;
2023-05-13 12:49:05 +00:00
case "const char *":
case "char *":
this.declare(name, 'JSValue', false, `JS_NewString(ctx, ${src})`);
break;
2023-05-24 20:47:17 +00:00
// case "unsigned char *":
// this.declare(name, 'JSValue', false, `JS_NewString(ctx, ${src})`)
// break;
2023-05-08 14:43:50 +00:00
default:
2023-05-08 21:37:58 +00:00
const classId = classIds[type];
if (!classId)
2023-05-10 21:26:53 +00:00
throw new Error("Cannot convert parameter type to Javascript: " + type);
2023-05-08 21:37:58 +00:00
this.jsStructToOpq(type, name, src, classId);
2023-05-08 14:43:50 +00:00
}
}
2023-05-08 21:37:58 +00:00
jsStructToOpq(structType, jsVar, srcVar, classId) {
2023-05-10 21:26:53 +00:00
this.declare(jsVar + "_ptr", structType + "*", false, `(${structType}*)js_malloc(ctx, sizeof(${structType}))`);
this.statement("*" + jsVar + "_ptr = " + srcVar);
2023-05-08 21:37:58 +00:00
this.declare(jsVar, "JSValue", false, `JS_NewObjectClass(ctx, ${classId})`);
2023-05-10 21:26:53 +00:00
this.call("JS_SetOpaque", [jsVar, jsVar + "_ptr"]);
2023-05-08 21:37:58 +00:00
}
2023-05-08 14:43:50 +00:00
jsCleanUpParameter(type, name) {
switch (type) {
case "char *":
2023-05-08 14:43:50 +00:00
case "const char *":
this.statement(`JS_FreeCString(ctx, ${name})`);
break;
case "const void *":
case "void *":
case "float *":
case "unsigned short *":
case "unsigned char *":
2023-05-24 20:47:17 +00:00
case "const unsigned char *":
this.statement(`free((void *)${name})`);
break;
2023-05-08 14:43:50 +00:00
default:
break;
}
}
jsFunctionList(name) {
this.line("static const JSCFunctionListEntry " + name + "[] = {");
2023-05-06 21:43:40 +00:00
this.indent();
2023-05-08 14:43:50 +00:00
const sub = this.createGenerator();
sub.setTag("_type", "js-function-list");
sub.setTag("_name", name);
this.child(sub);
2023-05-06 21:43:40 +00:00
this.unindent();
2023-05-08 14:43:50 +00:00
this.statement("}");
this.breakLine();
return sub;
}
jsFuncDef(jsName, numArgs, cName) {
this.line(`JS_CFUNC_DEF("${jsName}",${numArgs},${cName}),`);
}
jsClassId(id) {
this.declare(id, "JSClassID", true);
return id;
}
jsPropStringDef(key, value) {
this.line(`JS_PROP_STRING_DEF("${key}","${value}", JS_PROP_CONFIGURABLE),`);
}
2023-05-08 16:05:03 +00:00
jsGetSetDef(key, getFunc, setFunc) {
this.line(`JS_CGETSET_DEF("${key}",${getFunc || "NULL"},${setFunc || "NULL"}),`);
}
2023-05-08 14:43:50 +00:00
jsStructFinalizer(classId, structName, onFinalize) {
const args = [{ type: "JSRuntime *", name: "rt" }, { type: "JSValue", name: "val" }];
const body = this.function(`js_${structName}_finalizer`, "void", args, true);
body.statement(`${structName}* ptr = JS_GetOpaque(val, ${classId})`);
body.if("ptr", cond => {
//cond.call("TraceLog", ["LOG_INFO",`"Finalize ${structName} %p"`,"ptr"])
2023-05-08 14:43:50 +00:00
if (onFinalize)
onFinalize(cond, "ptr");
cond.call("js_free_rt", ["rt", "ptr"]);
});
return body;
}
jsClassDeclaration(structName, classId, finalizerName, funcListName) {
const body = this.function("js_declare_" + structName, "int", [{ type: "JSContext *", name: "ctx" }, { type: "JSModuleDef *", name: "m" }], true);
body.call("JS_NewClassID", ["&" + classId]);
const classDefName = `js_${structName}_def`;
body.declare(classDefName, "JSClassDef", false, `{ .class_name = "${structName}", .finalizer = ${finalizerName} }`);
body.call("JS_NewClass", ["JS_GetRuntime(ctx)", classId, "&" + classDefName]);
body.declare("proto", "JSValue", false, "JS_NewObject(ctx)");
body.call("JS_SetPropertyFunctionList", ["ctx", "proto", funcListName, `countof(${funcListName})`]);
body.call("JS_SetClassProto", ["ctx", classId, "proto"]);
body.statement("return 0");
return body;
}
2023-05-13 12:49:05 +00:00
jsStructGetter(structName, classId, field, type, classIds) {
2023-05-08 14:43:50 +00:00
const args = [{ type: "JSContext*", name: "ctx" }, { type: "JSValueConst", name: "this_val" }];
const fun = this.function(`js_${structName}_get_${field}`, "JSValue", args, true);
fun.declare("ptr", structName + "*", false, `JS_GetOpaque2(ctx, this_val, ${classId})`);
fun.declare(field, type, false, "ptr->" + field);
2023-05-13 12:49:05 +00:00
fun.jsToJs(type, "ret", field, classIds);
2023-05-08 14:43:50 +00:00
fun.returnExp("ret");
2023-05-08 16:05:03 +00:00
return fun;
2023-05-06 21:43:40 +00:00
}
2023-05-13 12:49:05 +00:00
jsStructSetter(structName, classId, field, type, classIds) {
2023-05-08 21:37:58 +00:00
const args = [{ type: "JSContext*", name: "ctx" }, { type: "JSValueConst", name: "this_val" }, { type: "JSValueConst", name: "v" }];
const fun = this.function(`js_${structName}_set_${field}`, "JSValue", args, true);
fun.declare("ptr", structName + "*", false, `JS_GetOpaque2(ctx, this_val, ${classId})`);
2023-05-13 12:49:05 +00:00
fun.jsToC(type, "value", "v", classIds);
2023-05-08 21:37:58 +00:00
fun.statement("ptr->" + field + " = value");
fun.returnExp("JS_UNDEFINED");
return fun;
}
2023-05-10 21:26:53 +00:00
jsOpqToStructPtr(structType, structVar, srcVar, classId) {
this.declare(structVar, structType + "*", false, `(${structType}*)JS_GetOpaque2(ctx, ${srcVar}, ${classId})`);
}
2023-05-13 12:49:05 +00:00
jsStructConstructor(structName, fields, classId, classIds) {
2023-05-09 21:25:28 +00:00
const body = this.jsBindingFunction(structName + "_constructor");
for (let i = 0; i < fields.length; i++) {
const para = fields[i];
2023-05-13 12:49:05 +00:00
body.jsToC(para.type, para.name, "argv[" + i + "]", classIds);
2023-05-09 21:25:28 +00:00
}
2023-05-10 21:26:53 +00:00
body.declareStruct(structName, "_struct", fields.map(x => x.name));
2023-05-09 21:25:28 +00:00
body.jsStructToOpq(structName, "_return", "_struct", classId);
body.returnExp("_return");
return body;
}
2023-05-06 21:43:40 +00:00
}
2023-05-08 14:43:50 +00:00
exports.GenericQuickJsGenerator = GenericQuickJsGenerator;
class QuickJsGenerator extends GenericQuickJsGenerator {
createGenerator() {
return new QuickJsGenerator();
2023-05-06 21:43:40 +00:00
}
}
2023-05-08 14:43:50 +00:00
exports.QuickJsGenerator = QuickJsGenerator;
2023-05-06 21:43:40 +00:00
/***/ }),
2023-05-08 14:43:50 +00:00
/***/ "./src/raylib-header.ts":
/*!******************************!*\
!*** ./src/raylib-header.ts ***!
\******************************/
2023-05-06 21:43:40 +00:00
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
Object.defineProperty(exports, "__esModule", ({ value: true }));
2023-05-08 14:43:50 +00:00
exports.RayLibHeader = void 0;
const quickjs_1 = __webpack_require__(/*! ./quickjs */ "./src/quickjs.ts");
2023-05-14 20:19:47 +00:00
const typescript_1 = __webpack_require__(/*! ./typescript */ "./src/typescript.ts");
2023-05-08 14:43:50 +00:00
class RayLibHeader extends quickjs_1.QuickJsHeader {
2023-06-02 09:52:33 +00:00
constructor(name) {
2023-05-08 14:43:50 +00:00
super(name);
2023-05-14 20:19:47 +00:00
this.typings = new typescript_1.TypeScriptDeclaration();
2023-05-08 14:43:50 +00:00
this.includes.include("raylib.h");
2023-05-15 15:44:28 +00:00
//this.includes.line("#define RAYMATH_IMPLEMENTATION")
2023-05-08 14:43:50 +00:00
}
2023-06-02 09:52:33 +00:00
addApiFunction(api) {
const options = api.binding || {};
if (options.ignore)
return;
const jName = options.jsName || api.name.charAt(0).toLowerCase() + api.name.slice(1);
2023-06-02 16:58:53 +00:00
console.log("Binding function " + api.name);
2023-05-08 14:43:50 +00:00
const fun = this.functions.jsBindingFunction(jName);
2023-05-16 06:28:30 +00:00
if (options.body) {
options.body(fun);
2023-05-08 14:43:50 +00:00
}
else {
2023-05-16 06:28:30 +00:00
if (options.before)
options.before(fun);
// read parameters
2023-06-02 09:52:33 +00:00
api.params = api.params || [];
2023-06-04 20:31:15 +00:00
const activeParams = api.params.filter(x => !x.binding?.ignore);
for (let i = 0; i < activeParams.length; i++) {
const para = activeParams[i];
2023-06-03 07:15:38 +00:00
if (para.binding?.customConverter)
2023-06-04 20:31:15 +00:00
para.binding.customConverter(fun, "argv[" + i + "]");
2023-06-03 07:15:38 +00:00
else
fun.jsToC(para.type, para.name, "argv[" + i + "]", this.structLookup, false, para.binding?.typeAlias);
2023-05-16 06:28:30 +00:00
}
// call c function
2023-05-29 22:03:29 +00:00
if (options.customizeCall)
fun.line(options.customizeCall);
else
fun.call(api.name, api.params.map(x => x.name), api.returnType === "void" ? null : { type: api.returnType, name: "returnVal" });
2023-05-16 06:28:30 +00:00
// clean up parameters
2023-06-04 20:31:15 +00:00
for (let i = 0; i < activeParams.length; i++) {
const param = activeParams[i];
2023-06-03 07:15:38 +00:00
if (param.binding?.customCleanup)
2023-06-04 20:31:15 +00:00
param.binding.customCleanup(fun, "argv[" + i + "]");
2023-06-04 11:41:18 +00:00
else
fun.jsCleanUpParameter(param.type, param.name);
2023-05-16 06:28:30 +00:00
}
// return result
if (api.returnType === "void") {
2023-05-29 22:03:29 +00:00
if (options.after)
options.after(fun);
2023-05-16 06:28:30 +00:00
fun.statement("return JS_UNDEFINED");
}
else {
fun.jsToJs(api.returnType, "ret", "returnVal", this.structLookup);
2023-05-29 22:03:29 +00:00
if (options.after)
options.after(fun);
2023-05-16 06:28:30 +00:00
fun.returnExp("ret");
}
2023-05-08 14:43:50 +00:00
}
// add binding to function declaration
2023-06-02 16:58:53 +00:00
this.moduleFunctionList.jsFuncDef(jName, (api.params || []).filter(x => !x.binding?.ignore).length, fun.getTag("_name"));
2023-05-14 20:19:47 +00:00
this.typings.addFunction(jName, api);
2023-05-06 21:43:40 +00:00
}
2023-06-02 09:52:33 +00:00
addEnum(renum) {
2023-06-02 16:58:53 +00:00
console.log("Binding enum " + renum.name);
2023-06-02 09:52:33 +00:00
renum.values.forEach(x => this.exportGlobalConstant(x.name, x.description));
2023-05-06 21:43:40 +00:00
}
2023-06-02 09:52:33 +00:00
addApiStruct(struct) {
const options = struct.binding || {};
2023-06-02 16:58:53 +00:00
console.log("Binding struct " + struct.name);
2023-05-14 20:19:47 +00:00
const classId = this.definitions.jsClassId(`js_${struct.name}_class_id`);
2023-05-08 21:37:58 +00:00
this.registerStruct(struct.name, classId);
2023-06-02 09:52:33 +00:00
options.aliases?.forEach(x => this.registerStruct(x, classId));
const finalizer = this.structs.jsStructFinalizer(classId, struct.name, (gen, ptr) => options.destructor && gen.call(options.destructor.name, ["*" + ptr]));
2023-05-08 16:05:03 +00:00
const propDeclarations = this.structs.createGenerator();
if (options && options.properties) {
for (const field of Object.keys(options.properties)) {
const type = struct.fields.find(x => x.name === field)?.type;
if (!type)
throw new Error(`Struct ${struct.name} does not contain field ${field}`);
const el = options.properties[field];
let _get = undefined;
let _set = undefined;
if (el.get)
2023-05-16 06:28:30 +00:00
_get = this.structs.jsStructGetter(struct.name, classId, field, type, /*Be carefull when allocating memory in a getter*/ this.structLookup);
2023-05-08 21:37:58 +00:00
if (el.set)
2023-05-13 12:49:05 +00:00
_set = this.structs.jsStructSetter(struct.name, classId, field, type, this.structLookup);
2023-05-08 21:37:58 +00:00
propDeclarations.jsGetSetDef(field, _get?.getTag("_name"), _set?.getTag("_name"));
2023-05-08 16:05:03 +00:00
}
}
2023-05-08 14:43:50 +00:00
const classFuncList = this.structs.jsFunctionList(`js_${struct.name}_proto_funcs`);
2023-05-08 16:05:03 +00:00
classFuncList.child(propDeclarations);
2023-05-08 21:37:58 +00:00
classFuncList.jsPropStringDef("[Symbol.toStringTag]", struct.name);
2023-05-08 14:43:50 +00:00
const classDecl = this.structs.jsClassDeclaration(struct.name, classId, finalizer.getTag("_name"), classFuncList.getTag("_name"));
this.moduleInit.call(classDecl.getTag("_name"), ["ctx", "m"]);
if (options?.createConstructor || options?.createEmptyConstructor) {
const body = this.functions.jsStructConstructor(struct.name, options?.createEmptyConstructor ? [] : struct.fields, classId, this.structLookup);
2023-05-09 21:25:28 +00:00
this.moduleInit.statement(`JSValue ${struct.name}_constr = JS_NewCFunction2(ctx, ${body.getTag("_name")},"${struct.name})", ${struct.fields.length}, JS_CFUNC_constructor_or_func, 0)`);
this.moduleInit.call("JS_SetModuleExport", ["ctx", "m", `"${struct.name}"`, struct.name + "_constr"]);
this.moduleEntry.call("JS_AddModuleExport", ["ctx", "m", '"' + struct.name + '"']);
}
2023-06-02 16:58:53 +00:00
this.typings.addStruct(struct);
2023-05-06 21:43:40 +00:00
}
2023-05-14 20:19:47 +00:00
exportGlobalStruct(structName, exportName, values, description) {
2023-05-10 21:26:53 +00:00
this.moduleInit.declareStruct(structName, exportName + "_struct", values);
const classId = this.structLookup[structName];
if (!classId)
throw new Error("Struct " + structName + " not found in register");
this.moduleInit.jsStructToOpq(structName, exportName + "_js", exportName + "_struct", classId);
this.moduleInit.call("JS_SetModuleExport", ["ctx", "m", `"${exportName}"`, exportName + "_js"]);
this.moduleEntry.call("JS_AddModuleExport", ["ctx", "m", `"${exportName}"`]);
2023-05-14 20:19:47 +00:00
this.typings.constants.tsDeclareConstant(exportName, structName, description);
2023-05-10 21:26:53 +00:00
}
2023-05-14 20:19:47 +00:00
exportGlobalConstant(name, description) {
2023-05-11 18:54:49 +00:00
this.moduleInit.statement(`JS_SetModuleExport(ctx, m, "${name}", JS_NewInt32(ctx, ${name}))`);
this.moduleEntry.statement(`JS_AddModuleExport(ctx, m, "${name}")`);
2023-05-14 20:19:47 +00:00
this.typings.constants.tsDeclareConstant(name, "number", description);
2023-05-11 18:54:49 +00:00
}
2023-05-06 21:43:40 +00:00
}
2023-05-08 14:43:50 +00:00
exports.RayLibHeader = RayLibHeader;
2023-05-06 21:43:40 +00:00
2023-05-14 20:19:47 +00:00
/***/ }),
/***/ "./src/typescript.ts":
/*!***************************!*\
!*** ./src/typescript.ts ***!
\***************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.TypescriptGenerator = exports.GenericTypescriptGenerator = exports.TypeScriptDeclaration = void 0;
const generation_1 = __webpack_require__(/*! ./generation */ "./src/generation.ts");
const fs_1 = __webpack_require__(/*! fs */ "fs");
class TypeScriptDeclaration {
constructor() {
this.root = new TypescriptGenerator();
this.structs = this.root.child();
this.functions = this.root.child();
this.constants = this.root.child();
}
addFunction(name, api) {
2023-06-02 16:58:53 +00:00
const options = api.binding || {};
2023-06-03 07:15:38 +00:00
const para = (api.params || []).filter(x => !x.binding?.ignore).map(x => ({ name: x.name, type: x.binding?.jsType ?? this.toJsType(x.type) }));
2023-06-02 16:58:53 +00:00
const returnType = options.jsReturns ?? this.toJsType(api.returnType);
2023-05-14 20:19:47 +00:00
this.functions.tsDeclareFunction(name, para, returnType, api.description);
}
2023-06-02 16:58:53 +00:00
addStruct(api) {
const options = api.binding || {};
2023-05-14 20:19:47 +00:00
var fields = api.fields.filter(x => !!(options.properties || {})[x.name]).map(x => ({ name: x.name, description: x.description, type: this.toJsType(x.type) }));
this.structs.tsDeclareInterface(api.name, fields);
this.structs.tsDeclareType(api.name, !!(options.createConstructor || options.createEmptyConstructor), options.createEmptyConstructor ? [] : fields);
2023-05-14 20:19:47 +00:00
}
toJsType(type) {
switch (type) {
case "int":
case "long":
case "unsigned int":
case "unsigned char":
case "float":
case "double":
return "number";
2023-05-24 20:47:17 +00:00
case "const unsigned char *":
case "unsigned char *":
case "unsigned short *":
case "float *":
return "ArrayBuffer";
2023-05-14 20:19:47 +00:00
case "bool":
return "boolean";
case "const char *":
case "char *":
return "string";
2023-05-16 06:28:30 +00:00
case "void *":
case "const void *":
return "any";
case "Camera":
case "Camera *":
2023-05-16 06:28:30 +00:00
return "Camera3D";
case "Texture2D":
case "Texture2D *":
2023-05-16 06:28:30 +00:00
case "TextureCubemap":
return "Texture";
case "RenderTexture2D":
case "RenderTexture2D *":
return "RenderTexture";
2023-05-16 06:28:30 +00:00
case "Quaternion":
return "Vector4";
2023-05-14 20:19:47 +00:00
default:
return type.replace(" *", "").replace("const ", "");
2023-05-14 20:19:47 +00:00
}
}
writeTo(filename) {
const writer = new generation_1.CodeWriter();
writer.writeGenerator(this.root);
(0, fs_1.writeFileSync)(filename, writer.toString());
}
}
exports.TypeScriptDeclaration = TypeScriptDeclaration;
class GenericTypescriptGenerator extends generation_1.GenericCodeGenerator {
tsDeclareFunction(name, parameters, returnType, description) {
this.tsDocComment(description);
this.statement(`declare function ${name}(${parameters.map(x => x.name + ': ' + x.type).join(', ')}): ${returnType}`);
}
tsDeclareConstant(name, type, description) {
this.tsDocComment(description);
this.statement(`declare var ${name}: ${type}`);
}
tsDeclareType(name, hasConstructor, parameters) {
this.line(`declare var ${name}: {`);
this.indent();
this.statement("prototype: " + name);
if (hasConstructor)
this.statement(`new(${parameters.map(x => x.name + ": " + x.type).join(', ')}): ${name}`);
this.unindent();
this.line("}");
}
tsDeclareInterface(name, fields) {
this.line(`interface ${name} {`);
this.indent();
for (const field of fields) {
if (field.description)
this.tsDocComment(field.description);
this.line(field.name + ": " + field.type + ",");
}
this.unindent();
this.line("}");
}
tsDocComment(comment) {
this.line(`/** ${comment} */`);
}
}
exports.GenericTypescriptGenerator = GenericTypescriptGenerator;
class TypescriptGenerator extends GenericTypescriptGenerator {
createGenerator() {
return new TypescriptGenerator();
}
}
exports.TypescriptGenerator = TypescriptGenerator;
2023-05-06 21:43:40 +00:00
/***/ }),
/***/ "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");
2023-05-08 14:43:50 +00:00
const raylib_header_1 = __webpack_require__(/*! ./raylib-header */ "./src/raylib-header.ts");
2023-05-26 20:09:53 +00:00
const header_parser_1 = __webpack_require__(/*! ./header-parser */ "./src/header-parser.ts");
2023-06-02 16:58:53 +00:00
let api;
2023-06-02 09:52:33 +00:00
function getFunction(funList, name) {
return funList.find(x => x.name === name);
}
function getStruct(strList, name) {
return strList.find(x => x.name === name);
}
function getAliases(aliasList, name) {
return aliasList.filter(x => x.type === name).map(x => x.name);
}
2023-06-02 16:58:53 +00:00
function ignore(name) {
getFunction(api.functions, name).binding = { ignore: true };
}
2023-05-06 21:43:40 +00:00
function main() {
// Load the pre-generated raylib api
2023-06-02 16:58:53 +00:00
api = JSON.parse((0, fs_1.readFileSync)("thirdparty/raylib/parser/output/raylib_api.json", 'utf8'));
2023-05-26 20:09:53 +00:00
const parser = new header_parser_1.HeaderParser();
const rmathHeader = (0, fs_1.readFileSync)("thirdparty/raylib/src/raymath.h", "utf8");
2023-05-26 22:47:44 +00:00
const mathApi = parser.parseFunctions(rmathHeader);
2023-05-15 15:44:28 +00:00
mathApi.forEach(x => api.functions.push(x));
2023-05-26 20:09:53 +00:00
const rcameraHeader = (0, fs_1.readFileSync)("thirdparty/raylib/src/rcamera.h", "utf8");
2023-05-26 22:47:44 +00:00
const cameraApi = parser.parseFunctionDefinitions(rcameraHeader);
cameraApi.forEach(x => api.functions.push(x));
2023-06-02 06:03:36 +00:00
const rguiHeader = (0, fs_1.readFileSync)("thirdparty/raygui/src/raygui.h", "utf8");
2023-05-26 22:47:44 +00:00
const rguiFunctions = parser.parseFunctionDefinitions(rguiHeader);
2023-06-02 06:03:36 +00:00
const rguiEnums = parser.parseEnums(rguiHeader);
2023-05-26 22:47:44 +00:00
//rguiApi.forEach(x => console.log(`core.addApiFunctionByName("${x.name}")`))
rguiFunctions.forEach(x => api.functions.push(x));
2023-06-02 06:03:36 +00:00
rguiEnums.forEach(x => api.enums.push(x));
const rlightsHeader = (0, fs_1.readFileSync)("include/rlights.h", "utf8");
2023-05-26 22:47:44 +00:00
const rlightsFunctions = parser.parseFunctions(rlightsHeader, true);
api.functions.push(rlightsFunctions[0]);
api.functions.push(rlightsFunctions[1]);
2023-06-02 06:03:36 +00:00
const reasingsHeader = (0, fs_1.readFileSync)("include/reasings.h", "utf8");
2023-05-26 22:47:44 +00:00
const reasingsFunctions = parser.parseFunctions(reasingsHeader);
reasingsFunctions.forEach(x => api.functions.push(x));
2023-06-02 09:52:33 +00:00
// Custom Rayjs functions
api.functions.push({
name: "SetModelMaterial",
description: "Replace material in slot materialIndex",
returnType: "void",
params: [{ type: "Model *", name: "model" }, { type: "int", name: "materialIndex" }, { type: "Material", name: "material" }]
});
// Define a new header
const core = new raylib_header_1.RayLibHeader("raylib_core");
core.includes.include("raymath.h");
core.includes.include("rcamera.h");
2023-05-26 22:47:44 +00:00
core.includes.line("#define RAYGUI_IMPLEMENTATION");
core.includes.include("raygui.h");
core.includes.line("#define RLIGHTS_IMPLEMENTATION");
core.includes.include("rlights.h");
core.includes.include("reasings.h");
2023-06-02 09:52:33 +00:00
getStruct(api.structs, "Color").binding = {
2023-05-08 21:37:58 +00:00
properties: {
r: { get: true, set: true },
g: { get: true, set: true },
b: { get: true, set: true },
a: { get: true, set: true },
2023-05-09 21:25:28 +00:00
},
createConstructor: true
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Rectangle").binding = {
2023-05-11 20:10:40 +00:00
properties: {
x: { get: true, set: true },
y: { get: true, set: true },
width: { get: true, set: true },
height: { get: true, set: true },
},
createConstructor: true
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Vector2").binding = {
2023-05-14 20:19:47 +00:00
properties: {
x: { get: true, set: true },
y: { get: true, set: true },
},
createConstructor: true
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Vector3").binding = {
2023-05-10 21:26:53 +00:00
properties: {
x: { get: true, set: true },
y: { get: true, set: true },
2023-05-14 20:19:47 +00:00
z: { get: true, set: true },
2023-05-10 21:26:53 +00:00
},
createConstructor: true
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Vector4").binding = {
2023-05-13 12:49:05 +00:00
properties: {
x: { get: true, set: true },
y: { get: true, set: true },
z: { get: true, set: true },
2023-05-14 20:19:47 +00:00
w: { get: true, set: true },
2023-05-13 12:49:05 +00:00
},
2023-06-02 09:52:33 +00:00
createConstructor: true,
aliases: getAliases(api.aliases, "Vector4")
};
getStruct(api.structs, "Ray").binding = {
2023-05-13 12:49:05 +00:00
properties: {
position: { get: false, set: true },
direction: { get: false, set: true },
},
createConstructor: true
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "RayCollision").binding = {
2023-05-14 20:19:47 +00:00
properties: {
hit: { get: true, set: false },
distance: { get: true, set: false },
2023-05-24 20:47:17 +00:00
point: { get: true, set: false },
normal: { get: true, set: false },
2023-05-14 20:19:47 +00:00
},
createConstructor: false
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Camera2D").binding = {
2023-05-13 12:49:05 +00:00
properties: {
2023-05-24 20:47:17 +00:00
offset: { get: true, set: true },
target: { get: true, set: true },
2023-05-13 12:49:05 +00:00
rotation: { get: true, set: true },
zoom: { get: true, set: true },
},
createConstructor: true
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Camera3D").binding = {
2023-05-14 20:19:47 +00:00
properties: {
2023-05-16 06:28:30 +00:00
position: { get: true, set: true },
target: { get: true, set: true },
2023-05-14 20:19:47 +00:00
up: { get: false, set: true },
fovy: { get: true, set: true },
projection: { get: true, set: true },
},
2023-06-02 09:52:33 +00:00
createConstructor: true,
aliases: getAliases(api.aliases, "Camera3D")
};
getStruct(api.structs, "BoundingBox").binding = {
2023-05-24 20:47:17 +00:00
properties: {
min: { get: true, set: true },
max: { get: true, set: true },
},
2023-05-14 20:19:47 +00:00
createConstructor: true
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Matrix").binding = {
2023-05-13 12:49:05 +00:00
properties: {},
createConstructor: false
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "NPatchInfo").binding = {
properties: {
source: { get: true, set: true },
left: { get: true, set: true },
top: { get: true, set: true },
right: { get: true, set: true },
bottom: { get: true, set: true },
layout: { get: true, set: true },
},
createConstructor: true
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Image").binding = {
2023-05-14 20:19:47 +00:00
properties: {
2023-05-24 20:47:17 +00:00
//data: { set: true },
2023-05-14 20:19:47 +00:00
width: { get: true },
height: { get: true },
mipmaps: { get: true },
format: { get: true }
},
//destructor: "UnloadImage"
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Wave").binding = {
2023-05-14 20:19:47 +00:00
properties: {
frameCount: { get: true },
sampleRate: { get: true },
sampleSize: { get: true },
channels: { get: true }
},
//destructor: "UnloadWave"
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Sound").binding = {
2023-05-14 20:19:47 +00:00
properties: {
frameCount: { get: true }
},
//destructor: "UnloadSound"
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Music").binding = {
2023-05-14 20:19:47 +00:00
properties: {
frameCount: { get: true },
2023-05-24 20:47:17 +00:00
looping: { get: true, set: true },
ctxType: { get: true },
2023-05-14 20:19:47 +00:00
},
//destructor: "UnloadMusicStream"
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Model").binding = {
2023-05-24 20:47:17 +00:00
properties: {
transform: { get: true, set: true },
meshCount: { get: true },
materialCount: { get: true },
boneCount: { get: true },
},
//destructor: "UnloadModel"
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Mesh").binding = {
properties: {
vertexCount: { get: true, set: true },
triangleCount: { get: true, set: true },
// TODO: Free previous pointers before overwriting
vertices: { set: true },
texcoords: { set: true },
texcoords2: { set: true },
normals: { set: true },
tangents: { set: true },
colors: { set: true },
indices: { set: true },
animVertices: { set: true },
animNormals: { set: true },
boneIds: { set: true },
boneWeights: { set: true },
},
createEmptyConstructor: true
//destructor: "UnloadMesh"
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Shader").binding = {
2023-05-24 20:47:17 +00:00
properties: {
id: { get: true }
},
//destructor: "UnloadShader"
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Texture").binding = {
2023-05-14 20:19:47 +00:00
properties: {
width: { get: true },
2023-05-24 20:47:17 +00:00
height: { get: true },
mipmaps: { get: true },
format: { get: true },
2023-05-14 20:19:47 +00:00
},
2023-06-02 09:52:33 +00:00
aliases: getAliases(api.aliases, "Texture")
//destructor: "UnloadTexture"
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Font").binding = {
2023-05-14 20:19:47 +00:00
properties: {
2023-05-24 20:47:17 +00:00
baseSize: { get: true },
glyphCount: { get: true },
glyphPadding: { get: true },
2023-05-14 20:19:47 +00:00
},
//destructor: "UnloadFont"
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "RenderTexture").binding = {
2023-05-24 20:47:17 +00:00
properties: {
id: { get: true }
},
2023-06-02 09:52:33 +00:00
aliases: getAliases(api.aliases, "RenderTexture")
//destructor: "UnloadRenderTexture"
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "MaterialMap").binding = {
properties: {
texture: { set: true },
color: { set: true, get: true },
value: { get: true, set: true }
},
//destructor: "UnloadMaterialMap"
2023-06-02 09:52:33 +00:00
};
getStruct(api.structs, "Material").binding = {
properties: {
shader: { set: true }
},
//destructor: "UnloadMaterial"
2023-06-02 09:52:33 +00:00
};
2023-06-05 20:03:08 +00:00
getFunction(api.functions, "EndDrawing").binding = { after: gen => gen.call("app_update_quickjs", []) };
2023-06-02 16:58:53 +00:00
ignore("SetWindowIcons");
ignore("GetWindowHandle");
2023-05-13 12:49:05 +00:00
// Custom frame control functions
// NOT SUPPORTED BECAUSE NEEDS COMPILER FLAG
2023-06-02 16:58:53 +00:00
ignore("SwapScreenBuffer");
ignore("PollInputEvents");
ignore("WaitTime");
ignore("BeginVrStereoMode");
ignore("EndVrStereoMode");
ignore("LoadVrStereoConfig");
ignore("UnloadVrStereoConfig");
2023-06-02 09:52:33 +00:00
getFunction(api.functions, "SetShaderValue").binding = { body: (gen) => {
2023-05-16 06:28:30 +00:00
gen.jsToC("Shader", "shader", "argv[0]", core.structLookup);
gen.jsToC("int", "locIndex", "argv[1]", core.structLookup);
gen.declare("value", "void *", false, "NULL");
2023-05-16 09:59:29 +00:00
gen.declare("valueFloat", "float");
gen.declare("valueInt", "int");
2023-05-16 06:28:30 +00:00
gen.jsToC("int", "uniformType", "argv[3]", core.structLookup);
const sw = gen.switch("uniformType");
let b = sw.caseBreak("SHADER_UNIFORM_FLOAT");
2023-05-16 09:59:29 +00:00
b.jsToC("float", "valueFloat", "argv[2]", core.structLookup, true);
2023-05-16 06:28:30 +00:00
b.statement("value = (void *)&valueFloat");
b = sw.caseBreak("SHADER_UNIFORM_VEC2");
b.jsToC("Vector2 *", "valueV2", "argv[2]", core.structLookup);
b.statement("value = (void*)valueV2");
b = sw.caseBreak("SHADER_UNIFORM_VEC3");
b.jsToC("Vector3 *", "valueV3", "argv[2]", core.structLookup);
b.statement("value = (void*)valueV3");
b = sw.caseBreak("SHADER_UNIFORM_VEC4");
b.jsToC("Vector4 *", "valueV4", "argv[2]", core.structLookup);
b.statement("value = (void*)valueV4");
b = sw.caseBreak("SHADER_UNIFORM_INT");
2023-05-16 09:59:29 +00:00
b.jsToC("int", "valueInt", "argv[2]", core.structLookup, true);
2023-05-16 06:28:30 +00:00
b.statement("value = (void*)&valueInt");
b = sw.defaultBreak();
b.returnExp("JS_EXCEPTION");
gen.call("SetShaderValue", ["shader", "locIndex", "value", "uniformType"]);
gen.returnExp("JS_UNDEFINED");
2023-06-02 09:52:33 +00:00
} };
2023-06-02 16:58:53 +00:00
ignore("SetShaderValueV");
const traceLog = getFunction(api.functions, "TraceLog");
traceLog.params?.pop();
// Memory functions not supported on JS, just use ArrayBuffer
ignore("MemAlloc");
ignore("MemRealloc");
ignore("MemFree");
2023-05-13 12:49:05 +00:00
// Callbacks not supported on JS
2023-06-02 16:58:53 +00:00
ignore("SetTraceLogCallback");
ignore("SetLoadFileDataCallback");
ignore("SetSaveFileDataCallback");
ignore("SetLoadFileTextCallback");
ignore("SetSaveFileTextCallback");
2023-05-13 12:49:05 +00:00
// Files management functions
2023-06-02 16:58:53 +00:00
const lfd = getFunction(api.functions, "LoadFileData");
lfd.params[lfd.params.length - 1].binding = { ignore: true };
lfd.binding = {
body: gen => {
2023-05-24 20:47:17 +00:00
gen.jsToC("const char *", "fileName", "argv[0]");
gen.declare("bytesRead", "unsigned int");
gen.call("LoadFileData", ["fileName", "&bytesRead"], { type: "unsigned char *", name: "retVal" });
gen.statement("JSValue buffer = JS_NewArrayBufferCopy(ctx, (const uint8_t*)retVal, bytesRead)");
gen.call("UnloadFileData", ["retVal"]);
gen.jsCleanUpParameter("const char*", "fileName");
gen.returnExp("buffer");
2023-06-02 16:58:53 +00:00
}
};
ignore("UnloadFileData");
// TODO: SaveFileData works but unnecessary makes copy of memory
getFunction(api.functions, "SaveFileData").binding = {};
ignore("ExportDataAsCode");
getFunction(api.functions, "LoadFileText").binding = { after: gen => gen.call("UnloadFileText", ["returnVal"]) };
2023-06-03 07:15:38 +00:00
getFunction(api.functions, "SaveFileText").params[1].binding = { typeAlias: "const char *" };
2023-06-02 16:58:53 +00:00
ignore("UnloadFileText");
const createFileList = (gen, loadName, unloadName, args) => {
gen.call(loadName, args, { type: "FilePathList", name: "files" });
gen.call("JS_NewArray", ["ctx"], { type: "JSValue", name: "ret" });
const f = gen.for("i", "files.count");
f.call("JS_SetPropertyUint32", ["ctx", "ret", "i", "JS_NewString(ctx,files.paths[i])"]);
gen.call(unloadName, ["files"]);
};
getFunction(api.functions, "LoadDirectoryFiles").binding = {
jsReturns: "string[]",
2023-06-02 06:03:36 +00:00
body: gen => {
2023-06-02 16:58:53 +00:00
gen.jsToC("const char *", "dirPath", "argv[0]");
createFileList(gen, "LoadDirectoryFiles", "UnloadDirectoryFiles", ["dirPath"]);
gen.jsCleanUpParameter("const char *", "dirPath");
2023-06-02 06:03:36 +00:00
gen.returnExp("ret");
}
2023-06-02 16:58:53 +00:00
};
getFunction(api.functions, "LoadDirectoryFilesEx").binding = {
jsReturns: "string[]",
body: gen => {
gen.jsToC("const char *", "basePath", "argv[0]");
gen.jsToC("const char *", "filter", "argv[1]");
gen.jsToC("bool", "scanSubdirs", "argv[2]");
createFileList(gen, "LoadDirectoryFilesEx", "UnloadDirectoryFiles", ["basePath", "filter", "scanSubdirs"]);
gen.jsCleanUpParameter("const char *", "basePath");
gen.jsCleanUpParameter("const char *", "filter");
gen.returnExp("ret");
}
};
ignore("UnloadDirectoryFiles");
getFunction(api.functions, "LoadDroppedFiles").binding = {
jsReturns: "string[]",
body: gen => {
createFileList(gen, "LoadDroppedFiles", "UnloadDroppedFiles", []);
gen.returnExp("ret");
}
};
ignore("UnloadDroppedFiles");
2023-05-24 20:47:17 +00:00
// Compression/encoding functionality
2023-06-02 16:58:53 +00:00
ignore("CompressData");
ignore("DecompressData");
ignore("EncodeDataBase64");
ignore("DecodeDataBase64");
ignore("DrawLineStrip");
ignore("DrawTriangleFan");
ignore("DrawTriangleStrip");
ignore("CheckCollisionPointPoly");
ignore("CheckCollisionLines");
ignore("LoadImageAnim");
ignore("ExportImageAsCode");
getFunction(api.functions, "LoadImageColors").binding = {
jsReturns: "ArrayBuffer",
body: gen => {
gen.jsToC("Image", "image", "argv[0]", core.structLookup);
gen.call("LoadImageColors", ["image"], { name: "colors", type: "Color *" });
gen.statement("JSValue retVal = JS_NewArrayBufferCopy(ctx, (const uint8_t*)colors, image.width*image.height*sizeof(Color))");
gen.call("UnloadImageColors", ["colors"]);
gen.returnExp("retVal");
2023-06-02 16:58:53 +00:00
}
};
ignore("LoadImagePalette");
ignore("UnloadImageColors");
ignore("UnloadImagePalette");
ignore("GetPixelColor");
ignore("SetPixelColor");
const lfx = getFunction(api.functions, "LoadFontEx");
lfx.params[2].binding = { ignore: true };
lfx.params[3].binding = { ignore: true };
lfx.binding = { customizeCall: "Font returnVal = LoadFontEx(fileName, fontSize, NULL, 0);" };
ignore("LoadFontFromMemory");
ignore("LoadFontData");
ignore("GenImageFontAtlas");
ignore("UnloadFontData");
ignore("ExportFontAsCode");
ignore("DrawTextCodepoints");
ignore("GetGlyphInfo");
ignore("LoadUTF8");
ignore("UnloadUTF8");
ignore("LoadCodepoints");
ignore("UnloadCodepoints");
ignore("GetCodepointCount");
ignore("GetCodepoint");
ignore("GetCodepointNext");
ignore("GetCodepointPrevious");
ignore("CodepointToUTF8");
2023-05-15 21:02:41 +00:00
// Not supported, use JS Stdlib instead
2023-06-02 16:58:53 +00:00
api.functions.filter(x => x.name.startsWith("Text")).forEach(x => ignore(x.name));
ignore("DrawTriangleStrip3D");
ignore("LoadMaterials");
ignore("LoadModelAnimations");
ignore("UpdateModelAnimation");
ignore("UnloadModelAnimation");
ignore("UnloadModelAnimations");
ignore("IsModelAnimationValid");
ignore("ExportWaveAsCode");
2023-05-14 20:19:47 +00:00
// Wave/Sound management functions
2023-06-02 16:58:53 +00:00
ignore("LoadWaveSamples");
ignore("UnloadWaveSamples");
ignore("LoadMusicStreamFromMemory");
ignore("LoadAudioStream");
ignore("IsAudioStreamReady");
ignore("UnloadAudioStream");
ignore("UpdateAudioStream");
ignore("IsAudioStreamProcessed");
ignore("PlayAudioStream");
ignore("PauseAudioStream");
ignore("ResumeAudioStream");
ignore("IsAudioStreamPlaying");
ignore("StopAudioStream");
ignore("SetAudioStreamVolume");
ignore("SetAudioStreamPitch");
ignore("SetAudioStreamPan");
ignore("SetAudioStreamBufferSizeDefault");
ignore("SetAudioStreamCallback");
ignore("AttachAudioStreamProcessor");
ignore("DetachAudioStreamProcessor");
ignore("AttachAudioMixedProcessor");
ignore("DetachAudioMixedProcessor");
ignore("Vector3OrthoNormalize");
ignore("Vector3ToFloatV");
ignore("MatrixToFloatV");
ignore("QuaternionToAxisAngle");
core.exportGlobalConstant("DEG2RAD", "(PI/180.0)");
core.exportGlobalConstant("RAD2DEG", "(180.0/PI)");
2023-06-03 07:15:38 +00:00
const setOutParam = (fun, index) => {
const param = fun.params[index];
param.binding = {
jsType: `{ ${param.name}: number }`,
2023-06-04 20:31:15 +00:00
customConverter: (gen, src) => {
gen.declare(param.name, param.type, false, "NULL");
2023-06-03 07:15:38 +00:00
gen.declare(param.name + "_out", param.type.replace(" *", ""));
2023-06-04 20:31:15 +00:00
const body = gen.if("!JS_IsNull(" + src + ")");
body.statement(param.name + " = &" + param.name + "_out");
body.call("JS_GetPropertyStr", ["ctx", src, '"' + param.name + '"'], { name: param.name + "_js", type: "JSValue" });
body.call("JS_ToInt32", ["ctx", param.name, param.name + "_js"]);
2023-06-03 07:15:38 +00:00
},
2023-06-04 20:31:15 +00:00
customCleanup: (gen, src) => {
const body = gen.if("!JS_IsNull(" + src + ")");
body.call("JS_SetPropertyStr", ["ctx", src, `"${param.name}"`, "JS_NewInt32(ctx," + param.name + "_out)"]);
2023-06-03 07:15:38 +00:00
}
};
};
2023-06-04 11:41:18 +00:00
const setOutParamString = (fun, index, indexLen) => {
const lenParam = fun.params[indexLen];
lenParam.binding = { ignore: true };
const param = fun.params[index];
param.binding = {
jsType: `{ ${param.name}: string }`,
2023-06-04 20:31:15 +00:00
customConverter: (gen, src) => {
gen.call("JS_GetPropertyStr", ["ctx", src, '"' + param.name + '"'], { name: param.name + "_js", type: "JSValue" });
2023-06-04 11:41:18 +00:00
gen.declare(param.name + "_len", "size_t");
gen.call("JS_ToCStringLen", ["ctx", "&" + param.name + "_len", param.name + "_js"], { name: param.name + "_val", type: "const char *" });
gen.call("memcpy", ["(void *)textbuffer", param.name + "_val", param.name + "_len"]);
gen.statement("textbuffer[" + param.name + "_len] = 0");
gen.declare(param.name, param.type, false, "textbuffer");
gen.declare(lenParam.name, lenParam.type, false, "4096");
},
2023-06-04 20:31:15 +00:00
customCleanup: (gen, src) => {
2023-06-04 11:41:18 +00:00
gen.jsCleanUpParameter("const char *", param.name + "_val");
2023-06-04 20:31:15 +00:00
gen.call("JS_SetPropertyStr", ["ctx", src, `"${param.name}"`, "JS_NewString(ctx," + param.name + ")"]);
2023-06-04 11:41:18 +00:00
}
};
};
core.definitions.declare("textbuffer[4096]", "char", true);
2023-06-03 07:15:38 +00:00
setOutParam(getFunction(api.functions, "GuiDropdownBox"), 2);
setOutParam(getFunction(api.functions, "GuiSpinner"), 2);
setOutParam(getFunction(api.functions, "GuiValueBox"), 2);
setOutParam(getFunction(api.functions, "GuiListView"), 2);
2023-06-05 19:38:33 +00:00
// const setStringListParam = (fun: RayLibFunction, index: number, indexLen: number) => {
// const lenParam = fun!.params![indexLen]
// lenParam.binding = { ignore: true }
// const param = fun!.params![index]
// fun.binding = { customizeCall: "int returnVal = GuiListViewEx(bounds, text, count, focus, scrollIndex, active);" }
// param.binding = {
// jsType: `{ ${param.name}: string[] }`,
// customConverter: (gen,src) => {
// gen.line("// TODO: Read string values")
// },
// customCleanup: (gen, src) => {
// gen.line("// TODO: Dispose strings")
// }
// }
// }
//const glve = getFunction(api.functions, "GuiListViewEx")!
//setStringListParam(glve, 1,2)
//setOutParam(glve, 3)
//setOutParam(glve, 4)
2023-06-02 16:58:53 +00:00
ignore("GuiListViewEx");
2023-06-04 11:41:18 +00:00
setOutParamString(getFunction(api.functions, "GuiTextBox"), 1, 2);
2023-06-04 20:31:15 +00:00
const gtib = getFunction(api.functions, "GuiTextInputBox");
setOutParamString(gtib, 4, 5);
setOutParam(gtib, 6);
2023-06-05 19:38:33 +00:00
// needs string array
2023-06-02 16:58:53 +00:00
ignore("GuiTabBar");
2023-06-03 07:15:38 +00:00
ignore("GuiGetIcons");
2023-06-02 16:58:53 +00:00
ignore("GuiLoadIcons");
2023-05-26 22:47:44 +00:00
// TODO: Parse and support light struct
2023-06-02 16:58:53 +00:00
ignore("CreateLight");
ignore("UpdateLightValues");
api.structs.forEach(x => core.addApiStruct(x));
api.functions.forEach(x => core.addApiFunction(x));
2023-05-14 20:19:47 +00:00
api.defines.filter(x => x.type === "COLOR").map(x => ({ name: x.name, description: x.description, values: (x.value.match(/\{([^}]+)\}/) || "")[1].split(',').map(x => x.trim()) })).forEach(x => {
core.exportGlobalStruct("Color", x.name, x.values, x.description);
2023-05-08 21:37:58 +00:00
});
2023-06-02 16:58:53 +00:00
api.enums.forEach(x => core.addEnum(x));
core.exportGlobalConstant("MATERIAL_MAP_DIFFUSE", "Albedo material (same as: MATERIAL_MAP_DIFFUSE");
core.exportGlobalConstant("MATERIAL_MAP_SPECULAR", "Metalness material (same as: MATERIAL_MAP_SPECULAR)");
2023-05-14 20:19:47 +00:00
core.writeTo("src/bindings/js_raylib_core.h");
core.typings.writeTo("examples/lib.raylib.d.ts");
2023-06-02 16:58:53 +00:00
const ignored = api.functions.filter(x => x.binding?.ignore).length;
console.log(`Converted ${api.functions.length - ignored} function. ${ignored} ignored`);
console.log("Success!");
2023-05-06 21:43:40 +00:00
}
main();
})();
/******/ })()
;