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 }