View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.weaver.privilizer;
20  
21  import java.lang.reflect.Modifier;
22  import java.security.PrivilegedAction;
23  import java.security.PrivilegedExceptionAction;
24  import java.util.Map;
25  
26  import org.apache.commons.lang3.ArrayUtils;
27  import org.apache.commons.lang3.Validate;
28  import org.apache.commons.lang3.builder.Builder;
29  import org.objectweb.asm.ClassWriter;
30  import org.objectweb.asm.Label;
31  import org.objectweb.asm.Opcodes;
32  import org.objectweb.asm.Type;
33  import org.objectweb.asm.commons.GeneratorAdapter;
34  import org.objectweb.asm.commons.Method;
35  import org.objectweb.asm.signature.SignatureReader;
36  import org.objectweb.asm.signature.SignatureVisitor;
37  import org.objectweb.asm.signature.SignatureWriter;
38  
39  /**
40   * Generates the Privileged[Exception?]Action class to privilize a given Method.
41   */
42  class ActionGenerator extends Privilizer.WriteClass implements Builder<Type> {
43      final PrivilizingVisitor owner;
44      final Method methd;
45      final boolean exc;
46      final Type[] exceptions;
47      final String simpleName;
48      final Type action;
49      final Method impl;
50      final int index;
51      final boolean implIsStatic;
52      final Method helper;
53      final Type result;
54      final Field[] fields;
55      private final Type actionInterface;
56  
57      /**
58       * Create a new {@link ActionGenerator}.
59       * @param access modifier
60       * @param methd {@link Method} to implement
61       * @param exceptions thrown
62       * @param owner of the action class
63       */
64      ActionGenerator(final int access, final Method methd, final String[] exceptions, final PrivilizingVisitor owner) {
65          owner.privilizer().super(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
66          this.methd = methd;
67          this.exc = ArrayUtils.isNotEmpty(exceptions);
68          this.exceptions = exc ? new Type[] { Type.getType(Exception.class) } : null;
69          this.owner = owner;
70          this.simpleName = generateName(methd);
71          this.action = Type.getObjectType(owner.className + '$' + simpleName);
72  
73          int privilegedAccessIndex = -1;
74          String implName = null;
75          for (final Map.Entry<Method, String> entry : owner.privilegedMethods.entrySet()) {
76              privilegedAccessIndex++;
77              if (entry.getKey().equals(methd)) {
78                  implName = entry.getValue();
79                  break;
80              }
81          }
82          Validate.validState(implName != null);
83  
84          this.index = privilegedAccessIndex;
85  
86          this.impl = new Method(implName, methd.getDescriptor());
87          this.implIsStatic = Modifier.isStatic(access);
88          final Type[] args =
89              implIsStatic ? methd.getArgumentTypes() : ArrayUtils.insert(0, methd.getArgumentTypes(), owner.target);
90          this.helper = new Method(privilizer().generateName("access$" + index), methd.getReturnType(), args);
91          this.result = Privilizer.wrap(methd.getReturnType());
92          this.fields = fields(args);
93          this.actionInterface = Type.getType(exc ? PrivilegedExceptionAction.class : PrivilegedAction.class);
94      }
95  
96      private static String generateName(final Method methd) {
97          final StringBuilder buf = new StringBuilder(methd.getName());
98          if (methd.getArgumentTypes().length > 0) {
99              buf.append("$$");
100             for (final Type arg : methd.getArgumentTypes()) {
101                 buf.append(arg.getDescriptor().replace("[", "arrayOf").replace('/', '_').replace(';', '$'));
102             }
103         }
104         return buf.append("_ACTION").toString();
105     }
106 
107     @SuppressWarnings("PMD.UseVarargs") //not needed
108     private static Field[] fields(final Type[] args) {
109         final Field[] result = new Field[args.length];
110 
111         for (int i = 0; i < args.length; i++) {
112             final String name = new StringBuilder("f").append(i + 1).toString();
113             result[i] = new Field(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL, name, args[i]);
114         }
115         return result;
116     }
117 
118     @Override
119     public Type build() {
120         generateHelper();
121         begin();
122         init();
123         impl();
124         visitEnd();
125         owner.privilizer().env.debug("Generated %s implementation %s to call %s#%s", actionInterface.getClassName(),
126             action.getClassName(), owner.target.getClassName(), helper);
127         return action;
128     }
129 
130     /**
131      * We must add special methods for inner classes to invoke their owners' methods, according to the scheme "access$n"
132      * where n is the index into this (ordered) map. Additionally we will prefix the whole thing like we usually do
133      * (__privileged_):
134      */
135     private void generateHelper() {
136         owner.privilizer().env.debug("Generating static helper method %s.%s to call %s", owner.target.getClassName(),
137             helper, impl);
138         final GeneratorAdapter mgen =
139             new GeneratorAdapter(Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, helper, null, exceptions, owner);
140 
141         mgen.visitCode();
142         mgen.loadArgs();
143         if (implIsStatic) {
144             mgen.invokeStatic(owner.target, impl);
145         } else {
146             mgen.invokeVirtual(owner.target, impl);
147         }
148         mgen.returnValue();
149         mgen.endMethod();
150     }
151 
152     private void begin() {
153         owner.visitInnerClass(action.getInternalName(), owner.className, simpleName, Opcodes.ACC_PRIVATE
154             | Opcodes.ACC_STATIC);
155 
156         final SignatureWriter type = new SignatureWriter();
157         final SignatureVisitor actionImplemented = type.visitInterface();
158         actionImplemented.visitClassType(actionInterface.getInternalName());
159         final SignatureVisitor visitTypeArgument = actionImplemented.visitTypeArgument('=');
160         new SignatureReader(Privilizer.wrap(methd.getReturnType()).getDescriptor()).accept(visitTypeArgument);
161         actionImplemented.visitEnd();
162 
163         final String signature = type.toString();
164 
165         visit(Opcodes.V1_5, Opcodes.ACC_SUPER | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_FINAL, action.getInternalName(),
166             signature, Type.getType(Object.class).getInternalName(),
167             new String[] { actionInterface.getInternalName() });
168     }
169 
170     /**
171      * Add fields and generate constructor.
172      */
173     private void init() {
174         for (final Field field : fields) {
175             visitField(field.access, field.name, field.type.getDescriptor(), null, null).visitEnd();
176         }
177         final Method init = new Method("<init>", Type.VOID_TYPE, helper.getArgumentTypes());
178 
179         final GeneratorAdapter mgen =
180             new GeneratorAdapter(0, init, null, Privilizer.EMPTY_TYPE_ARRAY, this);
181 
182         mgen.visitCode();
183         final Label begin = mgen.mark();
184 
185         // invoke super constructor
186         mgen.loadThis();
187         mgen.invokeConstructor(Type.getType(Object.class), Method.getMethod("void <init> ()"));
188         // assign remaining fields
189 
190         int arg = 0;
191         for (final Field field : fields) {
192             mgen.loadThis();
193             mgen.loadArg(arg++);
194             mgen.putField(action, field.name, field.type);
195         }
196         mgen.returnValue();
197         final Label end = mgen.mark();
198 
199         // declare local vars
200         mgen.visitLocalVariable("this", action.getDescriptor(), null, begin, end, 0);
201         arg = 1;
202         for (final Field field : fields) {
203             mgen.visitLocalVariable("arg" + arg, field.type.getDescriptor(), null, begin, end, arg++);
204         }
205         mgen.endMethod();
206     }
207 
208     /**
209      * Generate impl method.
210      */
211     private void impl() {
212         final Method run = Method.getMethod("Object run()");
213 
214         final GeneratorAdapter mgen = new GeneratorAdapter(Opcodes.ACC_PUBLIC, run, null, exceptions, this);
215 
216         for (final Field field : fields) {
217             mgen.loadThis();
218             mgen.getField(action, field.name, field.type);
219         }
220         mgen.invokeStatic(owner.target, helper);
221 
222         if (methd.getReturnType().getSort() < Type.ARRAY) {
223             mgen.valueOf(methd.getReturnType());
224         }
225         mgen.returnValue();
226         mgen.endMethod();
227     }
228 }