1 /* 2 * Copyright (c) 2015, 2017, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package jdk.internal.module; 26 27 import java.io.IOException; 28 import java.io.OutputStream; 29 import java.lang.module.ModuleDescriptor; 30 import java.nio.ByteBuffer; 31 import java.util.Map; 32 import java.util.stream.Stream; 33 34 import jdk.internal.org.objectweb.asm.ClassWriter; 35 import jdk.internal.org.objectweb.asm.ModuleVisitor; 36 import jdk.internal.org.objectweb.asm.Opcodes; 37 import jdk.internal.org.objectweb.asm.commons.ModuleTargetAttribute; 38 import static jdk.internal.org.objectweb.asm.Opcodes.*; 39 40 /** 41 * Utility class to write a ModuleDescriptor as a module-info.class. 42 */ 43 44 public final class ModuleInfoWriter { 45 46 private static final Map<ModuleDescriptor.Modifier, Integer> 47 MODULE_MODS_TO_FLAGS = Map.of( 48 ModuleDescriptor.Modifier.OPEN, ACC_OPEN, 49 ModuleDescriptor.Modifier.SYNTHETIC, ACC_SYNTHETIC, 50 ModuleDescriptor.Modifier.MANDATED, ACC_MANDATED 51 ); 52 53 private static final Map<ModuleDescriptor.Requires.Modifier, Integer> 54 REQUIRES_MODS_TO_FLAGS = Map.of( 55 ModuleDescriptor.Requires.Modifier.TRANSITIVE, ACC_TRANSITIVE, 56 ModuleDescriptor.Requires.Modifier.STATIC, ACC_STATIC_PHASE, 57 ModuleDescriptor.Requires.Modifier.SYNTHETIC, ACC_SYNTHETIC, 58 ModuleDescriptor.Requires.Modifier.MANDATED, ACC_MANDATED 59 ); 60 61 private static final Map<ModuleDescriptor.Exports.Modifier, Integer> 62 EXPORTS_MODS_TO_FLAGS = Map.of( 63 ModuleDescriptor.Exports.Modifier.SYNTHETIC, ACC_SYNTHETIC, 64 ModuleDescriptor.Exports.Modifier.MANDATED, ACC_MANDATED 65 ); 66 67 private static final Map<ModuleDescriptor.Opens.Modifier, Integer> 68 OPENS_MODS_TO_FLAGS = Map.of( 69 ModuleDescriptor.Opens.Modifier.SYNTHETIC, ACC_SYNTHETIC, 70 ModuleDescriptor.Opens.Modifier.MANDATED, ACC_MANDATED 71 ); 72 73 private static final String[] EMPTY_STRING_ARRAY = new String[0]; 74 75 private ModuleInfoWriter() { } 76 77 /** 78 * Writes the given module descriptor to a module-info.class file, 79 * returning it in a byte array. 80 */ 81 private static byte[] toModuleInfo(ModuleDescriptor md, ModuleTarget target) { 82 ClassWriter cw = new ClassWriter(0); 83 cw.visit(Opcodes.V9, ACC_MODULE, "module-info", null, null, null); 84 85 int moduleFlags = md.modifiers().stream() 86 .map(MODULE_MODS_TO_FLAGS::get) 87 .reduce(0, (x, y) -> (x | y)); 88 String vs = md.rawVersion().orElse(null); 89 ModuleVisitor mv = cw.visitModule(md.name(), moduleFlags, vs); 90 91 // requires 92 for (ModuleDescriptor.Requires r : md.requires()) { 93 int flags = r.modifiers().stream() 94 .map(REQUIRES_MODS_TO_FLAGS::get) 95 .reduce(0, (x, y) -> (x | y)); 96 vs = r.rawCompiledVersion().orElse(null); 97 mv.visitRequire(r.name(), flags, vs); 98 } 99 100 // exports 101 for (ModuleDescriptor.Exports e : md.exports()) { 102 int flags = e.modifiers().stream() 103 .map(EXPORTS_MODS_TO_FLAGS::get) 104 .reduce(0, (x, y) -> (x | y)); 105 String[] targets = e.targets().toArray(EMPTY_STRING_ARRAY); 106 mv.visitExport(e.source().replace('.', '/'), flags, targets); 107 } 108 109 // opens 110 for (ModuleDescriptor.Opens opens : md.opens()) { 111 int flags = opens.modifiers().stream() 112 .map(OPENS_MODS_TO_FLAGS::get) 113 .reduce(0, (x, y) -> (x | y)); 114 String[] targets = opens.targets().toArray(EMPTY_STRING_ARRAY); 115 mv.visitOpen(opens.source().replace('.', '/'), flags, targets); 116 } 117 118 // uses 119 md.uses().stream().map(sn -> sn.replace('.', '/')).forEach(mv::visitUse); 120 121 // provides 122 for (ModuleDescriptor.Provides p : md.provides()) { 123 mv.visitProvide(p.service().replace('.', '/'), 124 p.providers() 125 .stream() 126 .map(pn -> pn.replace('.', '/')) 127 .toArray(String[]::new)); 128 } 129 130 // add the ModulePackages attribute when there are packages that aren't 131 // exported or open 132 Stream<String> exported = md.exports().stream() 133 .map(ModuleDescriptor.Exports::source); 134 Stream<String> open = md.opens().stream() 135 .map(ModuleDescriptor.Opens::source); 136 long exportedOrOpen = Stream.concat(exported, open).distinct().count(); 137 if (md.packages().size() > exportedOrOpen) { 138 md.packages().stream() 139 .map(pn -> pn.replace('.', '/')) 140 .forEach(mv::visitPackage); 141 } 142 143 // ModuleMainClass attribute 144 md.mainClass() 145 .map(mc -> mc.replace('.', '/')) 146 .ifPresent(mv::visitMainClass); 147 148 mv.visitEnd(); 149 150 // write ModuleTarget attribute if there is a target platform 151 if (target != null && target.targetPlatform().length() > 0) { 152 cw.visitAttribute(new ModuleTargetAttribute(target.targetPlatform())); 153 } 154 155 cw.visitEnd(); 156 return cw.toByteArray(); 157 } 158 159 /** 160 * Writes a module descriptor to the given output stream as a 161 * module-info.class. 162 */ 163 public static void write(ModuleDescriptor descriptor, 164 ModuleTarget target, 165 OutputStream out) 166 throws IOException 167 { 168 byte[] bytes = toModuleInfo(descriptor, target); 169 out.write(bytes); 170 } 171 172 /** 173 * Writes a module descriptor to the given output stream as a 174 * module-info.class. 175 */ 176 public static void write(ModuleDescriptor descriptor, OutputStream out) 177 throws IOException 178 { 179 write(descriptor, null, out); 180 } 181 182 /** 183 * Returns a {@code ByteBuffer} containing the given module descriptor 184 * in module-info.class format. 185 */ 186 public static ByteBuffer toByteBuffer(ModuleDescriptor descriptor) { 187 byte[] bytes = toModuleInfo(descriptor, null); 188 return ByteBuffer.wrap(bytes); 189 } 190 }