generate-interface.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. const stream = require('stream');
  2. const reader = require('./reader.js');
  3. const type2llvm = {
  4. 'double': 'double',
  5. 'int': 'i32'
  6. }
  7. const argGenerators = {
  8. double: function (name, needStack) {
  9. let code = '';
  10. code += ` // argument: double ${name}\n`;
  11. code += ` if (typeof ${name} !== 'number') {\n`;
  12. if (needStack) code += ' cephes.stackRestore(stacktop);\n';
  13. code += ` throw new TypeError('${name} must be a number');\n`;
  14. code += ` }\n`;
  15. code += ` const carg_${name} = ${name};\n`;
  16. return code;
  17. },
  18. int: function (name, needStack) {
  19. let code = '';
  20. code += ` // argument: int ${name}\n`;
  21. code += ` if (typeof ${name} !== 'number') {\n`;
  22. if (needStack) code += ' cephes.stackRestore(stacktop);\n';
  23. code += ` throw new TypeError('${name} must be a number');\n`;
  24. code += ` }\n`;
  25. code += ` const carg_${name} = ${name} | 0;\n`;
  26. return code;
  27. },
  28. "double*": function (name, needStack) {
  29. let code = '';
  30. code += ` // argument: double* ${name}\n`;
  31. code += ` const carg_${name} = cephes.stackAlloc(8); // No need to zero-set it.\n`;
  32. return code;
  33. },
  34. "int*": function (name, needStack) {
  35. let code = '';
  36. code += ` // argument: int* ${name}\n`;
  37. code += ` const carg_${name} = cephes.stackAlloc(4); // No need to zero-set it.\n`;
  38. return code;
  39. },
  40. "double[]": function (name, needStack) {
  41. let code = '';
  42. code += ` // argument: double[] ${name}\n`;
  43. code += ` if (!(${name} instanceof Float64Array)) {\n`;
  44. if (needStack) code += ' cephes.stackRestore(stacktop);\n';
  45. code += ` throw new TypeError('${name} must be either a Float64Array');\n`;
  46. code += ` }\n`;
  47. code += ` const carg_${name} = cephes.stackAlloc(${name}.length << 3);\n`;
  48. code += ` cephes.writeArrayToMemory(new Uint8Array(${name}.buffer, ${name}.byteOffset, ${name}.byteLength), carg_${name});\n`;
  49. return code;
  50. }
  51. };
  52. const header = `
  53. const cephes = require('./cephes.js');
  54. // Export compiled promise, in Node.js this is just a dummy promise as the
  55. // WebAssembly program will be compiled synchronously. It takes about 20ms
  56. // as of Node.js v10.6.1.
  57. exports.compiled = cephes.compiled;
  58. `;
  59. class InterfaceGenerator extends stream.Transform {
  60. constructor() {
  61. super({ objectMode: true });
  62. this.push(header);
  63. }
  64. _transform(data, encoding, done) {
  65. const {filename, returnType, functionName, functionArgs} = data;
  66. // Check if the stack will be needed because of isPointer or isArray
  67. const needStack = functionArgs.some((arg) => arg.isArray || arg.isPointer);
  68. // Check if there is extra data returned
  69. const extraReturn = functionArgs.some((arg) => arg.isPointer);
  70. //
  71. // Start code generation
  72. //
  73. let code = '';
  74. //
  75. // function header
  76. //
  77. // function name
  78. code += `// from cephes/${filename}.c\n`;
  79. code += `exports.${functionName} = function ${functionName}(`
  80. // function arguments
  81. for (const {type, isPointer, isArray, name} of functionArgs) {
  82. if (isPointer) continue;
  83. code += `/* ${type}${isArray ? '[]' : ''} */ ${name}, `;
  84. }
  85. // Remove training comma
  86. code = code.slice(0, -2);
  87. // finish function header
  88. code += `) {\n`;
  89. if (needStack) {
  90. code += ' //Save the STACKTOP because the following code will do some stack allocs\n';
  91. code += ` const stacktop = cephes.stackSave();\n`;
  92. code += '\n';
  93. }
  94. //
  95. // function arguments
  96. //
  97. for (const {fullType, name} of functionArgs) {
  98. code += argGenerators[fullType](name, needStack);
  99. code += '\n';
  100. }
  101. //
  102. // function call
  103. //
  104. code += ` // return: ${returnType}\n`;
  105. // function call
  106. code += ` const fn_ret = cephes._cephes_${functionName}(`;
  107. // function arguments
  108. for (const { name } of functionArgs) {
  109. code += `carg_${name}, `;
  110. }
  111. // Remove training comma
  112. code = code.slice(0, -2);
  113. // finish function header
  114. code += `)${returnType === 'int' ? ' | 0' : ''};\n`;
  115. code += '\n';
  116. //
  117. // function return
  118. //
  119. if (extraReturn) {
  120. code += ' // There are pointers, so return the values of thoese too\n';
  121. code += ' const ret = [fn_ret, {\n';
  122. for (const { isPointer, name, type } of functionArgs) {
  123. if (!isPointer) continue;
  124. code += ` '${name}': cephes.getValue(carg_${name}, '${type2llvm[type]}'),\n`;
  125. }
  126. code += ' }];\n';
  127. } else {
  128. code += ' // No pointers, so just return fn_ret\n';
  129. code += ' const ret = fn_ret;\n';
  130. }
  131. code += '\n';
  132. //
  133. // function footer
  134. //
  135. if (needStack) {
  136. code += ' // Restore internal stacktop before returning\n';
  137. code += ' cephes.stackRestore(stacktop);\n';
  138. }
  139. code += ' return ret;\n';
  140. code += '};\n';
  141. code += '\n';
  142. done(null, code);
  143. }
  144. }
  145. process.stdin
  146. .pipe(reader())
  147. .pipe(new InterfaceGenerator())
  148. .pipe(process.stdout)