/* * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package org.openjdk.asmtools.jasm; import org.openjdk.asmtools.common.Module; import java.io.IOException; import java.util.*; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; /** * The module attribute */ class ModuleAttr extends AttrData { // shared data private Module.Builder builder; private final ClassData clsData; private final Function findCellAsciz; private final Function findCellClassByName; private final Function findCellModuleByName; private final Function findCellPackageByName; // entries to populate tables of the module attribute BiConsumer requires = (mn, f) -> this.builder.require(mn, f); BiConsumer> exports = (pn, ms) -> this.builder.exports(new Module.Exported(pn), ms); BiConsumer> opens = (pn, ms) -> this.builder.opens(new Module.Opened(pn), ms); BiConsumer> provides = (tn, ts) -> this.builder.provides(new Module.Provided(tn), ts); Consumer> uses = (ts) -> this.builder.uses(ts); ModuleAttr(ClassData cdata) { super(cdata, Tables.AttrTag.ATT_Module.parsekey()); builder = new Module.Builder(); clsData = cdata; findCellAsciz = (name) -> clsData.pool.FindCellAsciz(name); findCellClassByName = (name) -> clsData.pool.FindCellClassByName(name); findCellModuleByName = (name) -> clsData.pool.FindCellModuleByName(name); findCellPackageByName = (name) -> clsData.pool.FindCellPackageByName(name); } void openModule() { builder.setModuleFlags(Module.Modifier.ACC_OPEN); } void setModuleName(String value) { builder.setModuleName(value);} ModuleAttr build() { Module module = builder.build(); Content.instance.header = new HeaderStruct(module.header, findCellModuleByName, findCellAsciz); Content.instance.requiresStruct = new SetStruct<>(module.requires, findCellModuleByName, findCellAsciz); Content.instance.exportsMapStruct = new MapStruct<>(module.exports, findCellPackageByName, findCellModuleByName ); Content.instance.opensMapStruct = new MapStruct<>(module.opens,findCellPackageByName, findCellModuleByName ); Content.instance.usesStruct = new SetStruct<>(module.uses, findCellClassByName, null); Content.instance.providesMapStruct = new MapStruct<>(module.provides, findCellClassByName, findCellClassByName); return this; } @Override public int attrLength() { return Content.instance.getLength(); } @Override public void write(CheckedDataOutputStream out) throws IOException { super.write(out); Content.instance.write(out); } private enum Content implements Data { instance { @Override public int getLength() { return header.getLength() + requiresStruct.getLength() + exportsMapStruct.getLength() + opensMapStruct.getLength() + usesStruct.getLength() + providesMapStruct.getLength(); } @Override public void write(CheckedDataOutputStream out) throws IOException { // keep order! header.write(out); requiresStruct.write(out); exportsMapStruct.write(out); opensMapStruct.write(out); usesStruct.write(out); providesMapStruct.write(out); } }; HeaderStruct header ; SetStruct requiresStruct; MapStruct exportsMapStruct; MapStruct opensMapStruct; SetStruct usesStruct; MapStruct providesMapStruct; } /** * u2 {exports|opens}_count; * { u2 {exports|opens}_index; * u2 {exports|opens}_flags; * u2 {exports|opens}_to_count; * u2 {exports|opens}_to_index[{exports|opens}_to_count]; * } {exports|opens}[{exports|opens}_count]; * or * u2 provides_count; * { u2 provides_index; * u2 provides_with_count; * u2 provides_with_index[provides_with_count]; * } provides[provides_count]; */ private class MapStruct implements Data { final List>> exportsOpensList = new ArrayList<>(); final List>> providesList = new ArrayList<>(); MapStruct(Map> source, Function nameFinder, Function targetFinder) { Objects.requireNonNull(source); source.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .forEach(e -> { ArrayList to = new ArrayList<>(); e.getValue().forEach(mn -> to.add(targetFinder.apply(mn))); if (e.getKey().isFlagged()) { exportsOpensList.add(new Triplet<> ( nameFinder.apply(e.getKey().getTypeName()), ((Module.FlaggedTargetType) e.getKey()).getFlags(), to)); } else { providesList.add(new Pair<>(nameFinder.apply(e.getKey().getTypeName()), to)); } } ); } @Override public void write(CheckedDataOutputStream out) throws IOException { if (providesList.isEmpty()) { out.writeShort(exportsOpensList.size()); // u2 {exports|opens}_count; for (Triplet> triplet : exportsOpensList) { out.writeShort(triplet.first.arg); // { u2 {exports|opens}_index; out.writeShort(triplet.second); // u2 {exports|opens}_flags; out.writeShort(triplet.third.size()); // u2 {exports|opens}_to_count; for (ConstantPool.ConstCell to : triplet.third) out.writeShort(to.arg); // u2 {exports|opens}_to_index[{exports|opens}_to_count]; } } } else { out.writeShort(providesList.size()); // u2 provides_count; for (Pair> pair : providesList) { out.writeShort(pair.first.arg); // { u2 provides_index; out.writeShort(pair.second.size()); // u2 provides_with_count; for (ConstantPool.ConstCell to : pair.second) out.writeShort(to.arg); // u2 provides_with_index[provides_with_count]; } } } } @Override public int getLength() { if (providesList.isEmpty()) { // (u2:{exports|opens}_count) + (u2:{exports|opens}_index + u2:{exports|opens}_flags u2:{exports|opens}_to_count) * {exports|opens}_count + return 2 + 6 * exportsOpensList.size() + // (u2:{exports|opens}_to_index) * {exports|opens}_to_count exportsOpensList.stream().mapToInt(p -> p.third.size()).filter(s -> s > 0).sum() * 2; } else { // (u2 : provides_count) + (u2:provides_index + u2:provides_with_count) * provides_count + return 2 + 4 * providesList.size() + // (u2:provides_with_index) * provides_with_count providesList.stream().mapToInt(p -> p.second.size()).filter(s -> s > 0).sum() * 2; } } } private class HeaderStruct implements Data { final ConstantPool.ConstCell index; final int flags; final ConstantPool.ConstCell versionIndex; HeaderStruct(Module.Header source, Function nameFinder, Function versionFinder) { index = nameFinder.apply(source.getModuleName()); versionIndex = (source.getModuleVersion() == null ) ? null : versionFinder.apply(source.getModuleVersion()); flags = source.getModuleFlags(); } @Override public void write(CheckedDataOutputStream out) throws IOException { out.writeShort(index.arg); // u2 module_name_index; out.writeShort(flags); // u2 module_flags; out.writeShort(versionIndex == null ? 0 : versionIndex.arg); // u2 module_version_index; } @Override public int getLength() { // u2:module_name_index) + u2:module_flags +u2:module_version_index return 6; } } /** * u2 uses_count; * u2 uses_index[uses_count]; * or * u2 requires_count; * { u2 requires_index; * u2 requires_flags; * u2 requires_version_index; * } requires[requires_count]; */ private class SetStruct implements Data { final List usesList = new ArrayList<>(); final List> requiresList = new ArrayList<>(); SetStruct(Set source, Function nameFinder, Function versionFinder) { Objects.requireNonNull(source); source.forEach(e -> { if (e.isFlagged()) { requiresList.add(new Triplet<>( nameFinder.apply(e.getTypeName()), ((Module.FlaggedTargetType) e).getFlags(), (((Module.VersionedFlaggedTargetType) e).getVersion() == null) ? null : versionFinder.apply(((Module.VersionedFlaggedTargetType) e).getVersion()))); } else { usesList.add(nameFinder.apply((e.getTypeName()))); } }); } @Override public void write(CheckedDataOutputStream out) throws IOException { if (usesList.isEmpty()) { out.writeShort(requiresList.size()); // u2 requires_count; for (Triplet r : requiresList) { out.writeShort(r.first.arg); // u2 requires_index; out.writeShort(r.second); // u2 requires_flags; out.writeShort(r.third == null ? 0 : r.third.arg); // u2 requires_version_index; } } else { out.writeShort(usesList.size()); // u2 uses_count; for (ConstantPool.ConstCell u : usesList) out.writeShort(u.arg); // u2 uses_index[uses_count]; } } @Override public int getLength() { return usesList.isEmpty() ? // (u2:requires_count) + (u2:requires_index + u2:requires_flags + u2:requires_version_index) * requires_count 2 + 6 * requiresList.size() : // (u2:uses_count) + (u2:uses_index) * uses_count 2 + 2 * usesList.size(); } } // Helper classes private class Pair { final F first; final S second; Pair(F first, S second) { this.first = first; this.second = second; } } public class Triplet extends Pair { private final T third; Triplet(F first, S second, T third) { super(first,second); this.third = third; } } }