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 |
|
27 import java.util.*; |
|
28 import java.lang.reflect.*; |
|
29 import java.io.*; |
|
30 import xmlkit.XMLKit.Element; |
|
31 /* |
|
32 * @author jrose |
|
33 */ |
|
34 public class ClassWriter extends ClassSyntax implements ClassSyntax.GetCPIndex { |
|
35 |
|
36 private static final CommandLineParser CLP = new CommandLineParser("" |
|
37 + "-source: +> = \n" |
|
38 + "-dest: +> = \n" |
|
39 + "-encoding: +> = \n" |
|
40 + "-parseBytes $ \n" |
|
41 + "- *? \n" |
|
42 + "\n"); |
|
43 |
|
44 public static void main(String[] ava) throws IOException { |
|
45 ArrayList<String> av = new ArrayList<String>(Arrays.asList(ava)); |
|
46 HashMap<String, String> props = new HashMap<String, String>(); |
|
47 props.put("-encoding:", "UTF8"); // default |
|
48 CLP.parse(av, props); |
|
49 File source = asFile(props.get("-source:")); |
|
50 File dest = asFile(props.get("-dest:")); |
|
51 String encoding = props.get("-encoding:"); |
|
52 boolean parseBytes = props.containsKey("-parseBytes"); |
|
53 boolean destMade = false; |
|
54 |
|
55 for (String a : av) { |
|
56 File f; |
|
57 File inf = new File(source, a); |
|
58 System.out.println("Reading " + inf); |
|
59 Element e; |
|
60 if (inf.getName().endsWith(".class")) { |
|
61 ClassReader cr = new ClassReader(); |
|
62 cr.parseBytes = parseBytes; |
|
63 e = cr.readFrom(inf); |
|
64 f = new File(a); |
|
65 } else if (inf.getName().endsWith(".xml")) { |
|
66 InputStream in = new FileInputStream(inf); |
|
67 Reader inw = ClassReader.makeReader(in, encoding); |
|
68 e = XMLKit.readFrom(inw); |
|
69 e.findAllInTree(XMLKit.and(XMLKit.elementFilter(nonAttrTags()), |
|
70 XMLKit.methodFilter(Element.method("trimText")))); |
|
71 //System.out.println(e); |
|
72 inw.close(); |
|
73 f = new File(a.substring(0, a.length() - ".xml".length()) + ".class"); |
|
74 } else { |
|
75 System.out.println("Warning: unknown input " + a); |
|
76 continue; |
|
77 } |
|
78 // Now write it: |
|
79 if (!destMade) { |
|
80 destMade = true; |
|
81 if (dest == null) { |
|
82 dest = File.createTempFile("TestOut", ".dir", new File(".")); |
|
83 dest.delete(); |
|
84 System.out.println("Writing results to " + dest); |
|
85 } |
|
86 if (!(dest.isDirectory() || dest.mkdir())) { |
|
87 throw new RuntimeException("Cannot create " + dest); |
|
88 } |
|
89 } |
|
90 File outf = new File(dest, f.isAbsolute() ? f.getName() : f.getPath()); |
|
91 outf.getParentFile().mkdirs(); |
|
92 new ClassWriter(e).writeTo(outf); |
|
93 } |
|
94 } |
|
95 |
|
96 private static File asFile(String str) { |
|
97 return (str == null) ? null : new File(str); |
|
98 } |
|
99 |
|
100 public void writeTo(File file) throws IOException { |
|
101 OutputStream out = null; |
|
102 try { |
|
103 out = new BufferedOutputStream(new FileOutputStream(file)); |
|
104 writeTo(out); |
|
105 } finally { |
|
106 if (out != null) { |
|
107 out.close(); |
|
108 } |
|
109 } |
|
110 } |
|
111 protected String[] callables; // varies |
|
112 protected int cpoolSize = 0; |
|
113 protected HashMap<String, String> attrTypesByTag; |
|
114 protected OutputStream out; |
|
115 protected HashMap<String, int[]> cpMap = new HashMap<String, int[]>(); |
|
116 protected ArrayList<ByteArrayOutputStream> attrBufs = new ArrayList<ByteArrayOutputStream>(); |
|
117 |
|
118 private void setupAttrTypes() { |
|
119 attrTypesByTag = new HashMap<String, String>(); |
|
120 for (String key : attrTypes.keySet()) { |
|
121 String pfx = key.substring(0, key.indexOf('.') + 1); |
|
122 String val = attrTypes.get(key); |
|
123 int pos = val.indexOf('<'); |
|
124 if (pos >= 0) { |
|
125 String tag = val.substring(pos + 1, val.indexOf('>', pos)); |
|
126 attrTypesByTag.put(pfx + tag, key); |
|
127 } |
|
128 } |
|
129 //System.out.println("attrTypesByTag: "+attrTypesByTag); |
|
130 } |
|
131 |
|
132 protected ByteArrayOutputStream getAttrBuf() { |
|
133 int nab = attrBufs.size(); |
|
134 if (nab == 0) { |
|
135 return new ByteArrayOutputStream(1024); |
|
136 } |
|
137 ByteArrayOutputStream ab = attrBufs.get(nab - 1); |
|
138 attrBufs.remove(nab - 1); |
|
139 return ab; |
|
140 } |
|
141 |
|
142 protected void putAttrBuf(ByteArrayOutputStream ab) { |
|
143 ab.reset(); |
|
144 attrBufs.add(ab); |
|
145 } |
|
146 |
|
147 public ClassWriter(Element root) { |
|
148 this(root, null); |
|
149 } |
|
150 |
|
151 public ClassWriter(Element root, ClassSyntax cr) { |
|
152 if (cr != null) { |
|
153 attrTypes = cr.attrTypes; |
|
154 } |
|
155 setupAttrTypes(); |
|
156 if (root.getName() == "ClassFile") { |
|
157 cfile = root; |
|
158 cpool = root.findElement("ConstantPool"); |
|
159 klass = root.findElement("Class"); |
|
160 } else if (root.getName() == "Class") { |
|
161 cfile = new Element("ClassFile", |
|
162 new String[]{ |
|
163 "magic", String.valueOf(0xCAFEBABE), |
|
164 "minver", "0", "majver", "46",}); |
|
165 cpool = new Element("ConstantPool"); |
|
166 klass = root; |
|
167 } else { |
|
168 throw new IllegalArgumentException("bad element type " + root.getName()); |
|
169 } |
|
170 if (cpool == null) { |
|
171 cpool = new Element("ConstantPool"); |
|
172 } |
|
173 |
|
174 int cpLen = 1 + cpool.size(); |
|
175 for (Element c : cpool.elements()) { |
|
176 int id = (int) c.getAttrLong("id"); |
|
177 int tag = cpTagValue(c.getName()); |
|
178 setCPIndex(tag, c.getText().toString(), id); |
|
179 switch (tag) { |
|
180 case CONSTANT_Long: |
|
181 case CONSTANT_Double: |
|
182 cpLen += 1; |
|
183 } |
|
184 } |
|
185 cpoolSize = cpLen; |
|
186 } |
|
187 |
|
188 public int findCPIndex(int tag, String name) { |
|
189 if (name == null) { |
|
190 return 0; |
|
191 } |
|
192 int[] ids = cpMap.get(name.toString()); |
|
193 return (ids == null) ? 0 : ids[tag]; |
|
194 } |
|
195 |
|
196 public int getCPIndex(int tag, String name) { |
|
197 //System.out.println("getCPIndex "+cpTagName(tag)+" "+name); |
|
198 if (name == null) { |
|
199 return 0; |
|
200 } |
|
201 int id = findCPIndex(tag, name); |
|
202 if (id == 0) { |
|
203 id = cpoolSize; |
|
204 cpoolSize += 1; |
|
205 setCPIndex(tag, name, id); |
|
206 cpool.add(new Element(cpTagName(tag), |
|
207 new String[]{"id", "" + id}, |
|
208 new Object[]{name})); |
|
209 int pos; |
|
210 switch (tag) { |
|
211 case CONSTANT_Long: |
|
212 case CONSTANT_Double: |
|
213 cpoolSize += 1; |
|
214 break; |
|
215 case CONSTANT_Class: |
|
216 case CONSTANT_String: |
|
217 getCPIndex(CONSTANT_Utf8, name); |
|
218 break; |
|
219 case CONSTANT_Fieldref: |
|
220 case CONSTANT_Methodref: |
|
221 case CONSTANT_InterfaceMethodref: |
|
222 pos = name.indexOf(' '); |
|
223 getCPIndex(CONSTANT_Class, name.substring(0, pos)); |
|
224 getCPIndex(CONSTANT_NameAndType, name.substring(pos + 1)); |
|
225 break; |
|
226 case CONSTANT_NameAndType: |
|
227 pos = name.indexOf(' '); |
|
228 getCPIndex(CONSTANT_Utf8, name.substring(0, pos)); |
|
229 getCPIndex(CONSTANT_Utf8, name.substring(pos + 1)); |
|
230 break; |
|
231 } |
|
232 } |
|
233 return id; |
|
234 } |
|
235 |
|
236 public void setCPIndex(int tag, String name, int id) { |
|
237 //System.out.println("setCPIndex id="+id+" tag="+tag+" name="+name); |
|
238 int[] ids = cpMap.get(name); |
|
239 if (ids == null) { |
|
240 cpMap.put(name, ids = new int[13]); |
|
241 } |
|
242 if (ids[tag] != 0 && ids[tag] != id) { |
|
243 System.out.println("Warning: Duplicate CP entries for " + ids[tag] + " and " + id); |
|
244 } |
|
245 //assert(ids[tag] == 0 || ids[tag] == id); |
|
246 ids[tag] = id; |
|
247 } |
|
248 |
|
249 public int parseFlags(String flagString) { |
|
250 int flags = 0; |
|
251 int i = -1; |
|
252 for (String[] names : modifierNames) { |
|
253 ++i; |
|
254 for (String name : names) { |
|
255 if (name == null) { |
|
256 continue; |
|
257 } |
|
258 int pos = flagString.indexOf(name); |
|
259 if (pos >= 0) { |
|
260 flags |= (1 << i); |
|
261 } |
|
262 } |
|
263 } |
|
264 return flags; |
|
265 } |
|
266 |
|
267 public void writeTo(OutputStream realOut) throws IOException { |
|
268 OutputStream headOut = realOut; |
|
269 ByteArrayOutputStream tailOut = new ByteArrayOutputStream(); |
|
270 |
|
271 // write the body of the class file first |
|
272 this.out = tailOut; |
|
273 writeClass(); |
|
274 |
|
275 // write the file header last |
|
276 this.out = headOut; |
|
277 u4((int) cfile.getAttrLong("magic")); |
|
278 u2((int) cfile.getAttrLong("minver")); |
|
279 u2((int) cfile.getAttrLong("majver")); |
|
280 writeCP(); |
|
281 |
|
282 // recopy the file tail |
|
283 this.out = null; |
|
284 tailOut.writeTo(realOut); |
|
285 } |
|
286 |
|
287 void writeClass() throws IOException { |
|
288 int flags = parseFlags(klass.getAttr("flags")); |
|
289 flags ^= Modifier.SYNCHRONIZED; |
|
290 u2(flags); |
|
291 cpRef(CONSTANT_Class, klass.getAttr("name")); |
|
292 cpRef(CONSTANT_Class, klass.getAttr("super")); |
|
293 Element interfaces = klass.findAllElements("Interface"); |
|
294 u2(interfaces.size()); |
|
295 for (Element e : interfaces.elements()) { |
|
296 cpRef(CONSTANT_Class, e.getAttr("name")); |
|
297 } |
|
298 for (int isMethod = 0; isMethod <= 1; isMethod++) { |
|
299 Element members = klass.findAllElements(isMethod != 0 ? "Method" : "Field"); |
|
300 u2(members.size()); |
|
301 for (Element m : members.elements()) { |
|
302 writeMember(m, isMethod != 0); |
|
303 } |
|
304 } |
|
305 writeAttributesFor(klass); |
|
306 } |
|
307 |
|
308 private void writeMember(Element member, boolean isMethod) throws IOException { |
|
309 //System.out.println("writeMember "+member); |
|
310 u2(parseFlags(member.getAttr("flags"))); |
|
311 cpRef(CONSTANT_Utf8, member.getAttr("name")); |
|
312 cpRef(CONSTANT_Utf8, member.getAttr("type")); |
|
313 writeAttributesFor(member); |
|
314 } |
|
315 |
|
316 protected void writeAttributesFor(Element x) throws IOException { |
|
317 LinkedHashSet<String> attrNames = new LinkedHashSet<String>(); |
|
318 for (Element e : x.elements()) { |
|
319 attrNames.add(e.getName()); // uniquifying |
|
320 } |
|
321 attrNames.removeAll(nonAttrTags()); |
|
322 u2(attrNames.size()); |
|
323 if (attrNames.isEmpty()) { |
|
324 return; |
|
325 } |
|
326 Element prevCurrent; |
|
327 if (x.getName() == "Code") { |
|
328 prevCurrent = currentCode; |
|
329 currentCode = x; |
|
330 } else { |
|
331 prevCurrent = currentMember; |
|
332 currentMember = x; |
|
333 } |
|
334 OutputStream realOut = this.out; |
|
335 for (String utag : attrNames) { |
|
336 String qtag = x.getName() + "." + utag; |
|
337 String wtag = "*." + utag; |
|
338 String key = attrTypesByTag.get(qtag); |
|
339 if (key == null) { |
|
340 key = attrTypesByTag.get(wtag); |
|
341 } |
|
342 String type = attrTypes.get(key); |
|
343 //System.out.println("tag "+qtag+" => key "+key+"; type "+type); |
|
344 Element attrs = x.findAllElements(utag); |
|
345 ByteArrayOutputStream attrBuf = getAttrBuf(); |
|
346 if (type == null) { |
|
347 if (attrs.size() != 1 || !attrs.get(0).equals(new Element(utag))) { |
|
348 System.out.println("Warning: No attribute type description: " + qtag); |
|
349 } |
|
350 key = wtag; |
|
351 } else { |
|
352 try { |
|
353 this.out = attrBuf; |
|
354 // unparse according to type desc. |
|
355 if (type.equals("<Code>...")) { |
|
356 writeCode((Element) attrs.get(0)); // assume only 1 |
|
357 } else if (type.equals("<Frame>...")) { |
|
358 writeStackMap(attrs, false); |
|
359 } else if (type.equals("<FrameX>...")) { |
|
360 writeStackMap(attrs, true); |
|
361 } else if (type.startsWith("[")) { |
|
362 writeAttributeRecursive(attrs, type); |
|
363 } else { |
|
364 writeAttribute(attrs, type); |
|
365 } |
|
366 } finally { |
|
367 //System.out.println("Attr Bytes = \""+attrBuf.toString(EIGHT_BIT_CHAR_ENCODING).replace('"', (char)('"'|0x80))+"\""); |
|
368 this.out = realOut; |
|
369 } |
|
370 } |
|
371 cpRef(CONSTANT_Utf8, key.substring(key.indexOf('.') + 1)); |
|
372 u4(attrBuf.size()); |
|
373 attrBuf.writeTo(out); |
|
374 putAttrBuf(attrBuf); |
|
375 } |
|
376 if (x.getName() == "Code") { |
|
377 currentCode = prevCurrent; |
|
378 } else { |
|
379 currentMember = prevCurrent; |
|
380 } |
|
381 } |
|
382 |
|
383 private void writeAttributeRecursive(Element aval, String type) throws IOException { |
|
384 assert (callables == null); |
|
385 callables = getBodies(type); |
|
386 writeAttribute(aval, callables[0]); |
|
387 callables = null; |
|
388 } |
|
389 |
|
390 private void writeAttribute(Element aval, String type) throws IOException { |
|
391 //System.out.println("writeAttribute "+aval+" using "+type); |
|
392 String nextAttrName = null; |
|
393 boolean afterElemHead = false; |
|
394 for (int len = type.length(), next, i = 0; i < len; i = next) { |
|
395 int value; |
|
396 char intKind; |
|
397 int tag; |
|
398 int sigChar; |
|
399 String attrValue; |
|
400 switch (type.charAt(i)) { |
|
401 case '<': |
|
402 assert (nextAttrName == null); |
|
403 next = type.indexOf('>', i); |
|
404 String form = type.substring(i + 1, next++); |
|
405 if (form.indexOf('=') < 0) { |
|
406 // elem_placement = '<' elemname '>' |
|
407 if (aval.isAnonymous()) { |
|
408 assert (aval.size() == 1); |
|
409 aval = (Element) aval.get(0); |
|
410 } |
|
411 assert (aval.getName().equals(form)) : aval + " // " + form; |
|
412 afterElemHead = true; |
|
413 } else { |
|
414 // attr_placement = '(' attrname '=' (value)? ')' |
|
415 int eqPos = form.indexOf('='); |
|
416 assert (eqPos >= 0); |
|
417 nextAttrName = form.substring(0, eqPos).intern(); |
|
418 if (eqPos != form.length() - 1) { |
|
419 // value is implicit, not placed in file |
|
420 nextAttrName = null; |
|
421 } |
|
422 afterElemHead = false; |
|
423 } |
|
424 continue; |
|
425 case '(': |
|
426 next = type.indexOf(')', ++i); |
|
427 int callee = Integer.parseInt(type.substring(i, next++)); |
|
428 writeAttribute(aval, callables[callee]); |
|
429 continue; |
|
430 case 'N': // replication = 'N' int '[' type ... ']' |
|
431 { |
|
432 assert (nextAttrName == null); |
|
433 afterElemHead = false; |
|
434 char countType = type.charAt(i + 1); |
|
435 next = i + 2; |
|
436 String type1 = getBody(type, next); |
|
437 Element elems = aval; |
|
438 if (type1.startsWith("<")) { |
|
439 // Select only matching members of aval. |
|
440 String elemName = type1.substring(1, type1.indexOf('>')); |
|
441 elems = aval.findAllElements(elemName); |
|
442 } |
|
443 putInt(elems.size(), countType); |
|
444 next += type1.length() + 2; // skip body and brackets |
|
445 for (Element elem : elems.elements()) { |
|
446 writeAttribute(elem, type1); |
|
447 } |
|
448 } |
|
449 continue; |
|
450 case 'T': // union = 'T' any_int union_case* '(' ')' '[' body ']' |
|
451 // write the value |
|
452 value = (int) aval.getAttrLong("tag"); |
|
453 assert (aval.getAttr("tag") != null) : aval; |
|
454 intKind = type.charAt(++i); |
|
455 if (intKind == 'S') { |
|
456 intKind = type.charAt(++i); |
|
457 } |
|
458 putInt(value, intKind); |
|
459 nextAttrName = null; |
|
460 afterElemHead = false; |
|
461 ++i; // skip the int type char |
|
462 // union_case = '(' ('-')? digit+ ')' '[' body ']' |
|
463 for (boolean foundCase = false;;) { |
|
464 assert (type.charAt(i) == '('); |
|
465 next = type.indexOf(')', ++i); |
|
466 assert (next >= i); |
|
467 String caseStr = type.substring(i, next++); |
|
468 String type1 = getBody(type, next); |
|
469 next += type1.length() + 2; // skip body and brackets |
|
470 boolean lastCase = (caseStr.length() == 0); |
|
471 if (!foundCase |
|
472 && (lastCase || matchTag(value, caseStr))) { |
|
473 foundCase = true; |
|
474 // Execute this body. |
|
475 writeAttribute(aval, type1); |
|
476 } |
|
477 if (lastCase) { |
|
478 break; |
|
479 } |
|
480 } |
|
481 continue; |
|
482 case 'B': |
|
483 case 'H': |
|
484 case 'I': // int = oneof "BHI" |
|
485 value = (int) aval.getAttrLong(nextAttrName); |
|
486 intKind = type.charAt(i); |
|
487 next = i + 1; |
|
488 break; |
|
489 case 'K': |
|
490 sigChar = type.charAt(i + 1); |
|
491 if (sigChar == 'Q') { |
|
492 assert (currentMember.getName() == "Field"); |
|
493 assert (aval.getName() == "ConstantValue"); |
|
494 String sig = currentMember.getAttr("type"); |
|
495 sigChar = sig.charAt(0); |
|
496 switch (sigChar) { |
|
497 case 'Z': |
|
498 case 'B': |
|
499 case 'C': |
|
500 case 'S': |
|
501 sigChar = 'I'; |
|
502 break; |
|
503 } |
|
504 } |
|
505 switch (sigChar) { |
|
506 case 'I': |
|
507 tag = CONSTANT_Integer; |
|
508 break; |
|
509 case 'J': |
|
510 tag = CONSTANT_Long; |
|
511 break; |
|
512 case 'F': |
|
513 tag = CONSTANT_Float; |
|
514 break; |
|
515 case 'D': |
|
516 tag = CONSTANT_Double; |
|
517 break; |
|
518 case 'L': |
|
519 tag = CONSTANT_String; |
|
520 break; |
|
521 default: |
|
522 assert (false); |
|
523 tag = 0; |
|
524 } |
|
525 assert (type.charAt(i + 2) == 'H'); // only H works for now |
|
526 next = i + 3; |
|
527 assert (afterElemHead || nextAttrName != null); |
|
528 //System.out.println("get attr "+nextAttrName+" in "+aval); |
|
529 if (nextAttrName != null) { |
|
530 attrValue = aval.getAttr(nextAttrName); |
|
531 assert (attrValue != null); |
|
532 } else { |
|
533 assert (aval.isText()) : aval; |
|
534 attrValue = aval.getText().toString(); |
|
535 } |
|
536 value = getCPIndex(tag, attrValue); |
|
537 intKind = 'H'; //type.charAt(i+2); |
|
538 break; |
|
539 case 'R': |
|
540 sigChar = type.charAt(i + 1); |
|
541 switch (sigChar) { |
|
542 case 'C': |
|
543 tag = CONSTANT_Class; |
|
544 break; |
|
545 case 'S': |
|
546 tag = CONSTANT_Utf8; |
|
547 break; |
|
548 case 'D': |
|
549 tag = CONSTANT_Class; |
|
550 break; |
|
551 case 'F': |
|
552 tag = CONSTANT_Fieldref; |
|
553 break; |
|
554 case 'M': |
|
555 tag = CONSTANT_Methodref; |
|
556 break; |
|
557 case 'I': |
|
558 tag = CONSTANT_InterfaceMethodref; |
|
559 break; |
|
560 case 'U': |
|
561 tag = CONSTANT_Utf8; |
|
562 break; |
|
563 //case 'Q': tag = CONSTANT_Class; break; |
|
564 default: |
|
565 assert (false); |
|
566 tag = 0; |
|
567 } |
|
568 assert (type.charAt(i + 2) == 'H'); // only H works for now |
|
569 next = i + 3; |
|
570 assert (afterElemHead || nextAttrName != null); |
|
571 //System.out.println("get attr "+nextAttrName+" in "+aval); |
|
572 if (nextAttrName != null) { |
|
573 attrValue = aval.getAttr(nextAttrName); |
|
574 } else if (aval.hasText()) { |
|
575 attrValue = aval.getText().toString(); |
|
576 } else { |
|
577 attrValue = null; |
|
578 } |
|
579 value = getCPIndex(tag, attrValue); |
|
580 intKind = 'H'; //type.charAt(i+2); |
|
581 break; |
|
582 case 'P': // bci = 'P' int |
|
583 case 'S': // signed_int = 'S' int |
|
584 next = i + 2; |
|
585 value = (int) aval.getAttrLong(nextAttrName); |
|
586 intKind = type.charAt(i + 1); |
|
587 break; |
|
588 case 'F': |
|
589 next = i + 2; |
|
590 value = parseFlags(aval.getAttr(nextAttrName)); |
|
591 intKind = type.charAt(i + 1); |
|
592 break; |
|
593 default: |
|
594 throw new RuntimeException("bad attr format '" + type.charAt(i) + "': " + type); |
|
595 } |
|
596 // write the value |
|
597 putInt(value, intKind); |
|
598 nextAttrName = null; |
|
599 afterElemHead = false; |
|
600 } |
|
601 assert (nextAttrName == null); |
|
602 } |
|
603 |
|
604 private void putInt(int x, char ch) throws IOException { |
|
605 switch (ch) { |
|
606 case 'B': |
|
607 u1(x); |
|
608 break; |
|
609 case 'H': |
|
610 u2(x); |
|
611 break; |
|
612 case 'I': |
|
613 u4(x); |
|
614 break; |
|
615 } |
|
616 assert ("BHI".indexOf(ch) >= 0); |
|
617 } |
|
618 |
|
619 private void writeCode(Element code) throws IOException { |
|
620 //System.out.println("writeCode "+code); |
|
621 //Element m = new Element(currentMember); m.remove(code); |
|
622 //System.out.println(" in "+m); |
|
623 int stack = (int) code.getAttrLong("stack"); |
|
624 int local = (int) code.getAttrLong("local"); |
|
625 Element bytes = code.findElement("Bytes"); |
|
626 Element insns = code.findElement("Instructions"); |
|
627 String bytecodes; |
|
628 if (insns == null) { |
|
629 bytecodes = bytes.getText().toString(); |
|
630 } else { |
|
631 bytecodes = InstructionSyntax.assemble(insns, this); |
|
632 // Cache the assembled bytecodes: |
|
633 bytes = new Element("Bytes", (String[]) null, bytecodes); |
|
634 code.add(0, bytes); |
|
635 } |
|
636 u2(stack); |
|
637 u2(local); |
|
638 int length = bytecodes.length(); |
|
639 u4(length); |
|
640 for (int i = 0; i < length; i++) { |
|
641 u1((byte) bytecodes.charAt(i)); |
|
642 } |
|
643 Element handlers = code.findAllElements("Handler"); |
|
644 u2(handlers.size()); |
|
645 for (Element handler : handlers.elements()) { |
|
646 int start = (int) handler.getAttrLong("start"); |
|
647 int end = (int) handler.getAttrLong("end"); |
|
648 int catsh = (int) handler.getAttrLong("catch"); |
|
649 u2(start); |
|
650 u2(end); |
|
651 u2(catsh); |
|
652 cpRef(CONSTANT_Class, handler.getAttr("class")); |
|
653 } |
|
654 writeAttributesFor(code); |
|
655 } |
|
656 |
|
657 protected void writeStackMap(Element attrs, boolean hasXOption) throws IOException { |
|
658 Element bytes = currentCode.findElement("Bytes"); |
|
659 assert (bytes != null && bytes.size() == 1); |
|
660 int byteLength = ((String) bytes.get(0)).length(); |
|
661 boolean uoffsetIsU4 = (byteLength >= (1 << 16)); |
|
662 boolean ulocalvarIsU4 = currentCode.getAttrLong("local") >= (1 << 16); |
|
663 boolean ustackIsU4 = currentCode.getAttrLong("stack") >= (1 << 16); |
|
664 if (uoffsetIsU4) { |
|
665 u4(attrs.size()); |
|
666 } else { |
|
667 u2(attrs.size()); |
|
668 } |
|
669 for (Element frame : attrs.elements()) { |
|
670 int bci = (int) frame.getAttrLong("bci"); |
|
671 if (uoffsetIsU4) { |
|
672 u4(bci); |
|
673 } else { |
|
674 u2(bci); |
|
675 } |
|
676 if (hasXOption) { |
|
677 u1((int) frame.getAttrLong("flags")); |
|
678 } |
|
679 // Scan local and stack types in this frame: |
|
680 final int LOCALS = 0, STACK = 1; |
|
681 for (int j = LOCALS; j <= STACK; j++) { |
|
682 Element types = frame.findElement(j == LOCALS ? "Local" : "Stack"); |
|
683 int typeSize = (types == null) ? 0 : types.size(); |
|
684 if (j == LOCALS) { |
|
685 if (ulocalvarIsU4) { |
|
686 u4(typeSize); |
|
687 } else { |
|
688 u2(typeSize); |
|
689 } |
|
690 } else { // STACK |
|
691 if (ustackIsU4) { |
|
692 u4(typeSize); |
|
693 } else { |
|
694 u2(typeSize); |
|
695 } |
|
696 } |
|
697 if (types == null) { |
|
698 continue; |
|
699 } |
|
700 for (Element type : types.elements()) { |
|
701 int tag = itemTagValue(type.getName()); |
|
702 u1(tag); |
|
703 switch (tag) { |
|
704 case ITEM_Object: |
|
705 cpRef(CONSTANT_Class, type.getAttr("class")); |
|
706 break; |
|
707 case ITEM_Uninitialized: |
|
708 case ITEM_ReturnAddress: { |
|
709 int offset = (int) type.getAttrLong("bci"); |
|
710 if (uoffsetIsU4) { |
|
711 u4(offset); |
|
712 } else { |
|
713 u2(offset); |
|
714 } |
|
715 } |
|
716 break; |
|
717 } |
|
718 } |
|
719 } |
|
720 } |
|
721 } |
|
722 |
|
723 public void writeCP() throws IOException { |
|
724 int cpLen = cpoolSize; |
|
725 u2(cpLen); |
|
726 ByteArrayOutputStream buf = getAttrBuf(); |
|
727 for (Element c : cpool.elements()) { |
|
728 if (!c.isText()) { |
|
729 System.out.println("## !isText " + c); |
|
730 } |
|
731 int id = (int) c.getAttrLong("id"); |
|
732 int tag = cpTagValue(c.getName()); |
|
733 String name = c.getText().toString(); |
|
734 int pos; |
|
735 u1(tag); |
|
736 switch (tag) { |
|
737 case CONSTANT_Utf8: { |
|
738 int done = 0; |
|
739 buf.reset(); |
|
740 int nameLen = name.length(); |
|
741 while (done < nameLen) { |
|
742 int next = name.indexOf((char) 0, done); |
|
743 if (next < 0) { |
|
744 next = nameLen; |
|
745 } |
|
746 if (done < next) { |
|
747 buf.write(name.substring(done, next).getBytes(UTF8_ENCODING)); |
|
748 } |
|
749 if (next < nameLen) { |
|
750 buf.write(0300); |
|
751 buf.write(0200); |
|
752 next++; |
|
753 } |
|
754 done = next; |
|
755 } |
|
756 u2(buf.size()); |
|
757 buf.writeTo(out); |
|
758 } |
|
759 break; |
|
760 case CONSTANT_Integer: |
|
761 u4(Integer.parseInt(name)); |
|
762 break; |
|
763 case CONSTANT_Float: |
|
764 u4(Float.floatToIntBits(Float.parseFloat(name))); |
|
765 break; |
|
766 case CONSTANT_Long: |
|
767 u8(Long.parseLong(name)); |
|
768 //i += 1; // no need: extra cp slot is implicit |
|
769 break; |
|
770 case CONSTANT_Double: |
|
771 u8(Double.doubleToLongBits(Double.parseDouble(name))); |
|
772 //i += 1; // no need: extra cp slot is implicit |
|
773 break; |
|
774 case CONSTANT_Class: |
|
775 case CONSTANT_String: |
|
776 u2(getCPIndex(CONSTANT_Utf8, name)); |
|
777 break; |
|
778 case CONSTANT_Fieldref: |
|
779 case CONSTANT_Methodref: |
|
780 case CONSTANT_InterfaceMethodref: |
|
781 pos = name.indexOf(' '); |
|
782 u2(getCPIndex(CONSTANT_Class, name.substring(0, pos))); |
|
783 u2(getCPIndex(CONSTANT_NameAndType, name.substring(pos + 1))); |
|
784 break; |
|
785 case CONSTANT_NameAndType: |
|
786 pos = name.indexOf(' '); |
|
787 u2(getCPIndex(CONSTANT_Utf8, name.substring(0, pos))); |
|
788 u2(getCPIndex(CONSTANT_Utf8, name.substring(pos + 1))); |
|
789 break; |
|
790 } |
|
791 } |
|
792 putAttrBuf(buf); |
|
793 } |
|
794 |
|
795 public void cpRef(int tag, String name) throws IOException { |
|
796 u2(getCPIndex(tag, name)); |
|
797 } |
|
798 |
|
799 public void u8(long x) throws IOException { |
|
800 u4((int) (x >>> 32)); |
|
801 u4((int) (x >>> 0)); |
|
802 } |
|
803 |
|
804 public void u4(int x) throws IOException { |
|
805 u2(x >>> 16); |
|
806 u2(x >>> 0); |
|
807 } |
|
808 |
|
809 public void u2(int x) throws IOException { |
|
810 u1(x >>> 8); |
|
811 u1(x >>> 0); |
|
812 } |
|
813 |
|
814 public void u1(int x) throws IOException { |
|
815 out.write(x & 0xFF); |
|
816 } |
|
817 } |
|
818 |
|