From d7cfa79a398653508229fad9b43cffe38bf06fa2 Mon Sep 17 00:00:00 2001 From: aclement Date: Thu, 28 Aug 2008 00:02:13 +0000 Subject: [PATCH] moved some stuff into verifier package from base bcel (eg. visitor) --- .../bcel/verifier/InstructionWalker.java | 459 ++++++++ .../bcel/verifier/statics/Pass2Verifier.java | 7 +- .../bcel/verifier/statics/Pass3aVerifier.java | 1010 +++++++++-------- .../statics/StringRepresentation.java | 5 +- .../structurals/ControlFlowGraph.java | 299 +++-- .../structurals/ExecutionVisitor.java | 5 +- .../structurals/InstConstraintVisitor.java | 5 +- .../verifier/structurals/Subroutines.java | 4 +- .../bcel/verifier/util/BCELFactory.java | 548 +++++---- .../bcel/verifier/util/InstructionFinder.java | 50 +- 10 files changed, 1421 insertions(+), 971 deletions(-) create mode 100644 bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/InstructionWalker.java diff --git a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/InstructionWalker.java b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/InstructionWalker.java new file mode 100644 index 000000000..19a888cbd --- /dev/null +++ b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/InstructionWalker.java @@ -0,0 +1,459 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, i list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, i list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "i product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, i acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" and + * "Apache BCEL" must not be used to endorse or promote products + * derived from i software without prior written permission. For + * written permission, please contact apache@apache.org. + * + * 5. Products derived from i software may not be called "Apache", + * "Apache BCEL", nor may "Apache" appear in their name, without + * prior written permission of the Apache Software Foundation. + * + * i SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF i SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * i software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.aspectj.apache.bcel.verifier; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.generic.FieldInstruction; +import org.aspectj.apache.bcel.generic.InstVisitor; +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionBranch; +import org.aspectj.apache.bcel.generic.InstructionLV; +import org.aspectj.apache.bcel.generic.InvokeInstruction; + +/** + * Traverse an instruction + * + * @author Andy Clement + */ +public class InstructionWalker implements Constants { + + /** + * Call corresponding visitor method(s). The order is: Call visitor methods of implemented interfaces first, then call methods + * according to the class hierarchy in descending order, i.e., the most specific visitXXX() call comes last. + * + * @param i the instruction to visit + * @param v Visitor object + */ + public static void accept(Instruction i, InstVisitor v) { + switch (i.opcode) { + case IMPDEP1: + v.visitIMPDEP1(i); + break; + case IMPDEP2: + v.visitIMPDEP2(i); + break; + case MONITORENTER: + v.visitExceptionThrower(i); + v.visitStackConsumer(i); + v.visitMONITORENTER(i); + break; + case MONITOREXIT: + v.visitExceptionThrower(i); + v.visitStackConsumer(i); + v.visitMONITOREXIT(i); + break; + case LCMP: + v.visitTypedInstruction(i); + v.visitStackProducer(i); + v.visitStackConsumer(i); + v.visitLCMP(i); + break; + case FCMPL: + v.visitTypedInstruction(i); + v.visitStackProducer(i); + v.visitStackConsumer(i); + v.visitFCMPL(i); + break; + case FCMPG: + v.visitTypedInstruction(i); + v.visitStackProducer(i); + v.visitStackConsumer(i); + v.visitFCMPG(i); + break; + case DCMPL: + v.visitTypedInstruction(i); + v.visitStackProducer(i); + v.visitStackConsumer(i); + v.visitDCMPL(i); + break; + case DCMPG: + v.visitTypedInstruction(i); + v.visitStackProducer(i); + v.visitStackConsumer(i); + v.visitDCMPG(i); + break; + case NOP: + v.visitNOP(i); + break; + case BREAKPOINT: + v.visitBREAKPOINT(i); + break; + case SWAP: + v.visitStackConsumer(i); + v.visitStackProducer(i); + v.visitStackInstruction(i); + v.visitSWAP(i); + break; + case POP: + v.visitStackConsumer(i); + v.visitPopInstruction(i); + v.visitStackInstruction(i); + v.visitPOP(i); + break; + case POP2: + v.visitStackConsumer(i); + v.visitPopInstruction(i); + v.visitStackInstruction(i); + v.visitPOP2(i); + break; + case DUP2_X1: + v.visitStackInstruction(i); + v.visitDUP2_X1(i); + break; + case DUP2_X2: + v.visitStackInstruction(i); + v.visitDUP2_X2(i); + break; + case DUP2: + v.visitStackProducer(i); + v.visitPushInstruction(i); + v.visitStackInstruction(i); + v.visitDUP2(i); + break; + case DUP_X1: + v.visitStackInstruction(i); + v.visitDUP_X1(i); + break; + case DUP_X2: + v.visitStackInstruction(i); + v.visitDUP_X2(i); + break; + case DUP: + v.visitStackProducer(i); + v.visitPushInstruction(i); + v.visitStackInstruction(i); + v.visitDUP(i); + break; + case BASTORE: + v.visitStackConsumer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitBASTORE(i); + break; + case CASTORE: + v.visitStackConsumer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitCASTORE(i); + break; + case SASTORE: + v.visitStackConsumer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitSASTORE(i); + break; + case DASTORE: + v.visitStackConsumer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitDASTORE(i); + break; + case FASTORE: + v.visitStackConsumer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitFASTORE(i); + break; + case LASTORE: + v.visitStackConsumer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitLASTORE(i); + break; + case IASTORE: + v.visitStackConsumer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitIASTORE(i); + break; + case AASTORE: + v.visitStackConsumer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitAASTORE(i); + break; + case SALOAD: + v.visitStackProducer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitSALOAD(i); + break; + case CALOAD: + v.visitStackProducer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitCALOAD(i); + break; + case DALOAD: + v.visitStackProducer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitDALOAD(i); + break; + case FALOAD: + v.visitStackProducer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitFALOAD(i); + break; + case LALOAD: + v.visitStackProducer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitLALOAD(i); + break; + case AALOAD: + v.visitStackProducer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitAALOAD(i); + break; + case ATHROW: + v.visitUnconditionalBranch(i); + v.visitExceptionThrower(i); + v.visitATHROW(i); + break; + case ACONST_NULL: + v.visitStackProducer(i); + v.visitPushInstruction(i); + v.visitTypedInstruction(i); + v.visitACONST_NULL(i); + break; + case ICONST_M1: + case ICONST_0: + case ICONST_1: + case ICONST_2: + case ICONST_3: + case ICONST_4: + case ICONST_5: + v.visitPushInstruction(i); + v.visitStackProducer(i); + v.visitTypedInstruction(i); + v.visitConstantPushInstruction(i); + v.visitICONST(i); + break; + case LCONST_0: + case LCONST_1: + v.visitPushInstruction(i); + v.visitStackProducer(i); + v.visitTypedInstruction(i); + v.visitConstantPushInstruction(i); + v.visitLCONST(i); + break; + case FCONST_0: + case FCONST_1: + case FCONST_2: + v.visitPushInstruction(i); + v.visitStackProducer(i); + v.visitTypedInstruction(i); + v.visitConstantPushInstruction(i); + v.visitFCONST(i); + break; + case DCONST_0: + case DCONST_1: + v.visitPushInstruction(i); + v.visitStackProducer(i); + v.visitTypedInstruction(i); + v.visitConstantPushInstruction(i); + v.visitDCONST(i); + break; + case BALOAD: + v.visitStackProducer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitBALOAD(i); + break; + case IALOAD: + v.visitStackProducer(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitArrayInstruction(i); + v.visitIALOAD(i); + break; + case BIPUSH: + v.visitPushInstruction(i); + v.visitStackProducer(i); + v.visitTypedInstruction(i); + v.visitConstantPushInstruction(i); + v.visitBIPUSH(i); + break; + case SIPUSH: + v.visitPushInstruction(i); + v.visitStackProducer(i); + v.visitTypedInstruction(i); + v.visitConstantPushInstruction(i); + v.visitSIPUSH(i); + break; + case LDC: + case LDC_W: + v.visitStackProducer(i); + v.visitPushInstruction(i); + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitCPInstruction(i); + v.visitLDC(i); + break; + case LDC2_W: + v.visitStackProducer(i); + v.visitPushInstruction(i); + v.visitTypedInstruction(i); + v.visitCPInstruction(i); + v.visitLDC2_W(i); + break; + case ARRAYLENGTH: + v.visitExceptionThrower(i); + v.visitStackProducer(i); + v.visitARRAYLENGTH(i); + break; + case ASTORE_0: + v.visitStackConsumer(i); + v.visitPopInstruction(i); + v.visitStoreInstruction(i); + v.visitTypedInstruction(i); + v.visitLocalVariableInstruction((InstructionLV) i); + v.visitStoreInstruction(i); + v.visitASTORE(i); + break; + case ALOAD_0: + v.visitStackConsumer(i); + v.visitPopInstruction(i); + v.visitStoreInstruction(i); + v.visitTypedInstruction(i); + v.visitLocalVariableInstruction((InstructionLV) i); + v.visitStoreInstruction(i); + v.visitALOAD(i); + break; + // for store instructions: ISTORE > ASTORE_3 - needs to visit the instruction too + // v.visitStackConsumer(i); + // v.visitPopInstruction(i); + // v.visitStoreInstruction(i); + // v.visitTypedInstruction(i); + // v.visitLocalVariableInstruction(i); + // v.visitStoreInstruction(i); + // for load instructions: ILOAD > ALOAD_3 - needs to visit the instruction too + // v.visitStackProducer(i); + // v.visitPushInstruction(i); + // v.visitTypedInstruction(i); + // v.visitLocalVariableInstruction(i); + // v.visitLoadInstruction(i); + + // for conversion instructions: (all 15 of them) - needs to visit conversion instruction too + // v.visitTypedInstruction(i); + // v.visitStackProducer(i); + // v.visitStackConsumer(i); + // v.visitConversionInstruction(i); + + // arithmetic instructions - need to visit the instructions too (iadd etc) + // v.visitTypedInstruction(i); + // v.visitStackProducer(i); + // v.visitStackConsumer(i); + // v.visitArithmeticInstruction(i); + + case INVOKESTATIC: + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitStackConsumer(i); + v.visitStackProducer(i); + v.visitLoadClass(i); + v.visitCPInstruction(i); + v.visitFieldOrMethod(i); + v.visitInvokeInstruction((InvokeInstruction) i); + v.visitINVOKESTATIC((InvokeInstruction) i); + break; + + case GOTO: + v.visitVariableLengthInstruction(i); + v.visitUnconditionalBranch(i); + v.visitBranchInstruction((InstructionBranch) i); + v.visitGotoInstruction(i); + v.visitGOTO(i); + break; + case PUTSTATIC: + v.visitExceptionThrower(i); + v.visitStackConsumer(i); + v.visitPopInstruction(i); + v.visitTypedInstruction(i); + v.visitLoadClass(i); + v.visitCPInstruction(i); + v.visitFieldOrMethod(i); + v.visitFieldInstruction(i); + v.visitPUTSTATIC((FieldInstruction) i); + break; + case RETURN: + v.visitExceptionThrower(i); + v.visitTypedInstruction(i); + v.visitStackConsumer(i); + v.visitReturnInstruction(i); + v.visitRETURN(i); + break; + default: + throw new IllegalStateException("visiting not yet implemented for " + i.getName().toUpperCase()); + } + } +} diff --git a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/statics/Pass2Verifier.java b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/statics/Pass2Verifier.java index 47b8090e6..a0740d279 100644 --- a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/statics/Pass2Verifier.java +++ b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/statics/Pass2Verifier.java @@ -91,7 +91,6 @@ import org.aspectj.apache.bcel.classfile.Node; import org.aspectj.apache.bcel.classfile.SourceFile; import org.aspectj.apache.bcel.classfile.Synthetic; import org.aspectj.apache.bcel.classfile.Unknown; -import org.aspectj.apache.bcel.classfile.ClassVisitor; import org.aspectj.apache.bcel.generic.ArrayType; import org.aspectj.apache.bcel.generic.ObjectType; import org.aspectj.apache.bcel.generic.Type; @@ -112,7 +111,7 @@ import org.aspectj.apache.bcel.verifier.exc.LocalVariableInfoInconsistentExcepti * More detailed information is to be found at the do_verify() * method's documentation. * - * @version $Id: Pass2Verifier.java,v 1.2 2008/05/28 23:52:54 aclement Exp $ + * @version $Id: Pass2Verifier.java,v 1.3 2008/08/28 00:02:14 aclement Exp $ * @author Enver Haase * @see #do_verify() */ @@ -313,7 +312,7 @@ public final class Pass2Verifier extends PassVerifier implements Constants{ * * @see #constant_pool_entries_satisfy_static_constraints() */ - private class CPESSC_Visitor extends org.aspectj.apache.bcel.verifier.EmptyClassVisitor implements ClassVisitor{ + private class CPESSC_Visitor extends org.aspectj.apache.bcel.verifier.EmptyClassVisitor{ private Class CONST_Class; /* private Class CONST_Fieldref; @@ -1126,7 +1125,7 @@ public final class Pass2Verifier extends PassVerifier implements Constants{ * @see #constant_pool_entries_satisfy_static_constraints() * @see org.aspectj.apache.bcel.classfile.ConstantCP */ - private class FAMRAV_Visitor extends EmptyClassVisitor implements ClassVisitor{ + private class FAMRAV_Visitor extends EmptyClassVisitor{ private final ConstantPool cp; // ==jc.getConstantPool() -- only here to save typing work. private FAMRAV_Visitor(JavaClass _jc){ cp = _jc.getConstantPool(); diff --git a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/statics/Pass3aVerifier.java b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/statics/Pass3aVerifier.java index dda916070..b6ae90b20 100644 --- a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/statics/Pass3aVerifier.java +++ b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/statics/Pass3aVerifier.java @@ -95,6 +95,7 @@ import org.aspectj.apache.bcel.generic.ObjectType; import org.aspectj.apache.bcel.generic.RET; import org.aspectj.apache.bcel.generic.TABLESWITCH; import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.apache.bcel.verifier.InstructionWalker; import org.aspectj.apache.bcel.verifier.PassVerifier; import org.aspectj.apache.bcel.verifier.VerificationResult; import org.aspectj.apache.bcel.verifier.Verifier; @@ -107,70 +108,63 @@ import org.aspectj.apache.bcel.verifier.exc.StaticCodeInstructionConstraintExcep import org.aspectj.apache.bcel.verifier.exc.StaticCodeInstructionOperandConstraintException; /** - * This PassVerifier verifies a class file according to - * pass 3, static part as described in The Java Virtual - * Machine Specification, 2nd edition. - * More detailed information is to be found at the do_verify() - * method's documentation. - * - * @version $Id: Pass3aVerifier.java,v 1.3 2008/05/30 17:29:27 aclement Exp $ + * This PassVerifier verifies a class file according to pass 3, static part as described in The Java Virtual Machine Specification, + * 2nd edition. More detailed information is to be found at the do_verify() method's documentation. + * + * @version $Id: Pass3aVerifier.java,v 1.4 2008/08/28 00:02:13 aclement Exp $ * @author Enver Haase * @see #do_verify() */ -public final class Pass3aVerifier extends PassVerifier{ +public final class Pass3aVerifier extends PassVerifier { /** The Verifier that created this. */ - private Verifier myOwner; + private final Verifier myOwner; - /** - * The method number to verify. - * This is the index in the array returned - * by JavaClass.getMethods(). + /** + * The method number to verify. This is the index in the array returned by JavaClass.getMethods(). */ - private int method_no; + private final int method_no; - /** The one and only InstructionList object used by an instance of this class. It's here for performance reasons by do_verify() and its callees. */ + /** + * The one and only InstructionList object used by an instance of this class. It's here for performance reasons by do_verify() + * and its callees. + */ InstructionList instructionList; - /** The one and only Code object used by an instance of this class. It's here for performance reasons by do_verify() and its callees. */ + /** + * The one and only Code object used by an instance of this class. It's here for performance reasons by do_verify() and its + * callees. + */ Code code; /** Should only be instantiated by a Verifier. */ - public Pass3aVerifier(Verifier owner, int method_no){ + public Pass3aVerifier(Verifier owner, int method_no) { myOwner = owner; this.method_no = method_no; } /** - * Pass 3a is the verification of static constraints of - * JVM code (such as legal targets of branch instructions). - * This is the part of pass 3 where you do not need data - * flow analysis. - * JustIce also delays the checks for a correct exception - * table of a Code attribute and correct line number entries - * in a LineNumberTable attribute of a Code attribute (which - * conceptually belong to pass 2) to this pass. Also, most - * of the check for valid local variable entries in a - * LocalVariableTable attribute of a Code attribute is - * delayed until this pass. - * All these checks need access to the code array of the - * Code attribute. - * + * Pass 3a is the verification of static constraints of JVM code (such as legal targets of branch instructions). This is the + * part of pass 3 where you do not need data flow analysis. JustIce also delays the checks for a correct exception table of a + * Code attribute and correct line number entries in a LineNumberTable attribute of a Code attribute (which conceptually belong + * to pass 2) to this pass. Also, most of the check for valid local variable entries in a LocalVariableTable attribute of a Code + * attribute is delayed until this pass. All these checks need access to the code array of the Code attribute. + * * @throws InvalidMethodException if the method to verify does not exist. */ - public VerificationResult do_verify(){ - if (myOwner.doPass2().equals(VerificationResult.VR_OK)){ + public VerificationResult do_verify() { + if (myOwner.doPass2().equals(VerificationResult.VR_OK)) { // Okay, class file was loaded correctly by Pass 1 // and satisfies static constraints of Pass 2. JavaClass jc = Repository.lookupClass(myOwner.getClassName()); Method[] methods = jc.getMethods(); - if (method_no >= methods.length){ + if (method_no >= methods.length) { throw new InvalidMethodException("METHOD DOES NOT EXIST!"); } Method method = methods[method_no]; code = method.getCode(); - + // No Code? Nothing to verify! - if ( method.isAbstract() || method.isNative() ){ // IF mg HAS NO CODE (static constraint of Pass 2) + if (method.isAbstract() || method.isNative()) { // IF mg HAS NO CODE (static constraint of Pass 2) return VerificationResult.VR_OK; } @@ -183,268 +177,286 @@ public final class Pass3aVerifier extends PassVerifier{ // That examination should be implemented in a byte-oriented way, i.e. look for // an instruction, make sure its validity, count its length, find the next // instruction and so on. - try{ + try { instructionList = new InstructionList(method.getCode().getCode()); + } catch (RuntimeException re) { + return new VerificationResult(VerificationResult.VERIFIED_REJECTED, + "Bad bytecode in the code array of the Code attribute of method '" + method + "'."); } - catch(RuntimeException re){ - return new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Bad bytecode in the code array of the Code attribute of method '"+method+"'."); - } - + instructionList.setPositions(true); // Start verification. - VerificationResult vr = VerificationResult.VR_OK; //default - try{ + VerificationResult vr = VerificationResult.VR_OK; // default + try { delayedPass2Checks(); - } - catch(ClassConstraintException cce){ + } catch (ClassConstraintException cce) { vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage()); return vr; } - try{ + try { pass3StaticInstructionChecks(); pass3StaticInstructionOperandsChecks(); - } - catch(StaticCodeConstraintException scce){ + } catch (StaticCodeConstraintException scce) { vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, scce.getMessage()); } return vr; - } - else{ //did not pass Pass 2. + } else { // did not pass Pass 2. return VerificationResult.VR_NOTYET; } } /** - * These are the checks that could be done in pass 2 but are delayed to pass 3 - * for performance reasons. Also, these checks need access to the code array - * of the Code attribute of a Method so it's okay to perform them here. - * Also see the description of the do_verify() method. - * + * These are the checks that could be done in pass 2 but are delayed to pass 3 for performance reasons. Also, these checks need + * access to the code array of the Code attribute of a Method so it's okay to perform them here. Also see the description of the + * do_verify() method. + * * @throws ClassConstraintException if the verification fails. * @see #do_verify() */ - private void delayedPass2Checks(){ + private void delayedPass2Checks() { int[] instructionPositions = instructionList.getInstructionPositions(); int codeLength = code.getCode().length; - ///////////////////// + // /////////////////// // LineNumberTable // - ///////////////////// + // /////////////////// LineNumberTable lnt = code.getLineNumberTable(); - if (lnt != null){ + if (lnt != null) { LineNumber[] lineNumbers = lnt.getLineNumberTable(); IntList offsets = new IntList(); - lineNumber_loop: for (int i=0; i < lineNumbers.length; i++){ // may appear in any order. - for (int j=0; j < instructionPositions.length; j++){ + lineNumber_loop: for (int i = 0; i < lineNumbers.length; i++) { // may appear in any order. + for (int j = 0; j < instructionPositions.length; j++) { // TODO: Make this a binary search! The instructionPositions array is naturally ordered! int offset = lineNumbers[i].getStartPC(); - if (instructionPositions[j] == offset){ - if (offsets.contains(offset)){ - addMessage("LineNumberTable attribute '"+code.getLineNumberTable()+"' refers to the same code offset ('"+offset+"') more than once which is violating the semantics [but is sometimes produced by IBM's 'jikes' compiler]."); - } - else{ + if (instructionPositions[j] == offset) { + if (offsets.contains(offset)) { + addMessage("LineNumberTable attribute '" + + code.getLineNumberTable() + + "' refers to the same code offset ('" + + offset + + "') more than once which is violating the semantics [but is sometimes produced by IBM's 'jikes' compiler]."); + } else { offsets.add(offset); } continue lineNumber_loop; } } - throw new ClassConstraintException("Code attribute '"+code+"' has a LineNumberTable attribute '"+code.getLineNumberTable()+"' referring to a code offset ('"+lineNumbers[i].getStartPC()+"') that does not exist."); + throw new ClassConstraintException("Code attribute '" + code + "' has a LineNumberTable attribute '" + + code.getLineNumberTable() + "' referring to a code offset ('" + lineNumbers[i].getStartPC() + + "') that does not exist."); } } - /////////////////////////// + // ///////////////////////// // LocalVariableTable(s) // - /////////////////////////// - /* We cannot use code.getLocalVariableTable() because there could be more - than only one. This is a bug in BCEL. */ + // ///////////////////////// + /* + * We cannot use code.getLocalVariableTable() because there could be more than only one. This is a bug in BCEL. + */ Attribute[] atts = code.getAttributes(); - for (int a=0; a= endpc){ - throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+exceptionTable[i]+"' that has its start_pc ('"+startpc+"') not smaller than its end_pc ('"+endpc+"')."); + if (startpc >= endpc) { + throw new ClassConstraintException("Code attribute '" + code + "' has an exception_table entry '" + + exceptionTable[i] + "' that has its start_pc ('" + startpc + "') not smaller than its end_pc ('" + endpc + + "')."); } - if (!contains(instructionPositions, startpc)){ - throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+exceptionTable[i]+"' that has a non-existant bytecode offset as its start_pc ('"+startpc+"')."); + if (!contains(instructionPositions, startpc)) { + throw new ClassConstraintException("Code attribute '" + code + "' has an exception_table entry '" + + exceptionTable[i] + "' that has a non-existant bytecode offset as its start_pc ('" + startpc + "')."); } - if ( (!contains(instructionPositions, endpc)) && (endpc != codeLength)){ - throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+exceptionTable[i]+"' that has a non-existant bytecode offset as its end_pc ('"+startpc+"') [that is also not equal to code_length ('"+codeLength+"')]."); + if (!contains(instructionPositions, endpc) && endpc != codeLength) { + throw new ClassConstraintException("Code attribute '" + code + "' has an exception_table entry '" + + exceptionTable[i] + "' that has a non-existant bytecode offset as its end_pc ('" + startpc + + "') [that is also not equal to code_length ('" + codeLength + "')]."); } - if (!contains(instructionPositions, handlerpc)){ - throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+exceptionTable[i]+"' that has a non-existant bytecode offset as its handler_pc ('"+handlerpc+"')."); + if (!contains(instructionPositions, handlerpc)) { + throw new ClassConstraintException("Code attribute '" + code + "' has an exception_table entry '" + + exceptionTable[i] + "' that has a non-existant bytecode offset as its handler_pc ('" + handlerpc + "')."); } } } /** - * These are the checks if constraints are satisfied which are described in the - * Java Virtual Machine Specification, Second Edition as Static Constraints on - * the instructions of Java Virtual Machine Code (chapter 4.8.1). - * + * These are the checks if constraints are satisfied which are described in the Java Virtual Machine Specification, Second + * Edition as Static Constraints on the instructions of Java Virtual Machine Code (chapter 4.8.1). + * * @throws StaticCodeConstraintException if the verification fails. */ - private void pass3StaticInstructionChecks(){ - + private void pass3StaticInstructionChecks() { + // Code array must not be empty: // Enforced in pass 2 (also stated in the static constraints of the Code // array in vmspec2), together with pass 1 (reading code_length bytes and // interpreting them as code[]). So this must not be checked again here. - if (! (code.getCode().length < 65536)){// contradicts vmspec2 page 152 ("Limitations"), but is on page 134. - throw new StaticCodeInstructionConstraintException("Code array in code attribute '"+code+"' too big: must be smaller than 65536 bytes."); + if (!(code.getCode().length < 65536)) {// contradicts vmspec2 page 152 ("Limitations"), but is on page 134. + throw new StaticCodeInstructionConstraintException("Code array in code attribute '" + code + + "' too big: must be smaller than 65536 bytes."); } // First opcode at offset 0: okay, that's clear. Nothing to do. - + // Only instances of the instructions documented in Section 6.4 may appear in // the code array. - + // For BCEL's sake, we cannot handle WIDE stuff, but hopefully BCEL does its job right :) - + // The last byte of the last instruction in the code array must be the byte at index // code_length-1 : See the do_verify() comments. We actually don't iterate through the // byte array, but use an InstructionList so we cannot check for this. But BCEL does // things right, so it's implicitly okay. - + // TODO: Check how BCEL handles (and will handle) instructions like IMPDEP1, IMPDEP2, - // BREAKPOINT... that BCEL knows about but which are illegal anyway. - // We currently go the safe way here. + // BREAKPOINT... that BCEL knows about but which are illegal anyway. + // We currently go the safe way here. InstructionHandle ih = instructionList.getStart(); - while (ih != null){ + while (ih != null) { Instruction i = ih.getInstruction(); - if (i.getOpcode()==Constants.IMPDEP1){ - throw new StaticCodeInstructionConstraintException("IMPDEP1 must not be in the code, it is an illegal instruction for _internal_ JVM use!"); + if (i.getOpcode() == Constants.IMPDEP1) { + throw new StaticCodeInstructionConstraintException( + "IMPDEP1 must not be in the code, it is an illegal instruction for _internal_ JVM use!"); } - if (i.getOpcode()==Constants.IMPDEP2){ - throw new StaticCodeInstructionConstraintException("IMPDEP2 must not be in the code, it is an illegal instruction for _internal_ JVM use!"); + if (i.getOpcode() == Constants.IMPDEP2) { + throw new StaticCodeInstructionConstraintException( + "IMPDEP2 must not be in the code, it is an illegal instruction for _internal_ JVM use!"); } - if (i.getOpcode()==Constants.BREAKPOINT){ - throw new StaticCodeInstructionConstraintException("BREAKPOINT must not be in the code, it is an illegal instruction for _internal_ JVM use!"); + if (i.getOpcode() == Constants.BREAKPOINT) { + throw new StaticCodeInstructionConstraintException( + "BREAKPOINT must not be in the code, it is an illegal instruction for _internal_ JVM use!"); } ih = ih.getNext(); } - + // The original verifier seems to do this check here, too. // An unreachable last instruction may also not fall through the // end of the code, which is stupid -- but with the original // verifier's subroutine semantics one cannot predict reachability. Instruction last = instructionList.getEnd().getInstruction(); - if (! ((last.isReturnInstruction()) || - (last instanceof RET) || - (last.getOpcode()==Constants.GOTO || last.getOpcode()==Constants.GOTO_W) || - (last.getOpcode()==Constants.ATHROW))) // JSR / JSR_W would possibly RETurn and then fall off the code! - throw new StaticCodeInstructionConstraintException("Execution must not fall off the bottom of the code array. This constraint is enforced statically as some existing verifiers do - so it may be a false alarm if the last instruction is not reachable."); + if (!(last.isReturnInstruction() || last instanceof RET || last.getOpcode() == Constants.GOTO + || last.getOpcode() == Constants.GOTO_W || last.getOpcode() == Constants.ATHROW)) { + throw new StaticCodeInstructionConstraintException( + "Execution must not fall off the bottom of the code array. This constraint is enforced statically as some existing verifiers do - so it may be a false alarm if the last instruction is not reachable."); + } } /** - * These are the checks for the satisfaction of constraints which are described in the - * Java Virtual Machine Specification, Second Edition as Static Constraints on - * the operands of instructions of Java Virtual Machine Code (chapter 4.8.1). - * BCEL parses the code array to create an InstructionList and therefore has to check - * some of these constraints. Additional checks are also implemented here. - * + * These are the checks for the satisfaction of constraints which are described in the Java Virtual Machine Specification, + * Second Edition as Static Constraints on the operands of instructions of Java Virtual Machine Code (chapter 4.8.1). BCEL + * parses the code array to create an InstructionList and therefore has to check some of these constraints. Additional checks + * are also implemented here. + * * @throws StaticCodeConstraintException if the verification fails. */ - private void pass3StaticInstructionOperandsChecks(){ + private void pass3StaticInstructionOperandsChecks() { // When building up the InstructionList, BCEL has already done all those checks // mentioned in The Java Virtual Machine Specification, Second Edition, as // "static constraints on the operands of instructions in the code array". // TODO: see the do_verify() comments. Maybe we should really work on the - // byte array first to give more comprehensive messages. + // byte array first to give more comprehensive messages. // TODO: Review Exception API, possibly build in some "offending instruction" thing - // when we're ready to insulate the offending instruction by doing the - // above thing. + // when we're ready to insulate the offending instruction by doing the + // above thing. // TODO: Implement as much as possible here. BCEL does _not_ check everything. ConstantPool cpg = new ConstantPool(Repository.lookupClass(myOwner.getClassName()).getConstantPool().getConstantPool()); InstOperandConstraintVisitor v = new InstOperandConstraintVisitor(cpg); - + // Checks for the things BCEL does _not_ handle itself. InstructionHandle ih = instructionList.getStart(); - while (ih != null){ + while (ih != null) { Instruction i = ih.getInstruction(); - + // An "own" constraint, due to JustIce's new definition of what "subroutine" means. - if (i.isJsrInstruction()){ + if (i.isJsrInstruction()) { InstructionHandle target = ((InstructionBranch) i).getTarget(); - if (target == instructionList.getStart()){ - throw new StaticCodeInstructionOperandConstraintException("Due to JustIce's clear definition of subroutines, no JSR or JSR_W may have a top-level instruction (such as the very first instruction, which is targeted by instruction '"+ih+"' as its target."); + if (target == instructionList.getStart()) { + throw new StaticCodeInstructionOperandConstraintException( + "Due to JustIce's clear definition of subroutines, no JSR or JSR_W may have a top-level instruction (such as the very first instruction, which is targeted by instruction '" + + ih + "' as its target."); } - if (!(target.getInstruction().isASTORE())){ - throw new StaticCodeInstructionOperandConstraintException("Due to JustIce's clear definition of subroutines, no JSR or JSR_W may target anything else than an ASTORE instruction. Instruction '"+ih+"' targets '"+target+"'."); + if (!target.getInstruction().isASTORE()) { + throw new StaticCodeInstructionOperandConstraintException( + "Due to JustIce's clear definition of subroutines, no JSR or JSR_W may target anything else than an ASTORE instruction. Instruction '" + + ih + "' targets '" + target + "'."); } } - + // vmspec2, page 134-137 - ih.accept(v); - + InstructionWalker.accept(ih.getInstruction(), v); + ih = ih.getNext(); } } - + /** A small utility method returning if a given int i is in the given int[] ints. */ - private static boolean contains(int[] ints, int i){ - for (int j=0; j= cpg.getSize()){ - constraintViolated(i, "Illegal constant pool index '"+idx+"'."); + private void indexValid(Instruction i, int idx) { + if (idx < 0 || idx >= cpg.getSize()) { + constraintViolated(i, "Illegal constant pool index '" + idx + "'."); } } - /////////////////////////////////////////////////////////// + // ///////////////////////////////////////////////////////// // The Java Virtual Machine Specification, pages 134-137 // - /////////////////////////////////////////////////////////// + // ///////////////////////////////////////////////////////// /** - * Assures the generic preconditions of a LoadClass instance. - * The referenced class is loaded and pass2-verified. + * Assures the generic preconditions of a LoadClass instance. The referenced class is loaded and pass2-verified. */ - public void visitLoadClass(Instruction o){ + public void visitLoadClass(Instruction o) { ObjectType t = o.getLoadClassType(cpg); - if (t != null){// null means "no class is loaded" + if (t != null) {// null means "no class is loaded" Verifier v = VerifierFactory.getVerifier(t.getClassName()); VerificationResult vr = v.doPass1(); - if (vr.getStatus() != VerificationResult.VERIFIED_OK){ - constraintViolated((Instruction) o, "Class '"+o.getLoadClassType(cpg).getClassName()+"' is referenced, but cannot be loaded: '"+vr+"'."); + if (vr.getStatus() != VerificationResult.VERIFIED_OK) { + constraintViolated(o, "Class '" + o.getLoadClassType(cpg).getClassName() + + "' is referenced, but cannot be loaded: '" + vr + "'."); } } } - + // The target of each jump and branch instruction [...] must be the opcode [...] // BCEL _DOES_ handle this. // tableswitch: BCEL will do it, supposedly. - + // lookupswitch: BCEL will do it, supposedly. - + /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ // LDC and LDC_W (LDC_W is a subclass of LDC in BCEL's model) - public void visitLDC(Instruction o){ + public void visitLDC(Instruction o) { indexValid(o, o.getIndex()); Constant c = cpg.getConstant(o.getIndex()); - if (! ( (c instanceof ConstantInteger) || - (c instanceof ConstantFloat) || - (c instanceof ConstantString) ) ){ - constraintViolated(o, "Operand of LDC or LDC_W must be one of CONSTANT_Integer, CONSTANT_Float or CONSTANT_String, but is '"+c+"'."); + if (!(c instanceof ConstantInteger || c instanceof ConstantFloat || c instanceof ConstantString)) { + constraintViolated(o, + "Operand of LDC or LDC_W must be one of CONSTANT_Integer, CONSTANT_Float or CONSTANT_String, but is '" + c + + "'."); } } /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ // LDC2_W - public void visitLDC2_W(Instruction o){ + public void visitLDC2_W(Instruction o) { indexValid(o, o.getIndex()); Constant c = cpg.getConstant(o.getIndex()); - if (! ( (c instanceof ConstantLong) || - (c instanceof ConstantDouble) ) ){ - constraintViolated(o, "Operand of LDC2_W must be CONSTANT_Long or CONSTANT_Double, but is '"+c+"'."); - } - try{ - indexValid(o, o.getIndex()+1); + if (!(c instanceof ConstantLong || c instanceof ConstantDouble)) { + constraintViolated(o, "Operand of LDC2_W must be CONSTANT_Long or CONSTANT_Double, but is '" + c + "'."); } - catch(StaticCodeInstructionOperandConstraintException e){ + try { + indexValid(o, o.getIndex() + 1); + } catch (StaticCodeInstructionOperandConstraintException e) { throw new AssertionViolatedException("OOPS: Does not BCEL handle that? LDC2_W operand has a problem."); } } /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - //getfield, putfield, getstatic, putstatic - public void visitFieldInstruction(FieldInstruction o){ + // getfield, putfield, getstatic, putstatic + public void visitFieldInstruction(FieldInstruction o) { indexValid(o, o.getIndex()); Constant c = cpg.getConstant(o.getIndex()); - if (! (c instanceof ConstantFieldref)){ - constraintViolated(o, "Indexing a constant that's not a CONSTANT_Fieldref but a '"+c+"'."); + if (!(c instanceof ConstantFieldref)) { + constraintViolated(o, "Indexing a constant that's not a CONSTANT_Fieldref but a '" + c + "'."); } - + String field_name = o.getFieldName(cpg); - + JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName()); Field[] fields = jc.getFields(); Field f = null; - for (int i=0; i or - ConstantNameAndType cnat = (ConstantNameAndType) (cpg.getConstant(((ConstantInterfaceMethodref)c).getNameAndTypeIndex())); - String name = ((ConstantUtf8) (cpg.getConstant(cnat.getNameIndex()))).getBytes(); - if (name.equals(Constants.CONSTRUCTOR_NAME)){ - constraintViolated(o, "Method to invoke must not be '"+Constants.CONSTRUCTOR_NAME+"'."); + ConstantNameAndType cnat = (ConstantNameAndType) cpg.getConstant(((ConstantInterfaceMethodref) c) + .getNameAndTypeIndex()); + String name = ((ConstantUtf8) cpg.getConstant(cnat.getNameIndex())).getBytes(); + if (name.equals(Constants.CONSTRUCTOR_NAME)) { + constraintViolated(o, "Method to invoke must not be '" + Constants.CONSTRUCTOR_NAME + "'."); } - if (name.equals(Constants.STATIC_INITIALIZER_NAME)){ - constraintViolated(o, "Method to invoke must not be '"+Constants.STATIC_INITIALIZER_NAME+"'."); + if (name.equals(Constants.STATIC_INITIALIZER_NAME)) { + constraintViolated(o, "Method to invoke must not be '" + Constants.STATIC_INITIALIZER_NAME + "'."); } } - + // The LoadClassType is the method-declaring class, so we have to check the other types. - + Type t = o.getReturnType(cpg); - if (t instanceof ArrayType){ + if (t instanceof ArrayType) { t = ((ArrayType) t).getBasicType(); } - if (t instanceof ObjectType){ + if (t instanceof ObjectType) { Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName()); VerificationResult vr = v.doPass2(); - if (vr.getStatus() != VerificationResult.VERIFIED_OK){ - constraintViolated(o, "Return type class/interface could not be verified successfully: '"+vr.getMessage()+"'."); + if (vr.getStatus() != VerificationResult.VERIFIED_OK) { + constraintViolated(o, "Return type class/interface could not be verified successfully: '" + vr.getMessage() + + "'."); } } - + Type[] ts = o.getArgumentTypes(cpg); - for (int i=0; i= 255){ + if (dimensions >= 255) { constraintViolated(o, "Not allowed to create an array with more than 255 dimensions."); } } } /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitNEWARRAY(Instruction o){ - byte t = ((InstructionByte)o).getTypecode(); - if (! ( (t == Constants.T_BOOLEAN) || - (t == Constants.T_CHAR) || - (t == Constants.T_FLOAT) || - (t == Constants.T_DOUBLE) || - (t == Constants.T_BYTE) || - (t == Constants.T_SHORT) || - (t == Constants.T_INT) || - (t == Constants.T_LONG) ) ){ + public void visitNEWARRAY(Instruction o) { + byte t = ((InstructionByte) o).getTypecode(); + if (!(t == Constants.T_BOOLEAN || t == Constants.T_CHAR || t == Constants.T_FLOAT || t == Constants.T_DOUBLE + || t == Constants.T_BYTE || t == Constants.T_SHORT || t == Constants.T_INT || t == Constants.T_LONG)) { constraintViolated(o, "Illegal type code '+t+' for 'atype' operand."); } } /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitILOAD(Instruction o){ + public void visitILOAD(Instruction o) { int idx = o.getIndex(); - if (idx < 0){ - constraintViolated(o, "Index '"+idx+"' must be non-negative."); - } - else{ - int maxminus1 = max_locals()-1; - if (idx > maxminus1){ - constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); + if (idx < 0) { + constraintViolated(o, "Index '" + idx + "' must be non-negative."); + } else { + int maxminus1 = max_locals() - 1; + if (idx > maxminus1) { + constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); } } } /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitFLOAD(Instruction o){ + public void visitFLOAD(Instruction o) { int idx = o.getIndex(); - if (idx < 0){ - constraintViolated(o, "Index '"+idx+"' must be non-negative."); - } - else{ - int maxminus1 = max_locals()-1; - if (idx > maxminus1){ - constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); + if (idx < 0) { + constraintViolated(o, "Index '" + idx + "' must be non-negative."); + } else { + int maxminus1 = max_locals() - 1; + if (idx > maxminus1) { + constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); } } } /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitALOAD(Instruction o){ + public void visitALOAD(Instruction o) { int idx = o.getIndex(); - if (idx < 0){ - constraintViolated(o, "Index '"+idx+"' must be non-negative."); - } - else{ - int maxminus1 = max_locals()-1; - if (idx > maxminus1){ - constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); + if (idx < 0) { + constraintViolated(o, "Index '" + idx + "' must be non-negative."); + } else { + int maxminus1 = max_locals() - 1; + if (idx > maxminus1) { + constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); } } } - + /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitISTORE(Instruction o){ + public void visitISTORE(Instruction o) { int idx = o.getIndex(); - if (idx < 0){ - constraintViolated(o, "Index '"+idx+"' must be non-negative."); - } - else{ - int maxminus1 = max_locals()-1; - if (idx > maxminus1){ - constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); + if (idx < 0) { + constraintViolated(o, "Index '" + idx + "' must be non-negative."); + } else { + int maxminus1 = max_locals() - 1; + if (idx > maxminus1) { + constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); } } } - + /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitFSTORE(Instruction o){ + public void visitFSTORE(Instruction o) { int idx = o.getIndex(); - if (idx < 0){ - constraintViolated(o, "Index '"+idx+"' must be non-negative."); - } - else{ - int maxminus1 = max_locals()-1; - if (idx > maxminus1){ - constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); + if (idx < 0) { + constraintViolated(o, "Index '" + idx + "' must be non-negative."); + } else { + int maxminus1 = max_locals() - 1; + if (idx > maxminus1) { + constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); } } } /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitASTORE(Instruction o){ + public void visitASTORE(Instruction o) { int idx = o.getIndex(); - if (idx < 0){ - constraintViolated(o, "Index '"+idx+"' must be non-negative."); - } - else{ - int maxminus1 = max_locals()-1; - if (idx > maxminus1){ - constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); + if (idx < 0) { + constraintViolated(o, "Index '" + idx + "' must be non-negative."); + } else { + int maxminus1 = max_locals() - 1; + if (idx > maxminus1) { + constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); } } } /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitIINC(IINC o){ + public void visitIINC(IINC o) { int idx = o.getIndex(); - if (idx < 0){ - constraintViolated(o, "Index '"+idx+"' must be non-negative."); - } - else{ - int maxminus1 = max_locals()-1; - if (idx > maxminus1){ - constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); + if (idx < 0) { + constraintViolated(o, "Index '" + idx + "' must be non-negative."); + } else { + int maxminus1 = max_locals() - 1; + if (idx > maxminus1) { + constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); } } } /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitRET(RET o){ + public void visitRET(RET o) { int idx = o.getIndex(); - if (idx < 0){ - constraintViolated(o, "Index '"+idx+"' must be non-negative."); - } - else{ - int maxminus1 = max_locals()-1; - if (idx > maxminus1){ - constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); + if (idx < 0) { + constraintViolated(o, "Index '" + idx + "' must be non-negative."); + } else { + int maxminus1 = max_locals() - 1; + if (idx > maxminus1) { + constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); } } } /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitLLOAD(Instruction o){ + public void visitLLOAD(Instruction o) { int idx = o.getIndex(); - if (idx < 0){ - constraintViolated(o, "Index '"+idx+"' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]"); - } - else{ - int maxminus2 = max_locals()-2; - if (idx > maxminus2){ - constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'."); + if (idx < 0) { + constraintViolated( + o, + "Index '" + + idx + + "' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]"); + } else { + int maxminus2 = max_locals() - 2; + if (idx > maxminus2) { + constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'."); } } } - + /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitDLOAD(Instruction o){ + public void visitDLOAD(Instruction o) { int idx = o.getIndex(); - if (idx < 0){ - constraintViolated(o, "Index '"+idx+"' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]"); - } - else{ - int maxminus2 = max_locals()-2; - if (idx > maxminus2){ - constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'."); + if (idx < 0) { + constraintViolated( + o, + "Index '" + + idx + + "' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]"); + } else { + int maxminus2 = max_locals() - 2; + if (idx > maxminus2) { + constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'."); } } } - + /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitLSTORE(Instruction o){ + public void visitLSTORE(Instruction o) { int idx = o.getIndex(); - if (idx < 0){ - constraintViolated(o, "Index '"+idx+"' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]"); - } - else{ - int maxminus2 = max_locals()-2; - if (idx > maxminus2){ - constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'."); + if (idx < 0) { + constraintViolated( + o, + "Index '" + + idx + + "' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]"); + } else { + int maxminus2 = max_locals() - 2; + if (idx > maxminus2) { + constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'."); } } } - + /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitDSTORE(Instruction o){ + public void visitDSTORE(Instruction o) { int idx = o.getIndex(); - if (idx < 0){ - constraintViolated(o, "Index '"+idx+"' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]"); - } - else{ - int maxminus2 = max_locals()-2; - if (idx > maxminus2){ - constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'."); + if (idx < 0) { + constraintViolated( + o, + "Index '" + + idx + + "' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]"); + } else { + int maxminus2 = max_locals() - 2; + if (idx > maxminus2) { + constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'."); } } } /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitLOOKUPSWITCH(LOOKUPSWITCH o){ + public void visitLOOKUPSWITCH(LOOKUPSWITCH o) { int[] matchs = o.getMatchs(); int max = Integer.MIN_VALUE; - for (int i=0; i= "low". We cannot check this, as BCEL hides // it from us. } /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitPUTSTATIC(FieldInstruction o){ + public void visitPUTSTATIC(FieldInstruction o) { String field_name = o.getFieldName(cpg); JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName()); Field[] fields = jc.getFields(); Field f = null; - for (int i=0; i. - if ((!(jc.isClass())) && (!(meth_name.equals(Constants.STATIC_INITIALIZER_NAME)))){ - constraintViolated(o, "Interface field '"+f+"' must be set in a '"+Constants.STATIC_INITIALIZER_NAME+"' method."); + if (!jc.isClass() && !meth_name.equals(Constants.STATIC_INITIALIZER_NAME)) { + constraintViolated(o, "Interface field '" + f + "' must be set in a '" + Constants.STATIC_INITIALIZER_NAME + + "' method."); } } /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ - public void visitGETSTATIC(FieldInstruction o){ + public void visitGETSTATIC(FieldInstruction o) { String field_name = o.getFieldName(cpg); JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName()); Field[] fields = jc.getFields(); Field f = null; - for (int i=0; iEnver Haase */ -public class StringRepresentation extends org.aspectj.apache.bcel.verifier.EmptyClassVisitor implements ClassVisitor{ +public class StringRepresentation extends org.aspectj.apache.bcel.verifier.EmptyClassVisitor{ /** The string representation, created by a visitXXX() method, output by toString(). */ private String tostring; /** The node we ask for its string representation. Not really needed; only for debug output. */ diff --git a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/ControlFlowGraph.java b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/ControlFlowGraph.java index 203080c3f..7c2fdcc7b 100644 --- a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/ControlFlowGraph.java +++ b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/ControlFlowGraph.java @@ -65,27 +65,26 @@ import org.aspectj.apache.bcel.generic.InstructionHandle; import org.aspectj.apache.bcel.generic.InstructionSelect; import org.aspectj.apache.bcel.generic.MethodGen; import org.aspectj.apache.bcel.generic.RET; +import org.aspectj.apache.bcel.verifier.InstructionWalker; import org.aspectj.apache.bcel.verifier.exc.AssertionViolatedException; import org.aspectj.apache.bcel.verifier.exc.StructuralCodeConstraintException; /** * This class represents a control flow graph of a method. - * - * @version $Id: ControlFlowGraph.java,v 1.2 2008/05/28 23:53:02 aclement Exp $ + * + * @version $Id: ControlFlowGraph.java,v 1.3 2008/08/28 00:02:13 aclement Exp $ * @author Enver Haase */ -public class ControlFlowGraph{ +public class ControlFlowGraph { /** - * Objects of this class represent a node in a ControlFlowGraph. - * These nodes are instructions, not basic blocks. + * Objects of this class represent a node in a ControlFlowGraph. These nodes are instructions, not basic blocks. */ - private class InstructionContextImpl implements InstructionContext{ + private class InstructionContextImpl implements InstructionContext { /** - * The TAG field is here for external temporary flagging, such - * as graph colouring. - * + * The TAG field is here for external temporary flagging, such as graph colouring. + * * @see #getTag() * @see #setTag(int) */ @@ -94,57 +93,59 @@ public class ControlFlowGraph{ /** * The InstructionHandle this InstructionContext is wrapped around. */ - private InstructionHandle instruction; + private final InstructionHandle instruction; /** * The 'incoming' execution Frames. */ - private HashMap inFrames; // key: the last-executed JSR + private final HashMap inFrames; // key: the last-executed JSR /** * The 'outgoing' execution Frames. */ - private HashMap outFrames; // key: the last-executed JSR + private final HashMap outFrames; // key: the last-executed JSR /** - * The 'execution predecessors' - a list of type InstructionContext - * of those instances that have been execute()d before in that order. + * The 'execution predecessors' - a list of type InstructionContext of those instances that have been execute()d before in + * that order. */ private ArrayList executionPredecessors = null; // Type: InstructionContext - + /** - * Creates an InstructionHandleImpl object from an InstructionHandle. - * Creation of one per InstructionHandle suffices. Don't create more. + * Creates an InstructionHandleImpl object from an InstructionHandle. Creation of one per InstructionHandle suffices. Don't + * create more. */ - public InstructionContextImpl(InstructionHandle inst){ - if (inst == null) throw new AssertionViolatedException("Cannot instantiate InstructionContextImpl from NULL."); - + public InstructionContextImpl(InstructionHandle inst) { + if (inst == null) { + throw new AssertionViolatedException("Cannot instantiate InstructionContextImpl from NULL."); + } + instruction = inst; inFrames = new java.util.HashMap(); outFrames = new java.util.HashMap(); } /* Satisfies InstructionContext.getTag(). */ - public int getTag(){ + public int getTag() { return TAG; } /* Satisfies InstructionContext.setTag(int). */ - public void setTag(int tag){ + public void setTag(int tag) { TAG = tag; } /** * Returns the exception handlers of this instruction. */ - public ExceptionHandler[] getExceptionHandlers(){ + public ExceptionHandler[] getExceptionHandlers() { return exceptionhandlers.getExceptionHandlers(getInstruction()); } /** * Returns a clone of the "outgoing" frame situation with respect to the given ExecutionChain. - */ - public Frame getOutFrame(ArrayList execChain){ + */ + public Frame getOutFrame(ArrayList execChain) { executionPredecessors = execChain; Frame org; @@ -153,252 +154,235 @@ public class ControlFlowGraph{ org = (Frame) outFrames.get(jsr); - if (org == null){ - throw new AssertionViolatedException("outFrame not set! This:\n"+this+"\nExecutionChain: "+getExecutionChain()+"\nOutFrames: '"+outFrames+"'."); + if (org == null) { + throw new AssertionViolatedException("outFrame not set! This:\n" + this + "\nExecutionChain: " + + getExecutionChain() + "\nOutFrames: '" + outFrames + "'."); } return org.getClone(); } /** - * "Merges in" (vmspec2, page 146) the "incoming" frame situation; - * executes the instructions symbolically - * and therefore calculates the "outgoing" frame situation. - * Returns: True iff the "incoming" frame situation changed after - * merging with "inFrame". - * The execPreds ArrayList must contain the InstructionContext - * objects executed so far in the correct order. This is just - * one execution path [out of many]. This is needed to correctly - * "merge" in the special case of a RET's successor. - * The InstConstraintVisitor and ExecutionVisitor instances - * must be set up correctly. - * @return true - if and only if the "outgoing" frame situation - * changed from the one before execute()ing. + * "Merges in" (vmspec2, page 146) the "incoming" frame situation; executes the instructions symbolically and therefore + * calculates the "outgoing" frame situation. Returns: True iff the "incoming" frame situation changed after merging with + * "inFrame". The execPreds ArrayList must contain the InstructionContext objects executed so far in the correct order. This + * is just one execution path [out of many]. This is needed to correctly "merge" in the special case of a RET's successor. + * The InstConstraintVisitor and ExecutionVisitor instances must be set up correctly. + * + * @return true - if and only if the "outgoing" frame situation changed from the one before execute()ing. */ - public boolean execute(Frame inFrame, ArrayList execPreds, InstConstraintVisitor icv, ExecutionVisitor ev){ + public boolean execute(Frame inFrame, ArrayList execPreds, InstConstraintVisitor icv, ExecutionVisitor ev) { executionPredecessors = (ArrayList) execPreds.clone(); - //sanity check - if ( (lastExecutionJSR() == null) && (subroutines.subroutineOf(getInstruction()) != subroutines.getTopLevel() ) ){ - throw new AssertionViolatedException("Huh?! Am I '"+this+"' part of a subroutine or not?"); + // sanity check + if (lastExecutionJSR() == null && subroutines.subroutineOf(getInstruction()) != subroutines.getTopLevel()) { + throw new AssertionViolatedException("Huh?! Am I '" + this + "' part of a subroutine or not?"); } - if ( (lastExecutionJSR() != null) && (subroutines.subroutineOf(getInstruction()) == subroutines.getTopLevel() ) ){ - throw new AssertionViolatedException("Huh?! Am I '"+this+"' part of a subroutine or not?"); + if (lastExecutionJSR() != null && subroutines.subroutineOf(getInstruction()) == subroutines.getTopLevel()) { + throw new AssertionViolatedException("Huh?! Am I '" + this + "' part of a subroutine or not?"); } Frame inF = (Frame) inFrames.get(lastExecutionJSR()); - if (inF == null){// no incoming frame was set, so set it. + if (inF == null) {// no incoming frame was set, so set it. inFrames.put(lastExecutionJSR(), inFrame); inF = inFrame; - } - else{// if there was an "old" inFrame - if (inF.equals(inFrame)){ //shortcut: no need to merge equal frames. + } else {// if there was an "old" inFrame + if (inF.equals(inFrame)) { // shortcut: no need to merge equal frames. return false; } - if (! mergeInFrames(inFrame)){ + if (!mergeInFrames(inFrame)) { return false; } } - + // Now we're sure the inFrame has changed! - - // new inFrame is already merged in, see above. + + // new inFrame is already merged in, see above. Frame workingFrame = inF.getClone(); - try{ + try { // This verifies the InstructionConstraint for the current // instruction, but does not modify the workingFrame object. -//InstConstraintVisitor icv = InstConstraintVisitor.getInstance(VerifierFactory.getVerifier(method_gen.getClassName())); + // InstConstraintVisitor icv = + // InstConstraintVisitor.getInstance(VerifierFactory.getVerifier(method_gen.getClassName())); icv.setFrame(workingFrame); - getInstruction().accept(icv); - } - catch(StructuralCodeConstraintException ce){ - ce.extendMessage("","\nInstructionHandle: "+getInstruction()+"\n"); - ce.extendMessage("","\nExecution Frame:\n"+workingFrame); + InstructionWalker.accept(getInstruction().getInstruction(), icv); + } catch (StructuralCodeConstraintException ce) { + ce.extendMessage("", "\nInstructionHandle: " + getInstruction() + "\n"); + ce.extendMessage("", "\nExecution Frame:\n" + workingFrame); extendMessageWithFlow(ce); throw ce; } // This executes the Instruction. // Therefore the workingFrame object is modified. -//ExecutionVisitor ev = ExecutionVisitor.getInstance(VerifierFactory.getVerifier(method_gen.getClassName())); + // ExecutionVisitor ev = ExecutionVisitor.getInstance(VerifierFactory.getVerifier(method_gen.getClassName())); ev.setFrame(workingFrame); - getInstruction().accept(ev); - //getInstruction().accept(ExecutionVisitor.withFrame(workingFrame)); + InstructionWalker.accept(getInstruction().getInstruction(), ev); + // getInstruction().accept(ExecutionVisitor.withFrame(workingFrame)); outFrames.put(lastExecutionJSR(), workingFrame); - return true; // new inFrame was different from old inFrame so merging them - // yielded a different this.inFrame. + return true; // new inFrame was different from old inFrame so merging them + // yielded a different this.inFrame. } /** * Returns a simple String representation of this InstructionContext. */ - public String toString(){ - //TODO: Put information in the brackets, e.g. - // Is this an ExceptionHandler? Is this a RET? Is this the start of - // a subroutine? - String ret = getInstruction().toString(false)+"\t[InstructionContext]"; + public String toString() { + // TODO: Put information in the brackets, e.g. + // Is this an ExceptionHandler? Is this a RET? Is this the start of + // a subroutine? + String ret = getInstruction().toString(false) + "\t[InstructionContext]"; return ret; } /** - * Does the actual merging (vmspec2, page 146). - * Returns true IFF this.inFrame was changed in course of merging with inFrame. + * Does the actual merging (vmspec2, page 146). Returns true IFF this.inFrame was changed in course of merging with inFrame. */ - private boolean mergeInFrames(Frame inFrame){ + private boolean mergeInFrames(Frame inFrame) { // TODO: Can be performance-improved. Frame inF = (Frame) inFrames.get(lastExecutionJSR()); OperandStack oldstack = inF.getStack().getClone(); LocalVariables oldlocals = inF.getLocals().getClone(); - try{ + try { inF.getStack().merge(inFrame.getStack()); inF.getLocals().merge(inFrame.getLocals()); - } - catch (StructuralCodeConstraintException sce){ + } catch (StructuralCodeConstraintException sce) { extendMessageWithFlow(sce); throw sce; } - if ( oldstack.equals(inF.getStack()) && - oldlocals.equals(inF.getLocals()) ){ + if (oldstack.equals(inF.getStack()) && oldlocals.equals(inF.getLocals())) { return false; - } - else{ + } else { return true; } } /** - * Returns the control flow execution chain. This is built - * while execute(Frame, ArrayList)-ing the code represented - * by the surrounding ControlFlowGraph. + * Returns the control flow execution chain. This is built while execute(Frame, ArrayList)-ing the code represented by the + * surrounding ControlFlowGraph. */ - private String getExecutionChain(){ + private String getExecutionChain() { String s = this.toString(); - for (int i=executionPredecessors.size()-1; i>=0; i--){ - s = executionPredecessors.get(i)+"\n" + s; + for (int i = executionPredecessors.size() - 1; i >= 0; i--) { + s = executionPredecessors.get(i) + "\n" + s; } return s; } - /** - * Extends the StructuralCodeConstraintException ("e") object with an at-the-end-extended message. - * This extended message will then reflect the execution flow needed to get to the constraint - * violation that triggered the throwing of the "e" object. + * Extends the StructuralCodeConstraintException ("e") object with an at-the-end-extended message. This extended message + * will then reflect the execution flow needed to get to the constraint violation that triggered the throwing of the "e" + * object. */ - private void extendMessageWithFlow(StructuralCodeConstraintException e){ + private void extendMessageWithFlow(StructuralCodeConstraintException e) { String s = "Execution flow:\n"; - e.extendMessage("", s+getExecutionChain()); + e.extendMessage("", s + getExecutionChain()); } /* * Fulfils the contract of InstructionContext.getInstruction(). */ - public InstructionHandle getInstruction(){ + public InstructionHandle getInstruction() { return instruction; } /** - * Returns the InstructionContextImpl with an JSR/JSR_W - * that was last in the ExecutionChain, without - * a corresponding RET, i.e. - * we were called by this one. - * Returns null if we were called from the top level. + * Returns the InstructionContextImpl with an JSR/JSR_W that was last in the ExecutionChain, without a corresponding RET, + * i.e. we were called by this one. Returns null if we were called from the top level. */ - private InstructionContextImpl lastExecutionJSR(){ - + private InstructionContextImpl lastExecutionJSR() { + int size = executionPredecessors.size(); int retcount = 0; - - for (int i=size-1; i>=0; i--){ - InstructionContextImpl current = (InstructionContextImpl) (executionPredecessors.get(i)); + + for (int i = size - 1; i >= 0; i--) { + InstructionContextImpl current = (InstructionContextImpl) executionPredecessors.get(i); Instruction currentlast = current.getInstruction().getInstruction(); - if (currentlast instanceof RET) retcount++; - if (currentlast.isJsrInstruction()){ + if (currentlast instanceof RET) { + retcount++; + } + if (currentlast.isJsrInstruction()) { retcount--; - if (retcount == -1) return current; + if (retcount == -1) { + return current; + } } } return null; } /* Satisfies InstructionContext.getSuccessors(). */ - public InstructionContext[] getSuccessors(){ + public InstructionContext[] getSuccessors() { return contextsOf(_getSuccessors()); } /** - * A utility method that calculates the successors of a given InstructionHandle - * That means, a RET does have successors as defined here. - * A JsrInstruction has its target as its successor - * (opposed to its physical successor) as defined here. + * A utility method that calculates the successors of a given InstructionHandle That means, a RET does have successors as + * defined here. A JsrInstruction has its target as its successor (opposed to its physical successor) as defined here. */ -// TODO: implement caching! - private InstructionHandle[] _getSuccessors(){ + // TODO: implement caching! + private InstructionHandle[] _getSuccessors() { final InstructionHandle[] empty = new InstructionHandle[0]; final InstructionHandle[] single = new InstructionHandle[1]; final InstructionHandle[] pair = new InstructionHandle[2]; - + Instruction inst = getInstruction().getInstruction(); - - if (inst instanceof RET){ + + if (inst instanceof RET) { Subroutine s = subroutines.subroutineOf(getInstruction()); - if (s==null){ //return empty; // RET in dead code. "empty" would be the correct answer, but we know something about the surrounding project... + if (s == null) { // return empty; // RET in dead code. "empty" would be the correct answer, but we know something + // about the surrounding project... throw new AssertionViolatedException("Asking for successors of a RET in dead code?!"); } -//TODO: remove -throw new AssertionViolatedException("DID YOU REALLY WANT TO ASK FOR RET'S SUCCS?"); -/* - InstructionHandle[] jsrs = s.getEnteringJsrInstructions(); - InstructionHandle[] ret = new InstructionHandle[jsrs.length]; - for (int i=0; i(NOT ORDERED!). + * Returns an InstructionContext[] with all the InstructionContext instances for the method whose control flow is represented by + * this ControlFlowGraph (NOT ORDERED!). */ - public InstructionContext[] getInstructionContexts(){ + public InstructionContext[] getInstructionContexts() { InstructionContext[] ret = new InstructionContext[instructionContexts.values().size()]; return (InstructionContext[]) instructionContexts.values().toArray(ret); } /** - * Returns true, if and only if the said instruction is not reachable; that means, - * if it not part of this ControlFlowGraph. + * Returns true, if and only if the said instruction is not reachable; that means, if it not part of this ControlFlowGraph. */ - public boolean isDead(InstructionHandle i){ + public boolean isDead(InstructionHandle i) { return instructionContexts.containsKey(i); - } + } } diff --git a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/ExecutionVisitor.java b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/ExecutionVisitor.java index f6eed74cc..d0326f7e6 100644 --- a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/ExecutionVisitor.java +++ b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/ExecutionVisitor.java @@ -77,7 +77,6 @@ import org.aspectj.apache.bcel.generic.RET; import org.aspectj.apache.bcel.generic.ReturnaddressType; import org.aspectj.apache.bcel.generic.TABLESWITCH; import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.apache.bcel.generic.InstVisitor; import org.aspectj.apache.bcel.verifier.EmptyInstVisitor; /** @@ -103,12 +102,12 @@ import org.aspectj.apache.bcel.verifier.EmptyInstVisitor; * If a two-slot type is stored into a local variable, the next variable * is given the type Type.UNKNOWN. * - * @version $Id: ExecutionVisitor.java,v 1.2 2008/05/28 23:53:02 aclement Exp $ + * @version $Id: ExecutionVisitor.java,v 1.3 2008/08/28 00:02:13 aclement Exp $ * @author Enver Haase * @see #visitDSTORE(DSTORE o) * @see InstConstraintVisitor */ -public class ExecutionVisitor extends EmptyInstVisitor implements InstVisitor { +public class ExecutionVisitor extends EmptyInstVisitor { /** * The executionframe we're operating on. diff --git a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/InstConstraintVisitor.java b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/InstConstraintVisitor.java index f6977d177..7c146ced7 100644 --- a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/InstConstraintVisitor.java +++ b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/InstConstraintVisitor.java @@ -89,7 +89,6 @@ import org.aspectj.apache.bcel.generic.ReferenceType; import org.aspectj.apache.bcel.generic.ReturnaddressType; import org.aspectj.apache.bcel.generic.TABLESWITCH; import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.apache.bcel.generic.InstVisitor; /** @@ -100,12 +99,12 @@ import org.aspectj.apache.bcel.generic.InstVisitor; * TODO: Currently, the JVM's behaviour concerning monitors (MONITORENTER, * MONITOREXIT) is not modeled in JustIce. * - * @version $Id: InstConstraintVisitor.java,v 1.2 2008/05/28 23:53:03 aclement Exp $ + * @version $Id: InstConstraintVisitor.java,v 1.3 2008/08/28 00:02:13 aclement Exp $ * @author Enver Haase * @see org.aspectj.apache.bcel.verifier.exc.StructuralCodeConstraintException * @see org.aspectj.apache.bcel.verifier.exc.LinkingConstraintException */ -public class InstConstraintVisitor extends EmptyInstVisitor implements InstVisitor { +public class InstConstraintVisitor extends EmptyInstVisitor { private static ObjectType GENERIC_ARRAY = new ObjectType("org.aspectj.apache.bcel.verifier.structurals.GenericArray"); diff --git a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/Subroutines.java b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/Subroutines.java index 267d1394d..b83d6fc97 100644 --- a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/Subroutines.java +++ b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/structurals/Subroutines.java @@ -84,7 +84,7 @@ import java.util.Iterator; * * TODO: refer to the paper. * - * @version $Id: Subroutines.java,v 1.2 2008/05/28 23:53:02 aclement Exp $ + * @version $Id: Subroutines.java,v 1.3 2008/08/28 00:02:13 aclement Exp $ * @author Enver Haase * @see #getTopLevel() */ @@ -98,7 +98,7 @@ public class Subroutines{ * field. This is used for the "top-level" Subroutine; * i.e. no subroutine. */ - private final int UNSET = -1; + private final static int UNSET = -1; /** * The Local Variable slot where the first diff --git a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/util/BCELFactory.java b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/util/BCELFactory.java index feda6dcae..4f1ae7e8b 100644 --- a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/util/BCELFactory.java +++ b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/util/BCELFactory.java @@ -25,6 +25,7 @@ import org.aspectj.apache.bcel.generic.MethodGen; import org.aspectj.apache.bcel.generic.ObjectType; import org.aspectj.apache.bcel.generic.RET; import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.apache.bcel.verifier.InstructionWalker; /* ==================================================================== * The Apache Software License, Version 1.1 @@ -81,297 +82,274 @@ import org.aspectj.apache.bcel.generic.Type; */ /** - * Factory creates il.append() statements, and sets instruction targets. - * A helper class for BCELifier. - * + * Factory creates il.append() statements, and sets instruction targets. A helper class for BCELifier. + * * @see BCELifier - * @version $Id: BCELFactory.java,v 1.3 2008/05/30 17:29:27 aclement Exp $ - * @author M. Dahm + * @version $Id: BCELFactory.java,v 1.4 2008/08/28 00:02:14 aclement Exp $ + * @author M. Dahm */ class BCELFactory extends org.aspectj.apache.bcel.verifier.EmptyInstVisitor { - private MethodGen _mg; - private PrintWriter _out; - private ConstantPool _cp; - - BCELFactory(MethodGen mg, PrintWriter out) { - _mg = mg; - _cp = mg.getConstantPool(); - _out = out; - } - - private HashMap branch_map = new HashMap(); // Map - - public void start() { - if(!_mg.isAbstract() && !_mg.isNative()) { - for(InstructionHandle ih = _mg.getInstructionList().getStart(); - ih != null; ih = ih.getNext()) { - Instruction i = ih.getInstruction(); - - if(i instanceof InstructionBranch) { - branch_map.put(i, ih); // memorize container + private final MethodGen _mg; + private final PrintWriter _out; + private final ConstantPool _cp; + + BCELFactory(MethodGen mg, PrintWriter out) { + _mg = mg; + _cp = mg.getConstantPool(); + _out = out; + } + + private final HashMap branch_map = new HashMap(); // Map + + public void start() { + if (!_mg.isAbstract() && !_mg.isNative()) { + for (InstructionHandle ih = _mg.getInstructionList().getStart(); ih != null; ih = ih.getNext()) { + Instruction i = ih.getInstruction(); + + if (i instanceof InstructionBranch) { + branch_map.put(i, ih); // memorize container + } + + if (ih.hasTargeters()) { + if (i instanceof InstructionBranch) { + _out.println(" InstructionHandle ih_" + ih.getPosition() + ";"); + } else { + _out.print(" InstructionHandle ih_" + ih.getPosition() + " = "); + } + } else { + _out.print(" "); + } + + if (!visitInstruction(i)) { + InstructionWalker.accept(i, this); + } + } + + updateBranchTargets(); + updateExceptionHandlers(); + } + } + + private boolean visitInstruction(Instruction i) { + short opcode = i.getOpcode(); + + if (InstructionConstants.INSTRUCTIONS[opcode] != null && !i.isConstantInstruction() && !i.isReturnInstruction()) { // Handled + // below + _out.println("il.append(InstructionConstants." + i.getName().toUpperCase() + ");"); + return true; + } + + return false; + } + + public void visitLocalVariableInstruction(Instruction i) { + short opcode = i.getOpcode(); + Type type = i.getType(_cp); + + if (opcode == Constants.IINC) { + _out.println("il.append(new IINC(" + i.getIndex() + ", " + ((IINC) i).getIncrement() + "));"); + } else { + String kind = opcode < Constants.ISTORE ? "Load" : "Store"; + _out.println("il.append(_factory.create" + kind + "(" + BCELifier.printType(type) + ", " + i.getIndex() + "));"); + } + } + + public void visitArrayInstruction(Instruction i) { + short opcode = i.getOpcode(); + Type type = i.getType(_cp); + String kind = opcode < Constants.IASTORE ? "Load" : "Store"; + + _out.println("il.append(_factory.createArray" + kind + "(" + BCELifier.printType(type) + "));"); + } + + public void visitFieldInstruction(FieldInstruction i) { + short opcode = i.getOpcode(); + + String class_name = i.getClassName(_cp); + String field_name = i.getFieldName(_cp); + Type type = i.getFieldType(_cp); + + _out.println("il.append(_factory.createFieldAccess(\"" + class_name + "\", \"" + field_name + "\", " + + BCELifier.printType(type) + ", " + "Constants." + Constants.OPCODE_NAMES[opcode].toUpperCase() + "));"); + } + + public void visitInvokeInstruction(InvokeInstruction i) { + short opcode = i.getOpcode(); + String class_name = i.getClassName(_cp); + String method_name = i.getMethodName(_cp); + Type type = i.getReturnType(_cp); + Type[] arg_types = i.getArgumentTypes(_cp); + + _out.println("il.append(_factory.createInvoke(\"" + class_name + "\", \"" + method_name + "\", " + + BCELifier.printType(type) + ", " + BCELifier.printArgumentTypes(arg_types) + ", " + "Constants." + + Constants.OPCODE_NAMES[opcode].toUpperCase() + "));"); + } + + public void visitAllocationInstruction(Instruction i) { + Type type; + + if (i.isConstantPoolInstruction()) { + type = ((InstructionCP) i).getType(_cp); + } else { + type = ((InstructionByte) i).getType(); + } + + short opcode = i.getOpcode(); + int dim = 1; + + switch (opcode) { + case Constants.NEW: + _out.println("il.append(_factory.createNew(\"" + ((ObjectType) type).getClassName() + "\"));"); + break; + + case Constants.MULTIANEWARRAY: + dim = ((MULTIANEWARRAY) i).getDimensions(); + + case Constants.ANEWARRAY: + case Constants.NEWARRAY: + _out.println("il.append(_factory.createNewArray(" + BCELifier.printType(type) + ", (short) " + dim + "));"); + break; + + default: + throw new RuntimeException("Oops: " + opcode); + } + } + + private void createConstant(Object value) { + String embed = value.toString(); + + if (value instanceof String) { + embed = '"' + Utility.convertString(value.toString()) + '"'; + } else if (value instanceof Character) { + embed = "(char)0x" + Integer.toHexString(((Character) value).charValue()); + } + + _out.println("il.append(new PUSH(_cp, " + embed + "));"); + } + + public void visitLDC(Instruction i) { + createConstant(i.getValue(_cp)); + } + + public void visitLDC2_W(Instruction i) { + createConstant(i.getValue(_cp)); + } + + public void visitConstantPushInstruction(Instruction i) { + createConstant(i.getValue()); + } + + public void visitINSTANCEOF(Instruction i) { + Type type = i.getType(_cp); + + _out.println("il.append(new INSTANCEOF(_cp.addClass(" + BCELifier.printType(type) + ")));"); + } + + public void visitCHECKCAST(Instruction i) { + Type type = i.getType(_cp); + + _out.println("il.append(_factory.createCheckCast(" + BCELifier.printType(type) + "));"); } - if(ih.hasTargeters()) { - if(i instanceof InstructionBranch) { - _out.println(" InstructionHandle ih_" + ih.getPosition() + ";"); - } else { - _out.print(" InstructionHandle ih_" + ih.getPosition() + " = "); - } - } else { - _out.print(" "); + public void visitReturnInstruction(Instruction i) { + Type type = i.getType(_cp); + + _out.println("il.append(_factory.createReturn(" + BCELifier.printType(type) + "));"); } - if(!visitInstruction(i)) - i.accept(this); - } - - updateBranchTargets(); - updateExceptionHandlers(); - } - } - - private boolean visitInstruction(Instruction i) { - short opcode = i.getOpcode(); - - if((InstructionConstants.INSTRUCTIONS[opcode] != null) && - !(i.isConstantInstruction()) && - !(i.isReturnInstruction())) { // Handled below - _out.println("il.append(InstructionConstants." + - i.getName().toUpperCase() + ");"); - return true; - } - - return false; - } - - public void visitLocalVariableInstruction(Instruction i) { - short opcode = i.getOpcode(); - Type type = i.getType(_cp); - - if(opcode == Constants.IINC) { - _out.println("il.append(new IINC(" + i.getIndex() + ", " + - ((IINC)i).getIncrement() + "));"); - } else { - String kind = (opcode < Constants.ISTORE)? "Load" : "Store"; - _out.println("il.append(_factory.create" + kind + "(" + - BCELifier.printType(type) + ", " + - i.getIndex() + "));"); - } - } - - public void visitArrayInstruction(Instruction i) { - short opcode = i.getOpcode(); - Type type = i.getType(_cp); - String kind = (opcode < Constants.IASTORE)? "Load" : "Store"; - - _out.println("il.append(_factory.createArray" + kind + "(" + - BCELifier.printType(type) + "));"); - } - - public void visitFieldInstruction(FieldInstruction i) { - short opcode = i.getOpcode(); - - String class_name = i.getClassName(_cp); - String field_name = i.getFieldName(_cp); - Type type = i.getFieldType(_cp); - - _out.println("il.append(_factory.createFieldAccess(\"" + - class_name + "\", \"" + field_name + "\", " + - BCELifier.printType(type) + ", " + - "Constants." + Constants.OPCODE_NAMES[opcode].toUpperCase() + - "));"); - } - - public void visitInvokeInstruction(InvokeInstruction i) { - short opcode = i.getOpcode(); - String class_name = i.getClassName(_cp); - String method_name = i.getMethodName(_cp); - Type type = i.getReturnType(_cp); - Type[] arg_types = i.getArgumentTypes(_cp); - - _out.println("il.append(_factory.createInvoke(\"" + - class_name + "\", \"" + method_name + "\", " + - BCELifier.printType(type) + ", " + - BCELifier.printArgumentTypes(arg_types) + ", " + - "Constants." + Constants.OPCODE_NAMES[opcode].toUpperCase() + - "));"); - } - - public void visitAllocationInstruction(Instruction i) { - Type type; - - if(i.isConstantPoolInstruction()) { - type = ((InstructionCP)i).getType(_cp); - } else { - type = ((InstructionByte)i).getType(); - } - - short opcode = ((Instruction)i).getOpcode(); - int dim = 1; - - switch(opcode) { - case Constants.NEW: - _out.println("il.append(_factory.createNew(\"" + - ((ObjectType)type).getClassName() + "\"));"); - break; - - case Constants.MULTIANEWARRAY: - dim = ((MULTIANEWARRAY)i).getDimensions(); - - case Constants.ANEWARRAY: - case Constants.NEWARRAY: - _out.println("il.append(_factory.createNewArray(" + - BCELifier.printType(type) + ", (short) " + dim + "));"); - break; - - default: - throw new RuntimeException("Oops: " + opcode); - } - } - - private void createConstant(Object value) { - String embed = value.toString(); - - if(value instanceof String) - embed = '"' + Utility.convertString(value.toString()) + '"'; - else if(value instanceof Character) - embed = "(char)0x" + Integer.toHexString(((Character)value).charValue()); - - _out.println("il.append(new PUSH(_cp, " + embed + "));"); - } - - public void visitLDC(Instruction i) { - createConstant(i.getValue(_cp)); - } - - public void visitLDC2_W(Instruction i) { - createConstant(i.getValue(_cp)); - } - - public void visitConstantPushInstruction(Instruction i) { - createConstant(i.getValue()); - } - - public void visitINSTANCEOF(Instruction i) { - Type type = i.getType(_cp); - - _out.println("il.append(new INSTANCEOF(_cp.addClass(" + - BCELifier.printType(type) + ")));"); - } - - public void visitCHECKCAST(Instruction i) { - Type type = i.getType(_cp); - - _out.println("il.append(_factory.createCheckCast(" + - BCELifier.printType(type) + "));"); - } - - public void visitReturnInstruction(Instruction i) { - Type type = i.getType(_cp); - - _out.println("il.append(_factory.createReturn(" + - BCELifier.printType(type) + "));"); - } - - // Memorize BranchInstructions that need an update - private ArrayList branches = new ArrayList(); - - public void visitBranchInstruction(InstructionBranch bi) { - BranchHandle bh = (BranchHandle)branch_map.get(bi); - int pos = bh.getPosition(); - String name = bi.getName() + "_" + pos; - - if(bi instanceof InstructionSelect) { - InstructionSelect s = (InstructionSelect)bi; - branches.add(bi); - - StringBuffer args = new StringBuffer("new int[] { "); - int[] matchs = s.getMatchs(); - - for(int i=0; i < matchs.length; i++) { - args.append(matchs[i]); - - if(i < matchs.length - 1) - args.append(", "); - } - - args.append(" }"); - - _out.print(" Select " + name + " = new " + - bi.getName().toUpperCase() + "(" + args + - ", new InstructionHandle[] { "); - - for(int i=0; i < matchs.length; i++) { - _out.print("null"); - - if(i < matchs.length - 1) - _out.print(", "); - } - - _out.println(");"); - } else { - int t_pos = bh.getTarget().getPosition(); - String target; - - if(pos > t_pos) { - target = "ih_" + t_pos; - } else { - branches.add(bi); - target = "null"; - } - - _out.println(" BranchInstruction " + name + - " = _factory.createBranchInstruction(" + - "Constants." + bi.getName().toUpperCase() + ", " + - target + ");"); - } - - if(bh.hasTargeters()) - _out.println(" ih_" + pos + " = il.append(" + name + ");"); - else - _out.println(" il.append(" + name + ");"); - } - - public void visitRET(RET i) { - _out.println("il.append(new RET(" + i.getIndex() + ")));"); - } - - private void updateBranchTargets() { - for(Iterator i = branches.iterator(); i.hasNext(); ) { - InstructionBranch bi = (InstructionBranch)i.next(); - BranchHandle bh = (BranchHandle)branch_map.get(bi); - int pos = bh.getPosition(); - String name = bi.getName() + "_" + pos; - int t_pos = bh.getTarget().getPosition(); - - _out.println(" " + name + ".setTarget(ih_" + t_pos + ");"); - - if(bi instanceof InstructionSelect) { - InstructionHandle[] ihs = ((InstructionSelect)bi).getTargets(); - - for(int j = 0; j < ihs.length; j++) { - t_pos = ihs[j].getPosition(); - - _out.println(" " + name + ".setTarget(" + j + - ", ih_" + t_pos + ");"); + // Memorize BranchInstructions that need an update + private final ArrayList branches = new ArrayList(); + + public void visitBranchInstruction(InstructionBranch bi) { + BranchHandle bh = (BranchHandle) branch_map.get(bi); + int pos = bh.getPosition(); + String name = bi.getName() + "_" + pos; + + if (bi instanceof InstructionSelect) { + InstructionSelect s = (InstructionSelect) bi; + branches.add(bi); + + StringBuffer args = new StringBuffer("new int[] { "); + int[] matchs = s.getMatchs(); + + for (int i = 0; i < matchs.length; i++) { + args.append(matchs[i]); + + if (i < matchs.length - 1) { + args.append(", "); + } + } + + args.append(" }"); + + _out.print(" Select " + name + " = new " + bi.getName().toUpperCase() + "(" + args + ", new InstructionHandle[] { "); + + for (int i = 0; i < matchs.length; i++) { + _out.print("null"); + + if (i < matchs.length - 1) { + _out.print(", "); + } + } + + _out.println(");"); + } else { + int t_pos = bh.getTarget().getPosition(); + String target; + + if (pos > t_pos) { + target = "ih_" + t_pos; + } else { + branches.add(bi); + target = "null"; + } + + _out.println(" BranchInstruction " + name + " = _factory.createBranchInstruction(" + "Constants." + + bi.getName().toUpperCase() + ", " + target + ");"); + } + + if (bh.hasTargeters()) { + _out.println(" ih_" + pos + " = il.append(" + name + ");"); + } else { + _out.println(" il.append(" + name + ");"); + } + } + + public void visitRET(RET i) { + _out.println("il.append(new RET(" + i.getIndex() + ")));"); + } + + private void updateBranchTargets() { + for (Iterator i = branches.iterator(); i.hasNext();) { + InstructionBranch bi = (InstructionBranch) i.next(); + BranchHandle bh = (BranchHandle) branch_map.get(bi); + int pos = bh.getPosition(); + String name = bi.getName() + "_" + pos; + int t_pos = bh.getTarget().getPosition(); + + _out.println(" " + name + ".setTarget(ih_" + t_pos + ");"); + + if (bi instanceof InstructionSelect) { + InstructionHandle[] ihs = ((InstructionSelect) bi).getTargets(); + + for (int j = 0; j < ihs.length; j++) { + t_pos = ihs[j].getPosition(); + + _out.println(" " + name + ".setTarget(" + j + ", ih_" + t_pos + ");"); + } + } + } + } + + private void updateExceptionHandlers() { + CodeExceptionGen[] handlers = _mg.getExceptionHandlers(); + + for (int i = 0; i < handlers.length; i++) { + CodeExceptionGen h = handlers[i]; + String type = h.getCatchType() == null ? "null" : BCELifier.printType(h.getCatchType()); + + _out.println(" method.addExceptionHandler(" + "ih_" + h.getStartPC().getPosition() + ", " + "ih_" + + h.getEndPC().getPosition() + ", " + "ih_" + h.getHandlerPC().getPosition() + ", " + type + ");"); + } } - } - } - } - - private void updateExceptionHandlers() { - CodeExceptionGen[] handlers = _mg.getExceptionHandlers(); - - for(int i=0; i < handlers.length; i++) { - CodeExceptionGen h = handlers[i]; - String type = (h.getCatchType() == null)? - "null" : BCELifier.printType(h.getCatchType()); - - _out.println(" method.addExceptionHandler(" + - "ih_" + h.getStartPC().getPosition() + ", " + - "ih_" + h.getEndPC().getPosition() + ", " + - "ih_" + h.getHandlerPC().getPosition() + ", " + - type + ");"); - } - } } diff --git a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/util/InstructionFinder.java b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/util/InstructionFinder.java index 6fad9bcb7..58ce3c1ef 100644 --- a/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/util/InstructionFinder.java +++ b/bcel-builder/verifier-src/org/aspectj/apache/bcel/verifier/util/InstructionFinder.java @@ -81,7 +81,7 @@ import org.apache.regexp.*; ... } - * @version $Id: InstructionFinder.java,v 1.2 2008/05/28 23:53:04 aclement Exp $ + * @version $Id: InstructionFinder.java,v 1.3 2008/08/28 00:02:14 aclement Exp $ * @author M. Dahm * @see Instruction * @see InstructionList @@ -413,28 +413,28 @@ public class InstructionFinder { return buf.toString(); } - /* - * Internal debugging routines. - */ - private static final String pattern2string(String pattern) { - return pattern2string(pattern, true); - } - - private static final String pattern2string(String pattern, boolean make_string) { - StringBuffer buf = new StringBuffer(); - - for(int i=0; i < pattern.length(); i++) { - char ch = pattern.charAt(i); - - if(ch >= OFFSET) { - if(make_string) - buf.append(Constants.OPCODE_NAMES[ch - OFFSET]); - else - buf.append((int)(ch - OFFSET)); - } else - buf.append(ch); - } - - return buf.toString(); - } +// /* +// * Internal debugging routines. +// */ +// private static final String pattern2string(String pattern) { +// return pattern2string(pattern, true); +// } +// +// private static final String pattern2string(String pattern, boolean make_string) { +// StringBuffer buf = new StringBuffer(); +// +// for(int i=0; i < pattern.length(); i++) { +// char ch = pattern.charAt(i); +// +// if(ch >= OFFSET) { +// if(make_string) +// buf.append(Constants.OPCODE_NAMES[ch - OFFSET]); +// else +// buf.append((int)(ch - OFFSET)); +// } else +// buf.append(ch); +// } +// +// return buf.toString(); +// } } -- 2.39.5