public static void main(String... args) throws Exception { new File("d1").mkdir(); new File("d2").mkdir(); // Step 1. build an empty class with an interface makeClass("d1", "Empty.java", "abstract class Empty implements Readable {}"); // Step 2. Modify classfile to have invalid constant pool index ClassFile cf = ClassFile.read(new File("d1","Empty.class")); cf.interfaces[0] = cf.constant_pool.size() + 10; ClassWriter cw = new ClassWriter(); cw.write(cf, new File("d2","Empty.class")); // Step 3. Compile use of invalid class String result = makeClass("d2", "EmptyUse.java", "class EmptyUse { Empty e; }"); if (!result.contains("compiler.misc.bad.class.file")) { System.out.println(result); throw new Exception("test failed"); } }
/** * Test an invalid class, with this_class == 0. */ @Test public void testNoNameClass(Path base) throws Exception { Path src = base.resolve("src"); Path classes = Files.createDirectories(base.resolve("classes")); tb.writeJavaFiles(src, "class A { }"); new JavacTask(tb) .outdir(classes.toString()) .files(tb.findJavaFiles(src)) .run() .writeAll(); ClassFile cf = ClassFile.read(classes.resolve("A.class")); ClassFile cf2 = new ClassFile( cf.magic, cf.minor_version, cf.major_version, cf.constant_pool, cf.access_flags, 0, // this_class, cf.super_class, cf.interfaces, cf.fields, cf.methods, cf.attributes); new ClassWriter().write(cf2, Files.newOutputStream(classes.resolve("Z.class"))); List<String> log = new JavapTask(tb) .classpath(classes.toString()) .classes("Z") .run() .writeAll() .getOutputLines(Task.OutputKind.DIRECT); checkOutput(log, true, "Warning:.*Z.class does not contain class Z"); }
private void addModuleResolutionAttribute(Path classfile, int resolution_flags) throws Exception { ClassFile cf = ClassFile.read(classfile); Attributes attrs = cf.attributes; List<CPInfo> cpData = new ArrayList<>(); cpData.add(null); for (CPInfo info : cf.constant_pool.entries()) { cpData.add(info); if (info.size() == 2) cpData.add(null); } cpData.add(new CONSTANT_Utf8_info(Attribute.ModuleResolution)); ConstantPool newCP = new ConstantPool(cpData.toArray(new CPInfo[0])); ModuleResolution_attribute res = new ModuleResolution_attribute(newCP, resolution_flags); Map<String, Attribute> newAttributeMap = new HashMap<>(attrs.map); newAttributeMap.put(Attribute.ModuleResolution, res); Attributes newAttrs = new Attributes(newAttributeMap); ClassFile newCF = new ClassFile(cf.magic, cf.minor_version, cf.major_version, newCP, cf.access_flags, cf.this_class, cf.super_class, cf.interfaces, cf.fields, cf.methods, newAttrs); try (OutputStream out = Files.newOutputStream(classfile)) { new ClassWriter().write(newCF, out); } }
@Test public void testNonZeroOpensInOpen(Path base) throws Exception { Path m1 = base.resolve("m1x"); tb.writeJavaFiles(m1, "module m1x { opens api; }", "package api; public class Api {}"); Path classes = base.resolve("classes"); Path m1Classes = classes.resolve("m1x"); tb.createDirectories(m1Classes); new JavacTask(tb) .options("-XDrawDiagnostics") .outdir(m1Classes) .files(findJavaFiles(m1)) .run(Expect.SUCCESS) .writeAll(); Path miClass = m1Classes.resolve("module-info.class"); ClassFile cf = ClassFile.read(miClass); Module_attribute module = (Module_attribute) cf.attributes.map.get(Attribute.Module); Module_attribute newModule = new Module_attribute(module.attribute_name_index, module.module_name, module.module_flags | Module_attribute.ACC_OPEN, module.module_version_index, module.requires, module.exports, module.opens, module.uses_index, module.provides); Map<String, Attribute> attrs = new HashMap<>(cf.attributes.map); attrs.put(Attribute.Module, newModule); Attributes newAttributes = new Attributes(attrs); ClassFile newClassFile = new ClassFile(cf.magic, cf.minor_version, cf.major_version, cf.constant_pool, cf.access_flags, cf.this_class, cf.super_class, cf.interfaces, cf.fields, cf.methods, newAttributes); try (OutputStream out = Files.newOutputStream(miClass)) { new ClassWriter().write(newClassFile, out); } Path test = base.resolve("test"); tb.writeJavaFiles(test, "package impl; public class Impl extends api.Api {}"); Path testClasses = base.resolve("test-classes"); tb.createDirectories(testClasses); List<String> log = new JavacTask(tb) .options("-XDrawDiagnostics", "--module-path", classes.toString(), "--add-modules", "m1x") .outdir(testClasses) .files(findJavaFiles(test)) .run(Expect.FAIL) .writeAll() .getOutputLines(Task.OutputKind.DIRECT); List<String> expected = Arrays.asList( "- compiler.err.cant.access: m1x.module-info, " + "(compiler.misc.bad.class.file.header: module-info.class, " + "(compiler.misc.module.non.zero.opens: m1x))", "1 error"); if (!expected.equals(log)) throw new Exception("expected output not found: " + log); }
public static void main(String[] args) throws Exception { if (args.length < 2) { System.err.println("Usage: java RemoveMethods classfile output [method...]"); System.exit(-1); } // class file to read Path input = Paths.get(args[0]); // class file to write, if directory then use the name of the input Path output = Paths.get(args[1]); if (Files.isDirectory(output)) output = output.resolve(input.getFileName()); // the methods to remove Set<String> methodsToRemove = new HashSet<>(); int i = 2; while (i < args.length) methodsToRemove.add(args[i++]); // read class file ClassFile cf; try (InputStream in = Files.newInputStream(input)) { cf = ClassFile.read(in); } final int magic = cf.magic; final int major_version = cf.major_version; final int minor_version = cf.minor_version; final ConstantPool cp = cf.constant_pool; final AccessFlags access_flags = cf.access_flags; final int this_class = cf.this_class; final int super_class = cf.super_class; final int[] interfaces = cf.interfaces; final Field[] fields = cf.fields; final Attributes class_attrs = cf.attributes; // remove the requested methods, no signature check at this time Method[] methods = cf.methods; i = 0; while (i < methods.length) { Method m = methods[i]; String name = m.getName(cp); if (methodsToRemove.contains(name)) { int len = methods.length; Method[] newMethods = new Method[len-1]; if (i > 0) System.arraycopy(methods, 0, newMethods, 0, i); int after = methods.length - i - 1; if (after > 0) System.arraycopy(methods, i+1, newMethods, i, after); methods = newMethods; String paramTypes = m.descriptor.getParameterTypes(cp); System.out.format("Removed method %s%s from %s%n", name, paramTypes, cf.getName()); continue; } i++; } // TBD, prune constant pool of entries that are no longer referenced // re-write class file cf = new ClassFile(magic, minor_version, major_version, cp, access_flags, this_class, super_class, interfaces, fields, methods, class_attrs); try (OutputStream out = Files.newOutputStream(output)) { new ClassWriter().write(cf, out); } }