xref: /aosp_15_r20/art/test/1980-obsolete-object-cleared/src/Main.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2017 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker import art.*;
18*795d594fSAndroid Build Coastguard Worker import java.lang.ref.*;
19*795d594fSAndroid Build Coastguard Worker import java.lang.reflect.*;
20*795d594fSAndroid Build Coastguard Worker import java.util.*;
21*795d594fSAndroid Build Coastguard Worker import java.util.function.Consumer;
22*795d594fSAndroid Build Coastguard Worker import sun.misc.Unsafe;
23*795d594fSAndroid Build Coastguard Worker 
24*795d594fSAndroid Build Coastguard Worker public class Main {
25*795d594fSAndroid Build Coastguard Worker   public static class Transform {
26*795d594fSAndroid Build Coastguard Worker     static {
27*795d594fSAndroid Build Coastguard Worker     }
28*795d594fSAndroid Build Coastguard Worker 
29*795d594fSAndroid Build Coastguard Worker     public static Object SECRET_ARRAY = new byte[] {1, 2, 3, 4};
30*795d594fSAndroid Build Coastguard Worker     public static long SECRET_NUMBER = 42;
31*795d594fSAndroid Build Coastguard Worker 
foo()32*795d594fSAndroid Build Coastguard Worker     public static void foo() {}
33*795d594fSAndroid Build Coastguard Worker   }
34*795d594fSAndroid Build Coastguard Worker 
35*795d594fSAndroid Build Coastguard Worker   /* Base64 for
36*795d594fSAndroid Build Coastguard Worker    * public static class Trasform {
37*795d594fSAndroid Build Coastguard Worker    *   static {}
38*795d594fSAndroid Build Coastguard Worker    *   public static Object AAA_PADDING;
39*795d594fSAndroid Build Coastguard Worker    *   public static Object SECRET_ARRAY;
40*795d594fSAndroid Build Coastguard Worker    *   public static long SECRET_NUMBER;
41*795d594fSAndroid Build Coastguard Worker    *   public static void foo() {}
42*795d594fSAndroid Build Coastguard Worker    *   public static void bar() {}
43*795d594fSAndroid Build Coastguard Worker    * }
44*795d594fSAndroid Build Coastguard Worker    */
45*795d594fSAndroid Build Coastguard Worker   public static final byte[] REDEFINED_DEX_FILE =
46*795d594fSAndroid Build Coastguard Worker       Base64.getDecoder()
47*795d594fSAndroid Build Coastguard Worker           .decode(
48*795d594fSAndroid Build Coastguard Worker               "ZGV4CjAzNQDdmsOAlizFD4Ogb6+/mfSdVzhmL8e/mRcYBAAAcAAAAHhWNBIAAAAAAAAAAGADAAAU"
49*795d594fSAndroid Build Coastguard Worker                   + "AAAAcAAAAAcAAADAAAAAAQAAANwAAAADAAAA6AAAAAUAAAAAAQAAAQAAACgBAADQAgAASAEAAKwB"
50*795d594fSAndroid Build Coastguard Worker                   + "AAC2AQAAvgEAAMsBAADOAQAA4AEAAOgBAAAMAgAALAIAAEACAABLAgAAWQIAAGgCAABzAgAAdgIA"
51*795d594fSAndroid Build Coastguard Worker                   + "AIMCAACIAgAAjQIAAJMCAACaAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAADQAAAA0AAAAGAAAA"
52*795d594fSAndroid Build Coastguard Worker                   + "AAAAAAEABQACAAAAAQAFAAoAAAABAAAACwAAAAEAAAAAAAAAAQAAAAEAAAABAAAADwAAAAEAAAAQ"
53*795d594fSAndroid Build Coastguard Worker                   + "AAAABQAAAAEAAAABAAAAAQAAAAUAAAAAAAAACQAAAFADAAAhAwAAAAAAAAAAAAAAAAAAmgEAAAEA"
54*795d594fSAndroid Build Coastguard Worker                   + "AAAOAAAAAQABAAEAAACeAQAABAAAAHAQBAAAAA4AAAAAAAAAAACiAQAAAQAAAA4AAAAAAAAAAAAA"
55*795d594fSAndroid Build Coastguard Worker                   + "AKYBAAABAAAADgAHAA4ABgAOAAsADgAKAA4AAAAIPGNsaW5pdD4ABjxpbml0PgALQUFBX1BBRERJ"
56*795d594fSAndroid Build Coastguard Worker                   + "TkcAAUoAEExNYWluJFRyYW5zZm9ybTsABkxNYWluOwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xv"
57*795d594fSAndroid Build Coastguard Worker                   + "c2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABJMamF2YS9sYW5nL09i"
58*795d594fSAndroid Build Coastguard Worker                   + "amVjdDsACU1haW4uamF2YQAMU0VDUkVUX0FSUkFZAA1TRUNSRVRfTlVNQkVSAAlUcmFuc2Zvcm0A"
59*795d594fSAndroid Build Coastguard Worker                   + "AVYAC2FjY2Vzc0ZsYWdzAANiYXIAA2ZvbwAEbmFtZQAFdmFsdWUAdn5+RDh7ImNvbXBpbGF0aW9u"
60*795d594fSAndroid Build Coastguard Worker                   + "LW1vZGUiOiJkZWJ1ZyIsIm1pbi1hcGkiOjEsInNoYS0xIjoiYTgzNTJmMjU0ODg1MzYyY2NkOGQ5"
61*795d594fSAndroid Build Coastguard Worker                   + "MDlkMzUyOWM2MDA5NGRkODk2ZSIsInZlcnNpb24iOiIxLjYuMjAtZGV2In0AAgMBEhgCAgQCDgQJ"
62*795d594fSAndroid Build Coastguard Worker                   + "ERcMAwAEAAAJAQkBCQCIgATIAgGBgATcAgEJ9AIBCYgDAAAAAAACAAAAEgMAABgDAABEAwAAAAAA"
63*795d594fSAndroid Build Coastguard Worker                   + "AAAAAAAAAAAADwAAAAAAAAABAAAAAAAAAAEAAAAUAAAAcAAAAAIAAAAHAAAAwAAAAAMAAAABAAAA"
64*795d594fSAndroid Build Coastguard Worker                   + "3AAAAAQAAAADAAAA6AAAAAUAAAAFAAAAAAEAAAYAAAABAAAAKAEAAAEgAAAEAAAASAEAAAMgAAAE"
65*795d594fSAndroid Build Coastguard Worker                   + "AAAAmgEAAAIgAAAUAAAArAEAAAQgAAACAAAAEgMAAAAgAAABAAAAIQMAAAMQAAACAAAAQAMAAAYg"
66*795d594fSAndroid Build Coastguard Worker                   + "AAABAAAAUAMAAAAQAAABAAAAYAMAAA==");
67*795d594fSAndroid Build Coastguard Worker 
68*795d594fSAndroid Build Coastguard Worker   private interface TConsumer<T> {
accept(T t)69*795d594fSAndroid Build Coastguard Worker     public void accept(T t) throws Exception;
70*795d594fSAndroid Build Coastguard Worker   }
71*795d594fSAndroid Build Coastguard Worker 
72*795d594fSAndroid Build Coastguard Worker   private interface ResetIterator<T> extends Iterator<T> {
reset()73*795d594fSAndroid Build Coastguard Worker     public void reset();
74*795d594fSAndroid Build Coastguard Worker   }
75*795d594fSAndroid Build Coastguard Worker 
76*795d594fSAndroid Build Coastguard Worker   private static final class BaseResetIter implements ResetIterator<Object[]> {
77*795d594fSAndroid Build Coastguard Worker     private boolean have_next = true;
78*795d594fSAndroid Build Coastguard Worker 
next()79*795d594fSAndroid Build Coastguard Worker     public Object[] next() {
80*795d594fSAndroid Build Coastguard Worker       if (have_next) {
81*795d594fSAndroid Build Coastguard Worker         have_next = false;
82*795d594fSAndroid Build Coastguard Worker         return new Object[0];
83*795d594fSAndroid Build Coastguard Worker       } else {
84*795d594fSAndroid Build Coastguard Worker         throw new NoSuchElementException("only one element");
85*795d594fSAndroid Build Coastguard Worker       }
86*795d594fSAndroid Build Coastguard Worker     }
87*795d594fSAndroid Build Coastguard Worker 
hasNext()88*795d594fSAndroid Build Coastguard Worker     public boolean hasNext() {
89*795d594fSAndroid Build Coastguard Worker       return have_next;
90*795d594fSAndroid Build Coastguard Worker     }
91*795d594fSAndroid Build Coastguard Worker 
reset()92*795d594fSAndroid Build Coastguard Worker     public void reset() {
93*795d594fSAndroid Build Coastguard Worker       have_next = true;
94*795d594fSAndroid Build Coastguard Worker     }
95*795d594fSAndroid Build Coastguard Worker   }
96*795d594fSAndroid Build Coastguard Worker 
main(String[] args)97*795d594fSAndroid Build Coastguard Worker   public static void main(String[] args) throws Exception {
98*795d594fSAndroid Build Coastguard Worker     System.loadLibrary(args[0]);
99*795d594fSAndroid Build Coastguard Worker     Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
100*795d594fSAndroid Build Coastguard Worker 
101*795d594fSAndroid Build Coastguard Worker     // Get the Unsafe object.
102*795d594fSAndroid Build Coastguard Worker     Field f = Unsafe.class.getDeclaredField("THE_ONE");
103*795d594fSAndroid Build Coastguard Worker     f.setAccessible(true);
104*795d594fSAndroid Build Coastguard Worker     Unsafe u = (Unsafe) f.get(null);
105*795d594fSAndroid Build Coastguard Worker 
106*795d594fSAndroid Build Coastguard Worker     // Get the offsets into the original Transform class of the fields
107*795d594fSAndroid Build Coastguard Worker     long off_secret_array = genericFieldOffset(Transform.class.getDeclaredField("SECRET_ARRAY"));
108*795d594fSAndroid Build Coastguard Worker     long off_secret_number = genericFieldOffset(Transform.class.getDeclaredField("SECRET_NUMBER"));
109*795d594fSAndroid Build Coastguard Worker 
110*795d594fSAndroid Build Coastguard Worker     System.out.println("Reading normally.");
111*795d594fSAndroid Build Coastguard Worker     System.out.println("\tOriginal secret number is: " + Transform.SECRET_NUMBER);
112*795d594fSAndroid Build Coastguard Worker     System.out.println("\tOriginal secret array is: " + Arrays.toString((byte[])Transform.SECRET_ARRAY));
113*795d594fSAndroid Build Coastguard Worker     System.out.println("Using unsafe to access values directly from memory.");
114*795d594fSAndroid Build Coastguard Worker     System.out.println(
115*795d594fSAndroid Build Coastguard Worker         "\tOriginal secret number is: " + u.getLong(Transform.class, off_secret_number));
116*795d594fSAndroid Build Coastguard Worker     System.out.println(
117*795d594fSAndroid Build Coastguard Worker         "\tOriginal secret array is: "
118*795d594fSAndroid Build Coastguard Worker             + Arrays.toString((byte[]) u.getObject(Transform.class, off_secret_array)));
119*795d594fSAndroid Build Coastguard Worker 
120*795d594fSAndroid Build Coastguard Worker     // Redefine in a way that changes the offsets.
121*795d594fSAndroid Build Coastguard Worker     Redefinition.doCommonStructuralClassRedefinition(Transform.class, REDEFINED_DEX_FILE);
122*795d594fSAndroid Build Coastguard Worker 
123*795d594fSAndroid Build Coastguard Worker     // Make sure the value is the same.
124*795d594fSAndroid Build Coastguard Worker     System.out.println("Reading normally post redefinition.");
125*795d594fSAndroid Build Coastguard Worker     System.out.println("\tPost-redefinition secret number is: " + Transform.SECRET_NUMBER);
126*795d594fSAndroid Build Coastguard Worker     System.out.println("\tPost-redefinition secret array is: " + Arrays.toString((byte[])Transform.SECRET_ARRAY));
127*795d594fSAndroid Build Coastguard Worker 
128*795d594fSAndroid Build Coastguard Worker     // Get the (old) obsolete class from the ClassExt
129*795d594fSAndroid Build Coastguard Worker     Field ext_field = Class.class.getDeclaredField("extData");
130*795d594fSAndroid Build Coastguard Worker     ext_field.setAccessible(true);
131*795d594fSAndroid Build Coastguard Worker     Object ext_data = ext_field.get(Transform.class);
132*795d594fSAndroid Build Coastguard Worker     Field oc_field = ext_data.getClass().getDeclaredField("obsoleteClass");
133*795d594fSAndroid Build Coastguard Worker     oc_field.setAccessible(true);
134*795d594fSAndroid Build Coastguard Worker     Class<?> obsolete_class = (Class<?>) oc_field.get(ext_data);
135*795d594fSAndroid Build Coastguard Worker 
136*795d594fSAndroid Build Coastguard Worker     // Try reading the fields directly out of memory using unsafe.
137*795d594fSAndroid Build Coastguard Worker     System.out.println("Obsolete class is: " + obsolete_class);
138*795d594fSAndroid Build Coastguard Worker     System.out.println("Using unsafe to access obsolete values directly from memory.");
139*795d594fSAndroid Build Coastguard Worker     System.out.println(
140*795d594fSAndroid Build Coastguard Worker         "\tObsolete secret number is: " + u.getLong(obsolete_class, off_secret_number));
141*795d594fSAndroid Build Coastguard Worker     System.out.println(
142*795d594fSAndroid Build Coastguard Worker         "\tObsolete secret array is: "
143*795d594fSAndroid Build Coastguard Worker             + Arrays.toString((byte[]) u.getObject(obsolete_class, off_secret_array)));
144*795d594fSAndroid Build Coastguard Worker 
145*795d594fSAndroid Build Coastguard Worker     // Try calling all the public, non-static methods on the obsolete class. Make sure we cannot get
146*795d594fSAndroid Build Coastguard Worker     // j.l.r.{Method,Field} objects or instances.
147*795d594fSAndroid Build Coastguard Worker     TConsumer<Class> cc =
148*795d594fSAndroid Build Coastguard Worker         (Class c) -> {
149*795d594fSAndroid Build Coastguard Worker           for (Method m : Class.class.getDeclaredMethods()) {
150*795d594fSAndroid Build Coastguard Worker             if (Modifier.isPublic(m.getModifiers()) && !Modifier.isStatic(m.getModifiers())) {
151*795d594fSAndroid Build Coastguard Worker               Iterable<Object[]> iter = CollectParameterValues(m, obsolete_class);
152*795d594fSAndroid Build Coastguard Worker               System.out.println("Calling " + m + " with params: " + iter);
153*795d594fSAndroid Build Coastguard Worker               for (Object[] arr : iter) {
154*795d594fSAndroid Build Coastguard Worker                 try {
155*795d594fSAndroid Build Coastguard Worker                   System.out.println(
156*795d594fSAndroid Build Coastguard Worker                       m
157*795d594fSAndroid Build Coastguard Worker                           + " on "
158*795d594fSAndroid Build Coastguard Worker                           + safePrint(c)
159*795d594fSAndroid Build Coastguard Worker                           + " with "
160*795d594fSAndroid Build Coastguard Worker                           + deepPrint(arr)
161*795d594fSAndroid Build Coastguard Worker                           + " = "
162*795d594fSAndroid Build Coastguard Worker                           + safePrint(m.invoke(c, arr)));
163*795d594fSAndroid Build Coastguard Worker                 } catch (Throwable e) {
164*795d594fSAndroid Build Coastguard Worker                   System.out.println(
165*795d594fSAndroid Build Coastguard Worker                       m + " with " + deepPrint(arr) + " throws " + safePrint(e) + ": " + safePrint(e.getCause()));
166*795d594fSAndroid Build Coastguard Worker                 }
167*795d594fSAndroid Build Coastguard Worker               }
168*795d594fSAndroid Build Coastguard Worker             }
169*795d594fSAndroid Build Coastguard Worker           }
170*795d594fSAndroid Build Coastguard Worker         };
171*795d594fSAndroid Build Coastguard Worker     System.out.println("\n\nUsing obsolete class object!\n\n");
172*795d594fSAndroid Build Coastguard Worker     cc.accept(obsolete_class);
173*795d594fSAndroid Build Coastguard Worker     System.out.println("\n\nUsing non-obsolete class object!\n\n");
174*795d594fSAndroid Build Coastguard Worker     cc.accept(Transform.class);
175*795d594fSAndroid Build Coastguard Worker   }
176*795d594fSAndroid Build Coastguard Worker 
CollectParameterValues(Method m, Class<?> obsolete_class)177*795d594fSAndroid Build Coastguard Worker   public static Iterable<Object[]> CollectParameterValues(Method m, Class<?> obsolete_class) throws Exception {
178*795d594fSAndroid Build Coastguard Worker     Class<?>[] types = m.getParameterTypes();
179*795d594fSAndroid Build Coastguard Worker     final Object[][] params = new Object[types.length][];
180*795d594fSAndroid Build Coastguard Worker     for (int i = 0; i < types.length; i++) {
181*795d594fSAndroid Build Coastguard Worker       if (types[i].equals(Class.class)) {
182*795d594fSAndroid Build Coastguard Worker         params[i] =
183*795d594fSAndroid Build Coastguard Worker             new Object[] {
184*795d594fSAndroid Build Coastguard Worker               null, Object.class, obsolete_class, Transform.class, Long.TYPE, Class.class
185*795d594fSAndroid Build Coastguard Worker             };
186*795d594fSAndroid Build Coastguard Worker       } else if (types[i].equals(Boolean.TYPE)) {
187*795d594fSAndroid Build Coastguard Worker         params[i] = new Object[] {Boolean.TRUE, Boolean.FALSE};
188*795d594fSAndroid Build Coastguard Worker       } else if (types[i].equals(String.class)) {
189*795d594fSAndroid Build Coastguard Worker         params[i] = new Object[] {"NOT_USED_STRING", "foo", "SECRET_ARRAY"};
190*795d594fSAndroid Build Coastguard Worker       } else if (types[i].equals(Object.class)) {
191*795d594fSAndroid Build Coastguard Worker         params[i] = new Object[] {null, "foo", "NOT_USED_STRING", Transform.class};
192*795d594fSAndroid Build Coastguard Worker       } else if (types[i].isArray()) {
193*795d594fSAndroid Build Coastguard Worker         params[i] = new Object[] {new Object[0], new Class[0], null};
194*795d594fSAndroid Build Coastguard Worker       } else {
195*795d594fSAndroid Build Coastguard Worker         throw new Exception("Unknown type " + types[i] + " at " + i + " in " + m);
196*795d594fSAndroid Build Coastguard Worker       }
197*795d594fSAndroid Build Coastguard Worker     }
198*795d594fSAndroid Build Coastguard Worker     // Build the reset-iter.
199*795d594fSAndroid Build Coastguard Worker     ResetIterator<Object[]> iter = new BaseResetIter();
200*795d594fSAndroid Build Coastguard Worker     for (int i = params.length - 1; i >= 0; i--) {
201*795d594fSAndroid Build Coastguard Worker       iter = new ComboIter(Arrays.asList(params[i]), iter);
202*795d594fSAndroid Build Coastguard Worker     }
203*795d594fSAndroid Build Coastguard Worker     final Iterator<Object[]> fiter = iter;
204*795d594fSAndroid Build Coastguard Worker     // Wrap in an iterator with a useful toString method.
205*795d594fSAndroid Build Coastguard Worker     return new Iterable<Object[]>() {
206*795d594fSAndroid Build Coastguard Worker       public Iterator<Object[]> iterator() { return fiter; }
207*795d594fSAndroid Build Coastguard Worker       public String toString() { return deepPrint(params); }
208*795d594fSAndroid Build Coastguard Worker     };
209*795d594fSAndroid Build Coastguard Worker   }
210*795d594fSAndroid Build Coastguard Worker 
211*795d594fSAndroid Build Coastguard Worker   public static String deepPrint(Object[] o) {
212*795d594fSAndroid Build Coastguard Worker     return Arrays.toString(
213*795d594fSAndroid Build Coastguard Worker         Arrays.stream(o)
214*795d594fSAndroid Build Coastguard Worker             .map(
215*795d594fSAndroid Build Coastguard Worker                 (x) -> {
216*795d594fSAndroid Build Coastguard Worker                   if (x == null) {
217*795d594fSAndroid Build Coastguard Worker                     return "null";
218*795d594fSAndroid Build Coastguard Worker                   } else if (x.getClass().isArray()) {
219*795d594fSAndroid Build Coastguard Worker                     if (((Object[]) x).length == 0) {
220*795d594fSAndroid Build Coastguard Worker                       return "new " + x.getClass().getComponentType().getName() + "[0]";
221*795d594fSAndroid Build Coastguard Worker                     } else {
222*795d594fSAndroid Build Coastguard Worker                       return deepPrint((Object[]) x);
223*795d594fSAndroid Build Coastguard Worker                     }
224*795d594fSAndroid Build Coastguard Worker                   } else {
225*795d594fSAndroid Build Coastguard Worker                     return safePrint(x);
226*795d594fSAndroid Build Coastguard Worker                   }
227*795d594fSAndroid Build Coastguard Worker                 })
228*795d594fSAndroid Build Coastguard Worker             .toArray());
229*795d594fSAndroid Build Coastguard Worker   }
230*795d594fSAndroid Build Coastguard Worker 
231*795d594fSAndroid Build Coastguard Worker   public static String safePrint(Object o) {
232*795d594fSAndroid Build Coastguard Worker     if (o instanceof ClassLoader) {
233*795d594fSAndroid Build Coastguard Worker       return o.getClass().getName();
234*795d594fSAndroid Build Coastguard Worker     } else if (o == null) {
235*795d594fSAndroid Build Coastguard Worker       return "null";
236*795d594fSAndroid Build Coastguard Worker     } else if (o instanceof Exception) {
237*795d594fSAndroid Build Coastguard Worker       String res = o.toString();
238*795d594fSAndroid Build Coastguard Worker       if (res.endsWith("-transformed)")) {
239*795d594fSAndroid Build Coastguard Worker         res = res.substring(0, res.lastIndexOf(" ")) + " <transformed-jar>)";
240*795d594fSAndroid Build Coastguard Worker       } else if (res.endsWith(".jar)")) {
241*795d594fSAndroid Build Coastguard Worker         res = res.substring(0, res.lastIndexOf(" ")) + " <original-jar>)";
242*795d594fSAndroid Build Coastguard Worker       }
243*795d594fSAndroid Build Coastguard Worker       return res;
244*795d594fSAndroid Build Coastguard Worker     } else if (o instanceof Transform) {
245*795d594fSAndroid Build Coastguard Worker       return "Transform Instance";
246*795d594fSAndroid Build Coastguard Worker     } else if (o instanceof Class && isObsoleteObject((Class) o)) {
247*795d594fSAndroid Build Coastguard Worker       return "(obsolete)" + o.toString();
248*795d594fSAndroid Build Coastguard Worker     } else if (o.getClass().isArray()) {
249*795d594fSAndroid Build Coastguard Worker       return Arrays.toString((Object[])o);
250*795d594fSAndroid Build Coastguard Worker     } else {
251*795d594fSAndroid Build Coastguard Worker       return o.toString();
252*795d594fSAndroid Build Coastguard Worker     }
253*795d594fSAndroid Build Coastguard Worker   }
254*795d594fSAndroid Build Coastguard Worker 
255*795d594fSAndroid Build Coastguard Worker   private static class ComboIter implements ResetIterator<Object[]> {
256*795d594fSAndroid Build Coastguard Worker     private ResetIterator<Object[]> next;
257*795d594fSAndroid Build Coastguard Worker     private Object cur;
258*795d594fSAndroid Build Coastguard Worker     private boolean first;
259*795d594fSAndroid Build Coastguard Worker     private Iterator<Object> my_vals;
260*795d594fSAndroid Build Coastguard Worker     private Iterable<Object> my_vals_reset;
261*795d594fSAndroid Build Coastguard Worker 
262*795d594fSAndroid Build Coastguard Worker     public Object[] next() {
263*795d594fSAndroid Build Coastguard Worker       if (!next.hasNext()) {
264*795d594fSAndroid Build Coastguard Worker         cur = my_vals.next();
265*795d594fSAndroid Build Coastguard Worker         first = false;
266*795d594fSAndroid Build Coastguard Worker         if (next != null) {
267*795d594fSAndroid Build Coastguard Worker           next.reset();
268*795d594fSAndroid Build Coastguard Worker         }
269*795d594fSAndroid Build Coastguard Worker       }
270*795d594fSAndroid Build Coastguard Worker       if (first) {
271*795d594fSAndroid Build Coastguard Worker         first = false;
272*795d594fSAndroid Build Coastguard Worker         cur = my_vals.next();
273*795d594fSAndroid Build Coastguard Worker       }
274*795d594fSAndroid Build Coastguard Worker       Object[] nv = next.next();
275*795d594fSAndroid Build Coastguard Worker       Object[] res = new Object[nv.length + 1];
276*795d594fSAndroid Build Coastguard Worker       res[0] = cur;
277*795d594fSAndroid Build Coastguard Worker       for (int i = 0; i < nv.length; i++) {
278*795d594fSAndroid Build Coastguard Worker         res[i + 1] = nv[i];
279*795d594fSAndroid Build Coastguard Worker       }
280*795d594fSAndroid Build Coastguard Worker       return res;
281*795d594fSAndroid Build Coastguard Worker     }
282*795d594fSAndroid Build Coastguard Worker 
283*795d594fSAndroid Build Coastguard Worker     public boolean hasNext() {
284*795d594fSAndroid Build Coastguard Worker       return next.hasNext() || my_vals.hasNext();
285*795d594fSAndroid Build Coastguard Worker     }
286*795d594fSAndroid Build Coastguard Worker 
287*795d594fSAndroid Build Coastguard Worker     public void reset() {
288*795d594fSAndroid Build Coastguard Worker       my_vals = my_vals_reset.iterator();
289*795d594fSAndroid Build Coastguard Worker       next.reset();
290*795d594fSAndroid Build Coastguard Worker       cur = null;
291*795d594fSAndroid Build Coastguard Worker       first = true;
292*795d594fSAndroid Build Coastguard Worker     }
293*795d594fSAndroid Build Coastguard Worker 
294*795d594fSAndroid Build Coastguard Worker     public ComboIter(Iterable<Object> this_reset, ResetIterator<Object[]> next_reset) {
295*795d594fSAndroid Build Coastguard Worker       my_vals_reset = this_reset;
296*795d594fSAndroid Build Coastguard Worker       next = next_reset;
297*795d594fSAndroid Build Coastguard Worker       reset();
298*795d594fSAndroid Build Coastguard Worker     }
299*795d594fSAndroid Build Coastguard Worker   }
300*795d594fSAndroid Build Coastguard Worker 
301*795d594fSAndroid Build Coastguard Worker   public static native long genericFieldOffset(Field f);
302*795d594fSAndroid Build Coastguard Worker 
303*795d594fSAndroid Build Coastguard Worker   public static native boolean isObsoleteObject(Class c);
304*795d594fSAndroid Build Coastguard Worker }
305