001: /* Soot - a J*va Optimization Framework
002: * Copyright (C) 1999 Patrick Lam
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2.1 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the
016: * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
017: * Boston, MA 02111-1307, USA.
018: */
019:
020: /*
021: * Modified by the Sable Research Group and others 1997-1999.
022: * See the 'credits' file distributed with Soot for the complete list of
023: * contributors. (Soot is distributed at http://www.sable.mcgill.ca/soot)
024: */
025:
026: package soot.jimple.toolkits.invoke;
027:
028: import soot.*;
029: import soot.jimple.*;
030: import java.util.*;
031:
032: /** Methods for checking safety requirements for inlining. */
033: public class InlinerSafetyManager {
034: // true if safe to inline
035: public static boolean checkSpecialInlineRestrictions(
036: SootMethod container, SootMethod target, String options) {
037: // Check the body of the method to inline for specialinvoke's
038:
039: boolean accessors = options.equals("accessors");
040:
041: Body inlineeBody = target.getActiveBody();
042:
043: Iterator unitsIt = inlineeBody.getUnits().iterator();
044: while (unitsIt.hasNext()) {
045: Stmt st = (Stmt) unitsIt.next();
046: if (st.containsInvokeExpr()) {
047: InvokeExpr ie1 = st.getInvokeExpr();
048:
049: if (ie1 instanceof SpecialInvokeExpr) {
050: if ((InlinerSafetyManager
051: .specialInvokePerformsLookupIn(ie1,
052: container.getDeclaringClass()) || InlinerSafetyManager
053: .specialInvokePerformsLookupIn(ie1, target
054: .getDeclaringClass()))) {
055: return false;
056:
057: }
058:
059: SootMethod specialTarget = ie1.getMethod();
060:
061: if (specialTarget.isPrivate()) {
062: if (specialTarget.getDeclaringClass() != container
063: .getDeclaringClass()) {
064: // Do not inline a call which contains a specialinvoke call to a private method outside
065: // the current class. This avoids a verifier error and we assume will not have a big
066: // impact because we are inlining methods bottom-up, so such a call will be rare
067:
068: if (!accessors)
069: return false;
070: }
071: }
072: }
073: }
074: }
075:
076: return true;
077: }
078:
079: public static boolean checkAccessRestrictions(SootMethod container,
080: SootMethod target, String modifierOptions) {
081: // Check the body of the method to inline for
082: // method or field access restrictions
083: {
084: Body inlineeBody = target.getActiveBody();
085:
086: Iterator unitsIt = inlineeBody.getUnits().iterator();
087: while (unitsIt.hasNext()) {
088: Stmt st = (Stmt) unitsIt.next();
089: if (st.containsInvokeExpr()) {
090: InvokeExpr ie1 = st.getInvokeExpr();
091:
092: if (!AccessManager.ensureAccess(container, ie1
093: .getMethod(), modifierOptions))
094: return false;
095: }
096:
097: if (st instanceof AssignStmt) {
098: Value lhs = ((AssignStmt) st).getLeftOp();
099: Value rhs = ((AssignStmt) st).getRightOp();
100:
101: if (lhs instanceof FieldRef
102: && !AccessManager.ensureAccess(container,
103: ((FieldRef) lhs).getField(),
104: modifierOptions))
105: return false;
106:
107: if (rhs instanceof FieldRef
108: && !AccessManager.ensureAccess(container,
109: ((FieldRef) rhs).getField(),
110: modifierOptions))
111: return false;
112:
113: }
114: }
115: }
116:
117: return true;
118:
119: }
120:
121: /** Returns true if this method can be inlined at the given site.
122: Will try as hard as it can to change things to allow
123: inlining (modifierOptions controls what it's allowed to do:
124: safe, unsafe and nochanges)
125:
126: Returns false otherwise.
127: */
128:
129: public static boolean ensureInlinability(SootMethod target,
130: Stmt toInline, SootMethod container, String modifierOptions) {
131: if (!InlinerSafetyManager.canSafelyInlineInto(target, toInline,
132: container)) {
133: //System.out.println("canSafelyInlineInto failed");
134: return false;
135: }
136:
137: if (!AccessManager.ensureAccess(container, target,
138: modifierOptions)) {
139: //System.out.println("ensure access failed");
140: return false;
141: }
142:
143: if (!checkSpecialInlineRestrictions(container, target,
144: modifierOptions)) {
145: //System.out.println("checkSpecialInlineRestrictions failed");
146: return false;
147: }
148:
149: if (!checkAccessRestrictions(container, target, modifierOptions)) {
150: //System.out.println("checkAccessRestrictions failed");
151: return false;
152: }
153:
154: return true;
155: }
156:
157: /** Checks the safety criteria enumerated in section 3.1.4
158: * (Safety Criteria for Method Inlining) of Vijay's thesis. */
159: private static boolean canSafelyInlineInto(SootMethod inlinee,
160: Stmt toInline, SootMethod container)
161:
162: {
163: /* first, check the simple (one-line) safety criteria. */
164:
165: // Rule 0: Don't inline constructors.
166: if (inlinee.getName().equals("<init>"))
167: return false;
168:
169: // Rule 2: inlinee != container.
170: if (inlinee.getSignature().equals(container.getSignature()))
171: return false;
172:
173: // Rule 3: inlinee is neither native nor abstract.
174: if (inlinee.isNative() || inlinee.isAbstract())
175: return false;
176:
177: // Ok, that wraps up the simple criteria. Now for the more
178: // complicated criteria.
179:
180: // Rule 4: Don't inline away IllegalAccessErrors of the original
181: // source code (e.g. by moving a call to a private method
182: // *from* a bad class *to* a good class) occuring in the
183: // toInline statement.
184: // Does not occur for static methods, because there is no base?
185:
186: InvokeExpr ie = toInline.getInvokeExpr();
187: Value base = (ie instanceof InstanceInvokeExpr) ? ((InstanceInvokeExpr) ie)
188: .getBase()
189: : null;
190:
191: if (base != null
192: && base.getType() instanceof RefType
193: && invokeThrowsAccessErrorIn(((RefType) base.getType())
194: .getSootClass(), inlinee, container))
195: return false;
196:
197: // Rule 5: Don't inline away any class, method or field access
198: // (in inlinee) resulting in an IllegalAccess error.
199:
200: // Rule 6: Don't introduce a spurious IllegalAccessError from
201: // inlining (by twiddling modifiers).
202:
203: // This is better handled by a pre-phase Scene transformation.
204: // Inliner Safety should just report the absence of such
205: // IllegalAccessErrors after the transformation (and, conversely,
206: // their presence without the twiddling.)
207:
208: // Rule 7: Don't change semantics of program by moving
209: // an invokespecial.
210: if (ie instanceof SpecialInvokeExpr
211: && (specialInvokePerformsLookupIn(ie, inlinee
212: .getDeclaringClass()) || specialInvokePerformsLookupIn(
213: ie, container.getDeclaringClass())))
214: return false;
215:
216: return true;
217: }
218:
219: /** Returns true if any of the following cases holds:
220: * 1. inlinee is private, but container.declaringClass() !=
221: * inlinee.declaringClass(); or,
222: * 2. inlinee is package-visible, and its package differs from
223: * that of container; or,
224: * 3. inlinee is protected, and either:
225: * a. inlinee doesn't belong to container.declaringClass,
226: * or any superclass of container;
227: * b. the class of the base is not a (non-strict) subclass
228: * of container's declaringClass.
229: * The base class may be null, in which case 3b is omitted.
230: * (for instance, for a static method invocation.) */
231: private static boolean invokeThrowsAccessErrorIn(SootClass base,
232: SootMethod inlinee, SootMethod container) {
233: SootClass inlineeClass = inlinee.getDeclaringClass();
234: SootClass containerClass = container.getDeclaringClass();
235:
236: // Condition 1 above.
237: if (inlinee.isPrivate()
238: && !inlineeClass.getName().equals(
239: containerClass.getName()))
240: return true;
241:
242: // Condition 2. Check the package names.
243: if (!inlinee.isPrivate() && !inlinee.isProtected()
244: && !inlinee.isPublic()) {
245: if (!inlineeClass.getPackageName().equals(
246: containerClass.getPackageName()))
247: return true;
248: }
249:
250: // Condition 3.
251: if (inlinee.isProtected()) {
252: Hierarchy h = Scene.v().getActiveHierarchy();
253: boolean saved = false;
254:
255: // protected means that you can be accessed by your children.
256: // i.e. container must be in a child of inlinee.
257: if (h.isClassSuperclassOfIncluding(inlineeClass,
258: containerClass)
259: || ((base != null) && h
260: .isClassSuperclassOfIncluding(base,
261: containerClass)))
262: saved = true;
263:
264: if (!saved)
265: return true;
266: }
267:
268: return false;
269: }
270:
271: // m is the method being called; container is the class from which m
272: // is being called.
273: static boolean specialInvokePerformsLookupIn(InvokeExpr ie,
274: SootClass containerClass) {
275: // If all of the conditions are true, a lookup is performed.
276: SootMethod m = ie.getMethod();
277:
278: if (m.getName().equals("<init>")) {
279: return false;
280: }
281:
282: if (m.isPrivate()) {
283: return false;
284: }
285:
286: Hierarchy h = Scene.v().getActiveHierarchy();
287:
288: if (!h.isClassSuperclassOf(m.getDeclaringClass(),
289: containerClass))
290: return false;
291:
292: // ACC_SUPER must always be set, eh?
293:
294: return true;
295: }
296: }
|