📄 bcmethod.java
字号:
/* Derby - Class org.apache.derby.impl.services.bytecode.BCMethod Copyright 1997, 2005 The Apache Software Foundation or its licensors, as applicable. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */package org.apache.derby.impl.services.bytecode;import org.apache.derby.iapi.services.compiler.ClassBuilder;import org.apache.derby.iapi.services.compiler.MethodBuilder;import org.apache.derby.iapi.services.classfile.ClassFormatOutput;import org.apache.derby.iapi.services.compiler.LocalField;import org.apache.derby.iapi.services.classfile.ClassHolder;import org.apache.derby.iapi.services.classfile.ClassMember;import org.apache.derby.iapi.services.sanity.SanityManager;import org.apache.derby.iapi.services.classfile.VMDescriptor;import org.apache.derby.iapi.services.classfile.VMOpcode;import java.lang.reflect.Modifier;import java.util.Vector;import java.io.IOException;/** * MethodBuilder is used to piece together a method when * building a java class definition. * <p> * When a method is first created, it has: * <ul> * <li> a return type * <li> modifiers * <li> a name * <li> an empty parameter list * <li> an empty throws list * <li> an empty statement block * </ul> * <p> * MethodBuilder implementations are required to supply a way for * Statements and Expressions to give them code. Most typically, they may have * a stream to which their contents writes the code that is of * the type to satisfy what the contents represent. * MethodBuilder implementations also have to have a way to supply * ClassBuilders with their code, that satisfies the type of class * builder they are implemented with. This is implementation-dependent, * so ClassBuilders, MethodBuilders, Statements, and Expressions all have * to be of the same implementation in order to interact to generate a class. * <p> * Method Builder implementation for generating bytecode. * */public class BCMethod implements MethodBuilder { final BCClass cb; protected final ClassHolder modClass; // the class it is in (modifiable fmt) private final String myReturnType; /** * The original name of the method, this * represents how any user would call this method. */ private final String myName; protected BCLocalField[] parameters; protected Vector thrownExceptions; // expected to be names of Classes under Throwable CodeChunk myCode; protected ClassMember myEntry; private int currentVarNum; private int statementNum; /** * True if we are currently switching control * over to a sub method to avoid hitting the code generation * limit of 65535 bytes per method. */ private boolean handlingOverflow; /** * How many sub-methods we have overflowed to. */ private int subMethodCount; BCMethod(ClassBuilder cb, String returnType, String methodName, int modifiers, String[] parms, BCJava factory) { this.cb = (BCClass) cb; modClass = this.cb.modify(); myReturnType = returnType; myName = methodName; if (SanityManager.DEBUG) { this.cb.validateType(returnType); } // if the method is not static, allocate for "this". if ((modifiers & Modifier.STATIC) == 0 ) currentVarNum = 1; String[] vmParamterTypes; if (parms != null) { int len = parms.length; vmParamterTypes = new String[len]; parameters = new BCLocalField[len]; for (int i = 0; i < len; i++) { Type t = factory.type(parms[i]); parameters[i] = new BCLocalField(t, currentVarNum); currentVarNum += t.width(); // convert to vmname for the BCMethodDescriptor.get() call vmParamterTypes[i] = t.vmName(); } } else vmParamterTypes = BCMethodDescriptor.EMPTY; // create a code attribute String sig = BCMethodDescriptor.get(vmParamterTypes, factory.type(returnType).vmName(), factory); // stuff the completed information into the class. myEntry = modClass.addMember(methodName, sig, modifiers); // get code chunk myCode = new CodeChunk(true); } // // MethodBuilder interface // /** * Return the logical name of the method. The current * myEntry refers to the sub method we are currently * overflowing to. Those sub-methods are hidden from any caller. */ public String getName() { return myName; } public void getParameter(int id) { int num = parameters[id].cpi; short typ = parameters[id].type.vmType(); if (num < 4) myCode.addInstr((short) (CodeChunk.LOAD_VARIABLE_FAST[typ]+num)); else myCode.addInstrWide(CodeChunk.LOAD_VARIABLE[typ], num); growStack(parameters[id].type); } /** * a throwable can be added to the end of * the list of thrownExceptions. */ public void addThrownException(String exceptionClass) { // cannot add exceptions after code generation has started. // Allowing this would cause the method overflow/split to // break as the top-level method would not have the exception // added in the sub method. if (SanityManager.DEBUG) { if (myCode.getRelativePC() != 0) SanityManager.THROWASSERT("Adding exception after code generation " + exceptionClass + " to method " + getName()); } if (thrownExceptions == null) thrownExceptions = new Vector(); thrownExceptions.addElement(exceptionClass); } /** * when the method has had all of its parameters * and thrown exceptions defined, and its statement * block has been completed, it can be completed and * its class file information generated. * <p> * further alterations of the method will not be * reflected in the code generated for it. */ public void complete() { // write exceptions attribute info writeExceptions(); // get the code attribute to put itself into the class // provide the final header information needed myCode.complete(modClass, myEntry, maxStack, currentVarNum); } /* * class interface */ /** * In their giveCode methods, the parts of the method body * will want to get to the constant pool to add their constants. * We really only want them treating it like a constant pool * inclusion mechanism, we could write a wrapper to limit it to that. */ ClassHolder constantPool() { return modClass; } // // Class implementation // /** * sets exceptionBytes to the attribute_info needed * for a method's Exceptions attribute. * The ClassUtilities take care of the header 6 bytes for us, * so they are not included here. * See The Java Virtual Machine Specification Section 4.7.5, * Exceptions attribute. */ protected void writeExceptions() { if (thrownExceptions == null) return; int numExc = thrownExceptions.size(); // don't write an Exceptions attribute if there are no exceptions. if (numExc != 0) { try{ ClassFormatOutput eout = new ClassFormatOutput((numExc * 2) + 2); eout.putU2(numExc); // number_of_exceptions for (int i = 0; i < numExc; i++) { // put each exception into the constant pool String e = thrownExceptions.elementAt(i).toString(); int ei2 = modClass.addClassReference(e); // add constant pool index to exception attribute_info eout.putU2(ei2); } myEntry.addAttribute("Exceptions", eout); } catch (IOException ioe) { } } } /* ** New push compiler api. */ private Type[] stackTypes = new Type[8]; private int stackTypeOffset; private int maxStack; private int stackDepth; // public Stack stackTypes = new Stack(); private void growStack(int size, Type type) { stackDepth += size; if (stackDepth > maxStack) maxStack = stackDepth; if (stackTypeOffset >= stackTypes.length) { Type[] newStackTypes = new Type[stackTypes.length + 8]; System.arraycopy(stackTypes, 0, newStackTypes, 0, stackTypes.length); stackTypes = newStackTypes; } stackTypes[stackTypeOffset++] = type; if (SanityManager.DEBUG) { int sum = 0; for (int i = 0 ; i < stackTypeOffset; i++) { sum += stackTypes[i].width(); } if (sum != stackDepth) { SanityManager.THROWASSERT("invalid stack depth " + stackDepth + " calc " + sum); } } } private void growStack(Type type) { growStack(type.width(), type); } private Type popStack() { stackTypeOffset--; Type topType = stackTypes[stackTypeOffset]; stackDepth -= topType.width(); return topType; } public void pushThis() { myCode.addInstr(VMOpcode.ALOAD_0); growStack(1, cb.classType); } public void push(byte value) { push(value, Type.BYTE); } public void push(boolean value) { push(value ? 1 : 0, Type.BOOLEAN); } public void push(short value) { push(value, Type.SHORT); } public void push(int value) { push(value, Type.INT); } public void dup() { Type dup = popStack(); myCode.addInstr(dup.width() == 2 ? VMOpcode.DUP2 : VMOpcode.DUP); growStack(dup); growStack(dup); } public void swap() { // have A,B // want B,A Type wB = popStack(); Type wA = popStack(); growStack(wB); growStack(wA); if (wB.width() == 1) { // top value is one word if (wA.width() == 1) { myCode.addInstr(VMOpcode.SWAP); return; } else { myCode.addInstr(VMOpcode.DUP_X2); myCode.addInstr(VMOpcode.POP); } } else { // top value is two words if (wA.width() == 1) { myCode.addInstr(VMOpcode.DUP2_X1); myCode.addInstr(VMOpcode.POP2); } else { myCode.addInstr(VMOpcode.DUP2_X2); myCode.addInstr(VMOpcode.POP2); } } // all except the simple swap push an extra // copy of B which needs to be popped. growStack(wB); popStack(); } private void push(int value, Type type) { CodeChunk chunk = myCode; if (value >= -1 && value <= 5) chunk.addInstr((short)(VMOpcode.ICONST_0+value)); else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) chunk.addInstrU1(VMOpcode.BIPUSH,value); else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) chunk.addInstrU2(VMOpcode.SIPUSH,value); else { int cpe = modClass.addConstant(value); chunk.addInstrCPE(VMOpcode.LDC, cpe); } growStack(1, type); } public void push(long value){ CodeChunk chunk = myCode; if (value == 0 || value == 1) { chunk.addInstr((short)(VMOpcode.LCONST_0+(short)value)); } else { int cpe = modClass.addConstant(value); chunk.addInstrU2(VMOpcode.LDC2_W, cpe); } growStack(2, Type.LONG); } public void push(float value) { CodeChunk chunk = myCode; if (value == 0.0) { chunk.addInstr(VMOpcode.FCONST_0); } else if (value == 1.0) { chunk.addInstr(VMOpcode.FCONST_1); } else if (value == 2.0) { chunk.addInstr(VMOpcode.FCONST_2); } else { int cpe = modClass.addConstant(value); chunk.addInstrCPE(VMOpcode.LDC, cpe); } growStack(1, Type.FLOAT); } public void push(double value) { CodeChunk chunk = myCode; if (value == 0.0) { chunk.addInstr(VMOpcode.DCONST_0); } else { int cpe = modClass.addConstant(value); chunk.addInstrU2(VMOpcode.LDC2_W, cpe); } growStack(2, Type.DOUBLE); } public void push(String value) { int cpe = modClass.addConstant(value); myCode.addInstrCPE(VMOpcode.LDC, cpe); growStack(1, Type.STRING); } public void methodReturn() { short opcode; if (stackDepth != 0) { Type topType = popStack(); opcode = CodeChunk.RETURN_OPCODE[topType.vmType()]; } else { opcode = VMOpcode.RETURN; } myCode.addInstr(opcode); if (SanityManager.DEBUG) { if (stackDepth != 0) SanityManager.THROWASSERT("items left on stack " + stackDepth); } } public Object describeMethod(short opcode, String declaringClass, String methodName, String returnType) { Type rt = cb.factory.type(returnType); String methodDescriptor = BCMethodDescriptor.get(BCMethodDescriptor.EMPTY, rt.vmName(), cb.factory); if ((declaringClass == null) && (opcode != VMOpcode.INVOKESTATIC)) { Type dt = stackTypes[stackTypeOffset - 1]; if (declaringClass == null) declaringClass = dt.javaName(); } int cpi = modClass.addMethodReference(declaringClass, methodName, methodDescriptor, opcode == VMOpcode.INVOKEINTERFACE); return new BCMethodCaller(opcode, rt, cpi); } public int callMethod(Object methodDescriptor) { // pop the reference off the stack popStack(); BCMethodCaller mc = (BCMethodCaller) methodDescriptor; int cpi = mc.cpi; short opcode = mc.opcode; if (opcode == VMOpcode.INVOKEINTERFACE) { myCode.addInstrU2U1U1(opcode, cpi, (short) 1, (short) 0); } else myCode.addInstrU2(opcode, cpi); // this is the return type of the method Type rt = mc.type; int rw = rt.width(); if (rw != 0) growStack(rw, rt); else { if (stackDepth == 0) overflowMethodCheck(); } return cpi; } public int callMethod(short opcode, String declaringClass, String methodName, String returnType, int numArgs) { Type rt = cb.factory.type(returnType); int initialStackDepth = stackDepth; // get the array of parameter types String [] debugParameterTypes = null; String[] vmParameterTypes; if (numArgs == 0) { vmParameterTypes = BCMethodDescriptor.EMPTY; } else { if (SanityManager.DEBUG) { debugParameterTypes = new String[numArgs]; } vmParameterTypes = new String[numArgs]; for (int i = numArgs - 1; i >= 0; i--) { Type at = popStack(); vmParameterTypes[i] = at.vmName(); if (SanityManager.DEBUG) { debugParameterTypes[i] = at.javaName(); } } } String methodDescriptor = BCMethodDescriptor.get(vmParameterTypes, rt.vmName(), cb.factory); Type dt = null; if (opcode != VMOpcode.INVOKESTATIC) { dt = popStack(); } Type dtu = vmNameDeclaringClass(declaringClass); if (dtu != null) dt = dtu; int cpi = modClass.addMethodReference(dt.vmNameSimple, methodName, methodDescriptor, opcode == VMOpcode.INVOKEINTERFACE); if (opcode == VMOpcode.INVOKEINTERFACE) { short callArgCount = (short) (initialStackDepth - stackDepth); myCode.addInstrU2U1U1(opcode, cpi, callArgCount, (short) 0); } else myCode.addInstrU2(opcode, cpi); // this is the return type of the method int rw = rt.width(); if (rw != 0) growStack(rw, rt); else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -