diff a/test/lib/org/openjdk/asmtools/common/Module.java b/test/lib/org/openjdk/asmtools/common/Module.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/common/Module.java
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2016, 2020, 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.common;
+
+import org.openjdk.asmtools.jdis.Indenter;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.lang.String.format;
+
+/**
+ * Internal presentation of a module
+ */
+public final class Module extends Indenter {
+
+  //* A module name and module_flags
+  public final Header header;
+  //* A service dependence's of this module
+  public final Set<Uses> uses;
+  //* Modules on which the current module has a dependence.
+  public final Set<Dependence> requires;
+  //* A module exports, may be qualified or unqualified.
+  public final Map<Exported, Set<String>> exports;
+  //* Packages, to be opened by the current module
+  public final Map<Opened, Set<String>> opens;
+  //* A service that a module provides one or more implementations of.
+  public final Map<Provided, Set<String>> provides;
+
+  private Module(Builder builder) {
+    this.header = builder.header;
+    this.requires = Collections.unmodifiableSet(builder.requires);
+    this.exports = Collections.unmodifiableMap(builder.exports);
+    this.opens = Collections.unmodifiableMap(builder.opens);
+    this.uses = Collections.unmodifiableSet(builder.uses);
+    this.provides = Collections.unmodifiableMap(builder.provides);
+  }
+
+  public String getModuleFlags () {
+    return Modifier.getModuleModifiers(header.getFlags());
+  }
+  public String getModuleName () { return header.getModuleName();  }
+  public String getModuleVersion()  { return header.getModuleVersion(); };
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    int l = 0;
+    requires.stream()
+        .sorted()
+        .forEach(d -> sb.append(getIndentString()).append(format("requires %s;%s%n",
+            d.toString(),
+            d.getModuleVersion() == null ? "" : " // @" + d.getModuleVersion())));
+    //
+    l = newLine(sb,l);
+    exports.entrySet().stream()
+        .filter(e -> e.getValue().isEmpty())
+        .sorted(Map.Entry.comparingByKey())
+        .map(e -> format("%sexports %s;%n", getIndentString(), e.getKey().toString()))
+        .forEach(sb::append);
+    exports.entrySet().stream()
+        .filter(e -> !e.getValue().isEmpty())
+        .sorted(Map.Entry.comparingByKey())
+        .map(e -> format("%sexports %s to%n%s;%n", getIndentString(), e.getKey().toString(),
+            e.getValue().stream().sorted()
+                .map(mn -> format("%s          %s", getIndentString(), mn))
+                .collect(Collectors.joining(",\n"))))
+        .forEach(sb::append);
+    //
+    l = newLine(sb,l);
+    opens.entrySet().stream()
+        .filter(e -> e.getValue().isEmpty())
+        .sorted(Map.Entry.comparingByKey())
+        .map(e -> format("%sopens %s;%n", getIndentString(), e.getKey().toString()))
+        .forEach(sb::append);
+    opens.entrySet().stream()
+        .filter(e -> !e.getValue().isEmpty())
+        .sorted(Map.Entry.comparingByKey())
+        .map(e -> format("%sopens %s to%n%s;%n", getIndentString(), e.getKey().toString(),
+            e.getValue().stream().sorted()
+                .map(mn -> format("%s          %s", getIndentString(), mn))
+                .collect(Collectors.joining(",\n"))))
+        .forEach(sb::append);
+    //
+    l = newLine(sb,l);
+    uses.stream().sorted()
+        .map(s -> format("%suses %s;%n", getIndentString(), s))
+        .forEach(sb::append);
+    //
+    l = newLine(sb,l);
+    provides.entrySet().stream()
+        .filter(e -> !e.getValue().isEmpty())
+        .sorted(Map.Entry.comparingByKey())
+        .map(e -> format("%sprovides %s with%n%s;%n", getIndentString(), e.getKey().toString(),
+            e.getValue().stream().sorted()
+                .map(mn -> format("%s          %s", getIndentString(), mn))
+                .collect(Collectors.joining(",\n"))))
+        .forEach(sb::append);
+    //
+    if( Character.isWhitespace(sb.charAt(sb.length()-1)) )
+      sb.deleteCharAt(sb.length()-1);
+    return sb.toString();
+  }
+
+  private int newLine(StringBuilder sb, int length) {
+    if(sb.length() > length) {
+      sb.append("\n");
+      return sb.length() + 1;
+    }
+    return length;
+  }
+
+  /**
+   * Modules flags
+   */
+  public enum Modifier {
+    ACC_NONE(0x0000, "", ""),
+    ACC_OPEN(0x0020, "open", "ACC_OPEN"),
+    ACC_TRANSITIVE(0x0020, "transitive", "ACC_TRANSITIVE"),
+    ACC_STATIC_PHASE(0x0040, "static", "ACC_STATIC_PHASE"),
+    ACC_SYNTHETIC(0x1000, "", "ACC_SYNTHETIC"),
+    ACC_MANDATED(0x8000, "", "ACC_MANDATED");
+    private final int value;
+    private final String keyword;
+    private final String flag;
+    Modifier(int value, String keyword, String flagName) {
+      this.value = value;
+      this.keyword = keyword;
+      this.flag = flagName;
+    }
+
+    public int asInt() { return value; }
+
+    public static String getModuleModifiers(int flag) {
+      return asString(flag, false, ACC_TRANSITIVE);
+    }
+
+    public static String getModuleFlags(int flag) {
+      return asString(flag, true, ACC_TRANSITIVE);
+    }
+
+    public static String getStatementModifiers(int flag) {
+      return asString(flag, false, ACC_OPEN);
+    }
+
+    public static String getStatementFlags(int flag) {
+      return asString(flag, true, ACC_OPEN);
+    }
+
+    private static String asString(int value, boolean flagFormat, Modifier skipped ) {
+      String buf = "";
+      for(Module.Modifier m : values()) {
+        if( m != skipped && (value & m.value) != 0) {
+          buf += ((flagFormat) ? m.flag : m.keyword) + " ";
+          value ^= m.value;
+        }
+      }
+      if( flagFormat && value != 0 )
+        buf += String.format("0x%04X ", value);
+      return buf;
+    }
+  }
+
+  // A module header consists of a module name and module flags
+  public final static class Header extends VersionedFlaggedTargetType{
+    Header(String typeName, int flag) { this(typeName, flag, null); }
+    Header(String typeName, int flag, String moduleVersion) { super(typeName, flag, moduleVersion); }
+    public String getModuleName()    { return getTypeName(); }
+    public int    getModuleFlags()   { return getFlags();    }
+    public String getModuleVersion() { return getVersion();  }
+  }
+
+  //* A module on which the current module has a dependence.
+  public final static class Dependence extends VersionedFlaggedTargetType {
+    public Dependence(String moduleName, int flag) {this(moduleName, flag, null);}
+    public Dependence(String moduleName, int flag, String moduleVersion) {super(moduleName, flag, moduleVersion);}
+    public Dependence(String moduleName, boolean transitive, boolean staticPhase) { this(moduleName,transitive,staticPhase,null);}
+    public Dependence(String moduleName, boolean transitive, boolean staticPhase, String moduleVersion) {
+      this(moduleName,
+          (transitive ? Modifier.ACC_TRANSITIVE.value : Modifier.ACC_NONE.value) |
+          (staticPhase ? Modifier.ACC_STATIC_PHASE.value : Modifier.ACC_NONE.value), moduleVersion);
+    }
+    public String getModuleVersion()          { return getVersion();  }
+  }
+
+  public final static class Uses extends TargetType {
+    public Uses(String typeName) { super(typeName); }
+  }
+
+  //* A provided type of the current module.
+  public final static class Provided extends TargetType {
+    public Provided(String typeName) { super(typeName); }
+  }
+
+  //* An opened package of the current module.
+  public final static class Opened extends FlaggedTargetType {
+    public Opened(String typeName) {
+      super(typeName, 0);
+    }
+    public Opened(String typeName, int opensFlags) {
+      super(typeName, opensFlags);
+    }
+  }
+
+  //* An exported package of the current module.
+  public final static class Exported extends FlaggedTargetType {
+    public Exported(String typeName) {
+      super(typeName, 0);
+    }
+
+    public Exported(String typeName, int exportsFlags) {
+      super(typeName, exportsFlags);
+    }
+  }
+
+  public static class VersionedFlaggedTargetType extends FlaggedTargetType {
+    private String version;
+
+    VersionedFlaggedTargetType(String typeName, int flag) {
+      this(typeName,flag, null);
+    }
+
+    VersionedFlaggedTargetType(String typeName, int flag, String version) {
+      super(typeName, flag);
+      this.version = version != null && !version.isEmpty() ? version : null;
+    }
+    public String getVersion() { return version; }
+
+    @Override
+    public int hashCode() {
+      int code = version == null ? 0 : version.hashCode();
+      return code + super.hashCode();
+    }
+  }
+
+  public static class FlaggedTargetType extends TargetType {
+    private int flag;
+
+    FlaggedTargetType(String typeName, int flag) {
+      super(typeName);
+      this.flag = flag;
+    }
+
+    public boolean isFlagged() {
+      return true;
+    }
+
+    public int getFlags() {
+      return flag;
+    }
+
+    public void setFlag(int value) { flag = value; }
+
+    @Override
+    public int hashCode() {
+      return super.hashCode() + flag;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      return super.equals(o) && ((FlaggedTargetType) o).flag == this.flag;
+    }
+
+    @Override
+    public String toString() {
+      return Modifier.getStatementModifiers(this.flag)+ super.toString();
+    }
+  }
+
+  public static class TargetType implements Comparable<TargetType> {
+    private String typeName;
+
+    TargetType(String typeName) { this.typeName = typeName; }
+
+    public String getTypeName() {
+      return typeName;
+    }
+
+    public void setTypeName(String value) { typeName = value; }
+
+    public boolean isFlagged() {
+      return false;
+    }
+
+    @Override
+    public int hashCode() { return typeName.hashCode() * 11; }
+
+    @Override
+    public boolean equals(Object o) {
+      if (o instanceof TargetType) {
+        TargetType t = (TargetType) o;
+        return this.typeName.equals(t.getTypeName());
+      }
+      return false;
+    }
+
+    @Override
+    public int compareTo(TargetType t) {
+      return this.typeName.compareTo(t.getTypeName());
+    }
+
+    @Override
+    public String toString() {
+      return typeName;
+    }
+  }
+
+  /**
+   * The module builder.
+   */
+  public static final class Builder {
+    final Header header;
+    final Set<Dependence> requires = new HashSet<>();
+    final Map<Exported, Set<String>> exports = new HashMap<>();
+    final Map<Opened, Set<String>> opens = new HashMap<>();
+    final Set<Uses> uses = new HashSet<>();
+    final Map<Provided, Set<String>> provides = new HashMap<>();
+
+
+    public Builder() {
+      this("", Modifier.ACC_NONE.asInt(), null);
+    }
+
+    public Builder(String moduleName, int moduleFlags, String moduleVersion) {
+      header = new Header( moduleName,moduleFlags, moduleVersion);
+    }
+
+    public Builder setModuleFlags(int moduleFlags) {
+      header.setFlag(header.getFlags() | moduleFlags);
+      return this;
+    }
+
+    public Builder setModuleFlags(Modifier... moduleFlags) {
+      for (Modifier m : moduleFlags)
+        setModuleFlags(m.value);
+      return this;
+    }
+
+    public Builder setModuleName(String value) {
+      header.setTypeName(value);
+      return this;
+    }
+
+    public Builder require(String d, boolean transitive, boolean staticPhase, String version) {
+      requires.add(new Dependence(d, transitive, staticPhase, version));
+      return this;
+    }
+
+    public Builder require(String d, int requiresFlag, String version) {
+      requires.add(new Dependence(d, requiresFlag, version));
+      return this;
+    }
+
+    public Builder require(String d, int requiresFlag) {
+      requires.add(new Dependence(d, requiresFlag, null));
+      return this;
+    }
+
+    public Builder opens(Opened p, Set<String> ms) {
+      return add(opens, p, ms);
+    }
+
+    public Builder opens(String packageName, int exportFlags, Set<String> ms) {
+      return add(opens, new Opened(packageName, exportFlags), ms);
+    }
+
+    public Builder opens(String packageName, int exportFlags) {
+      return add(opens, new Opened(packageName, exportFlags), new HashSet<>());
+    }
+
+
+    public Builder exports(Exported p, Set<String> ms) {
+      return add(exports, p, ms);
+    }
+
+    public Builder exports(String packageName, int exportFlags, Set<String> ms) {
+      return add(exports, new Exported(packageName, exportFlags), ms);
+    }
+
+    public Builder exports(String packageName, int exportFlags) {
+      return add(exports, new Exported(packageName, exportFlags), new HashSet<>());
+    }
+
+    public Builder uses(String serviceName) {
+      uses.add(new Uses(serviceName));
+      return this;
+    }
+
+
+    public Builder uses(Set<String> serviceNames) {
+      uses.addAll(serviceNames.stream().map(Uses::new).collect(Collectors.toList()));
+      return this;
+    }
+
+    public Builder provides(Provided t, Set<String> implementations) {
+      return add(provides, t, implementations);
+    }
+
+    public Builder provides(String serviceName, Set<String> implementations) {
+      return add(provides, new Provided(serviceName), implementations);
+    }
+
+
+    /**
+     * @return The new module
+     */
+    public Module build() {
+      return new Module(this);
+    }
+
+    private <T extends TargetType> Builder  add( Map<T, Set<String>> collection, T source, Set<String> target) {
+      Objects.requireNonNull(source);
+      Objects.requireNonNull(target);
+      if (!collection.containsKey(source))
+        collection.put(source, new HashSet<>());
+      collection.get(source).addAll(target);
+      return this;
+    }
+  }
+}
