/**
* Copyright (c) 2012-2016 André Bargull
* Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms.
*
* <https://github.com/anba/es6draft>
*/
package com.github.anba.es6draft.compiler.analyzer;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import com.github.anba.es6draft.ast.*;
import com.github.anba.es6draft.ast.synthetic.ExpressionMethod;
import com.github.anba.es6draft.ast.synthetic.MethodDefinitionsMethod;
import com.github.anba.es6draft.ast.synthetic.PropertyDefinitionsMethod;
import com.github.anba.es6draft.ast.synthetic.SpreadArrayLiteral;
import com.github.anba.es6draft.ast.synthetic.SpreadElementMethod;
import com.github.anba.es6draft.ast.synthetic.StatementListMethod;
import com.github.anba.es6draft.ast.synthetic.SyntheticNode;
/**
* Estimates the generated code size and splits statements or expressions into sub-methods to avoid exceeding the 64K
* bytecode size limit.
*/
public final class CodeSize implements IntNodeVisitor<CodeSize.State> {
private static final boolean DEBUG = false;
private static final int MAX_SIZE = 65535;
private static final int MAX_SIZE_ALLOWED = MAX_SIZE / 2;
private static final int MAX_EXPRESSION_SIZE = 1024 + 512;
private static final int MAX_STATEMENT_SIZE = 8192;
private static final int MAX_TOP_STATEMENT_SIZE = MAX_SIZE_ALLOWED;
private static final int MAX_ARRAY_ELEMENTS_SIZE = 2 * MAX_EXPRESSION_SIZE;
private static final int MAX_CLASS_PROPERTIES_SIZE = 2 * MAX_EXPRESSION_SIZE;
private static final int MAX_OBJECT_PROPERTIES_SIZE = 2 * MAX_EXPRESSION_SIZE;
private static final int MAX_TEMPLATE_ELEMENTS_SIZE = 2 * MAX_EXPRESSION_SIZE;
/**
* Splits statements or expressions into sub-methods to avoid exceeding the 64K bytecode size limit.
*
* @param topLevelNode
* the top level node
* @throws CodeSizeException
* if the estimated code size exceeds the bytecode limit
*/
public static void analyze(TopLevelNode<?> topLevelNode) throws CodeSizeException {
topLevelNode.accept(new CodeSize(size -> {
throw new CodeSizeException(size);
}), null);
}
/**
* Splits statements or expressions into sub-methods to avoid exceeding the 64K bytecode size limit and returns the
* estimated size for the generated code.
*
* @param topLevelNode
* the top level node
* @return the estimated code size
*/
public static int calculate(TopLevelNode<?> topLevelNode) {
return topLevelNode.accept(new CodeSize(size -> {
// Ignored.
}), null);
}
private final Consumer<Integer> onSizeViolation;
private CodeSize(Consumer<Integer> onSizeViolation) {
this.onSizeViolation = onSizeViolation;
}
// TODO: Add 'quota' field to track current size?
static final class State {
final TopLevelNode<?> top;
State(TopLevelNode<?> top) {
this.top = top;
}
private boolean isGeneratorOrAsync() {
if (top instanceof FunctionNode) {
FunctionNode function = (FunctionNode) top;
return function.isGenerator() || function.isAsync();
}
return false;
}
void notifySyntheticNodes(Node newNode) {
if (isGeneratorOrAsync()) {
newNode.accept(new FindYieldOrAwait(), yieldOrAwait -> {
((SyntheticNode) newNode).setResumePoint(true);
});
}
}
Program program() {
TopLevelNode<?> top = this.top;
while (!(top instanceof Program)) {
top = top.getScope().getEnclosingScope().getTop().getNode();
}
return (Program) top;
}
}
private static void debug(String format, Object... args) {
System.out.printf(format, args);
}
private int accept(Node node, State state) {
return node.accept(this, state);
}
private int accept(List<? extends Node> nodes, State state) {
// return nodes.stream().mapToInt(e -> e.accept(this, state)).sum();
int size = 0;
for (Node node : nodes) {
size += node.accept(this, state);
}
return size;
}
private int acceptIfPresent(Node node, State state) {
if (node != null) {
return node.accept(this, state);
}
return 0;
}
private <NODE extends Node> int accept(NODE node, State state, Function<NODE, NODE> mapper, Consumer<NODE> updater,
int limit) {
int size = node.accept(this, state);
if (size < limit) {
return size;
}
if (DEBUG)
debug("Replace %s=%d [%s]%n", node.getClass().getSimpleName(), size, state.program().getSource());
NODE newNode = mapper.apply(node);
state.notifySyntheticNodes(newNode);
updater.accept(newNode);
return newNode.accept(this, state);
}
private <NODE extends Node> int accept(List<NODE> nodes, State state, Function<NODE, NODE> mapper,
Consumer<List<NODE>> updater, int limit) {
int size = 0;
List<NODE> newNodes = null;
for (int i = 0; i < nodes.size(); ++i) {
NODE node = nodes.get(i);
int nodeSize = node.accept(this, state);
if (nodeSize < limit) {
size += nodeSize;
continue;
}
if (DEBUG)
debug("Replace %s=%d [%s]%n", node.getClass().getSimpleName(), nodeSize, state.program().getSource());
NODE newNode = mapper.apply(node);
if (newNodes == null) {
newNodes = new ArrayList<>(nodes);
}
newNodes.set(i, newNode);
state.notifySyntheticNodes(newNode);
size += newNode.accept(this, state);
}
if (newNodes != null) {
updater.accept(newNodes);
}
return size;
}
private int expression(Expression expression, State state, Consumer<Expression> updater) {
return accept(expression, state, ExpressionMethod::new, updater, MAX_EXPRESSION_SIZE);
}
private int statement(Statement statement, State state, Consumer<Statement> updater) {
return accept(statement, state, StatementListMethod::new, updater, MAX_STATEMENT_SIZE);
}
private int statementList(Supplier<List<StatementListItem>> getter, State state,
Consumer<List<StatementListItem>> updater) {
return statementList(getter, state, updater, MAX_TOP_STATEMENT_SIZE);
}
private int statementList(Supplier<List<StatementListItem>> getter, State state,
Consumer<List<StatementListItem>> updater, int limit) {
int statements = accept(getter.get(), state, StatementListMethod::new, updater, MAX_STATEMENT_SIZE);
if (statements < limit) {
return statements;
}
int newStatements = split(getter.get(), state, StatementListMethod::new, updater, limit);
if (DEBUG)
debug("Split statementList: %d -> %d [%s]%n", statements, newStatements, state.program().getSource());
return newStatements;
}
private int moduleItemList(Supplier<List<ModuleItem>> getter, State state, Consumer<List<ModuleItem>> updater) {
int statements = accept(getter.get(), state, StatementListMethod::new, updater, MAX_STATEMENT_SIZE);
if (statements < MAX_TOP_STATEMENT_SIZE) {
return statements;
}
int newStatements = split(getter.get(), state, StatementListMethod::new, updater, MAX_TOP_STATEMENT_SIZE);
if (DEBUG)
debug("Split moduleItemList: %d -> %d [%s]%n", statements, newStatements, state.program().getSource());
return newStatements;
}
private <NODE extends Node> int split(List<NODE> nodes, State state, Function<List<NODE>, NODE> mapper,
Consumer<List<NODE>> updater, int limit) {
ArrayList<NODE> list = new ArrayList<>(nodes);
int newSize;
boolean replaced;
do {
newSize = 0;
replaced = false;
int chunkSize = 0, end = list.size();
for (int i = list.size() - 1; i >= 0; --i) {
int nodeSize = list.get(i).accept(this, state);
if (chunkSize + nodeSize < limit) {
chunkSize += nodeSize;
} else {
// Insert new chunk.
int start = i + 1;
if (start < end) {
int rangeSize = replaceRange(list, start, end, state, mapper);
if (DEBUG)
debug("ReplaceRange: %d -> %d [%s]%n", chunkSize, rangeSize, state.program().getSource());
newSize += rangeSize;
replaced = true;
}
chunkSize = nodeSize;
end = start;
}
}
if (chunkSize > limit) {
int rangeSize = replaceRange(list, 0, end, state, mapper);
if (DEBUG)
debug("ReplaceRange: %d -> %d [%s]%n", chunkSize, rangeSize, state.program().getSource());
newSize += rangeSize;
replaced = true;
} else {
newSize += chunkSize;
}
} while (replaced && newSize > limit);
updater.accept(list);
return newSize;
}
private <NODE extends Node> int replaceRange(List<NODE> list, int start, int end, State state,
Function<List<NODE>, NODE> mapper) {
List<NODE> view = list.subList(start, end);
NODE newNode = mapper.apply(new ArrayList<>(view));
view.clear();
list.add(start, newNode);
state.notifySyntheticNodes(newNode);
return newNode.accept(this, state);
}
private void checkValidSize(int size) {
if (size > MAX_SIZE_ALLOWED) {
onSizeViolation.accept(size);
}
}
private int listSize(List<?> list, int size) {
return list.size() * size;
}
private int stringSize(String s) {
if (s.length() <= 32768) {
return 5;
}
return ((s.length() / 32768) + 1) * 10;
}
@Override
public int visit(ArrayAssignmentPattern node, State state) {
int elements = accept(node.getElements(), state);
return 30 + elements + listSize(node.getElements(), 5);
}
@Override
public int visit(ArrayBindingPattern node, State state) {
int elements = accept(node.getElements(), state);
return 30 + elements + listSize(node.getElements(), 5);
}
@Override
public int visit(ArrayComprehension node, State state) {
int comprehension = accept(node.getComprehension(), state);
return 25 + comprehension;
}
@Override
public int visit(ArrayLiteral node, State state) {
int elements = accept(node.getElements(), state, ExpressionMethod::new, node::setElements, MAX_EXPRESSION_SIZE);
if (elements > MAX_ARRAY_ELEMENTS_SIZE) {
elements = split(node.getElements(), state, SpreadElementMethod::new, node::setElements,
MAX_ARRAY_ELEMENTS_SIZE);
}
return 25 + elements + listSize(node.getElements(), 5);
}
@Override
public int visit(ArrowFunction node, State state) {
if (node.getExpression() != null) {
visitConciseFunction(node, node::getExpression, state);
} else {
visitFunction(node, state);
}
return 10;
}
@Override
public int visit(AssignmentElement node, State state) {
int target = accept(node.getTarget(), state);
int initializer = acceptIfPresent(node.getInitializer(), state);
return 25 + target + initializer;
}
@Override
public int visit(AssignmentExpression node, State state) {
int left = accept(node.getLeft(), state);
int right = accept(node.getRight(), state);
return 10 + left + right;
}
@Override
public int visit(AssignmentProperty node, State state) {
int propertyName = acceptIfPresent(node.getPropertyName(), state);
int target = accept(node.getTarget(), state);
int initializer = acceptIfPresent(node.getInitializer(), state);
return 25 + propertyName + target + initializer;
}
@Override
public int visit(AssignmentRestElement node, State state) {
int target = accept(node.getTarget(), state);
return 25 + target;
}
@Override
public int visit(AssignmentRestProperty node, State state) {
int target = accept(node.getTarget(), state);
return 25 + target;
}
@Override
public int visit(AsyncArrowFunction node, State state) {
if (node.getExpression() != null) {
visitConciseFunction(node, node::getExpression, state);
} else {
visitFunction(node, state);
}
return 10;
}
@Override
public int visit(AsyncFunctionDeclaration node, State state) {
visitFunction(node, state);
return 0;
}
@Override
public int visit(AsyncFunctionExpression node, State state) {
visitFunction(node, state);
return 10;
}
@Override
public int visit(AsyncGeneratorDeclaration node, State state) {
visitFunction(node, state);
return 0;
}
@Override
public int visit(AsyncGeneratorExpression node, State state) {
visitFunction(node, state);
return 10;
}
@Override
public int visit(AwaitExpression node, State state) {
int expression = accept(node.getExpression(), state);
return 150 + expression;
}
@Override
public int visit(BinaryExpression node, State state) {
int left = expression(node.getLeft(), state, node::setLeft);
int right = expression(node.getRight(), state, node::setRight);
return 20 + left + right;
}
@Override
public int visit(BindingElement node, State state) {
int binding = accept(node.getBinding(), state);
int initializer = acceptIfPresent(node.getInitializer(), state);
return 25 + binding + initializer;
}
@Override
public int visit(BindingElision node, State state) {
return 0;
}
@Override
public int visit(BindingIdentifier node, State state) {
return 15;
}
@Override
public int visit(BindingProperty node, State state) {
int propertyName = acceptIfPresent(node.getPropertyName(), state);
int binding = accept(node.getBinding(), state);
int initializer = acceptIfPresent(node.getInitializer(), state);
return 25 + propertyName + binding + initializer;
}
@Override
public int visit(BindingRestElement node, State state) {
int binding = accept(node.getBinding(), state);
return 25 + binding;
}
@Override
public int visit(BindingRestProperty node, State state) {
int bindingIdentifier = accept(node.getBindingIdentifier(), state);
return 25 + bindingIdentifier;
}
@Override
public int visit(BlockStatement node, State state) {
int statements = statementList(node::getStatements, state, node::setStatements, MAX_STATEMENT_SIZE);
return 15 + statements;
}
@Override
public int visit(BooleanLiteral node, State state) {
return 5;
}
@Override
public int visit(BreakStatement node, State state) {
return 5;
}
@Override
public int visit(CallExpression node, State state) {
int base = accept(node.getBase(), state);
int arguments = accept(node.getArguments(), state);
return 30 + base + arguments + listSize(node.getArguments(), 5);
}
@Override
public int visit(CallSpreadElement node, State state) {
int expression = accept(node.getExpression(), state);
return 10 + expression;
}
@Override
public int visit(CatchNode node, State state) {
int catchParameter = accept(node.getCatchParameter(), state);
int catchBlock = accept(node.getCatchBlock(), state);
return 35 + catchParameter + catchBlock;
}
@Override
public int visit(ClassDeclaration node, State state) {
return visitClassDefinition(node, state);
}
@Override
public int visit(ClassExpression node, State state) {
return visitClassDefinition(node, state);
}
private int visitClassDefinition(ClassDefinition node, State state) {
int heritage = acceptIfPresent(node.getHeritage(), state);
int decorators = accept(node.getDecorators(), state);
int properties = accept(node.getProperties(), state);
if (properties > MAX_CLASS_PROPERTIES_SIZE) {
properties = split(node.getProperties(), state, MethodDefinitionsMethod::new, node::setProperties,
MAX_CLASS_PROPERTIES_SIZE);
}
return 50 + heritage + decorators + properties + listSize(node.getDecorators(), 10)
+ listSize(node.getProperties(), 10);
}
@Override
public int visit(CommaExpression node, State state) {
int operands = accept(node.getOperands(), state, ExpressionMethod::new, node::setOperands, MAX_EXPRESSION_SIZE);
if (operands > MAX_EXPRESSION_SIZE) {
operands = split(node.getOperands(), state, list -> new ExpressionMethod(new CommaExpression(list)),
node::setOperands, MAX_EXPRESSION_SIZE);
}
return 5 + operands;
}
@Override
public int visit(Comprehension node, State state) {
int list = accept(node.getList(), state);
int expression = accept(node.getExpression(), state);
return 50 + list + expression;
}
@Override
public int visit(ComprehensionFor node, State state) {
int binding = accept(node.getBinding(), state);
int expression = accept(node.getExpression(), state);
return 40 + binding + expression;
}
@Override
public int visit(ComprehensionIf node, State state) {
int test = accept(node.getTest(), state);
return 15 + test;
}
@Override
public int visit(ComputedPropertyName node, State state) {
int expression = accept(node.getExpression(), state);
return 15 + expression;
}
@Override
public int visit(ConditionalExpression node, State state) {
int test = accept(node.getTest(), state);
int then = accept(node.getThen(), state);
int otherwise = accept(node.getOtherwise(), state);
return 20 + test + then + otherwise;
}
@Override
public int visit(ContinueStatement node, State state) {
return 5;
}
@Override
public int visit(DebuggerStatement node, State state) {
return 5;
}
@Override
public int visit(DoExpression node, State state) {
int statement = accept(node.getStatement(), state);
checkValidSize(statement);
return 25;
}
@Override
public int visit(DoWhileStatement node, State state) {
int test = accept(node.getTest(), state);
int statement = statement(node.getStatement(), state, node::setStatement);
// int statement = accept(node.getStatement(), state, StatementListMethod::new, node::setStatement, 0);
return 25 + test + statement;
}
@Override
public int visit(ElementAccessor node, State state) {
int base = accept(node.getBase(), state);
int element = accept(node.getElement(), state);
return 10 + base + element;
}
@Override
public int visit(Elision node, State state) {
return 0;
}
@Override
public int visit(EmptyExpression node, State state) {
return 0;
}
@Override
public int visit(EmptyStatement node, State state) {
return 0;
}
@Override
public int visit(ExportClause node, State state) {
return 0;
}
@Override
public int visit(ExportDeclaration node, State state) {
switch (node.getType()) {
case All:
case External:
case Local:
return 0;
case Variable:
return accept(node.getVariableStatement(), state);
case Declaration:
return accept(node.getDeclaration(), state);
case DefaultHoistableDeclaration:
return accept(node.getHoistableDeclaration(), state);
case DefaultClassDeclaration:
return accept(node.getClassDeclaration(), state);
case DefaultExpression:
return accept(node.getExpression(), state);
default:
throw new AssertionError();
}
}
@Override
public int visit(ExportDefaultExpression node, State state) {
int expression = accept(node.getExpression(), state);
return 15 + expression;
}
@Override
public int visit(ExportSpecifier node, State state) {
return 0;
}
@Override
public int visit(ExpressionMethod node, State state) {
// Don't descend into synthetic nodes.
return 5;
}
@Override
public int visit(ExpressionStatement node, State state) {
int expression = accept(node.getExpression(), state);
return 5 + expression;
}
@Override
public int visit(ForAwaitStatement node, State state) {
return visitForIteration(node, state);
}
@Override
public int visit(ForEachStatement node, State state) {
return visitForIteration(node, state);
}
@Override
public int visit(ForInStatement node, State state) {
return visitForIteration(node, state);
}
@Override
public int visit(FormalParameter node, State state) {
return accept(node.getElement(), state);
}
@Override
public int visit(FormalParameterList node, State state) {
return accept(node.getFormals(), state);
}
@Override
public int visit(ForOfStatement node, State state) {
return visitForIteration(node, state);
}
private int visitForIteration(ForIterationNode node, State state) {
int head = acceptIfPresent(node.getHead(), state);
int expression = acceptIfPresent(node.getExpression(), state);
int statement = statement(node.getStatement(), state, node::setStatement);
return 150 + head + expression + statement;
}
@Override
public int visit(ForStatement node, State state) {
int head = acceptIfPresent(node.getHead(), state);
int test = acceptIfPresent(node.getTest(), state);
int step = acceptIfPresent(node.getStep(), state);
int statement = statement(node.getStatement(), state, node::setStatement);
return 30 + head + test + step + statement;
}
@Override
public int visit(FunctionDeclaration node, State state) {
visitFunction(node, state);
return 0;
}
@Override
public int visit(FunctionExpression node, State state) {
visitFunction(node, state);
return 10;
}
private void visitConciseFunction(FunctionNode node, Supplier<Expression> concise, State state) {
int parameters = accept(node.getParameters(), new State(node));
checkValidSize(parameters);
int expression = accept(concise.get(), new State(node));
checkValidSize(expression);
}
private void visitFunction(FunctionNode node, State state) {
int parameters = accept(node.getParameters(), new State(node));
checkValidSize(parameters);
int statements = statementList(node::getStatements, new State(node), node::setStatements);
checkValidSize(statements);
}
@Override
public int visit(FunctionSent node, State state) {
return 5;
}
@Override
public int visit(GeneratorComprehension node, State state) {
int comprehension = accept(node.getComprehension(), new State(node));
checkValidSize(comprehension);
return 10;
}
@Override
public int visit(GeneratorDeclaration node, State state) {
visitFunction(node, state);
return 0;
}
@Override
public int visit(GeneratorExpression node, State state) {
visitFunction(node, state);
return 10;
}
@Override
public int visit(GuardedCatchNode node, State state) {
int catchParameter = accept(node.getCatchParameter(), state);
int guard = accept(node.getGuard(), state);
int catchBlock = accept(node.getCatchBlock(), state);
return 45 + catchParameter + guard + catchBlock;
}
@Override
public int visit(IdentifierName node, State state) {
return 5;
}
@Override
public int visit(IdentifierReference node, State state) {
return 10;
}
@Override
public int visit(IfStatement node, State state) {
int test = accept(node.getTest(), state);
int then = statement(node.getThen(), state, node::setThen);
int otherwise = node.getOtherwise() != null ? statement(node.getOtherwise(), state, node::setOtherwise) : 0;
return 10 + test + then + otherwise;
}
@Override
public int visit(ImportClause node, State state) {
throw new IllegalStateException();
}
@Override
public int visit(ImportDeclaration node, State state) {
return 0;
}
@Override
public int visit(ImportSpecifier node, State state) {
throw new IllegalStateException();
}
@Override
public int visit(LabelledFunctionStatement node, State state) {
return accept(node.getFunction(), state);
}
@Override
public int visit(LabelledStatement node, State state) {
int statement = accept(node.getStatement(), state);
return 15 + statement;
}
@Override
public int visit(LegacyComprehension node, State state) {
int list = accept(node.getList(), state);
int expression = accept(node.getExpression(), state);
return 50 + list + expression;
}
@Override
public int visit(LegacyComprehensionFor node, State state) {
int binding = accept(node.getBinding(), state);
int expression = accept(node.getExpression(), state);
return 40 + binding + expression;
}
@Override
public int visit(LegacyGeneratorDeclaration node, State state) {
visitFunction(node, state);
return 0;
}
@Override
public int visit(LegacyGeneratorExpression node, State state) {
visitFunction(node, state);
return 10;
}
@Override
public int visit(LetExpression node, State state) {
int bindings = accept(node.getBindings(), state);
int expression = accept(node.getExpression(), state);
return 25 + bindings + expression;
}
@Override
public int visit(LetStatement node, State state) {
int bindings = accept(node.getBindings(), state);
int statements = accept(node.getStatement(), state);
return 25 + bindings + statements;
}
@Override
public int visit(LexicalBinding node, State state) {
int binding = accept(node.getBinding(), state);
int initializer = acceptIfPresent(node.getInitializer(), state);
return 20 + binding + initializer;
}
@Override
public int visit(LexicalDeclaration node, State state) {
return accept(node.getElements(), state);
}
@Override
public int visit(MethodDefinition node, State state) {
visitFunction(node, state);
int propertyName = accept(node.getPropertyName(), state);
int decorators = accept(node.getDecorators(), state);
return 10 + propertyName + decorators + listSize(node.getDecorators(), 10);
}
@Override
public int visit(MethodDefinitionsMethod node, State state) {
// Don't descend into synthetic nodes.
return 10;
}
@Override
public int visit(Module node, State state) {
int statements = moduleItemList(node::getStatements, new State(node), node::setStatements);
checkValidSize(statements);
return statements;
}
@Override
public int visit(NativeCallExpression node, State state) {
int base = accept(node.getBase(), state);
int arguments = accept(node.getArguments(), state);
return 30 + base + arguments + listSize(node.getArguments(), 5);
}
@Override
public int visit(NewExpression node, State state) {
int expression = accept(node.getExpression(), state);
int arguments = accept(node.getArguments(), state);
return 15 + expression + arguments + listSize(node.getArguments(), 5);
}
@Override
public int visit(NewTarget node, State state) {
return 5;
}
@Override
public int visit(NullLiteral node, State state) {
return 5;
}
@Override
public int visit(NumericLiteral node, State state) {
return 5;
}
@Override
public int visit(ObjectAssignmentPattern node, State state) {
int properties = accept(node.getProperties(), state);
int rest = acceptIfPresent(node.getRest(), state);
return 20 + properties + rest + listSize(node.getProperties(), 5);
}
@Override
public int visit(ObjectBindingPattern node, State state) {
int properties = accept(node.getProperties(), state);
int rest = acceptIfPresent(node.getRest(), state);
return 20 + properties + rest + listSize(node.getProperties(), 5);
}
@Override
public int visit(ObjectLiteral node, State state) {
int properties = accept(node.getProperties(), state, PropertyDefinitionsMethod::new, node::setProperties,
MAX_EXPRESSION_SIZE);
if (properties > MAX_OBJECT_PROPERTIES_SIZE) {
properties = split(node.getProperties(), state, PropertyDefinitionsMethod::new, node::setProperties,
MAX_OBJECT_PROPERTIES_SIZE);
}
return 15 + properties + listSize(node.getProperties(), 5);
}
@Override
public int visit(PropertyAccessor node, State state) {
int base = accept(node.getBase(), state);
int propertyName = stringSize(node.getName());
return 5 + base + propertyName;
}
@Override
public int visit(PropertyDefinitionsMethod node, State state) {
// Don't descend into synthetic nodes.
return 10;
}
@Override
public int visit(PropertyNameDefinition node, State state) {
return 15;
}
@Override
public int visit(PropertyValueDefinition node, State state) {
int propertyName = accept(node.getPropertyName(), state);
int propertyValue = accept(node.getPropertyValue(), state);
return 5 + propertyName + propertyValue;
}
@Override
public int visit(RegularExpressionLiteral node, State state) {
return 10;
}
@Override
public int visit(ReturnStatement node, State state) {
int expression = acceptIfPresent(node.getExpression(), state);
return 10 + expression;
}
@Override
public int visit(Script node, State state) {
int statements = statementList(node::getStatements, new State(node), node::setStatements);
checkValidSize(statements);
return statements;
}
@Override
public int visit(SpreadArrayLiteral node, State state) {
// Don't descend into synthetic nodes.
return 10;
}
@Override
public int visit(SpreadElement node, State state) {
int expression = accept(node.getExpression(), state);
return 10 + expression;
}
@Override
public int visit(SpreadElementMethod node, State state) {
// Don't descend into synthetic nodes.
return 10;
}
@Override
public int visit(SpreadProperty node, State state) {
int expression = accept(node.getExpression(), state);
return 10 + expression;
}
@Override
public int visit(StatementListMethod node, State state) {
// Don't descend into synthetic nodes.
return 25;
}
@Override
public int visit(StringLiteral node, State state) {
return stringSize(node.getValue());
}
@Override
public int visit(SuperCallExpression node, State state) {
int arguments = accept(node.getArguments(), state);
return 25 + arguments + listSize(node.getArguments(), 5);
}
@Override
public int visit(SuperElementAccessor node, State state) {
int element = accept(node.getElement(), state);
return 15 + element;
}
@Override
public int visit(SuperNewExpression node, State state) {
int arguments = accept(node.getArguments(), state);
return 25 + arguments + listSize(node.getArguments(), 5);
}
@Override
public int visit(SuperPropertyAccessor node, State state) {
int propertyName = stringSize(node.getName());
return 10 + propertyName;
}
@Override
public int visit(SwitchClause node, State state) {
int expression = acceptIfPresent(node.getExpression(), state);
int statements = statementList(node::getStatements, state, node::setStatements, MAX_STATEMENT_SIZE);
return 15 + expression + statements;
}
@Override
public int visit(SwitchStatement node, State state) {
// TODO: Doesn't take optimized switches (int,char,string) into account.
int expression = accept(node.getExpression(), state);
int clauses = accept(node.getClauses(), state);
return 100 + expression + clauses;
}
@Override
public int visit(TemplateCallExpression node, State state) {
int base = accept(node.getBase(), state);
int template = accept(node.getTemplate(), state);
return 30 + base + template;
}
@Override
public int visit(TemplateCharacters node, State state) {
return stringSize(node.getValue()) + stringSize(node.getRawValue());
}
@Override
public int visit(TemplateLiteral node, State state) {
int elements = accept(node.getElements(), state, ExpressionMethod::new, node::setElements, MAX_EXPRESSION_SIZE);
if (elements > MAX_TEMPLATE_ELEMENTS_SIZE) {
elements = split(node.getElements(), state, list -> {
assert !list.isEmpty();
long beginPosition = list.get(0).getBeginPosition();
long endPosition = list.get(list.size() - 1).getEndPosition();
return new ExpressionMethod(new TemplateLiteral(beginPosition, endPosition, false, list));
} , node::setElements, MAX_TEMPLATE_ELEMENTS_SIZE);
}
return 25 + elements + listSize(node.getElements(), 10);
}
@Override
public int visit(ThisExpression node, State state) {
return 10;
}
@Override
public int visit(ThrowStatement node, State state) {
int expression = accept(node.getExpression(), state);
return 10 + expression;
}
@Override
public int visit(TryStatement node, State state) {
int tryBlock = accept(node.getTryBlock(), state);
int catchNode = acceptIfPresent(node.getCatchNode(), state);
int guardedNodes = accept(node.getGuardedCatchNodes(), state);
int finallyBlock = acceptIfPresent(node.getFinallyBlock(), state);
return 40 + tryBlock + catchNode + guardedNodes + finallyBlock + listSize(node.getGuardedCatchNodes(), 20);
}
@Override
public int visit(UnaryExpression node, State state) {
int operand = accept(node.getOperand(), state);
return 15 + operand;
}
@Override
public int visit(VariableDeclaration node, State state) {
int binding = accept(node.getBinding(), state);
int initializer = acceptIfPresent(node.getInitializer(), state);
return 15 + binding + initializer;
}
@Override
public int visit(VariableStatement node, State state) {
return accept(node.getElements(), state);
}
@Override
public int visit(WhileStatement node, State state) {
int test = accept(node.getTest(), state);
int statement = statement(node.getStatement(), state, node::setStatement);
return 25 + test + statement;
}
@Override
public int visit(WithStatement node, State state) {
int expression = accept(node.getExpression(), state);
int statement = statement(node.getStatement(), state, node::setStatement);
return 25 + expression + statement;
}
@Override
public int visit(YieldExpression node, State state) {
int expression = acceptIfPresent(node.getExpression(), state);
if (node.isDelegatedYield()) {
return 300 + expression;
}
return 150 + expression;
}
}