123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- const stream = require('stream');
- const reader = require('./reader.js');
- const type2llvm = {
- 'double': 'double',
- 'int': 'i32'
- }
- const argGenerators = {
- double: function (name, needStack) {
- let code = '';
- code += ` // argument: double ${name}\n`;
- code += ` if (typeof ${name} !== 'number') {\n`;
- if (needStack) code += ' cephes.stackRestore(stacktop);\n';
- code += ` throw new TypeError('${name} must be a number');\n`;
- code += ` }\n`;
- code += ` const carg_${name} = ${name};\n`;
- return code;
- },
- int: function (name, needStack) {
- let code = '';
- code += ` // argument: int ${name}\n`;
- code += ` if (typeof ${name} !== 'number') {\n`;
- if (needStack) code += ' cephes.stackRestore(stacktop);\n';
- code += ` throw new TypeError('${name} must be a number');\n`;
- code += ` }\n`;
- code += ` const carg_${name} = ${name} | 0;\n`;
- return code;
- },
- "double*": function (name, needStack) {
- let code = '';
- code += ` // argument: double* ${name}\n`;
- code += ` const carg_${name} = cephes.stackAlloc(8); // No need to zero-set it.\n`;
- return code;
- },
- "int*": function (name, needStack) {
- let code = '';
- code += ` // argument: int* ${name}\n`;
- code += ` const carg_${name} = cephes.stackAlloc(4); // No need to zero-set it.\n`;
- return code;
- },
- "double[]": function (name, needStack) {
- let code = '';
- code += ` // argument: double[] ${name}\n`;
- code += ` if (!(${name} instanceof Float64Array)) {\n`;
- if (needStack) code += ' cephes.stackRestore(stacktop);\n';
- code += ` throw new TypeError('${name} must be either a Float64Array');\n`;
- code += ` }\n`;
- code += ` const carg_${name} = cephes.stackAlloc(${name}.length << 3);\n`;
- code += ` cephes.writeArrayToMemory(new Uint8Array(${name}.buffer, ${name}.byteOffset, ${name}.byteLength), carg_${name});\n`;
- return code;
- }
- };
- const header = `
- const cephes = require('./cephes.js');
- // Export compiled promise, in Node.js this is just a dummy promise as the
- // WebAssembly program will be compiled synchronously. It takes about 20ms
- // as of Node.js v10.6.1.
- exports.compiled = cephes.compiled;
- `;
- class InterfaceGenerator extends stream.Transform {
- constructor() {
- super({ objectMode: true });
- this.push(header);
- }
- _transform(data, encoding, done) {
- const {filename, returnType, functionName, functionArgs} = data;
- // Check if the stack will be needed because of isPointer or isArray
- const needStack = functionArgs.some((arg) => arg.isArray || arg.isPointer);
- // Check if there is extra data returned
- const extraReturn = functionArgs.some((arg) => arg.isPointer);
- //
- // Start code generation
- //
- let code = '';
- //
- // function header
- //
- // function name
- code += `// from cephes/${filename}.c\n`;
- code += `exports.${functionName} = function ${functionName}(`
- // function arguments
- for (const {type, isPointer, isArray, name} of functionArgs) {
- if (isPointer) continue;
- code += `/* ${type}${isArray ? '[]' : ''} */ ${name}, `;
- }
- // Remove training comma
- code = code.slice(0, -2);
- // finish function header
- code += `) {\n`;
- if (needStack) {
- code += ' //Save the STACKTOP because the following code will do some stack allocs\n';
- code += ` const stacktop = cephes.stackSave();\n`;
- code += '\n';
- }
- //
- // function arguments
- //
- for (const {fullType, name} of functionArgs) {
- code += argGenerators[fullType](name, needStack);
- code += '\n';
- }
- //
- // function call
- //
- code += ` // return: ${returnType}\n`;
- // function call
- code += ` const fn_ret = cephes._cephes_${functionName}(`;
- // function arguments
- for (const { name } of functionArgs) {
- code += `carg_${name}, `;
- }
- // Remove training comma
- code = code.slice(0, -2);
- // finish function header
- code += `)${returnType === 'int' ? ' | 0' : ''};\n`;
- code += '\n';
- //
- // function return
- //
- if (extraReturn) {
- code += ' // There are pointers, so return the values of thoese too\n';
- code += ' const ret = [fn_ret, {\n';
- for (const { isPointer, name, type } of functionArgs) {
- if (!isPointer) continue;
- code += ` '${name}': cephes.getValue(carg_${name}, '${type2llvm[type]}'),\n`;
- }
- code += ' }];\n';
- } else {
- code += ' // No pointers, so just return fn_ret\n';
- code += ' const ret = fn_ret;\n';
- }
- code += '\n';
- //
- // function footer
- //
- if (needStack) {
- code += ' // Restore internal stacktop before returning\n';
- code += ' cephes.stackRestore(stacktop);\n';
- }
- code += ' return ret;\n';
- code += '};\n';
- code += '\n';
- done(null, code);
- }
- }
- process.stdin
- .pipe(reader())
- .pipe(new InterfaceGenerator())
- .pipe(process.stdout)
|