1 | /* |
2 | |
3 | Derby - Class org.apache.derby.iapi.services.classfile.ClassHolder |
4 | |
5 | Copyright 2000, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.iapi.services.classfile; |
22 | |
23 | import org.apache.derby.iapi.services.sanity.SanityManager; |
24 | |
25 | |
26 | import java.io.InputStream; |
27 | import java.io.OutputStream; |
28 | import java.util.Enumeration; |
29 | |
30 | import java.io.IOException; |
31 | import java.util.Vector; |
32 | |
33 | import org.apache.derby.iapi.util.ByteArray; |
34 | import org.apache.derby.iapi.services.classfile.VMDescriptor; |
35 | import org.apache.derby.iapi.services.classfile.VMDescriptor; |
36 | |
37 | import java.util.Hashtable; |
38 | import java.util.Vector; |
39 | import java.util.Enumeration; |
40 | |
41 | |
42 | /** Based upon "THE class FILE FORMAT" chapter of "The Java Virtual Machine Specification" |
43 | corresponding to version 1.0.2 of the Java Virtual Machine and 1.0.2 of the |
44 | Java Language Specification. |
45 | |
46 | ISBN 0-201-63452-X, September 1996. |
47 | */ |
48 | |
49 | public class ClassHolder { |
50 | |
51 | |
52 | /* |
53 | ** Constants. |
54 | */ |
55 | |
56 | /* |
57 | ** Fields |
58 | */ |
59 | |
60 | protected int access_flags; |
61 | protected int this_class; |
62 | protected int super_class; |
63 | |
64 | // protected InterfacesArray interfaces; // can be null |
65 | protected int[] interfaces; //can be null |
66 | |
67 | protected MemberTable field_info; // can be null |
68 | protected MemberTable method_info; // can be null |
69 | protected Attributes attribute_info; // can be null |
70 | |
71 | /* |
72 | ** Fields for Constant Pool Table |
73 | */ |
74 | protected Hashtable cptHashTable; |
75 | protected Vector cptEntries; |
76 | private int cptEstimatedSize; |
77 | |
78 | /** |
79 | Used to search for index entries to avoid object allocation |
80 | in the case a referecne already exists. |
81 | */ |
82 | private final CONSTANT_Index_info searchIndex = new CONSTANT_Index_info(0, 0, 0); |
83 | |
84 | /* |
85 | ** Constructors. |
86 | */ |
87 | |
88 | protected ClassHolder(int estimatedConstantPoolCount) { |
89 | // Constant Pool Information |
90 | // 100 is the estimate of the number of entries that will be generated |
91 | cptEntries = new Vector(estimatedConstantPoolCount); |
92 | cptHashTable = new Hashtable(estimatedConstantPoolCount, (float)0.75); |
93 | |
94 | // reserve the 0'th constant pool entry |
95 | cptEntries.setSize(1); |
96 | } |
97 | |
98 | |
99 | /** |
100 | This will not define a constructor -- it is up |
101 | to the caller to add at least one. |
102 | */ |
103 | |
104 | public ClassHolder(String fullyQualifiedName, String superClassName, |
105 | int modifiers) { |
106 | |
107 | this(100); |
108 | |
109 | access_flags = modifiers | /* Modifier.SUPER */ 0x0020; |
110 | |
111 | this_class = addClassReference(fullyQualifiedName); |
112 | super_class = addClassReference(superClassName); |
113 | method_info = new MemberTable(0); |
114 | } |
115 | |
116 | private void put(ClassFormatOutput out) throws IOException { |
117 | |
118 | /* Write out the header */ |
119 | out.putU4(VMDescriptor.JAVA_CLASS_FORMAT_MAGIC); |
120 | out.putU2(VMDescriptor.JAVA_CLASS_FORMAT_MINOR_VERSION); |
121 | out.putU2(VMDescriptor.JAVA_CLASS_FORMAT_MAJOR_VERSION); |
122 | |
123 | // special case checking that the number of constant |
124 | // pool entries does not exceed the limit of 65535 |
125 | // (as it is stored as a U2). |
126 | // Special case to allow somewhat easier debugging |
127 | // of the resulting failure. |
128 | out.putU2("constant_pool", cptEntries.size()); |
129 | cptPut(out); |
130 | |
131 | out.putU2(access_flags); |
132 | out.putU2(this_class); |
133 | out.putU2(super_class); |
134 | |
135 | if (interfaces != null) { |
136 | int ilen = interfaces.length; |
137 | out.putU2(ilen); |
138 | for (int i = 0; i < ilen; i++) { |
139 | out.putU2(interfaces[i]); |
140 | } |
141 | } else { |
142 | out.putU2(0); |
143 | } |
144 | |
145 | if (field_info != null) { |
146 | out.putU2(field_info.size()); |
147 | field_info.put(out); |
148 | } else { |
149 | out.putU2(0); |
150 | } |
151 | |
152 | if (method_info != null) { |
153 | out.putU2(method_info.size()); |
154 | method_info.put(out); |
155 | } else { |
156 | out.putU2(0); |
157 | } |
158 | |
159 | if (attribute_info != null) { |
160 | out.putU2(attribute_info.size()); |
161 | attribute_info.put(out); |
162 | } else { |
163 | out.putU2(0); |
164 | } |
165 | |
166 | } |
167 | |
168 | |
169 | /* |
170 | ** Public methods from ClassHolder. |
171 | */ |
172 | |
173 | /** |
174 | * Convert the object representation of the class into |
175 | * its class file format. |
176 | * @exception IOException error writing the class |
177 | */ |
178 | public ByteArray getFileFormat() throws IOException { |
179 | |
180 | int classFileSize = 4 + (10 * 2); |
181 | classFileSize += cptEstimatedSize; |
182 | |
183 | if (interfaces != null) |
184 | classFileSize += (interfaces.length * 2); |
185 | |
186 | if (field_info != null) |
187 | classFileSize += field_info.classFileSize(); |
188 | |
189 | if (method_info != null) |
190 | classFileSize += method_info.classFileSize(); |
191 | |
192 | if (attribute_info != null) |
193 | classFileSize += attribute_info.classFileSize(); |
194 | |
195 | |
196 | ClassFormatOutput cfo = new ClassFormatOutput(classFileSize + 200); |
197 | |
198 | put(cfo); |
199 | |
200 | return new ByteArray(cfo.getData(), 0, cfo.size()); |
201 | |
202 | } |
203 | |
204 | /* |
205 | ** Public methods from ClassMember |
206 | */ |
207 | |
208 | /** @see ClassMember |
209 | */ |
210 | public int getModifier() { return access_flags; } |
211 | |
212 | /** @see ClassMember |
213 | */ |
214 | public String getName() { |
215 | return className(this_class).replace('/', '.'); |
216 | } |
217 | /* |
218 | ** Public methods from ClassHolder |
219 | */ |
220 | |
221 | /** @see ClassHolder#addMember */ |
222 | public ClassMember addMember(String simpleName, String descriptor, int modifier) |
223 | { |
224 | if (SanityManager.DEBUG) |
225 | { |
226 | if (descriptor.startsWith("(")) { |
227 | if (method_info != null) { |
228 | if (method_info.find(simpleName, descriptor) != null) { |
229 | SanityManager.THROWASSERT("Method already exists " + simpleName + " " + descriptor); |
230 | } |
231 | } |
232 | |
233 | } else { |
234 | if (field_info != null) { |
235 | if (field_info.find(simpleName, descriptor) != null) { |
236 | SanityManager.THROWASSERT("Field already exists " + simpleName + " " + descriptor); |
237 | } |
238 | } |
239 | } |
240 | } |
241 | |
242 | CONSTANT_Utf8_info utf = addUtf8Entry(simpleName); |
243 | |
244 | int nameIndex = utf.getIndex(); |
245 | int descriptorIndex = addUtf8Entry(descriptor).getIndex(); |
246 | |
247 | ClassMember item = new ClassMember(this, modifier, nameIndex, descriptorIndex); |
248 | MemberTable mt; |
249 | if (descriptor.startsWith("(")) { |
250 | mt = method_info; |
251 | if (mt == null) |
252 | mt = method_info = new MemberTable(0); |
253 | |
254 | } |
255 | else { |
256 | mt = field_info; |
257 | if (mt == null) |
258 | mt = field_info = new MemberTable(0); |
259 | } |
260 | |
261 | mt.addEntry(item); |
262 | return item; |
263 | } |
264 | |
265 | /** @see ClassHolder#addFieldReference */ |
266 | public int addFieldReference(String className, String simpleName, String descriptor) { |
267 | return addReference(VMDescriptor.CONSTANT_Fieldref, className, simpleName, descriptor); |
268 | } |
269 | |
270 | public int addFieldReference(ClassMember field) { |
271 | return addReference(VMDescriptor.CONSTANT_Fieldref, (ClassMember) field); |
272 | } |
273 | |
274 | /** @see ClassHolder#addMethodReference */ |
275 | public int addMethodReference(String className, String simpleName, String descriptor, boolean isInterface) { |
276 | |
277 | int tag = isInterface ? VMDescriptor.CONSTANT_InterfaceMethodref : |
278 | VMDescriptor.CONSTANT_Methodref; |
279 | |
280 | return addReference(tag, className, simpleName, descriptor); |
281 | } |
282 | |
283 | private int addReference(int tag, String className, String simpleName, String descriptor) { |
284 | |
285 | int classIndex = addClassReference(className); |
286 | int nameTypeIndex = addNameAndType(simpleName, descriptor); |
287 | |
288 | return addIndexReference(tag, classIndex, nameTypeIndex); |
289 | } |
290 | |
291 | private int addReference(int tag, ClassMember member) { |
292 | |
293 | int nameTypeIndex = addIndexReference(VMDescriptor.CONSTANT_NameAndType, |
294 | member.name_index, member.descriptor_index); |
295 | |
296 | return addIndexReference(tag, this_class, nameTypeIndex); |
297 | } |
298 | |
299 | /** @see ClassHolder#addConstant */ |
300 | public int addConstant(String value) { |
301 | |
302 | return addString(value); |
303 | } |
304 | |
305 | /** @see ClassHolder#addUtf8 */ |
306 | public int addUtf8(String value) { |
307 | |
308 | return addUtf8Entry(value).getIndex(); |
309 | } |
310 | |
311 | |
312 | /** @see ClassHolder#addConstant */ |
313 | public int addConstant(int value) { |
314 | return addDirectEntry(new CONSTANT_Integer_info(value)); |
315 | } |
316 | |
317 | /** @see ClassHolder#addConstant */ |
318 | public int addConstant(float value) { |
319 | return addDirectEntry(new CONSTANT_Float_info(value)); |
320 | } |
321 | |
322 | /** @see ClassHolder#addConstant */ |
323 | public int addConstant(long value) { |
324 | return addDirectEntry(new CONSTANT_Long_info(value)); |
325 | } |
326 | |
327 | /** @see ClassHolder#addConstant */ |
328 | public int addConstant(double value) { |
329 | return addDirectEntry(new CONSTANT_Double_info(value)); |
330 | } |
331 | |
332 | |
333 | /** @see ClassMember |
334 | */ |
335 | public int getConstantPoolIndex() { return this_class; } |
336 | |
337 | public void addAttribute(String attributeName, ClassFormatOutput info) { |
338 | |
339 | if (attribute_info == null) |
340 | attribute_info = new Attributes(1); |
341 | |
342 | |
343 | CONSTANT_Utf8_info autf = addUtf8Entry(attributeName); |
344 | |
345 | int index = autf.getIndex(); |
346 | |
347 | attribute_info.addEntry(new AttributeEntry(index, info)); |
348 | } |
349 | |
350 | |
351 | public String getSuperClassName() { |
352 | if (super_class == 0) |
353 | return null; |
354 | else |
355 | return className(super_class).replace('/', '.'); |
356 | } |
357 | |
358 | |
359 | /* |
360 | public ClassMember getMemberReference(String fullyQualifiedClassName, String simpleName, String descriptor) { |
361 | |
362 | int classIndex; |
363 | |
364 | if (fullyQualifiedClassName == null) |
365 | classIndex = this_class; |
366 | else |
367 | classIndex = constantPool.findClass(fullyQualifiedClassName); |
368 | |
369 | if (classIndex < 0) |
370 | return null; |
371 | |
372 | int nameAndTypeIndex = constantPool.findNameAndType(simpleName, descriptor); |
373 | if (nameAndTypeIndex < 0) |
374 | return null; |
375 | |
376 | return constantPool.findReference(classIndex, nameAndTypeIndex); |
377 | } |
378 | */ |
379 | /* |
380 | ** Public methods from ClassRead |
381 | */ |
382 | |
383 | |
384 | |
385 | /* |
386 | ** Implementation specific methods. |
387 | */ |
388 | |
389 | /* |
390 | ** Methods related to Constant Pool Table |
391 | */ |
392 | /** |
393 | Generic add entry to constant pool. Includes the logic |
394 | for an entry to occupy more than one slot (e.g. long). |
395 | |
396 | @return The number of slots occupied by the entry. |
397 | . |
398 | */ |
399 | protected int addEntry(Object key, ConstantPoolEntry item) { |
400 | |
401 | item.setIndex(cptEntries.size()); |
402 | if (key != null) |
403 | cptHashTable.put(key, item); |
404 | cptEntries.addElement(item); |
405 | |
406 | cptEstimatedSize += item.classFileSize(); |
407 | |
408 | if (item.doubleSlot()) { |
409 | cptEntries.addElement(null); |
410 | return 2; |
411 | } else { |
412 | return 1; |
413 | } |
414 | } |
415 | |
416 | /** |
417 | Add an entry, but only if it doesn't exist. |
418 | |
419 | @return the constant pool index of the added |
420 | or existing item. |
421 | */ |
422 | private int addDirectEntry(ConstantPoolEntry item) { |
423 | ConstantPoolEntry existingItem = findMatchingEntry(item); |
424 | if (existingItem != null) { |
425 | item = existingItem; |
426 | //foundCount++; |
427 | } |
428 | else { |
429 | addEntry(item.getKey(), item); |
430 | } |
431 | return item.getIndex(); |
432 | } |
433 | |
434 | /** |
435 | Add an index reference. |
436 | */ |
437 | private int addIndexReference(int tag, int i1, int i2) { |
438 | |
439 | // search for the item using the pre-allocated object |
440 | searchIndex.set(tag, i1, i2); |
441 | |
442 | ConstantPoolEntry item = findMatchingEntry(searchIndex); |
443 | |
444 | if (item == null) { |
445 | item = new CONSTANT_Index_info(tag, i1, i2); |
446 | addEntry(item.getKey(), item); |
447 | } |
448 | |
449 | return item.getIndex(); |
450 | } |
451 | |
452 | /** |
453 | Add a class entry to the pool. |
454 | */ |
455 | public int addClassReference(String fullyQualifiedName) { |
456 | if (ClassHolder.isExternalClassName(fullyQualifiedName)) { |
457 | fullyQualifiedName = ClassHolder.convertToInternalClassName(fullyQualifiedName); |
458 | // System.out.println("addClassReference " + fullyQualifiedName); |
459 | } |
460 | |
461 | int name_index = addUtf8Entry(fullyQualifiedName).getIndex(); |
462 | |
463 | return addIndexReference(VMDescriptor.CONSTANT_Class, name_index, 0); |
464 | } |
465 | |
466 | /** |
467 | Add a name and type entry |
468 | */ |
469 | private int addNameAndType(String name, String descriptor) { |
470 | int nameIndex = addUtf8Entry(name).getIndex(); |
471 | |
472 | int descriptorIndex = addUtf8Entry(descriptor).getIndex(); |
473 | |
474 | return addIndexReference(VMDescriptor.CONSTANT_NameAndType, nameIndex, descriptorIndex); |
475 | } |
476 | |
477 | /** |
478 | Add a UTF8 into the pool and return the index to it. |
479 | */ |
480 | private CONSTANT_Utf8_info addUtf8Entry(String value) { |
481 | |
482 | CONSTANT_Utf8_info item = (CONSTANT_Utf8_info) findMatchingEntry(value); |
483 | |
484 | if (item == null) { |
485 | |
486 | item = new CONSTANT_Utf8_info(value); |
487 | addEntry(value, item); |
488 | } |
489 | return item; |
490 | } |
491 | /** |
492 | Add an extra UTF8 into the pool |
493 | */ |
494 | private CONSTANT_Utf8_info addExtraUtf8(String value) { |
495 | |
496 | CONSTANT_Utf8_info item = new CONSTANT_Utf8_info(value); |
497 | addEntry(null, item); |
498 | |
499 | return item; |
500 | } |
501 | |
502 | /** |
503 | Add a string entry |
504 | */ |
505 | private int addString(String value) { |
506 | CONSTANT_Utf8_info sutf = addUtf8Entry(value); |
507 | int valueIndex = sutf.setAsString(); |
508 | if (valueIndex == 0) { |
509 | // string is already being used as code |
510 | valueIndex = addExtraUtf8(value).getIndex(); |
511 | sutf.setAlternative(valueIndex); |
512 | } |
513 | |
514 | return addIndexReference(VMDescriptor.CONSTANT_String, valueIndex, 0); |
515 | } |
516 | |
517 | /** |
518 | Add a string entry |
519 | */ |
520 | private int addCodeUtf8(String value) { |
521 | CONSTANT_Utf8_info sutf = addUtf8Entry(value); |
522 | int index = sutf.setAsCode(); |
523 | if (index == 0) { |
524 | // code string is already being used as string |
525 | CONSTANT_Utf8_info eutf = addExtraUtf8(value); |
526 | eutf.setAsCode(); // ensure the replace will happen |
527 | index = eutf.getIndex(); |
528 | sutf.setAlternative(index); |
529 | } |
530 | |
531 | return index; |
532 | } |
533 | protected void cptPut(ClassFormatOutput out) throws IOException { |
534 | |
535 | for (Enumeration e = cptEntries.elements(); e.hasMoreElements(); ) { |
536 | ConstantPoolEntry item = (ConstantPoolEntry) e.nextElement(); |
537 | if (item == null) { |
538 | continue; |
539 | } |
540 | |
541 | item.put(out); |
542 | } |
543 | } |
544 | |
545 | /* |
546 | ** Methods to convert indexes to constant pool entries and vice-versa. |
547 | */ |
548 | |
549 | public ConstantPoolEntry getEntry(int index) { |
550 | return (ConstantPoolEntry) cptEntries.elementAt(index); |
551 | } |
552 | |
553 | /** |
554 | Return the class name for an index to a CONSTANT_Class_info. |
555 | */ |
556 | |
557 | protected String className(int classIndex) { |
558 | CONSTANT_Index_info ci = (CONSTANT_Index_info) getEntry(classIndex); |
559 | |
560 | return nameIndexToString(ci.getI1()).replace('/', '.'); |
561 | |
562 | } |
563 | |
564 | /* |
565 | ** Methods to find specific types of constant pool entries. |
566 | In these methods we try to avoid using the ConstantPoolEntry.matchValue() |
567 | as that requires creating a new object for the search. The matchValue() |
568 | call is really intended for when objects are being added to the constant pool. |
569 | */ |
570 | |
571 | /** |
572 | Return the index of a UTF entry or -1 if it doesn't exist. |
573 | */ |
574 | int findUtf8(String value) { |
575 | |
576 | ConstantPoolEntry item = findMatchingEntry(value); |
577 | if (item == null) |
578 | return -1; |
579 | |
580 | return item.getIndex(); |
581 | } |
582 | |
583 | /** |
584 | Find a class descriptor (section 4.4.1) and return its |
585 | index, returns -1 if not found. |
586 | */ |
587 | public int findClass(String fullyQualifiedName) { |
588 | String internalName = ClassHolder.convertToInternalClassName(fullyQualifiedName); |
589 | int utf_index = findUtf8(internalName); |
590 | if (utf_index < 0) |
591 | return -1; |
592 | |
593 | return findIndexIndex(VMDescriptor.CONSTANT_Class, |
594 | utf_index, 0); |
595 | } |
596 | |
597 | |
598 | /** |
599 | Find a name and type descriptor (section 4.4.6) and |
600 | return it's index. |
601 | <p> |
602 | returns -1 if not found. |
603 | */ |
604 | public int findNameAndType(String name, String descriptor) { |
605 | |
606 | int name_index = findUtf8(name); |
607 | if (name_index < 0) |
608 | return -1; |
609 | int descriptor_index = findUtf8(descriptor); |
610 | if (descriptor_index < 0) |
611 | return -1; |
612 | |
613 | return findIndexIndex(VMDescriptor.CONSTANT_NameAndType, |
614 | name_index, descriptor_index); |
615 | } |
616 | /* |
617 | public ClassMember findReference(int classIndex, int nameAndTypeIndex) { |
618 | |
619 | CONSTANT_Index_info item = findIndexEntry(VMDescriptor.CONSTANT_Methodref, |
620 | classIndex, nameAndTypeIndex); |
621 | |
622 | if (item == null) { |
623 | |
624 | item = findIndexEntry(VMDescriptor.CONSTANT_InterfaceMethodref, |
625 | classIndex, nameAndTypeIndex); |
626 | |
627 | if (item == null) { |
628 | item = findIndexEntry(VMDescriptor.CONSTANT_Fieldref, |
629 | classIndex, nameAndTypeIndex); |
630 | |
631 | if (item == null) |
632 | return null; |
633 | |
634 | } |
635 | } |
636 | |
637 | return new ReferenceMember(this, item); |
638 | } |
639 | */ |
640 | protected CONSTANT_Index_info findIndexEntry(int tag, int i1, int i2) { |
641 | // search for the item using the pre-allocated object |
642 | searchIndex.set(tag, i1, i2); |
643 | |
644 | return (CONSTANT_Index_info) findMatchingEntry(searchIndex); |
645 | } |
646 | |
647 | protected int findIndexIndex(int tag, int i1, int i2) { |
648 | CONSTANT_Index_info item = findIndexEntry(tag, i1, i2); |
649 | if (item == null) |
650 | return -1; |
651 | |
652 | return item.getIndex(); |
653 | } |
654 | |
655 | protected ConstantPoolEntry findMatchingEntry(Object key) { |
656 | return (ConstantPoolEntry) cptHashTable.get(key); |
657 | } |
658 | |
659 | /** get a string (UTF) given a name_index into the constant pool |
660 | */ |
661 | String nameIndexToString(int index) { |
662 | |
663 | return getEntry(index).toString(); |
664 | } |
665 | |
666 | /** get the class name of a Class given the index of its CONSTANT_Class_info |
667 | entry in the Constant Pool. |
668 | */ |
669 | |
670 | protected String getClassName(int index) { |
671 | |
672 | if (index == 0) |
673 | return ""; // must be the super class of java.lang.Object, ie. nothing. |
674 | |
675 | return nameIndexToString(getEntry(index).getI1()); |
676 | } |
677 | |
678 | /* |
679 | * Determine whether the class descriptor string is |
680 | * in external format or not. Assumes that to be in external |
681 | * format means it must have a '.' or end in an ']'. |
682 | * |
683 | * @param className the name of the class to check |
684 | * |
685 | * @return true/false |
686 | */ |
687 | public static boolean isExternalClassName(String className) |
688 | { |
689 | int len; |
690 | if (className.indexOf('.') != -1) |
691 | { |
692 | return true; |
693 | } |
694 | else if ((len = className.length()) == 0) |
695 | { |
696 | return false; |
697 | } |
698 | return (className.charAt(len - 1) == ']'); |
699 | } |
700 | |
701 | /* |
702 | * Convert a class name to the internal VM class name format. |
703 | See sections 4.3.2, 4.4.1 of the vm spec. |
704 | * The normal leading 'L' and trailing ';' are left |
705 | * off of objects. This is intended primarily for |
706 | * the class manager. |
707 | * <p> |
708 | * An example of a conversion would be java.lang.Double[] |
709 | * to "[Ljava/lang/Double;". |
710 | <BR> |
711 | java.lang.Double would be converted to "java/lang/Double" |
712 | |
713 | <BR> |
714 | Note that for array types the result of convertToInternalClassName() |
715 | and convertToInternalDescriptor() are identical. |
716 | |
717 | * |
718 | * @param the external name (cannot be null) |
719 | * |
720 | * @return the internal string |
721 | */ |
722 | public static String convertToInternalClassName(String externalName) |
723 | { |
724 | return convertToInternal(externalName, false); |
725 | } |
726 | |
727 | /* |
728 | * Convert a class name to internal JVM descriptor format. |
729 | See sections 4.3.2 of the vm spec. |
730 | * <p> |
731 | * An example of a conversion would be "java.lang.Double[]" |
732 | * to "[Ljava/lang/Double;". |
733 | * |
734 | <BR> |
735 | java.lang.Double would be converted to "Ljava/lang/Double;" |
736 | |
737 | <BR> |
738 | Note that for array types the result of convertToInternalClassName() |
739 | and convertToInternalDescriptor() are identical. |
740 | |
741 | * @param the external name (cannot be null) |
742 | * |
743 | * @return the internal string |
744 | */ |
745 | public static String convertToInternalDescriptor(String externalName) |
746 | { |
747 | return convertToInternal(externalName, true); |
748 | } |
749 | |
750 | /* |
751 | ** Workhorse method. Convert to internal format. |
752 | |
753 | @param descriptor True if converting to descriptor format, false if |
754 | converting to class name format. |
755 | ** |
756 | ** Lifted from BCClass.java. |
757 | ** |
758 | ** Returns the result string. |
759 | */ |
760 | private static String convertToInternal(String externalName, boolean descriptor) |
761 | { |
762 | if (SanityManager.DEBUG) |
763 | { |
764 | SanityManager.ASSERT(externalName != null, "unexpected null"); |
765 | } |
766 | |
767 | int len = externalName.length(); |
768 | |
769 | String internalName; |
770 | String retVal = null; |
771 | int origLen = len; |
772 | int arity = 0; |
773 | |
774 | // first walk through all array-ness |
775 | if (externalName.charAt(len-1) == ']') |
776 | { |
777 | while (len > 0 |
778 | && externalName.charAt(len-1) == ']' |
779 | && externalName.charAt(len-2) == '[') |
780 | { |
781 | len -= 2; |
782 | arity++; |
783 | } |
784 | } |
785 | if (SanityManager.DEBUG) { |
786 | SanityManager.ASSERT(len > 0); |
787 | } |
788 | |
789 | internalName = (origLen == len)? |
790 | externalName |
791 | : externalName.substring(0,len); |
792 | |
793 | // then check for primitive types ... |
794 | // in length by expected frequency order |
795 | |
796 | switch (len) { |
797 | case 7 : |
798 | if ("boolean".equals(internalName)) { |
799 | retVal = makeDesc(VMDescriptor.C_BOOLEAN, arity); |
800 | } |
801 | break; |
802 | case 4 : |
803 | if ("void".equals(internalName)) { |
804 | retVal = makeDesc(VMDescriptor.C_VOID, arity); |
805 | } |
806 | else if ("long".equals(internalName)) { |
807 | retVal = makeDesc(VMDescriptor.C_LONG, arity); |
808 | } |
809 | else if ("byte".equals(internalName)) { |
810 | retVal = makeDesc(VMDescriptor.C_BYTE, arity); |
811 | } |
812 | else if ("char".equals(internalName)) { |
813 | retVal = makeDesc(VMDescriptor.C_CHAR, arity); |
814 | } |
815 | break; |
816 | case 3 : |
817 | if ("int".equals(internalName)) { |
818 | retVal = makeDesc(VMDescriptor.C_INT, arity); |
819 | } |
820 | break; |
821 | case 6 : |
822 | if ("double".equals(internalName)) { |
823 | retVal = makeDesc(VMDescriptor.C_DOUBLE, arity); |
824 | } |
825 | break; |
826 | case 5 : |
827 | if ("short".equals(internalName)) { |
828 | retVal = makeDesc(VMDescriptor.C_SHORT, arity); |
829 | } |
830 | else if ("float".equals(internalName)) { |
831 | retVal = makeDesc(VMDescriptor.C_FLOAT, arity); |
832 | } |
833 | break; |
834 | } |
835 | |
836 | // then it must be a Java class |
837 | if (retVal == null) |
838 | retVal = makeDesc(internalName, arity, descriptor); |
839 | |
840 | return retVal; |
841 | } |
842 | |
843 | /** |
844 | A helper to build a type description based on a built-in type |
845 | and an array arity. |
846 | */ |
847 | static private String makeDesc (char builtin, int arity) { |
848 | if (arity == 0) |
849 | switch (builtin) { |
850 | case VMDescriptor.C_BYTE : return VMDescriptor.BYTE; |
851 | case VMDescriptor.C_CHAR : return VMDescriptor.CHAR; |
852 | case VMDescriptor.C_DOUBLE : return VMDescriptor.DOUBLE; |
853 | case VMDescriptor.C_FLOAT : return VMDescriptor.FLOAT; |
854 | case VMDescriptor.C_INT : return VMDescriptor.INT; |
855 | case VMDescriptor.C_LONG : return VMDescriptor.LONG; |
856 | case VMDescriptor.C_SHORT : return VMDescriptor.SHORT; |
857 | case VMDescriptor.C_BOOLEAN : return VMDescriptor.BOOLEAN; |
858 | case VMDescriptor.C_VOID : return VMDescriptor.VOID; |
859 | default: |
860 | if (SanityManager.DEBUG) |
861 | SanityManager.THROWASSERT("No type match"); |
862 | return null; |
863 | } |
864 | else { |
865 | StringBuffer desc = new StringBuffer(arity+3); |
866 | |
867 | for (int i=0;i<arity;i++) |
868 | desc.append(VMDescriptor.C_ARRAY); |
869 | |
870 | desc.append(ClassHolder.makeDesc(builtin, 0)); |
871 | |
872 | return desc.toString(); |
873 | } |
874 | } |
875 | |
876 | /** |
877 | A helper to build a type description based on a Java class |
878 | and an array arity. |
879 | |
880 | If descriptor is true create a descriptor according to |
881 | section 4.3.2 of the vm spec. If false create a class name |
882 | according to sections 4.3.2 and 4.4.1 of the vm spec. |
883 | |
884 | */ |
885 | static private String makeDesc (String className, int arity, boolean descriptor) { |
886 | |
887 | if (!descriptor && (arity == 0)) { |
888 | return className.replace('.','/'); |
889 | } |
890 | |
891 | StringBuffer desc = new StringBuffer(arity+2+className.length()); |
892 | |
893 | for (int i=0;i<arity;i++) |
894 | desc.append(VMDescriptor.C_ARRAY); |
895 | |
896 | desc.append(VMDescriptor.C_CLASS); |
897 | |
898 | desc.append(className.replace('.','/')); |
899 | |
900 | desc.append(VMDescriptor.C_ENDCLASS); |
901 | |
902 | return desc.toString(); |
903 | } |
904 | |
905 | |
906 | } |