package js.tinyvm; import java.io.IOException; import java.util.logging.Logger; import java.util.HashSet; import java.util.Iterator; import js.tinyvm.io.IByteWriter; import js.tinyvm.io.IOUtilities; import js.tinyvm.util.HashVector; import org.apache.bcel.Constants; import org.apache.bcel.classfile.Code; import org.apache.bcel.classfile.CodeException; import org.apache.bcel.classfile.JavaClass; import org.apache.bcel.classfile.Method; import org.apache.bcel.generic.Type; public class MethodRecord implements WritableData { Method iMethod; ClassRecord iClassRecord; RecordTable<ExceptionRecord> iExceptionTable = null; CodeSequence iCodeSequence = null; int iSignatureId; // DONE int iNumLocals; // DONE int iNumOperands; // DONE int iNumParameters; // DONE int iNumExceptionHandlers; // DONE int iFlags; // DONE boolean isCalled; int iCodeStart; HashSet<MethodRecord> iIsHiddenBy = new HashSet<MethodRecord>(); int markCount = -1; public MethodRecord (Method aEntry, Signature aSignature, ClassRecord aClassRec, Binary aBinary, RecordTable<RecordTable<ExceptionRecord>> aExceptionTables, HashVector<Signature> aSignatures) throws TinyVMException { iClassRecord = aClassRec; iMethod = aEntry; Code pCodeAttrib = iMethod.getCode(); boolean pNoBody = iMethod.isAbstract() || iMethod.isNative(); assert pCodeAttrib != null || pNoBody: "Check: body is present"; assert pCodeAttrib == null || !pNoBody: "Check: no body is present"; aSignatures.addElement(aSignature); iSignatureId = aSignatures.indexOf(aSignature); if (iSignatureId >= TinyVMConstants.MAX_SIGNATURES) { throw new TinyVMException( "The total number of unique signatures exceeds " + TinyVMConstants.MAX_SIGNATURES); } iNumLocals = pCodeAttrib == null? 0 : pCodeAttrib.getMaxLocals(); if (iNumLocals > TinyVMConstants.MAX_LOCALS) { throw new TinyVMException("Method " + aClassRec.getName() + "." + iMethod.getName() + " has " + iNumLocals + " local words. Only " + TinyVMConstants.MAX_LOCALS + " are allowed."); } iNumOperands = pCodeAttrib == null? 0 : pCodeAttrib.getMaxStack(); if (iNumOperands > TinyVMConstants.MAX_OPERANDS) { throw new TinyVMException("Method " + aClassRec.getName() + "." + iMethod.getName() + " has an operand stack " + " whose potential size is " + iNumOperands + ". " + "Only " + TinyVMConstants.MAX_OPERANDS + " are allowed."); } iNumParameters = getNumParamWords(iMethod); if (iNumParameters > TinyVMConstants.MAX_PARAMETER_WORDS) { throw new TinyVMException("Method " + aClassRec.getName() + "." + iMethod.getName() + " has " + iNumParameters + " parameter words. Only " + TinyVMConstants.MAX_PARAMETER_WORDS + " are allowed."); } if (iMethod.isNative() && !aBinary.isSpecialSignature(aSignature)) { throw new TinyVMException("Method " + aClassRec.getName() + "." + iMethod.getName() + " is an unknown native method." + " You are probably using JDK APIs" + " or libraries that cannot be run under leJOS."); } if (pCodeAttrib != null) { iExceptionTable = new RecordTable<ExceptionRecord>("exceptions", true, false); CodeException[] pExcepTable = pCodeAttrib.getExceptionTable(); iNumExceptionHandlers = pExcepTable.length; if (iNumExceptionHandlers > TinyVMConstants.MAX_EXCEPTION_HANDLERS) { throw new TinyVMException("Method " + aClassRec.getName() + "." + iMethod.getName() + " has " + iNumExceptionHandlers + " exception handlers. Only " + TinyVMConstants.MAX_EXCEPTION_HANDLERS + " are allowed."); } storeExceptionTable(pExcepTable, aBinary, aClassRec.iCF); aExceptionTables.add(iExceptionTable); } initFlags(); } public int getFlags () { return iFlags; } public ClassRecord getClassRecord () { return iClassRecord; } public void initFlags () { iFlags = 0; if (iMethod.isNative()) iFlags |= TinyVMConstants.M_NATIVE; if (iMethod.isSynchronized()) iFlags |= TinyVMConstants.M_SYNCHRONIZED; if (iMethod.isStatic()) iFlags |= TinyVMConstants.M_STATIC; } public void copyCode (RecordTable<CodeSequence> aCodeSequences, JavaClass aClassFile, Binary aBinary) { Code pCodeAttrib = iMethod.getCode(); if (pCodeAttrib != null) { iCodeSequence = new CodeSequence(); copyCode(pCodeAttrib.getCode(), aClassFile, aBinary); aCodeSequences.add(iCodeSequence); } } public void postProcessCode (RecordTable<CodeSequence> aCodeSequences, JavaClass aClassFile, Binary aBinary) throws TinyVMException { Code pCodeAttrib = iMethod.getCode(); if (pCodeAttrib != null) { postProcessCode(pCodeAttrib.getCode(), aClassFile, aBinary); iCodeStart = (iCodeSequence == null? 0 : iCodeSequence.getOffset()); } } /** * Number of parameter words, including <code>this</code>. * * @param aMethod bcel method object */ public static int getNumParamWords (Method aMethod) { Type[] types = aMethod.getArgumentTypes(); int pWords = 0; for (int i = 0; i < types.length; i++) { assert types[i].getType() <= 0xF: "Check: known type"; switch (types[i].getType()) { case Constants.T_LONG: case Constants.T_DOUBLE: pWords += 2; break; default: pWords++; } } return pWords + (aMethod.isStatic()? 0 : 1); } public void storeExceptionTable (CodeException[] aExcepTable, Binary aBinary, JavaClass aCF) { for (int i = 0; i < aExcepTable.length; i++) { CodeException pExcep = aExcepTable[i]; try { iExceptionTable.add(new ExceptionRecord(pExcep, aBinary, aCF)); } catch (Throwable t) { t.printStackTrace(); } } } public void copyCode (byte[] aCode, JavaClass aClassFile, Binary aBinary) { if (aCode == null) return; iCodeSequence.setBytes(aCode); } public void postProcessCode (byte[] aCode, JavaClass aClassFile, Binary aBinary) throws TinyVMException { if (aCode == null) return; CodeUtilities pUtils = new CodeUtilities(iMethod.getName().toString(), aClassFile, aBinary); byte[] pNewCode = pUtils.processCode(aCode); iCodeSequence.setBytes(pNewCode); } public int getLength () { return IOUtilities.adjustedSize(2 + // signature 2 + // exception table offset 2 + // code offset 1 + // number of locals 1 + // max. operands 1 + // number of parameters 1 + // number of exception handlers 1, // flags 2); } public void dump (IByteWriter aOut) throws TinyVMException { try { aOut.writeU2(iSignatureId); aOut.writeU2(iExceptionTable == null? 0 : iExceptionTable.getOffset()); iCodeStart = (iCodeSequence == null? 0 : iCodeSequence.getOffset()); aOut.writeU2(iCodeSequence == null? 0 : iCodeSequence.getOffset()); aOut.writeU1(iNumLocals); aOut.writeU1(iNumOperands); aOut.writeU1(iNumParameters); aOut.writeU1(iNumExceptionHandlers); aOut.writeU1(iFlags); IOUtilities.writePadding(aOut, 2); } catch (IOException e) { throw new TinyVMException(e.getMessage(), e); } } public int getNumParameterWords () { return iNumParameters; } public int getSignatureId () { return iSignatureId; } public boolean equals (Object aOther) { if (!(aOther instanceof MethodRecord)) return false; MethodRecord mr = (MethodRecord)aOther; return mr.iMethod.equals(iMethod) && mr.iClassRecord.equals(iClassRecord); } public int hashCode () { return iMethod.hashCode() ^ iClassRecord.hashCode(); } private static final Logger _logger = Logger.getLogger("TinyVM"); public void markCalled (JavaClass aClassFile, Binary aBinary) throws TinyVMException { // Mark the current method as being called. Then process the associated // byte code looking for other called methods. // _logger.log(Level.INFO, "Marking :" + iClassRecord.getName() + " : " + iMethod.getName()); if (isCalled && markCount == aBinary.getGeneration()) return; isCalled = true; markCount = aBinary.getGeneration(); for (Iterator<MethodRecord> iter = iIsHiddenBy.iterator(); iter.hasNext();) { MethodRecord pMeth = iter.next(); // _logger.log(Level.INFO, "Marking sub method " + pMeth.iMethod.getName()); pMeth.iClassRecord.markMethod(pMeth, false); } Code pCodeAttrib = iMethod.getCode(); if (pCodeAttrib == null) return; byte [] aCode = pCodeAttrib.getCode(); if (aCode == null) return; CodeUtilities pUtils = new CodeUtilities(iMethod.getName().toString(), aClassFile, aBinary); pUtils.processCalls(aCode, aClassFile, aBinary); } public boolean isCalled() { return isCalled; } public int getCodeStart() { return iCodeStart; } public void setHiddenBy(MethodRecord pRec) { // Mark this method as being hidden by a sub-class method. // We need to mark all such methods if this method is ever marked. // _logger.log(Level.INFO, "Count is " + iIsHiddenBy.size()); if (!iIsHiddenBy.contains(pRec)) { iIsHiddenBy.add(pRec); } } public RecordTable<ExceptionRecord> getExceptions() { return iExceptionTable; } }