/* * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package org.openjdk.asmtools.jasm; import java.io.*; import java.util.ArrayList; import java.util.List; import static org.openjdk.asmtools.jasm.Tables.*; /** * ClassData * * This is the main data structure for representing parsed class data. This structure * renders directly to a class file. * */ class ClassData extends MemberData { /* ClassData Fields */ CFVersion cfv; ConstantPool.ConstCell me, father; String myClassName; AttrData sourceFileNameAttr; ArrayList interfaces; ArrayList fields = new ArrayList<>(); ArrayList methods = new ArrayList<>(); DataVectorAttr innerClasses = null; DataVectorAttr bootstrapMethodsAttr = null; // JEP 181 - NestHost, NestMembers attributes since class version 55.0 CPXAttr nestHostAttr; NestMembersAttr nestMembersAttr; // JEP 359 - Record attribute since class file 58.65535 private RecordData recordData; // JEP 360 - PermittedSubtypes attribute since class file 59.65535 private PermittedTypesAttr permittedTypesAttr; ModuleAttr moduleAttribute = null; Environment env; protected ConstantPool pool; private static final String DEFAULT_EXTENSION = ".class"; String fileExtension = DEFAULT_EXTENSION; public CDOutputStream cdos; /** * Initializes the ClassData. * * @param me The constant pool reference to this class * @param father The constant pool reference to the super class * @param interfaces A list of interfaces that this class implements */ public final void init(int access, ConstantPool.ConstCell me, ConstantPool.ConstCell father, ArrayList interfaces) { this.access = access; // normalize the modifiers to access flags if (Modifiers.hasPseudoMod(access)) { createPseudoMod(); } this.me = me; if (father == null) { father = pool.FindCellClassByName("java/lang/Object"); } this.father = father; this.interfaces = interfaces; // Set default class file version if it is not set. cfv.initClassDefaults(); } public final void initAsModule() { this.access = RuntimeConstants.ACC_MODULE; // this_class" module-info this.me = pool.FindCellClassByName("module-info"); // super_class: zero this.father = new ConstantPool.ConstCell(0); cfv.initModuleDefaults(); } /** * canonical default constructor * * @param env The error reporting environment. * @param cfv The class file version that this class file supports. */ public ClassData(Environment env, CFVersion cfv) { super(null, 0); // for a class, these get inited in the super - later. cls = this; this.env = env; this.cfv = cfv; pool = new ConstantPool(env); cdos = new CDOutputStream(); } /** * Predicate that describes if this class has an access flag indicating that it is an * interface. * * @return True if the classes access flag indicates it is an interface. */ public final boolean isInterface() { return Modifiers.isInterface(access); } /* * After a constant pool has been explicitly declared, * this method links the Constant_InvokeDynamic Constant_Dynamic * constants with any bootstrap methods that they index in the * Bootstrap Methods Attribute */ protected void relinkBootstrapMethods() { if (bootstrapMethodsAttr == null) { return; } env.traceln("relinkBootstrapMethods"); for (ConstantPool.ConstCell cell : pool) { ConstantPool.ConstValue ref = null; if (cell != null) { ref = cell.ref; } if (ref != null && (ref.tag == ConstType.CONSTANT_INVOKEDYNAMIC || ref.tag == ConstType.CONSTANT_DYNAMIC)) { // Find only the Constant ConstantPool.ConstValue_IndyOrCondyPair refval = (ConstantPool.ConstValue_IndyOrCondyPair) ref; BootstrapMethodData bsmdata = refval.bsmData; // only care about BSM Data that were placeholders if (bsmdata != null && bsmdata.isPlaceholder()) { // find the real BSM Data at the index int bsmindex = bsmdata.placeholder_index; if (bsmindex < 0 || bsmindex > bootstrapMethodsAttr.size()) { // bad BSM index -- // give a warning, but place the index in the arg anyway env.traceln("Warning: (ClassData.relinkBootstrapMethods()): Bad bootstrapMethods index: " + bsmindex); // env.error("const.bsmindex", bsmindex); bsmdata.arg = bsmindex; } else { BootstrapMethodData realbsmdata = bootstrapMethodsAttr.get(bsmindex); // make the IndyPairs BSM Data point to the one from the attribute refval.bsmData = realbsmdata; } } } } } protected void numberBootstrapMethods() { env.traceln("Numbering Bootstrap Methods"); if (bootstrapMethodsAttr == null) { return; } int index = 0; for (BootstrapMethodData data : bootstrapMethodsAttr) { data.arg = index++; } } // API // Record public RecordData setRecord(int where) { if( recordAttributeExists() ) { env.error(where, "warn.record.repeated"); } this.recordData = new RecordData(cls); return this.recordData; } /** * Rejects a record: removes the record attribute if there are no components */ public void rejectRecord() { this.recordData = null; } // Field public ConstantPool.ConstValue_Pair mkNape(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { return new ConstantPool.ConstValue_Pair(ConstType.CONSTANT_NAMEANDTYPE, name, sig); } public ConstantPool.ConstValue_Pair mkNape(String name, String sig) { return mkNape(pool.FindCellAsciz(name), pool.FindCellAsciz(sig)); } public FieldData addFieldIfAbsent(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { ConstantPool.ConstValue_Pair nape = mkNape(name, sig); env.traceln(" [ClassData.addFieldIfAbsent]: #" + nape.left.arg + ":#" + nape.right.arg); FieldData fd = getField(nape); if( fd == null ) { env.traceln(" [ClassData.addFieldIfAbsent]: new field."); fd = addField(access,nape); } return fd; } private FieldData getField(ConstantPool.ConstValue_Pair nape) { for (FieldData fd : fields) { if( fd.getNameDesc().equals(nape) ) { return fd; } } return null; } public FieldData addField(int access, ConstantPool.ConstValue_Pair nape) { env.traceln(" [ClassData.addField]: #" + nape.left.arg + ":#" + nape.right.arg); FieldData res = new FieldData(this, access, nape); fields.add(res); return res; } public FieldData addField(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { return addField(access, mkNape(name, sig)); } public FieldData addField(int access, String name, String type) { return addField(access, pool.FindCellAsciz(name), pool.FindCellAsciz(type)); } public ConstantPool.ConstCell LocalFieldRef(FieldData field) { return pool.FindCell(ConstType.CONSTANT_FIELD, me, pool.FindCell(field.getNameDesc())); } public ConstantPool.ConstCell LocalFieldRef(ConstantPool.ConstValue nape) { return pool.FindCell(ConstType.CONSTANT_FIELD, me, pool.FindCell(nape)); } public ConstantPool.ConstCell LocalFieldRef(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { return LocalFieldRef(mkNape(name, sig)); } public ConstantPool.ConstCell LocalFieldRef(String name, String sig) { return LocalFieldRef(pool.FindCellAsciz(name), pool.FindCellAsciz(sig)); } MethodData curMethod; public MethodData StartMethod(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig, ArrayList exc_table) { EndMethod(); env.traceln(" [ClassData.StartMethod]: #" + name.arg + ":#" + sig.arg); curMethod = new MethodData(this, access, name, sig, exc_table); methods.add(curMethod); return curMethod; } public void EndMethod() { curMethod = null; } public ConstantPool.ConstCell LocalMethodRef(ConstantPool.ConstValue nape) { return pool.FindCell(ConstType.CONSTANT_METHOD, me, pool.FindCell(nape)); } public ConstantPool.ConstCell LocalMethodRef(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { return LocalMethodRef(mkNape(name, sig)); } void addLocVarData(int opc, Argument arg) { } public void addInnerClass(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell innerClass, ConstantPool.ConstCell outerClass) { env.traceln("addInnerClass (with indexes: Name (" + name.toString() + "), Inner (" + innerClass.toString() + "), Outer (" + outerClass.toString() + ")."); if (innerClasses == null) { innerClasses = new DataVectorAttr<>(this, AttrTag.ATT_InnerClasses.parsekey()); } innerClasses.add(new InnerClassData(access, name, innerClass, outerClass)); } public void addBootstrapMethod(BootstrapMethodData bsmData) { env.traceln("addBootstrapMethod"); if (bootstrapMethodsAttr == null) { bootstrapMethodsAttr = new DataVectorAttr<>(this, AttrTag.ATT_BootstrapMethods.parsekey()); } bootstrapMethodsAttr.add(bsmData); } public void addNestHost(ConstantPool.ConstCell hostClass) { env.traceln("addNestHost"); nestHostAttr = new CPXAttr(this, AttrTag.ATT_NestHost.parsekey(), hostClass); } public void addNestMembers(List classes) { env.traceln("addNestMembers"); nestMembersAttr = new NestMembersAttr(this, classes); } public void addPermittedSubtypes(List classes) { env.traceln("addPermittedSubtypes"); permittedTypesAttr = new PermittedTypesAttr(this, classes); } public void endClass() { sourceFileNameAttr = new CPXAttr(this, AttrTag.ATT_SourceFile.parsekey(), pool.FindCellAsciz(env.getSimpleInputFileName())); pool.NumberizePool(); pool.CheckGlobals(); numberBootstrapMethods(); try { me = pool.uncheckedGetCell(me.arg); env.traceln("me=" + me); ConstantPool.ConstValue_Cell me_value = (ConstantPool.ConstValue_Cell) me.ref; ConstantPool.ConstCell ascicell = me_value.cell; env.traceln("ascicell=" + ascicell); ConstantPool.ConstValue_String me_str = (ConstantPool.ConstValue_String) ascicell.ref; myClassName = me_str.value; env.traceln("-------------------"); env.traceln("-- Constant Pool --"); env.traceln("-------------------"); pool.printPool(); env.traceln("-------------------"); env.traceln(" "); env.traceln(" "); env.traceln("-------------------"); env.traceln("-- Inner Classes --"); env.traceln("-------------------"); printInnerClasses(); } catch (Throwable e) { env.traceln("check name:" + e); env.error("no.classname"); e.printStackTrace(); } } public void endModule(ModuleAttr moduleAttr) { moduleAttribute = moduleAttr.build(); pool.NumberizePool(); pool.CheckGlobals(); myClassName = "module-info"; } private void printInnerClasses() { if (innerClasses != null) { int i = 1; for (InnerClassData entry : innerClasses) { env.trace(" InnerClass[" + i + "]: (" + Modifiers.toString(entry.access, CF_Context.CTX_INNERCLASS) + "]), "); env.trace("Name: " + entry.name.toString() + " "); env.trace("IC_info: " + entry.innerClass.toString() + " "); env.trace("OC_info: " + entry.outerClass.toString() + " "); env.traceln(" "); i += 1; } } else { env.traceln("<< NO INNER CLASSES >>"); } } public void write(CheckedDataOutputStream out) throws IOException { // Write the header out.writeInt(JAVA_MAGIC); out.writeShort(cfv.minor_version()); out.writeShort(cfv.major_version()); pool.write(out); out.writeShort(access); // & MM_CLASS; // Q out.writeShort(me.arg); out.writeShort(father.arg); // Write the interface names if (interfaces != null) { out.writeShort(interfaces.size()); for (Argument intf : interfaces) { out.writeShort(intf.arg); } } else { out.writeShort(0); } // Write the fields if (fields != null) { out.writeShort(fields.size()); for (FieldData field : fields) { field.write(out); } } else { out.writeShort(0); } // Write the methods if (methods != null) { out.writeShort(methods.size()); for (MethodData method : methods) { method.write(out); } } else { out.writeShort(0); } // Write the attributes DataVector attrs = getAttrVector(); attrs.write(out); } // end ClassData.write() @Override protected DataVector getAttrVector() { DataVector attrs = new DataVector(); if( moduleAttribute != null ) { if (annotAttrVis != null) attrs.add(annotAttrVis); if (annotAttrInv != null) attrs.add(annotAttrInv); attrs.add(moduleAttribute); } else { attrs.add(sourceFileNameAttr); // JEP 359 since class file 58.65535 if( recordData != null ) { attrs.add(recordData); } if (innerClasses != null) attrs.add(innerClasses); if (syntheticAttr != null) attrs.add(syntheticAttr); if (deprecatedAttr != null) attrs.add(deprecatedAttr); if (annotAttrVis != null) attrs.add(annotAttrVis); if (annotAttrInv != null) attrs.add(annotAttrInv); if (type_annotAttrVis != null) attrs.add(type_annotAttrVis); if (type_annotAttrInv != null) attrs.add(type_annotAttrInv); if (bootstrapMethodsAttr != null) attrs.add(bootstrapMethodsAttr); // since class version 55.0 if(nestHostAttributeExists()) attrs.add(nestHostAttr); if(nestMembersAttributesExist()) attrs.add(nestMembersAttr); // since class version 59.65535 (JEP 360) if ( permittedSubtypesAttributesExist() ) attrs.add(permittedTypesAttr); } return attrs; } static char fileSeparator; //=System.getProperty("file.separator"); /** * Writes to the directory passed with -d option */ public void write(File destdir) throws IOException { File outfile; if (destdir == null) { int startofname = myClassName.lastIndexOf("/"); if (startofname != -1) { myClassName = myClassName.substring(startofname + 1); } outfile = new File(myClassName + fileExtension); } else { env.traceln("writing -d " + destdir.getPath()); if (fileSeparator == 0) { fileSeparator = System.getProperty("file.separator").charAt(0); } if (fileSeparator != '/') { myClassName = myClassName.replace('/', fileSeparator); } outfile = new File(destdir, myClassName + fileExtension); File outdir = new File(outfile.getParent()); if (!outdir.exists() && !outdir.mkdirs()) { env.error("cannot.write", outdir.getPath()); return; } } DataOutputStream dos = new DataOutputStream( new BufferedOutputStream(new FileOutputStream(outfile))); cdos.setDataOutputStream(dos); try { write(cdos); } finally { dos.close(); } } // end write() public void setByteLimit(int bytelimit) { cdos.enable(); cdos.setLimit(bytelimit); } public boolean nestHostAttributeExists() { return nestHostAttr != null; } public boolean nestMembersAttributesExist() { return nestMembersAttr != null; } public boolean permittedSubtypesAttributesExist() { return permittedTypesAttr != null; } public boolean recordAttributeExists() { return recordData != null; } /** * This is a wrapper for DataOutputStream, used for debugging purposes. it allows * writing the byte-stream of a class up to a given byte number. */ static private class CDOutputStream implements CheckedDataOutputStream { private int bytelimit; private DataOutputStream dos; public boolean enabled = false; public CDOutputStream() { dos = null; } public CDOutputStream(OutputStream out) { setOutputStream(out); } public final void setOutputStream(OutputStream out) { dos = new DataOutputStream(out); } public void setDataOutputStream(DataOutputStream dos) { this.dos = dos; } public void setLimit(int lim) { bytelimit = lim; } public void enable() { enabled = true; } private synchronized void check(String loc) throws IOException { if (enabled && dos.size() >= bytelimit) { throw new IOException(loc); } } @Override public synchronized void write(int b) throws IOException { dos.write(b); check("Writing byte: " + b); } @Override public synchronized void write(byte b[], int off, int len) throws IOException { dos.write(b, off, len); check("Writing byte-array: " + b); } @Override public final void writeBoolean(boolean v) throws IOException { dos.writeBoolean(v); check("Writing writeBoolean: " + (v ? "true" : "false")); } @Override public final void writeByte(int v) throws IOException { dos.writeByte(v); check("Writing writeByte: " + v); } @Override public void writeShort(int v) throws IOException { dos.writeShort(v); check("Writing writeShort: " + v); } @Override public void writeChar(int v) throws IOException { dos.writeChar(v); check("Writing writeChar: " + v); } @Override public void writeInt(int v) throws IOException { dos.writeInt(v); check("Writing writeInt: " + v); } @Override public void writeLong(long v) throws IOException { dos.writeLong(v); check("Writing writeLong: " + v); } @Override public void writeFloat(float v) throws IOException { dos.writeFloat(v); check("Writing writeFloat: " + v); } @Override public void writeDouble(double v) throws IOException { dos.writeDouble(v); check("Writing writeDouble: " + v); } @Override public void writeBytes(String s) throws IOException { dos.writeBytes(s); check("Writing writeBytes: " + s); } @Override public void writeChars(String s) throws IOException { dos.writeChars(s); check("Writing writeChars: " + s); } @Override public void writeUTF(String s) throws IOException { dos.writeUTF(s); check("Writing writeUTF: " + s); } } }// end class ClassData