• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // ASM: a very small and fast Java bytecode manipulation framework
2 // Copyright (c) 2000-2011 INRIA, France Telecom
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions
7 // are met:
8 // 1. Redistributions of source code must retain the above copyright
9 //    notice, this list of conditions and the following disclaimer.
10 // 2. Redistributions in binary form must reproduce the above copyright
11 //    notice, this list of conditions and the following disclaimer in the
12 //    documentation and/or other materials provided with the distribution.
13 // 3. Neither the name of the copyright holders nor the names of its
14 //    contributors may be used to endorse or promote products derived from
15 //    this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 // THE POSSIBILITY OF SUCH DAMAGE.
28 package org.objectweb.asm.util;
29 
30 import java.util.HashSet;
31 import org.objectweb.asm.ModuleVisitor;
32 import org.objectweb.asm.Opcodes;
33 
34 /**
35  * A {@link ModuleVisitor} that checks that its methods are properly used.
36  *
37  * @author Remi Forax
38  */
39 public class CheckModuleAdapter extends ModuleVisitor {
40   /** Whether the visited module is open. */
41   private final boolean isOpen;
42 
43   /** The fully qualified names of the dependencies of the visited module. */
44   private final NameSet requiredModules = new NameSet("Modules requires");
45 
46   /** The internal names of the packages exported by the visited module. */
47   private final NameSet exportedPackages = new NameSet("Module exports");
48 
49   /** The internal names of the packages opened by the visited module. */
50   private final NameSet openedPackages = new NameSet("Module opens");
51 
52   /** The internal names of the services used by the visited module. */
53   private final NameSet usedServices = new NameSet("Module uses");
54 
55   /** The internal names of the services provided by the visited module. */
56   private final NameSet providedServices = new NameSet("Module provides");
57 
58   /** The class version number. */
59   int classVersion;
60 
61   /** Whether the {@link #visitEnd} method has been called. */
62   private boolean visitEndCalled;
63 
64   /**
65    * Constructs a new {@link CheckModuleAdapter}. <i>Subclasses must not use this constructor</i>.
66    * Instead, they must use the {@link #CheckModuleAdapter(int, ModuleVisitor, boolean)} version.
67    *
68    * @param moduleVisitor the module visitor to which this adapter must delegate calls.
69    * @param isOpen whether the visited module is open. Open modules have their {@link
70    *     Opcodes#ACC_OPEN} access flag set in {@link org.objectweb.asm.ClassVisitor#visitModule}.
71    * @throws IllegalStateException If a subclass calls this constructor.
72    */
CheckModuleAdapter(final ModuleVisitor moduleVisitor, final boolean isOpen)73   public CheckModuleAdapter(final ModuleVisitor moduleVisitor, final boolean isOpen) {
74     this(/* latest api = */ Opcodes.ASM9, moduleVisitor, isOpen);
75     if (getClass() != CheckModuleAdapter.class) {
76       throw new IllegalStateException();
77     }
78   }
79 
80   /**
81    * Constructs a new {@link CheckModuleAdapter}.
82    *
83    * @param api the ASM API version implemented by this visitor. Must be one of the {@code
84    *     ASM}<i>x</i> values in {@link Opcodes}.
85    * @param moduleVisitor the module visitor to which this adapter must delegate calls.
86    * @param isOpen whether the visited module is open. Open modules have their {@link
87    *     Opcodes#ACC_OPEN} access flag set in {@link org.objectweb.asm.ClassVisitor#visitModule}.
88    */
CheckModuleAdapter( final int api, final ModuleVisitor moduleVisitor, final boolean isOpen)89   protected CheckModuleAdapter(
90       final int api, final ModuleVisitor moduleVisitor, final boolean isOpen) {
91     super(api, moduleVisitor);
92     this.isOpen = isOpen;
93   }
94 
95   @Override
visitMainClass(final String mainClass)96   public void visitMainClass(final String mainClass) {
97     // Modules can only appear in V9 or more classes.
98     CheckMethodAdapter.checkInternalName(Opcodes.V9, mainClass, "module main class");
99     super.visitMainClass(mainClass);
100   }
101 
102   @Override
visitPackage(final String packaze)103   public void visitPackage(final String packaze) {
104     CheckMethodAdapter.checkInternalName(Opcodes.V9, packaze, "module package");
105     super.visitPackage(packaze);
106   }
107 
108   @Override
visitRequire(final String module, final int access, final String version)109   public void visitRequire(final String module, final int access, final String version) {
110     checkVisitEndNotCalled();
111     CheckClassAdapter.checkFullyQualifiedName(Opcodes.V9, module, "required module");
112     requiredModules.checkNameNotAlreadyDeclared(module);
113     CheckClassAdapter.checkAccess(
114         access,
115         Opcodes.ACC_STATIC_PHASE
116             | Opcodes.ACC_TRANSITIVE
117             | Opcodes.ACC_SYNTHETIC
118             | Opcodes.ACC_MANDATED);
119     if (classVersion >= Opcodes.V10
120         && module.equals("java.base")
121         && (access & (Opcodes.ACC_STATIC_PHASE | Opcodes.ACC_TRANSITIVE)) != 0) {
122       throw new IllegalArgumentException(
123           "Invalid access flags: "
124               + access
125               + " java.base can not be declared ACC_TRANSITIVE or ACC_STATIC_PHASE");
126     }
127     super.visitRequire(module, access, version);
128   }
129 
130   @Override
visitExport(final String packaze, final int access, final String... modules)131   public void visitExport(final String packaze, final int access, final String... modules) {
132     checkVisitEndNotCalled();
133     CheckMethodAdapter.checkInternalName(Opcodes.V9, packaze, "package name");
134     exportedPackages.checkNameNotAlreadyDeclared(packaze);
135     CheckClassAdapter.checkAccess(access, Opcodes.ACC_SYNTHETIC | Opcodes.ACC_MANDATED);
136     if (modules != null) {
137       for (String module : modules) {
138         CheckClassAdapter.checkFullyQualifiedName(Opcodes.V9, module, "module export to");
139       }
140     }
141     super.visitExport(packaze, access, modules);
142   }
143 
144   @Override
visitOpen(final String packaze, final int access, final String... modules)145   public void visitOpen(final String packaze, final int access, final String... modules) {
146     checkVisitEndNotCalled();
147     if (isOpen) {
148       throw new UnsupportedOperationException("An open module can not use open directive");
149     }
150     CheckMethodAdapter.checkInternalName(Opcodes.V9, packaze, "package name");
151     openedPackages.checkNameNotAlreadyDeclared(packaze);
152     CheckClassAdapter.checkAccess(access, Opcodes.ACC_SYNTHETIC | Opcodes.ACC_MANDATED);
153     if (modules != null) {
154       for (String module : modules) {
155         CheckClassAdapter.checkFullyQualifiedName(Opcodes.V9, module, "module open to");
156       }
157     }
158     super.visitOpen(packaze, access, modules);
159   }
160 
161   @Override
visitUse(final String service)162   public void visitUse(final String service) {
163     checkVisitEndNotCalled();
164     CheckMethodAdapter.checkInternalName(Opcodes.V9, service, "service");
165     usedServices.checkNameNotAlreadyDeclared(service);
166     super.visitUse(service);
167   }
168 
169   @Override
visitProvide(final String service, final String... providers)170   public void visitProvide(final String service, final String... providers) {
171     checkVisitEndNotCalled();
172     CheckMethodAdapter.checkInternalName(Opcodes.V9, service, "service");
173     providedServices.checkNameNotAlreadyDeclared(service);
174     if (providers == null || providers.length == 0) {
175       throw new IllegalArgumentException("Providers cannot be null or empty");
176     }
177     for (String provider : providers) {
178       CheckMethodAdapter.checkInternalName(Opcodes.V9, provider, "provider");
179     }
180     super.visitProvide(service, providers);
181   }
182 
183   @Override
visitEnd()184   public void visitEnd() {
185     checkVisitEndNotCalled();
186     visitEndCalled = true;
187     super.visitEnd();
188   }
189 
checkVisitEndNotCalled()190   private void checkVisitEndNotCalled() {
191     if (visitEndCalled) {
192       throw new IllegalStateException("Cannot call a visit method after visitEnd has been called");
193     }
194   }
195 
196   private static class NameSet {
197 
198     private final String type;
199     private final HashSet<String> names;
200 
NameSet(final String type)201     NameSet(final String type) {
202       this.type = type;
203       this.names = new HashSet<>();
204     }
205 
checkNameNotAlreadyDeclared(final String name)206     void checkNameNotAlreadyDeclared(final String name) {
207       if (!names.add(name)) {
208         throw new IllegalArgumentException(type + " '" + name + "' already declared");
209       }
210     }
211   }
212 }
213