1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.codegen;
  27 
  28 import java.util.List;
  29 import java.util.Map;
  30 import jdk.nashorn.internal.ir.AccessNode;
  31 import jdk.nashorn.internal.ir.BinaryNode;
  32 import jdk.nashorn.internal.ir.Block;
  33 import jdk.nashorn.internal.ir.BreakNode;
  34 import jdk.nashorn.internal.ir.CallNode;
  35 import jdk.nashorn.internal.ir.CatchNode;
  36 import jdk.nashorn.internal.ir.ContinueNode;
  37 import jdk.nashorn.internal.ir.ExpressionStatement;
  38 import jdk.nashorn.internal.ir.ForNode;
  39 import jdk.nashorn.internal.ir.FunctionNode;
  40 import jdk.nashorn.internal.ir.IdentNode;
  41 import jdk.nashorn.internal.ir.IfNode;
  42 import jdk.nashorn.internal.ir.IndexNode;
  43 import jdk.nashorn.internal.ir.JumpToInlinedFinally;
  44 import jdk.nashorn.internal.ir.LexicalContext;
  45 import jdk.nashorn.internal.ir.LiteralNode;
  46 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
  47 import jdk.nashorn.internal.ir.Node;
  48 import jdk.nashorn.internal.ir.ObjectNode;
  49 import jdk.nashorn.internal.ir.PropertyNode;
  50 import jdk.nashorn.internal.ir.ReturnNode;
  51 import jdk.nashorn.internal.ir.RuntimeNode;
  52 import jdk.nashorn.internal.ir.SplitNode;
  53 import jdk.nashorn.internal.ir.Splittable;
  54 import jdk.nashorn.internal.ir.SwitchNode;
  55 import jdk.nashorn.internal.ir.ThrowNode;
  56 import jdk.nashorn.internal.ir.TryNode;
  57 import jdk.nashorn.internal.ir.UnaryNode;
  58 import jdk.nashorn.internal.ir.VarNode;
  59 import jdk.nashorn.internal.ir.WhileNode;
  60 import jdk.nashorn.internal.ir.WithNode;
  61 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
  62 
  63 
  64 /**
  65  * Computes the "byte code" weight of an AST segment. This is used
  66  * for Splitting too large class files
  67  */
  68 final class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
  69     /*
  70      * Weight constants.
  71      */
  72     static final long FUNCTION_WEIGHT  = 40;
  73     static final long AASTORE_WEIGHT   =  2;
  74     static final long ACCESS_WEIGHT    =  4;
  75     static final long ADD_WEIGHT       = 10;
  76     static final long BREAK_WEIGHT     =  1;
  77     static final long CALL_WEIGHT      = 10;
  78     static final long CATCH_WEIGHT     = 10;
  79     static final long COMPARE_WEIGHT   =  6;
  80     static final long CONTINUE_WEIGHT  =  1;
  81     static final long IF_WEIGHT        =  2;
  82     static final long LITERAL_WEIGHT   = 10;
  83     static final long LOOP_WEIGHT      =  4;
  84     static final long NEW_WEIGHT       =  6;
  85     static final long FUNC_EXPR_WEIGHT = 20;
  86     static final long RETURN_WEIGHT    =  2;
  87     static final long SPLIT_WEIGHT     = 40;
  88     static final long SWITCH_WEIGHT    =  8;
  89     static final long THROW_WEIGHT     =  2;
  90     static final long VAR_WEIGHT       = 40;
  91     static final long WITH_WEIGHT      =  8;
  92     static final long OBJECT_WEIGHT    = 16;
  93     static final long SETPROP_WEIGHT   =  5;
  94 
  95     /** Accumulated weight. */
  96     private long weight;
  97 
  98     /** Optional cache for weight of block nodes. */
  99     private final Map<Node, Long> weightCache;
 100 
 101     private final FunctionNode topFunction;
 102 
 103     /**
 104      * Constructor
 105      *
 106      * @param weightCache cache of already calculated block weights
 107      */
 108     private WeighNodes(final FunctionNode topFunction, final Map<Node, Long> weightCache) {
 109         super(new LexicalContext());
 110         this.topFunction = topFunction;
 111         this.weightCache = weightCache;
 112     }
 113 
 114     static long weigh(final Node node) {
 115         return weigh(node, null);
 116     }
 117 
 118     static long weigh(final Node node, final Map<Node, Long> weightCache) {
 119         final WeighNodes weighNodes = new WeighNodes(node instanceof FunctionNode ? (FunctionNode)node : null, weightCache);
 120         node.accept(weighNodes);
 121         return weighNodes.weight;
 122     }
 123 
 124     @Override
 125     public Node leaveAccessNode(final AccessNode accessNode) {
 126         weight += ACCESS_WEIGHT;
 127         return accessNode;
 128     }
 129 
 130     @Override
 131     public boolean enterBlock(final Block block) {
 132         if (weightCache != null && weightCache.containsKey(block)) {
 133             weight += weightCache.get(block);
 134             return false;
 135         }
 136 
 137         return true;
 138     }
 139 
 140     @Override
 141     public Node leaveBreakNode(final BreakNode breakNode) {
 142         weight += BREAK_WEIGHT;
 143         return breakNode;
 144     }
 145 
 146     @Override
 147     public Node leaveCallNode(final CallNode callNode) {
 148         weight += CALL_WEIGHT;
 149         return callNode;
 150     }
 151 
 152     @Override
 153     public Node leaveCatchNode(final CatchNode catchNode) {
 154         weight += CATCH_WEIGHT;
 155         return catchNode;
 156     }
 157 
 158     @Override
 159     public Node leaveContinueNode(final ContinueNode continueNode) {
 160         weight += CONTINUE_WEIGHT;
 161         return continueNode;
 162     }
 163 
 164     @Override
 165     public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) {
 166         return expressionStatement;
 167     }
 168 
 169     @Override
 170     public Node leaveForNode(final ForNode forNode) {
 171         weight += LOOP_WEIGHT;
 172         return forNode;
 173     }
 174 
 175     @Override
 176     public boolean enterFunctionNode(final FunctionNode functionNode) {
 177         if (functionNode == topFunction) {
 178             // the function being weighted; descend into its statements
 179             return true;
 180         }
 181         // just a reference to inner function from outer function
 182         weight += FUNC_EXPR_WEIGHT;
 183         return false;
 184     }
 185 
 186     @Override
 187     public Node leaveIdentNode(final IdentNode identNode) {
 188         weight += ACCESS_WEIGHT + identNode.getName().length() * 2;
 189         return identNode;
 190     }
 191 
 192     @Override
 193     public Node leaveIfNode(final IfNode ifNode) {
 194         weight += IF_WEIGHT;
 195         return ifNode;
 196     }
 197 
 198     @Override
 199     public Node leaveIndexNode(final IndexNode indexNode) {
 200         weight += ACCESS_WEIGHT;
 201         return indexNode;
 202     }
 203 
 204     @Override
 205     public Node leaveJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) {
 206         weight += BREAK_WEIGHT;
 207         return jumpToInlinedFinally;
 208     }
 209 
 210     @SuppressWarnings("rawtypes")
 211     @Override
 212     public boolean enterLiteralNode(final LiteralNode literalNode) {
 213         weight += LITERAL_WEIGHT;
 214 
 215         if (literalNode instanceof ArrayLiteralNode) {
 216             final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
 217             final Node[]           value            = arrayLiteralNode.getValue();
 218             final int[]            postsets         = arrayLiteralNode.getPostsets();
 219             final List<Splittable.SplitRange>  units            = arrayLiteralNode.getSplitRanges();
 220 
 221             if (units == null) {
 222                 for (final int postset : postsets) {
 223                     weight += AASTORE_WEIGHT;
 224                     final Node element = value[postset];
 225 
 226                     if (element != null) {
 227                         element.accept(this);
 228                     }
 229                 }
 230             }
 231 
 232             return false;
 233         }
 234 
 235         return true;
 236     }
 237 
 238     @Override
 239     public boolean enterObjectNode(final ObjectNode objectNode) {
 240         weight += OBJECT_WEIGHT;
 241         final List<PropertyNode> properties = objectNode.getElements();
 242         final boolean isSpillObject = properties.size() > CodeGenerator.OBJECT_SPILL_THRESHOLD;
 243 
 244         for (final PropertyNode property : properties) {
 245             if (!LiteralNode.isConstant(property.getValue())) {
 246                 weight += SETPROP_WEIGHT;
 247                 property.getValue().accept(this);
 248             } else if (!isSpillObject) {
 249                 // constants in spill object are set via preset spill array,
 250                 // but fields objects need to set constants.
 251                 weight += SETPROP_WEIGHT;
 252             }
 253 
 254         }
 255 
 256         return false;
 257     }
 258 
 259     @Override
 260     public Node leavePropertyNode(final PropertyNode propertyNode) {
 261         weight += LITERAL_WEIGHT;
 262         return propertyNode;
 263     }
 264 
 265     @Override
 266     public Node leaveReturnNode(final ReturnNode returnNode) {
 267         weight += RETURN_WEIGHT;
 268         return returnNode;
 269     }
 270 
 271     @Override
 272     public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
 273         weight += CALL_WEIGHT;
 274         return runtimeNode;
 275     }
 276 
 277     @Override
 278     public boolean enterSplitNode(final SplitNode splitNode) {
 279         weight += SPLIT_WEIGHT;
 280         return false;
 281     }
 282 
 283     @Override
 284     public Node leaveSwitchNode(final SwitchNode switchNode) {
 285         weight += SWITCH_WEIGHT;
 286         return switchNode;
 287     }
 288 
 289     @Override
 290     public Node leaveThrowNode(final ThrowNode throwNode) {
 291         weight += THROW_WEIGHT;
 292         return throwNode;
 293     }
 294 
 295     @Override
 296     public Node leaveTryNode(final TryNode tryNode) {
 297         weight += THROW_WEIGHT;
 298         return tryNode;
 299     }
 300 
 301     @Override
 302     public Node leaveVarNode(final VarNode varNode) {
 303         weight += VAR_WEIGHT;
 304         return varNode;
 305     }
 306 
 307     @Override
 308     public Node leaveWhileNode(final WhileNode whileNode) {
 309         weight += LOOP_WEIGHT;
 310         return whileNode;
 311     }
 312 
 313     @Override
 314     public Node leaveWithNode(final WithNode withNode) {
 315         weight += WITH_WEIGHT;
 316         return withNode;
 317     }
 318 
 319     @Override
 320     public Node leaveADD(final UnaryNode unaryNode) {
 321         return unaryNodeWeight(unaryNode);
 322     }
 323 
 324     @Override
 325     public Node leaveBIT_NOT(final UnaryNode unaryNode) {
 326         return unaryNodeWeight(unaryNode);
 327     }
 328 
 329     @Override
 330     public Node leaveDECINC(final UnaryNode unaryNode) {
 331          return unaryNodeWeight(unaryNode);
 332     }
 333 
 334     @Override
 335     public Node leaveDELETE(final UnaryNode unaryNode) {
 336         return runtimeNodeWeight(unaryNode);
 337     }
 338 
 339     @Override
 340     public Node leaveNEW(final UnaryNode unaryNode) {
 341         weight += NEW_WEIGHT;
 342         return unaryNode;
 343     }
 344 
 345     @Override
 346     public Node leaveNOT(final UnaryNode unaryNode) {
 347         return unaryNodeWeight(unaryNode);
 348     }
 349 
 350     @Override
 351     public Node leaveSUB(final UnaryNode unaryNode) {
 352         return unaryNodeWeight(unaryNode);
 353     }
 354 
 355     @Override
 356     public Node leaveTYPEOF(final UnaryNode unaryNode) {
 357         return runtimeNodeWeight(unaryNode);
 358     }
 359 
 360     @Override
 361     public Node leaveVOID(final UnaryNode unaryNode) {
 362         return unaryNodeWeight(unaryNode);
 363     }
 364 
 365     @Override
 366     public Node leaveADD(final BinaryNode binaryNode) {
 367         weight += ADD_WEIGHT;
 368         return binaryNode;
 369     }
 370 
 371     @Override
 372     public Node leaveAND(final BinaryNode binaryNode) {
 373         return binaryNodeWeight(binaryNode);
 374     }
 375 
 376     @Override
 377     public Node leaveASSIGN(final BinaryNode binaryNode) {
 378         return binaryNodeWeight(binaryNode);
 379     }
 380 
 381     @Override
 382     public Node leaveASSIGN_ADD(final BinaryNode binaryNode) {
 383         weight += ADD_WEIGHT;
 384         return binaryNode;
 385     }
 386 
 387     @Override
 388     public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) {
 389         return binaryNodeWeight(binaryNode);
 390     }
 391 
 392     @Override
 393     public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) {
 394         return binaryNodeWeight(binaryNode);
 395     }
 396 
 397     @Override
 398     public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) {
 399         return binaryNodeWeight(binaryNode);
 400     }
 401 
 402     @Override
 403     public Node leaveASSIGN_DIV(final BinaryNode binaryNode) {
 404         return binaryNodeWeight(binaryNode);
 405     }
 406 
 407     @Override
 408     public Node leaveASSIGN_MOD(final BinaryNode binaryNode) {
 409         return binaryNodeWeight(binaryNode);
 410     }
 411 
 412     @Override
 413     public Node leaveASSIGN_MUL(final BinaryNode binaryNode) {
 414         return binaryNodeWeight(binaryNode);
 415     }
 416 
 417     @Override
 418     public Node leaveASSIGN_SAR(final BinaryNode binaryNode) {
 419         return binaryNodeWeight(binaryNode);
 420     }
 421 
 422     @Override
 423     public Node leaveASSIGN_SHL(final BinaryNode binaryNode) {
 424         return binaryNodeWeight(binaryNode);
 425     }
 426 
 427     @Override
 428     public Node leaveASSIGN_SHR(final BinaryNode binaryNode) {
 429         return binaryNodeWeight(binaryNode);
 430     }
 431 
 432     @Override
 433     public Node leaveASSIGN_SUB(final BinaryNode binaryNode) {
 434         return binaryNodeWeight(binaryNode);
 435     }
 436 
 437     @Override
 438     public Node leaveARROW(final BinaryNode binaryNode) {
 439         return binaryNodeWeight(binaryNode);
 440     }
 441 
 442     @Override
 443     public Node leaveBIT_AND(final BinaryNode binaryNode) {
 444         return binaryNodeWeight(binaryNode);
 445     }
 446 
 447     @Override
 448     public Node leaveBIT_OR(final BinaryNode binaryNode) {
 449         return binaryNodeWeight(binaryNode);
 450     }
 451 
 452     @Override
 453     public Node leaveBIT_XOR(final BinaryNode binaryNode) {
 454         return binaryNodeWeight(binaryNode);
 455     }
 456 
 457     @Override
 458     public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
 459         return binaryNodeWeight(binaryNode);
 460     }
 461 
 462     @Override
 463     public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
 464         return binaryNodeWeight(binaryNode);
 465     }
 466 
 467     @Override
 468     public Node leaveDIV(final BinaryNode binaryNode) {
 469         return binaryNodeWeight(binaryNode);
 470     }
 471 
 472     @Override
 473     public Node leaveEQ(final BinaryNode binaryNode) {
 474         return compareWeight(binaryNode);
 475     }
 476 
 477     @Override
 478     public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
 479         return compareWeight(binaryNode);
 480     }
 481 
 482     @Override
 483     public Node leaveGE(final BinaryNode binaryNode) {
 484         return compareWeight(binaryNode);
 485     }
 486 
 487     @Override
 488     public Node leaveGT(final BinaryNode binaryNode) {
 489         return compareWeight(binaryNode);
 490     }
 491 
 492     @Override
 493     public Node leaveIN(final BinaryNode binaryNode) {
 494         weight += CALL_WEIGHT;
 495         return binaryNode;
 496     }
 497 
 498     @Override
 499     public Node leaveINSTANCEOF(final BinaryNode binaryNode) {
 500         weight += CALL_WEIGHT;
 501         return binaryNode;
 502     }
 503 
 504     @Override
 505     public Node leaveLE(final BinaryNode binaryNode) {
 506         return compareWeight(binaryNode);
 507     }
 508 
 509     @Override
 510     public Node leaveLT(final BinaryNode binaryNode) {
 511         return compareWeight(binaryNode);
 512     }
 513 
 514     @Override
 515     public Node leaveMOD(final BinaryNode binaryNode) {
 516         return binaryNodeWeight(binaryNode);
 517     }
 518 
 519     @Override
 520     public Node leaveMUL(final BinaryNode binaryNode) {
 521         return binaryNodeWeight(binaryNode);
 522     }
 523 
 524     @Override
 525     public Node leaveNE(final BinaryNode binaryNode) {
 526         return compareWeight(binaryNode);
 527     }
 528 
 529     @Override
 530     public Node leaveNE_STRICT(final BinaryNode binaryNode) {
 531         return compareWeight(binaryNode);
 532     }
 533 
 534     @Override
 535     public Node leaveOR(final BinaryNode binaryNode) {
 536         return binaryNodeWeight(binaryNode);
 537     }
 538 
 539     @Override
 540     public Node leaveSAR(final BinaryNode binaryNode) {
 541         return binaryNodeWeight(binaryNode);
 542     }
 543 
 544     @Override
 545     public Node leaveSHL(final BinaryNode binaryNode) {
 546         return binaryNodeWeight(binaryNode);
 547     }
 548 
 549     @Override
 550     public Node leaveSHR(final BinaryNode binaryNode) {
 551         return binaryNodeWeight(binaryNode);
 552     }
 553 
 554     @Override
 555     public Node leaveSUB(final BinaryNode binaryNode) {
 556         return binaryNodeWeight(binaryNode);
 557     }
 558 
 559     private Node unaryNodeWeight(final UnaryNode unaryNode) {
 560         weight += 1;
 561         return unaryNode;
 562     }
 563 
 564     private Node binaryNodeWeight(final BinaryNode binaryNode) {
 565         weight += 1;
 566         return binaryNode;
 567     }
 568 
 569     private Node runtimeNodeWeight(final UnaryNode unaryNode) {
 570         weight += CALL_WEIGHT;
 571         return unaryNode;
 572     }
 573 
 574     private Node compareWeight(final BinaryNode binaryNode) {
 575         weight += COMPARE_WEIGHT;
 576         return binaryNode;
 577     }
 578 }