8141551: C2 can not handle returns with inccompatible interface arrays

Thu, 21 Apr 2016 21:53:15 +0530

author
shshahma
date
Thu, 21 Apr 2016 21:53:15 +0530
changeset 8422
09687c445ce1
parent 8421
3e1cd663c2d3
child 8423
2988e5adeb8c

8141551: C2 can not handle returns with inccompatible interface arrays
Reviewed-by: kvn

src/share/vm/opto/cfgnode.cpp file | annotate | diff | comparison | revisions
src/share/vm/opto/parse1.cpp file | annotate | diff | comparison | revisions
src/share/vm/opto/type.cpp file | annotate | diff | comparison | revisions
src/share/vm/opto/type.hpp file | annotate | diff | comparison | revisions
test/compiler/types/TestMeetIncompatibleInterfaceArrays.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/vm/opto/cfgnode.cpp	Fri Apr 15 12:02:37 2016 +0530
     1.2 +++ b/src/share/vm/opto/cfgnode.cpp	Thu Apr 21 21:53:15 2016 +0530
     1.3 @@ -973,7 +973,7 @@
     1.4  #ifdef ASSERT
     1.5    // The following logic has been moved into TypeOopPtr::filter.
     1.6    const Type* jt = t->join_speculative(_type);
     1.7 -  if( jt->empty() ) {           // Emptied out???
     1.8 +  if (jt->empty()) {           // Emptied out???
     1.9  
    1.10      // Check for evil case of 't' being a class and '_type' expecting an
    1.11      // interface.  This can happen because the bytecodes do not contain
    1.12 @@ -984,14 +984,21 @@
    1.13      // be 'I' or 'j/l/O'.  Thus we'll pick 'j/l/O'.  If this then flows
    1.14      // into a Phi which "knows" it's an Interface type we'll have to
    1.15      // uplift the type.
    1.16 -    if( !t->empty() && ttip && ttip->is_loaded() && ttip->klass()->is_interface() )
    1.17 -      { assert(ft == _type, ""); } // Uplift to interface
    1.18 -    else if( !t->empty() && ttkp && ttkp->is_loaded() && ttkp->klass()->is_interface() )
    1.19 -      { assert(ft == _type, ""); } // Uplift to interface
    1.20 -    // Otherwise it's something stupid like non-overlapping int ranges
    1.21 -    // found on dying counted loops.
    1.22 -    else
    1.23 -      { assert(ft == Type::TOP, ""); } // Canonical empty value
    1.24 +    if (!t->empty() && ttip && ttip->is_loaded() && ttip->klass()->is_interface()) {
    1.25 +      assert(ft == _type, ""); // Uplift to interface
    1.26 +    } else if (!t->empty() && ttkp && ttkp->is_loaded() && ttkp->klass()->is_interface()) {
    1.27 +      assert(ft == _type, ""); // Uplift to interface
    1.28 +    } else {
    1.29 +      // We also have to handle 'evil cases' of interface- vs. class-arrays
    1.30 +      Type::get_arrays_base_elements(jt, _type, NULL, &ttip);
    1.31 +      if (!t->empty() && ttip != NULL && ttip->is_loaded() && ttip->klass()->is_interface()) {
    1.32 +          assert(ft == _type, "");   // Uplift to array of interface
    1.33 +      } else {
    1.34 +        // Otherwise it's something stupid like non-overlapping int ranges
    1.35 +        // found on dying counted loops.
    1.36 +        assert(ft == Type::TOP, ""); // Canonical empty value
    1.37 +      }
    1.38 +    }
    1.39    }
    1.40  
    1.41    else {
     2.1 --- a/src/share/vm/opto/parse1.cpp	Fri Apr 15 12:02:37 2016 +0530
     2.2 +++ b/src/share/vm/opto/parse1.cpp	Thu Apr 21 21:53:15 2016 +0530
     2.3 @@ -962,13 +962,18 @@
     2.4        // In case of concurrent class loading, the type we set for the
     2.5        // ret_phi in build_exits() may have been too optimistic and the
     2.6        // ret_phi may be top now.
     2.7 -#ifdef ASSERT
     2.8 +      // Otherwise, we've encountered an error and have to mark the method as
     2.9 +      // not compilable. Just using an assertion instead would be dangerous
    2.10 +      // as this could lead to an infinite compile loop in non-debug builds.
    2.11        {
    2.12          MutexLockerEx ml(Compile_lock, Mutex::_no_safepoint_check_flag);
    2.13 -        assert(ret_type->isa_ptr() && C->env()->system_dictionary_modification_counter_changed(), "return value must be well defined");
    2.14 +        if (C->env()->system_dictionary_modification_counter_changed()) {
    2.15 +          C->record_failure(C2Compiler::retry_class_loading_during_parsing());
    2.16 +        } else {
    2.17 +          C->record_method_not_compilable("Can't determine return type.");
    2.18 +        }
    2.19        }
    2.20 -#endif
    2.21 -      C->record_failure(C2Compiler::retry_class_loading_during_parsing());
    2.22 +      return;
    2.23      }
    2.24      _exits.push_node(ret_type->basic_type(), ret_phi);
    2.25    }
    2.26 @@ -2093,15 +2098,24 @@
    2.27      // here.
    2.28      Node* phi = _exits.argument(0);
    2.29      const TypeInstPtr *tr = phi->bottom_type()->isa_instptr();
    2.30 -    if( tr && tr->klass()->is_loaded() &&
    2.31 -        tr->klass()->is_interface() ) {
    2.32 +    if (tr && tr->klass()->is_loaded() &&
    2.33 +        tr->klass()->is_interface()) {
    2.34        const TypeInstPtr *tp = value->bottom_type()->isa_instptr();
    2.35        if (tp && tp->klass()->is_loaded() &&
    2.36            !tp->klass()->is_interface()) {
    2.37          // sharpen the type eagerly; this eases certain assert checking
    2.38          if (tp->higher_equal(TypeInstPtr::NOTNULL))
    2.39            tr = tr->join_speculative(TypeInstPtr::NOTNULL)->is_instptr();
    2.40 -        value = _gvn.transform(new (C) CheckCastPPNode(0,value,tr));
    2.41 +        value = _gvn.transform(new (C) CheckCastPPNode(0, value, tr));
    2.42 +      }
    2.43 +    } else {
    2.44 +      // Also handle returns of oop-arrays to an arrays-of-interface return
    2.45 +      const TypeInstPtr* phi_tip;
    2.46 +      const TypeInstPtr* val_tip;
    2.47 +      Type::get_arrays_base_elements(phi->bottom_type(), value->bottom_type(), &phi_tip, &val_tip);
    2.48 +      if (phi_tip != NULL && phi_tip->is_loaded() && phi_tip->klass()->is_interface() &&
    2.49 +          val_tip != NULL && val_tip->is_loaded() && !val_tip->klass()->is_interface()) {
    2.50 +         value = _gvn.transform(new (C) CheckCastPPNode(0, value, phi->bottom_type()));
    2.51        }
    2.52      }
    2.53      phi->add_req(value);
     3.1 --- a/src/share/vm/opto/type.cpp	Fri Apr 15 12:02:37 2016 +0530
     3.2 +++ b/src/share/vm/opto/type.cpp	Thu Apr 21 21:53:15 2016 +0530
     3.3 @@ -149,6 +149,33 @@
     3.4    return bt;
     3.5  }
     3.6  
     3.7 +// For two instance arrays of same dimension, return the base element types.
     3.8 +// Otherwise or if the arrays have different dimensions, return NULL.
     3.9 +void Type::get_arrays_base_elements(const Type *a1, const Type *a2,
    3.10 +                                    const TypeInstPtr **e1, const TypeInstPtr **e2) {
    3.11 +
    3.12 +  if (e1) *e1 = NULL;
    3.13 +  if (e2) *e2 = NULL;
    3.14 +  const TypeAryPtr* a1tap = (a1 == NULL) ? NULL : a1->isa_aryptr();
    3.15 +  const TypeAryPtr* a2tap = (a2 == NULL) ? NULL : a2->isa_aryptr();
    3.16 +
    3.17 +  if (a1tap != NULL && a2tap != NULL) {
    3.18 +    // Handle multidimensional arrays
    3.19 +    const TypePtr* a1tp = a1tap->elem()->make_ptr();
    3.20 +    const TypePtr* a2tp = a2tap->elem()->make_ptr();
    3.21 +    while (a1tp && a1tp->isa_aryptr() && a2tp && a2tp->isa_aryptr()) {
    3.22 +      a1tap = a1tp->is_aryptr();
    3.23 +      a2tap = a2tp->is_aryptr();
    3.24 +      a1tp = a1tap->elem()->make_ptr();
    3.25 +      a2tp = a2tap->elem()->make_ptr();
    3.26 +    }
    3.27 +    if (a1tp && a1tp->isa_instptr() && a2tp && a2tp->isa_instptr()) {
    3.28 +      if (e1) *e1 = a1tp->is_instptr();
    3.29 +      if (e2) *e2 = a2tp->is_instptr();
    3.30 +    }
    3.31 +  }
    3.32 +}
    3.33 +
    3.34  //---------------------------get_typeflow_type---------------------------------
    3.35  // Import a type produced by ciTypeFlow.
    3.36  const Type* Type::get_typeflow_type(ciType* type) {
    3.37 @@ -1982,7 +2009,11 @@
    3.38  bool TypeAry::interface_vs_oop(const Type *t) const {
    3.39    const TypeAry* t_ary = t->is_ary();
    3.40    if (t_ary) {
    3.41 -    return _elem->interface_vs_oop(t_ary->_elem);
    3.42 +    const TypePtr* this_ptr = _elem->make_ptr(); // In case we have narrow_oops
    3.43 +    const TypePtr*    t_ptr = t_ary->_elem->make_ptr();
    3.44 +    if(this_ptr != NULL && t_ptr != NULL) {
    3.45 +      return this_ptr->interface_vs_oop(t_ptr);
    3.46 +    }
    3.47    }
    3.48    return false;
    3.49  }
    3.50 @@ -2834,8 +2865,17 @@
    3.51      // be 'I' or 'j/l/O'.  Thus we'll pick 'j/l/O'.  If this then flows
    3.52      // into a Phi which "knows" it's an Interface type we'll have to
    3.53      // uplift the type.
    3.54 -    if (!empty() && ktip != NULL && ktip->is_loaded() && ktip->klass()->is_interface())
    3.55 -      return kills;             // Uplift to interface
    3.56 +    if (!empty()) {
    3.57 +      if (ktip != NULL && ktip->is_loaded() && ktip->klass()->is_interface()) {
    3.58 +        return kills;           // Uplift to interface
    3.59 +      }
    3.60 +      // Also check for evil cases of 'this' being a class array
    3.61 +      // and 'kills' expecting an array of interfaces.
    3.62 +      Type::get_arrays_base_elements(ft, kills, NULL, &ktip);
    3.63 +      if (ktip != NULL && ktip->is_loaded() && ktip->klass()->is_interface()) {
    3.64 +        return kills;           // Uplift to array of interface
    3.65 +      }
    3.66 +    }
    3.67  
    3.68      return Type::TOP;           // Canonical empty value
    3.69    }
     4.1 --- a/src/share/vm/opto/type.hpp	Fri Apr 15 12:02:37 2016 +0530
     4.2 +++ b/src/share/vm/opto/type.hpp	Thu Apr 21 21:53:15 2016 +0530
     4.3 @@ -367,6 +367,11 @@
     4.4      return _const_basic_type[type];
     4.5    }
     4.6  
     4.7 +  // For two instance arrays of same dimension, return the base element types.
     4.8 +  // Otherwise or if the arrays have different dimensions, return NULL.
     4.9 +  static void get_arrays_base_elements(const Type *a1, const Type *a2,
    4.10 +                                       const TypeInstPtr **e1, const TypeInstPtr **e2);
    4.11 +
    4.12    // Mapping to the array element's basic type.
    4.13    BasicType array_element_basic_type() const;
    4.14  
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/test/compiler/types/TestMeetIncompatibleInterfaceArrays.java	Thu Apr 21 21:53:15 2016 +0530
     5.3 @@ -0,0 +1,351 @@
     5.4 +/*
     5.5 + * Copyright 2015 SAP AG.  All Rights Reserved.
     5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     5.7 + *
     5.8 + * This code is free software; you can redistribute it and/or modify it
     5.9 + * under the terms of the GNU General Public License version 2 only, as
    5.10 + * published by the Free Software Foundation.
    5.11 + *
    5.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    5.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    5.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    5.15 + * version 2 for more details (a copy is included in the LICENSE file that
    5.16 + * accompanied this code).
    5.17 + *
    5.18 + * You should have received a copy of the GNU General Public License version
    5.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    5.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    5.21 + *
    5.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    5.23 + * or visit www.oracle.com if you need additional information or have any
    5.24 + * questions.
    5.25 + */
    5.26 +
    5.27 +/*
    5.28 + * @test
    5.29 + * @bug 8141551
    5.30 + * @summary C2 can not handle returns with inccompatible interface arrays
    5.31 + * @library /testlibrary /testlibrary/whitebox/
    5.32 + * @build sun.hotspot.WhiteBox
    5.33 + * @run main ClassFileInstaller sun.hotspot.WhiteBox
    5.34 + *                              sun.hotspot.WhiteBox$WhiteBoxPermission
    5.35 + * @run main/othervm
    5.36 + *        -Xbootclasspath/a:.
    5.37 + *        -XX:+UnlockDiagnosticVMOptions
    5.38 + *        -XX:+WhiteBoxAPI
    5.39 + *        -Xbatch
    5.40 + *        -XX:CompileThreshold=1
    5.41 + *        -XX:-TieredCompilation
    5.42 + *        -XX:CICompilerCount=1
    5.43 + *        -XX:+PrintCompilation
    5.44 + *        -XX:+PrintInlining
    5.45 + *        -XX:CompileCommand=compileonly,MeetIncompatibleInterfaceArrays*.run
    5.46 + *        -XX:CompileCommand=dontinline,TestMeetIncompatibleInterfaceArrays$Helper.createI2*
    5.47 + *        -XX:CompileCommand=quiet
    5.48 + *        TestMeetIncompatibleInterfaceArrays 0
    5.49 + * @run main/othervm
    5.50 + *        -Xbootclasspath/a:.
    5.51 + *        -XX:+UnlockDiagnosticVMOptions
    5.52 + *        -XX:+WhiteBoxAPI
    5.53 + *        -Xbatch
    5.54 + *        -XX:CompileThreshold=1
    5.55 + *        -XX:-TieredCompilation
    5.56 + *        -XX:CICompilerCount=1
    5.57 + *        -XX:+PrintCompilation
    5.58 + *        -XX:+PrintInlining
    5.59 + *        -XX:CompileCommand=compileonly,MeetIncompatibleInterfaceArrays*.run
    5.60 + *        -XX:CompileCommand=inline,TestMeetIncompatibleInterfaceArrays$Helper.createI2*
    5.61 + *        -XX:CompileCommand=quiet
    5.62 + *        TestMeetIncompatibleInterfaceArrays 1
    5.63 + * @run main/othervm
    5.64 + *        -Xbootclasspath/a:.
    5.65 + *        -XX:+UnlockDiagnosticVMOptions
    5.66 + *        -XX:+WhiteBoxAPI
    5.67 + *        -Xbatch
    5.68 + *        -XX:CompileThreshold=1
    5.69 + *        -XX:Tier0InvokeNotifyFreqLog=0 -XX:Tier2InvokeNotifyFreqLog=0 -XX:Tier3InvokeNotifyFreqLog=0 -XX:Tier23InlineeNotifyFreqLog=0
    5.70 + *        -XX:Tier3InvocationThreshold=2 -XX:Tier3MinInvocationThreshold=2 -XX:Tier3CompileThreshold=2
    5.71 + *        -XX:Tier4InvocationThreshold=1 -XX:Tier4MinInvocationThreshold=1 -XX:Tier4CompileThreshold=1
    5.72 + *        -XX:+TieredCompilation
    5.73 + *        -XX:CICompilerCount=2
    5.74 + *        -XX:+PrintCompilation
    5.75 + *        -XX:+PrintInlining
    5.76 + *        -XX:CompileCommand=compileonly,MeetIncompatibleInterfaceArrays*.run
    5.77 + *        -XX:CompileCommand=compileonly,TestMeetIncompatibleInterfaceArrays$Helper.createI2*
    5.78 + *        -XX:CompileCommand=inline,TestMeetIncompatibleInterfaceArrays$Helper.createI2*
    5.79 + *        -XX:CompileCommand=quiet
    5.80 + *        TestMeetIncompatibleInterfaceArrays 2
    5.81 + *
    5.82 + * @author volker.simonis@gmail.com
    5.83 + */
    5.84 +
    5.85 +import java.io.FileOutputStream;
    5.86 +import java.lang.reflect.InvocationTargetException;
    5.87 +import java.lang.reflect.Method;
    5.88 +import jdk.internal.org.objectweb.asm.ClassWriter;
    5.89 +import jdk.internal.org.objectweb.asm.MethodVisitor;
    5.90 +import static jdk.internal.org.objectweb.asm.Opcodes.*;
    5.91 +import sun.hotspot.WhiteBox;
    5.92 +
    5.93 +public class TestMeetIncompatibleInterfaceArrays extends ClassLoader {
    5.94 +
    5.95 +    private static final WhiteBox WB = WhiteBox.getWhiteBox();
    5.96 +
    5.97 +    public static interface I1 { public String getName(); }
    5.98 +    public static interface I2 { public String getName(); }
    5.99 +    public static class I2C implements I2 { public String getName() { return "I2";} }
   5.100 +    public static class I21C implements I2, I1 { public String getName() { return "I2 and I1";} }
   5.101 +
   5.102 +    public static class Helper {
   5.103 +        public static I2 createI2Array0() {
   5.104 +            return new I2C();
   5.105 +        }
   5.106 +        public static I2[] createI2Array1() {
   5.107 +            return new I2C[] { new I2C() };
   5.108 +        }
   5.109 +        public static I2[][] createI2Array2() {
   5.110 +            return new I2C[][] { new I2C[] { new I2C() } };
   5.111 +        }
   5.112 +        public static I2[][][] createI2Array3() {
   5.113 +            return new I2C[][][] { new I2C[][] { new I2C[] { new I2C() } } };
   5.114 +        }
   5.115 +        public static I2[][][][] createI2Array4() {
   5.116 +            return new I2C[][][][] { new I2C[][][] { new I2C[][] { new I2C[] { new I2C() } } } };
   5.117 +        }
   5.118 +        public static I2[][][][][] createI2Array5() {
   5.119 +            return new I2C[][][][][] { new I2C[][][][] { new I2C[][][] { new I2C[][] { new I2C[] { new I2C() } } } } };
   5.120 +        }
   5.121 +        public static I2 createI21Array0() {
   5.122 +            return new I21C();
   5.123 +        }
   5.124 +        public static I2[] createI21Array1() {
   5.125 +            return new I21C[] { new I21C() };
   5.126 +        }
   5.127 +        public static I2[][] createI21Array2() {
   5.128 +            return new I21C[][] { new I21C[] { new I21C() } };
   5.129 +        }
   5.130 +        public static I2[][][] createI21Array3() {
   5.131 +            return new I21C[][][] { new I21C[][] { new I21C[] { new I21C() } } };
   5.132 +        }
   5.133 +        public static I2[][][][] createI21Array4() {
   5.134 +            return new I21C[][][][] { new I21C[][][] { new I21C[][] { new I21C[] { new I21C() } } } };
   5.135 +        }
   5.136 +        public static I2[][][][][] createI21Array5() {
   5.137 +            return new I21C[][][][][] { new I21C[][][][] { new I21C[][][] { new I21C[][] { new I21C[] { new I21C() } } } } };
   5.138 +        }
   5.139 +    }
   5.140 +
   5.141 +    // Location for the generated class files
   5.142 +    public static final String PATH = System.getProperty("test.classes", ".") + java.io.File.separator;
   5.143 +
   5.144 +    /*
   5.145 +     * With 'good == false' this helper method creates the following classes
   5.146 +     * (using the nested 'Helper' class and the nested interfaces 'I1' and 'I2').
   5.147 +     * For brevity I omit the enclosing class 'TestMeetIncompatibleInterfaceArrays' in the
   5.148 +     * following examples:
   5.149 +     *
   5.150 +     * public class MeetIncompatibleInterfaceArrays0ASM {
   5.151 +     *   public static I1 run() {
   5.152 +     *     return Helper.createI2Array0(); // returns I2
   5.153 +     *   }
   5.154 +     *   public static void test() {
   5.155 +     *     I1 i1 = run();
   5.156 +     *     System.out.println(i1.getName());
   5.157 +     *   }
   5.158 +     * }
   5.159 +     * public class MeetIncompatibleInterfaceArrays1ASM {
   5.160 +     *   public static I1[] run() {
   5.161 +     *     return Helper.createI2Array1(); // returns I2[]
   5.162 +     *   }
   5.163 +     *   public static void test() {
   5.164 +     *     I1[] i1 = run();
   5.165 +     *     System.out.println(i1[0].getName());
   5.166 +     *   }
   5.167 +     * }
   5.168 +     * ...
   5.169 +     * // MeetIncompatibleInterfaceArrays4ASM is special because it creates
   5.170 +     * // an illegal class which will be rejected by the verifier.
   5.171 +     * public class MeetIncompatibleInterfaceArrays4ASM {
   5.172 +     *   public static I1[][][][] run() {
   5.173 +     *     return Helper.createI2Array3(); // returns I1[][][] which gives a verifier error because return expects I1[][][][]
   5.174 +     *   }
   5.175 +     *   public static void test() {
   5.176 +     *     I1[][][][][] i1 = run();
   5.177 +     *     System.out.println(i1[0][0][0][0][0].getName());
   5.178 +     *   }
   5.179 +     * ...
   5.180 +     * public class MeetIncompatibleInterfaceArrays5ASM {
   5.181 +     *   public static I1[][][][][] run() {
   5.182 +     *     return Helper.createI2Array5(); // returns I2[][][][][]
   5.183 +     *   }
   5.184 +     *   public static void test() {
   5.185 +     *     I1[][][][][] i1 = run();
   5.186 +     *     System.out.println(i1[0][0][0][0][0].getName());
   5.187 +     *   }
   5.188 +     * }
   5.189 +     *
   5.190 +     * Notice that this is not legal Java code. We would have to use a cast in "run()" to make it legal:
   5.191 +     *
   5.192 +     *   public static I1[] run() {
   5.193 +     *     return (I1[])Helper.createI2Array1(); // returns I2[]
   5.194 +     *   }
   5.195 +     *
   5.196 +     * But in pure bytecode, the "run()" methods are perfectly legal:
   5.197 +     *
   5.198 +     *   public static I1[] run();
   5.199 +     *     Code:
   5.200 +     *       0: invokestatic  #16  // Method Helper.createI2Array1:()[LI2;
   5.201 +     *       3: areturn
   5.202 +     *
   5.203 +     * The "test()" method calls the "getName()" function from I1 on the objects returned by "run()".
   5.204 +     * This will epectedly fail with an "IncompatibleClassChangeError" because the objects returned
   5.205 +     * by "run()" (and by createI2Array()) are actually of type "I2C" and only implement "I2" but not "I1".
   5.206 +     *
   5.207 +     *
   5.208 +     * With 'good == true' this helper method will create the following classes:
   5.209 +     *
   5.210 +     * public class MeetIncompatibleInterfaceArraysGood0ASM {
   5.211 +     *   public static I1 run() {
   5.212 +     *     return Helper.createI21Array0(); // returns I2
   5.213 +     *   }
   5.214 +     *   public static void test() {
   5.215 +     *     I1 i1 = run();
   5.216 +     *     System.out.println(i1.getName());
   5.217 +     *   }
   5.218 +     * }
   5.219 +     *
   5.220 +     * Calling "test()" on these objects will succeed and output "I2 and I1" because now the "run()"
   5.221 +     * method calls "createI21Array()" which actually return an object (or an array of objects) of
   5.222 +     * type "I21C" which implements both "I2" and "I1".
   5.223 +     *
   5.224 +     * Notice that at the bytecode level, the code for the "run()" and "test()" methods in
   5.225 +     * "MeetIncompatibleInterfaceArraysASM" and "MeetIncompatibleInterfaceArraysGoodASM" look exactly
   5.226 +     * the same. I.e. the verifier has no chance to verify if the I2 object returned by "createI1Array()"
   5.227 +     * or "createI21Array()" implements "I1" or not. That's actually the reason why both versions of
   5.228 +     * generated classes are legal from a verifier point of view.
   5.229 +     *
   5.230 +     */
   5.231 +    static void generateTestClass(int dim, boolean good) throws Exception {
   5.232 +        String baseClassName = "MeetIncompatibleInterfaceArrays";
   5.233 +        if (good)
   5.234 +            baseClassName += "Good";
   5.235 +        String createName = "createI2" + (good ? "1" : "") + "Array";
   5.236 +        String a = "";
   5.237 +        for (int i = 0; i < dim; i++)
   5.238 +            a += "[";
   5.239 +        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
   5.240 +        cw.visit(V1_8, ACC_PUBLIC, baseClassName + dim + "ASM", null, "java/lang/Object", null);
   5.241 +        MethodVisitor constr = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
   5.242 +        constr.visitCode();
   5.243 +        constr.visitVarInsn(ALOAD, 0);
   5.244 +        constr.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
   5.245 +        constr.visitInsn(RETURN);
   5.246 +        constr.visitMaxs(0, 0);
   5.247 +        constr.visitEnd();
   5.248 +        MethodVisitor run = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "run",
   5.249 +                "()" + a + "LTestMeetIncompatibleInterfaceArrays$I1;", null, null);
   5.250 +        run.visitCode();
   5.251 +        if (dim == 4) {
   5.252 +            run.visitMethodInsn(INVOKESTATIC, "TestMeetIncompatibleInterfaceArrays$Helper", createName + 3,
   5.253 +                    "()" + "[[[" + "LTestMeetIncompatibleInterfaceArrays$I2;", false);
   5.254 +        } else {
   5.255 +            run.visitMethodInsn(INVOKESTATIC, "TestMeetIncompatibleInterfaceArrays$Helper", createName + dim,
   5.256 +                    "()" + a + "LTestMeetIncompatibleInterfaceArrays$I2;", false);
   5.257 +        }
   5.258 +        run.visitInsn(ARETURN);
   5.259 +        run.visitMaxs(0, 0);
   5.260 +        run.visitEnd();
   5.261 +        MethodVisitor test = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "test", "()V", null, null);
   5.262 +        test.visitCode();
   5.263 +        test.visitMethodInsn(INVOKESTATIC, baseClassName + dim + "ASM", "run",
   5.264 +                "()" + a + "LTestMeetIncompatibleInterfaceArrays$I1;", false);
   5.265 +        test.visitVarInsn(ASTORE, 0);
   5.266 +        if (dim > 0) {
   5.267 +            test.visitVarInsn(ALOAD, 0);
   5.268 +            for (int i = 1; i <= dim; i++) {
   5.269 +                test.visitInsn(ICONST_0);
   5.270 +                test.visitInsn(AALOAD);
   5.271 +            }
   5.272 +            test.visitVarInsn(ASTORE, 1);
   5.273 +        }
   5.274 +        test.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
   5.275 +        test.visitVarInsn(ALOAD, dim > 0 ? 1 : 0);
   5.276 +        test.visitMethodInsn(INVOKEINTERFACE, "TestMeetIncompatibleInterfaceArrays$I1", "getName",
   5.277 +                "()Ljava/lang/String;", true);
   5.278 +        test.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false);
   5.279 +        test.visitInsn(RETURN);
   5.280 +        test.visitMaxs(0, 0);
   5.281 +        test.visitEnd();
   5.282 +
   5.283 +        // Get the bytes of the class..
   5.284 +        byte[] b = cw.toByteArray();
   5.285 +        // ..and write them into a class file (for debugging)
   5.286 +        FileOutputStream fos = new FileOutputStream(PATH + baseClassName + dim + "ASM.class");
   5.287 +        fos.write(b);
   5.288 +        fos.close();
   5.289 +
   5.290 +    }
   5.291 +
   5.292 +    public static String[][] tier = { { "interpreted", "C2 (tier 4) without inlining", "C2 (tier4) without inlining" },
   5.293 +            { "interpreted", "C2 (tier 4) with inlining", "C2 (tier4) with inlining" },
   5.294 +            { "interpreted", "C1 (tier 3) with inlining", "C2 (tier4) with inlining" } };
   5.295 +
   5.296 +    public static void main(String[] args) throws Exception {
   5.297 +        final int pass = Integer.parseInt(args.length > 0 ? args[0] : "0");
   5.298 +
   5.299 +        // Load and initialize some classes required for compilation
   5.300 +        Class.forName("TestMeetIncompatibleInterfaceArrays$I1");
   5.301 +        Class.forName("TestMeetIncompatibleInterfaceArrays$I2");
   5.302 +        Class.forName("TestMeetIncompatibleInterfaceArrays$Helper");
   5.303 +
   5.304 +        for (int g = 0; g < 2; g++) {
   5.305 +            String baseClassName = "MeetIncompatibleInterfaceArrays";
   5.306 +            boolean good = (g == 0) ? false : true;
   5.307 +            if (good)
   5.308 +                baseClassName += "Good";
   5.309 +            for (int i = 0; i < 6; i++) {
   5.310 +                System.out.println();
   5.311 +                System.out.println("Creating " + baseClassName + i + "ASM.class");
   5.312 +                System.out.println("========================================" + "=" + "=========");
   5.313 +                // Create the "MeetIncompatibleInterfaceArrays<i>ASM" class
   5.314 +                generateTestClass(i, good);
   5.315 +                Class<?> c = null;
   5.316 +                try {
   5.317 +                    c = Class.forName(baseClassName + i + "ASM");
   5.318 +                } catch (VerifyError ve) {
   5.319 +                    if (i == 4) {
   5.320 +                        System.out.println("OK - must be (" + ve.getMessage() + ").");
   5.321 +                    } else {
   5.322 +                        throw ve;
   5.323 +                    }
   5.324 +                    continue;
   5.325 +                }
   5.326 +                // Call MeetIncompatibleInterfaceArrays<i>ASM.test()
   5.327 +                Method m = c.getMethod("test");
   5.328 +                Method r = c.getMethod("run");
   5.329 +                for (int j = 0; j < 3; j++) {
   5.330 +                    System.out.println((j + 1) + ". invokation of " + baseClassName + i + "ASM.test() [should be "
   5.331 +                            + tier[pass][j] + "]");
   5.332 +                    try {
   5.333 +                        m.invoke(null);
   5.334 +                    } catch (InvocationTargetException ite) {
   5.335 +                        if (good) {
   5.336 +                            throw ite;
   5.337 +                        } else {
   5.338 +                            if (ite.getCause() instanceof IncompatibleClassChangeError) {
   5.339 +                                System.out.println("  OK - catched InvocationTargetException("
   5.340 +                                        + ite.getCause().getMessage() + ").");
   5.341 +                            } else {
   5.342 +                                throw ite;
   5.343 +                            }
   5.344 +                        }
   5.345 +                    }
   5.346 +                }
   5.347 +                System.out.println("Method " + r + (WB.isMethodCompiled(r) ? " has" : " has not") + " been compiled.");
   5.348 +                if (!WB.isMethodCompiled(r)) {
   5.349 +                    throw new Exception("Method " + r + " must be compiled!");
   5.350 +                }
   5.351 +            }
   5.352 +        }
   5.353 +    }
   5.354 +}

mercurial