in benchmark/old.js [1151:1897]
function generateExpression(expr, option) {
var result,
precedence,
type,
currentPrecedence,
i,
len,
fragment,
multiline,
leftCharCode,
leftSource,
rightCharCode,
allowIn,
allowCall,
allowUnparenthesizedNew,
property,
isGenerator;
precedence = option.precedence;
allowIn = option.allowIn;
allowCall = option.allowCall;
type = expr.type || option.type;
if (extra.verbatim && expr.hasOwnProperty(extra.verbatim)) {
return generateVerbatim(expr, option);
}
switch (type) {
case Syntax.SequenceExpression:
result = [];
allowIn |= (Precedence.Sequence < precedence);
for (i = 0, len = expr.expressions.length; i < len; ++i) {
result.push(generateExpression(expr.expressions[i], {
precedence: Precedence.Assignment,
allowIn: allowIn,
allowCall: true
}));
if (i + 1 < len) {
result.push(',' + space);
}
}
result = parenthesize(result, Precedence.Sequence, precedence);
break;
case Syntax.AssignmentExpression:
result = generateAssignment(expr.left, expr.right, expr.operator, option);
break;
case Syntax.ArrowFunctionExpression:
allowIn |= (Precedence.ArrowFunction < precedence);
result = parenthesize(generateFunctionBody(expr), Precedence.ArrowFunction, precedence);
break;
case Syntax.ConditionalExpression:
allowIn |= (Precedence.Conditional < precedence);
result = parenthesize(
[
generateExpression(expr.test, {
precedence: Precedence.LogicalOR,
allowIn: allowIn,
allowCall: true
}),
space + '?' + space,
generateExpression(expr.consequent, {
precedence: Precedence.Assignment,
allowIn: allowIn,
allowCall: true
}),
space + ':' + space,
generateExpression(expr.alternate, {
precedence: Precedence.Assignment,
allowIn: allowIn,
allowCall: true
})
],
Precedence.Conditional,
precedence
);
break;
case Syntax.LogicalExpression:
case Syntax.BinaryExpression:
currentPrecedence = BinaryPrecedence[expr.operator];
allowIn |= (currentPrecedence < precedence);
fragment = generateExpression(expr.left, {
precedence: currentPrecedence,
allowIn: allowIn,
allowCall: true
});
leftSource = fragment.toString();
if (leftSource.charCodeAt(leftSource.length - 1) === 0x2F /* / */ && esutils.code.isIdentifierPart(expr.operator.charCodeAt(0))) {
result = [fragment, noEmptySpace(), expr.operator];
} else {
result = join(fragment, expr.operator);
}
fragment = generateExpression(expr.right, {
precedence: currentPrecedence + 1,
allowIn: allowIn,
allowCall: true
});
if (expr.operator === '/' && fragment.toString().charAt(0) === '/' ||
expr.operator.slice(-1) === '<' && fragment.toString().slice(0, 3) === '!--') {
// If '/' concats with '/' or `<` concats with `!--`, it is interpreted as comment start
result.push(noEmptySpace());
result.push(fragment);
} else {
result = join(result, fragment);
}
if (expr.operator === 'in' && !allowIn) {
result = ['(', result, ')'];
} else {
result = parenthesize(result, currentPrecedence, precedence);
}
break;
case Syntax.CallExpression:
result = [generateExpression(expr.callee, {
precedence: Precedence.Call,
allowIn: true,
allowCall: true,
allowUnparenthesizedNew: false
})];
result.push('(');
for (i = 0, len = expr['arguments'].length; i < len; ++i) {
result.push(generateExpression(expr['arguments'][i], {
precedence: Precedence.Assignment,
allowIn: true,
allowCall: true
}));
if (i + 1 < len) {
result.push(',' + space);
}
}
result.push(')');
if (!allowCall) {
result = ['(', result, ')'];
} else {
result = parenthesize(result, Precedence.Call, precedence);
}
break;
case Syntax.NewExpression:
len = expr['arguments'].length;
allowUnparenthesizedNew = option.allowUnparenthesizedNew === undefined || option.allowUnparenthesizedNew;
result = join(
'new',
generateExpression(expr.callee, {
precedence: Precedence.New,
allowIn: true,
allowCall: false,
allowUnparenthesizedNew: allowUnparenthesizedNew && !parentheses && len === 0
})
);
if (!allowUnparenthesizedNew || parentheses || len > 0) {
result.push('(');
for (i = 0; i < len; ++i) {
result.push(generateExpression(expr['arguments'][i], {
precedence: Precedence.Assignment,
allowIn: true,
allowCall: true
}));
if (i + 1 < len) {
result.push(',' + space);
}
}
result.push(')');
}
result = parenthesize(result, Precedence.New, precedence);
break;
case Syntax.MemberExpression:
result = [generateExpression(expr.object, {
precedence: Precedence.Call,
allowIn: true,
allowCall: allowCall,
allowUnparenthesizedNew: false
})];
if (expr.computed) {
result.push('[');
result.push(generateExpression(expr.property, {
precedence: Precedence.Sequence,
allowIn: true,
allowCall: allowCall
}));
result.push(']');
} else {
if (expr.object.type === Syntax.Literal && typeof expr.object.value === 'number') {
fragment = toSourceNodeWhenNeeded(result).toString();
// When the following conditions are all true,
// 1. No floating point
// 2. Don't have exponents
// 3. The last character is a decimal digit
// 4. Not hexadecimal OR octal number literal
// we should add a floating point.
if (
fragment.indexOf('.') < 0 &&
!/[eExX]/.test(fragment) &&
esutils.code.isDecimalDigit(fragment.charCodeAt(fragment.length - 1)) &&
!(fragment.length >= 2 && fragment.charCodeAt(0) === 48) // '0'
) {
result.push('.');
}
}
result.push('.');
result.push(generateIdentifier(expr.property));
}
result = parenthesize(result, Precedence.Member, precedence);
break;
case Syntax.UnaryExpression:
fragment = generateExpression(expr.argument, {
precedence: Precedence.Unary,
allowIn: true,
allowCall: true
});
if (space === '') {
result = join(expr.operator, fragment);
} else {
result = [expr.operator];
if (expr.operator.length > 2) {
// delete, void, typeof
// get `typeof []`, not `typeof[]`
result = join(result, fragment);
} else {
// Prevent inserting spaces between operator and argument if it is unnecessary
// like, `!cond`
leftSource = toSourceNodeWhenNeeded(result).toString();
leftCharCode = leftSource.charCodeAt(leftSource.length - 1);
rightCharCode = fragment.toString().charCodeAt(0);
if (((leftCharCode === 0x2B /* + */ || leftCharCode === 0x2D /* - */) && leftCharCode === rightCharCode) ||
(esutils.code.isIdentifierPart(leftCharCode) && esutils.code.isIdentifierPart(rightCharCode))) {
result.push(noEmptySpace());
result.push(fragment);
} else {
result.push(fragment);
}
}
}
result = parenthesize(result, Precedence.Unary, precedence);
break;
case Syntax.YieldExpression:
if (expr.delegate) {
result = 'yield*';
} else {
result = 'yield';
}
if (expr.argument) {
result = join(
result,
generateExpression(expr.argument, {
precedence: Precedence.Yield,
allowIn: true,
allowCall: true
})
);
}
result = parenthesize(result, Precedence.Yield, precedence);
break;
case Syntax.UpdateExpression:
if (expr.prefix) {
result = parenthesize(
[
expr.operator,
generateExpression(expr.argument, {
precedence: Precedence.Unary,
allowIn: true,
allowCall: true
})
],
Precedence.Unary,
precedence
);
} else {
result = parenthesize(
[
generateExpression(expr.argument, {
precedence: Precedence.Postfix,
allowIn: true,
allowCall: true
}),
expr.operator
],
Precedence.Postfix,
precedence
);
}
break;
case Syntax.FunctionExpression:
isGenerator = expr.generator && !extra.moz.starlessGenerator;
result = isGenerator ? 'function*' : 'function';
if (expr.id) {
result = [result, (isGenerator) ? space : noEmptySpace(),
generateIdentifier(expr.id),
generateFunctionBody(expr)];
} else {
result = [result + space, generateFunctionBody(expr)];
}
break;
case Syntax.ExportBatchSpecifier:
result = '*';
break;
case Syntax.ArrayPattern:
case Syntax.ArrayExpression:
if (!expr.elements.length) {
result = '[]';
break;
}
multiline = expr.elements.length > 1;
result = ['[', multiline ? newline : ''];
withIndent(function (indent) {
for (i = 0, len = expr.elements.length; i < len; ++i) {
if (!expr.elements[i]) {
if (multiline) {
result.push(indent);
}
if (i + 1 === len) {
result.push(',');
}
} else {
result.push(multiline ? indent : '');
result.push(generateExpression(expr.elements[i], {
precedence: Precedence.Assignment,
allowIn: true,
allowCall: true
}));
}
if (i + 1 < len) {
result.push(',' + (multiline ? newline : space));
}
}
});
if (multiline && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
result.push(newline);
}
result.push(multiline ? base : '');
result.push(']');
break;
case Syntax.ClassExpression:
result = ['class'];
if (expr.id) {
result = join(result, generateExpression(expr.id, {
allowIn: true,
allowCall: true
}));
}
if (expr.superClass) {
fragment = join('extends', generateExpression(expr.superClass, {
precedence: Precedence.Assignment,
allowIn: true,
allowCall: true
}));
result = join(result, fragment);
}
result.push(space);
result.push(generateStatement(expr.body, {
semicolonOptional: true,
directiveContext: false
}));
break;
case Syntax.MethodDefinition:
if (expr['static']) {
result = ['static' + space];
} else {
result = [];
}
if (expr.kind === 'get' || expr.kind === 'set') {
result = join(result, [
join(expr.kind, generatePropertyKey(expr.key, expr.computed, {
precedence: Precedence.Sequence,
allowIn: true,
allowCall: true
})),
generateFunctionBody(expr.value)
]);
} else {
fragment = [
generatePropertyKey(expr.key, expr.computed, {
precedence: Precedence.Sequence,
allowIn: true,
allowCall: true
}),
generateFunctionBody(expr.value)
];
if (expr.value.generator) {
result.push('*');
result.push(fragment);
} else {
result = join(result, fragment);
}
}
break;
case Syntax.Property:
if (expr.kind === 'get' || expr.kind === 'set') {
result = [
expr.kind, noEmptySpace(),
generatePropertyKey(expr.key, expr.computed, {
precedence: Precedence.Sequence,
allowIn: true,
allowCall: true
}),
generateFunctionBody(expr.value)
];
} else {
if (expr.shorthand) {
result = generatePropertyKey(expr.key, expr.computed, {
precedence: Precedence.Sequence,
allowIn: true,
allowCall: true
});
} else if (expr.method) {
result = [];
if (expr.value.generator) {
result.push('*');
}
result.push(generatePropertyKey(expr.key, expr.computed, {
precedence: Precedence.Sequence,
allowIn: true,
allowCall: true
}));
result.push(generateFunctionBody(expr.value));
} else {
result = [
generatePropertyKey(expr.key, expr.computed, {
precedence: Precedence.Sequence,
allowIn: true,
allowCall: true
}),
':' + space,
generateExpression(expr.value, {
precedence: Precedence.Assignment,
allowIn: true,
allowCall: true
})
];
}
}
break;
case Syntax.ObjectExpression:
if (!expr.properties.length) {
result = '{}';
break;
}
multiline = expr.properties.length > 1;
withIndent(function () {
fragment = generateExpression(expr.properties[0], {
precedence: Precedence.Sequence,
allowIn: true,
allowCall: true,
type: Syntax.Property
});
});
if (!multiline) {
// issues 4
// Do not transform from
// dejavu.Class.declare({
// method2: function () {}
// });
// to
// dejavu.Class.declare({method2: function () {
// }});
if (!hasLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
result = [ '{', space, fragment, space, '}' ];
break;
}
}
withIndent(function (indent) {
result = [ '{', newline, indent, fragment ];
if (multiline) {
result.push(',' + newline);
for (i = 1, len = expr.properties.length; i < len; ++i) {
result.push(indent);
result.push(generateExpression(expr.properties[i], {
precedence: Precedence.Sequence,
allowIn: true,
allowCall: true,
type: Syntax.Property
}));
if (i + 1 < len) {
result.push(',' + newline);
}
}
}
});
if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
result.push(newline);
}
result.push(base);
result.push('}');
break;
case Syntax.ObjectPattern:
if (!expr.properties.length) {
result = '{}';
break;
}
multiline = false;
if (expr.properties.length === 1) {
property = expr.properties[0];
if (property.value.type !== Syntax.Identifier) {
multiline = true;
}
} else {
for (i = 0, len = expr.properties.length; i < len; ++i) {
property = expr.properties[i];
if (!property.shorthand) {
multiline = true;
break;
}
}
}
result = ['{', multiline ? newline : '' ];
withIndent(function (indent) {
for (i = 0, len = expr.properties.length; i < len; ++i) {
result.push(multiline ? indent : '');
result.push(generateExpression(expr.properties[i], {
precedence: Precedence.Sequence,
allowIn: true,
allowCall: true
}));
if (i + 1 < len) {
result.push(',' + (multiline ? newline : space));
}
}
});
if (multiline && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
result.push(newline);
}
result.push(multiline ? base : '');
result.push('}');
break;
case Syntax.ThisExpression:
result = 'this';
break;
case Syntax.Identifier:
result = generateIdentifier(expr);
break;
case Syntax.ImportDefaultSpecifier:
result = generateIdentifier(expr.id);
break;
case Syntax.ImportNamespaceSpecifier:
result = ['*'];
if (expr.id) {
result.push(space + 'as' + noEmptySpace() + generateIdentifier(expr.id));
}
break;
case Syntax.ImportSpecifier:
case Syntax.ExportSpecifier:
result = [ expr.id.name ];
if (expr.name) {
result.push(noEmptySpace() + 'as' + noEmptySpace() + generateIdentifier(expr.name));
}
break;
case Syntax.Literal:
result = generateLiteral(expr);
break;
case Syntax.GeneratorExpression:
case Syntax.ComprehensionExpression:
// GeneratorExpression should be parenthesized with (...), ComprehensionExpression with [...]
// Due to https://bugzilla.mozilla.org/show_bug.cgi?id=883468 position of expr.body can differ in Spidermonkey and ES6
result = (type === Syntax.GeneratorExpression) ? ['('] : ['['];
if (extra.moz.comprehensionExpressionStartsWithAssignment) {
fragment = generateExpression(expr.body, {
precedence: Precedence.Assignment,
allowIn: true,
allowCall: true
});
result.push(fragment);
}
if (expr.blocks) {
withIndent(function () {
for (i = 0, len = expr.blocks.length; i < len; ++i) {
fragment = generateExpression(expr.blocks[i], {
precedence: Precedence.Sequence,
allowIn: true,
allowCall: true
});
if (i > 0 || extra.moz.comprehensionExpressionStartsWithAssignment) {
result = join(result, fragment);
} else {
result.push(fragment);
}
}
});
}
if (expr.filter) {
result = join(result, 'if' + space);
fragment = generateExpression(expr.filter, {
precedence: Precedence.Sequence,
allowIn: true,
allowCall: true
});
result = join(result, [ '(', fragment, ')' ]);
}
if (!extra.moz.comprehensionExpressionStartsWithAssignment) {
fragment = generateExpression(expr.body, {
precedence: Precedence.Assignment,
allowIn: true,
allowCall: true
});
result = join(result, fragment);
}
result.push((type === Syntax.GeneratorExpression) ? ')' : ']');
break;
case Syntax.ComprehensionBlock:
if (expr.left.type === Syntax.VariableDeclaration) {
fragment = [
expr.left.kind, noEmptySpace(),
generateStatement(expr.left.declarations[0], {
allowIn: false
})
];
} else {
fragment = generateExpression(expr.left, {
precedence: Precedence.Call,
allowIn: true,
allowCall: true
});
}
fragment = join(fragment, expr.of ? 'of' : 'in');
fragment = join(fragment, generateExpression(expr.right, {
precedence: Precedence.Sequence,
allowIn: true,
allowCall: true
}));
result = [ 'for' + space + '(', fragment, ')' ];
break;
case Syntax.SpreadElement:
result = [
'...',
generateExpression(expr.argument, {
precedence: Precedence.Assignment,
allowIn: true,
allowCall: true
})
];
break;
case Syntax.TaggedTemplateExpression:
result = [
generateExpression(expr.tag, {
precedence: Precedence.Call,
allowIn: true,
allowCall: allowCall,
allowUnparenthesizedNew: false
}),
generateExpression(expr.quasi, {
precedence: Precedence.Primary
})
];
result = parenthesize(result, Precedence.TaggedTemplate, precedence);
break;
case Syntax.TemplateElement:
// Don't use "cooked". Since tagged template can use raw template
// representation. So if we do so, it breaks the script semantics.
result = expr.value.raw;
break;
case Syntax.TemplateLiteral:
result = [ '`' ];
for (i = 0, len = expr.quasis.length; i < len; ++i) {
result.push(generateExpression(expr.quasis[i], {
precedence: Precedence.Primary,
allowIn: true,
allowCall: true
}));
if (i + 1 < len) {
result.push('${' + space);
result.push(generateExpression(expr.expressions[i], {
precedence: Precedence.Sequence,
allowIn: true,
allowCall: true
}));
result.push(space + '}');
}
}
result.push('`');
break;
case Syntax.ModuleSpecifier:
result = generateModuleSpecifier(expr);
break;
default:
throw new Error('Unknown expression type: ' + expr.type);
}
if (extra.comment) {
result = addComments(expr,result);
}
return toSourceNodeWhenNeeded(result, expr);
}