From d01609411c0061107c805448a0e25e723bb8bef3 Mon Sep 17 00:00:00 2001 From: milahu Date: Mon, 6 Sep 2021 22:22:01 +0200 Subject: [PATCH] fix: Maximum call stack size exceeded (#40) --- src/print/handlers.js | 370 ++++++++++++++++++++++-------------------- 1 file changed, 193 insertions(+), 177 deletions(-) diff --git a/src/print/handlers.js b/src/print/handlers.js index 9603f88..08dca6c 100644 --- a/src/print/handlers.js +++ b/src/print/handlers.js @@ -1,3 +1,19 @@ +// push-array.js start +// TODO move pushArray to npm module +Array.prototype.pushArray = function pushArray(...otherList) { + let c = 0; // count pushed elements + for (let a = 0; a < otherList.length; a++) { + const other = otherList[a]; + for (let i = 0; i < other.length; i++) { + this.push(other[i]); + c++; + } + } + return c; +}; +// push-array.js end + + // heavily based on https://github.com/davidbonnet/astring // released under MIT license https://github.com/davidbonnet/astring/blob/master/LICENSE @@ -248,7 +264,7 @@ const join = (nodes, separator) => { const joined = [...nodes[0]]; for (let i = 1; i < nodes.length; i += 1) { - joined.push(separator, ...nodes[i]); + joined.pushArray([separator], nodes[i]); } return joined; }; @@ -326,8 +342,8 @@ const handle_body = (nodes, state) => { ); } - chunks.push( - ...body[i] + chunks.pushArray( + body[i] ); needed_padding = needs_padding; @@ -356,10 +372,10 @@ const handle_var_declaration = (node, state) => { const separator = c(multiple_lines ? `,\n${state.indent}\t` : ', '); if (multiple_lines) { - chunks.push(...join(declarators, separator)); + chunks.pushArray(join(declarators, separator)); } else { - chunks.push( - ...join(declarators, separator) + chunks.pushArray( + join(declarators, separator) ); } @@ -416,9 +432,9 @@ const handlers = { ]; if (node.alternate) { - chunks.push( - c(' else '), - ...handle(node.alternate, state) + chunks.pushArray( + [c(' else ')], + handle(node.alternate, state) ); } @@ -463,19 +479,19 @@ const handlers = { node.cases.forEach(block => { if (block.test) { - chunks.push( - c(`\n${state.indent}\tcase `), - ...handle(block.test, { ...state, indent: `${state.indent}\t` }), - c(':') + chunks.pushArray( + [c(`\n${state.indent}\tcase `)], + handle(block.test, { ...state, indent: `${state.indent}\t` }), + [c(':')] ); } else { chunks.push(c(`\n${state.indent}\tdefault:`)) } block.consequent.forEach(statement => { - chunks.push( - c(`\n${state.indent}\t\t`), - ...handle(statement, { ...state, indent: `${state.indent}\t\t` }) + chunks.pushArray( + [c(`\n${state.indent}\t\t`)], + handle(statement, { ...state, indent: `${state.indent}\t\t` }) ); }); }); @@ -513,20 +529,20 @@ const handlers = { if (node.handler) { if (node.handler.param) { - chunks.push( - c(' catch('), - ...handle(node.handler.param, state), - c(') ') + chunks.pushArray( + [c(' catch(')], + handle(node.handler.param, state), + [c(') ')] ); } else { chunks.push(c(' catch ')); } - chunks.push(...handle(node.handler.body, state)); + chunks.pushArray(handle(node.handler.body, state)); } if (node.finalizer) { - chunks.push(c(' finally '), ...handle(node.finalizer, state)); + chunks.pushArray([c(' finally ')], handle(node.finalizer, state)); } return chunks; @@ -556,20 +572,20 @@ const handlers = { if (node.init) { if (node.init.type === 'VariableDeclaration') { - chunks.push(...handle_var_declaration(node.init, state)); + chunks.pushArray(handle_var_declaration(node.init, state)); } else { - chunks.push(...handle(node.init, state)); + chunks.pushArray(handle(node.init, state)); } } chunks.push(c('; ')); - if (node.test) chunks.push(...handle(node.test, state)); + if (node.test) chunks.pushArray(handle(node.test, state)); chunks.push(c('; ')); - if (node.update) chunks.push(...handle(node.update, state)); + if (node.update) chunks.pushArray(handle(node.update, state)); - chunks.push( - c(') '), - ...handle(node.body, state) + chunks.pushArray( + [c(') ')], + handle(node.body, state) ); return chunks; @@ -581,16 +597,16 @@ const handlers = { ]; if (node.left.type === 'VariableDeclaration') { - chunks.push(...handle_var_declaration(node.left, state)); + chunks.pushArray(handle_var_declaration(node.left, state)); } else { - chunks.push(...handle(node.left, state)); + chunks.pushArray(handle(node.left, state)); } - chunks.push( - c(node.type === 'ForInStatement' ? ` in ` : ` of `), - ...handle(node.right, state), - c(') '), - ...handle(node.body, state) + chunks.pushArray( + [c(node.type === 'ForInStatement' ? ` in ` : ` of `)], + handle(node.right, state), + [c(') ')], + handle(node.body, state) ); return chunks; @@ -605,7 +621,7 @@ const handlers = { if (node.async) chunks.push(c('async ')); chunks.push(c(node.generator ? 'function* ' : 'function ')); - if (node.id) chunks.push(...handle(node.id, state)); + if (node.id) chunks.pushArray(handle(node.id, state)); chunks.push(c('(')); const params = node.params.map(p => handle(p, { @@ -621,20 +637,20 @@ const handlers = { const separator = c(multiple_lines ? `,\n${state.indent}` : ', '); if (multiple_lines) { - chunks.push( - c(`\n${state.indent}\t`), - ...join(params, separator), - c(`\n${state.indent}`) + chunks.pushArray( + [c(`\n${state.indent}\t`)], + join(params, separator), + [c(`\n${state.indent}`)] ); } else { - chunks.push( - ...join(params, separator) + chunks.pushArray( + join(params, separator) ); } - chunks.push( - c(') '), - ...handle(node.body, state) + chunks.pushArray( + [c(') ')], + handle(node.body, state) ); return chunks; @@ -659,17 +675,17 @@ const handlers = { ClassDeclaration(node, state) { const chunks = [c('class ')]; - if (node.id) chunks.push(...handle(node.id, state), c(' ')); + if (node.id) chunks.pushArray(handle(node.id, state), [c(' ')]); if (node.superClass) { - chunks.push( - c('extends '), - ...handle(node.superClass, state), - c(' ') + chunks.pushArray( + [c('extends ')], + handle(node.superClass, state), + [c(' ')] ); } - chunks.push(...handle(node.body, state)); + chunks.pushArray(handle(node.body, state)); return chunks; }, @@ -717,16 +733,16 @@ const handlers = { const width = get_length(chunks) + specifiers.map(get_length).reduce(sum, 0) + (2 * specifiers.length) + 6 + get_length(source); if (width > 80) { - chunks.push( - c(`{\n\t`), - ...join(specifiers, c(',\n\t')), - c('\n}') + chunks.pushArray( + [c(`{\n\t`)], + join(specifiers, c(',\n\t')), + [c('\n}')] ); } else { - chunks.push( - c(`{ `), - ...join(specifiers, c(', ')), - c(' }') + chunks.pushArray( + [c(`{ `)], + join(specifiers, c(', ')), + [c(' }')] ); } } @@ -734,9 +750,9 @@ const handlers = { chunks.push(c(' from ')); } - chunks.push( - ...source, - c(';') + chunks.pushArray( + source, + [c(';')] ); return chunks; @@ -763,7 +779,7 @@ const handlers = { const chunks = [c('export ')]; if (node.declaration) { - chunks.push(...handle(node.declaration, state)); + chunks.pushArray(handle(node.declaration, state)); } else { const specifiers = node.specifiers.map((/** @type {ExportSpecifier} */ specifier) => { const name = handle(specifier.local, state)[0]; @@ -779,23 +795,23 @@ const handlers = { const width = 7 + specifiers.map(get_length).reduce(sum, 0) + 2 * specifiers.length; if (width > 80) { - chunks.push( - c('{\n\t'), - ...join(specifiers, c(',\n\t')), - c('\n}') + chunks.pushArray( + [c('{\n\t')], + join(specifiers, c(',\n\t')), + [c('\n}')] ); } else { - chunks.push( - c('{ '), - ...join(specifiers, c(', ')), - c(' }') + chunks.pushArray( + [c('{ ')], + join(specifiers, c(', ')), + [c(' }')] ); } if (node.source) { - chunks.push( - c(' from '), - ...handle(node.source, state) + chunks.pushArray( + [c(' from ')], + handle(node.source, state) ); } } @@ -834,26 +850,26 @@ const handlers = { } if (node.computed) { - chunks.push( - c('['), - ...handle(node.key, state), - c(']') + chunks.pushArray( + [c('[')], + handle(node.key, state), + [c(']')] ); } else { - chunks.push(...handle(node.key, state)); + chunks.pushArray(handle(node.key, state)); } chunks.push(c('(')); const { params } = node.value; for (let i = 0; i < params.length; i += 1) { - chunks.push(...handle(params[i], state)); + chunks.pushArray(handle(params[i], state)); if (i < params.length - 1) chunks.push(c(', ')); } - chunks.push( - c(') '), - ...handle(node.value.body, state) + chunks.pushArray( + [c(') ')], + handle(node.value.body, state) ); return chunks; @@ -865,30 +881,30 @@ const handlers = { if (node.async) chunks.push(c('async ')); if (node.params.length === 1 && node.params[0].type === 'Identifier') { - chunks.push(...handle(node.params[0], state)); + chunks.pushArray(handle(node.params[0], state)); } else { const params = node.params.map(param => handle(param, { ...state, indent: state.indent + '\t' })); - chunks.push( - c('('), - ...join(params, c(', ')), - c(')') + chunks.pushArray( + [c('(')], + join(params, c(', ')), + [c(')')] ); } chunks.push(c(' => ')); if (node.body.type === 'ObjectExpression') { - chunks.push( - c('('), - ...handle(node.body, state), - c(')') + chunks.pushArray( + [c('(')], + handle(node.body, state), + [c(')')] ); } else { - chunks.push(...handle(node.body, state)); + chunks.pushArray(handle(node.body, state)); } return chunks; @@ -934,11 +950,11 @@ const handlers = { const { quasis, expressions } = node; for (let i = 0; i < expressions.length; i++) { - chunks.push( - c(quasis[i].value.raw), - c('${'), - ...handle(expressions[i], state), - c('}') + chunks.pushArray( + [c(quasis[i].value.raw)], + [c('${')], + handle(expressions[i], state), + [c('}')] ); } @@ -983,14 +999,14 @@ const handlers = { ); if (multiple_lines) { - chunks.push( - c(`\n${state.indent}\t`), - ...join(elements, c(`,\n${state.indent}\t`)), - c(`\n${state.indent}`), - ...sparse_commas + chunks.pushArray( + [c(`\n${state.indent}\t`)], + join(elements, c(`,\n${state.indent}\t`)), + [c(`\n${state.indent}`)], + sparse_commas ); } else { - chunks.push(...join(elements, c(', ')), ...sparse_commas); + chunks.pushArray(join(elements, c(', ')), sparse_commas); } chunks.push(c(']')); @@ -1010,7 +1026,7 @@ const handlers = { const separator = c(', '); node.properties.forEach((p, i) => { - chunks.push(...handle(p, { + chunks.pushArray(handle(p, { ...state, indent: state.indent + '\t' })); @@ -1098,12 +1114,12 @@ const handlers = { chunks.push(c('*')); } - chunks.push( - ...(node.computed ? [c('['), ...key, c(']')] : key), - c('('), - ...join(node.value.params.map((/** @type {Pattern} */ param) => handle(param, state)), c(', ')), - c(') '), - ...handle(node.value.body, state) + chunks.pushArray( + node.computed ? [c('['), ...key, c(']')] : key, + [c('(')], + join(node.value.params.map((/** @type {Pattern} */ param) => handle(param, state)), c(', ')), + [c(') ')], + handle(node.value.body, state) ); return chunks; @@ -1129,7 +1145,7 @@ const handlers = { const chunks = [c('{ ')]; for (let i = 0; i < node.properties.length; i += 1) { - chunks.push(...handle(node.properties[i], state)); + chunks.pushArray(handle(node.properties[i], state)); if (i < node.properties.length - 1) chunks.push(c(', ')); } @@ -1159,13 +1175,13 @@ const handlers = { EXPRESSIONS_PRECEDENCE[node.argument.type] < EXPRESSIONS_PRECEDENCE.UnaryExpression ) { - chunks.push( - c('('), - ...handle(node.argument, state), - c(')') + chunks.pushArray( + [c('(')], + handle(node.argument, state), + [c(')')] ); } else { - chunks.push(...handle(node.argument, state)); + chunks.pushArray(handle(node.argument, state)); } return chunks; @@ -1196,25 +1212,25 @@ const handlers = { // } if (needs_parens(node.left, node, false)) { - chunks.push( - c('('), - ...handle(node.left, state), - c(')') + chunks.pushArray( + [c('(')], + handle(node.left, state), + [c(')')] ); } else { - chunks.push(...handle(node.left, state)); + chunks.pushArray(handle(node.left, state)); } chunks.push(c(` ${node.operator} `)); if (needs_parens(node.right, node, true)) { - chunks.push( - c('('), - ...handle(node.right, state), - c(')') + chunks.pushArray( + [c('(')], + handle(node.right, state), + [c(')')] ); } else { - chunks.push(...handle(node.right, state)); + chunks.pushArray(handle(node.right, state)); } return chunks; @@ -1227,12 +1243,12 @@ const handlers = { EXPRESSIONS_PRECEDENCE[node.test.type] > EXPRESSIONS_PRECEDENCE.ConditionalExpression ) { - chunks.push(...handle(node.test, state)); + chunks.pushArray(handle(node.test, state)); } else { - chunks.push( - c('('), - ...handle(node.test, state), - c(')') + chunks.pushArray( + [c('(')], + handle(node.test, state), + [c(')')] ); } @@ -1247,18 +1263,18 @@ const handlers = { ); if (multiple_lines) { - chunks.push( - c(`\n${state.indent}? `), - ...consequent, - c(`\n${state.indent}: `), - ...alternate + chunks.pushArray( + [c(`\n${state.indent}? `)], + consequent, + [c(`\n${state.indent}: `)], + alternate ); } else { - chunks.push( - c(` ? `), - ...consequent, - c(` : `), - ...alternate + chunks.pushArray( + [c(` ? `)], + consequent, + [c(` : `)], + alternate ); } @@ -1272,13 +1288,13 @@ const handlers = { EXPRESSIONS_PRECEDENCE[node.callee.type] < EXPRESSIONS_PRECEDENCE.CallExpression || has_call_expression(node.callee) ) { - chunks.push( - c('('), - ...handle(node.callee, state), - c(')') + chunks.pushArray( + [c('(')], + handle(node.callee, state), + [c(')')] ) } else { - chunks.push(...handle(node.callee, state)); + chunks.pushArray(handle(node.callee, state)); } // TODO this is copied from CallExpression — DRY it out @@ -1291,10 +1307,10 @@ const handlers = { ? c(',\n' + state.indent) : c(', '); - chunks.push( - c('('), - ...join(args, separator), - c(')') + chunks.pushArray( + [c('(')], + join(args, separator), + [c(')')] ); return chunks; @@ -1311,13 +1327,13 @@ const handlers = { EXPRESSIONS_PRECEDENCE[node.callee.type] < EXPRESSIONS_PRECEDENCE.CallExpression ) { - chunks.push( - c('('), - ...handle(node.callee, state), - c(')') + chunks.pushArray( + [c('(')], + handle(node.callee, state), + [c(')')] ); } else { - chunks.push(...handle(node.callee, state)); + chunks.pushArray(handle(node.callee, state)); } if (/** @type {SimpleCallExpression} */ (node).optional) { @@ -1335,16 +1351,16 @@ const handlers = { indent: `${state.indent}\t` })); - chunks.push( - c(`(\n${state.indent}\t`), - ...join(args, c(`,\n${state.indent}\t`)), - c(`\n${state.indent})`) + chunks.pushArray( + [c(`(\n${state.indent}\t`)], + join(args, c(`,\n${state.indent}\t`)), + [c(`\n${state.indent})`)] ); } else { - chunks.push( - c('('), - ...join(args, c(', ')), - c(')') + chunks.pushArray( + [c('(')], + join(args, c(', ')), + [c(')')] ); } @@ -1355,28 +1371,28 @@ const handlers = { const chunks = []; if (EXPRESSIONS_PRECEDENCE[node.object.type] < EXPRESSIONS_PRECEDENCE.MemberExpression) { - chunks.push( - c('('), - ...handle(node.object, state), - c(')') + chunks.pushArray( + [c('(')], + handle(node.object, state), + [c(')')] ); } else { - chunks.push(...handle(node.object, state)); + chunks.pushArray(handle(node.object, state)); } if (node.computed) { if (node.optional) { chunks.push(c('?.')); } - chunks.push( - c('['), - ...handle(node.property, state), - c(']') + chunks.pushArray( + [c('[')], + handle(node.property, state), + [c(']')] ); } else { - chunks.push( - c(node.optional ? '?.' : '.'), - ...handle(node.property, state) + chunks.pushArray( + [c(node.optional ? '?.' : '.')], + handle(node.property, state) ); }