1 /* |
|
2 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 package xmlkit; // -*- mode: java; indent-tabs-mode: nil -*- |
|
26 import xmlkit.XMLKit.*; |
|
27 |
|
28 import java.util.*; |
|
29 import java.security.MessageDigest; |
|
30 import java.nio.ByteBuffer; |
|
31 import xmlkit.XMLKit.Element; |
|
32 /* |
|
33 * @author jrose |
|
34 */ |
|
35 public abstract class ClassSyntax { |
|
36 |
|
37 public interface GetCPIndex { |
|
38 |
|
39 int getCPIndex(int tag, String name); // cp finder |
|
40 } |
|
41 public static final int CONSTANT_Utf8 = 1, |
|
42 CONSTANT_Integer = 3, |
|
43 CONSTANT_Float = 4, |
|
44 CONSTANT_Long = 5, |
|
45 CONSTANT_Double = 6, |
|
46 CONSTANT_Class = 7, |
|
47 CONSTANT_String = 8, |
|
48 CONSTANT_Fieldref = 9, |
|
49 CONSTANT_Methodref = 10, |
|
50 CONSTANT_InterfaceMethodref = 11, |
|
51 CONSTANT_NameAndType = 12; |
|
52 private static final String[] cpTagName = { |
|
53 /* 0: */null, |
|
54 /* 1: */ "Utf8", |
|
55 /* 2: */ null, |
|
56 /* 3: */ "Integer", |
|
57 /* 4: */ "Float", |
|
58 /* 5: */ "Long", |
|
59 /* 6: */ "Double", |
|
60 /* 7: */ "Class", |
|
61 /* 8: */ "String", |
|
62 /* 9: */ "Fieldref", |
|
63 /* 10: */ "Methodref", |
|
64 /* 11: */ "InterfaceMethodref", |
|
65 /* 12: */ "NameAndType", |
|
66 null |
|
67 }; |
|
68 private static final Set<String> cpTagNames; |
|
69 |
|
70 static { |
|
71 Set<String> set = new HashSet<String>(Arrays.asList(cpTagName)); |
|
72 set.remove(null); |
|
73 cpTagNames = Collections.unmodifiableSet(set); |
|
74 } |
|
75 public static final int ITEM_Top = 0, // replicates by [1..4,1..4] |
|
76 ITEM_Integer = 1, // (ditto) |
|
77 ITEM_Float = 2, |
|
78 ITEM_Double = 3, |
|
79 ITEM_Long = 4, |
|
80 ITEM_Null = 5, |
|
81 ITEM_UninitializedThis = 6, |
|
82 ITEM_Object = 7, |
|
83 ITEM_Uninitialized = 8, |
|
84 ITEM_ReturnAddress = 9, |
|
85 ITEM_LIMIT = 10; |
|
86 private static final String[] itemTagName = { |
|
87 "Top", |
|
88 "Integer", |
|
89 "Float", |
|
90 "Double", |
|
91 "Long", |
|
92 "Null", |
|
93 "UninitializedThis", |
|
94 "Object", |
|
95 "Uninitialized", |
|
96 "ReturnAddress",}; |
|
97 private static final Set<String> itemTagNames; |
|
98 |
|
99 static { |
|
100 Set<String> set = new HashSet<String>(Arrays.asList(itemTagName)); |
|
101 set.remove(null); |
|
102 itemTagNames = Collections.unmodifiableSet(set); |
|
103 } |
|
104 protected static final HashMap<String, String> attrTypesBacking; |
|
105 protected static final Map<String, String> attrTypesInit; |
|
106 |
|
107 static { |
|
108 HashMap<String, String> at = new HashMap<String, String>(); |
|
109 |
|
110 //at.put("*.Deprecated", "<deprecated=true>"); |
|
111 //at.put("*.Synthetic", "<synthetic=true>"); |
|
112 ////at.put("Field.ConstantValue", "<constantValue=>KQH"); |
|
113 //at.put("Class.SourceFile", "<sourceFile=>RUH"); |
|
114 at.put("Method.Bridge", "<Bridge>"); |
|
115 at.put("Method.Varargs", "<Varargs>"); |
|
116 at.put("Class.Enum", "<Enum>"); |
|
117 at.put("*.Signature", "<Signature>RSH"); |
|
118 //at.put("*.Deprecated", "<Deprecated>"); |
|
119 //at.put("*.Synthetic", "<Synthetic>"); |
|
120 at.put("Field.ConstantValue", "<ConstantValue>KQH"); |
|
121 at.put("Class.SourceFile", "<SourceFile>RUH"); |
|
122 at.put("Class.InnerClasses", "NH[<InnerClass><class=>RCH<outer=>RCH<name=>RUH<flags=>FH]"); |
|
123 at.put("Code.LineNumberTable", "NH[<LineNumber><bci=>PH<line=>H]"); |
|
124 at.put("Code.LocalVariableTable", "NH[<LocalVariable><bci=>PH<span=>H<name=>RUH<type=>RSH<slot=>H]"); |
|
125 at.put("Code.LocalVariableTypeTable", "NH[<LocalVariableType><bci=>PH<span=>H<name=>RUH<type=>RSH<slot=>H]"); |
|
126 at.put("Method.Exceptions", "NH[<Exception><name=>RCH]"); |
|
127 at.put("Method.Code", "<Code>..."); |
|
128 at.put("Code.StackMapTable", "<Frame>..."); |
|
129 //at.put("Code.StkMapX", "<FrameX>..."); |
|
130 if (true) { |
|
131 at.put("Code.StackMapTable", |
|
132 "[NH[<Frame>(1)]]" |
|
133 + "[TB" |
|
134 + "(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79" |
|
135 + ",80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95" |
|
136 + ",96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111" |
|
137 + ",112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127" |
|
138 + ")[<SameLocals1StackItemFrame>(4)]" |
|
139 + "(247)[<SameLocals1StackItemExtended>H(4)]" |
|
140 + "(248)[<Chop3>H]" |
|
141 + "(249)[<Chop2>H]" |
|
142 + "(250)[<Chop1>H]" |
|
143 + "(251)[<SameFrameExtended>H]" |
|
144 + "(252)[<Append1>H(4)]" |
|
145 + "(253)[<Append2>H(4)(4)]" |
|
146 + "(254)[<Append3>H(4)(4)(4)]" |
|
147 + "(255)[<FullFrame>H(2)(3)]" |
|
148 + "()[<SameFrame>]]" |
|
149 + "[NH[<Local>(4)]]" |
|
150 + "[NH[<Stack>(4)]]" |
|
151 + "[TB" |
|
152 + ("(0)[<Top>]" |
|
153 + "(1)[<ItemInteger>](2)[<ItemFloat>](3)[<ItemDouble>](4)[<ItemLong>]" |
|
154 + "(5)[<ItemNull>](6)[<ItemUninitializedThis>]" |
|
155 + "(7)[<ItemObject><class=>RCH]" |
|
156 + "(8)[<ItemUninitialized><bci=>PH]" |
|
157 + "()[<ItemUnknown>]]")); |
|
158 } |
|
159 |
|
160 at.put("Class.EnclosingMethod", "<EnclosingMethod><class=>RCH<desc=>RDH");//RDNH |
|
161 |
|
162 // Layouts of metadata attrs: |
|
163 String vpf = "[<RuntimeVisibleAnnotation>"; |
|
164 String ipf = "[<RuntimeInvisibleAnnotation>"; |
|
165 String apf = "[<Annotation>"; |
|
166 String mdanno2 = "" |
|
167 + "<type=>RSHNH[<Member><name=>RUH(3)]]" |
|
168 + ("[TB" |
|
169 + "(\\B,\\C,\\I,\\S,\\Z)[<value=>KIH]" |
|
170 + "(\\D)[<value=>KDH]" |
|
171 + "(\\F)[<value=>KFH]" |
|
172 + "(\\J)[<value=>KJH]" |
|
173 + "(\\c)[<class=>RSH]" |
|
174 + "(\\e)[<type=>RSH<name=>RUH]" |
|
175 + "(\\s)[<String>RUH]" |
|
176 + "(\\@)[(2)]" |
|
177 + "(\\[)[NH[<Element>(3)]]" |
|
178 + "()[]" |
|
179 + "]"); |
|
180 String visanno = "[NH[(2)]][(1)]" + vpf + mdanno2; |
|
181 String invanno = "[NH[(2)]][(1)]" + ipf + mdanno2; |
|
182 String vparamanno = "" |
|
183 + "[NB[<RuntimeVisibleParameterAnnotation>(1)]][NH[(2)]]" |
|
184 + apf + mdanno2; |
|
185 String iparamanno = "" |
|
186 + "[NB[<RuntimeInvisibleParameterAnnotation>(1)]][NH[(2)]]" |
|
187 + apf + mdanno2; |
|
188 String mdannodef = "[<AnnotationDefault>(3)][(1)]" + apf + mdanno2; |
|
189 String[] mdplaces = {"Class", "Field", "Method"}; |
|
190 for (String place : mdplaces) { |
|
191 at.put(place + ".RuntimeVisibleAnnotations", visanno); |
|
192 at.put(place + ".RuntimeInvisibleAnnotations", invanno); |
|
193 } |
|
194 at.put("Method.RuntimeVisibleParameterAnnotations", vparamanno); |
|
195 at.put("Method.RuntimeInvisibleParameterAnnotations", iparamanno); |
|
196 at.put("Method.AnnotationDefault", mdannodef); |
|
197 |
|
198 attrTypesBacking = at; |
|
199 attrTypesInit = Collections.unmodifiableMap(at); |
|
200 } |
|
201 |
|
202 ; |
|
203 private static final String[] jcovAttrTypes = { |
|
204 "Code.CoverageTable=NH[<Coverage><bci=>PH<type=>H<line=>I<pos=>I]", |
|
205 "Code.CharacterRangeTable=NH[<CharacterRange><bci=>PH<endbci=>POH<from=>I<to=>I<flag=>H]", |
|
206 "Class.SourceID=<SourceID><id=>RUH", |
|
207 "Class.CompilationID=<CompilationID><id=>RUH" |
|
208 }; |
|
209 protected static final String[][] modifierNames = { |
|
210 {"public"}, |
|
211 {"private"}, |
|
212 {"protected"}, |
|
213 {"static"}, |
|
214 {"final"}, |
|
215 {"synchronized"}, |
|
216 {null, "volatile", "bridge"}, |
|
217 {null, "transient", "varargs"}, |
|
218 {null, null, "native"}, |
|
219 {"interface"}, |
|
220 {"abstract"}, |
|
221 {"strictfp"}, |
|
222 {"synthetic"}, |
|
223 {"annotation"}, |
|
224 {"enum"},}; |
|
225 protected static final String EIGHT_BIT_CHAR_ENCODING = "ISO8859_1"; |
|
226 protected static final String UTF8_ENCODING = "UTF8"; |
|
227 // What XML tags are used by this syntax, apart from attributes? |
|
228 protected static final Set<String> nonAttrTags; |
|
229 |
|
230 static { |
|
231 HashSet<String> tagSet = new HashSet<String>(); |
|
232 Collections.addAll(tagSet, new String[]{ |
|
233 "ConstantPool",// the CP |
|
234 "Class", // the class |
|
235 "Interface", // implemented interfaces |
|
236 "Method", // methods |
|
237 "Field", // fields |
|
238 "Handler", // exception handler pseudo-attribute |
|
239 "Attribute", // unparsed attribute |
|
240 "Bytes", // bytecodes |
|
241 "Instructions" // bytecodes, parsed |
|
242 }); |
|
243 nonAttrTags = Collections.unmodifiableSet(tagSet); |
|
244 } |
|
245 |
|
246 // Accessors. |
|
247 public static Set<String> nonAttrTags() { |
|
248 return nonAttrTags; |
|
249 } |
|
250 |
|
251 public static String cpTagName(int t) { |
|
252 t &= 0xFF; |
|
253 String ts = null; |
|
254 if (t < cpTagName.length) { |
|
255 ts = cpTagName[t]; |
|
256 } |
|
257 if (ts != null) { |
|
258 return ts; |
|
259 } |
|
260 return ("UnknownTag" + (int) t).intern(); |
|
261 } |
|
262 |
|
263 public static int cpTagValue(String name) { |
|
264 for (int t = 0; t < cpTagName.length; t++) { |
|
265 if (name.equals(cpTagName[t])) { |
|
266 return t; |
|
267 } |
|
268 } |
|
269 return 0; |
|
270 } |
|
271 |
|
272 public static String itemTagName(int t) { |
|
273 t &= 0xFF; |
|
274 String ts = null; |
|
275 if (t < itemTagName.length) { |
|
276 ts = itemTagName[t]; |
|
277 } |
|
278 if (ts != null) { |
|
279 return ts; |
|
280 } |
|
281 return ("UnknownItem" + (int) t).intern(); |
|
282 } |
|
283 |
|
284 public static int itemTagValue(String name) { |
|
285 for (int t = 0; t < itemTagName.length; t++) { |
|
286 if (name.equals(itemTagName[t])) { |
|
287 return t; |
|
288 } |
|
289 } |
|
290 return -1; |
|
291 } |
|
292 |
|
293 public void addJcovAttrTypes() { |
|
294 addAttrTypes(jcovAttrTypes); |
|
295 } |
|
296 // Public methods for declaring attribute types. |
|
297 protected Map<String, String> attrTypes = attrTypesInit; |
|
298 |
|
299 public void addAttrType(String opt) { |
|
300 int eqpos = opt.indexOf('='); |
|
301 addAttrType(opt.substring(0, eqpos), opt.substring(eqpos + 1)); |
|
302 } |
|
303 |
|
304 public void addAttrTypes(String[] opts) { |
|
305 for (String opt : opts) { |
|
306 addAttrType(opt); |
|
307 } |
|
308 } |
|
309 |
|
310 private void checkAttr(String attr) { |
|
311 if (!attr.startsWith("Class.") |
|
312 && !attr.startsWith("Field.") |
|
313 && !attr.startsWith("Method.") |
|
314 && !attr.startsWith("Code.") |
|
315 && !attr.startsWith("*.")) { |
|
316 throw new IllegalArgumentException("attr name must start with 'Class.', etc."); |
|
317 } |
|
318 String uattr = attr.substring(attr.indexOf('.') + 1); |
|
319 if (nonAttrTags.contains(uattr)) { |
|
320 throw new IllegalArgumentException("attr name must not be one of " + nonAttrTags); |
|
321 } |
|
322 } |
|
323 |
|
324 private void checkAttrs(Map<String, String> at) { |
|
325 for (String attr : at.keySet()) { |
|
326 checkAttr(attr); |
|
327 } |
|
328 } |
|
329 |
|
330 private void modAttrs() { |
|
331 if (attrTypes == attrTypesInit) { |
|
332 // Make modifiable. |
|
333 attrTypes = new HashMap<String, String>(attrTypesBacking); |
|
334 } |
|
335 } |
|
336 |
|
337 public void addAttrType(String attr, String fmt) { |
|
338 checkAttr(attr); |
|
339 modAttrs(); |
|
340 attrTypes.put(attr, fmt); |
|
341 } |
|
342 |
|
343 public void addAttrTypes(Map<String, String> at) { |
|
344 checkAttrs(at); |
|
345 modAttrs(); |
|
346 attrTypes.putAll(at); |
|
347 } |
|
348 |
|
349 public Map<String, String> getAttrTypes() { |
|
350 if (attrTypes == attrTypesInit) { |
|
351 return attrTypes; |
|
352 } |
|
353 return Collections.unmodifiableMap(attrTypes); |
|
354 } |
|
355 |
|
356 public void setAttrTypes(Map<String, String> at) { |
|
357 checkAttrs(at); |
|
358 modAttrs(); |
|
359 attrTypes.keySet().retainAll(at.keySet()); |
|
360 attrTypes.putAll(at); |
|
361 } |
|
362 |
|
363 // attr format helpers |
|
364 protected static boolean matchTag(int tagValue, String caseStr) { |
|
365 //System.out.println("matchTag "+tagValue+" in "+caseStr); |
|
366 for (int pos = 0, max = caseStr.length(), comma; |
|
367 pos < max; |
|
368 pos = comma + 1) { |
|
369 int caseValue; |
|
370 if (caseStr.charAt(pos) == '\\') { |
|
371 caseValue = caseStr.charAt(pos + 1); |
|
372 comma = pos + 2; |
|
373 assert (comma == max || caseStr.charAt(comma) == ','); |
|
374 } else { |
|
375 comma = caseStr.indexOf(',', pos); |
|
376 if (comma < 0) { |
|
377 comma = max; |
|
378 } |
|
379 caseValue = Integer.parseInt(caseStr.substring(pos, comma)); |
|
380 } |
|
381 if (tagValue == caseValue) { |
|
382 return true; |
|
383 } |
|
384 } |
|
385 return false; |
|
386 } |
|
387 |
|
388 protected static String[] getBodies(String type) { |
|
389 ArrayList<String> bodies = new ArrayList<String>(); |
|
390 for (int i = 0; i < type.length();) { |
|
391 String body = getBody(type, i); |
|
392 bodies.add(body); |
|
393 i += body.length() + 2; // skip body and brackets |
|
394 } |
|
395 return bodies.toArray(new String[bodies.size()]); |
|
396 } |
|
397 |
|
398 protected static String getBody(String type, int i) { |
|
399 assert (type.charAt(i) == '['); |
|
400 int next = ++i; // skip bracket |
|
401 for (int depth = 1; depth > 0; next++) { |
|
402 switch (type.charAt(next)) { |
|
403 case '[': |
|
404 depth++; |
|
405 break; |
|
406 case ']': |
|
407 depth--; |
|
408 break; |
|
409 case '(': |
|
410 next = type.indexOf(')', next); |
|
411 break; |
|
412 case '<': |
|
413 next = type.indexOf('>', next); |
|
414 break; |
|
415 } |
|
416 assert (next > 0); |
|
417 } |
|
418 --next; // get before bracket |
|
419 assert (type.charAt(next) == ']'); |
|
420 return type.substring(i, next); |
|
421 } |
|
422 |
|
423 public Element makeCPDigest(int length) { |
|
424 MessageDigest md; |
|
425 try { |
|
426 md = MessageDigest.getInstance("MD5"); |
|
427 } catch (java.security.NoSuchAlgorithmException ee) { |
|
428 throw new Error(ee); |
|
429 } |
|
430 int items = 0; |
|
431 for (Element e : cpool.elements()) { |
|
432 if (items == length) { |
|
433 break; |
|
434 } |
|
435 if (cpTagNames.contains(e.getName())) { |
|
436 items += 1; |
|
437 md.update((byte) cpTagValue(e.getName())); |
|
438 try { |
|
439 md.update(e.getText().toString().getBytes(UTF8_ENCODING)); |
|
440 } catch (java.io.UnsupportedEncodingException ee) { |
|
441 throw new Error(ee); |
|
442 } |
|
443 } |
|
444 } |
|
445 ByteBuffer bb = ByteBuffer.wrap(md.digest()); |
|
446 String l0 = Long.toHexString(bb.getLong(0)); |
|
447 String l1 = Long.toHexString(bb.getLong(8)); |
|
448 while (l0.length() < 16) { |
|
449 l0 = "0" + l0; |
|
450 } |
|
451 while (l1.length() < 16) { |
|
452 l1 = "0" + l1; |
|
453 } |
|
454 return new Element("Digest", |
|
455 "length", "" + items, |
|
456 "bytes", l0 + l1); |
|
457 } |
|
458 |
|
459 public Element getCPDigest(int length) { |
|
460 if (length == -1) { |
|
461 length = cpool.countAll(XMLKit.elementFilter(cpTagNames)); |
|
462 } |
|
463 for (Element md : cpool.findAllElements("Digest").elements()) { |
|
464 if (md.getAttrLong("length") == length) { |
|
465 return md; |
|
466 } |
|
467 } |
|
468 Element md = makeCPDigest(length); |
|
469 cpool.add(md); |
|
470 return md; |
|
471 } |
|
472 |
|
473 public Element getCPDigest() { |
|
474 return getCPDigest(-1); |
|
475 } |
|
476 |
|
477 public boolean checkCPDigest(Element md) { |
|
478 return md.equals(getCPDigest((int) md.getAttrLong("length"))); |
|
479 } |
|
480 |
|
481 public static int computeInterfaceNum(String intMethRef) { |
|
482 intMethRef = intMethRef.substring(1 + intMethRef.lastIndexOf(' ')); |
|
483 if (!intMethRef.startsWith("(")) { |
|
484 return -1; |
|
485 } |
|
486 int signum = 1; // start with one for "this" |
|
487 scanSig: |
|
488 for (int i = 1; i < intMethRef.length(); i++) { |
|
489 char ch = intMethRef.charAt(i); |
|
490 signum++; |
|
491 switch (ch) { |
|
492 case ')': |
|
493 --signum; |
|
494 break scanSig; |
|
495 case 'L': |
|
496 i = intMethRef.indexOf(';', i); |
|
497 break; |
|
498 case '[': |
|
499 while (ch == '[') { |
|
500 ch = intMethRef.charAt(++i); |
|
501 } |
|
502 if (ch == 'L') { |
|
503 i = intMethRef.indexOf(';', i); |
|
504 } |
|
505 break; |
|
506 } |
|
507 } |
|
508 int num = (signum << 8) | 0; |
|
509 //System.out.println("computeInterfaceNum "+intMethRef+" => "+num); |
|
510 return num; |
|
511 } |
|
512 // Protected state for representing the class file. |
|
513 protected Element cfile; // <ClassFile ...> |
|
514 protected Element cpool; // <ConstantPool ...> |
|
515 protected Element klass; // <Class ...> |
|
516 protected Element currentMember; // varies during scans |
|
517 protected Element currentCode; // varies during scans |
|
518 } |
|