1 /*
2  * Copyright (c) 2007, 2019, 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 nsk.share.jdi.sde;
24 
25 import java.io.File;
26 import java.io.FileInputStream;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.UnsupportedEncodingException;
31 
32 import nsk.share.Consts;
33 
34 /*
35  * Inserts in class file 'SourceDebugExtension' attribute based on input .SMAP file.
36  */
37 public class InstallSDE {
38     static final String nameSDE = "SourceDebugExtension";
39 
40     private byte[] orig;
41 
42     private byte[] sdeAttr;
43 
44     private byte[] gen;
45 
46     private int origPos = 0;
47 
48     private int genPos = 0;
49 
50     private int sdeIndex;
51 
install(File inClassFile, File smapFile, File outClassFile, boolean verbose)52     public static void install(File inClassFile, File smapFile, File outClassFile, boolean verbose) throws IOException {
53         new InstallSDE(inClassFile, smapFile, outClassFile, verbose);
54     }
55 
install(byte[] aOrig, byte[] aSdeAttr, File outClassFile, boolean verbose)56     public static void install(byte[] aOrig, byte[] aSdeAttr, File outClassFile, boolean verbose) throws IOException {
57         new InstallSDE(aOrig, aSdeAttr, outClassFile, verbose);
58     }
59 
install(File inOutClassFile, File attrFile, boolean verbose)60     public static void install(File inOutClassFile, File attrFile, boolean verbose) throws IOException {
61         File tmpFile = new File(inOutClassFile.getPath() + "tmp-out");
62         File tmpInOutClassFile = new File(inOutClassFile.getPath() + "tmp-in");
63 
64         // Workaround delayed file deletes on Windows using a tmp file name
65         if (!inOutClassFile.renameTo(tmpInOutClassFile)) {
66             throw new IOException("inOutClassFile.renameTo(tmpInOutClassFile) failed");
67         }
68 
69         new InstallSDE(tmpInOutClassFile, attrFile, tmpFile, verbose);
70 
71         if (!tmpInOutClassFile.delete()) {
72             throw new IOException("tmpInOutClassFile.delete() failed");
73         }
74         if (!tmpFile.renameTo(inOutClassFile)) {
75             throw new IOException("tmpFile.renameTo(inOutClassFile) failed");
76         }
77     }
78 
abort(String msg)79     private static void abort(String msg) {
80         System.err.println(msg);
81         System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_FAILED);
82     }
83 
InstallSDE(File inClassFile, File attrFile, File outClassFile, boolean verbose)84     private InstallSDE(File inClassFile, File attrFile, File outClassFile, boolean verbose) throws IOException {
85         if (!inClassFile.exists()) {
86             abort("no such file: " + inClassFile);
87         }
88         if (!attrFile.exists()) {
89             abort("no such file: " + attrFile);
90         }
91 
92         // get the bytes
93         orig = readWhole(inClassFile);
94         sdeAttr = readWhole(attrFile);
95         gen = new byte[orig.length + sdeAttr.length + 100];
96 
97         // do it
98         addSDE(verbose);
99 
100         // write result
101         FileOutputStream outStream = new FileOutputStream(outClassFile);
102         outStream.write(gen, 0, genPos);
103         outStream.close();
104     }
105 
InstallSDE(byte[] aOrig, byte[] aSdeAttr, File outClassFile, boolean verbose)106     private InstallSDE(byte[] aOrig, byte[] aSdeAttr, File outClassFile, boolean verbose) throws IOException {
107         orig = aOrig;
108         sdeAttr = aSdeAttr;
109         gen = new byte[orig.length + sdeAttr.length + 100];
110 
111         // do it
112         addSDE(verbose);
113 
114         // write result
115         FileOutputStream outStream = new FileOutputStream(outClassFile);
116         outStream.write(gen, 0, genPos);
117         outStream.close();
118     }
119 
readWhole(File input)120     private byte[] readWhole(File input) throws IOException {
121         FileInputStream inStream = new FileInputStream(input);
122         try {
123                 return readWhole(inStream, (int) input.length());
124         } finally {
125                 inStream.close();
126         }
127     }
128 
readWhole(InputStream inStream, int len)129     private byte[] readWhole(InputStream inStream, int len) throws IOException {
130         byte[] bytes = new byte[len];
131 
132         if (inStream.read(bytes, 0, len) != len) {
133             abort("expected size: " + len);
134         }
135 
136         return bytes;
137     }
138 
addSDE(boolean verbose)139     private void addSDE(boolean verbose) throws UnsupportedEncodingException {
140         copy(4 + 2 + 2); // magic min/maj version
141         int constantPoolCountPos = genPos;
142         int constantPoolCount = readU2();
143         writeU2(constantPoolCount);
144         // copy old constant pool return index of SDE symbol, if found
145         sdeIndex = copyConstantPool(constantPoolCount, verbose);
146         if (sdeIndex < 0) {
147             // if "SourceDebugExtension" symbol not there add it
148             writeUtf8ForSDE();
149 
150             // increment the countantPoolCount
151             sdeIndex = constantPoolCount;
152             ++constantPoolCount;
153             randomAccessWriteU2(constantPoolCountPos, constantPoolCount);
154 
155             if (verbose) {
156                 System.out.println("SourceDebugExtension not found, installed at: " + sdeIndex);
157             }
158         } else {
159             if (verbose) {
160                 System.out.println("SourceDebugExtension found at: " + sdeIndex);
161             }
162         }
163         copy(2 + 2 + 2); // access, this, super
164         int interfaceCount = readU2();
165         writeU2(interfaceCount);
166         if (verbose) {
167             System.out.println("interfaceCount: " + interfaceCount);
168         }
169         copy(interfaceCount * 2);
170         copyMembers(verbose); // fields
171         copyMembers(verbose); // methods
172         int attrCountPos = genPos;
173         int attrCount = readU2();
174         writeU2(attrCount);
175         if (verbose) {
176             System.out.println("class attrCount: " + attrCount);
177         }
178         // copy the class attributes, return true if SDE attr found (not copied)
179         if (!copyAttrs(attrCount, verbose)) {
180             // we will be adding SDE and it isn't already counted
181             ++attrCount;
182             randomAccessWriteU2(attrCountPos, attrCount);
183             if (verbose) {
184                 System.out.println("class attrCount incremented");
185             }
186         }
187         writeAttrForSDE(sdeIndex);
188     }
189 
copyMembers(boolean verbose)190     private void copyMembers(boolean verbose) {
191         int count = readU2();
192         writeU2(count);
193         if (verbose) {
194             System.out.println("members count: " + count);
195         }
196         for (int i = 0; i < count; ++i) {
197             copy(6); // access, name, descriptor
198             int attrCount = readU2();
199             writeU2(attrCount);
200             if (verbose) {
201                 System.out.println("member attr count: " + attrCount);
202             }
203             copyAttrs(attrCount, verbose);
204         }
205     }
206 
copyAttrs(int attrCount, boolean verbose)207     private boolean copyAttrs(int attrCount, boolean verbose) {
208         boolean sdeFound = false;
209         for (int i = 0; i < attrCount; ++i) {
210             int nameIndex = readU2();
211             // don't write old SDE
212             if (nameIndex == sdeIndex) {
213                 sdeFound = true;
214                 if (verbose) {
215                     System.out.println("SDE attr found");
216                 }
217             } else {
218                 writeU2(nameIndex); // name
219                 int len = readU4();
220                 writeU4(len);
221                 copy(len);
222                 if (verbose) {
223                     System.out.println("attr len: " + len);
224                 }
225             }
226         }
227         return sdeFound;
228     }
229 
writeAttrForSDE(int index)230     private void writeAttrForSDE(int index) {
231         writeU2(index);
232         writeU4(sdeAttr.length);
233         for (int i = 0; i < sdeAttr.length; ++i) {
234             writeU1(sdeAttr[i]);
235         }
236     }
237 
randomAccessWriteU2(int pos, int val)238     private void randomAccessWriteU2(int pos, int val) {
239         int savePos = genPos;
240         genPos = pos;
241         writeU2(val);
242         genPos = savePos;
243     }
244 
readU1()245     private int readU1() {
246         return ((int) orig[origPos++]) & 0xFF;
247     }
248 
readU2()249     private int readU2() {
250         int res = readU1();
251         return (res << 8) + readU1();
252     }
253 
readU4()254     private int readU4() {
255         int res = readU2();
256         return (res << 16) + readU2();
257     }
258 
writeU1(int val)259     private void writeU1(int val) {
260         gen[genPos++] = (byte) val;
261     }
262 
writeU2(int val)263     private void writeU2(int val) {
264         writeU1(val >> 8);
265         writeU1(val & 0xFF);
266     }
267 
writeU4(int val)268     private void writeU4(int val) {
269         writeU2(val >> 16);
270         writeU2(val & 0xFFFF);
271     }
272 
copy(int count)273     private void copy(int count) {
274         for (int i = 0; i < count; ++i) {
275             gen[genPos++] = orig[origPos++];
276         }
277     }
278 
readBytes(int count)279     private byte[] readBytes(int count) {
280         byte[] bytes = new byte[count];
281         for (int i = 0; i < count; ++i) {
282             bytes[i] = orig[origPos++];
283         }
284         return bytes;
285     }
286 
writeBytes(byte[] bytes)287     private void writeBytes(byte[] bytes) {
288         for (int i = 0; i < bytes.length; ++i) {
289             gen[genPos++] = bytes[i];
290         }
291     }
292 
copyConstantPool(int constantPoolCount, boolean verbose)293     private int copyConstantPool(int constantPoolCount, boolean verbose) throws UnsupportedEncodingException {
294         int sdeIndex = -1;
295         // copy const pool index zero not in class file
296         if ( verbose )
297                 System.out.println("cp count=" + constantPoolCount);
298         for (int i = 1; i < constantPoolCount; ++i) {
299             int tag = readU1();
300             writeU1(tag);
301             if ( verbose )
302                 System.out.println(i + ": tag=" + tag);
303             switch (tag) {
304             case 7: // Class
305             case 8: // String
306             case 16: // MethodType
307                 copy(2);
308                 break;
309             case 15: // MethodHandle
310                 copy(3);
311                 break;
312             case 9: // Field
313             case 10: // Method
314             case 11: // InterfaceMethod
315             case 3: // Integer
316             case 4: // Float
317             case 12: // NameAndType
318             case 17: // InvokeDynamic_17 (will go away)
319             case 18: // InokeDynamic
320                 copy(4);
321                 break;
322             case 5: // Long
323             case 6: // Double
324                 copy(8);
325                 ++i;
326                 break;
327             case 1: // Utf8
328                 int len = readU2();
329                 writeU2(len);
330                 byte[] utf8 = readBytes(len);
331                 String str = new String(utf8, "UTF-8");
332                 if (verbose) {
333                     System.out.println(i + " read class attr -- '" + str + "'");
334                 }
335                 if (str.equals(nameSDE)) {
336                     sdeIndex = i;
337                 }
338                 writeBytes(utf8);
339                 break;
340             default:
341                 abort("unexpected tag: " + tag);
342                 break;
343             }
344         }
345         return sdeIndex;
346     }
347 
writeUtf8ForSDE()348     private void writeUtf8ForSDE() {
349         int len = nameSDE.length();
350         writeU1(1); // Utf8 tag
351         writeU2(len);
352         for (int i = 0; i < len; ++i) {
353             writeU1(nameSDE.charAt(i));
354         }
355     }
356 }
357