1 /*
2 * Copyright (c) 2015, 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 java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.Map;
31 import java.util.Stack;
32
33
34 import com.sun.org.apache.bcel.internal.Constants;
35 import com.sun.org.apache.bcel.internal.classfile.Field;
36 import com.sun.org.apache.bcel.internal.classfile.Method;
37 import com.sun.org.apache.bcel.internal.generic.ALOAD;
38 import com.sun.org.apache.bcel.internal.generic.ASTORE;
39 import com.sun.org.apache.bcel.internal.generic.BranchHandle;
40 import com.sun.org.apache.bcel.internal.generic.BranchInstruction;
41 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
42 import com.sun.org.apache.bcel.internal.generic.DLOAD;
43 import com.sun.org.apache.bcel.internal.generic.DSTORE;
44 import com.sun.org.apache.bcel.internal.generic.FLOAD;
45 import com.sun.org.apache.bcel.internal.generic.FSTORE;
46 import com.sun.org.apache.bcel.internal.generic.GETFIELD;
47 import com.sun.org.apache.bcel.internal.generic.GOTO;
48 import com.sun.org.apache.bcel.internal.generic.ICONST;
49 import com.sun.org.apache.bcel.internal.generic.IfInstruction;
50 import com.sun.org.apache.bcel.internal.generic.ILOAD;
51 import com.sun.org.apache.bcel.internal.generic.IndexedInstruction;
52 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
53 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
54 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
55 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
56 import com.sun.org.apache.bcel.internal.generic.ISTORE;
57 import com.sun.org.apache.bcel.internal.generic.Instruction;
58 import com.sun.org.apache.bcel.internal.generic.InstructionConstants;
59 import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
60 import com.sun.org.apache.bcel.internal.generic.InstructionList;
61 import com.sun.org.apache.bcel.internal.generic.InstructionTargeter;
62 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
63 import com.sun.org.apache.bcel.internal.generic.LocalVariableInstruction;
64 import com.sun.org.apache.bcel.internal.generic.LLOAD;
65 import com.sun.org.apache.bcel.internal.generic.LSTORE;
66 import com.sun.org.apache.bcel.internal.generic.MethodGen;
67 import com.sun.org.apache.bcel.internal.generic.NEW;
68 import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
69 import com.sun.org.apache.bcel.internal.generic.RET;
70 import com.sun.org.apache.bcel.internal.generic.Select;
71 import com.sun.org.apache.bcel.internal.generic.TargetLostException;
72 import com.sun.org.apache.bcel.internal.generic.Type;
73
74 import com.sun.org.apache.xalan.internal.xsltc.compiler.Pattern;
75 import com.sun.org.apache.xalan.internal.xsltc.compiler.XSLTC;
76
77 /**
78 * @author Jacek Ambroziak
79 * @author Santiago Pericas-Geertsen
80 */
81 public class MethodGenerator extends MethodGen
82 implements com.sun.org.apache.xalan.internal.xsltc.compiler.Constants {
83 protected static final int INVALID_INDEX = -1;
84
85 private static final String START_ELEMENT_SIG
86 = "(" + STRING_SIG + ")V";
87 private static final String END_ELEMENT_SIG
88 = START_ELEMENT_SIG;
89
90 private InstructionList _mapTypeSub;
91
92 private static final int DOM_INDEX = 1;
93 private static final int ITERATOR_INDEX = 2;
94 private static final int HANDLER_INDEX = 3;
95
96 private static final int MAX_METHOD_SIZE = 65535;
97 private static final int MAX_BRANCH_TARGET_OFFSET = 32767;
98 private static final int MIN_BRANCH_TARGET_OFFSET = -32768;
99
100 private static final int TARGET_METHOD_SIZE = 60000;
101 private static final int MINIMUM_OUTLINEABLE_CHUNK_SIZE = 1000;
102
103 private Instruction _iloadCurrent;
104 private Instruction _istoreCurrent;
105 private final Instruction _astoreHandler;
106 private final Instruction _aloadHandler;
107 private final Instruction _astoreIterator;
108 private final Instruction _aloadIterator;
109 private final Instruction _aloadDom;
110 private final Instruction _astoreDom;
111
1298 // InstructionLists for copying values into and out of an instance of
1299 // CopyLocals:
1300 // oldMethCoypInIL - from locals in old method into an instance
1301 // of the CopyLocals class (oldMethCopyInIL)
1302 // oldMethCopyOutIL - from CopyLocals back into locals in the old
1303 // method
1304 // newMethCopyInIL - from CopyLocals into locals in the new
1305 // method
1306 // newMethCopyOutIL - from locals in new method into the instance
1307 // of the CopyLocals class
1308 InstructionList oldMethCopyInIL = new InstructionList();
1309 InstructionList oldMethCopyOutIL = new InstructionList();
1310 InstructionList newMethCopyInIL = new InstructionList();
1311 InstructionList newMethCopyOutIL = new InstructionList();
1312
1313 // Allocate instance of class in which we'll copy in or copy out locals
1314 // and make two copies: last copy is used to invoke constructor;
1315 // other two are used for references to fields in the CopyLocals object
1316 InstructionHandle outlinedMethodCallSetup =
1317 oldMethCopyInIL.append(new NEW(cpg.addClass(argTypeName)));
1318 oldMethCopyInIL.append(InstructionConstants.DUP);
1319 oldMethCopyInIL.append(InstructionConstants.DUP);
1320 oldMethCopyInIL.append(
1321 new INVOKESPECIAL(cpg.addMethodref(argTypeName, "<init>", "()V")));
1322
1323 // Generate code to invoke the new outlined method, and place the code
1324 // on oldMethCopyOutIL
1325 InstructionHandle outlinedMethodRef;
1326
1327 if (isStaticMethod) {
1328 outlinedMethodRef =
1329 oldMethCopyOutIL.append(
1330 new INVOKESTATIC(cpg.addMethodref(
1331 classGen.getClassName(),
1332 outlinedMethodName,
1333 outlinedMethodGen.getSignature())));
1334 } else {
1335 oldMethCopyOutIL.append(InstructionConstants.THIS);
1336 oldMethCopyOutIL.append(InstructionConstants.SWAP);
1337 outlinedMethodRef =
1338 oldMethCopyOutIL.append(
1339 new INVOKEVIRTUAL(cpg.addMethodref(
1340 classGen.getClassName(),
1341 outlinedMethodName,
1342 outlinedMethodGen.getSignature())));
1343 }
1344
1345 // Used to keep track of the first in a sequence of
1346 // OutlineableChunkStart instructions
1347 boolean chunkStartTargetMappingsPending = false;
1348 InstructionHandle pendingTargetMappingHandle = null;
1349
1350 // Used to keep track of the last instruction that was copied
1351 InstructionHandle lastCopyHandle = null;
1352
1353 // Keeps track of the mapping from instruction handles in the old
1354 // method to instruction handles in the outlined method. Only need
1355 // to track instructions that are targeted by something else in the
1356 // generated BCEL
1462
1463 copyAreaFieldCount++;
1464 String copyAreaFieldName =
1465 "field" + copyAreaFieldCount;
1466 copyAreaCG.addField(
1467 new Field(ACC_PUBLIC,
1468 copyAreaCPG.addUtf8(copyAreaFieldName),
1469 copyAreaCPG.addUtf8(varSignature),
1470 null, copyAreaCPG.getConstantPool()));
1471
1472 int fieldRef = cpg.addFieldref(argTypeName,
1473 copyAreaFieldName,
1474 varSignature);
1475
1476 if (copyInLocalValue) {
1477 // Generate code for the old method to store the
1478 // value of the local into the correct field in
1479 // CopyLocals prior to invocation of the
1480 // outlined method.
1481 oldMethCopyInIL.append(
1482 InstructionConstants.DUP);
1483 InstructionHandle copyInLoad =
1484 oldMethCopyInIL.append(
1485 loadLocal(oldLocalVarIndex, varType));
1486 oldMethCopyInIL.append(new PUTFIELD(fieldRef));
1487
1488 // If the end of the live range of the old
1489 // variable was in the middle of the outlined
1490 // chunk. Make the load of its value the new
1491 // end of its range.
1492 if (!copyOutLocalValue) {
1493 revisedLocalVarEnd.put(oldLVG, copyInLoad);
1494 }
1495
1496 // Generate code for start of the outlined
1497 // method to copy the value from a field in
1498 // CopyLocals to the new local in the outlined
1499 // method
1500 newMethCopyInIL.append(
1501 InstructionConstants.ALOAD_1);
1502 newMethCopyInIL.append(new GETFIELD(fieldRef));
1503 newMethCopyInIL.append(
1504 storeLocal(newLocalVarIndex, varType));
1505 }
1506
1507 if (copyOutLocalValue) {
1508 // Generate code for the end of the outlined
1509 // method to copy the value from the new local
1510 // variable into a field in CopyLocals
1511 // method
1512 newMethCopyOutIL.append(
1513 InstructionConstants.ALOAD_1);
1514 newMethCopyOutIL.append(
1515 loadLocal(newLocalVarIndex, varType));
1516 newMethCopyOutIL.append(new PUTFIELD(fieldRef));
1517
1518 // Generate code to copy the value from a field
1519 // in CopyLocals into a local in the original
1520 // method following invocation of the outlined
1521 // method.
1522 oldMethCopyOutIL.append(
1523 InstructionConstants.DUP);
1524 oldMethCopyOutIL.append(new GETFIELD(fieldRef));
1525 InstructionHandle copyOutStore =
1526 oldMethCopyOutIL.append(
1527 storeLocal(oldLocalVarIndex, varType));
1528
1529 // If the start of the live range of the old
1530 // variable was in the middle of the outlined
1531 // chunk. Make this store into it the new start
1532 // of its range.
1533 if (!copyInLocalValue) {
1534 revisedLocalVarStart.put(oldLVG,
1535 copyOutStore);
1536 }
1537 }
1538 }
1539 }
1540 }
1541
1542 if (ih.hasTargeters()) {
1543 targetMap.put(ih, lastCopyHandle);
1650 Object newLVG = localVarMap.get(targeter);
1651 if (newLVG != null) {
1652 outlinedMethodGen.removeLocalVariable(
1653 (LocalVariableGen)newLVG);
1654 }
1655 }
1656 }
1657 }
1658
1659 // If the current instruction in the original list was a marker,
1660 // it wasn't copied, so don't advance through the list of copied
1661 // instructions yet.
1662 if (!(i instanceof MarkerInstruction)) {
1663 ch = ch.getNext();
1664 }
1665 ih = ih.getNext();
1666
1667 }
1668
1669 // POP the reference to the CopyLocals object from the stack
1670 oldMethCopyOutIL.append(InstructionConstants.POP);
1671
1672 // Now that the generation of the outlined code is complete, update
1673 // the old local variables with new start and end ranges, as required.
1674 Iterator revisedLocalVarStartPairIter = revisedLocalVarStart.entrySet()
1675 .iterator();
1676 while (revisedLocalVarStartPairIter.hasNext()) {
1677 Map.Entry lvgRangeStartPair =
1678 (Map.Entry)revisedLocalVarStartPairIter.next();
1679 LocalVariableGen lvg = (LocalVariableGen)lvgRangeStartPair.getKey();
1680 InstructionHandle startInst =
1681 (InstructionHandle)lvgRangeStartPair.getValue();
1682
1683 lvg.setStart(startInst);
1684
1685 }
1686
1687 Iterator revisedLocalVarEndPairIter = revisedLocalVarEnd.entrySet()
1688 .iterator();
1689 while (revisedLocalVarEndPairIter.hasNext()) {
1690 Map.Entry lvgRangeEndPair =
1691 (Map.Entry)revisedLocalVarEndPairIter.next();
1692 LocalVariableGen lvg = (LocalVariableGen)lvgRangeEndPair.getKey();
1693 InstructionHandle endInst =
1694 (InstructionHandle)lvgRangeEndPair.getValue();
1695
1696 lvg.setEnd(endInst);
1697 }
1698
1699 xsltc.dumpClass(copyAreaCG.getJavaClass());
1700
1701 // Assemble the instruction lists so that the old method invokes the
1702 // new outlined method
1703 InstructionList oldMethodIL = getInstructionList();
1704
1705 oldMethodIL.insert(first, oldMethCopyInIL);
1706 oldMethodIL.insert(first, oldMethCopyOutIL);
1707
1708 // Insert the copying code into the outlined method
1709 newIL.insert(newMethCopyInIL);
1710 newIL.append(newMethCopyOutIL);
1711 newIL.append(InstructionConstants.RETURN);
1712
1713 // Discard instructions in outlineable chunk from old method
1714 try {
1715 oldMethodIL.delete(first, last);
1716 } catch (TargetLostException e) {
1717 InstructionHandle[] targets = e.getTargets();
1718 // If there were still references to old instructions lingering,
1719 // clean those up. The only instructions targetting the deleted
1720 // instructions should have been part of the chunk that was just
1721 // deleted, except that instructions might branch to the start of
1722 // the outlined chunk; similarly, all the live ranges of local
1723 // variables should have been adjusted, except for unreferenced
1724 // variables.
1725 for (int i = 0; i < targets.length; i++) {
1726 InstructionHandle lostTarget = targets[i];
1727 InstructionTargeter[] targeters = lostTarget.getTargeters();
1728 for (int j = 0; j < targeters.length; j++) {
1729 if (targeters[j] instanceof LocalVariableGen) {
1730 LocalVariableGen lvgTargeter =
1731 (LocalVariableGen) targeters[j];
1982 InstructionList il = getInstructionList();
1983
1984 // Loop through all the instructions, finding those that would be
1985 // affected by inserting new instructions in the InstructionList, and
1986 // calculating the maximum amount by which the relative offset between
1987 // two instructions could possibly change.
1988 // In part this loop duplicates code in
1989 // org.apache.bcel.generic.InstructionList.setPosition(), which does
1990 // this to determine whether to use 16-bit or 32-bit offsets for GOTO
1991 // and JSR instructions. Ideally, that method would do the same for
1992 // conditional branch instructions, but it doesn't, so we duplicate the
1993 // processing here.
1994 for (InstructionHandle ih = il.getStart();
1995 ih != null;
1996 ih = ih.getNext()) {
1997 Instruction inst = ih.getInstruction();
1998
1999 switch (inst.getOpcode()) {
2000 // Instructions that may have 16-bit or 32-bit branch targets.
2001 // The size of the branch offset might increase by two bytes.
2002 case Constants.GOTO:
2003 case Constants.JSR:
2004 maxOffsetChange = maxOffsetChange + 2;
2005 break;
2006 // Instructions that contain padding for alignment purposes
2007 // Up to three bytes of padding might be needed. For greater
2008 // accuracy, we should be able to discount any padding already
2009 // added to these instructions by InstructionList.setPosition(),
2010 // their APIs do not expose that information.
2011 case Constants.TABLESWITCH:
2012 case Constants.LOOKUPSWITCH:
2013 maxOffsetChange = maxOffsetChange + 3;
2014 break;
2015 // Instructions that might be rewritten by this method as a
2016 // conditional branch followed by an unconditional branch.
2017 // The unconditional branch would require five bytes.
2018 case Constants.IF_ACMPEQ:
2019 case Constants.IF_ACMPNE:
2020 case Constants.IF_ICMPEQ:
2021 case Constants.IF_ICMPGE:
2022 case Constants.IF_ICMPGT:
2023 case Constants.IF_ICMPLE:
2024 case Constants.IF_ICMPLT:
2025 case Constants.IF_ICMPNE:
2026 case Constants.IFEQ:
2027 case Constants.IFGE:
2028 case Constants.IFGT:
2029 case Constants.IFLE:
2030 case Constants.IFLT:
2031 case Constants.IFNE:
2032 case Constants.IFNONNULL:
2033 case Constants.IFNULL:
2034 maxOffsetChange = maxOffsetChange + 5;
2035 break;
2036 }
2037 }
2038
2039 // Now that the maximum number of bytes by which the method might grow
2040 // has been determined, look for conditional branches to see which
2041 // might possibly exceed the 16-bit relative offset.
2042 for (InstructionHandle ih = il.getStart();
2043 ih != null;
2044 ih = ih.getNext()) {
2045 Instruction inst = ih.getInstruction();
2046
2047 if (inst instanceof IfInstruction) {
2048 IfInstruction oldIfInst = (IfInstruction)inst;
2049 BranchHandle oldIfHandle = (BranchHandle)ih;
2050 InstructionHandle target = oldIfInst.getTarget();
2051 int relativeTargetOffset = target.getPosition()
2052 - oldIfHandle.getPosition();
2053
2060 < MIN_BRANCH_TARGET_OFFSET)
2061 || (relativeTargetOffset + maxOffsetChange
2062 > MAX_BRANCH_TARGET_OFFSET)) {
2063 // Invert the logic of the IF instruction, and append
2064 // that to the InstructionList following the original IF
2065 // instruction
2066 InstructionHandle nextHandle = oldIfHandle.getNext();
2067 IfInstruction invertedIfInst = oldIfInst.negate();
2068 BranchHandle invertedIfHandle = il.append(oldIfHandle,
2069 invertedIfInst);
2070
2071 // Append an unconditional branch to the target of the
2072 // original IF instruction after the new IF instruction
2073 BranchHandle gotoHandle = il.append(invertedIfHandle,
2074 new GOTO(target));
2075
2076 // If the original IF was the last instruction in
2077 // InstructionList, add a new no-op to act as the target
2078 // of the new IF
2079 if (nextHandle == null) {
2080 nextHandle = il.append(gotoHandle, NOP);
2081 }
2082
2083 // Make the new IF instruction branch around the GOTO
2084 invertedIfHandle.updateTarget(target, nextHandle);
2085
2086 // If anything still "points" to the old IF instruction,
2087 // make adjustments to refer to either the new IF or GOTO
2088 // instruction
2089 if (oldIfHandle.hasTargeters()) {
2090 InstructionTargeter[] targeters =
2091 oldIfHandle.getTargeters();
2092
2093 for (int i = 0; i < targeters.length; i++) {
2094 InstructionTargeter targeter = targeters[i];
2095 // Ideally, one should simply be able to use
2096 // InstructionTargeter.updateTarget to change
2097 // references to the old IF instruction to the new
2098 // IF instruction. However, if a LocalVariableGen
2099 // indicated the old IF marked the end of the range
2100 // in which the IF variable is in use, the live
|
1 /*
2 * Copyright (c) 2015, 2017, 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 java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.Map;
31 import java.util.Stack;
32
33
34 import com.sun.org.apache.bcel.internal.Const;
35 import com.sun.org.apache.bcel.internal.classfile.Field;
36 import com.sun.org.apache.bcel.internal.classfile.Method;
37 import com.sun.org.apache.bcel.internal.generic.ALOAD;
38 import com.sun.org.apache.bcel.internal.generic.ASTORE;
39 import com.sun.org.apache.bcel.internal.generic.BranchHandle;
40 import com.sun.org.apache.bcel.internal.generic.BranchInstruction;
41 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
42 import com.sun.org.apache.bcel.internal.generic.DLOAD;
43 import com.sun.org.apache.bcel.internal.generic.DSTORE;
44 import com.sun.org.apache.bcel.internal.generic.FLOAD;
45 import com.sun.org.apache.bcel.internal.generic.FSTORE;
46 import com.sun.org.apache.bcel.internal.generic.GETFIELD;
47 import com.sun.org.apache.bcel.internal.generic.GOTO;
48 import com.sun.org.apache.bcel.internal.generic.ICONST;
49 import com.sun.org.apache.bcel.internal.generic.IfInstruction;
50 import com.sun.org.apache.bcel.internal.generic.ILOAD;
51 import com.sun.org.apache.bcel.internal.generic.IndexedInstruction;
52 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
53 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
54 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
55 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
56 import com.sun.org.apache.bcel.internal.generic.ISTORE;
57 import com.sun.org.apache.bcel.internal.generic.Instruction;
58 import com.sun.org.apache.bcel.internal.generic.InstructionConst;
59 import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
60 import com.sun.org.apache.bcel.internal.generic.InstructionList;
61 import com.sun.org.apache.bcel.internal.generic.InstructionTargeter;
62 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
63 import com.sun.org.apache.bcel.internal.generic.LocalVariableInstruction;
64 import com.sun.org.apache.bcel.internal.generic.LLOAD;
65 import com.sun.org.apache.bcel.internal.generic.LSTORE;
66 import com.sun.org.apache.bcel.internal.generic.MethodGen;
67 import com.sun.org.apache.bcel.internal.generic.NEW;
68 import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
69 import com.sun.org.apache.bcel.internal.generic.RET;
70 import com.sun.org.apache.bcel.internal.generic.Select;
71 import com.sun.org.apache.bcel.internal.generic.TargetLostException;
72 import com.sun.org.apache.bcel.internal.generic.Type;
73
74 import com.sun.org.apache.xalan.internal.xsltc.compiler.Pattern;
75 import com.sun.org.apache.xalan.internal.xsltc.compiler.XSLTC;
76
77 /**
78 * @author Jacek Ambroziak
79 * @author Santiago Pericas-Geertsen
80 */
81 public class MethodGenerator extends MethodGen
82 implements com.sun.org.apache.xalan.internal.xsltc.compiler.Constants {
83 protected static final int INVALID_INDEX = -1;
84
85 private static final String START_ELEMENT_SIG
86 = "(" + STRING_SIG + ")V";
87 private static final String END_ELEMENT_SIG
88 = START_ELEMENT_SIG;
89
90 private static final int DOM_INDEX = 1;
91 private static final int ITERATOR_INDEX = 2;
92 private static final int HANDLER_INDEX = 3;
93
94 private static final int MAX_METHOD_SIZE = 65535;
95 private static final int MAX_BRANCH_TARGET_OFFSET = 32767;
96 private static final int MIN_BRANCH_TARGET_OFFSET = -32768;
97
98 private static final int TARGET_METHOD_SIZE = 60000;
99 private static final int MINIMUM_OUTLINEABLE_CHUNK_SIZE = 1000;
100
101 private Instruction _iloadCurrent;
102 private Instruction _istoreCurrent;
103 private final Instruction _astoreHandler;
104 private final Instruction _aloadHandler;
105 private final Instruction _astoreIterator;
106 private final Instruction _aloadIterator;
107 private final Instruction _aloadDom;
108 private final Instruction _astoreDom;
109
1296 // InstructionLists for copying values into and out of an instance of
1297 // CopyLocals:
1298 // oldMethCoypInIL - from locals in old method into an instance
1299 // of the CopyLocals class (oldMethCopyInIL)
1300 // oldMethCopyOutIL - from CopyLocals back into locals in the old
1301 // method
1302 // newMethCopyInIL - from CopyLocals into locals in the new
1303 // method
1304 // newMethCopyOutIL - from locals in new method into the instance
1305 // of the CopyLocals class
1306 InstructionList oldMethCopyInIL = new InstructionList();
1307 InstructionList oldMethCopyOutIL = new InstructionList();
1308 InstructionList newMethCopyInIL = new InstructionList();
1309 InstructionList newMethCopyOutIL = new InstructionList();
1310
1311 // Allocate instance of class in which we'll copy in or copy out locals
1312 // and make two copies: last copy is used to invoke constructor;
1313 // other two are used for references to fields in the CopyLocals object
1314 InstructionHandle outlinedMethodCallSetup =
1315 oldMethCopyInIL.append(new NEW(cpg.addClass(argTypeName)));
1316 oldMethCopyInIL.append(InstructionConst.DUP);
1317 oldMethCopyInIL.append(InstructionConst.DUP);
1318 oldMethCopyInIL.append(
1319 new INVOKESPECIAL(cpg.addMethodref(argTypeName, "<init>", "()V")));
1320
1321 // Generate code to invoke the new outlined method, and place the code
1322 // on oldMethCopyOutIL
1323 InstructionHandle outlinedMethodRef;
1324
1325 if (isStaticMethod) {
1326 outlinedMethodRef =
1327 oldMethCopyOutIL.append(
1328 new INVOKESTATIC(cpg.addMethodref(
1329 classGen.getClassName(),
1330 outlinedMethodName,
1331 outlinedMethodGen.getSignature())));
1332 } else {
1333 oldMethCopyOutIL.append(InstructionConst.THIS);
1334 oldMethCopyOutIL.append(InstructionConst.SWAP);
1335 outlinedMethodRef =
1336 oldMethCopyOutIL.append(
1337 new INVOKEVIRTUAL(cpg.addMethodref(
1338 classGen.getClassName(),
1339 outlinedMethodName,
1340 outlinedMethodGen.getSignature())));
1341 }
1342
1343 // Used to keep track of the first in a sequence of
1344 // OutlineableChunkStart instructions
1345 boolean chunkStartTargetMappingsPending = false;
1346 InstructionHandle pendingTargetMappingHandle = null;
1347
1348 // Used to keep track of the last instruction that was copied
1349 InstructionHandle lastCopyHandle = null;
1350
1351 // Keeps track of the mapping from instruction handles in the old
1352 // method to instruction handles in the outlined method. Only need
1353 // to track instructions that are targeted by something else in the
1354 // generated BCEL
1460
1461 copyAreaFieldCount++;
1462 String copyAreaFieldName =
1463 "field" + copyAreaFieldCount;
1464 copyAreaCG.addField(
1465 new Field(ACC_PUBLIC,
1466 copyAreaCPG.addUtf8(copyAreaFieldName),
1467 copyAreaCPG.addUtf8(varSignature),
1468 null, copyAreaCPG.getConstantPool()));
1469
1470 int fieldRef = cpg.addFieldref(argTypeName,
1471 copyAreaFieldName,
1472 varSignature);
1473
1474 if (copyInLocalValue) {
1475 // Generate code for the old method to store the
1476 // value of the local into the correct field in
1477 // CopyLocals prior to invocation of the
1478 // outlined method.
1479 oldMethCopyInIL.append(
1480 InstructionConst.DUP);
1481 InstructionHandle copyInLoad =
1482 oldMethCopyInIL.append(
1483 loadLocal(oldLocalVarIndex, varType));
1484 oldMethCopyInIL.append(new PUTFIELD(fieldRef));
1485
1486 // If the end of the live range of the old
1487 // variable was in the middle of the outlined
1488 // chunk. Make the load of its value the new
1489 // end of its range.
1490 if (!copyOutLocalValue) {
1491 revisedLocalVarEnd.put(oldLVG, copyInLoad);
1492 }
1493
1494 // Generate code for start of the outlined
1495 // method to copy the value from a field in
1496 // CopyLocals to the new local in the outlined
1497 // method
1498 newMethCopyInIL.append(
1499 InstructionConst.ALOAD_1);
1500 newMethCopyInIL.append(new GETFIELD(fieldRef));
1501 newMethCopyInIL.append(
1502 storeLocal(newLocalVarIndex, varType));
1503 }
1504
1505 if (copyOutLocalValue) {
1506 // Generate code for the end of the outlined
1507 // method to copy the value from the new local
1508 // variable into a field in CopyLocals
1509 // method
1510 newMethCopyOutIL.append(
1511 InstructionConst.ALOAD_1);
1512 newMethCopyOutIL.append(
1513 loadLocal(newLocalVarIndex, varType));
1514 newMethCopyOutIL.append(new PUTFIELD(fieldRef));
1515
1516 // Generate code to copy the value from a field
1517 // in CopyLocals into a local in the original
1518 // method following invocation of the outlined
1519 // method.
1520 oldMethCopyOutIL.append(
1521 InstructionConst.DUP);
1522 oldMethCopyOutIL.append(new GETFIELD(fieldRef));
1523 InstructionHandle copyOutStore =
1524 oldMethCopyOutIL.append(
1525 storeLocal(oldLocalVarIndex, varType));
1526
1527 // If the start of the live range of the old
1528 // variable was in the middle of the outlined
1529 // chunk. Make this store into it the new start
1530 // of its range.
1531 if (!copyInLocalValue) {
1532 revisedLocalVarStart.put(oldLVG,
1533 copyOutStore);
1534 }
1535 }
1536 }
1537 }
1538 }
1539
1540 if (ih.hasTargeters()) {
1541 targetMap.put(ih, lastCopyHandle);
1648 Object newLVG = localVarMap.get(targeter);
1649 if (newLVG != null) {
1650 outlinedMethodGen.removeLocalVariable(
1651 (LocalVariableGen)newLVG);
1652 }
1653 }
1654 }
1655 }
1656
1657 // If the current instruction in the original list was a marker,
1658 // it wasn't copied, so don't advance through the list of copied
1659 // instructions yet.
1660 if (!(i instanceof MarkerInstruction)) {
1661 ch = ch.getNext();
1662 }
1663 ih = ih.getNext();
1664
1665 }
1666
1667 // POP the reference to the CopyLocals object from the stack
1668 oldMethCopyOutIL.append(InstructionConst.POP);
1669
1670 // Now that the generation of the outlined code is complete, update
1671 // the old local variables with new start and end ranges, as required.
1672 Iterator revisedLocalVarStartPairIter = revisedLocalVarStart.entrySet()
1673 .iterator();
1674 while (revisedLocalVarStartPairIter.hasNext()) {
1675 Map.Entry lvgRangeStartPair =
1676 (Map.Entry)revisedLocalVarStartPairIter.next();
1677 LocalVariableGen lvg = (LocalVariableGen)lvgRangeStartPair.getKey();
1678 InstructionHandle startInst =
1679 (InstructionHandle)lvgRangeStartPair.getValue();
1680
1681 lvg.setStart(startInst);
1682
1683 }
1684
1685 Iterator revisedLocalVarEndPairIter = revisedLocalVarEnd.entrySet()
1686 .iterator();
1687 while (revisedLocalVarEndPairIter.hasNext()) {
1688 Map.Entry lvgRangeEndPair =
1689 (Map.Entry)revisedLocalVarEndPairIter.next();
1690 LocalVariableGen lvg = (LocalVariableGen)lvgRangeEndPair.getKey();
1691 InstructionHandle endInst =
1692 (InstructionHandle)lvgRangeEndPair.getValue();
1693
1694 lvg.setEnd(endInst);
1695 }
1696
1697 xsltc.dumpClass(copyAreaCG.getJavaClass());
1698
1699 // Assemble the instruction lists so that the old method invokes the
1700 // new outlined method
1701 InstructionList oldMethodIL = getInstructionList();
1702
1703 oldMethodIL.insert(first, oldMethCopyInIL);
1704 oldMethodIL.insert(first, oldMethCopyOutIL);
1705
1706 // Insert the copying code into the outlined method
1707 newIL.insert(newMethCopyInIL);
1708 newIL.append(newMethCopyOutIL);
1709 newIL.append(InstructionConst.RETURN);
1710
1711 // Discard instructions in outlineable chunk from old method
1712 try {
1713 oldMethodIL.delete(first, last);
1714 } catch (TargetLostException e) {
1715 InstructionHandle[] targets = e.getTargets();
1716 // If there were still references to old instructions lingering,
1717 // clean those up. The only instructions targetting the deleted
1718 // instructions should have been part of the chunk that was just
1719 // deleted, except that instructions might branch to the start of
1720 // the outlined chunk; similarly, all the live ranges of local
1721 // variables should have been adjusted, except for unreferenced
1722 // variables.
1723 for (int i = 0; i < targets.length; i++) {
1724 InstructionHandle lostTarget = targets[i];
1725 InstructionTargeter[] targeters = lostTarget.getTargeters();
1726 for (int j = 0; j < targeters.length; j++) {
1727 if (targeters[j] instanceof LocalVariableGen) {
1728 LocalVariableGen lvgTargeter =
1729 (LocalVariableGen) targeters[j];
1980 InstructionList il = getInstructionList();
1981
1982 // Loop through all the instructions, finding those that would be
1983 // affected by inserting new instructions in the InstructionList, and
1984 // calculating the maximum amount by which the relative offset between
1985 // two instructions could possibly change.
1986 // In part this loop duplicates code in
1987 // org.apache.bcel.generic.InstructionList.setPosition(), which does
1988 // this to determine whether to use 16-bit or 32-bit offsets for GOTO
1989 // and JSR instructions. Ideally, that method would do the same for
1990 // conditional branch instructions, but it doesn't, so we duplicate the
1991 // processing here.
1992 for (InstructionHandle ih = il.getStart();
1993 ih != null;
1994 ih = ih.getNext()) {
1995 Instruction inst = ih.getInstruction();
1996
1997 switch (inst.getOpcode()) {
1998 // Instructions that may have 16-bit or 32-bit branch targets.
1999 // The size of the branch offset might increase by two bytes.
2000 case Const.GOTO:
2001 case Const.JSR:
2002 maxOffsetChange = maxOffsetChange + 2;
2003 break;
2004 // Instructions that contain padding for alignment purposes
2005 // Up to three bytes of padding might be needed. For greater
2006 // accuracy, we should be able to discount any padding already
2007 // added to these instructions by InstructionList.setPosition(),
2008 // their APIs do not expose that information.
2009 case Const.TABLESWITCH:
2010 case Const.LOOKUPSWITCH:
2011 maxOffsetChange = maxOffsetChange + 3;
2012 break;
2013 // Instructions that might be rewritten by this method as a
2014 // conditional branch followed by an unconditional branch.
2015 // The unconditional branch would require five bytes.
2016 case Const.IF_ACMPEQ:
2017 case Const.IF_ACMPNE:
2018 case Const.IF_ICMPEQ:
2019 case Const.IF_ICMPGE:
2020 case Const.IF_ICMPGT:
2021 case Const.IF_ICMPLE:
2022 case Const.IF_ICMPLT:
2023 case Const.IF_ICMPNE:
2024 case Const.IFEQ:
2025 case Const.IFGE:
2026 case Const.IFGT:
2027 case Const.IFLE:
2028 case Const.IFLT:
2029 case Const.IFNE:
2030 case Const.IFNONNULL:
2031 case Const.IFNULL:
2032 maxOffsetChange = maxOffsetChange + 5;
2033 break;
2034 }
2035 }
2036
2037 // Now that the maximum number of bytes by which the method might grow
2038 // has been determined, look for conditional branches to see which
2039 // might possibly exceed the 16-bit relative offset.
2040 for (InstructionHandle ih = il.getStart();
2041 ih != null;
2042 ih = ih.getNext()) {
2043 Instruction inst = ih.getInstruction();
2044
2045 if (inst instanceof IfInstruction) {
2046 IfInstruction oldIfInst = (IfInstruction)inst;
2047 BranchHandle oldIfHandle = (BranchHandle)ih;
2048 InstructionHandle target = oldIfInst.getTarget();
2049 int relativeTargetOffset = target.getPosition()
2050 - oldIfHandle.getPosition();
2051
2058 < MIN_BRANCH_TARGET_OFFSET)
2059 || (relativeTargetOffset + maxOffsetChange
2060 > MAX_BRANCH_TARGET_OFFSET)) {
2061 // Invert the logic of the IF instruction, and append
2062 // that to the InstructionList following the original IF
2063 // instruction
2064 InstructionHandle nextHandle = oldIfHandle.getNext();
2065 IfInstruction invertedIfInst = oldIfInst.negate();
2066 BranchHandle invertedIfHandle = il.append(oldIfHandle,
2067 invertedIfInst);
2068
2069 // Append an unconditional branch to the target of the
2070 // original IF instruction after the new IF instruction
2071 BranchHandle gotoHandle = il.append(invertedIfHandle,
2072 new GOTO(target));
2073
2074 // If the original IF was the last instruction in
2075 // InstructionList, add a new no-op to act as the target
2076 // of the new IF
2077 if (nextHandle == null) {
2078 nextHandle = il.append(gotoHandle, InstructionConst.NOP);
2079 }
2080
2081 // Make the new IF instruction branch around the GOTO
2082 invertedIfHandle.updateTarget(target, nextHandle);
2083
2084 // If anything still "points" to the old IF instruction,
2085 // make adjustments to refer to either the new IF or GOTO
2086 // instruction
2087 if (oldIfHandle.hasTargeters()) {
2088 InstructionTargeter[] targeters =
2089 oldIfHandle.getTargeters();
2090
2091 for (int i = 0; i < targeters.length; i++) {
2092 InstructionTargeter targeter = targeters[i];
2093 // Ideally, one should simply be able to use
2094 // InstructionTargeter.updateTarget to change
2095 // references to the old IF instruction to the new
2096 // IF instruction. However, if a LocalVariableGen
2097 // indicated the old IF marked the end of the range
2098 // in which the IF variable is in use, the live
|