1 /*
   2  * Copyright (c) 2008, 2015, 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 
  24 /*
  25  * @test
  26  * @bug 6622260
  27  * @summary javap prints negative bytes incorrectly in hex
  28  * @modules jdk.compiler
  29  */
  30 
  31 import java.io.*;
  32 
  33 public class T6622260 {
  34     public static void main(String[] args) throws Exception {
  35         new T6622260().run();
  36     }
  37 
  38     public void run() throws IOException {
  39         File javaFile = writeTestFile();
  40         File classFile = compileTestFile(javaFile);
  41         modifyClassFile(classFile);
  42         String output = javap(classFile);
  43         verify(output);
  44     }
  45 
  46     File writeTestFile() throws IOException {
  47         File f = new File("Test.java");
  48         PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(f)));
  49         out.println("@Deprecated class Test { int f; void m() { } }");
  50         out.close();
  51         return f;
  52     }
  53 
  54     File compileTestFile(File f) {
  55         int rc = com.sun.tools.javac.Main.compile(new String[] { f.getPath() });
  56         if (rc != 0)
  57             throw new Error("compilation failed. rc=" + rc);
  58         String path = f.getPath();
  59         return new File(path.substring(0, path.length() - 5) + ".class");
  60     }
  61 
  62     void modifyClassFile(File f) throws IOException {
  63         String newAttributeName = "NonstandardAttribute";
  64         byte[] newAttributeData = { 0, 1, 2, 127, (byte)128, (byte)129, (byte)254, (byte)255 };
  65 
  66         DataInputStream in = new DataInputStream(new FileInputStream(f));
  67         byte[] data = new byte[(int) f.length()];
  68         in.readFully(data);
  69         in.close();
  70 
  71         in = new DataInputStream(new ByteArrayInputStream(data));
  72         in.skipBytes(4); // magic
  73         in.skipBytes(2); // minor
  74         in.skipBytes(2); // minor
  75 
  76         int constantPoolPos = data.length - in.available();
  77         int constant_pool_count = skipConstantPool(in);
  78 
  79         int flagsPos = data.length - in.available();
  80         in.skipBytes(2); // access_flags
  81         in.skipBytes(2); // this_class
  82         in.skipBytes(2); // super_class
  83 
  84         int interfaces_count = in.readUnsignedShort();
  85         in.skipBytes(interfaces_count * 2);
  86 
  87         int field_count = in.readUnsignedShort();
  88         for (int i = 0; i < field_count; i++) {
  89             in.skipBytes(6); // access_flags, name_index, descriptor_index
  90             skipAttributes(in);
  91         }
  92 
  93         int method_count = in.readUnsignedShort();
  94         for (int i = 0; i < method_count; i++) {
  95             in.skipBytes(6); // access_flags, name_index, descriptor_index
  96             skipAttributes(in);
  97         }
  98 
  99         int classAttributesPos = data.length - in.available();
 100         int attributes_count = in.readUnsignedShort();
 101 
 102         f.renameTo(new File(f.getPath() + ".BAK"));
 103         DataOutputStream out = new DataOutputStream(new FileOutputStream(f));
 104 
 105         // copy head
 106         out.write(data, 0, constantPoolPos);
 107 
 108         // copy constant pool, adding in name of new attribute
 109         out.writeShort(constant_pool_count + 1);
 110         out.write(data, constantPoolPos + 2, flagsPos - constantPoolPos - 2);
 111         out.write(1); // CONSTANT_Utf8
 112         out.writeUTF(newAttributeName);
 113 
 114         // copy flags, class, superclass, interfaces, fields and methods
 115         out.write(data, flagsPos, classAttributesPos - flagsPos);
 116 
 117         // copy class attributes, adding in new attribute
 118         out.writeShort(attributes_count + 1);
 119         out.write(data, classAttributesPos + 2, data.length - classAttributesPos - 2);
 120         out.writeShort(constant_pool_count); // index of new attribute name
 121         out.writeInt(newAttributeData.length);
 122         out.write(newAttributeData);
 123         out.close();
 124     }
 125 
 126     int skipConstantPool(DataInputStream in) throws IOException {
 127         int constant_pool_count = in.readUnsignedShort();
 128         for (int i = 1; i < constant_pool_count; i++) {
 129             int tag = in.readUnsignedByte();
 130             switch (tag) {
 131             case  1: // CONSTANT_Utf8
 132                 int length = in.readUnsignedShort();
 133                 in.skipBytes(length); // bytes
 134                 break;
 135 
 136             case  3: // CONSTANT_Integer
 137             case  4: // CONSTANT_Float
 138                 in.skipBytes(4); // bytes
 139                 break;
 140 
 141             case  5: // CONSTANT_Long
 142             case  6: // CONSTANT_Double
 143                 in.skipBytes(8); // high_bytes, low_bytes
 144                 break;
 145 
 146             case  7: // CONSTANT_Class
 147                 in.skipBytes(2); // name_index
 148                 break;
 149 
 150             case  8: // CONSTANT_String
 151                 in.skipBytes(2); // string_index
 152                 break;
 153 
 154             case  9: // CONSTANT_FieldRef
 155             case 10: // CONSTANT_Methodref
 156             case 11: // CONSTANT_InterfaceMethodref
 157                 in.skipBytes(4); // class_index, name_and_type_index
 158                 break;
 159 
 160             case 12: // CONSTANT_NameAndType
 161                 in.skipBytes(4); // name_index, descriptor_index
 162                 break;
 163 
 164             default:
 165                 throw new Error("constant pool tag: " + tag);
 166             }
 167         }
 168         return constant_pool_count;
 169     }
 170 
 171     int skipAttributes(DataInputStream in) throws IOException {
 172         int attributes_count = in.readUnsignedShort();
 173         for (int i = 0; i < attributes_count; i++) {
 174             in.skipBytes(2); // attribute_name_index;
 175             int length = in.readInt();
 176             in.skipBytes(length); // info
 177         }
 178         return attributes_count;
 179     }
 180 
 181     String javap(File f) {
 182         StringWriter sw = new StringWriter();
 183         PrintWriter out = new PrintWriter(sw);
 184         int rc = com.sun.tools.javap.Main.run(new String[] { "-v", f.getPath() }, out);
 185         if (rc != 0)
 186             throw new Error("javap failed. rc=" + rc);
 187         out.close();
 188         return sw.toString();
 189     }
 190 
 191     void verify(String output) {
 192         System.out.println(output);
 193         output = output.substring(output.indexOf("Test.java"));
 194         if (output.indexOf("-") >= 0)
 195             throw new Error("- found in output");
 196         if (output.indexOf("FFFFFF") >= 0)
 197             throw new Error("FFFFFF found in output");
 198     }
 199 }