ben-sb-shift-codegen
Version:
code generator for Shift format ASTs (forked for use in https://github.com/ben-sb/javascript-deobfuscator)
1,218 lines (1,081 loc) • 55 kB
JavaScript
const objectAssign = require('object-assign');
const { keyword } = require('esutils');
const { Precedence, getPrecedence, escapeStringLiteral, CodeRep, Empty, Token, NumberCodeRep, Paren, Bracket, Brace, NoIn, ContainsIn, Seq, SemiOp } = require('./coderep');
const INDENT = ' ';
class Linebreak extends CodeRep {
constructor() {
super();
this.indentation = 0;
}
emit(ts) {
ts.put('\n');
for (let i = 0; i < this.indentation; ++i) {
ts.put(INDENT);
}
}
}
function empty() {
return new Empty();
}
function noIn(rep) {
return new NoIn(rep);
}
function markContainsIn(state) {
return state.containsIn ? new ContainsIn(state) : state;
}
function seq(...reps) {
return new Seq(reps);
}
function isEmpty(codeRep) {
return codeRep instanceof Empty || codeRep instanceof Linebreak || codeRep instanceof Seq && codeRep.children.every(isEmpty);
}
let Sep = {};
const separatorNames = [
'ARRAY_EMPTY',
'ARRAY_BEFORE_COMMA',
'ARRAY_AFTER_COMMA',
'SPREAD',
'AWAIT',
'AFTER_FORAWAIT_AWAIT',
'BEFORE_DEFAULT_EQUALS',
'AFTER_DEFAULT_EQUALS',
'REST',
'OBJECT_BEFORE_COMMA',
'OBJECT_AFTER_COMMA',
'BEFORE_PROP',
'AFTER_PROP',
'BEFORE_JUMP_LABEL',
'ARGS_BEFORE_COMMA',
'ARGS_AFTER_COMMA',
'CALL',
'BEFORE_CATCH_BINDING',
'AFTER_CATCH_BINDING',
'BEFORE_CLASS_NAME',
'BEFORE_EXTENDS',
'AFTER_EXTENDS',
'BEFORE_CLASS_DECLARATION_ELEMENTS',
'BEFORE_CLASS_EXPRESSION_ELEMENTS',
'AFTER_STATIC',
'BEFORE_CLASS_ELEMENT',
'AFTER_CLASS_ELEMENT',
'BEFORE_TERNARY_QUESTION',
'AFTER_TERNARY_QUESTION',
'BEFORE_TERNARY_COLON',
'AFTER_TERNARY_COLON',
'COMPUTED_MEMBER_EXPRESSION',
'COMPUTED_MEMBER_ASSIGNMENT_TARGET',
'AFTER_DO',
'BEFORE_DOWHILE_WHILE',
'AFTER_DOWHILE_WHILE',
'AFTER_FORIN_FOR',
'BEFORE_FORIN_IN',
'AFTER_FORIN_FOR',
'BEFORE_FORIN_BODY',
'AFTER_FOROF_FOR',
'BEFORE_FOROF_OF',
'AFTER_FOROF_FOR',
'BEFORE_FOROF_BODY',
'AFTER_FOR_FOR',
'BEFORE_FOR_INIT',
'AFTER_FOR_INIT',
'EMPTY_FOR_INIT',
'BEFORE_FOR_TEST',
'AFTER_FOR_TEST',
'EMPTY_FOR_TEST',
'BEFORE_FOR_UPDATE',
'AFTER_FOR_UPDATE',
'EMPTY_FOR_UPDATE',
'BEFORE_FOR_BODY',
'BEFORE_GENERATOR_STAR',
'AFTER_GENERATOR_STAR',
'BEFORE_FUNCTION_PARAMS',
'BEFORE_FUNCTION_DECLARATION_BODY',
'BEFORE_FUNCTION_EXPRESSION_BODY',
'AFTER_FUNCTION_DIRECTIVES',
'BEFORE_ARROW',
'AFTER_ARROW',
'AFTER_GET',
'BEFORE_GET_PARAMS',
'BEFORE_GET_BODY',
'AFTER_IF',
'AFTER_IF_TEST',
'BEFORE_ELSE',
'AFTER_ELSE',
'PARAMETER_BEFORE_COMMA',
'PARAMETER_AFTER_COMMA',
'NAMED_IMPORT_BEFORE_COMMA',
'NAMED_IMPORT_AFTER_COMMA',
'IMPORT_BEFORE_COMMA',
'IMPORT_AFTER_COMMA',
'BEFORE_IMPORT_BINDINGS',
'BEFORE_IMPORT_MODULE',
'AFTER_IMPORT_BINDINGS',
'AFTER_FROM',
'BEFORE_IMPORT_NAMESPACE',
'BEFORE_IMPORT_STAR',
'AFTER_IMPORT_STAR',
'AFTER_IMPORT_AS',
'AFTER_NAMESPACE_BINDING',
'BEFORE_IMPORT_AS',
'AFTER_IMPORT_AS',
'EXPORTS_BEFORE_COMMA',
'EXPORTS_AFTER_COMMA',
'BEFORE_EXPORT_STAR',
'AFTER_EXPORT_STAR',
'BEFORE_EXPORT_BINDINGS',
'AFTER_EXPORT_FROM_BINDINGS',
'AFTER_EXPORT_LOCAL_BINDINGS',
'AFTER_EXPORT',
'EXPORT_DEFAULT',
'AFTER_EXPORT_DEFAULT',
'BEFORE_EXPORT_AS',
'AFTER_EXPORT_AS',
'BEFORE_LABEL_COLON',
'AFTER_LABEL_COLON',
'AFTER_METHOD_GENERATOR_STAR',
'AFTER_METHOD_ASYNC',
'AFTER_METHOD_NAME',
'BEFORE_METHOD_BODY',
'AFTER_MODULE_DIRECTIVES',
'AFTER_NEW',
'BEFORE_NEW_ARGS',
'EMPTY_NEW_CALL',
'NEW_TARGET_BEFORE_DOT',
'NEW_TARGET_AFTER_DOT',
'RETURN',
'AFTER_SET',
'BEFORE_SET_PARAMS',
'BEFORE_SET_BODY',
'AFTER_SCRIPT_DIRECTIVES',
'BEFORE_STATIC_MEMBER_DOT',
'AFTER_STATIC_MEMBER_DOT',
'BEFORE_STATIC_MEMBER_ASSIGNMENT_TARGET_DOT',
'AFTER_STATIC_MEMBER_ASSIGNMENT_TARGET_DOT',
'BEFORE_CASE_TEST',
'AFTER_CASE_TEST',
'BEFORE_CASE_BODY',
'AFTER_CASE_BODY',
'DEFAULT',
'AFTER_DEFAULT_BODY',
'BEFORE_SWITCH_DISCRIM',
'BEFORE_SWITCH_BODY',
'TEMPLATE_TAG',
'BEFORE_TEMPLATE_EXPRESSION',
'AFTER_TEMPLATE_EXPRESSION',
'THROW',
'AFTER_TRY',
'BEFORE_CATCH',
'BEFORE_FINALLY',
'AFTER_FINALLY',
'VARIABLE_DECLARATION',
'YIELD',
'BEFORE_YIELD_STAR',
'AFTER_YIELD_STAR',
'DECLARATORS_BEFORE_COMMA',
'DECLARATORS_AFTER_COMMA',
'BEFORE_INIT_EQUALS',
'AFTER_INIT_EQUALS',
'AFTER_WHILE',
'BEFORE_WHILE_BODY',
'AFTER_WITH',
'BEFORE_WITH_BODY',
'PAREN_AVOIDING_DIRECTIVE_BEFORE',
'PAREN_AVOIDING_DIRECTIVE_AFTER',
'PRECEDENCE_BEFORE',
'PRECEDENCE_AFTER',
'EXPRESSION_PAREN_BEFORE',
'EXPRESSION_PAREN_AFTER',
'CALL_PAREN_BEFORE',
'CALL_PAREN_AFTER',
'CALL_PAREN_EMPTY',
'CATCH_PAREN_BEFORE',
'CATCH_PAREN_AFTER',
'DO_WHILE_TEST_PAREN_BEFORE',
'DO_WHILE_TEST_PAREN_AFTER',
'EXPRESSION_STATEMENT_PAREN_BEFORE',
'EXPRESSION_STATEMENT_PAREN_AFTER',
'FOR_LET_PAREN_BEFORE',
'FOR_LET_PAREN_AFTER',
'FOR_IN_LET_PAREN_BEFORE',
'FOR_IN_LET_PAREN_AFTER',
'FOR_IN_PAREN_BEFORE',
'FOR_IN_PAREN_AFTER',
'FOR_OF_LET_PAREN_BEFORE',
'FOR_OF_LET_PAREN_AFTER',
'FOR_OF_PAREN_BEFORE',
'FOR_OF_PAREN_AFTER',
'PARAMETERS_PAREN_BEFORE',
'PARAMETERS_PAREN_AFTER',
'PARAMETERS_PAREN_EMPTY',
'ARROW_PARAMETERS_PAREN_BEFORE',
'ARROW_PARAMETERS_PAREN_AFTER',
'ARROW_PARAMETERS_PAREN_EMPTY',
'ARROW_BODY_PAREN_BEFORE',
'ARROW_BODY_PAREN_AFTER',
'BEFORE_ARROW_ASYNC_PARAMS',
'GETTER_PARAMS',
'IF_PAREN_BEFORE',
'IF_PAREN_AFTER',
'EXPORT_PAREN_BEFORE',
'EXPORT_PAREN_AFTER',
'NEW_CALLEE_PAREN_BEFORE',
'NEW_CALLEE_PAREN_AFTER',
'NEW_PAREN_BEFORE',
'NEW_PAREN_AFTER',
'NEW_PAREN_EMPTY',
'SETTER_PARAM_BEFORE',
'SETTER_PARAM_AFTER',
'SWITCH_DISCRIM_PAREN_BEFORE',
'SWITCH_DISCRIM_PAREN_AFTER',
'WHILE_TEST_PAREN_BEFORE',
'WHILE_TEST_PAREN_AFTER',
'WITH_PAREN_BEFORE',
'WITH_PAREN_AFTER',
'OBJECT_BRACE_INITIAL',
'OBJECT_BRACE_FINAL',
'OBJECT_EMPTY',
'BLOCK_BRACE_INITIAL',
'BLOCK_BRACE_FINAL',
'BLOCK_EMPTY',
'CLASS_BRACE_INITIAL',
'CLASS_BRACE_FINAL',
'CLASS_EMPTY',
'CLASS_EXPRESSION_BRACE_INITIAL',
'CLASS_EXPRESSION_BRACE_FINAL',
'CLASS_EXPRESSION_BRACE_EMPTY',
'FUNCTION_BRACE_INITIAL',
'FUNCTION_BRACE_FINAL',
'FUNCTION_EMPTY',
'FUNCTION_EXPRESSION_BRACE_INITIAL',
'FUNCTION_EXPRESSION_BRACE_FINAL',
'FUNCTION_EXPRESSION_EMPTY',
'ARROW_BRACE_INITIAL',
'ARROW_BRACE_FINAL',
'ARROW_BRACE_EMPTY',
'GET_BRACE_INTIAL',
'GET_BRACE_FINAL',
'GET_BRACE_EMPTY',
'MISSING_ELSE_INTIIAL',
'MISSING_ELSE_FINAL',
'MISSING_ELSE_EMPTY',
'IMPORT_BRACE_INTIAL',
'IMPORT_BRACE_FINAL',
'IMPORT_BRACE_EMPTY',
'EXPORT_BRACE_INITIAL',
'EXPORT_BRACE_FINAL',
'EXPORT_BRACE_EMPTY',
'METHOD_BRACE_INTIAL',
'METHOD_BRACE_FINAL',
'METHOD_BRACE_EMPTY',
'SET_BRACE_INTIIAL',
'SET_BRACE_FINAL',
'SET_BRACE_EMPTY',
'SWITCH_BRACE_INTIAL',
'SWITCH_BRACE_FINAL',
'SWITCH_BRACE_EMPTY',
'ARRAY_INITIAL',
'ARRAY_FINAL',
'COMPUTED_MEMBER_BRACKET_INTIAL',
'COMPUTED_MEMBER_BRACKET_FINAL',
'COMPUTED_MEMBER_ASSIGNMENT_TARGET_BRACKET_INTIAL',
'COMPUTED_MEMBER_ASSIGNMENT_TARGET_BRACKET_FINAL',
'COMPUTED_PROPERTY_BRACKET_INTIAL',
'COMPUTED_PROPERTY_BRACKET_FINAL',
];
for (let i = 0; i < separatorNames.length; ++i) {
Sep[separatorNames[i]] = { type: separatorNames[i] };
}
Sep.BEFORE_ASSIGN_OP = function (op) {
return {
type: 'BEFORE_ASSIGN_OP',
op,
};
};
Sep.AFTER_ASSIGN_OP = function (op) {
return {
type: 'AFTER_ASSIGN_OP',
op,
};
};
Sep.BEFORE_BINOP = function (op) {
return {
type: 'BEFORE_BINOP',
op,
};
};
Sep.AFTER_BINOP = function (op) {
return {
type: 'AFTER_BINOP',
op,
};
};
Sep.BEFORE_POSTFIX = function (op) {
return {
type: 'BEFORE_POSTFIX',
op,
};
};
Sep.UNARY = function (op) {
return {
type: 'UNARY',
op,
};
};
Sep.AFTER_STATEMENT = function (node) {
return {
type: 'AFTER_STATEMENT',
node,
};
};
Sep.BEFORE_FUNCTION_NAME = function (node) {
return {
type: 'BEFORE_FUNCTION_NAME',
node,
};
};
class ExtensibleCodeGen {
parenToAvoidBeingDirective(element, original) {
if (element && element.type === 'ExpressionStatement' && element.expression.type === 'LiteralStringExpression') {
return seq(this.paren(original.children[0], Sep.PAREN_AVOIDING_DIRECTIVE_BEFORE, Sep.PAREN_AVOIDING_DIRECTIVE_AFTER), this.semiOp());
}
return original;
}
t(token, isRegExp = false) {
return new Token(token, isRegExp);
}
p(node, precedence, a) {
return getPrecedence(node) < precedence ? this.paren(a, Sep.PRECEDENCE_BEFORE, Sep.PRECEDENCE_AFTER) : a;
}
getAssignmentExpr(state) {
return state ? state.containsGroup ? this.paren(state, Sep.EXPRESSION_PAREN_BEFORE, Sep.EXPRESSION_PAREN_AFTER) : state : empty();
}
paren(rep, first, last, emptySep) {
if (isEmpty(rep)) {
return new Paren(this.sep(emptySep));
}
return new Paren(seq(first ? this.sep(first) : empty(), rep, last ? this.sep(last) : empty()));
}
brace(rep, node, first, last, emptySep) {
if (isEmpty(rep)) {
return new Brace(this.sep(emptySep));
}
return new Brace(seq(this.sep(first), rep, this.sep(last)));
}
bracket(rep, first, last, emptySep) {
if (isEmpty(rep)) {
return new Bracket(this.sep(emptySep));
}
return new Bracket(seq(this.sep(first), rep, this.sep(last)));
}
commaSep(pieces, before, after) {
let first = true;
pieces = pieces.map(p => {
if (first) {
first = false;
return p;
}
return seq(this.sep(before), this.t(','), this.sep(after), p);
});
return seq(...pieces);
}
semiOp() {
return new SemiOp;
}
sep(/* kind */) {
return empty();
}
reduceArrayExpression(node, { elements }) {
if (elements.length === 0) {
return this.bracket(empty(), null, null, Sep.ARRAY_EMPTY);
}
let content = this.commaSep(elements.map(e=>this.getAssignmentExpr(e)), Sep.ARRAY_BEFORE_COMMA, Sep.ARRAY_AFTER_COMMA);
if (elements.length > 0 && elements[elements.length - 1] == null) {
content = seq(content, this.sep(Sep.ARRAY_BEFORE_COMMA), this.t(','), this.sep(Sep.ARRAY_AFTER_COMMA));
}
return this.bracket(content, Sep.ARRAY_INITIAL, Sep.ARRAY_FINAL);
}
reduceAwaitExpression(node, { expression }) {
return seq(this.t('await'), this.sep(Sep.AWAIT), this.p(node.expression, getPrecedence(node), expression));
}
reduceSpreadElement(node, { expression }) {
return seq(this.t('...'), this.sep(Sep.SPREAD), this.p(node.expression, Precedence.Assignment, expression));
}
reduceSpreadProperty(node, { expression }) {
return seq(this.t('...'), this.sep(Sep.SPREAD), this.getAssignmentExpr(expression));
}
reduceAssignmentExpression(node, { binding, expression }) {
let leftCode = binding;
let rightCode = expression;
let containsIn = expression.containsIn;
let startsWithCurly = binding.startsWithCurly;
let startsWithLetSquareBracket = binding.startsWithLetSquareBracket;
let startsWithFunctionOrClass = binding.startsWithFunctionOrClass;
if (getPrecedence(node.expression) < getPrecedence(node)) {
rightCode = this.paren(rightCode, Sep.EXPRESSION_PAREN_BEFORE, Sep.EXPRESSION_PAREN_AFTER);
containsIn = false;
}
return objectAssign(seq(leftCode, this.sep(Sep.BEFORE_ASSIGN_OP('=')), this.t('='), this.sep(Sep.AFTER_ASSIGN_OP('=')), rightCode), { containsIn, startsWithCurly, startsWithLetSquareBracket, startsWithFunctionOrClass });
}
reduceAssignmentTargetIdentifier(node) {
let a = this.t(node.name);
if (node.name === 'let') {
a.startsWithLet = true;
}
return a;
}
reduceAssignmentTargetWithDefault(node, { binding, init }) {
return seq(binding, this.sep(Sep.BEFORE_DEFAULT_EQUALS), this.t('='), this.sep(Sep.AFTER_DEFAULT_EQUALS), this.p(node.init, Precedence.Assignment, init));
}
reduceCompoundAssignmentExpression(node, { binding, expression }) {
let leftCode = binding;
let rightCode = expression;
let containsIn = expression.containsIn;
let startsWithCurly = binding.startsWithCurly;
let startsWithLetSquareBracket = binding.startsWithLetSquareBracket;
let startsWithFunctionOrClass = binding.startsWithFunctionOrClass;
if (getPrecedence(node.expression) < getPrecedence(node)) {
rightCode = this.paren(rightCode, Sep.EXPRESSION_PAREN_BEFORE, Sep.EXPRESSION_PAREN_AFTER);
containsIn = false;
}
return objectAssign(seq(leftCode, this.sep(Sep.BEFORE_ASSIGN_OP(node.operator)), this.t(node.operator), this.sep(Sep.AFTER_ASSIGN_OP(node.operator)), rightCode), { containsIn, startsWithCurly, startsWithLetSquareBracket, startsWithFunctionOrClass });
}
reduceBinaryExpression(node, { left, right }) {
let leftCode = left;
let startsWithCurly = left.startsWithCurly;
let startsWithLetSquareBracket = left.startsWithLetSquareBracket;
let startsWithFunctionOrClass = left.startsWithFunctionOrClass;
let leftContainsIn = left.containsIn;
let isRightAssociative = node.operator === '**';
if (getPrecedence(node.left) < getPrecedence(node) || isRightAssociative && (getPrecedence(node.left) === getPrecedence(node) || node.left.type === 'UnaryExpression')) {
leftCode = this.paren(leftCode, Sep.EXPRESSION_PAREN_BEFORE, Sep.EXPRESSION_PAREN_AFTER);
startsWithCurly = false;
startsWithLetSquareBracket = false;
startsWithFunctionOrClass = false;
leftContainsIn = false;
}
let rightCode = right;
let rightContainsIn = right.containsIn;
if (getPrecedence(node.right) < getPrecedence(node) || !isRightAssociative && getPrecedence(node.right) === getPrecedence(node)) {
rightCode = this.paren(rightCode, Sep.EXPRESSION_PAREN_BEFORE, Sep.EXPRESSION_PAREN_AFTER);
rightContainsIn = false;
}
return objectAssign(
seq(leftCode, this.sep(Sep.BEFORE_BINOP(node.operator)), this.t(node.operator), this.sep(Sep.AFTER_BINOP(node.operator)), rightCode),
{
containsIn: leftContainsIn || rightContainsIn || node.operator === 'in',
containsGroup: node.operator === ',',
startsWithCurly,
startsWithLetSquareBracket,
startsWithFunctionOrClass,
}
);
}
reduceBindingWithDefault(node, { binding, init }) {
return seq(binding, this.sep(Sep.BEFORE_DEFAULT_EQUALS), this.t('='), this.sep(Sep.AFTER_DEFAULT_EQUALS), this.p(node.init, Precedence.Assignment, init));
}
reduceBindingIdentifier(node) {
let a = this.t(node.name);
if (node.name === 'let') {
a.startsWithLet = true;
}
return a;
}
reduceArrayAssignmentTarget(node, { elements, rest }) {
let content;
if (elements.length === 0) {
content = rest == null ? empty() : seq(this.t('...'), this.sep(Sep.REST), rest);
} else {
elements = elements.concat(rest == null ? [] : [seq(this.t('...'), this.sep(Sep.REST), rest)]);
content = this.commaSep(elements.map(e=>this.getAssignmentExpr(e)), Sep.ARRAY_BEFORE_COMMA, Sep.ARRAY_AFTER_COMMA);
if (elements.length > 0 && elements[elements.length - 1] == null) {
content = seq(content, this.sep(Sep.ARRAY_BEFORE_COMMA), this.t(','), this.sep(Sep.ARRAY_AFTER_COMMA));
}
}
return this.bracket(content, Sep.ARRAY_INITIAL, Sep.ARRAY_FINAL, Sep.ARRAY_EMPTY);
}
reduceArrayBinding(node, { elements, rest }) {
let content;
if (elements.length === 0) {
content = rest == null ? empty() : seq(this.t('...'), this.sep(Sep.REST), rest);
} else {
elements = elements.concat(rest == null ? [] : [seq(this.t('...'), this.sep(Sep.REST), rest)]);
content = this.commaSep(elements.map(e=>this.getAssignmentExpr(e)), Sep.ARRAY_BEFORE_COMMA, Sep.ARRAY_AFTER_COMMA);
if (elements.length > 0 && elements[elements.length - 1] == null) {
content = seq(content, this.sep(Sep.ARRAY_BEFORE_COMMA), this.t(','), this.sep(Sep.ARRAY_AFTER_COMMA));
}
}
return this.bracket(content, Sep.ARRAY_INITIAL, Sep.ARRAY_FINAL, Sep.ARRAY_EMPTY);
}
reduceObjectAssignmentTarget(node, { properties, rest }) {
let content;
if (properties.length === 0) {
content = rest == null ? empty() : seq(this.t('...'), this.sep(Sep.REST), rest);
} else {
content = this.commaSep(properties, Sep.OBJECT_BEFORE_COMMA, Sep.OBJECT_AFTER_COMMA);
content = rest == null ? content : this.commaSep([content, seq(this.t('...'), this.sep(Sep.REST), rest)], Sep.OBJECT_BEFORE_COMMA, Sep.OBJECT_AFTER_COMMA);
}
let state = this.brace(content, node, Sep.OBJECT_BRACE_INITIAL, Sep.OBJECT_BRACE_FINAL, Sep.OBJECT_EMPTY);
state.startsWithCurly = true;
return state;
}
reduceObjectBinding(node, { properties, rest }) {
let content;
if (properties.length === 0) {
content = rest == null ? empty() : seq(this.t('...'), this.sep(Sep.REST), rest);
} else {
content = this.commaSep(properties, Sep.OBJECT_BEFORE_COMMA, Sep.OBJECT_AFTER_COMMA);
content = rest == null ? content : this.commaSep([content, seq(this.t('...'), this.sep(Sep.REST), rest)], Sep.OBJECT_BEFORE_COMMA, Sep.OBJECT_AFTER_COMMA);
}
let state = this.brace(content, node, Sep.OBJECT_BRACE_INITIAL, Sep.OBJECT_BRACE_FINAL, Sep.OBJECT_EMPTY);
state.startsWithCurly = true;
return state;
}
reduceAssignmentTargetPropertyIdentifier(node, { binding, init }) {
if (node.init == null) return binding;
return seq(binding, this.sep(Sep.BEFORE_DEFAULT_EQUALS), this.t('='), this.sep(Sep.AFTER_DEFAULT_EQUALS), this.p(node.init, Precedence.Assignment, init));
}
reduceAssignmentTargetPropertyProperty(node, { name, binding }) {
return seq(name, this.sep(Sep.BEFORE_PROP), this.t(':'), this.sep(Sep.AFTER_PROP), binding);
}
reduceBindingPropertyIdentifier(node, { binding, init }) {
if (node.init == null) return binding;
return seq(binding, this.sep(Sep.BEFORE_DEFAULT_EQUALS), this.t('='), this.sep(Sep.AFTER_DEFAULT_EQUALS), this.p(node.init, Precedence.Assignment, init));
}
reduceBindingPropertyProperty(node, { name, binding }) {
return seq(name, this.sep(Sep.BEFORE_PROP), this.t(':'), this.sep(Sep.AFTER_PROP), binding);
}
reduceBlock(node, { statements }) {
return this.brace(seq(...statements), node, Sep.BLOCK_BRACE_INITIAL, Sep.BLOCK_BRACE_FINAL, Sep.BLOCK_EMPTY);
}
reduceBlockStatement(node, { block }) {
return seq(block, this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceBreakStatement(node) {
return seq(this.t('break'), node.label ? seq(this.sep(Sep.BEFORE_JUMP_LABEL), this.t(node.label)) : empty(), this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceCallExpression(node, { callee, arguments: args }) {
const parenthizedArgs = args.map((a, i) => this.p(node.arguments[i], Precedence.Assignment, a));
return objectAssign(
seq(this.p(node.callee, getPrecedence(node), callee), this.sep(Sep.CALL), this.paren(this.commaSep(parenthizedArgs, Sep.ARGS_BEFORE_COMMA, Sep.ARGS_AFTER_COMMA), Sep.CALL_PAREN_BEFORE, Sep.CALL_PAREN_AFTER, Sep.CALL_PAREN_EMPTY)),
{
startsWithCurly: callee.startsWithCurly,
startsWithLet: callee.startsWithLet,
startsWithLetSquareBracket: callee.startsWithLetSquareBracket,
startsWithFunctionOrClass: callee.startsWithFunctionOrClass,
}
);
}
reduceCatchClause(node, { binding, body }) {
if (binding == null) {
return seq(this.t('catch'), this.sep(Sep.BEFORE_CATCH_BINDING), body);
}
return seq(this.t('catch'),
this.sep(Sep.BEFORE_CATCH_BINDING),
this.paren(binding, Sep.CATCH_PAREN_BEFORE, Sep.CATCH_PAREN_AFTER),
this.sep(Sep.AFTER_CATCH_BINDING), body);
}
reduceClassDeclaration(node, { name, super: _super, elements }) {
let state = seq(this.t('class'), node.name.name === '*default*' ? empty() : seq(this.sep(Sep.BEFORE_CLASS_NAME), name));
if (_super != null) {
state = seq(state, this.sep(Sep.BEFORE_EXTENDS), this.t('extends'), this.sep(Sep.AFTER_EXTENDS), this.p(node.super, Precedence.New, _super));
}
state = seq(state, this.sep(Sep.BEFORE_CLASS_DECLARATION_ELEMENTS), this.brace(seq(...elements), node, Sep.CLASS_BRACE_INITIAL, Sep.CLASS_BRACE_FINAL, Sep.CLASS_EMPTY), this.sep(Sep.AFTER_STATEMENT(node)));
return state;
}
reduceClassExpression(node, { name, super: _super, elements }) {
let state = this.t('class');
if (name != null) {
state = seq(state, this.sep(Sep.BEFORE_CLASS_NAME), name);
}
if (_super != null) {
state = seq(state, this.sep(Sep.BEFORE_EXTENDS), this.t('extends'), this.sep(Sep.AFTER_EXTENDS), this.p(node.super, Precedence.New, _super));
}
state = seq(state, this.sep(Sep.BEFORE_CLASS_EXPRESSION_ELEMENTS), this.brace(seq(...elements), node, Sep.CLASS_EXPRESSION_BRACE_INITIAL, Sep.CLASS_EXPRESSION_BRACE_FINAL, Sep.CLASS_EXPRESSION_BRACE_EMPTY));
state.startsWithFunctionOrClass = true;
return state;
}
reduceClassElement(node, { method }) {
method = seq(this.sep(Sep.BEFORE_CLASS_ELEMENT), method, this.sep(Sep.AFTER_CLASS_ELEMENT));
if (!node.isStatic) return method;
return seq(this.t('static'), this.sep(Sep.AFTER_STATIC), method);
}
reduceComputedMemberAssignmentTarget(node, { object, expression }) {
let startsWithLetSquareBracket =
object.startsWithLetSquareBracket ||
node.object.type === 'IdentifierExpression' && node.object.name === 'let';
return objectAssign(
seq(this.p(node.object, getPrecedence(node), object), this.sep(Sep.COMPUTED_MEMBER_ASSIGNMENT_TARGET), this.bracket(expression, Sep.COMPUTED_MEMBER_ASSIGNMENT_TARGET_BRACKET_INTIAL, Sep.COMPUTED_MEMBER_ASSIGNMENT_TARGET_BRACKET_FINAL)),
{
startsWithLet: object.startsWithLet,
startsWithLetSquareBracket,
startsWithCurly: object.startsWithCurly,
startsWithFunctionOrClass: object.startsWithFunctionOrClass,
}
);
}
reduceComputedMemberExpression(node, { object, expression }) {
let startsWithLetSquareBracket =
object.startsWithLetSquareBracket ||
node.object.type === 'IdentifierExpression' && node.object.name === 'let';
return objectAssign(
seq(this.p(node.object, getPrecedence(node), object), this.sep(Sep.COMPUTED_MEMBER_EXPRESSION), this.bracket(expression, Sep.COMPUTED_MEMBER_BRACKET_INTIAL, Sep.COMPUTED_MEMBER_BRACKET_FINAL)),
{
startsWithLet: object.startsWithLet,
startsWithLetSquareBracket,
startsWithCurly: object.startsWithCurly,
startsWithFunctionOrClass: object.startsWithFunctionOrClass,
}
);
}
reduceComputedPropertyName(node, { expression }) {
return this.bracket(this.p(node.expression, Precedence.Assignment, expression), Sep.COMPUTED_PROPERTY_BRACKET_INTIAL, Sep.COMPUTED_PROPERTY_BRACKET_FINAL);
}
reduceConditionalExpression(node, { test, consequent, alternate }) {
let containsIn = test.containsIn || alternate.containsIn;
let startsWithCurly = test.startsWithCurly;
let startsWithLetSquareBracket = test.startsWithLetSquareBracket;
let startsWithFunctionOrClass = test.startsWithFunctionOrClass;
return objectAssign(
seq(
this.p(node.test, Precedence.LogicalOR, test), this.sep(Sep.BEFORE_TERNARY_QUESTION), this.t('?'), this.sep(Sep.AFTER_TERNARY_QUESTION),
this.p(node.consequent, Precedence.Assignment, consequent), this.sep(Sep.BEFORE_TERNARY_COLON), this.t(':'), this.sep(Sep.AFTER_TERNARY_COLON),
this.p(node.alternate, Precedence.Assignment, alternate)), {
containsIn,
startsWithCurly,
startsWithLetSquareBracket,
startsWithFunctionOrClass,
});
}
reduceContinueStatement(node) {
return seq(this.t('continue'), node.label ? seq(this.sep(Sep.BEFORE_JUMP_LABEL), this.t(node.label)) : empty(), this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceDataProperty(node, { name, expression }) {
return seq(name, this.sep(Sep.BEFORE_PROP), this.t(':'), this.sep(Sep.AFTER_PROP), this.getAssignmentExpr(expression));
}
reduceDebuggerStatement(node) {
return seq(this.t('debugger'), this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceDoWhileStatement(node, { body, test }) {
return seq(this.t('do'), this.sep(Sep.AFTER_DO), body, this.sep(Sep.BEFORE_DOWHILE_WHILE), this.t('while'), this.sep(Sep.AFTER_DOWHILE_WHILE), this.paren(test, Sep.DO_WHILE_TEST_PAREN_BEFORE, Sep.DO_WHILE_TEST_PAREN_AFTER), this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceEmptyStatement(node) {
return seq(this.t(';'), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceExpressionStatement(node, { expression }) {
let needsParens =
expression.startsWithCurly ||
expression.startsWithLetSquareBracket ||
expression.startsWithFunctionOrClass;
return seq(needsParens ? this.paren(expression, Sep.EXPRESSION_STATEMENT_PAREN_BEFORE, Sep.EXPRESSION_STATEMENT_PAREN_AFTER) : expression, this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceForInStatement(node, { left, right, body }) {
left = node.left.type === 'VariableDeclaration' ? noIn(markContainsIn(left)) : left;
return objectAssign(
seq(this.t('for'), this.sep(Sep.AFTER_FORIN_FOR), this.paren(seq(left.startsWithLet ? this.paren(left, Sep.FOR_IN_LET_PAREN_BEFORE, Sep.FOR_IN_LET_PAREN_AFTER) : left, this.sep(Sep.BEFORE_FORIN_IN), this.t('in'), this.sep(Sep.AFTER_FORIN_FOR), right), Sep.FOR_IN_PAREN_BEFORE, Sep.FOR_IN_PAREN_AFTER), this.sep(Sep.BEFORE_FORIN_BODY), body, this.sep(Sep.AFTER_STATEMENT(node))),
{ endsWithMissingElse: body.endsWithMissingElse });
}
reduceForOfStatement(node, { left, right, body }) {
left = node.left.type === 'VariableDeclaration' ? noIn(markContainsIn(left)) : left;
return objectAssign(
seq(this.t('for'), this.sep(Sep.AFTER_FOROF_FOR), this.paren(seq(left.startsWithLet ? this.paren(left, Sep.FOR_OF_LET_PAREN_BEFORE, Sep.FOR_OF_LET_PAREN_AFTER) : left, this.sep(Sep.BEFORE_FOROF_OF), this.t('of'), this.sep(Sep.AFTER_FOROF_FOR), this.p(node.right, Precedence.Assignment, right)), Sep.FOR_OF_PAREN_BEFORE, Sep.FOR_OF_PAREN_AFTER), this.sep(Sep.BEFORE_FOROF_BODY), body, this.sep(Sep.AFTER_STATEMENT(node))),
{ endsWithMissingElse: body.endsWithMissingElse });
}
reduceForStatement(node, { init, test, update, body }) {
if (init) {
if (init.startsWithLetSquareBracket) {
init = this.paren(init, Sep.FOR_LET_PAREN_BEFORE, Sep.FOR_LET_PAREN_AFTER);
}
init = noIn(markContainsIn(init));
}
return objectAssign(
seq(
this.t('for'), this.sep(Sep.AFTER_FOR_FOR),
this.paren(seq(init ? seq(this.sep(Sep.BEFORE_FOR_INIT), init, this.sep(Sep.AFTER_FOR_INIT)) : this.sep(Sep.EMPTY_FOR_INIT), this.t(';'), test ? seq(this.sep(Sep.BEFORE_FOR_TEST), test, this.sep(Sep.AFTER_FOR_TEST)) : this.sep(Sep.EMPTY_FOR_TEST), this.t(';'), update ? seq(this.sep(Sep.BEFORE_FOR_UPDATE), update, this.sep(Sep.AFTER_FOR_UPDATE)) : this.sep(Sep.EMPTY_FOR_UPDATE))),
this.sep(Sep.BEFORE_FOR_BODY), body, this.sep(Sep.AFTER_STATEMENT(node))),
{
endsWithMissingElse: body.endsWithMissingElse,
});
}
reduceForAwaitStatement(node, { left, right, body }) {
left = node.left.type === 'VariableDeclaration' ? noIn(markContainsIn(left)) : left;
return objectAssign(
seq(this.t('for'), this.sep(Sep.AFTER_FOROF_FOR), this.t('await'), this.sep(Sep.AFTER_FORAWAIT_AWAIT), this.paren(seq(left.startsWithLet ? this.paren(left, Sep.FOR_OF_LET_PAREN_BEFORE, Sep.FOR_OF_LET_PAREN_AFTER) : left, this.sep(Sep.BEFORE_FOROF_OF), this.t('of'), this.sep(Sep.AFTER_FOROF_FOR), this.p(node.right, Precedence.Assignment, right)), Sep.FOR_OF_PAREN_BEFORE, Sep.FOR_OF_PAREN_AFTER), this.sep(Sep.BEFORE_FOROF_BODY), body, this.sep(Sep.AFTER_STATEMENT(node))),
{ endsWithMissingElse: body.endsWithMissingElse });
}
reduceFunctionBody(node, { directives, statements }) {
if (statements.length) {
statements[0] = this.parenToAvoidBeingDirective(node.statements[0], statements[0]);
}
return seq(...directives, directives.length ? this.sep(Sep.AFTER_FUNCTION_DIRECTIVES) : empty(), ...statements);
}
reduceFunctionDeclaration(node, { name, params, body }) {
return seq(node.isAsync ? this.t('async') : empty(), this.t('function'), node.isGenerator ? seq(this.sep(Sep.BEFORE_GENERATOR_STAR), this.t('*'), this.sep(Sep.AFTER_GENERATOR_STAR)) : empty(), this.sep(Sep.BEFORE_FUNCTION_NAME(node)), node.name.name === '*default*' ? empty() : name, this.sep(Sep.BEFORE_FUNCTION_PARAMS), this.paren(params, Sep.PARAMETERS_PAREN_BEFORE, Sep.PARAMETERS_PAREN_AFTER, Sep.PARAMETERS_PAREN_EMPTY), this.sep(Sep.BEFORE_FUNCTION_DECLARATION_BODY), this.brace(body, node, Sep.FUNCTION_BRACE_INITIAL, Sep.FUNCTION_BRACE_FINAL, Sep.FUNCTION_EMPTY), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceFunctionExpression(node, { name, params, body }) {
let state = seq(node.isAsync ? this.t('async') : empty(), this.t('function'), node.isGenerator ? seq(this.sep(Sep.BEFORE_GENERATOR_STAR), this.t('*'), this.sep(Sep.AFTER_GENERATOR_STAR)) : empty(), this.sep(Sep.BEFORE_FUNCTION_NAME(node)), name ? name : empty(), this.sep(Sep.BEFORE_FUNCTION_PARAMS), this.paren(params, Sep.PARAMETERS_PAREN_BEFORE, Sep.PARAMETERS_PAREN_AFTER, Sep.PARAMETERS_PAREN_EMPTY), this.sep(Sep.BEFORE_FUNCTION_EXPRESSION_BODY), this.brace(body, node, Sep.FUNCTION_EXPRESSION_BRACE_INITIAL, Sep.FUNCTION_EXPRESSION_BRACE_FINAL, Sep.FUNCTION_EXPRESSION_EMPTY));
state.startsWithFunctionOrClass = true;
return state;
}
reduceFormalParameters(node, { items, rest }) {
return this.commaSep(items.concat(rest == null ? [] : [seq(this.t('...'), this.sep(Sep.REST), rest)]), Sep.PARAMETER_BEFORE_COMMA, Sep.PARAMETER_AFTER_COMMA);
}
reduceArrowExpression(node, { params, body }) {
if (node.params.rest != null || node.params.items.length !== 1 || node.params.items[0].type !== 'BindingIdentifier') {
params = this.paren(params, Sep.ARROW_PARAMETERS_PAREN_BEFORE, Sep.ARROW_PARAMETERS_PAREN_AFTER, Sep.ARROW_PARAMETERS_PAREN_EMPTY);
}
let containsIn = false;
if (node.body.type === 'FunctionBody') {
body = this.brace(body, node, Sep.ARROW_BRACE_INITIAL, Sep.ARROW_BRACE_FINAL, Sep.ARROW_BRACE_EMPTY);
} else if (body.startsWithCurly) {
body = this.paren(body, Sep.ARROW_BODY_PAREN_BEFORE, Sep.ARROW_BODY_PAREN_AFTER);
} else if (body.containsIn) {
containsIn = true;
}
return objectAssign(seq(node.isAsync ? seq(this.t('async'), this.sep(Sep.BEFORE_ARROW_ASYNC_PARAMS)) : empty(), params, this.sep(Sep.BEFORE_ARROW), this.t('=>'), this.sep(Sep.AFTER_ARROW), this.p(node.body, Precedence.Assignment, body)), { containsIn });
}
reduceGetter(node, { name, body }) {
return seq(this.t('get'), this.sep(Sep.AFTER_GET), name, this.sep(Sep.BEFORE_GET_PARAMS), this.paren(empty(), null, null, Sep.GETTER_PARAMS), this.sep(Sep.BEFORE_GET_BODY), this.brace(body, node, Sep.GET_BRACE_INTIAL, Sep.GET_BRACE_FINAL, Sep.GET_BRACE_EMPTY));
}
reduceIdentifierExpression(node) {
let a = this.t(node.name);
if (node.name === 'let') {
a.startsWithLet = true;
}
return a;
}
reduceIfStatement(node, { test, consequent, alternate }) {
if (alternate && consequent.endsWithMissingElse) {
consequent = this.brace(consequent, node, Sep.MISSING_ELSE_INTIIAL, Sep.MISSING_ELSE_FINAL, Sep.MISSING_ELSE_EMPTY);
}
return objectAssign(
seq(this.t('if'), this.sep(Sep.AFTER_IF),
this.paren(test, Sep.IF_PAREN_BEFORE, Sep.IF_PAREN_AFTER), this.sep(Sep.AFTER_IF_TEST),
consequent,
alternate ? seq(this.sep(Sep.BEFORE_ELSE), this.t('else'), this.sep(Sep.AFTER_ELSE), alternate) : empty(), this.sep(Sep.AFTER_STATEMENT(node))),
{ endsWithMissingElse: alternate ? alternate.endsWithMissingElse : true });
}
reduceImport(node, { defaultBinding, namedImports }) {
let bindings = [];
if (defaultBinding != null) {
bindings.push(defaultBinding);
}
if (namedImports.length > 0) {
bindings.push(this.brace(this.commaSep(namedImports, Sep.NAMED_IMPORT_BEFORE_COMMA, Sep.NAMED_IMPORT_AFTER_COMMA), node, Sep.IMPORT_BRACE_INTIAL, Sep.IMPORT_BRACE_FINAL, Sep.IMPORT_BRACE_EMPTY));
}
if (bindings.length === 0) {
return seq(this.t('import'), this.sep(Sep.BEFORE_IMPORT_MODULE), this.t(escapeStringLiteral(node.moduleSpecifier)), this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
return seq(this.t('import'), this.sep(Sep.BEFORE_IMPORT_BINDINGS), this.commaSep(bindings, Sep.IMPORT_BEFORE_COMMA, Sep.IMPORT_AFTER_COMMA), this.sep(Sep.AFTER_IMPORT_BINDINGS), this.t('from'), this.sep(Sep.AFTER_FROM), this.t(escapeStringLiteral(node.moduleSpecifier)), this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceImportNamespace(node, { defaultBinding, namespaceBinding }) {
return seq(
this.t('import'), this.sep(Sep.BEFORE_IMPORT_NAMESPACE),
defaultBinding == null ? empty() : seq(defaultBinding, this.sep(Sep.IMPORT_BEFORE_COMMA), this.t(','), this.sep(Sep.IMPORT_AFTER_COMMA)),
this.sep(Sep.BEFORE_IMPORT_STAR), this.t('*'), this.sep(Sep.AFTER_IMPORT_STAR),
this.t('as'), this.sep(Sep.AFTER_IMPORT_AS),
namespaceBinding, this.sep(Sep.AFTER_NAMESPACE_BINDING),
this.t('from'), this.sep(Sep.AFTER_FROM),
this.t(escapeStringLiteral(node.moduleSpecifier)),
this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node))
);
}
reduceImportSpecifier(node, { binding }) {
if (node.name == null) return binding;
return seq(this.t(node.name), this.sep(Sep.BEFORE_IMPORT_AS), this.t('as'), this.sep(Sep.AFTER_IMPORT_AS), binding);
}
reduceExportAllFrom(node) {
return seq(this.t('export'), this.sep(Sep.BEFORE_EXPORT_STAR), this.t('*'), this.sep(Sep.AFTER_EXPORT_STAR), this.t('from'), this.sep(Sep.AFTER_FROM), this.t(escapeStringLiteral(node.moduleSpecifier)), this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceExportFrom(node, { namedExports }) {
return seq(this.t('export'), this.sep(Sep.BEFORE_EXPORT_BINDINGS), this.brace(this.commaSep(namedExports, Sep.EXPORTS_BEFORE_COMMA, Sep.EXPORTS_AFTER_COMMA), node, Sep.EXPORT_BRACE_INITIAL, Sep.EXPORT_BRACE_FINAL, Sep.EXPORT_BRACE_EMPTY), this.sep(Sep.AFTER_EXPORT_FROM_BINDINGS), this.t('from'), this.sep(Sep.AFTER_FROM), this.t(escapeStringLiteral(node.moduleSpecifier)), this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceExportLocals(node, { namedExports }) {
return seq(this.t('export'), this.sep(Sep.BEFORE_EXPORT_BINDINGS), this.brace(this.commaSep(namedExports, Sep.EXPORTS_BEFORE_COMMA, Sep.EXPORTS_AFTER_COMMA), node, Sep.EXPORT_BRACE_INITIAL, Sep.EXPORT_BRACE_FINAL, Sep.EXPORT_BRACE_EMPTY), this.sep(Sep.AFTER_EXPORT_LOCAL_BINDINGS), this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceExport(node, { declaration }) {
switch (node.declaration.type) {
case 'FunctionDeclaration':
case 'ClassDeclaration':
break;
default:
declaration = seq(declaration, this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
return seq(this.t('export'), this.sep(Sep.AFTER_EXPORT), declaration);
}
reduceExportDefault(node, { body }) {
body = body.startsWithFunctionOrClass ? this.paren(body, Sep.EXPORT_PAREN_BEFORE, Sep.EXPORT_PAREN_AFTER) : body;
switch (node.body.type) {
case 'FunctionDeclaration':
case 'ClassDeclaration':
return seq(this.t('export'), this.sep(Sep.EXPORT_DEFAULT), this.t('default'), this.sep(Sep.AFTER_EXPORT_DEFAULT), body);
default:
return seq(this.t('export'), this.sep(Sep.EXPORT_DEFAULT), this.t('default'), this.sep(Sep.AFTER_EXPORT_DEFAULT), this.p(node.body, Precedence.Assignment, body), this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
}
reduceExportFromSpecifier(node) {
if (node.exportedName == null) return this.t(node.name);
return seq(this.t(node.name), this.sep(Sep.BEFORE_EXPORT_AS), this.t('as'), this.sep(Sep.AFTER_EXPORT_AS), this.t(node.exportedName));
}
reduceExportLocalSpecifier(node, { name }) {
if (node.exportedName == null) return name;
return seq(name, this.sep(Sep.BEFORE_EXPORT_AS), this.t('as'), this.sep(Sep.AFTER_EXPORT_AS), this.t(node.exportedName));
}
reduceLabeledStatement(node, { body }) {
return objectAssign(seq(this.t(node.label), this.sep(Sep.BEFORE_LABEL_COLON), this.t(':'), this.sep(Sep.AFTER_LABEL_COLON), body), { endsWithMissingElse: body.endsWithMissingElse });
}
reduceLiteralBooleanExpression(node) {
return this.t(node.value.toString());
}
reduceLiteralNullExpression(/* node */) {
return this.t('null');
}
reduceLiteralInfinityExpression(/* node */) {
return this.t('2e308');
}
reduceLiteralNumericExpression(node) {
return new NumberCodeRep(node.value);
}
reduceLiteralRegExpExpression(node) {
return this.t(`/${node.pattern}/${node.global ? 'g' : ''}${node.ignoreCase ? 'i' : ''}${node.multiLine ? 'm' : ''}${node.dotAll ? 's' : ''}${node.unicode ? 'u' : ''}${node.sticky ? 'y' : ''}`, true);
}
reduceLiteralStringExpression(node) {
return this.t(escapeStringLiteral(node.value));
}
reduceMethod(node, { name, params, body }) {
return seq(node.isAsync ? seq(this.t('async'), this.sep(Sep.AFTER_METHOD_ASYNC)) : empty(), node.isGenerator ? seq(this.t('*'), this.sep(Sep.AFTER_METHOD_GENERATOR_STAR)) : empty(), name, this.sep(Sep.AFTER_METHOD_NAME), this.paren(params, Sep.PARAMETERS_PAREN_BEFORE, Sep.PARAMETERS_PAREN_AFTER, Sep.PARAMETERS_PAREN_EMPTY), this.sep(Sep.BEFORE_METHOD_BODY), this.brace(body, node, Sep.METHOD_BRACE_INTIAL, Sep.METHOD_BRACE_FINAL, Sep.METHOD_BRACE_EMPTY));
}
reduceModule(node, { directives, items }) {
if (items.length) {
items[0] = this.parenToAvoidBeingDirective(node.items[0], items[0]);
}
return seq(...directives, directives.length ? this.sep(Sep.AFTER_MODULE_DIRECTIVES) : empty(), ...items);
}
reduceNewExpression(node, { callee, arguments: args }) {
const parenthizedArgs = args.map((a, i) => this.p(node.arguments[i], Precedence.Assignment, a));
let calleeRep = getPrecedence(node.callee) === Precedence.Call ? this.paren(callee, Sep.NEW_CALLEE_PAREN_BEFORE, Sep.NEW_CALLEE_PAREN_AFTER) :
this.p(node.callee, getPrecedence(node), callee);
return seq(this.t('new'), this.sep(Sep.AFTER_NEW), calleeRep, args.length === 0 ? this.sep(Sep.EMPTY_NEW_CALL) : seq(this.sep(Sep.BEFORE_NEW_ARGS), this.paren(this.commaSep(parenthizedArgs, Sep.ARGS_BEFORE_COMMA, Sep.ARGS_AFTER_COMMA), Sep.NEW_PAREN_BEFORE, Sep.NEW_PAREN_AFTER, Sep.NEW_PAREN_EMPTY)));
}
reduceNewTargetExpression() {
return seq(this.t('new'), this.sep(Sep.NEW_TARGET_BEFORE_DOT), this.t('.'), this.sep(Sep.NEW_TARGET_AFTER_DOT), this.t('target'));
}
reduceObjectExpression(node, { properties }) {
let state = this.brace(this.commaSep(properties, Sep.OBJECT_BEFORE_COMMA, Sep.OBJECT_AFTER_COMMA), node, Sep.OBJECT_BRACE_INITIAL, Sep.OBJECT_BRACE_FINAL, Sep.OBJECT_EMPTY);
state.startsWithCurly = true;
return state;
}
reduceUpdateExpression(node, { operand }) {
if (node.isPrefix) {
return this.reduceUnaryExpression(...arguments);
}
return objectAssign(
seq(this.p(node.operand, Precedence.New, operand), this.sep(Sep.BEFORE_POSTFIX(node.operator)), this.t(node.operator)),
{
startsWithCurly: operand.startsWithCurly,
startsWithLetSquareBracket: operand.startsWithLetSquareBracket,
startsWithFunctionOrClass: operand.startsWithFunctionOrClass,
}
);
}
reduceUnaryExpression(node, { operand }) {
return seq(this.t(node.operator), this.sep(Sep.UNARY(node.operator)), this.p(node.operand, getPrecedence(node), operand));
}
reduceReturnStatement(node, { expression }) {
return seq(this.t('return'), expression ? seq(this.sep(Sep.RETURN), expression) : empty(), this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceScript(node, { directives, statements }) {
if (statements.length) {
statements[0] = this.parenToAvoidBeingDirective(node.statements[0], statements[0]);
}
return seq(...directives, directives.length ? this.sep(Sep.AFTER_SCRIPT_DIRECTIVES) : empty(), ...statements);
}
reduceSetter(node, { name, param, body }) {
return seq(this.t('set'), this.sep(Sep.AFTER_SET), name, this.sep(Sep.BEFORE_SET_PARAMS), this.paren(param, Sep.SETTER_PARAM_BEFORE, Sep.SETTER_PARAM_AFTER), this.sep(Sep.BEFORE_SET_BODY), this.brace(body, node, Sep.SET_BRACE_INTIIAL, Sep.SET_BRACE_FINAL, Sep.SET_BRACE_EMPTY));
}
reduceShorthandProperty(node, { name }) {
return name;
}
reduceStaticMemberAssignmentTarget(node, { object }) {
const state = seq(this.p(node.object, getPrecedence(node), object), this.sep(Sep.BEFORE_STATIC_MEMBER_ASSIGNMENT_TARGET_DOT), this.t('.'), this.sep(Sep.AFTER_STATIC_MEMBER_ASSIGNMENT_TARGET_DOT), this.t(node.property));
state.startsWithLet = object.startsWithLet;
state.startsWithCurly = object.startsWithCurly;
state.startsWithLetSquareBracket = object.startsWithLetSquareBracket;
state.startsWithFunctionOrClass = object.startsWithFunctionOrClass;
return state;
}
reduceStaticMemberExpression(node, { object }) {
const state = seq(this.p(node.object, getPrecedence(node), object), this.sep(Sep.BEFORE_STATIC_MEMBER_DOT), this.t('.'), this.sep(Sep.AFTER_STATIC_MEMBER_DOT), this.t(node.property));
state.startsWithLet = object.startsWithLet;
state.startsWithCurly = object.startsWithCurly;
state.startsWithLetSquareBracket = object.startsWithLetSquareBracket;
state.startsWithFunctionOrClass = object.startsWithFunctionOrClass;
return state;
}
reduceStaticPropertyName(node) {
if (keyword.isIdentifierNameES6(node.value)) {
return this.t(node.value);
}
let n = parseFloat(node.value);
if (n >= 0 && n.toString() === node.value) {
return new NumberCodeRep(n);
}
return this.t(escapeStringLiteral(node.value));
}
reduceSuper() {
return this.t('super');
}
reduceSwitchCase(node, { test, consequent }) {
return seq(this.t('case'), this.sep(Sep.BEFORE_CASE_TEST), test, this.sep(Sep.AFTER_CASE_TEST), this.t(':'), this.sep(Sep.BEFORE_CASE_BODY), seq(...consequent), this.sep(Sep.AFTER_CASE_BODY));
}
reduceSwitchDefault(node, { consequent }) {
return seq(this.t('default'), this.sep(Sep.DEFAULT), this.t(':'), this.sep(Sep.BEFORE_CASE_BODY), seq(...consequent), this.sep(Sep.AFTER_DEFAULT_BODY));
}
reduceSwitchStatement(node, { discriminant, cases }) {
return seq(this.t('switch'), this.sep(Sep.BEFORE_SWITCH_DISCRIM), this.paren(discriminant, Sep.SWITCH_DISCRIM_PAREN_BEFORE, Sep.SWITCH_DISCRIM_PAREN_AFTER), this.sep(Sep.BEFORE_SWITCH_BODY), this.brace(seq(...cases), node, Sep.SWITCH_BRACE_INTIAL, Sep.SWITCH_BRACE_FINAL, Sep.SWITCH_BRACE_EMPTY), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceSwitchStatementWithDefault(node, { discriminant, preDefaultCases, defaultCase, postDefaultCases }) {
return seq(
this.t('switch'),
this.sep(Sep.BEFORE_SWITCH_DISCRIM), this.paren(discriminant, Sep.SWITCH_DISCRIM_PAREN_BEFORE, Sep.SWITCH_DISCRIM_PAREN_AFTER), this.sep(Sep.BEFORE_SWITCH_BODY),
this.brace(seq(...preDefaultCases, defaultCase, ...postDefaultCases), node, Sep.SWITCH_BRACE_INTIAL, Sep.SWITCH_BRACE_FINAL, Sep.SWITCH_BRACE_EMPTY), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceTemplateExpression(node, { tag, elements }) {
let state = node.tag == null ? empty() : seq(this.p(node.tag, getPrecedence(node), tag), this.sep(Sep.TEMPLATE_TAG));
state = seq(state, this.t('`'));
for (let i = 0, l = node.elements.length; i < l; ++i) {
if (node.elements[i].type === 'TemplateElement') {
let d = '';
if (i > 0) d += '}';
d += node.elements[i].rawValue;
if (i < l - 1) d += '${';
state = seq(state, this.t(d));
} else {
state = seq(state, this.sep(Sep.BEFORE_TEMPLATE_EXPRESSION), elements[i], this.sep(Sep.AFTER_TEMPLATE_EXPRESSION));
}
}
state = seq(state, this.t('`'));
if (node.tag != null) {
state.startsWithCurly = tag.startsWithCurly;
state.startsWithLet = tag.startsWithLet;
state.startsWithLetSquareBracket = tag.startsWithLetSquareBracket;
state.startsWithFunctionOrClass = tag.startsWithFunctionOrClass;
}
return state;
}
reduceTemplateElement(node) {
return this.t(node.rawValue);
}
reduceThisExpression(/* node */) {
return this.t('this');
}
reduceThrowStatement(node, { expression }) {
return seq(this.t('throw'), this.sep(Sep.THROW), expression, this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceTryCatchStatement(node, { body, catchClause }) {
return seq(this.t('try'), this.sep(Sep.AFTER_TRY), body, this.sep(Sep.BEFORE_CATCH), catchClause, this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceTryFinallyStatement(node, { body, catchClause, finalizer }) {
return seq(this.t('try'), this.sep(Sep.AFTER_TRY), body, catchClause ? seq(this.sep(Sep.BEFORE_CATCH), catchClause) : empty(), this.sep(Sep.BEFORE_FINALLY), this.t('finally'), this.sep(Sep.AFTER_FINALLY), finalizer, this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceYieldExpression(node, { expression }) {
if (node.expression == null) return this.t('yield');
return objectAssign(seq(this.t('yield'), this.sep(Sep.YIELD), this.p(node.expression, getPrecedence(node), expression)), { containsIn: expression.containsIn });
}
reduceYieldGeneratorExpression(node, { expression }) {
return objectAssign(seq(this.t('yield'), this.sep(Sep.BEFORE_YIELD_STAR), this.t('*'), this.sep(Sep.AFTER_YIELD_STAR), this.p(node.expression, getPrecedence(node), expression)), { containsIn: expression.containsIn });
}
reduceDirective(node) {
let delim = node.rawValue.match(/(^|[^\\])(\\\\)*"/) ? '\'' : '"';
return seq(this.t(delim + node.rawValue + delim), this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceVariableDeclaration(node, { declarators }) {
return seq(this.t(node.kind), this.sep(Sep.VARIABLE_DECLARATION), this.commaSep(declarators, Sep.DECLARATORS_BEFORE_COMMA, Sep.DECLARATORS_AFTER_COMMA));
}
reduceVariableDeclarationStatement(node, { declaration }) {
return seq(declaration, this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceVariableDeclarator(node, { binding, init }) {
let containsIn = init && init.containsIn && !init.containsGroup;
if (init) {
if (init.containsGroup) {
init = this.paren(init, Sep.EXPRESSION_PAREN_BEFORE, Sep.EXPRESSION_PAREN_AFTER);
} else {
init = markContainsIn(init);
}
}
return objectAssign(init == null ? binding : seq(binding, this.sep(Sep.BEFORE_INIT_EQUALS), this.t('='), this.sep(Sep.AFTER_INIT_EQUALS), init), { containsIn });
}
reduceWhileStatement(node, { test, body }) {
return objectAssign(seq(this.t('while'), this.sep(Sep.AFTER_WHILE), this.paren(test, Sep.WHILE_TEST_PAREN_BEFORE, Sep.WHILE_TEST_PAREN_AFTER), this.sep(Sep.BEFORE_WHILE_BODY), body, this.sep(Sep.AFTER_STATEMENT(node))), { endsWithMissingElse: body.endsWithMissingElse });
}
reduceWithStatement(node, { object, body }) {
return objectAssign(
seq(this.t('with'), this.sep(Sep.AFTER_WITH), this.paren(object, Sep.WITH_PAREN_BEFORE, Sep.WITH_PAREN_AFTER), this.sep(Sep.BEFORE_WITH_BODY), body, this.sep(Sep.AFTER_STATEMENT(node))),
{ endsWithMissingElse: body.endsWithMissingElse });
}
}
function withoutTrailingLinebreak(state) {
if (state && state instanceof Seq) {
let lastChild = state.children[state.children.length - 1];
/* istanbul ignore next */
while (lastChild instanceof Empty) {
state.children.pop();
lastChild = state.children[state.children.length - 1];
}
/* istanbul ignore else */
if (lastChild instanceof Seq) {
withoutTrailingLinebreak(lastChild);
} else if (lastChild instanceof Linebreak) {
state.children.pop();
}
}
return state;
}
function indent(rep, includingFinal) {
let finalLinebreak;
function indentNode(node) {
if (node instanceof Linebreak) {
finalLinebreak = node;
++node.indentation;
}
}
rep.forEach(indentNode);
if (!includingFinal) {
--finalLinebreak.indentation;
}
return rep;
}
class FormattedCodeGen extends ExtensibleCodeGen {
parenToAvoidBeingDirective(element, original) {
if (element && element.type === 'ExpressionStatement' && element.expression.type === 'LiteralStringExpression') {
return seq(this.paren(original.children[0], Sep.PAREN_AVOIDING_DIRECTIVE_BEFORE, Sep.PAREN_AVOIDING_DIRECTIVE_AFTER), this.semiOp(), this.sep(Sep.AFTER_STATEMENT(element)));
}
return original;
}
brace(rep, node) {
if (isEmpty(rep)) {
return this.t('{}');
}
switch (node.type) {
case 'ObjectAssignmentTarget':
case 'ObjectBinding':
case 'Import':
case 'ExportFrom':
case 'ExportLocals':
case 'ObjectExpression':
return new Brace(rep);
}
rep = seq(new Linebreak, rep);
indent(rep, false);
return new Brace(rep);
}
reduceDoWhileStatement(node, { body, test }) {
return seq(this.t('do'), this.sep(Sep.AFTER_DO), withoutTrailingLinebreak(body), this.sep(Sep.BEFORE_DOWHILE_WHILE), this.t('while'), this.sep(Sep.AFTER_DOWHILE_WHILE), this.paren(test, Sep.DO_WHILE_TEST_PAREN_BEFORE, Sep.DO_WHILE_TEST_PAREN_AFTER), this.semiOp(), this.sep(Sep.AFTER_STATEMENT(node)));
}
reduceIfStatement(node, { test, consequent, alternate }) {
if (alternate && consequent.endsWithMissingElse) {
consequent = this.brace(consequent, node);
}
return objectAssign(
seq(this.t('if'), this.sep(Sep.AFTER_IF),
this.paren(test, Sep.IF_PAREN_B