diff --git a/src/parseTools.mjs b/src/parseTools.mjs index 786c028d5a902..2b6c3865ee666 100644 --- a/src/parseTools.mjs +++ b/src/parseTools.mjs @@ -331,14 +331,39 @@ function splitI64(value) { export function indentify(text, indent) { // Don't try to indentify huge strings - we may run out of memory if (text.length > 1024 * 1024) return text; - if (typeof indent == 'number') { - const len = indent; - indent = ''; - for (let i = 0; i < len; i++) { - indent += ' '; + + indent = ' '.repeat(indent); + + // Perform indentation in a smart fashion that does not leak indentation + // inside multiline strings enclosed in `` characters. + let out = ''; + for (let i = 0; i < text.length; ++i) { + // Output a C++ comment as-is, don't get confused by ` inside a C++ comment. + if (text[i] == '/' && text[i + 1] == '/') { + for (; i < text.length && text[i] != '\n'; ++i) { + out += text[i]; + } + } + + if (text[i] == '/' && text[i + 1] == '*') { + // Skip /* so that /*/ won't be mistaken as start& end of a /* */ comment. + out += text[i++]; + out += text[i++]; + for (; i < text.length && !(text[i - 1] == '*' && text[i] == '/'); ++i) { + out += text[i]; + } + } + + if (text[i] == '`') { + out += text[i++]; // Emit ` + for (; i < text.length && text[i] != '`'; ++i) { + out += text[i]; + } } + out += text[i]; + if (text[i] == '\n') out += indent; } - return text.replace(/\n/g, `\n${indent}`); + return out; } // Correction tools diff --git a/test/codesize/test_codesize_file_preload.expected.js b/test/codesize/test_codesize_file_preload.expected.js index 6f271cf72afc0..0b5c62f89b9ef 100644 --- a/test/codesize/test_codesize_file_preload.expected.js +++ b/test/codesize/test_codesize_file_preload.expected.js @@ -681,15 +681,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; /** - * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given - * array that contains uint8 values, returns a copy of that string as a - * Javascript String object. - * heapOrArray is either a regular array, or a JavaScript typed array view. - * @param {number=} idx - * @param {number=} maxBytesToRead - * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. - * @return {string} - */ var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead, ignoreNul) => { + * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given + * array that contains uint8 values, returns a copy of that string as a + * Javascript String object. + * heapOrArray is either a regular array, or a JavaScript typed array view. + * @param {number=} idx + * @param {number=} maxBytesToRead + * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. + * @return {string} + */ var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead, ignoreNul) => { var endPtr = findStringEnd(heapOrArray, idx, maxBytesToRead, ignoreNul); // When using conditional TextDecoder, skip it for short strings as the overhead of the native call is not worth it. if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { @@ -2980,18 +2980,18 @@ var FS = { }; /** - * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the - * emscripten HEAP, returns a copy of that string as a Javascript String object. - * - * @param {number} ptr - * @param {number=} maxBytesToRead - An optional length that specifies the - * maximum number of bytes to read. You can omit this parameter to scan the - * string until the first 0 byte. If maxBytesToRead is passed, and the string - * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the - * string will cut short at that byte index. - * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. - * @return {string} - */ var UTF8ToString = (ptr, maxBytesToRead, ignoreNul) => ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead, ignoreNul) : ""; + * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the + * emscripten HEAP, returns a copy of that string as a Javascript String object. + * + * @param {number} ptr + * @param {number=} maxBytesToRead - An optional length that specifies the + * maximum number of bytes to read. You can omit this parameter to scan the + * string until the first 0 byte. If maxBytesToRead is passed, and the string + * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the + * string will cut short at that byte index. + * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. + * @return {string} + */ var UTF8ToString = (ptr, maxBytesToRead, ignoreNul) => ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead, ignoreNul) : ""; var SYSCALLS = { calculateAt(dirfd, path, allowEmpty) { diff --git a/test/codesize/test_codesize_minimal_O0.expected.js b/test/codesize/test_codesize_minimal_O0.expected.js index 7f5e25905054a..23e18a65970cc 100644 --- a/test/codesize/test_codesize_minimal_O0.expected.js +++ b/test/codesize/test_codesize_minimal_O0.expected.js @@ -797,9 +797,9 @@ async function createWasm() { /** - * @param {number} ptr - * @param {string} type - */ + * @param {number} ptr + * @param {string} type + */ function getValue(ptr, type = 'i8') { if (type.endsWith('*')) type = '*'; switch (type) { @@ -825,10 +825,10 @@ async function createWasm() { /** - * @param {number} ptr - * @param {number} value - * @param {string} type - */ + * @param {number} ptr + * @param {number} value + * @param {string} type + */ function setValue(ptr, value, type = 'i8') { if (type.endsWith('*')) type = '*'; switch (type) { diff --git a/test/codesize/test_unoptimized_code_size.json b/test/codesize/test_unoptimized_code_size.json index b1c6db56dd000..e6509de000c32 100644 --- a/test/codesize/test_unoptimized_code_size.json +++ b/test/codesize/test_unoptimized_code_size.json @@ -1,16 +1,16 @@ { - "hello_world.js": 56957, - "hello_world.js.gz": 17711, + "hello_world.js": 56901, + "hello_world.js.gz": 17707, "hello_world.wasm": 15138, "hello_world.wasm.gz": 7455, - "no_asserts.js": 26627, - "no_asserts.js.gz": 8882, + "no_asserts.js": 26571, + "no_asserts.js.gz": 8878, "no_asserts.wasm": 12187, "no_asserts.wasm.gz": 5984, - "strict.js": 54932, - "strict.js.gz": 17044, + "strict.js": 54876, + "strict.js.gz": 17043, "strict.wasm": 15138, "strict.wasm.gz": 7450, - "total": 180979, - "total_gz": 64526 + "total": 180811, + "total_gz": 64517 } diff --git a/test/jslib/test_multiline_string.c b/test/jslib/test_multiline_string.c new file mode 100644 index 0000000000000..d997d69956828 --- /dev/null +++ b/test/jslib/test_multiline_string.c @@ -0,0 +1,10 @@ +#include +#include + +char *test_multiline_string(void); + +int main() { + char *str = test_multiline_string(); + printf("%s\n", str); + free(str); +} diff --git a/test/jslib/test_multiline_string.js b/test/jslib/test_multiline_string.js new file mode 100644 index 0000000000000..d101a6f9b65c8 --- /dev/null +++ b/test/jslib/test_multiline_string.js @@ -0,0 +1,15 @@ +mergeInto(LibraryManager.library, { + test_multiline_string__deps: ['$stringToNewUTF8'], + test_multiline_string__sig: 'p', + test_multiline_string: function() { + // do not get confused by ` inside a comment. + var a = `abc +def +ghi`; + /* or a ` inside a C comment. */ + var b = `abc +def +ghi`; + return stringToNewUTF8(a + b); + } +}); diff --git a/test/jslib/test_multiline_string.out b/test/jslib/test_multiline_string.out new file mode 100644 index 0000000000000..bc7e0a2c6ed6a --- /dev/null +++ b/test/jslib/test_multiline_string.out @@ -0,0 +1,5 @@ +abc +def +ghiabc +def +ghi diff --git a/test/test_jslib.py b/test/test_jslib.py index a736b5082934c..1624e6e530a30 100644 --- a/test/test_jslib.py +++ b/test/test_jslib.py @@ -748,3 +748,12 @@ class ParentClass {} } ''') self.do_runf('src.c', 'MyClass: 42', cflags=['--js-library', 'lib.js']) + + # Tests that JS library functions containing multiline strings are not disturbed by e.g. inserting indentation into the output. + @parameterized({ + '': ([],), + 'single_file': (['-sSINGLE_FILE'],), + 'closure': (['--closure', '1'],), + }) + def test_multiline_string(self, args): + self.do_run_in_out_file_test('jslib/test_multiline_string.c', cflags=['--js-library', test_file('jslib/test_multiline_string.js')] + args)