]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/cojen/classfile/TypeDesc.java
acf2bb18d643ae4f40c1d3a6d1896d9782787f63
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / cojen / classfile / TypeDesc.java
1 /*
2  *  Copyright 2004-2010 Brian S O'Neill
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  */
16
17 package org.cojen.classfile;
18
19 import java.lang.ref.SoftReference;
20 import java.lang.reflect.Array;
21 import java.util.Collections;
22 import java.util.Map;
23
24 import org.cojen.util.SoftValuedHashMap;
25 import org.cojen.util.WeakCanonicalSet;
26 import org.cojen.util.WeakIdentityMap;
27
28 /**
29  * This class is used to build field and return type descriptor strings as 
30  * defined in <i>The Java Virtual Machine Specification</i>, section 4.3.2.
31  * TypeDesc instances are canonicalized and therefore "==" comparable.
32  *
33  * @author Brian S O'Neill
34  */
35 @SuppressWarnings("rawtypes")
36 public abstract class TypeDesc extends Descriptor {
37     /**
38      * Type code returned from getTypeCode, which can be used with the
39      * newarray instruction.
40      */
41     public final static int
42         OBJECT_CODE = 0,
43         VOID_CODE = 1,
44         BOOLEAN_CODE = 4,
45         CHAR_CODE = 5,
46         FLOAT_CODE = 6,
47         DOUBLE_CODE = 7,
48         BYTE_CODE = 8,
49         SHORT_CODE = 9,
50         INT_CODE = 10,
51         LONG_CODE = 11;
52
53     /** Primitive type void */
54     public final static TypeDesc VOID;
55     /** Primitive type boolean */
56     public final static TypeDesc BOOLEAN;
57     /** Primitive type char */
58     public final static TypeDesc CHAR;
59     /** Primitive type byte */
60     public final static TypeDesc BYTE;
61     /** Primitive type short */
62     public final static TypeDesc SHORT;
63     /** Primitive type int */
64     public final static TypeDesc INT;
65     /** Primitive type long */
66     public final static TypeDesc LONG;
67     /** Primitive type float */
68     public final static TypeDesc FLOAT;
69     /** Primitive type double */
70     public final static TypeDesc DOUBLE;
71
72     /** Object type java.lang.Object, provided for convenience */
73     public final static TypeDesc OBJECT;
74     /** Object type java.lang.String, provided for convenience */
75     public final static TypeDesc STRING;
76
77     // Pool of all shared instances. Ensures identity comparison works.
78     final static WeakCanonicalSet<Descriptor> cInstances;
79
80     // Cache that maps Classes to TypeDescs.
81     private final static Map<Class, TypeDesc> cClassesToInstances;
82
83     // Cache that maps String names to TypeDescs.
84     private final static Map<String, TypeDesc> cNamesToInstances;
85
86     // Cache that maps String descriptors to TypeDescs.
87     private final static Map<String, TypeDesc> cDescriptorsToInstances;
88
89     static {
90         cInstances = new WeakCanonicalSet<Descriptor>();
91
92         cClassesToInstances = Collections.synchronizedMap(new WeakIdentityMap<Class, TypeDesc>());
93         cNamesToInstances = Collections.synchronizedMap(new SoftValuedHashMap<String, TypeDesc>());
94         cDescriptorsToInstances = Collections.synchronizedMap
95             (new SoftValuedHashMap<String, TypeDesc>());
96
97         VOID = intern(new PrimitiveType("V", VOID_CODE));
98         BOOLEAN = intern(new PrimitiveType("Z", BOOLEAN_CODE));
99         CHAR = intern(new PrimitiveType("C", CHAR_CODE));
100         BYTE = intern(new PrimitiveType("B", BYTE_CODE));
101         SHORT = intern(new PrimitiveType("S", SHORT_CODE));
102         INT = intern(new PrimitiveType("I", INT_CODE));
103         LONG = intern(new PrimitiveType("J", LONG_CODE));
104         FLOAT = intern(new PrimitiveType("F", FLOAT_CODE));
105         DOUBLE = intern(new PrimitiveType("D", DOUBLE_CODE));
106
107         OBJECT = forClass("java.lang.Object");
108         STRING = forClass("java.lang.String");
109     }
110
111     static TypeDesc intern(TypeDesc type) {
112         return cInstances.put(type);
113     }
114
115     /**
116      * Acquire a TypeDesc from any class, including primitives and arrays.
117      */
118     public static TypeDesc forClass(final Class clazz) {
119         if (clazz == null) {
120             return null;
121         }
122
123         TypeDesc type = (TypeDesc)cClassesToInstances.get(clazz);
124
125         if (type == null || type.toClass() != clazz) {
126             if (clazz.isArray()) {
127                 type = forClass(clazz.getComponentType()).toArrayType();
128             } else if (clazz.isPrimitive()) {
129                 if (clazz == int.class) {
130                     type = INT;
131                 } else if (clazz == boolean.class) {
132                     type = BOOLEAN;
133                 } else if (clazz == char.class) {
134                     type = CHAR;
135                 } else if (clazz == byte.class) {
136                     type = BYTE;
137                 } else if (clazz == long.class) {
138                     type = LONG;
139                 } else if (clazz == float.class) {
140                     type = FLOAT;
141                 } else if (clazz == double.class) {
142                     type = DOUBLE;
143                 } else if (clazz == short.class) {
144                     type = SHORT;
145                 } else if (clazz == void.class) {
146                     type = VOID;
147                 }
148             } else {
149                 String name = clazz.getName();
150                 type = intern(new ObjectType(generateDescriptor(name), name));
151             }
152         
153             if (type.toClass() != clazz) {
154                 type = new ObjectType(type.getDescriptor(), clazz.getName());
155                 ((ObjectType) type).setClass(clazz);
156             }
157
158             synchronized (cClassesToInstances) {
159                 if (cClassesToInstances.containsKey(clazz)) {
160                     type = (TypeDesc)cClassesToInstances.get(clazz);
161                 } else {
162                     cClassesToInstances.put(clazz, type);
163                 }
164             }
165         }
166
167         return type;
168     }
169
170     /**
171      * Acquire a TypeDesc from any class name, including primitives and arrays.
172      * Primitive and array syntax matches Java declarations.
173      */
174     public static TypeDesc forClass(final String name) throws IllegalArgumentException {
175         if (name.length() < 1) {
176             throw invalidName(name);
177         }
178
179         // TODO: Support generics in name.
180
181         TypeDesc type = (TypeDesc)cNamesToInstances.get(name);
182         if (type != null) {
183             return type;
184         }
185
186         int index1 = name.lastIndexOf('[');
187         int index2 = name.lastIndexOf(']');
188         if (index2 >= 0) {
189             if (index2 + 1 != name.length() || index1 + 1 != index2) {
190                 throw invalidName(name);
191             }
192             try {
193                 type = forClass(name.substring(0, index1)).toArrayType();
194             } catch (IllegalArgumentException e) {
195                 throw invalidName(name);
196             }
197         } else if (index1 >= 0) {
198             throw invalidName(name);
199         } else {
200             setType: {
201                 switch (name.charAt(0)) {
202                 case 'v':
203                     if (name.equals("void")) {
204                         type = VOID;
205                         break setType;
206                     }
207                     break;
208                 case 'b':
209                     if (name.equals("boolean")) {
210                         type = BOOLEAN;
211                         break setType;
212                     } else if (name.equals("byte")) {
213                         type =  BYTE;
214                         break setType;
215                     }
216                     break;
217                 case 'c':
218                     if (name.equals("char")) {
219                         type = CHAR;
220                         break setType;
221                     }
222                     break;
223                 case 's':
224                     if (name.equals("short")) {
225                         type = SHORT;
226                         break setType;
227                     }
228                     break;
229                 case 'i':
230                     if (name.equals("int")) {
231                         type = INT;
232                         break setType;
233                     }
234                     break;
235                 case 'l':
236                     if (name.equals("long")) {
237                         type = LONG;
238                         break setType;
239                     }
240                     break;
241                 case 'f':
242                     if (name.equals("float")) {
243                         type = FLOAT;
244                         break setType;
245                     }
246                     break;
247                 case 'd':
248                     if (name.equals("double")) {
249                         type = DOUBLE;
250                         break setType;
251                     }
252                     break;
253                 }
254
255                 String desc = generateDescriptor(name);
256                 if (name.indexOf('/') < 0) {
257                     type = new ObjectType(desc, name);
258                 } else {
259                     type = new ObjectType(desc, name.replace('/', '.'));
260                 }
261                 type = intern(type);
262             }
263         }
264
265         cNamesToInstances.put(name, type);
266         return type;
267     }
268
269     private static IllegalArgumentException invalidName(String name) {
270         return new IllegalArgumentException("Invalid name: " + name);
271     }
272
273     /**
274      * Acquire a TypeDesc from a type descriptor. This syntax is described in
275      * section 4.3.2, Field Descriptors.
276      */
277     public static TypeDesc forDescriptor(final String desc) throws IllegalArgumentException {
278         TypeDesc type = (TypeDesc)cDescriptorsToInstances.get(desc);
279         if (type != null) {
280             return type;
281         }
282
283         // TODO: Support generics in descriptor.
284
285         String rootDesc = desc;
286         int cursor = 0;
287         int dim = 0;
288         try {
289             char c;
290             while ((c = rootDesc.charAt(cursor++)) == '[') {
291                 dim++;
292             }
293
294             switch (c) {
295             case 'V':
296                 type = VOID;
297                 break;
298             case 'Z':
299                 type = BOOLEAN;
300                 break;
301             case 'C':
302                 type = CHAR;
303                 break;
304             case 'B':
305                 type = BYTE;
306                 break;
307             case 'S':
308                 type = SHORT;
309                 break;
310             case 'I':
311                 type = INT;
312                 break;
313             case 'J':
314                 type = LONG;
315                 break;
316             case 'F':
317                 type = FLOAT;
318                 break;
319             case 'D':
320                 type = DOUBLE;
321                 break;
322             case 'L':
323                 if (dim > 0) {
324                     rootDesc = rootDesc.substring(dim);
325                     cursor = 1;
326                 }
327                 StringBuffer name = new StringBuffer(rootDesc.length() - 2);
328                 while ((c = rootDesc.charAt(cursor++)) != ';') {
329                     if (c == '/') {
330                         c = '.';
331                     }
332                     name.append(c);
333                 }
334                 type = intern(new ObjectType(rootDesc, name.toString()));
335                 break;
336             default:
337                 throw invalidDescriptor(desc);
338             }
339         } catch (NullPointerException e) {
340             throw invalidDescriptor(desc);
341         } catch (IndexOutOfBoundsException e) {
342             throw invalidDescriptor(desc);
343         }
344
345         if (cursor != rootDesc.length()) {
346             throw invalidDescriptor(desc);
347         }
348
349         while (--dim >= 0) {
350             type = type.toArrayType();
351         }
352
353         cDescriptorsToInstances.put(desc, type);
354         return type;
355     }
356
357     private static IllegalArgumentException invalidDescriptor(String desc) {
358         return new IllegalArgumentException("Invalid descriptor: " + desc);
359     }
360
361     private static String generateDescriptor(String classname) {
362         int length = classname.length();
363         char[] buf = new char[length + 2];
364         buf[0] = 'L';
365         classname.getChars(0, length, buf, 1);
366         int i;
367         for (i=1; i<=length; i++) {
368             char c = buf[i];
369             if (c == '.') {
370                 buf[i] = '/';
371             }
372         }
373         buf[i] = ';';
374         return new String(buf);
375     }
376
377     transient final String mDescriptor;
378
379     TypeDesc(String desc) {
380         mDescriptor = desc;
381     }
382
383     /**
384      * Returns a type descriptor string, excluding generics.
385      */
386     public final String getDescriptor() {
387         return mDescriptor;
388     }
389
390     /**
391      * Returns a type descriptor string, including any generics.
392      */
393     //public abstract String getGenericDescriptor();
394
395     /**
396      * Returns the class name for this descriptor. If the type is primitive,
397      * then the Java primitive type name is returned. If the type is an array,
398      * only the root component type name is returned.
399      */
400     public abstract String getRootName();
401
402     /**
403      * Returns the class name for this descriptor. If the type is primitive,
404      * then the Java primitive type name is returned. If the type is an array,
405      * "[]" is append at the end of the name for each dimension.
406      */
407     public abstract String getFullName();
408
409     // TODO
410     //public abstract String getGenericRootName();
411
412     // TODO
413     //public abstract String getGenericFullName();
414
415     /**
416      * Returns a type code for operating on primitive types in switches. If
417      * not primitive, OBJECT_CODE is returned.
418      */
419     public abstract int getTypeCode();
420
421     /**
422      * Returns true if this is a primitive type.
423      */
424     public abstract boolean isPrimitive();
425
426     /**
427      * Returns true if this is a primitive long or double type.
428      */
429     public abstract boolean isDoubleWord();
430
431     /**
432      * Returns true if this is an array type.
433      */
434     public abstract boolean isArray();
435
436     /**
437      * Returns the number of dimensions this array type has. If not an array,
438      * zero is returned.
439      */
440     public abstract int getDimensions();
441
442     /**
443      * Returns the component type of this array type. If not an array, null is
444      * returned.
445      */
446     public abstract TypeDesc getComponentType();
447
448     /**
449      * Returns the root component type of this array type. If not an array,
450      * null is returned.
451      */
452     public abstract TypeDesc getRootComponentType();
453
454     /**
455      * Convertes this type to an array type. If already an array, another
456      * dimension is added.
457      */
458     public abstract TypeDesc toArrayType();
459
460     /**
461      * Returns the object peer of this primitive type. For int, the object peer
462      * is java.lang.Integer. If this type is an object type, it is simply 
463      * returned.
464      */
465     public abstract TypeDesc toObjectType();
466
467     /**
468      * Returns the primitive peer of this object type, if one exists. For
469      * java.lang.Integer, the primitive peer is int. If this type is a
470      * primitive type, it is simply returned. Arrays have no primitive peer,
471      * and so null is returned instead.
472      */
473     public abstract TypeDesc toPrimitiveType();
474
475     /**
476      * Returns this type as a class. If the class isn't found, null is
477      * returned.
478      */
479     public abstract Class toClass();
480
481     /**
482      * Returns this type as a class. If the class isn't found, null is
483      * returned.
484      * @param loader optional ClassLoader to load class from
485      */
486     public abstract Class toClass(ClassLoader loader);
487
488     public String toString() {
489         // TODO: Return generic descriptor
490         return mDescriptor;
491     }
492
493     public int hashCode() {
494         return mDescriptor.hashCode();
495     }
496
497     public boolean equals(Object other) {
498         if (this == other) {
499             return true;
500         }
501         if (other instanceof TypeDesc) {
502             return ((TypeDesc)other).mDescriptor.equals(mDescriptor);
503         }
504         return false;
505     }
506
507     private static class PrimitiveType extends TypeDesc {
508         private transient final int mCode;
509         private transient TypeDesc mArrayType;
510         private transient TypeDesc mObjectType;
511         
512         PrimitiveType(String desc, int code) {
513             super(desc);
514             mCode = code;
515         }
516
517         public String getRootName() {
518             switch (mCode) {
519             default:
520             case VOID_CODE:
521                 return "void";
522             case BOOLEAN_CODE:
523                 return "boolean";
524             case CHAR_CODE:
525                 return "char";
526             case BYTE_CODE:
527                 return "byte";
528             case SHORT_CODE:
529                 return "short";
530             case INT_CODE:
531                 return "int";
532             case LONG_CODE:
533                 return "long";
534             case FLOAT_CODE:
535                 return "float";
536             case DOUBLE_CODE:
537                 return "double";
538             }
539         }
540         
541         public String getFullName() {
542             return getRootName();
543         }
544
545         public int getTypeCode() {
546             return mCode;
547         }
548         
549         public boolean isPrimitive() {
550             return true;
551         }
552         
553         public boolean isDoubleWord() {
554             return mCode == DOUBLE_CODE || mCode == LONG_CODE;
555         }
556         
557         public boolean isArray() {
558             return false;
559         }
560         
561         public int getDimensions() {
562             return 0;
563         }
564         
565         public TypeDesc getComponentType() {
566             return null;
567         }
568         
569         public TypeDesc getRootComponentType() {
570             return null;
571         }
572         
573         public TypeDesc toArrayType() {
574             if (mArrayType == null) {
575                 char[] buf = new char[2];
576                 buf[0] = '[';
577                 buf[1] = mDescriptor.charAt(0);
578                 mArrayType = intern(new ArrayType(new String(buf), this));
579             }
580             return mArrayType;
581         }
582         
583         public TypeDesc toObjectType() {
584             if (mObjectType == null) {
585                 switch (mCode) {
586                 default:
587                 case VOID_CODE:
588                     mObjectType = forClass("java.lang.Void");
589                     break;
590                 case BOOLEAN_CODE:
591                     mObjectType = forClass("java.lang.Boolean");
592                     break;
593                 case CHAR_CODE:
594                     mObjectType = forClass("java.lang.Character");
595                     break;
596                 case BYTE_CODE:
597                     mObjectType = forClass("java.lang.Byte");
598                     break;
599                 case SHORT_CODE:
600                     mObjectType = forClass("java.lang.Short");
601                     break;
602                 case INT_CODE:
603                     mObjectType = forClass("java.lang.Integer");
604                     break;
605                 case LONG_CODE:
606                     mObjectType = forClass("java.lang.Long");
607                     break;
608                 case FLOAT_CODE:
609                     mObjectType = forClass("java.lang.Float");
610                     break;
611                 case DOUBLE_CODE:
612                     mObjectType = forClass("java.lang.Double");
613                     break;
614                 }
615             }
616             return mObjectType;
617         }
618         
619         public TypeDesc toPrimitiveType() {
620             return this;
621         }
622
623         public Class toClass() {
624             switch (mCode) {
625             default:
626             case VOID_CODE:
627                 return void.class;
628             case BOOLEAN_CODE:
629                 return boolean.class;
630             case CHAR_CODE:
631                 return char.class;
632             case BYTE_CODE:
633                 return byte.class;
634             case SHORT_CODE:
635                 return short.class;
636             case INT_CODE:
637                 return int.class;
638             case LONG_CODE:
639                 return long.class;
640             case FLOAT_CODE:
641                 return float.class;
642             case DOUBLE_CODE:
643                 return double.class;
644             }
645         }
646
647         public Class toClass(ClassLoader loader) {
648             return toClass();
649         }
650     }
651
652     private static class ObjectType extends TypeDesc {
653         private transient final String mName;
654         private transient TypeDesc mArrayType;
655         private transient TypeDesc mPrimitiveType;
656
657         // Since cClassesToInstances may reference this instance, softly
658         // reference back to class to allow it to be garbage collected.
659         private transient SoftReference<Class> mClassRef;
660
661         ObjectType(String desc, String name) {
662             super(desc);
663             mName = name;
664         }
665
666         public String getRootName() {
667             return mName;
668         }
669
670         public String getFullName() {
671             return mName;
672         }
673
674         public int getTypeCode() {
675             return OBJECT_CODE;
676         }
677
678         public boolean isPrimitive() {
679             return false;
680         }
681         
682         public boolean isDoubleWord() {
683             return false;
684         }
685         
686         public boolean isArray() {
687             return false;
688         }
689         
690         public int getDimensions() {
691             return 0;
692         }
693         
694         public TypeDesc getComponentType() {
695             return null;
696         }
697         
698         public TypeDesc getRootComponentType() {
699             return null;
700         }
701         
702         public TypeDesc toArrayType() {
703             if (mArrayType == null) {
704                 int length = mDescriptor.length();
705                 char[] buf = new char[length + 1];
706                 buf[0] = '[';
707                 mDescriptor.getChars(0, length, buf, 1);
708                 mArrayType = intern(new ArrayType(new String(buf), this));
709             }
710             return mArrayType;
711         }
712         
713         public TypeDesc toObjectType() {
714             return this;
715         }
716         
717         public TypeDesc toPrimitiveType() {
718             if (mPrimitiveType == null) {
719                 String name = mName;
720                 if (name.startsWith("java.lang.") && name.length() > 10) {
721                     switch (name.charAt(10)) {
722                     case 'V':
723                         if (name.equals("java.lang.Void")) {
724                             mPrimitiveType = VOID;
725                         }
726                         break;
727                     case 'B':
728                         if (name.equals("java.lang.Boolean")) {
729                             mPrimitiveType = BOOLEAN;
730                         } else if (name.equals("java.lang.Byte")) {
731                             mPrimitiveType = BYTE;
732                         }
733                         break;
734                     case 'C':
735                         if (name.equals("java.lang.Character")) {
736                             mPrimitiveType = CHAR;
737                         }
738                         break;
739                     case 'S':
740                         if (name.equals("java.lang.Short")) {
741                             mPrimitiveType = SHORT;
742                         }
743                         break;
744                     case 'I':
745                         if (name.equals("java.lang.Integer")) {
746                             mPrimitiveType = INT;
747                         }
748                         break;
749                     case 'L':
750                         if (name.equals("java.lang.Long")) {
751                             mPrimitiveType = LONG;
752                         }
753                         break;
754                     case 'F':
755                         if (name.equals("java.lang.Float")) {
756                             mPrimitiveType = FLOAT;
757                         }
758                         break;
759                     case 'D':
760                         if (name.equals("java.lang.Double")) {
761                             mPrimitiveType = DOUBLE;
762                         }
763                         break;
764                     }
765                 }
766             }
767
768             return mPrimitiveType;
769         }
770
771         public final synchronized Class toClass() {
772             Class clazz;
773             if (mClassRef != null) {
774                 clazz = mClassRef.get();
775                 if (clazz != null) {
776                     return clazz;
777                 }
778             }
779             clazz = toClass(null);
780             mClassRef = new SoftReference<Class>(clazz);
781             return clazz;
782         }
783
784         public Class toClass(ClassLoader loader) {
785             TypeDesc type = toPrimitiveType();
786             if (type != null) {
787                 switch (type.getTypeCode()) {
788                 default:
789                 case VOID_CODE:
790                     return Void.class;
791                 case BOOLEAN_CODE:
792                     return Boolean.class;
793                 case CHAR_CODE:
794                     return Character.class;
795                 case FLOAT_CODE:
796                     return Float.class;
797                 case DOUBLE_CODE:
798                     return Double.class;
799                 case BYTE_CODE:
800                     return Byte.class;
801                 case SHORT_CODE:
802                     return Short.class;
803                 case INT_CODE:
804                     return Integer.class;
805                 case LONG_CODE:
806                     return Long.class;
807                 }
808             }
809
810             try {
811                 if (loader == null) {
812                     return Class.forName(mName);
813                 } else {
814                     return loader.loadClass(mName);
815                 }
816             } catch (ClassNotFoundException e) {
817                 return null;
818             }
819         }
820
821         void setClass(Class clazz) {
822             mClassRef = new SoftReference<Class>(clazz);
823         }
824     }
825
826     private static class ArrayType extends ObjectType {
827         private transient final TypeDesc mComponent;
828         private transient final String mFullName;
829
830         ArrayType(String desc, TypeDesc component) {
831             super(desc, component.getRootName());
832             mComponent = component;
833             mFullName = component.getFullName().concat("[]");
834         }
835
836         public String getFullName() {
837             return mFullName;
838         }
839
840         public boolean isArray() {
841             return true;
842         }
843         
844         public int getDimensions() {
845             return mComponent.getDimensions() + 1;
846         }
847         
848         public TypeDesc getComponentType() {
849             return mComponent;
850         }
851         
852         public TypeDesc getRootComponentType() {
853             TypeDesc type = mComponent;
854             while (type.isArray()) {
855                 type = type.getComponentType();
856             }
857             return type;
858         }
859         
860         public TypeDesc toPrimitiveType() {
861             return null;
862         }
863
864         public Class toClass(ClassLoader loader) {
865             if (loader == null) {
866                 return arrayClass(getRootComponentType().toClass());
867             } else {
868                 return arrayClass(getRootComponentType().toClass(loader));
869             }
870         }
871
872         private Class arrayClass(Class clazz) {
873             if (clazz == null) {
874                 return null;
875             }
876             int dim = getDimensions();
877             try {
878                 if (dim == 1) {
879                     return Array.newInstance(clazz, 0).getClass();
880                 } else {
881                     return Array.newInstance(clazz, new int[dim]).getClass();
882                 }
883             } catch (IllegalArgumentException e) {
884                 return null;
885             }
886         }
887     }
888 }