You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

181 lines
4.6 KiB

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
"use strict"; // Capture any uncaughtException listeners already set, see below.
const uncaughtExceptionHandlers = process.listeners("uncaughtException");
const hermesc = require("./emhermesc.js")({
noInitialRun: true,
noExitRuntime: true,
// Do not call console methods
print: () => {},
printErr: () => {},
}); // Workaround: Emscripten adds an uncaught exception listener on startup, which
// rethrows and causes node to exit with code 7 and print emhermesc.js (1.4MB)
// to stdout. This removes any newly-set listeners.
//
// Remove when emhermesc.js is rebuilt with NODEJS_CATCH_EXIT=0 (D34790356)
const hermesUncaughtExceptionHandler = process
.listeners("uncaughtException")
.find((listener) => !uncaughtExceptionHandlers.includes(listener));
if (hermesUncaughtExceptionHandler != null) {
process.removeListener("uncaughtException", hermesUncaughtExceptionHandler);
}
const compileToBytecode = hermesc.cwrap("hermesCompileToBytecode", "number", [
"number",
"number",
"string",
"number",
"number",
]);
const getError = hermesc.cwrap("hermesCompileResult_getError", "string", [
"number",
]);
const getBytecodeAddr = hermesc.cwrap(
"hermesCompileResult_getBytecodeAddr",
"number",
["number"]
);
const getBytecodeSize = hermesc.cwrap(
"hermesCompileResult_getBytecodeSize",
"number",
["number"]
);
const free = hermesc.cwrap("hermesCompileResult_free", "void", ["number"]);
const props = JSON.parse(
hermesc.ccall("hermesGetProperties", "string", [], [])
);
function strdup(str) {
var copy = Buffer.from(str, "utf8");
var size = copy.length + 1;
var address = hermesc._malloc(size);
if (!address) {
throw new Error("hermesc string allocation error");
}
hermesc.HEAP8.set(copy, address);
hermesc.HEAP8[address + copy.length] = 0;
return {
ptr: address,
size,
};
}
const align = (offset) =>
/* eslint-disable-next-line no-bitwise */
(offset + props.BYTECODE_ALIGNMENT - 1) & ~(props.BYTECODE_ALIGNMENT - 1);
module.exports.align = align;
module.exports.compile = function (source, { sourceURL, sourceMap }) {
const buffer =
typeof source === "string" ? Buffer.from(source, "utf8") : source;
const address = hermesc._malloc(buffer.length + 1);
if (!address) {
throw new Error("Hermesc is out of memory.");
}
try {
hermesc.HEAP8.set(buffer, address);
hermesc.HEAP8[address + buffer.length] = 0; // Strings are passed on the stack by default. Explicitly pass the source map
// on the heap to avoid problems with large ones.
const sourceMapNotNull =
sourceMap !== null && sourceMap !== void 0 ? sourceMap : "";
const mapOnHeap = strdup(sourceMapNotNull);
let result;
try {
result = compileToBytecode(
address,
buffer.length + 1,
sourceURL,
mapOnHeap.ptr,
mapOnHeap.size
);
} finally {
hermesc._free(mapOnHeap.ptr);
}
try {
const error = getError(result);
if (error) {
throw new Error(error);
}
const bufferFromHBC = Buffer.from(
hermesc.HEAP8.buffer,
getBytecodeAddr(result),
getBytecodeSize(result)
);
const bytecode = Buffer.alloc(align(bufferFromHBC.length));
bufferFromHBC.copy(bytecode, 0);
return {
bytecode,
};
} finally {
free(result);
}
} finally {
hermesc._free(address);
}
};
module.exports.validateBytecodeModule = function (bytecode, offset) {
if ((bytecode.byteOffset + offset) % props.BYTECODE_ALIGNMENT) {
throw new Error(
"Bytecode is not aligned to " + props.BYTECODE_ALIGNMENT + "."
);
}
const fileLength = bytecode.readUInt32LE(offset + props.LENGTH_OFFSET);
if (
bytecode.length - offset < props.HEADER_SIZE ||
bytecode.length - offset < fileLength
) {
throw new Error("Bytecode buffer is too small.");
}
if (
bytecode.readUInt32LE(offset + 0) !== props.MAGIC[0] ||
bytecode.readUInt32LE(offset + 4) !== props.MAGIC[1]
) {
throw new Error("Bytecode buffer is missing magic value.");
}
const version = bytecode.readUInt32LE(offset + 8);
if (version !== props.VERSION) {
throw new Error(
"Bytecode version is " +
version +
" but " +
props.VERSION +
" is required."
);
}
};
module.exports.getFileLength = function (bytecode, offset) {
return bytecode.readUInt32LE(offset + props.LENGTH_OFFSET);
};
module.exports.VERSION = props.VERSION;