/* * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.nashorn.internal.codegen; import java.util.List; import java.util.Map; import jdk.nashorn.internal.ir.AccessNode; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.BreakNode; import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.ContinueNode; import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.JumpToInlinedFinally; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyNode; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.SplitNode; import jdk.nashorn.internal.ir.Splittable; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.ThrowNode; import jdk.nashorn.internal.ir.TryNode; import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; /** * Computes the "byte code" weight of an AST segment. This is used * for Splitting too large class files */ final class WeighNodes extends NodeOperatorVisitor<LexicalContext> { /* * Weight constants. */ static final long FUNCTION_WEIGHT = 40; static final long AASTORE_WEIGHT = 2; static final long ACCESS_WEIGHT = 4; static final long ADD_WEIGHT = 10; static final long BREAK_WEIGHT = 1; static final long CALL_WEIGHT = 10; static final long CATCH_WEIGHT = 10; static final long COMPARE_WEIGHT = 6; static final long CONTINUE_WEIGHT = 1; static final long IF_WEIGHT = 2; static final long LITERAL_WEIGHT = 10; static final long LOOP_WEIGHT = 4; static final long NEW_WEIGHT = 6; static final long FUNC_EXPR_WEIGHT = 20; static final long RETURN_WEIGHT = 2; static final long SPLIT_WEIGHT = 40; static final long SWITCH_WEIGHT = 8; static final long THROW_WEIGHT = 2; static final long VAR_WEIGHT = 40; static final long WITH_WEIGHT = 8; static final long OBJECT_WEIGHT = 16; static final long SETPROP_WEIGHT = 5; /** Accumulated weight. */ private long weight; /** Optional cache for weight of block nodes. */ private final Map<Node, Long> weightCache; private final FunctionNode topFunction; /** * Constructor * * @param weightCache cache of already calculated block weights */ private WeighNodes(final FunctionNode topFunction, final Map<Node, Long> weightCache) { super(new LexicalContext()); this.topFunction = topFunction; this.weightCache = weightCache; } static long weigh(final Node node) { return weigh(node, null); } static long weigh(final Node node, final Map<Node, Long> weightCache) { final WeighNodes weighNodes = new WeighNodes(node instanceof FunctionNode ? (FunctionNode)node : null, weightCache); node.accept(weighNodes); return weighNodes.weight; } @Override public Node leaveAccessNode(final AccessNode accessNode) { weight += ACCESS_WEIGHT; return accessNode; } @Override public boolean enterBlock(final Block block) { if (weightCache != null && weightCache.containsKey(block)) { weight += weightCache.get(block); return false; } return true; } @Override public Node leaveBreakNode(final BreakNode breakNode) { weight += BREAK_WEIGHT; return breakNode; } @Override public Node leaveCallNode(final CallNode callNode) { weight += CALL_WEIGHT; return callNode; } @Override public Node leaveCatchNode(final CatchNode catchNode) { weight += CATCH_WEIGHT; return catchNode; } @Override public Node leaveContinueNode(final ContinueNode continueNode) { weight += CONTINUE_WEIGHT; return continueNode; } @Override public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) { return expressionStatement; } @Override public Node leaveForNode(final ForNode forNode) { weight += LOOP_WEIGHT; return forNode; } @Override public boolean enterFunctionNode(final FunctionNode functionNode) { if (functionNode == topFunction) { // the function being weighted; descend into its statements return true; } // just a reference to inner function from outer function weight += FUNC_EXPR_WEIGHT; return false; } @Override public Node leaveIdentNode(final IdentNode identNode) { weight += ACCESS_WEIGHT + identNode.getName().length() * 2; return identNode; } @Override public Node leaveIfNode(final IfNode ifNode) { weight += IF_WEIGHT; return ifNode; } @Override public Node leaveIndexNode(final IndexNode indexNode) { weight += ACCESS_WEIGHT; return indexNode; } @Override public Node leaveJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { weight += BREAK_WEIGHT; return jumpToInlinedFinally; } @SuppressWarnings("rawtypes") @Override public boolean enterLiteralNode(final LiteralNode literalNode) { weight += LITERAL_WEIGHT; if (literalNode instanceof ArrayLiteralNode) { final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode; final Node[] value = arrayLiteralNode.getValue(); final int[] postsets = arrayLiteralNode.getPostsets(); final List<Splittable.SplitRange> units = arrayLiteralNode.getSplitRanges(); if (units == null) { for (final int postset : postsets) { weight += AASTORE_WEIGHT; final Node element = value[postset]; if (element != null) { element.accept(this); } } } return false; } return true; } @Override public boolean enterObjectNode(final ObjectNode objectNode) { weight += OBJECT_WEIGHT; final List<PropertyNode> properties = objectNode.getElements(); final boolean isSpillObject = properties.size() > CodeGenerator.OBJECT_SPILL_THRESHOLD; for (final PropertyNode property : properties) { if (!LiteralNode.isConstant(property.getValue())) { weight += SETPROP_WEIGHT; property.getValue().accept(this); } else if (!isSpillObject) { // constants in spill object are set via preset spill array, // but fields objects need to set constants. weight += SETPROP_WEIGHT; } } return false; } @Override public Node leavePropertyNode(final PropertyNode propertyNode) { weight += LITERAL_WEIGHT; return propertyNode; } @Override public Node leaveReturnNode(final ReturnNode returnNode) { weight += RETURN_WEIGHT; return returnNode; } @Override public Node leaveRuntimeNode(final RuntimeNode runtimeNode) { weight += CALL_WEIGHT; return runtimeNode; } @Override public boolean enterSplitNode(final SplitNode splitNode) { weight += SPLIT_WEIGHT; return false; } @Override public Node leaveSwitchNode(final SwitchNode switchNode) { weight += SWITCH_WEIGHT; return switchNode; } @Override public Node leaveThrowNode(final ThrowNode throwNode) { weight += THROW_WEIGHT; return throwNode; } @Override public Node leaveTryNode(final TryNode tryNode) { weight += THROW_WEIGHT; return tryNode; } @Override public Node leaveVarNode(final VarNode varNode) { weight += VAR_WEIGHT; return varNode; } @Override public Node leaveWhileNode(final WhileNode whileNode) { weight += LOOP_WEIGHT; return whileNode; } @Override public Node leaveWithNode(final WithNode withNode) { weight += WITH_WEIGHT; return withNode; } @Override public Node leaveADD(final UnaryNode unaryNode) { return unaryNodeWeight(unaryNode); } @Override public Node leaveBIT_NOT(final UnaryNode unaryNode) { return unaryNodeWeight(unaryNode); } @Override public Node leaveDECINC(final UnaryNode unaryNode) { return unaryNodeWeight(unaryNode); } @Override public Node leaveDELETE(final UnaryNode unaryNode) { return runtimeNodeWeight(unaryNode); } @Override public Node leaveNEW(final UnaryNode unaryNode) { weight += NEW_WEIGHT; return unaryNode; } @Override public Node leaveNOT(final UnaryNode unaryNode) { return unaryNodeWeight(unaryNode); } @Override public Node leaveSUB(final UnaryNode unaryNode) { return unaryNodeWeight(unaryNode); } @Override public Node leaveTYPEOF(final UnaryNode unaryNode) { return runtimeNodeWeight(unaryNode); } @Override public Node leaveVOID(final UnaryNode unaryNode) { return unaryNodeWeight(unaryNode); } @Override public Node leaveADD(final BinaryNode binaryNode) { weight += ADD_WEIGHT; return binaryNode; } @Override public Node leaveAND(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveASSIGN(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveASSIGN_ADD(final BinaryNode binaryNode) { weight += ADD_WEIGHT; return binaryNode; } @Override public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveASSIGN_DIV(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveASSIGN_MOD(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveASSIGN_MUL(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveASSIGN_SAR(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveASSIGN_SHL(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveASSIGN_SHR(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveASSIGN_SUB(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveARROW(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveBIT_AND(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveBIT_OR(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveBIT_XOR(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveCOMMALEFT(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveCOMMARIGHT(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveDIV(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveEQ(final BinaryNode binaryNode) { return compareWeight(binaryNode); } @Override public Node leaveEQ_STRICT(final BinaryNode binaryNode) { return compareWeight(binaryNode); } @Override public Node leaveGE(final BinaryNode binaryNode) { return compareWeight(binaryNode); } @Override public Node leaveGT(final BinaryNode binaryNode) { return compareWeight(binaryNode); } @Override public Node leaveIN(final BinaryNode binaryNode) { weight += CALL_WEIGHT; return binaryNode; } @Override public Node leaveINSTANCEOF(final BinaryNode binaryNode) { weight += CALL_WEIGHT; return binaryNode; } @Override public Node leaveLE(final BinaryNode binaryNode) { return compareWeight(binaryNode); } @Override public Node leaveLT(final BinaryNode binaryNode) { return compareWeight(binaryNode); } @Override public Node leaveMOD(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveMUL(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveNE(final BinaryNode binaryNode) { return compareWeight(binaryNode); } @Override public Node leaveNE_STRICT(final BinaryNode binaryNode) { return compareWeight(binaryNode); } @Override public Node leaveOR(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveSAR(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveSHL(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveSHR(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } @Override public Node leaveSUB(final BinaryNode binaryNode) { return binaryNodeWeight(binaryNode); } private Node unaryNodeWeight(final UnaryNode unaryNode) { weight += 1; return unaryNode; } private Node binaryNodeWeight(final BinaryNode binaryNode) { weight += 1; return binaryNode; } private Node runtimeNodeWeight(final UnaryNode unaryNode) { weight += CALL_WEIGHT; return unaryNode; } private Node compareWeight(final BinaryNode binaryNode) { weight += COMPARE_WEIGHT; return binaryNode; } }