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