1 /*
2 * Copyright (c) 1996, 2020, 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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package org.openjdk.asmtools.jasm;
24
25 import java.io.*;
26 import java.util.ArrayList;
27 import java.util.List;
28
29 import static org.openjdk.asmtools.jasm.Tables.*;
30
31 /**
32 * ClassData
33 *
34 * This is the main data structure for representing parsed class data. This structure
35 * renders directly to a class file.
36 *
37 */
38 class ClassData extends MemberData {
39
40 /* ClassData Fields */
41 CFVersion cfv;
42 ConstantPool.ConstCell me, father;
43 String myClassName;
44 AttrData sourceFileNameAttr;
45 ArrayList<Argument> interfaces;
46 ArrayList<FieldData> fields = new ArrayList<>();
47 ArrayList<MethodData> methods = new ArrayList<>();
48 DataVectorAttr<InnerClassData> innerClasses = null;
49 DataVectorAttr<BootstrapMethodData> bootstrapMethodsAttr = null;
50
51 // JEP 181 - NestHost, NestMembers attributes since class version 55.0
52 CPXAttr nestHostAttr;
53 NestMembersAttr nestMembersAttr;
54
55 // JEP 359 - Record attribute since class file 58.65535
56 private RecordData recordData;
57
58 // JEP 360 - PermittedSubclasses attribute since class file 59.65535
59 private PermittedSubclassesAttr permittedSubclassesAttr;
60
61 private PreloadAttr preloadAttr;
62
63 ModuleAttr moduleAttribute = null;
64 Environment env;
65 protected ConstantPool pool;
66
67 private static final String DEFAULT_EXTENSION = ".class";
68 String fileExtension = DEFAULT_EXTENSION;
69 public CDOutputStream cdos;
70
71 /**
72 * Initializes the ClassData.
73 *
74 * @param me The constant pool reference to this class
75 * @param father The constant pool reference to the super class
76 * @param interfaces A list of interfaces that this class implements
77 */
78 public final void init(int access, ConstantPool.ConstCell me, ConstantPool.ConstCell father, ArrayList<Argument> interfaces) {
79 this.access = access;
80
81 // normalize the modifiers to access flags
82 if (Modifiers.hasPseudoMod(access)) {
83 createPseudoMod();
84 }
85
86 this.me = me;
87 if (father == null) {
88 father = pool.FindCellClassByName("java/lang/Object");
89 }
90 this.father = father;
91 this.interfaces = interfaces;
92 // Set default class file version if it is not set.
93 cfv.initClassDefaults();
94 }
95
96 public final void initAsModule() {
97 this.access = RuntimeConstants.ACC_MODULE;
98 // this_class" module-info
99 this.me = pool.FindCellClassByName("module-info");
100 // super_class: zero
101 this.father = new ConstantPool.ConstCell(0);
102 cfv.initModuleDefaults();
103 }
104
105 /**
106 * canonical default constructor
107 *
108 * @param env The error reporting environment.
109 * @param cfv The class file version that this class file supports.
110 */
111 public ClassData(Environment env, CFVersion cfv) {
112 super(null, 0); // for a class, these get inited in the super - later.
113 cls = this;
114
115 this.env = env;
116 this.cfv = cfv;
117
118 pool = new ConstantPool(env);
119 cdos = new CDOutputStream();
120 }
121
122 /**
123 * Predicate that describes if this class has an access flag indicating that it is an
124 * interface.
125 *
126 * @return True if the classes access flag indicates it is an interface.
127 */
128 public final boolean isInterface() {
129 return Modifiers.isInterface(access);
130 }
131
132 /*
133 * After a constant pool has been explicitly declared,
134 * this method links the Constant_InvokeDynamic Constant_Dynamic
135 * constants with any bootstrap methods that they index in the
136 * Bootstrap Methods Attribute
137 */
138 protected void relinkBootstrapMethods() {
139 if (bootstrapMethodsAttr == null) {
140 return;
141 }
142
143 env.traceln("relinkBootstrapMethods");
144
145 for (ConstantPool.ConstCell cell : pool) {
146 ConstantPool.ConstValue ref = null;
147 if (cell != null) {
148 ref = cell.ref;
149 }
150 if (ref != null
151 && (ref.tag == ConstType.CONSTANT_INVOKEDYNAMIC || ref.tag == ConstType.CONSTANT_DYNAMIC)) {
152 // Find only the Constant
153 ConstantPool.ConstValue_IndyOrCondyPair refval = (ConstantPool.ConstValue_IndyOrCondyPair) ref;
154 BootstrapMethodData bsmdata = refval.bsmData;
155 // only care about BSM Data that were placeholders
156 if (bsmdata != null && bsmdata.isPlaceholder()) {
157 // find the real BSM Data at the index
158 int bsmindex = bsmdata.placeholder_index;
159 if (bsmindex < 0 || bsmindex > bootstrapMethodsAttr.size()) {
160 // bad BSM index --
161 // give a warning, but place the index in the arg anyway
162 env.traceln("Warning: (ClassData.relinkBootstrapMethods()): Bad bootstrapMethods index: " + bsmindex);
163 // env.error("const.bsmindex", bsmindex);
164 bsmdata.arg = bsmindex;
165 } else {
166
167 BootstrapMethodData realbsmdata = bootstrapMethodsAttr.get(bsmindex);
168 // make the IndyPairs BSM Data point to the one from the attribute
169 refval.bsmData = realbsmdata;
170 }
171 }
172 }
173 }
174 }
175
176 protected void numberBootstrapMethods() {
177 env.traceln("Numbering Bootstrap Methods");
178 if (bootstrapMethodsAttr == null) {
179 return;
180 }
181
182 int index = 0;
183 for (BootstrapMethodData data : bootstrapMethodsAttr) {
184 data.arg = index++;
185 }
186 }
187
188 // API
189 // Record
190 public RecordData setRecord(int where) {
191 if( recordAttributeExists() ) {
192 env.error(where, "warn.record.repeated");
193 }
194 this.recordData = new RecordData(cls);
195 return this.recordData;
196 }
197
198 /**
199 * Rejects a record: removes the record attribute if there are no components
200 */
201 public void rejectRecord() {
202 this.recordData = null;
203 }
204
205 // Field
206 public ConstantPool.ConstValue_Pair mkNape(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) {
207 return new ConstantPool.ConstValue_Pair(ConstType.CONSTANT_NAMEANDTYPE, name, sig);
208 }
209
210 public ConstantPool.ConstValue_Pair mkNape(String name, String sig) {
211 return mkNape(pool.FindCellAsciz(name), pool.FindCellAsciz(sig));
212 }
213
214 public FieldData addFieldIfAbsent(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig) {
215 ConstantPool.ConstValue_Pair nape = mkNape(name, sig);
216 env.traceln(" [ClassData.addFieldIfAbsent]: #" + nape.left.arg + ":#" + nape.right.arg);
217 FieldData fd = getField(nape);
218 if( fd == null ) {
219 env.traceln(" [ClassData.addFieldIfAbsent]: new field.");
220 fd = addField(access,nape);
221 }
222 return fd;
223 }
224
225 private FieldData getField(ConstantPool.ConstValue_Pair nape) {
226 for (FieldData fd : fields) {
227 if( fd.getNameDesc().equals(nape) ) {
228 return fd;
229 }
230 }
231 return null;
232 }
233
234 public FieldData addField(int access, ConstantPool.ConstValue_Pair nape) {
235 env.traceln(" [ClassData.addField]: #" + nape.left.arg + ":#" + nape.right.arg);
236 FieldData res = new FieldData(this, access, nape);
237 fields.add(res);
238 return res;
239 }
240
241 public FieldData addField(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig) {
242 return addField(access, mkNape(name, sig));
243 }
244
245 public FieldData addField(int access, String name, String type) {
246 return addField(access, pool.FindCellAsciz(name), pool.FindCellAsciz(type));
247 }
248
249 public ConstantPool.ConstCell LocalFieldRef(FieldData field) {
250 return pool.FindCell(ConstType.CONSTANT_FIELD, me, pool.FindCell(field.getNameDesc()));
251 }
252
253 public ConstantPool.ConstCell LocalFieldRef(ConstantPool.ConstValue nape) {
254 return pool.FindCell(ConstType.CONSTANT_FIELD, me, pool.FindCell(nape));
255 }
256
257 public ConstantPool.ConstCell LocalFieldRef(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) {
258 return LocalFieldRef(mkNape(name, sig));
259 }
260
261 public ConstantPool.ConstCell LocalFieldRef(String name, String sig) {
262 return LocalFieldRef(pool.FindCellAsciz(name), pool.FindCellAsciz(sig));
263 }
264
265 MethodData curMethod;
266
267 public MethodData StartMethod(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig, ArrayList exc_table) {
268 EndMethod();
269 env.traceln(" [ClassData.StartMethod]: #" + name.arg + ":#" + sig.arg);
270 curMethod = new MethodData(this, access, name, sig, exc_table);
271 methods.add(curMethod);
272 return curMethod;
273 }
274
275 public void EndMethod() {
276 curMethod = null;
277 }
278
279 public ConstantPool.ConstCell LocalMethodRef(ConstantPool.ConstValue nape) {
280 return pool.FindCell(ConstType.CONSTANT_METHOD, me, pool.FindCell(nape));
281 }
282
283 public ConstantPool.ConstCell LocalMethodRef(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) {
284 return LocalMethodRef(mkNape(name, sig));
285 }
286
287 void addLocVarData(int opc, Argument arg) {
288 }
289
290 public void addInnerClass(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell innerClass, ConstantPool.ConstCell outerClass) {
291 env.traceln("addInnerClass (with indexes: Name (" + name.toString() + "), Inner (" + innerClass.toString() + "), Outer (" + outerClass.toString() + ").");
292 if (innerClasses == null) {
293 innerClasses = new DataVectorAttr<>(this, AttrTag.ATT_InnerClasses.parsekey());
294 }
295 innerClasses.add(new InnerClassData(access, name, innerClass, outerClass));
296 }
297
298 public void addBootstrapMethod(BootstrapMethodData bsmData) {
299 env.traceln("addBootstrapMethod");
300 if (bootstrapMethodsAttr == null) {
301 bootstrapMethodsAttr = new DataVectorAttr<>(this, AttrTag.ATT_BootstrapMethods.parsekey());
302 }
303 bootstrapMethodsAttr.add(bsmData);
304 }
305
306 public void addNestHost(ConstantPool.ConstCell hostClass) {
307 env.traceln("addNestHost");
308 nestHostAttr = new CPXAttr(this, AttrTag.ATT_NestHost.parsekey(), hostClass);
309 }
310
311 public void addNestMembers(List<ConstantPool.ConstCell> classes) {
312 env.traceln("addNestMembers");
313 nestMembersAttr = new NestMembersAttr(this, classes);
314 }
315
316 public void addPermittedSubclasses(List<ConstantPool.ConstCell> classes) {
317 env.traceln("addPermittedSubclasses");
318 permittedSubclassesAttr = new PermittedSubclassesAttr(this, classes);
319 }
320
321 public void addPreloads(List<ConstantPool.ConstCell> classes) {
322 env.traceln("addPreloads");
323 preloadAttr = new PreloadAttr(this, classes);
324 }
325
326 public void endClass() {
327 sourceFileNameAttr = new CPXAttr(this,
328 AttrTag.ATT_SourceFile.parsekey(),
329 pool.FindCellAsciz(env.getSimpleInputFileName()));
330 pool.NumberizePool();
331 pool.CheckGlobals();
332 numberBootstrapMethods();
333 try {
334 me = pool.uncheckedGetCell(me.arg);
335 env.traceln("me=" + me);
336 ConstantPool.ConstValue_Cell me_value = (ConstantPool.ConstValue_Cell) me.ref;
337 ConstantPool.ConstCell ascicell = me_value.cell;
338 env.traceln("ascicell=" + ascicell);
339 ConstantPool.ConstValue_String me_str = (ConstantPool.ConstValue_String) ascicell.ref;
340 myClassName = me_str.value;
341 env.traceln("-------------------");
342 env.traceln("-- Constant Pool --");
343 env.traceln("-------------------");
344 pool.printPool();
345 env.traceln("-------------------");
346 env.traceln(" ");
347 env.traceln(" ");
348 env.traceln("-------------------");
349 env.traceln("-- Inner Classes --");
350 env.traceln("-------------------");
351 printInnerClasses();
352
353 } catch (Throwable e) {
354 env.traceln("check name:" + e);
355 env.error("no.classname");
356 e.printStackTrace();
357 }
358 }
359
360 public void endModule(ModuleAttr moduleAttr) {
361 moduleAttribute = moduleAttr.build();
362 pool.NumberizePool();
363 pool.CheckGlobals();
364 myClassName = "module-info";
365 }
366
367 private void printInnerClasses() {
368 if (innerClasses != null) {
369 int i = 1;
370 for (InnerClassData entry : innerClasses) {
371 env.trace(" InnerClass[" + i + "]: (" + Modifiers.toString(entry.access, CF_Context.CTX_INNERCLASS) + "]), ");
372 env.trace("Name: " + entry.name.toString() + " ");
373 env.trace("IC_info: " + entry.innerClass.toString() + " ");
374 env.trace("OC_info: " + entry.outerClass.toString() + " ");
375 env.traceln(" ");
376 i += 1;
377 }
378 } else {
379 env.traceln("<< NO INNER CLASSES >>");
380 }
381
382 }
383
384 public void write(CheckedDataOutputStream out) throws IOException {
385
386 // Write the header
387 out.writeInt(JAVA_MAGIC);
388 out.writeShort(cfv.minor_version());
389 out.writeShort(cfv.major_version());
390
391 pool.write(out);
392 out.writeShort(access); // & MM_CLASS; // Q
393 out.writeShort(me.arg);
394 out.writeShort(father.arg);
395
396 // Write the interface names
397 if (interfaces != null) {
398 out.writeShort(interfaces.size());
399 for (Argument intf : interfaces) {
400 out.writeShort(intf.arg);
401 }
402 } else {
403 out.writeShort(0);
404 }
405
406 // Write the fields
407 if (fields != null) {
408 out.writeShort(fields.size());
409 for (FieldData field : fields) {
410 field.write(out);
411 }
412 } else {
413 out.writeShort(0);
414 }
415
416 // Write the methods
417 if (methods != null) {
418 out.writeShort(methods.size());
419 for (MethodData method : methods) {
420 method.write(out);
421 }
422 } else {
423 out.writeShort(0);
424 }
425
426 // Write the attributes
427 DataVector attrs = getAttrVector();
428 attrs.write(out);
429 } // end ClassData.write()
430
431 @Override
432 protected DataVector getAttrVector() {
433 DataVector attrs = new DataVector();
434 if( moduleAttribute != null ) {
435 if (annotAttrVis != null)
436 attrs.add(annotAttrVis);
437 if (annotAttrInv != null)
438 attrs.add(annotAttrInv);
439 attrs.add(moduleAttribute);
440 } else {
441 attrs.add(sourceFileNameAttr);
442 // JEP 359 since class file 58.65535
443 if( recordData != null ) {
444 attrs.add(recordData);
445 }
446 if (innerClasses != null)
447 attrs.add(innerClasses);
448 if (syntheticAttr != null)
449 attrs.add(syntheticAttr);
450 if (deprecatedAttr != null)
451 attrs.add(deprecatedAttr);
452 if (annotAttrVis != null)
453 attrs.add(annotAttrVis);
454 if (annotAttrInv != null)
455 attrs.add(annotAttrInv);
456 if (type_annotAttrVis != null)
457 attrs.add(type_annotAttrVis);
458 if (type_annotAttrInv != null)
459 attrs.add(type_annotAttrInv);
460 if (bootstrapMethodsAttr != null)
461 attrs.add(bootstrapMethodsAttr);
462 // since class version 55.0
463 if(nestHostAttributeExists())
464 attrs.add(nestHostAttr);
465 if(nestMembersAttributesExist())
466 attrs.add(nestMembersAttr);
467 // since class version 59.65535 (JEP 360)
468 if ( permittedSubclassesAttributesExist() )
469 attrs.add(permittedSubclassesAttr);
470 if (preloadAttributeExists())
471 attrs.add(preloadAttr);
472 }
473 return attrs;
474 }
475
476 static char fileSeparator; //=System.getProperty("file.separator");
477
478 /**
479 * Writes to the directory passed with -d option
480 */
481 public void write(File destdir) throws IOException {
482 File outfile;
483 if (destdir == null) {
484 int startofname = myClassName.lastIndexOf("/");
485 if (startofname != -1) {
486 myClassName = myClassName.substring(startofname + 1);
487 }
488 outfile = new File(myClassName + fileExtension);
489 } else {
490 env.traceln("writing -d " + destdir.getPath());
491 if (fileSeparator == 0) {
492 fileSeparator = System.getProperty("file.separator").charAt(0);
493 }
494 if (fileSeparator != '/') {
495 myClassName = myClassName.replace('/', fileSeparator);
496 }
497 outfile = new File(destdir, myClassName + fileExtension);
498 File outdir = new File(outfile.getParent());
499 if (!outdir.exists() && !outdir.mkdirs()) {
500 env.error("cannot.write", outdir.getPath());
501 return;
502 }
503 }
504
505 DataOutputStream dos = new DataOutputStream(
506 new BufferedOutputStream(new FileOutputStream(outfile)));
507 cdos.setDataOutputStream(dos);
508 try {
509 write(cdos);
510 } finally {
511 dos.close();
512 }
513 } // end write()
514
515 public void setByteLimit(int bytelimit) {
516 cdos.enable();
517 cdos.setLimit(bytelimit);
518 }
519
520 public boolean nestHostAttributeExists() {
521 return nestHostAttr != null;
522 }
523
524 public boolean nestMembersAttributesExist() { return nestMembersAttr != null; }
525
526 public boolean permittedSubclassesAttributesExist() { return permittedSubclassesAttr != null; }
527
528 public boolean recordAttributeExists() { return recordData != null; }
529
530 public boolean preloadAttributeExists() { return preloadAttr != null; }
531
532 /**
533 * This is a wrapper for DataOutputStream, used for debugging purposes. it allows
534 * writing the byte-stream of a class up to a given byte number.
535 */
536 static private class CDOutputStream implements CheckedDataOutputStream {
537
538 private int bytelimit;
539 private DataOutputStream dos;
540 public boolean enabled = false;
541
542 public CDOutputStream() {
543 dos = null;
544 }
545
546 public CDOutputStream(OutputStream out) {
547 setOutputStream(out);
548 }
549
550 public final void setOutputStream(OutputStream out) {
551 dos = new DataOutputStream(out);
552 }
553
554 public void setDataOutputStream(DataOutputStream dos) {
555 this.dos = dos;
556 }
557
558 public void setLimit(int lim) {
559 bytelimit = lim;
560 }
561
562 public void enable() {
563 enabled = true;
564 }
565
566 private synchronized void check(String loc) throws IOException {
567 if (enabled && dos.size() >= bytelimit) {
568 throw new IOException(loc);
569 }
570 }
571
572 @Override
573 public synchronized void write(int b) throws IOException {
574 dos.write(b);
575 check("Writing byte: " + b);
576 }
577
578 @Override
579 public synchronized void write(byte b[], int off, int len) throws IOException {
580 dos.write(b, off, len);
581 check("Writing byte-array: " + b);
582 }
583
584 @Override
585 public final void writeBoolean(boolean v) throws IOException {
586 dos.writeBoolean(v);
587 check("Writing writeBoolean: " + (v ? "true" : "false"));
588 }
589
590 @Override
591 public final void writeByte(int v) throws IOException {
592 dos.writeByte(v);
593 check("Writing writeByte: " + v);
594 }
595
596 @Override
597 public void writeShort(int v) throws IOException {
598 dos.writeShort(v);
599 check("Writing writeShort: " + v);
600 }
601
602 @Override
603 public void writeChar(int v) throws IOException {
604 dos.writeChar(v);
605 check("Writing writeChar: " + v);
606 }
607
608 @Override
609 public void writeInt(int v) throws IOException {
610 dos.writeInt(v);
611 check("Writing writeInt: " + v);
612 }
613
614 @Override
615 public void writeLong(long v) throws IOException {
616 dos.writeLong(v);
617 check("Writing writeLong: " + v);
618 }
619
620 @Override
621 public void writeFloat(float v) throws IOException {
622 dos.writeFloat(v);
623 check("Writing writeFloat: " + v);
624 }
625
626 @Override
627 public void writeDouble(double v) throws IOException {
628 dos.writeDouble(v);
629 check("Writing writeDouble: " + v);
630 }
631
632 @Override
633 public void writeBytes(String s) throws IOException {
634 dos.writeBytes(s);
635 check("Writing writeBytes: " + s);
636 }
637
638 @Override
639 public void writeChars(String s) throws IOException {
640 dos.writeChars(s);
641 check("Writing writeChars: " + s);
642 }
643
644 @Override
645 public void writeUTF(String s) throws IOException {
646 dos.writeUTF(s);
647 check("Writing writeUTF: " + s);
648 }
649 }
650 }// end class ClassData