1 /*
2 * Copyright (c) 2016, 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 org.openjdk.asmtools.jasm;
24
25 import org.openjdk.asmtools.common.Module;
26
27 import java.io.IOException;
28 import java.util.*;
29 import java.util.function.BiConsumer;
30 import java.util.function.Consumer;
31 import java.util.function.Function;
32
33 /**
34 * The module attribute
35 */
36 class ModuleAttr extends AttrData {
37 // shared data
38 private Module.Builder builder;
39 private final ClassData clsData;
40 private final Function<String, ConstantPool.ConstCell> findCellAsciz;
41 private final Function<String, ConstantPool.ConstCell> findCellClassByName;
42 private final Function<String, ConstantPool.ConstCell> findCellModuleByName;
43 private final Function<String, ConstantPool.ConstCell> findCellPackageByName;
44
45 // entries to populate tables of the module attribute
46 BiConsumer<String, Integer> requires = (mn, f) -> this.builder.require(mn, f);
47 BiConsumer<String, Set<String>> exports = (pn, ms) -> this.builder.exports(new Module.Exported(pn), ms);
48 BiConsumer<String, Set<String>> opens = (pn, ms) -> this.builder.opens(new Module.Opened(pn), ms);
49 BiConsumer<String, Set<String>> provides = (tn, ts) -> this.builder.provides(new Module.Provided(tn), ts);
50 Consumer<Set<String>> uses = (ts) -> this.builder.uses(ts);
51
52 ModuleAttr(ClassData cdata) {
53 super(cdata, Tables.AttrTag.ATT_Module.parsekey());
54 builder = new Module.Builder();
55 clsData = cdata;
56 findCellAsciz = (name) -> clsData.pool.FindCellAsciz(name);
57 findCellClassByName = (name) -> clsData.pool.FindCellClassByName(name);
58 findCellModuleByName = (name) -> clsData.pool.FindCellModuleByName(name);
59 findCellPackageByName = (name) -> clsData.pool.FindCellPackageByName(name);
60 }
61
62 void openModule() {
63 builder.setModuleFlags(Module.Modifier.ACC_OPEN);
64 }
65 void setModuleName(String value) { builder.setModuleName(value);}
66
67 ModuleAttr build() {
68 Module module = builder.build();
69 Content.instance.header = new HeaderStruct(module.header, findCellModuleByName, findCellAsciz);
70 Content.instance.requiresStruct = new SetStruct<>(module.requires, findCellModuleByName, findCellAsciz);
71 Content.instance.exportsMapStruct = new MapStruct<>(module.exports, findCellPackageByName, findCellModuleByName );
72 Content.instance.opensMapStruct = new MapStruct<>(module.opens,findCellPackageByName, findCellModuleByName );
73 Content.instance.usesStruct = new SetStruct<>(module.uses, findCellClassByName, null);
74 Content.instance.providesMapStruct = new MapStruct<>(module.provides, findCellClassByName, findCellClassByName);
75 return this;
76 }
77
78 @Override
79 public int attrLength() {
80 return Content.instance.getLength();
81 }
82
83 @Override
84 public void write(CheckedDataOutputStream out) throws IOException {
85 super.write(out);
86 Content.instance.write(out);
87 }
88
89 private enum Content implements Data {
90 instance {
91 @Override
92 public int getLength() {
93 return header.getLength() +
94 requiresStruct.getLength() +
95 exportsMapStruct.getLength() +
96 opensMapStruct.getLength() +
97 usesStruct.getLength() +
98 providesMapStruct.getLength();
99 }
100
101 @Override
102 public void write(CheckedDataOutputStream out) throws IOException {
103 // keep order!
104 header.write(out);
105 requiresStruct.write(out);
106 exportsMapStruct.write(out);
107 opensMapStruct.write(out);
108 usesStruct.write(out);
109 providesMapStruct.write(out);
110 }
111 };
112
113 HeaderStruct header ;
114 SetStruct<Module.Dependence> requiresStruct;
115 MapStruct<Module.Exported> exportsMapStruct;
116 MapStruct<Module.Opened> opensMapStruct;
117 SetStruct<Module.Uses> usesStruct;
118 MapStruct<Module.Provided> providesMapStruct;
119 }
120
121 /**
122 * u2 {exports|opens}_count;
123 * { u2 {exports|opens}_index;
124 * u2 {exports|opens}_flags;
125 * u2 {exports|opens}_to_count;
126 * u2 {exports|opens}_to_index[{exports|opens}_to_count];
127 * } {exports|opens}[{exports|opens}_count];
128 * or
129 * u2 provides_count;
130 * { u2 provides_index;
131 * u2 provides_with_count;
132 * u2 provides_with_index[provides_with_count];
133 * } provides[provides_count];
134 */
135 private class MapStruct<T extends Module.TargetType> implements Data {
136 final List<Triplet<ConstantPool.ConstCell, Integer, List<ConstantPool.ConstCell>>> exportsOpensList = new ArrayList<>();
137 final List<Pair<ConstantPool.ConstCell, List<ConstantPool.ConstCell>>> providesList = new ArrayList<>();
138
139 MapStruct(Map<T, Set<String>> source,
140 Function<String,ConstantPool.ConstCell> nameFinder,
141 Function<String,ConstantPool.ConstCell> targetFinder) {
142 Objects.requireNonNull(source);
143 source.entrySet().stream()
144 .sorted(Map.Entry.comparingByKey())
145 .forEach(e -> {
146 ArrayList<ConstantPool.ConstCell> to = new ArrayList<>();
147 e.getValue().forEach(mn -> to.add(targetFinder.apply(mn)));
148 if (e.getKey().isFlagged()) {
149 exportsOpensList.add(new Triplet<>
150 ( nameFinder.apply(e.getKey().getTypeName()),
151 ((Module.FlaggedTargetType) e.getKey()).getFlags(),
152 to));
153 } else {
154 providesList.add(new Pair<>(nameFinder.apply(e.getKey().getTypeName()),
155 to));
156 }
157 }
158 );
159 }
160
161 @Override
162 public void write(CheckedDataOutputStream out) throws IOException {
163 if (providesList.isEmpty()) {
164 out.writeShort(exportsOpensList.size()); // u2 {exports|opens}_count;
165 for (Triplet<ConstantPool.ConstCell, Integer, List<ConstantPool.ConstCell>> triplet : exportsOpensList) {
166 out.writeShort(triplet.first.arg); // { u2 {exports|opens}_index;
167 out.writeShort(triplet.second); // u2 {exports|opens}_flags;
168 out.writeShort(triplet.third.size()); // u2 {exports|opens}_to_count;
169 for (ConstantPool.ConstCell to : triplet.third)
170 out.writeShort(to.arg); // u2 {exports|opens}_to_index[{exports|opens}_to_count]; }
171 }
172 } else {
173 out.writeShort(providesList.size()); // u2 provides_count;
174 for (Pair<ConstantPool.ConstCell, List<ConstantPool.ConstCell>> pair : providesList) {
175 out.writeShort(pair.first.arg); // { u2 provides_index;
176 out.writeShort(pair.second.size()); // u2 provides_with_count;
177 for (ConstantPool.ConstCell to : pair.second)
178 out.writeShort(to.arg); // u2 provides_with_index[provides_with_count]; }
179 }
180 }
181 }
182
183 @Override
184 public int getLength() {
185 if (providesList.isEmpty()) {
186 // (u2:{exports|opens}_count) + (u2:{exports|opens}_index + u2:{exports|opens}_flags u2:{exports|opens}_to_count) * {exports|opens}_count +
187 return 2 + 6 * exportsOpensList.size() +
188 // (u2:{exports|opens}_to_index) * {exports|opens}_to_count
189 exportsOpensList.stream().mapToInt(p -> p.third.size()).filter(s -> s > 0).sum() * 2;
190 } else {
191 // (u2 : provides_count) + (u2:provides_index + u2:provides_with_count) * provides_count +
192 return 2 + 4 * providesList.size() +
193 // (u2:provides_with_index) * provides_with_count
194 providesList.stream().mapToInt(p -> p.second.size()).filter(s -> s > 0).sum() * 2;
195 }
196 }
197 }
198
199 private class HeaderStruct implements Data {
200 final ConstantPool.ConstCell index;
201 final int flags;
202 final ConstantPool.ConstCell versionIndex;
203
204 HeaderStruct(Module.Header source,
205 Function<String,ConstantPool.ConstCell> nameFinder,
206 Function<String,ConstantPool.ConstCell> versionFinder) {
207 index = nameFinder.apply(source.getModuleName());
208 versionIndex = (source.getModuleVersion() == null ) ? null : versionFinder.apply(source.getModuleVersion());
209 flags = source.getModuleFlags();
210 }
211
212 @Override
213 public void write(CheckedDataOutputStream out) throws IOException {
214 out.writeShort(index.arg); // u2 module_name_index;
215 out.writeShort(flags); // u2 module_flags;
216 out.writeShort(versionIndex == null ? 0 : versionIndex.arg); // u2 module_version_index;
217 }
218
219 @Override
220 public int getLength() {
221 // u2:module_name_index) + u2:module_flags +u2:module_version_index
222 return 6;
223 }
224 }
225
226 /**
227 * u2 uses_count;
228 * u2 uses_index[uses_count];
229 * or
230 * u2 requires_count;
231 * { u2 requires_index;
232 * u2 requires_flags;
233 * u2 requires_version_index;
234 * } requires[requires_count];
235 */
236 private class SetStruct<T extends Module.TargetType> implements Data {
237 final List<ConstantPool.ConstCell> usesList = new ArrayList<>();
238 final List<Triplet<ConstantPool.ConstCell, Integer, ConstantPool.ConstCell>> requiresList = new ArrayList<>();
239
240 SetStruct(Set<T> source,
241 Function<String,ConstantPool.ConstCell> nameFinder,
242 Function<String,ConstantPool.ConstCell> versionFinder) {
243 Objects.requireNonNull(source);
244 source.forEach(e -> {
245 if (e.isFlagged()) {
246 requiresList.add(new Triplet<>(
247 nameFinder.apply(e.getTypeName()),
248 ((Module.FlaggedTargetType) e).getFlags(),
249 (((Module.VersionedFlaggedTargetType) e).getVersion() == null) ?
250 null :
251 versionFinder.apply(((Module.VersionedFlaggedTargetType) e).getVersion())));
252 } else {
253 usesList.add(nameFinder.apply((e.getTypeName())));
254 }
255 });
256 }
257
258 @Override
259 public void write(CheckedDataOutputStream out) throws IOException {
260 if (usesList.isEmpty()) {
261 out.writeShort(requiresList.size()); // u2 requires_count;
262 for (Triplet<ConstantPool.ConstCell, Integer, ConstantPool.ConstCell> r : requiresList) {
263 out.writeShort(r.first.arg); // u2 requires_index;
264 out.writeShort(r.second); // u2 requires_flags;
265 out.writeShort(r.third == null ? 0 : r.third.arg); // u2 requires_version_index;
266 }
267 } else {
268 out.writeShort(usesList.size()); // u2 uses_count;
269 for (ConstantPool.ConstCell u : usesList)
270 out.writeShort(u.arg); // u2 uses_index[uses_count];
271 }
272 }
273
274 @Override
275 public int getLength() {
276 return usesList.isEmpty() ?
277 // (u2:requires_count) + (u2:requires_index + u2:requires_flags + u2:requires_version_index) * requires_count
278 2 + 6 * requiresList.size() :
279 // (u2:uses_count) + (u2:uses_index) * uses_count
280 2 + 2 * usesList.size();
281 }
282 }
283
284 // Helper classes
285 private class Pair<F, S> {
286 final F first;
287 final S second;
288
289 Pair(F first, S second) {
290 this.first = first;
291 this.second = second;
292 }
293 }
294
295 public class Triplet<F, S, T> extends Pair<F,S> {
296 private final T third;
297 Triplet(F first, S second, T third) {
298 super(first,second);
299 this.third = third;
300 }
301 }
302
303 }