1 /* 2 * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 /* 21 * $Id: MethodGenerator.java,v 1.2.4.1 2005/09/05 11:16:47 pvedula Exp $ 22 */ 23 24 package com.sun.org.apache.xalan.internal.xsltc.compiler.util; 25 26 import com.sun.org.apache.bcel.internal.Const; 27 import com.sun.org.apache.bcel.internal.classfile.Field; 28 import com.sun.org.apache.bcel.internal.classfile.Method; 29 import com.sun.org.apache.bcel.internal.generic.ALOAD; 30 import com.sun.org.apache.bcel.internal.generic.ASTORE; 31 import com.sun.org.apache.bcel.internal.generic.BranchHandle; 32 import com.sun.org.apache.bcel.internal.generic.BranchInstruction; 33 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen; 34 import com.sun.org.apache.bcel.internal.generic.DLOAD; 35 import com.sun.org.apache.bcel.internal.generic.DSTORE; 36 import com.sun.org.apache.bcel.internal.generic.FLOAD; 37 import com.sun.org.apache.bcel.internal.generic.FSTORE; 38 import com.sun.org.apache.bcel.internal.generic.GETFIELD; 39 import com.sun.org.apache.bcel.internal.generic.GOTO; 40 import com.sun.org.apache.bcel.internal.generic.ICONST; 41 import com.sun.org.apache.bcel.internal.generic.ILOAD; 42 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE; 43 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL; 44 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC; 45 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL; 46 import com.sun.org.apache.bcel.internal.generic.ISTORE; 47 import com.sun.org.apache.bcel.internal.generic.IfInstruction; 48 import com.sun.org.apache.bcel.internal.generic.IndexedInstruction; 49 import com.sun.org.apache.bcel.internal.generic.Instruction; 50 import com.sun.org.apache.bcel.internal.generic.InstructionConst; 51 import com.sun.org.apache.bcel.internal.generic.InstructionHandle; 52 import com.sun.org.apache.bcel.internal.generic.InstructionList; 53 import com.sun.org.apache.bcel.internal.generic.InstructionTargeter; 54 import com.sun.org.apache.bcel.internal.generic.LLOAD; 55 import com.sun.org.apache.bcel.internal.generic.LSTORE; 56 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen; 57 import com.sun.org.apache.bcel.internal.generic.LocalVariableInstruction; 58 import com.sun.org.apache.bcel.internal.generic.MethodGen; 59 import com.sun.org.apache.bcel.internal.generic.NEW; 60 import com.sun.org.apache.bcel.internal.generic.PUTFIELD; 61 import com.sun.org.apache.bcel.internal.generic.RET; 62 import com.sun.org.apache.bcel.internal.generic.Select; 63 import com.sun.org.apache.bcel.internal.generic.TargetLostException; 64 import com.sun.org.apache.bcel.internal.generic.Type; 65 import com.sun.org.apache.xalan.internal.xsltc.compiler.Pattern; 66 import com.sun.org.apache.xalan.internal.xsltc.compiler.XSLTC; 67 import java.util.ArrayList; 68 import java.util.Collections; 69 import java.util.HashMap; 70 import java.util.Iterator; 71 import java.util.List; 72 import java.util.Map; 73 import java.util.Stack; 74 75 /** 76 * @author Jacek Ambroziak 77 * @author Santiago Pericas-Geertsen 78 * @LastModified: July 2019 79 */ 80 public class MethodGenerator extends MethodGen 81 implements com.sun.org.apache.xalan.internal.xsltc.compiler.Constants { 82 protected static final int INVALID_INDEX = -1; 83 84 private static final String START_ELEMENT_SIG 85 = "(" + STRING_SIG + ")V"; 86 private static final String END_ELEMENT_SIG 87 = START_ELEMENT_SIG; 88 89 private static final int DOM_INDEX = 1; 90 private static final int ITERATOR_INDEX = 2; 91 private static final int HANDLER_INDEX = 3; 92 93 private static final int MAX_METHOD_SIZE = 65535; 94 private static final int MAX_BRANCH_TARGET_OFFSET = 32767; 95 private static final int MIN_BRANCH_TARGET_OFFSET = -32768; 96 97 private static final int TARGET_METHOD_SIZE = 60000; 98 private static final int MINIMUM_OUTLINEABLE_CHUNK_SIZE = 1000; 99 100 private Instruction _iloadCurrent; 101 private Instruction _istoreCurrent; 102 private final Instruction _astoreHandler; 103 private final Instruction _aloadHandler; 104 private final Instruction _astoreIterator; 105 private final Instruction _aloadIterator; 106 private final Instruction _aloadDom; 107 private final Instruction _astoreDom; 108 109 private final Instruction _startElement; 110 private final Instruction _endElement; 111 private final Instruction _startDocument; 112 private final Instruction _endDocument; 113 private final Instruction _attribute; 114 private final Instruction _uniqueAttribute; 115 private final Instruction _namespace; 116 117 private final Instruction _setStartNode; 118 private final Instruction _reset; 119 private final Instruction _nextNode; 120 121 private SlotAllocator _slotAllocator; 122 private boolean _allocatorInit = false; 123 private LocalVariableRegistry _localVariableRegistry; 124 /** 125 * A mapping between patterns and instruction lists used by 126 * test sequences to avoid compiling the same pattern multiple 127 * times. Note that patterns whose kernels are "*", "node()" 128 * and "@*" can between shared by test sequences. 129 */ 130 private Map<Pattern, InstructionList> _preCompiled = new HashMap<>(); 131 132 MethodGenerator(int access_flags, Type return_type, Type[] arg_types, String[] arg_names, String method_name, String class_name, InstructionList il, ConstantPoolGen cpg)133 public MethodGenerator(int access_flags, Type return_type, 134 Type[] arg_types, String[] arg_names, 135 String method_name, String class_name, 136 InstructionList il, ConstantPoolGen cpg) { 137 super(access_flags, return_type, arg_types, arg_names, method_name, 138 class_name, il, cpg); 139 140 _astoreHandler = new ASTORE(HANDLER_INDEX); 141 _aloadHandler = new ALOAD(HANDLER_INDEX); 142 _astoreIterator = new ASTORE(ITERATOR_INDEX); 143 _aloadIterator = new ALOAD(ITERATOR_INDEX); 144 _aloadDom = new ALOAD(DOM_INDEX); 145 _astoreDom = new ASTORE(DOM_INDEX); 146 147 final int startElement = 148 cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE, 149 "startElement", 150 START_ELEMENT_SIG); 151 _startElement = new INVOKEINTERFACE(startElement, 2); 152 153 final int endElement = 154 cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE, 155 "endElement", 156 END_ELEMENT_SIG); 157 _endElement = new INVOKEINTERFACE(endElement, 2); 158 159 final int attribute = 160 cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE, 161 "addAttribute", 162 "(" 163 + STRING_SIG 164 + STRING_SIG 165 + ")V"); 166 _attribute = new INVOKEINTERFACE(attribute, 3); 167 168 final int uniqueAttribute = 169 cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE, 170 "addUniqueAttribute", 171 "(" 172 + STRING_SIG 173 + STRING_SIG 174 + "I)V"); 175 _uniqueAttribute = new INVOKEINTERFACE(uniqueAttribute, 4); 176 177 final int namespace = 178 cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE, 179 "namespaceAfterStartElement", 180 "(" 181 + STRING_SIG 182 + STRING_SIG 183 + ")V"); 184 _namespace = new INVOKEINTERFACE(namespace, 3); 185 186 int index = cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE, 187 "startDocument", 188 "()V"); 189 _startDocument = new INVOKEINTERFACE(index, 1); 190 191 index = cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE, 192 "endDocument", 193 "()V"); 194 _endDocument = new INVOKEINTERFACE(index, 1); 195 196 197 index = cpg.addInterfaceMethodref(NODE_ITERATOR, 198 SET_START_NODE, 199 SET_START_NODE_SIG); 200 _setStartNode = new INVOKEINTERFACE(index, 2); 201 202 index = cpg.addInterfaceMethodref(NODE_ITERATOR, 203 "reset", "()"+NODE_ITERATOR_SIG); 204 _reset = new INVOKEINTERFACE(index, 1); 205 206 index = cpg.addInterfaceMethodref(NODE_ITERATOR, NEXT, NEXT_SIG); 207 _nextNode = new INVOKEINTERFACE(index, 1); 208 209 _slotAllocator = new SlotAllocator(); 210 _slotAllocator.initialize(getLocalVariableRegistry().getLocals()); 211 _allocatorInit = true; 212 } 213 214 /** 215 * Allocates a local variable. If the slot allocator has already been 216 * initialized, then call addLocalVariable2() so that the new variable 217 * is known to the allocator. Failing to do this may cause the allocator 218 * to return a slot that is already in use. 219 */ addLocalVariable(String name, Type type, InstructionHandle start, InstructionHandle end)220 public LocalVariableGen addLocalVariable(String name, Type type, 221 InstructionHandle start, 222 InstructionHandle end) 223 { 224 LocalVariableGen lvg; 225 226 if (_allocatorInit) { 227 lvg = addLocalVariable2(name, type, start); 228 } else { 229 lvg = super.addLocalVariable(name, type, start, end); 230 getLocalVariableRegistry().registerLocalVariable(lvg); 231 } 232 return lvg; 233 } 234 addLocalVariable2(String name, Type type, InstructionHandle start)235 public LocalVariableGen addLocalVariable2(String name, Type type, 236 InstructionHandle start) 237 { 238 LocalVariableGen lvg = super.addLocalVariable(name, type, 239 _slotAllocator.allocateSlot(type), 240 start, null); 241 getLocalVariableRegistry().registerLocalVariable(lvg); 242 return lvg; 243 } getLocalVariableRegistry()244 private LocalVariableRegistry getLocalVariableRegistry() { 245 if (_localVariableRegistry == null) { 246 _localVariableRegistry = new LocalVariableRegistry(); 247 } 248 249 return _localVariableRegistry; 250 } 251 252 /** 253 * Keeps track of all local variables used in the method. 254 * <p>The 255 * {@link MethodGen#addLocalVariable(String,Type,InstructionHandle,InstructionHandle)}</code> 256 * and 257 * {@link MethodGen#addLocalVariable(String,Type,int,InstructionHandle,InstructionHandle)}</code> 258 * methods of {@link MethodGen} will only keep track of 259 * {@link LocalVariableGen} object until it'ss removed by a call to 260 * {@link MethodGen#removeLocalVariable(LocalVariableGen)}.</p> 261 * <p>In order to support efficient copying of local variables to outlined 262 * methods by 263 * {@link #outline(InstructionHandle,InstructionHandle,String,ClassGenerator)}, 264 * this class keeps track of all local variables defined by the method.</p> 265 */ 266 protected class LocalVariableRegistry { 267 /** 268 * <p>A <code>java.lang.List</code> of all 269 * {@link LocalVariableGen}s created for this method, indexed by the 270 * slot number of the local variable. The JVM stack frame of local 271 * variables is divided into "slots". A single slot can be used to 272 * store more than one variable in a method, without regard to type, so 273 * long as the byte code keeps the ranges of the two disjoint.</p> 274 * <p>If only one registration of use of a particular slot occurs, the 275 * corresponding entry of <code>_variables</code> contains the 276 * <code>LocalVariableGen</code>; if more than one occurs, the 277 * corresponding entry contains all such <code>LocalVariableGen</code>s 278 * registered for the same slot; and if none occurs, the entry will be 279 * <code>null</code>. 280 */ 281 protected List<Object> _variables = new ArrayList<>(); 282 283 /** 284 * Maps a name to a {@link LocalVariableGen} 285 */ 286 protected Map<String, Object> _nameToLVGMap = new HashMap<>(); 287 288 /** 289 * Registers a {@link org.apache.bcel.generic.LocalVariableGen} 290 * for this method. 291 * <p><b>Preconditions:</b> 292 * <ul> 293 * <li>The range of instructions for <code>lvg</code> does not 294 * overlap with the range of instructions for any 295 * <code>LocalVariableGen</code> with the same slot index previously 296 * registered for this method. <b><em>(Unchecked.)</em></b></li> 297 * </ul></p> 298 * @param lvg The variable to be registered 299 */ 300 @SuppressWarnings("unchecked") registerLocalVariable(LocalVariableGen lvg)301 protected void registerLocalVariable(LocalVariableGen lvg) { 302 int slot = lvg.getIndex(); 303 304 int registrySize = _variables.size(); 305 306 // If the LocalVariableGen uses a slot index beyond any previously 307 // encountered, expand the _variables, padding with intervening null 308 // entries as required. 309 if (slot >= registrySize) { 310 for (int i = registrySize; i < slot; i++) { 311 _variables.add(null); 312 } 313 _variables.add(lvg); 314 } else { 315 // If the LocalVariableGen reuses a slot, make sure the entry 316 // in _variables contains an ArrayList and add the newly 317 // registered LocalVariableGen to the list. If the entry in 318 // _variables just contains null padding, store the 319 // LocalVariableGen directly. 320 Object localsInSlot = _variables.get(slot); 321 if (localsInSlot != null) { 322 if (localsInSlot instanceof LocalVariableGen) { 323 List<LocalVariableGen> listOfLocalsInSlot = new ArrayList<>(); 324 listOfLocalsInSlot.add((LocalVariableGen)localsInSlot); 325 listOfLocalsInSlot.add(lvg); 326 _variables.set(slot, listOfLocalsInSlot); 327 } else { 328 ((List<LocalVariableGen>) localsInSlot).add(lvg); 329 } 330 } else { 331 _variables.set(slot, lvg); 332 } 333 } 334 335 registerByName(lvg); 336 } 337 338 /** 339 * <p>Find which {@link LocalVariableGen}, if any, is registered for a 340 * particular JVM local stack frame slot at a particular position in the 341 * byte code for the method.</p> 342 * <p><b>Preconditions:</b> 343 * <ul> 344 * <li>The {@link InstructionList#setPositions()} has been called for 345 * the {@link InstructionList} associated with this 346 * {@link MethodGenerator}.</li> 347 * </ul></p> 348 * @param slot the JVM local stack frame slot number 349 * @param offset the position in the byte code 350 * @return the <code>LocalVariableGen</code> for the local variable 351 * stored in the relevant slot at the relevant offset; <code>null</code> 352 * if there is none. 353 */ lookupRegisteredLocalVariable(int slot, int offset)354 protected LocalVariableGen lookupRegisteredLocalVariable(int slot, 355 int offset) { 356 Object localsInSlot = (_variables != null) ? _variables.get(slot) 357 : null; 358 359 // If this slot index was never used, _variables.get will return 360 // null; if it was used once, it will return the LocalVariableGen; 361 // more than once it will return an ArrayList of all the 362 // LocalVariableGens for variables stored in that slot. For each 363 // LocalVariableGen, check whether its range includes the 364 // specified offset, and return the first such encountered. 365 if (localsInSlot != null) { 366 if (localsInSlot instanceof LocalVariableGen) { 367 LocalVariableGen lvg = (LocalVariableGen)localsInSlot; 368 if (offsetInLocalVariableGenRange(lvg, offset)) { 369 return lvg; 370 } 371 } else { 372 @SuppressWarnings("unchecked") 373 List<LocalVariableGen> listOfLocalsInSlot = 374 (List<LocalVariableGen>) localsInSlot; 375 376 for (LocalVariableGen lvg : listOfLocalsInSlot) { 377 if (offsetInLocalVariableGenRange(lvg, offset)) { 378 return lvg; 379 } 380 } 381 } 382 } 383 384 // No local variable stored in the specified slot at the specified 385 return null; 386 } 387 388 /** 389 * <p>Set up a mapping of the name of the specified 390 * {@link LocalVariableGen} object to the <code>LocalVariableGen</code> 391 * itself.</p> 392 * <p>This is a bit of a hack. XSLTC is relying on the fact that the 393 * name that is being looked up won't be duplicated, which isn't 394 * guaranteed. It replaces code which used to call 395 * {@link MethodGen#getLocalVariables()} and looped through the 396 * <code>LocalVariableGen</code> objects it contained to find the one 397 * with the specified name. However, <code>getLocalVariables()</code> 398 * has the side effect of setting the start and end for any 399 * <code>LocalVariableGen</code> which did not already have them 400 * set, which causes problems for outlining..</p> 401 * <p>See also {@link #lookUpByName(String)} and 402 * {@link #removeByNameTracking(LocalVariableGen)}</P 403 * @param lvg a <code>LocalVariableGen</code> 404 */ 405 @SuppressWarnings("unchecked") registerByName(LocalVariableGen lvg)406 protected void registerByName(LocalVariableGen lvg) { 407 Object duplicateNameEntry = _nameToLVGMap.get(lvg.getName()); 408 409 if (duplicateNameEntry == null) { 410 _nameToLVGMap.put(lvg.getName(), lvg); 411 } else { 412 List<LocalVariableGen> sameNameList; 413 414 if (duplicateNameEntry instanceof ArrayList) { 415 sameNameList = (List<LocalVariableGen>)duplicateNameEntry; 416 sameNameList.add(lvg); 417 } else { 418 sameNameList = new ArrayList<>(); 419 sameNameList.add((LocalVariableGen)duplicateNameEntry); 420 sameNameList.add(lvg); 421 } 422 423 _nameToLVGMap.put(lvg.getName(), sameNameList); 424 } 425 } 426 427 /** 428 * Remove the mapping from the name of the specified 429 * {@link LocalVariableGen} to itself. 430 * See also {@link #registerByName(LocalVariableGen)} and 431 * {@link #lookUpByName(String)} 432 * @param lvg a <code>LocalVariableGen</code> 433 */ 434 @SuppressWarnings("unchecked") removeByNameTracking(LocalVariableGen lvg)435 protected void removeByNameTracking(LocalVariableGen lvg) { 436 Object duplicateNameEntry = _nameToLVGMap.get(lvg.getName()); 437 438 if (duplicateNameEntry instanceof ArrayList) { 439 List<LocalVariableGen> sameNameList = 440 (List<LocalVariableGen>)duplicateNameEntry; 441 for (int i = 0; i < sameNameList.size(); i++) { 442 if (sameNameList.get(i) == lvg) { 443 sameNameList.remove(i); 444 break; 445 } 446 } 447 } else { 448 _nameToLVGMap.remove(lvg.getName()); 449 } 450 } 451 452 /** 453 * <p>Given the name of a variable, finds a {@link LocalVariableGen} 454 * corresponding to it.</p> 455 * <p>See also {@link #registerByName(LocalVariableGen)} and 456 * {@link #removeByNameTracking(LocalVariableGen)}</p> 457 * @param name 458 * @return 459 */ 460 @SuppressWarnings("unchecked") lookUpByName(String name)461 protected LocalVariableGen lookUpByName(String name) { 462 LocalVariableGen lvg = null; 463 Object duplicateNameEntry = _nameToLVGMap.get(name); 464 465 if (duplicateNameEntry instanceof ArrayList) { 466 List<LocalVariableGen> sameNameList = 467 (List<LocalVariableGen>)duplicateNameEntry; 468 469 for (int i = 0; i < sameNameList.size(); i++) { 470 lvg = sameNameList.get(i); 471 if (lvg.getName() == null ? name == null : lvg.getName().equals(name)) { 472 break; 473 } 474 } 475 } else { 476 lvg = (LocalVariableGen) duplicateNameEntry; 477 } 478 479 return lvg; 480 } 481 482 /** 483 * Gets all {@link LocalVariableGen} objects. 484 * This method replaces {@link MethodGen#getLocalVariables()} which has 485 * a side-effect of setting the start and end range for any 486 * {@code LocalVariableGen} if either was {@code null}. That 487 * side-effect causes problems for outlining of code in XSLTC. 488 * 489 * @return an array of {@code LocalVariableGen} containing all the 490 * local variables 491 */ 492 @SuppressWarnings("unchecked") getLocals()493 private LocalVariableGen[] getLocals() { 494 LocalVariableGen[] locals = null; 495 List<LocalVariableGen> allVarsEverDeclared = new ArrayList<>(); 496 497 for (Map.Entry<String, Object> nameVarsPair : _nameToLVGMap.entrySet()) { 498 Object vars = nameVarsPair.getValue(); 499 if (vars != null) { 500 if (vars instanceof ArrayList) { 501 List<LocalVariableGen> varsList = 502 (List<LocalVariableGen>) vars; 503 for (int i = 0; i < varsList.size(); i++) { 504 allVarsEverDeclared.add(varsList.get(i)); 505 } 506 } else { 507 allVarsEverDeclared.add((LocalVariableGen)vars); 508 } 509 } 510 } 511 512 locals = new LocalVariableGen[allVarsEverDeclared.size()]; 513 allVarsEverDeclared.toArray(locals); 514 515 return locals; 516 } 517 } 518 519 /** 520 * Determines whether a particular variable is in use at a particular offset 521 * in the byte code for this method. 522 * <p><b>Preconditions:</b> 523 * <ul> 524 * <li>The {@link InstructionList#setPositions()} has been called for the 525 * {@link InstructionList} associated with this {@link MethodGenerator}. 526 * </li></ul></p> 527 * @param lvg the {@link LocalVariableGen} for the variable 528 * @param offset the position in the byte code 529 * @return <code>true</code> if and only if the specified variable is in 530 * use at the particular byte code offset. 531 */ offsetInLocalVariableGenRange(LocalVariableGen lvg, int offset)532 boolean offsetInLocalVariableGenRange(LocalVariableGen lvg, int offset) { 533 InstructionHandle lvgStart = lvg.getStart(); 534 InstructionHandle lvgEnd = lvg.getEnd(); 535 536 // If no start handle is recorded for the LocalVariableGen, it is 537 // assumed to be in use from the beginning of the method. 538 if (lvgStart == null) { 539 lvgStart = getInstructionList().getStart(); 540 } 541 542 // If no end handle is recorded for the LocalVariableGen, it is assumed 543 // to be in use to the end of the method. 544 if (lvgEnd == null) { 545 lvgEnd = getInstructionList().getEnd(); 546 } 547 548 // Does the range of the instruction include the specified offset? 549 // Note that the InstructionHandle.getPosition method returns the 550 // offset of the beginning of an instruction. A LocalVariableGen's 551 // range includes the end instruction itself, so that instruction's 552 // length must be taken into consideration in computing whether the 553 // varible is in range at a particular offset. 554 return ((lvgStart.getPosition() <= offset) 555 && (lvgEnd.getPosition() 556 + lvgEnd.getInstruction().getLength() >= offset)); 557 } 558 removeLocalVariable(LocalVariableGen lvg)559 public void removeLocalVariable(LocalVariableGen lvg) { 560 _slotAllocator.releaseSlot(lvg); 561 getLocalVariableRegistry().removeByNameTracking(lvg); 562 super.removeLocalVariable(lvg); 563 } 564 loadDOM()565 public Instruction loadDOM() { 566 return _aloadDom; 567 } 568 storeDOM()569 public Instruction storeDOM() { 570 return _astoreDom; 571 } 572 storeHandler()573 public Instruction storeHandler() { 574 return _astoreHandler; 575 } 576 loadHandler()577 public Instruction loadHandler() { 578 return _aloadHandler; 579 } 580 storeIterator()581 public Instruction storeIterator() { 582 return _astoreIterator; 583 } 584 loadIterator()585 public Instruction loadIterator() { 586 return _aloadIterator; 587 } 588 setStartNode()589 public final Instruction setStartNode() { 590 return _setStartNode; 591 } 592 reset()593 public final Instruction reset() { 594 return _reset; 595 } 596 nextNode()597 public final Instruction nextNode() { 598 return _nextNode; 599 } 600 startElement()601 public final Instruction startElement() { 602 return _startElement; 603 } 604 endElement()605 public final Instruction endElement() { 606 return _endElement; 607 } 608 startDocument()609 public final Instruction startDocument() { 610 return _startDocument; 611 } 612 endDocument()613 public final Instruction endDocument() { 614 return _endDocument; 615 } 616 attribute()617 public final Instruction attribute() { 618 return _attribute; 619 } 620 uniqueAttribute()621 public final Instruction uniqueAttribute() { 622 return _uniqueAttribute; 623 } 624 namespace()625 public final Instruction namespace() { 626 return _namespace; 627 } 628 loadCurrentNode()629 public Instruction loadCurrentNode() { 630 if (_iloadCurrent == null) { 631 int idx = getLocalIndex("current"); 632 if (idx > 0) 633 _iloadCurrent = new ILOAD(idx); 634 else 635 _iloadCurrent = new ICONST(0); 636 } 637 return _iloadCurrent; 638 } 639 storeCurrentNode()640 public Instruction storeCurrentNode() { 641 return _istoreCurrent != null 642 ? _istoreCurrent 643 : (_istoreCurrent = new ISTORE(getLocalIndex("current"))); 644 } 645 646 /** by default context node is the same as current node. MK437 */ loadContextNode()647 public Instruction loadContextNode() { 648 return loadCurrentNode(); 649 } 650 storeContextNode()651 public Instruction storeContextNode() { 652 return storeCurrentNode(); 653 } 654 getLocalIndex(String name)655 public int getLocalIndex(String name) { 656 return getLocalVariable(name).getIndex(); 657 } 658 getLocalVariable(String name)659 public LocalVariableGen getLocalVariable(String name) { 660 return getLocalVariableRegistry().lookUpByName(name); 661 } 662 setMaxLocals()663 public void setMaxLocals() { 664 665 // Get the current number of local variable slots 666 int maxLocals = super.getMaxLocals(); 667 int prevLocals = maxLocals; 668 669 // Get numer of actual variables 670 final LocalVariableGen[] localVars = super.getLocalVariables(); 671 if (localVars != null) { 672 if (localVars.length > maxLocals) 673 maxLocals = localVars.length; 674 } 675 676 // We want at least 5 local variable slots (for parameters) 677 if (maxLocals < 5) maxLocals = 5; 678 679 super.setMaxLocals(maxLocals); 680 } 681 682 /** 683 * Add a pre-compiled pattern to this mode. 684 */ addInstructionList(Pattern pattern, InstructionList ilist)685 public void addInstructionList(Pattern pattern, InstructionList ilist) { 686 _preCompiled.put(pattern, ilist); 687 } 688 689 /** 690 * Get the instruction list for a pre-compiled pattern. Used by 691 * test sequences to avoid compiling patterns more than once. 692 */ getInstructionList(Pattern pattern)693 public InstructionList getInstructionList(Pattern pattern) { 694 return _preCompiled.get(pattern); 695 } 696 697 /** 698 * Used to keep track of an outlineable chunk of instructions in the 699 * current method. See {@link OutlineableChunkStart} and 700 * {@link OutlineableChunkEnd} for more information. 701 */ 702 private class Chunk implements Comparable<Object> { 703 /** 704 * {@link InstructionHandle} of the first instruction in the outlineable 705 * chunk. 706 */ 707 private InstructionHandle m_start; 708 709 /** 710 * {@link org.apache.bcel.generic.InstructionHandle} of the first 711 * instruction in the outlineable chunk. 712 */ 713 private InstructionHandle m_end; 714 715 /** 716 * Number of bytes in the instructions contained in this outlineable 717 * chunk. 718 */ 719 private int m_size; 720 721 /** 722 * <p>Constructor for an outlineable {@link MethodGenerator.Chunk}.</p> 723 * <p><b>Preconditions:</b> 724 * <ul> 725 * <li>The {@link InstructionList#setPositions()} has been called for 726 * the {@link InstructionList} associated with this 727 * {@link MethodGenerator}.</li> 728 * </ul></p> 729 * @param start The {@link InstructionHandle} of the first 730 * instruction in the outlineable chunk. 731 * @param end The {@link InstructionHandle} of the last 732 * instruction in the outlineable chunk. 733 */ Chunk(InstructionHandle start, InstructionHandle end)734 Chunk(InstructionHandle start, InstructionHandle end) { 735 m_start = start; 736 m_end = end; 737 m_size = end.getPosition() - start.getPosition(); 738 } 739 740 /** 741 * Determines whether this outlineable {@link MethodGenerator.Chunk} is 742 * followed immediately by the argument 743 * <code>MethodGenerator.Chunk</code>, with no other intervening 744 * instructions, including {@link OutlineableChunkStart} or 745 * {@link OutlineableChunkEnd} instructions. 746 * @param neighbour an outlineable {@link MethodGenerator.Chunk} 747 * @return <code>true</code> if and only if the argument chunk 748 * immediately follows <code>this</code> chunk 749 */ isAdjacentTo(Chunk neighbour)750 boolean isAdjacentTo(Chunk neighbour) { 751 return getChunkEnd().getNext() == neighbour.getChunkStart(); 752 } 753 754 /** 755 * Getter method for the start of this {@linke MethodGenerator.Chunk} 756 * @return the {@link org.apache.bcel.generic.InstructionHandle} of the 757 * start of this chunk 758 */ getChunkStart()759 InstructionHandle getChunkStart() { 760 return m_start; 761 } 762 763 /** 764 * Getter method for the end of this {@link MethodGenerator.Chunk} 765 * @return the {@link InstructionHandle} of the start of this chunk 766 */ getChunkEnd()767 InstructionHandle getChunkEnd() { 768 return m_end; 769 } 770 771 /** 772 * The size of this {@link MethodGenerator.Chunk} 773 * @return the number of bytes in the byte code represented by this 774 * chunk. 775 */ getChunkSize()776 int getChunkSize() { 777 return m_size; 778 } 779 780 /** 781 * Implements the <code>java.util.Comparable.compareTo(Object)</code> 782 * method. 783 * @return 784 * <ul> 785 * <li>A positive <code>int</code> if the length of <code>this</code> 786 * chunk in bytes is greater than that of <code>comparand</code></li> 787 * <li>A negative <code>int</code> if the length of <code>this</code> 788 * chunk in bytes is less than that of <code>comparand</code></li> 789 * <li>Zero, otherwise.</li> 790 * </ul> 791 */ compareTo(Object comparand)792 public int compareTo(Object comparand) { 793 return getChunkSize() - ((Chunk)comparand).getChunkSize(); 794 } 795 } 796 797 /** 798 * Find the outlineable chunks in this method that would be the best choices 799 * to outline, based on size and position in the method. 800 * @param classGen The {@link ClassGen} with which the generated methods 801 * will be associated 802 * @param totalMethodSize the size of the bytecode in the original method 803 * @return a <code>java.util.List</code> containing the 804 * {@link MethodGenerator.Chunk}s that may be outlined from this method 805 */ getCandidateChunks(ClassGenerator classGen, int totalMethodSize)806 private List<Chunk> getCandidateChunks(ClassGenerator classGen, 807 int totalMethodSize) { 808 Iterator<InstructionHandle> instructions = getInstructionList().iterator(); 809 List<Chunk> candidateChunks = new ArrayList<>(); 810 List<InstructionHandle> currLevelChunks = new ArrayList<>(); 811 Stack<List<InstructionHandle>> subChunkStack = new Stack<>(); 812 boolean openChunkAtCurrLevel = false; 813 boolean firstInstruction = true; 814 815 InstructionHandle currentHandle; 816 817 if (m_openChunks != 0) { 818 String msg = 819 (new ErrorMsg(ErrorMsg.OUTLINE_ERR_UNBALANCED_MARKERS)) 820 .toString(); 821 throw new InternalError(msg); 822 } 823 824 // Scan instructions in the method, keeping track of the nesting level 825 // of outlineable chunks. 826 // 827 // currLevelChunks 828 // keeps track of the child chunks of a chunk. For each chunk, 829 // there will be a pair of entries: the InstructionHandles for the 830 // start and for the end of the chunk 831 // subChunkStack 832 // a stack containing the partially accumulated currLevelChunks for 833 // each chunk that's still open at the current position in the 834 // InstructionList. 835 // candidateChunks 836 // the list of chunks which have been accepted as candidates chunks 837 // for outlining 838 do { 839 // Get the next instruction. The loop will perform one extra 840 // iteration after it reaches the end of the InstructionList, with 841 // currentHandle set to null. 842 currentHandle = instructions.hasNext() 843 ? instructions.next() 844 : null; 845 Instruction inst = 846 (currentHandle != null) ? currentHandle.getInstruction() 847 : null; 848 849 // At the first iteration, create a chunk representing all the 850 // code in the method. This is done just to simplify the logic - 851 // this chunk can never be outlined because it will be too big. 852 if (firstInstruction) { 853 openChunkAtCurrLevel = true; 854 currLevelChunks.add(currentHandle); 855 firstInstruction = false; 856 } 857 858 // Found a new chunk 859 if (inst instanceof OutlineableChunkStart) { 860 // If last MarkerInstruction encountered was an 861 // OutlineableChunkStart, this represents the first chunk 862 // nested within that previous chunk - push the list of chunks 863 // from the outer level onto the stack 864 if (openChunkAtCurrLevel) { 865 subChunkStack.push(currLevelChunks); 866 currLevelChunks = new ArrayList<>(); 867 } 868 869 openChunkAtCurrLevel = true; 870 currLevelChunks.add(currentHandle); 871 // Close off an open chunk 872 } else if (currentHandle == null 873 || inst instanceof OutlineableChunkEnd) { 874 List<InstructionHandle> nestedSubChunks = null; 875 876 // If the last MarkerInstruction encountered was an 877 // OutlineableChunkEnd, it means that the current instruction 878 // marks the end of a chunk that contained child chunks. 879 // Those children might need to be examined below in case they 880 // are better candidates for outlining than the current chunk. 881 if (!openChunkAtCurrLevel) { 882 nestedSubChunks = currLevelChunks; 883 currLevelChunks = subChunkStack.pop(); 884 } 885 886 // Get the handle for the start of this chunk (the last entry 887 // in currLevelChunks) 888 InstructionHandle chunkStart = 889 currLevelChunks.get(currLevelChunks.size()-1); 890 891 int chunkEndPosition = 892 (currentHandle != null) ? currentHandle.getPosition() 893 : totalMethodSize; 894 int chunkSize = chunkEndPosition - chunkStart.getPosition(); 895 896 // Two ranges of chunk size to consider: 897 // 898 // 1. [0,TARGET_METHOD_SIZE] 899 // Keep this chunk in consideration as a candidate, 900 // and ignore its subchunks, if any - there's nothing to be 901 // gained by outlining both the current chunk and its 902 // children! 903 // 904 // 2. (TARGET_METHOD_SIZE,+infinity) 905 // Ignore this chunk - it's too big. Add its subchunks 906 // as candidates, after merging adjacent chunks to produce 907 // chunks that are as large as possible 908 if (chunkSize <= TARGET_METHOD_SIZE) { 909 currLevelChunks.add(currentHandle); 910 } else { 911 if (!openChunkAtCurrLevel) { 912 int childChunkCount = nestedSubChunks.size() / 2; 913 if (childChunkCount > 0) { 914 Chunk[] childChunks = new Chunk[childChunkCount]; 915 916 // Gather all the child chunks of the current chunk 917 for (int i = 0; i < childChunkCount; i++) { 918 InstructionHandle start = nestedSubChunks.get(i*2); 919 InstructionHandle end = nestedSubChunks.get(i*2+1); 920 921 childChunks[i] = new Chunk(start, end); 922 } 923 924 // Merge adjacent siblings 925 List<Chunk> mergedChildChunks = 926 mergeAdjacentChunks(childChunks); 927 928 // Add chunks that mean minimum size requirements 929 // to the list of candidate chunks for outlining 930 for (Chunk mergedChunk : mergedChildChunks) { 931 int mergedSize = mergedChunk.getChunkSize(); 932 933 if (mergedSize >= MINIMUM_OUTLINEABLE_CHUNK_SIZE 934 && mergedSize <= TARGET_METHOD_SIZE) { 935 candidateChunks.add(mergedChunk); 936 } 937 } 938 } 939 } 940 941 // Drop the chunk which was too big 942 currLevelChunks.remove(currLevelChunks.size() - 1); 943 } 944 945 // currLevelChunks contains pairs of InstructionHandles. If 946 // its size is an odd number, the loop has encountered the 947 // start of a chunk at this level, but not its end. 948 openChunkAtCurrLevel = ((currLevelChunks.size() & 0x1) == 1); 949 } 950 951 } while (currentHandle != null); 952 953 return candidateChunks; 954 } 955 956 /** 957 * Merge adjacent sibling chunks to produce larger candidate chunks for 958 * outlining 959 * @param chunks array of sibling {@link MethodGenerator.Chunk}s that are 960 * under consideration for outlining. Chunks must be in 961 * the order encountered in the {@link InstructionList} 962 * @return a <code>java.util.List</code> of 963 * <code>MethodGenerator.Chunk</code>s maximally merged 964 */ mergeAdjacentChunks(Chunk[] chunks)965 private List<Chunk> mergeAdjacentChunks(Chunk[] chunks) { 966 int[] adjacencyRunStart = new int[chunks.length]; 967 int[] adjacencyRunLength = new int[chunks.length]; 968 boolean[] chunkWasMerged = new boolean[chunks.length]; 969 970 int maximumRunOfChunks = 0; 971 int startOfCurrentRun; 972 int numAdjacentRuns = 0; 973 974 List<Chunk> mergedChunks = new ArrayList<>(); 975 976 startOfCurrentRun = 0; 977 978 // Loop through chunks, and record in adjacencyRunStart where each 979 // run of adjacent chunks begins and how many are in that run. For 980 // example, given chunks A B C D E F, if A is adjacent to B, but not 981 // to C, and C, D, E and F are all adjacent, 982 // adjacencyRunStart[0] == 0; adjacencyRunLength[0] == 2 983 // adjacencyRunStart[1] == 2; adjacencyRunLength[1] == 4 984 for (int i = 1; i < chunks.length; i++) { 985 if (!chunks[i-1].isAdjacentTo(chunks[i])) { 986 int lengthOfRun = i - startOfCurrentRun; 987 988 // Track the longest run of chunks found 989 if (maximumRunOfChunks < lengthOfRun) { 990 maximumRunOfChunks = lengthOfRun; 991 } 992 993 if (lengthOfRun > 1 ) { 994 adjacencyRunLength[numAdjacentRuns] = lengthOfRun; 995 adjacencyRunStart[numAdjacentRuns] = startOfCurrentRun; 996 numAdjacentRuns++; 997 } 998 999 startOfCurrentRun = i; 1000 } 1001 } 1002 1003 if (chunks.length - startOfCurrentRun > 1) { 1004 int lengthOfRun = chunks.length - startOfCurrentRun; 1005 1006 // Track the longest run of chunks found 1007 if (maximumRunOfChunks < lengthOfRun) { 1008 maximumRunOfChunks = lengthOfRun; 1009 } 1010 1011 adjacencyRunLength[numAdjacentRuns] = 1012 chunks.length - startOfCurrentRun; 1013 adjacencyRunStart[numAdjacentRuns] = startOfCurrentRun; 1014 numAdjacentRuns++; 1015 } 1016 1017 // Try merging adjacent chunks to come up with better sized chunks for 1018 // outlining. This algorithm is not optimal, but it should be 1019 // reasonably fast. Consider an example like this, where four chunks 1020 // of the sizes specified in brackets are adjacent. The best way of 1021 // combining these chunks would be to merge the first pair and merge 1022 // the last three to form two chunks, but the algorithm will merge the 1023 // three in the middle instead, leaving three chunks in all. 1024 // [25000] [25000] [20000] [1000] [20000] 1025 1026 // Start by trying to merge the maximum number of adjacent chunks, and 1027 // work down from there. 1028 for (int numToMerge = maximumRunOfChunks; numToMerge>1; numToMerge--) { 1029 // Look at each run of adjacent chunks 1030 for (int run = 0; run < numAdjacentRuns; run++) { 1031 int runStart = adjacencyRunStart[run]; 1032 int runEnd = runStart + adjacencyRunLength[run] - 1; 1033 1034 boolean foundChunksToMerge = false; 1035 1036 // Within the current run of adjacent chunks, look at all 1037 // "subruns" of length numToMerge, until we run out or find 1038 // a subrun that can be merged. 1039 for (int mergeStart = runStart; 1040 mergeStart+numToMerge-1 <= runEnd && !foundChunksToMerge; 1041 mergeStart++) { 1042 int mergeEnd = mergeStart + numToMerge - 1; 1043 int mergeSize = 0; 1044 1045 // Find out how big the subrun is 1046 for (int j = mergeStart; j <= mergeEnd; j++) { 1047 mergeSize = mergeSize + chunks[j].getChunkSize(); 1048 } 1049 1050 // If the current subrun is small enough to outline, 1051 // merge it, and split the remaining chunks in the run 1052 if (mergeSize <= TARGET_METHOD_SIZE) { 1053 foundChunksToMerge = true; 1054 1055 for (int j = mergeStart; j <= mergeEnd; j++) { 1056 chunkWasMerged[j] = true; 1057 } 1058 1059 mergedChunks.add( 1060 new Chunk(chunks[mergeStart].getChunkStart(), 1061 chunks[mergeEnd].getChunkEnd())); 1062 1063 // Adjust the length of the current run of adjacent 1064 // chunks to end at the newly merged chunk... 1065 adjacencyRunLength[run] = 1066 adjacencyRunStart[run] - mergeStart; 1067 1068 int trailingRunLength = runEnd - mergeEnd; 1069 1070 // and any chunks that follow the newly merged chunk 1071 // in the current run of adjacent chunks form another 1072 // new run of adjacent chunks 1073 if (trailingRunLength >= 2) { 1074 adjacencyRunStart[numAdjacentRuns] = mergeEnd + 1; 1075 adjacencyRunLength[numAdjacentRuns] = 1076 trailingRunLength; 1077 numAdjacentRuns++; 1078 } 1079 } 1080 } 1081 } 1082 } 1083 1084 // Make a final pass for any chunk that wasn't merged with a sibling 1085 // and include it in the list of chunks after merging. 1086 for (int i = 0; i < chunks.length; i++) { 1087 if (!chunkWasMerged[i]) { 1088 mergedChunks.add(chunks[i]); 1089 } 1090 } 1091 1092 return mergedChunks; 1093 } 1094 1095 /** 1096 * Breaks up the IL for this {@link MethodGenerator} into separate 1097 * outlined methods so that no method exceeds the 64KB limit on the length 1098 * of the byte code associated with a method. 1099 * @param classGen The {@link ClassGen} with which the generated methods 1100 * will be associated 1101 * @param originalMethodSize The number of bytes of bytecode represented by 1102 * the {@link InstructionList} of this method 1103 * @return an array of the outlined <code>Method</code>s and the original 1104 * method itself 1105 */ outlineChunks(ClassGenerator classGen, int originalMethodSize)1106 public Method[] outlineChunks(ClassGenerator classGen, 1107 int originalMethodSize) { 1108 List<Method> methodsOutlined = new ArrayList<>(); 1109 int currentMethodSize = originalMethodSize; 1110 1111 int outlinedCount = 0; 1112 boolean moreMethodsOutlined; 1113 String originalMethodName = getName(); 1114 1115 // Special handling for initialization methods. No other methods can 1116 // include the less than and greater than characters in their names, 1117 // so we munge the names here. 1118 if (originalMethodName.equals("<init>")) { 1119 originalMethodName = "$lt$init$gt$"; 1120 } else if (originalMethodName.equals("<clinit>")) { 1121 originalMethodName = "$lt$clinit$gt$"; 1122 } 1123 1124 // Loop until the original method comes in under the JVM limit or 1125 // the loop was unable to outline any more methods 1126 do { 1127 // Get all the best candidates for outlining, and sort them in 1128 // ascending order of size 1129 List<Chunk> candidateChunks = getCandidateChunks(classGen, 1130 currentMethodSize); 1131 Collections.sort(candidateChunks); 1132 1133 moreMethodsOutlined = false; 1134 1135 // Loop over the candidates for outlining, from the largest to the 1136 // smallest and outline them one at a time, until the loop has 1137 // outlined all or the original method comes in under the JVM 1138 // limit on the size of a method. 1139 for (int i = candidateChunks.size()-1; 1140 i >= 0 && currentMethodSize > TARGET_METHOD_SIZE; 1141 i--) { 1142 Chunk chunkToOutline = candidateChunks.get(i); 1143 1144 methodsOutlined.add(outline(chunkToOutline.getChunkStart(), 1145 chunkToOutline.getChunkEnd(), 1146 originalMethodName + "$outline$" 1147 + outlinedCount, 1148 classGen)); 1149 outlinedCount++; 1150 moreMethodsOutlined = true; 1151 1152 InstructionList il = getInstructionList(); 1153 InstructionHandle lastInst = il.getEnd(); 1154 il.setPositions(); 1155 1156 // Check the size of the method now 1157 currentMethodSize = 1158 lastInst.getPosition() 1159 + lastInst.getInstruction().getLength(); 1160 } 1161 } while (moreMethodsOutlined && currentMethodSize > TARGET_METHOD_SIZE); 1162 1163 // Outlining failed to reduce the size of the current method 1164 // sufficiently. Throw an internal error. 1165 if (currentMethodSize > MAX_METHOD_SIZE) { 1166 String msg = (new ErrorMsg(ErrorMsg.OUTLINE_ERR_METHOD_TOO_BIG)) 1167 .toString(); 1168 throw new InternalError(msg); 1169 } 1170 1171 Method[] methodsArr = new Method[methodsOutlined.size() + 1]; 1172 methodsOutlined.toArray(methodsArr); 1173 1174 methodsArr[methodsOutlined.size()] = getThisMethod(); 1175 1176 return methodsArr; 1177 } 1178 1179 /** 1180 * Given an outlineable chunk of code in the current {@link MethodGenerator} 1181 * move ("outline") the chunk to a new method, and replace the chunk in the 1182 * old method with a reference to that new method. No 1183 * {@link OutlineableChunkStart} or {@link OutlineableChunkEnd} instructions 1184 * are copied. 1185 * @param first The {@link InstructionHandle} of the first instruction in 1186 * the chunk to outline 1187 * @param last The <code>InstructionHandle</code> of the last instruction in 1188 * the chunk to outline 1189 * @param outlinedMethodName The name of the new method 1190 * @param classGen The {@link ClassGenerator} of which the original 1191 * and new methods will be members 1192 * @return The new {@link Method} containing the outlined code. 1193 */ outline(InstructionHandle first, InstructionHandle last, String outlinedMethodName, ClassGenerator classGen)1194 private Method outline(InstructionHandle first, InstructionHandle last, 1195 String outlinedMethodName, ClassGenerator classGen) { 1196 // We're not equipped to deal with exception handlers yet. Bail out! 1197 if (getExceptionHandlers().length != 0) { 1198 String msg = (new ErrorMsg(ErrorMsg.OUTLINE_ERR_TRY_CATCH)) 1199 .toString(); 1200 throw new InternalError(msg); 1201 } 1202 1203 int outlineChunkStartOffset = first.getPosition(); 1204 int outlineChunkEndOffset = last.getPosition() 1205 + last.getInstruction().getLength(); 1206 1207 ConstantPoolGen cpg = getConstantPool(); 1208 1209 // Create new outlined method with signature: 1210 // 1211 // private final outlinedMethodName(CopyLocals copyLocals); 1212 // 1213 // CopyLocals is an object that is used to copy-in/copy-out local 1214 // variables that are used by the outlined method. Only locals whose 1215 // value is potentially set or referenced outside the range of the 1216 // chunk that is being outlined will be represented in CopyLocals. The 1217 // type of the variable for copying local variables is actually 1218 // generated to be unique - it is not named CopyLocals. 1219 // 1220 // The outlined method never needs to be referenced outside of this 1221 // class, and will never be overridden, so we mark it private final. 1222 final InstructionList newIL = new InstructionList(); 1223 1224 final XSLTC xsltc = classGen.getParser().getXSLTC(); 1225 final String argTypeName = xsltc.getHelperClassName(); 1226 final Type[] argTypes = 1227 new Type[] {(new ObjectType(argTypeName)).toJCType()}; 1228 final String argName = "copyLocals"; 1229 final String[] argNames = new String[] {argName}; 1230 1231 int methodAttributes = ACC_PRIVATE | ACC_FINAL; 1232 final boolean isStaticMethod = (getAccessFlags() & ACC_STATIC) != 0; 1233 1234 if (isStaticMethod) { 1235 methodAttributes = methodAttributes | ACC_STATIC; 1236 } 1237 1238 final MethodGenerator outlinedMethodGen = 1239 new MethodGenerator(methodAttributes, 1240 com.sun.org.apache.bcel.internal.generic.Type.VOID, 1241 argTypes, argNames, outlinedMethodName, 1242 getClassName(), newIL, cpg); 1243 1244 // Create class for copying local variables to the outlined method. 1245 // The fields the class will need to contain will be determined as the 1246 // code in the outlineable chunk is examined. 1247 ClassGenerator copyAreaCG 1248 = new ClassGenerator(argTypeName, OBJECT_CLASS, argTypeName+".java", 1249 ACC_FINAL | ACC_PUBLIC | ACC_SUPER, null, 1250 classGen.getStylesheet()) { 1251 public boolean isExternal() { 1252 return true; 1253 } 1254 }; 1255 ConstantPoolGen copyAreaCPG = copyAreaCG.getConstantPool(); 1256 copyAreaCG.addEmptyConstructor(ACC_PUBLIC); 1257 1258 // Number of fields in the copy class 1259 int copyAreaFieldCount = 0; 1260 1261 // The handle for the instruction after the last one to be outlined. 1262 // Note that this should never end up being null. An outlineable chunk 1263 // won't contain a RETURN instruction or other branch out of the chunk, 1264 // and the JVM specification prohibits code in a method from just 1265 // "falling off the end" so this should always point to a valid handle. 1266 InstructionHandle limit = last.getNext(); 1267 1268 // InstructionLists for copying values into and out of an instance of 1269 // CopyLocals: 1270 // oldMethCoypInIL - from locals in old method into an instance 1271 // of the CopyLocals class (oldMethCopyInIL) 1272 // oldMethCopyOutIL - from CopyLocals back into locals in the old 1273 // method 1274 // newMethCopyInIL - from CopyLocals into locals in the new 1275 // method 1276 // newMethCopyOutIL - from locals in new method into the instance 1277 // of the CopyLocals class 1278 InstructionList oldMethCopyInIL = new InstructionList(); 1279 InstructionList oldMethCopyOutIL = new InstructionList(); 1280 InstructionList newMethCopyInIL = new InstructionList(); 1281 InstructionList newMethCopyOutIL = new InstructionList(); 1282 1283 // Allocate instance of class in which we'll copy in or copy out locals 1284 // and make two copies: last copy is used to invoke constructor; 1285 // other two are used for references to fields in the CopyLocals object 1286 InstructionHandle outlinedMethodCallSetup = 1287 oldMethCopyInIL.append(new NEW(cpg.addClass(argTypeName))); 1288 oldMethCopyInIL.append(InstructionConst.DUP); 1289 oldMethCopyInIL.append(InstructionConst.DUP); 1290 oldMethCopyInIL.append( 1291 new INVOKESPECIAL(cpg.addMethodref(argTypeName, "<init>", "()V"))); 1292 1293 // Generate code to invoke the new outlined method, and place the code 1294 // on oldMethCopyOutIL 1295 InstructionHandle outlinedMethodRef; 1296 1297 if (isStaticMethod) { 1298 outlinedMethodRef = 1299 oldMethCopyOutIL.append( 1300 new INVOKESTATIC(cpg.addMethodref( 1301 classGen.getClassName(), 1302 outlinedMethodName, 1303 outlinedMethodGen.getSignature()))); 1304 } else { 1305 oldMethCopyOutIL.append(InstructionConst.THIS); 1306 oldMethCopyOutIL.append(InstructionConst.SWAP); 1307 outlinedMethodRef = 1308 oldMethCopyOutIL.append( 1309 new INVOKEVIRTUAL(cpg.addMethodref( 1310 classGen.getClassName(), 1311 outlinedMethodName, 1312 outlinedMethodGen.getSignature()))); 1313 } 1314 1315 // Used to keep track of the first in a sequence of 1316 // OutlineableChunkStart instructions 1317 boolean chunkStartTargetMappingsPending = false; 1318 InstructionHandle pendingTargetMappingHandle = null; 1319 1320 // Used to keep track of the last instruction that was copied 1321 InstructionHandle lastCopyHandle = null; 1322 1323 // Keeps track of the mapping from instruction handles in the old 1324 // method to instruction handles in the outlined method. Only need 1325 // to track instructions that are targeted by something else in the 1326 // generated BCEL 1327 HashMap<InstructionHandle, InstructionHandle> targetMap = new HashMap<>(); 1328 1329 // Keeps track of the mapping from local variables in the old method 1330 // to local variables in the outlined method. 1331 HashMap<LocalVariableGen, LocalVariableGen> localVarMap = new HashMap<>(); 1332 1333 HashMap<LocalVariableGen, InstructionHandle> revisedLocalVarStart = new HashMap<>(); 1334 HashMap<LocalVariableGen, InstructionHandle> revisedLocalVarEnd = new HashMap<>(); 1335 1336 // Pass 1: Make copies of all instructions, append them to the new list 1337 // and associate old instruction references with the new ones, i.e., 1338 // a 1:1 mapping. The special marker instructions are not copied. 1339 // Also, identify local variables whose values need to be copied into or 1340 // out of the new outlined method, and builds up targetMap and 1341 // localVarMap as described above. The code identifies those local 1342 // variables first so that they can have fixed slots in the stack 1343 // frame for the outlined method assigned them ahead of all those 1344 // variables that don't need to exist for the entirety of the outlined 1345 // method invocation. 1346 for (InstructionHandle ih = first; ih != limit; ih = ih.getNext()) { 1347 Instruction inst = ih.getInstruction(); 1348 1349 // MarkerInstructions are not copied, so if something else targets 1350 // one, the targetMap will point to the nearest copied sibling 1351 // InstructionHandle: for an OutlineableChunkEnd, the nearest 1352 // preceding sibling; for an OutlineableChunkStart, the nearest 1353 // following sibling. 1354 if (inst instanceof MarkerInstruction) { 1355 if (ih.hasTargeters()) { 1356 if (inst instanceof OutlineableChunkEnd) { 1357 targetMap.put(ih, lastCopyHandle); 1358 } else { 1359 if (!chunkStartTargetMappingsPending) { 1360 chunkStartTargetMappingsPending = true; 1361 pendingTargetMappingHandle = ih; 1362 } 1363 } 1364 } 1365 } else { 1366 // Copy the instruction and append it to the outlined method's 1367 // InstructionList. 1368 Instruction c = inst.copy(); // Use clone for shallow copy 1369 1370 if (c instanceof BranchInstruction) { 1371 lastCopyHandle = newIL.append((BranchInstruction)c); 1372 } else { 1373 lastCopyHandle = newIL.append(c); 1374 } 1375 1376 if (c instanceof LocalVariableInstruction 1377 || c instanceof RET) { 1378 // For any instruction that touches a local variable, 1379 // check whether the local variable's value needs to be 1380 // copied into or out of the outlined method. If so, 1381 // generate the code to perform the necessary copying, and 1382 // use localVarMap to map the variable in the original 1383 // method to the variable in the new method. 1384 IndexedInstruction lvi = (IndexedInstruction)c; 1385 int oldLocalVarIndex = lvi.getIndex(); 1386 LocalVariableGen oldLVG = 1387 getLocalVariableRegistry() 1388 .lookupRegisteredLocalVariable(oldLocalVarIndex, 1389 ih.getPosition()); 1390 LocalVariableGen newLVG = localVarMap.get(oldLVG); 1391 1392 // Has the code already mapped this local variable to a 1393 // local in the new method? 1394 if (localVarMap.get(oldLVG) == null) { 1395 // Determine whether the local variable needs to be 1396 // copied into or out of the outlined by checking 1397 // whether the range of instructions in which the 1398 // variable is accessible is outside the range of 1399 // instructions in the outlineable chunk. 1400 // Special case a chunk start offset of zero: a local 1401 // variable live at that position must be a method 1402 // parameter, so the code doesn't need to check whether 1403 // the variable is live before that point; being live 1404 // at offset zero is sufficient to know that the value 1405 // must be copied in to the outlined method. 1406 boolean copyInLocalValue = 1407 offsetInLocalVariableGenRange(oldLVG, 1408 (outlineChunkStartOffset != 0) 1409 ? outlineChunkStartOffset-1 1410 : 0); 1411 boolean copyOutLocalValue = 1412 offsetInLocalVariableGenRange(oldLVG, 1413 outlineChunkEndOffset+1); 1414 1415 // For any variable that needs to be copied into or out 1416 // of the outlined method, create a field in the 1417 // CopyLocals class, and generate the necessary code for 1418 // copying the value. 1419 if (copyInLocalValue || copyOutLocalValue) { 1420 String varName = oldLVG.getName(); 1421 Type varType = oldLVG.getType(); 1422 newLVG = outlinedMethodGen.addLocalVariable(varName, 1423 varType, 1424 null, 1425 null); 1426 int newLocalVarIndex = newLVG.getIndex(); 1427 String varSignature = varType.getSignature(); 1428 1429 // Record the mapping from the old local to the new 1430 localVarMap.put(oldLVG, newLVG); 1431 1432 copyAreaFieldCount++; 1433 String copyAreaFieldName = 1434 "field" + copyAreaFieldCount; 1435 copyAreaCG.addField( 1436 new Field(ACC_PUBLIC, 1437 copyAreaCPG.addUtf8(copyAreaFieldName), 1438 copyAreaCPG.addUtf8(varSignature), 1439 null, copyAreaCPG.getConstantPool())); 1440 1441 int fieldRef = cpg.addFieldref(argTypeName, 1442 copyAreaFieldName, 1443 varSignature); 1444 1445 if (copyInLocalValue) { 1446 // Generate code for the old method to store the 1447 // value of the local into the correct field in 1448 // CopyLocals prior to invocation of the 1449 // outlined method. 1450 oldMethCopyInIL.append( 1451 InstructionConst.DUP); 1452 InstructionHandle copyInLoad = 1453 oldMethCopyInIL.append( 1454 loadLocal(oldLocalVarIndex, varType)); 1455 oldMethCopyInIL.append(new PUTFIELD(fieldRef)); 1456 1457 // If the end of the live range of the old 1458 // variable was in the middle of the outlined 1459 // chunk. Make the load of its value the new 1460 // end of its range. 1461 if (!copyOutLocalValue) { 1462 revisedLocalVarEnd.put(oldLVG, copyInLoad); 1463 } 1464 1465 // Generate code for start of the outlined 1466 // method to copy the value from a field in 1467 // CopyLocals to the new local in the outlined 1468 // method 1469 newMethCopyInIL.append( 1470 InstructionConst.ALOAD_1); 1471 newMethCopyInIL.append(new GETFIELD(fieldRef)); 1472 newMethCopyInIL.append( 1473 storeLocal(newLocalVarIndex, varType)); 1474 } 1475 1476 if (copyOutLocalValue) { 1477 // Generate code for the end of the outlined 1478 // method to copy the value from the new local 1479 // variable into a field in CopyLocals 1480 // method 1481 newMethCopyOutIL.append( 1482 InstructionConst.ALOAD_1); 1483 newMethCopyOutIL.append( 1484 loadLocal(newLocalVarIndex, varType)); 1485 newMethCopyOutIL.append(new PUTFIELD(fieldRef)); 1486 1487 // Generate code to copy the value from a field 1488 // in CopyLocals into a local in the original 1489 // method following invocation of the outlined 1490 // method. 1491 oldMethCopyOutIL.append( 1492 InstructionConst.DUP); 1493 oldMethCopyOutIL.append(new GETFIELD(fieldRef)); 1494 InstructionHandle copyOutStore = 1495 oldMethCopyOutIL.append( 1496 storeLocal(oldLocalVarIndex, varType)); 1497 1498 // If the start of the live range of the old 1499 // variable was in the middle of the outlined 1500 // chunk. Make this store into it the new start 1501 // of its range. 1502 if (!copyInLocalValue) { 1503 revisedLocalVarStart.put(oldLVG, 1504 copyOutStore); 1505 } 1506 } 1507 } 1508 } 1509 } 1510 1511 if (ih.hasTargeters()) { 1512 targetMap.put(ih, lastCopyHandle); 1513 } 1514 1515 // If this is the first instruction copied following a sequence 1516 // of OutlineableChunkStart instructions, indicate that the 1517 // sequence of old instruction all map to this newly created 1518 // instruction 1519 if (chunkStartTargetMappingsPending) { 1520 do { 1521 targetMap.put(pendingTargetMappingHandle, 1522 lastCopyHandle); 1523 pendingTargetMappingHandle = 1524 pendingTargetMappingHandle.getNext(); 1525 } while(pendingTargetMappingHandle != ih); 1526 1527 chunkStartTargetMappingsPending = false; 1528 } 1529 } 1530 } 1531 1532 // Pass 2: Walk old and new instruction lists, updating branch targets 1533 // and local variable references in the new list 1534 InstructionHandle ih = first; 1535 InstructionHandle ch = newIL.getStart(); 1536 1537 while (ch != null) { 1538 // i == old instruction; c == copied instruction 1539 Instruction i = ih.getInstruction(); 1540 Instruction c = ch.getInstruction(); 1541 1542 if (i instanceof BranchInstruction) { 1543 BranchInstruction bc = (BranchInstruction)c; 1544 BranchInstruction bi = (BranchInstruction)i; 1545 InstructionHandle itarget = bi.getTarget(); // old target 1546 1547 // New target must be in targetMap 1548 InstructionHandle newTarget = targetMap.get(itarget); 1549 1550 bc.setTarget(newTarget); 1551 1552 // Handle LOOKUPSWITCH or TABLESWITCH which may have many 1553 // target instructions 1554 if (bi instanceof Select) { 1555 InstructionHandle[] itargets = ((Select)bi).getTargets(); 1556 InstructionHandle[] ctargets = ((Select)bc).getTargets(); 1557 1558 // Update all targets 1559 for (int j=0; j < itargets.length; j++) { 1560 ctargets[j] = targetMap.get(itargets[j]); 1561 } 1562 } 1563 } else if (i instanceof LocalVariableInstruction 1564 || i instanceof RET) { 1565 // For any instruction that touches a local variable, 1566 // map the location of the variable in the original 1567 // method to its location in the new method. 1568 IndexedInstruction lvi = (IndexedInstruction)c; 1569 int oldLocalVarIndex = lvi.getIndex(); 1570 LocalVariableGen oldLVG = 1571 getLocalVariableRegistry() 1572 .lookupRegisteredLocalVariable(oldLocalVarIndex, 1573 ih.getPosition()); 1574 LocalVariableGen newLVG = localVarMap.get(oldLVG); 1575 int newLocalVarIndex; 1576 1577 if (newLVG == null) { 1578 // Create new variable based on old variable - use same 1579 // name and type, but we will let the variable be active 1580 // for the entire outlined method. 1581 // LocalVariableGen oldLocal = oldLocals[oldLocalVarIndex]; 1582 String varName = oldLVG.getName(); 1583 Type varType = oldLVG.getType(); 1584 newLVG = outlinedMethodGen.addLocalVariable(varName, 1585 varType, 1586 null, 1587 null); 1588 newLocalVarIndex = newLVG.getIndex(); 1589 localVarMap.put(oldLVG, newLVG); 1590 1591 // The old variable's live range was wholly contained in 1592 // the outlined chunk. There should no longer be stores 1593 // of values into it or loads of its value, so we can just 1594 // mark its live range as the reference to the outlined 1595 // method. 1596 revisedLocalVarStart.put(oldLVG, outlinedMethodRef); 1597 revisedLocalVarEnd.put(oldLVG, outlinedMethodRef); 1598 } else { 1599 newLocalVarIndex = newLVG.getIndex(); 1600 } 1601 lvi.setIndex(newLocalVarIndex); 1602 } 1603 1604 // If the old instruction marks the end of the range of a local 1605 // variable, make sure that any slots on the stack reserved for 1606 // local variables are made available for reuse by calling 1607 // MethodGenerator.removeLocalVariable 1608 if (ih.hasTargeters()) { 1609 InstructionTargeter[] targeters = ih.getTargeters(); 1610 1611 for (int idx = 0; idx < targeters.length; idx++) { 1612 InstructionTargeter targeter = targeters[idx]; 1613 1614 if (targeter instanceof LocalVariableGen 1615 && ((LocalVariableGen)targeter).getEnd()==ih) { 1616 LocalVariableGen newLVG = localVarMap.get(targeter); 1617 if (newLVG != null) { 1618 outlinedMethodGen.removeLocalVariable(newLVG); 1619 } 1620 } 1621 } 1622 } 1623 1624 // If the current instruction in the original list was a marker, 1625 // it wasn't copied, so don't advance through the list of copied 1626 // instructions yet. 1627 if (!(i instanceof MarkerInstruction)) { 1628 ch = ch.getNext(); 1629 } 1630 ih = ih.getNext(); 1631 1632 } 1633 1634 // POP the reference to the CopyLocals object from the stack 1635 oldMethCopyOutIL.append(InstructionConst.POP); 1636 1637 for (Map.Entry<LocalVariableGen, InstructionHandle> lvgRangeStartPair : 1638 revisedLocalVarStart.entrySet()) { 1639 LocalVariableGen lvg = lvgRangeStartPair.getKey(); 1640 InstructionHandle startInst = lvgRangeStartPair.getValue(); 1641 1642 lvg.setStart(startInst); 1643 } 1644 1645 for (Map.Entry<LocalVariableGen, InstructionHandle> lvgRangeEndPair : 1646 revisedLocalVarEnd.entrySet()) { 1647 LocalVariableGen lvg = lvgRangeEndPair.getKey(); 1648 InstructionHandle endInst = lvgRangeEndPair.getValue(); 1649 1650 lvg.setEnd(endInst); 1651 } 1652 1653 xsltc.dumpClass(copyAreaCG.getJavaClass()); 1654 1655 // Assemble the instruction lists so that the old method invokes the 1656 // new outlined method 1657 InstructionList oldMethodIL = getInstructionList(); 1658 1659 oldMethodIL.insert(first, oldMethCopyInIL); 1660 oldMethodIL.insert(first, oldMethCopyOutIL); 1661 1662 // Insert the copying code into the outlined method 1663 newIL.insert(newMethCopyInIL); 1664 newIL.append(newMethCopyOutIL); 1665 newIL.append(InstructionConst.RETURN); 1666 1667 // Discard instructions in outlineable chunk from old method 1668 try { 1669 oldMethodIL.delete(first, last); 1670 } catch (TargetLostException e) { 1671 InstructionHandle[] targets = e.getTargets(); 1672 // If there were still references to old instructions lingering, 1673 // clean those up. The only instructions targetting the deleted 1674 // instructions should have been part of the chunk that was just 1675 // deleted, except that instructions might branch to the start of 1676 // the outlined chunk; similarly, all the live ranges of local 1677 // variables should have been adjusted, except for unreferenced 1678 // variables. 1679 for (int i = 0; i < targets.length; i++) { 1680 InstructionHandle lostTarget = targets[i]; 1681 InstructionTargeter[] targeters = lostTarget.getTargeters(); 1682 for (int j = 0; j < targeters.length; j++) { 1683 if (targeters[j] instanceof LocalVariableGen) { 1684 LocalVariableGen lvgTargeter = 1685 (LocalVariableGen) targeters[j]; 1686 // In the case of any lingering variable references, 1687 // just make the live range point to the outlined 1688 // function reference. Such variables should be unused 1689 // anyway. 1690 if (lvgTargeter.getStart() == lostTarget) { 1691 lvgTargeter.setStart(outlinedMethodRef); 1692 } 1693 if (lvgTargeter.getEnd() == lostTarget) { 1694 lvgTargeter.setEnd(outlinedMethodRef); 1695 } 1696 } else { 1697 targeters[j].updateTarget(lostTarget, 1698 outlinedMethodCallSetup); 1699 } 1700 } 1701 } 1702 } 1703 1704 // Make a copy for the new method of all exceptions that might be thrown 1705 String[] exceptions = getExceptions(); 1706 for (int i = 0; i < exceptions.length; i++) { 1707 outlinedMethodGen.addException(exceptions[i]); 1708 } 1709 1710 return outlinedMethodGen.getThisMethod(); 1711 } 1712 1713 /** 1714 * Helper method to generate an instance of a subclass of 1715 * {@link LoadInstruction} based on the specified {@link Type} that will 1716 * load the specified local variable 1717 * @param index the JVM stack frame index of the variable that is to be 1718 * loaded 1719 * @param type the {@link Type} of the variable 1720 * @return the generated {@link LoadInstruction} 1721 */ loadLocal(int index, Type type)1722 private static Instruction loadLocal(int index, Type type) { 1723 if (type == Type.BOOLEAN) { 1724 return new ILOAD(index); 1725 } else if (type == Type.INT) { 1726 return new ILOAD(index); 1727 } else if (type == Type.SHORT) { 1728 return new ILOAD(index); 1729 } else if (type == Type.LONG) { 1730 return new LLOAD(index); 1731 } else if (type == Type.BYTE) { 1732 return new ILOAD(index); 1733 } else if (type == Type.CHAR) { 1734 return new ILOAD(index); 1735 } else if (type == Type.FLOAT) { 1736 return new FLOAD(index); 1737 } else if (type == Type.DOUBLE) { 1738 return new DLOAD(index); 1739 } else { 1740 return new ALOAD(index); 1741 } 1742 } 1743 1744 /** 1745 * Helper method to generate an instance of a subclass of 1746 * {@link StoreInstruction} based on the specified {@link Type} that will 1747 * store a value in the specified local variable 1748 * @param index the JVM stack frame index of the variable that is to be 1749 * stored 1750 * @param type the {@link Type} of the variable 1751 * @return the generated {@link StoredInstruction} 1752 */ storeLocal(int index, Type type)1753 private static Instruction storeLocal(int index, Type type) { 1754 if (type == Type.BOOLEAN) { 1755 return new ISTORE(index); 1756 } else if (type == Type.INT) { 1757 return new ISTORE(index); 1758 } else if (type == Type.SHORT) { 1759 return new ISTORE(index); 1760 } else if (type == Type.LONG) { 1761 return new LSTORE(index); 1762 } else if (type == Type.BYTE) { 1763 return new ISTORE(index); 1764 } else if (type == Type.CHAR) { 1765 return new ISTORE(index); 1766 } else if (type == Type.FLOAT) { 1767 return new FSTORE(index); 1768 } else if (type == Type.DOUBLE) { 1769 return new DSTORE(index); 1770 } else { 1771 return new ASTORE(index); 1772 } 1773 } 1774 1775 /** 1776 * Track the number of outlineable chunks seen. 1777 */ 1778 private int m_totalChunks = 0; 1779 1780 /** 1781 * Track the number of outlineable chunks started but not yet ended. Used 1782 * to detect imbalances in byte code generation. 1783 */ 1784 private int m_openChunks = 0; 1785 1786 /** 1787 * Mark the end of the method's 1788 * {@link InstructionList} as the start of an outlineable chunk of code. 1789 * The outlineable chunk begins after the {@link InstructionHandle} that is 1790 * at the end of the method's {@link InstructionList}, or at the start of 1791 * the method if the <code>InstructionList</code> is empty. 1792 * See {@link OutlineableChunkStart} for more information. 1793 */ markChunkStart()1794 public void markChunkStart() { 1795 // m_chunkTree.markChunkStart(); 1796 getInstructionList() 1797 .append(OutlineableChunkStart.OUTLINEABLECHUNKSTART); 1798 m_totalChunks++; 1799 m_openChunks++; 1800 } 1801 1802 /** 1803 * Mark the end of an outlineable chunk of code. See 1804 * {@link OutlineableChunkStart} for more information. 1805 */ markChunkEnd()1806 public void markChunkEnd() { 1807 // m_chunkTree.markChunkEnd(); 1808 getInstructionList() 1809 .append(OutlineableChunkEnd.OUTLINEABLECHUNKEND); 1810 m_openChunks--; 1811 if (m_openChunks < 0) { 1812 String msg = (new ErrorMsg(ErrorMsg.OUTLINE_ERR_UNBALANCED_MARKERS)) 1813 .toString(); 1814 throw new InternalError(msg); 1815 } 1816 } 1817 1818 /** 1819 * <p>Get all {@link Method}s generated by this {@link MethodGenerator}. 1820 * The {@link MethodGen#getMethod()} only returns a single 1821 * <code>Method</code> object. This method takes into account the Java 1822 * Virtual Machine Specification limit of 64KB on the size of a method, and 1823 * may return more than one <code>Method</code>.</p> 1824 * <p>If the code associated with the <code>MethodGenerator</code> would 1825 * exceed the 64KB limit, this method will attempt to split the code in 1826 * the {@link InstructionList} associated with this 1827 * <code>MethodGenerator</code> into several methods.</p> 1828 * @param classGen the {@link ClassGenerator} of which these methods are 1829 * members 1830 * @return an array of all the <code>Method</code>s generated 1831 */ getGeneratedMethods(ClassGenerator classGen)1832 Method[] getGeneratedMethods(ClassGenerator classGen) { 1833 Method[] generatedMethods; 1834 InstructionList il = getInstructionList(); 1835 InstructionHandle last = il.getEnd(); 1836 1837 il.setPositions(); 1838 1839 int instructionListSize = 1840 last.getPosition() + last.getInstruction().getLength(); 1841 1842 // Need to look for any branch target offsets that exceed the range 1843 // [-32768,32767] 1844 if (instructionListSize > MAX_BRANCH_TARGET_OFFSET) { 1845 boolean ilChanged = widenConditionalBranchTargetOffsets(); 1846 1847 // If any branch instructions needed widening, recompute the size 1848 // of the byte code for the method 1849 if (ilChanged) { 1850 il.setPositions(); 1851 last = il.getEnd(); 1852 instructionListSize = 1853 last.getPosition() + last.getInstruction().getLength(); 1854 } 1855 } 1856 1857 if (instructionListSize > MAX_METHOD_SIZE) { 1858 generatedMethods = outlineChunks(classGen, instructionListSize); 1859 } else { 1860 generatedMethods = new Method[] {getThisMethod()}; 1861 } 1862 return generatedMethods; 1863 } 1864 getThisMethod()1865 protected Method getThisMethod() { 1866 stripAttributes(true); 1867 setMaxLocals(); 1868 setMaxStack(); 1869 removeNOPs(); 1870 1871 return getMethod(); 1872 } 1873 /** 1874 * <p>Rewrites branches to avoid the JVM limits of relative branch 1875 * offsets. There is no need to invoke this method if the bytecode for the 1876 * {@link MethodGenerator} does not exceed 32KB.</p> 1877 * <p>The Java Virtual Machine Specification permits the code portion of a 1878 * method to be up to 64KB in length. However, some control transfer 1879 * instructions specify relative offsets as a signed 16-bit quantity, 1880 * limiting the range to a subset of the instructions that might be in a 1881 * method.</p> 1882 * <p>The <code>TABLESWITCH</code> and <code>LOOKUPSWITCH</code> 1883 * instructions always use 32-bit signed relative offsets, so they are 1884 * immune to this problem.</p> 1885 * <p>The <code>GOTO</code> and <code>JSR</code> 1886 * instructions come in two forms, one of which uses 16-bit relative 1887 * offsets, and the other of which uses 32-bit relative offsets. The BCEL 1888 * library decides whether to use the wide form of <code>GOTO</code> or 1889 * <code>JSR</code>instructions based on the relative offset of the target 1890 * of the instruction without any intervention by the user of the 1891 * library.</p> 1892 * <p>This leaves the various conditional branch instructions, 1893 * <code>IFEQ</code>, <code>IFNULL</code>, <code>IF_ICMPEQ</code>, 1894 * <em>et al.</em>, all of which use 16-bit signed relative offsets, with no 1895 * 32-bit wide form available.</p> 1896 * <p>This method scans the {@link InstructionList} associated with this 1897 * {@link MethodGenerator} and finds all conditional branch instructions 1898 * that might exceed the 16-bit limitation for relative branch offsets. 1899 * The logic of each such instruction is inverted, and made to target the 1900 * instruction which follows it. An unconditional branch to the original 1901 * target of the instruction is then inserted between the conditional 1902 * branch and the instruction which previously followed it. The 1903 * unconditional branch is permitted to have a 16-bit or a 32-bit relative 1904 * offset, as described above. For example, 1905 * <code> 1906 * 1234: NOP 1907 * ... 1908 * 55278: IFEQ -54044 1909 * 55280: NOP 1910 * </code> 1911 * is rewritten as 1912 * <code> 1913 * 1234: NOP 1914 * ... 1915 * 55278: IFNE 7 1916 * 55280: GOTO_W -54046 1917 * 55285: NOP 1918 * </code></p> 1919 * <p><b>Preconditions:</b> 1920 * <ul><li>The {@link InstructionList#setPositions()} has been called for 1921 * the <code>InstructionList</code> associated with this 1922 * <code>MethodGenerator</code>. 1923 * </li></ul></p> 1924 * <p><b>Postconditions:</b> 1925 * <ul><li>Any further changes to the <code>InstructionList</code> for this 1926 * <code>MethodGenerator</code> will invalidate the changes made by this 1927 * method.</li></ul> 1928 * </p> 1929 * @return <code>true</code> if the <code>InstructionList</code> was 1930 * modified; <code>false</code> otherwise 1931 * @see The Java Virtual Machine Specification, Second Edition 1932 */ widenConditionalBranchTargetOffsets()1933 boolean widenConditionalBranchTargetOffsets() { 1934 boolean ilChanged = false; 1935 int maxOffsetChange = 0; 1936 InstructionList il = getInstructionList(); 1937 1938 // Loop through all the instructions, finding those that would be 1939 // affected by inserting new instructions in the InstructionList, and 1940 // calculating the maximum amount by which the relative offset between 1941 // two instructions could possibly change. 1942 // In part this loop duplicates code in 1943 // org.apache.bcel.generic.InstructionList.setPosition(), which does 1944 // this to determine whether to use 16-bit or 32-bit offsets for GOTO 1945 // and JSR instructions. Ideally, that method would do the same for 1946 // conditional branch instructions, but it doesn't, so we duplicate the 1947 // processing here. 1948 for (InstructionHandle ih = il.getStart(); 1949 ih != null; 1950 ih = ih.getNext()) { 1951 Instruction inst = ih.getInstruction(); 1952 1953 switch (inst.getOpcode()) { 1954 // Instructions that may have 16-bit or 32-bit branch targets. 1955 // The size of the branch offset might increase by two bytes. 1956 case Const.GOTO: 1957 case Const.JSR: 1958 maxOffsetChange = maxOffsetChange + 2; 1959 break; 1960 // Instructions that contain padding for alignment purposes 1961 // Up to three bytes of padding might be needed. For greater 1962 // accuracy, we should be able to discount any padding already 1963 // added to these instructions by InstructionList.setPosition(), 1964 // their APIs do not expose that information. 1965 case Const.TABLESWITCH: 1966 case Const.LOOKUPSWITCH: 1967 maxOffsetChange = maxOffsetChange + 3; 1968 break; 1969 // Instructions that might be rewritten by this method as a 1970 // conditional branch followed by an unconditional branch. 1971 // The unconditional branch would require five bytes. 1972 case Const.IF_ACMPEQ: 1973 case Const.IF_ACMPNE: 1974 case Const.IF_ICMPEQ: 1975 case Const.IF_ICMPGE: 1976 case Const.IF_ICMPGT: 1977 case Const.IF_ICMPLE: 1978 case Const.IF_ICMPLT: 1979 case Const.IF_ICMPNE: 1980 case Const.IFEQ: 1981 case Const.IFGE: 1982 case Const.IFGT: 1983 case Const.IFLE: 1984 case Const.IFLT: 1985 case Const.IFNE: 1986 case Const.IFNONNULL: 1987 case Const.IFNULL: 1988 maxOffsetChange = maxOffsetChange + 5; 1989 break; 1990 } 1991 } 1992 1993 // Now that the maximum number of bytes by which the method might grow 1994 // has been determined, look for conditional branches to see which 1995 // might possibly exceed the 16-bit relative offset. 1996 for (InstructionHandle ih = il.getStart(); 1997 ih != null; 1998 ih = ih.getNext()) { 1999 Instruction inst = ih.getInstruction(); 2000 2001 if (inst instanceof IfInstruction) { 2002 IfInstruction oldIfInst = (IfInstruction)inst; 2003 BranchHandle oldIfHandle = (BranchHandle)ih; 2004 InstructionHandle target = oldIfInst.getTarget(); 2005 int relativeTargetOffset = target.getPosition() 2006 - oldIfHandle.getPosition(); 2007 2008 // Consider the worst case scenario in which the conditional 2009 // branch and its target are separated by all the instructions 2010 // in the method that might increase in size. If that results 2011 // in a relative offset that cannot be represented as a 32-bit 2012 // signed quantity, rewrite the instruction as described above. 2013 if ((relativeTargetOffset - maxOffsetChange 2014 < MIN_BRANCH_TARGET_OFFSET) 2015 || (relativeTargetOffset + maxOffsetChange 2016 > MAX_BRANCH_TARGET_OFFSET)) { 2017 // Invert the logic of the IF instruction, and append 2018 // that to the InstructionList following the original IF 2019 // instruction 2020 InstructionHandle nextHandle = oldIfHandle.getNext(); 2021 IfInstruction invertedIfInst = oldIfInst.negate(); 2022 BranchHandle invertedIfHandle = il.append(oldIfHandle, 2023 invertedIfInst); 2024 2025 // Append an unconditional branch to the target of the 2026 // original IF instruction after the new IF instruction 2027 BranchHandle gotoHandle = il.append(invertedIfHandle, 2028 new GOTO(target)); 2029 2030 // If the original IF was the last instruction in 2031 // InstructionList, add a new no-op to act as the target 2032 // of the new IF 2033 if (nextHandle == null) { 2034 nextHandle = il.append(gotoHandle, InstructionConst.NOP); 2035 } 2036 2037 // Make the new IF instruction branch around the GOTO 2038 invertedIfHandle.updateTarget(target, nextHandle); 2039 2040 // If anything still "points" to the old IF instruction, 2041 // make adjustments to refer to either the new IF or GOTO 2042 // instruction 2043 if (oldIfHandle.hasTargeters()) { 2044 InstructionTargeter[] targeters = 2045 oldIfHandle.getTargeters(); 2046 2047 for (int i = 0; i < targeters.length; i++) { 2048 InstructionTargeter targeter = targeters[i]; 2049 // Ideally, one should simply be able to use 2050 // InstructionTargeter.updateTarget to change 2051 // references to the old IF instruction to the new 2052 // IF instruction. However, if a LocalVariableGen 2053 // indicated the old IF marked the end of the range 2054 // in which the IF variable is in use, the live 2055 // range of the variable must extend to include the 2056 // newly created GOTO instruction. The need for 2057 // this sort of specific knowledge of an 2058 // implementor of the InstructionTargeter interface 2059 // makes the code more fragile. Future implementors 2060 // of the interface might have similar requirements 2061 // which wouldn't be accommodated seemlessly. 2062 if (targeter instanceof LocalVariableGen) { 2063 LocalVariableGen lvg = 2064 (LocalVariableGen) targeter; 2065 if (lvg.getStart() == oldIfHandle) { 2066 lvg.setStart(invertedIfHandle); 2067 } else if (lvg.getEnd() == oldIfHandle) { 2068 lvg.setEnd(gotoHandle); 2069 } 2070 } else { 2071 targeter.updateTarget(oldIfHandle, 2072 invertedIfHandle); 2073 } 2074 } 2075 } 2076 2077 try { 2078 il.delete(oldIfHandle); 2079 } catch (TargetLostException tle) { 2080 // This can never happen - we updated the list of 2081 // instructions that target the deleted instruction 2082 // prior to deleting it. 2083 String msg = 2084 new ErrorMsg(ErrorMsg.OUTLINE_ERR_DELETED_TARGET, 2085 tle.getMessage()).toString(); 2086 throw new InternalError(msg); 2087 } 2088 2089 // Adjust the pointer in the InstructionList to point after 2090 // the newly inserted IF instruction 2091 ih = gotoHandle; 2092 2093 // Indicate that this method rewrote at least one IF 2094 ilChanged = true; 2095 } 2096 } 2097 } 2098 2099 // Did this method rewrite any IF instructions? 2100 return ilChanged; 2101 } 2102 } 2103