/* * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html. * * This file is a derivative of code released under the terms listed below. * */ /* * Copyright (c) 2013, * Tobias Blaschke <code@tobiasblaschke.de> * All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The names of the contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package com.ibm.wala.dalvik.ipa.callgraph.androidModel.parameters; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ibm.wala.classLoader.IField; import com.ibm.wala.dalvik.ipa.callgraph.androidModel.AndroidModelClass; import com.ibm.wala.dalvik.util.AndroidEntryPointManager; import com.ibm.wala.dalvik.util.AndroidTypes; import com.ibm.wala.ipa.callgraph.AnalysisScope; import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.ipa.summaries.VolatileMethodSummary; import com.ibm.wala.ssa.ConstantValue; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.types.Descriptor; import com.ibm.wala.types.MethodReference; import com.ibm.wala.types.TypeName; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.ssa.IInstantiator; import com.ibm.wala.util.ssa.ParameterAccessor; import com.ibm.wala.util.ssa.SSAValue; import com.ibm.wala.util.ssa.SSAValue.UniqueKey; import com.ibm.wala.util.ssa.SSAValue.VariableKey; import com.ibm.wala.util.ssa.SSAValueManager; import com.ibm.wala.util.ssa.TypeSafeInstructionFactory; /** * Instantiates certain android-types differently. * * For example instantiating an android.content.Context would pull in all Android-components in * scope resulting in a massivly overapproximated model. * * @author Tobias Blaschke <code@tobiasblaschke.de> */ public class SpecializedInstantiator extends FlatInstantiator { private static final Logger logger = LoggerFactory.getLogger(SpecializedInstantiator.class); final IInstantiator parent; public SpecializedInstantiator(final VolatileMethodSummary body, final TypeSafeInstructionFactory instructionFactory, final SSAValueManager pm, final IClassHierarchy cha, final MethodReference scope, final AnalysisScope analysisScope, final IInstantiator parent) { super(body, instructionFactory, pm, cha, scope, analysisScope, 100); this.parent = parent; } /** * Creates a new instance of type calling all that's necessary. * * If T is a class-type all its constructors are searched for the one found best suited (takes the least arguments, ...). * New instances are created for all parameters, then the constructor is called. * * If T represents multiple types (is an interface, abstract class, ...) _all_ implementors of that type are instantiated * After that they get Phi-ed together. * * If T is an array-type a new array of length 1 is generated. * * @todo Do we want to mix in REUSE-Parameters? */ public SSAValue createInstance(final TypeReference T, final boolean asManaged, VariableKey key, Set<? extends SSAValue> seen) { return createInstance(T, asManaged, key, seen, 0); } /* package private */ SSAValue createInstance(final TypeReference T, final boolean asManaged, VariableKey key, Set<? extends SSAValue> seen, int currentDepth) { if (seen == null) { logger.debug("Empty seen"); seen = new HashSet<SSAValue>(); } if (currentDepth > this.maxDepth) { final SSAValue instance = this.pm.getUnmanaged(T, key); instance.setAssigned(); return instance; } // final IClass klass = this.cha.lookupClass(T); { // fetch new value if (asManaged) { if (key == null) { throw new IllegalArgumentException("A managed variable needs a key - null given."); } } else { if (key == null) { key = new UniqueKey(); } } } assert(understands(T)); if (T.equals(AndroidTypes.Context)) { return createContext(T, asManaged, key, seen); } if (T.equals(AndroidTypes.ContextWrapper)) { return createContextWrapper(T, asManaged, key, seen); } return null; } private static final Set<TypeReference> understandTypes = new HashSet<TypeReference>(); static { understandTypes.add(AndroidTypes.Context); understandTypes.add(AndroidTypes.ContextWrapper); } public static boolean understands(TypeReference T) { return understandTypes.contains(T); } // Now for the specialized types... /** * Creates a new instance of android/content/Context. */ public SSAValue createContext(final TypeReference T, final boolean asManaged, VariableKey key, Set<? extends SSAValue> seen) { final List<SSAValue> appComponents = new ArrayList<SSAValue>(); { // TODO: Can we create a tighter conterxt? // TODO: Force an Application-Context? if (AndroidEntryPointManager.MANAGER.doFlatComponents()) { final AndroidModelClass mClass = AndroidModelClass.getInstance(cha); // At a given time context is expected to be only of one component already seen. // If it's seen there is a field in AndroidModelClass. for (final IField f : mClass.getAllFields()) { assert(f.isStatic()) : "All fields of AndroidModelClass are expected to be static! " + f + " is not."; final TypeReference fdType = f.getReference().getFieldType(); { // Test assignable if (! ParameterAccessor.isAssignable(fdType, T, cha) ) { assert(false) : "Unexpected but not fatal - remove assertion if this happens"; continue; } } final VariableKey iKey = new SSAValue.TypeKey(fdType.getName()); final SSAValue instance; if (this.pm.isSeen(iKey)) { instance = this.pm.getCurrent(iKey); } else { final int pc = this.body.getNextProgramCounter(); final VariableKey subKey = new SSAValue.WeaklyNamedKey(fdType.getName(), "ctx" + fdType.getName().getClassName().toString()); instance = this.pm.getUnallocated(fdType, subKey); final SSAInstruction getInst = instructionFactory.GetInstruction(pc, instance, f.getReference()); this.body.addStatement(getInst); this.pm.setAllocation(instance, getInst); } appComponents.add(instance); } } else { for (TypeReference component : AndroidEntryPointManager.MANAGER.getComponents()) { final VariableKey iKey = new SSAValue.TypeKey(component.getName()); if (this.pm.isSeen(iKey)) { final SSAValue instance; instance = this.pm.getCurrent(iKey); assert (instance.getNumber() > 0); appComponents.add(instance); } } } } final SSAValue instance; if ( appComponents.size() == 1) { instance = appComponents.get(0); } else if ( appComponents.size() > 0) { { // Phi them together final int pc = this.body.getNextProgramCounter(); instance = this.pm.getFree(T, key); assert (pc > 0); assert (instance.getNumber() > 0); final SSAInstruction phi = instructionFactory.PhiInstruction(pc, instance, appComponents); this.body.addStatement(phi); this.pm.setPhi(instance, phi); } } else { instance = this.pm.getUnmanaged(T, key); this.body.addConstant(instance.getNumber(), new ConstantValue(null)); instance.setAssigned(); } logger.info("Created Android-Context from " + appComponents.size() + " components"); return instance; } public SSAValue createContextWrapper(final TypeReference T, final boolean asManaged, VariableKey key, Set<? extends SSAValue> seen) { final VariableKey contextKey = new SSAValue.TypeKey(AndroidTypes.ContextName); final SSAValue context; { if (this.pm.isSeen(contextKey)) { context = this.pm.getCurrent(contextKey); } else { context = createContext(AndroidTypes.Context, true, contextKey, seen); } } final SSAValue instance = this.pm.getUnallocated(T, key); { // call: ContextWrapper(Context base) final MethodReference ctor = MethodReference.findOrCreate(T, MethodReference.initAtom, Descriptor.findOrCreate(new TypeName[] { AndroidTypes.ContextName }, TypeReference.VoidName)); final List<SSAValue> params = new ArrayList<SSAValue>(); params.add(context); addCallCtor(instance, ctor, params); } return instance; } /** * Satisfy the interface. */ public int createInstance(TypeReference type, Object... instantiatorArgs) { // public SSAValue createInstance(final TypeReference T, final boolean asManaged, VariableKey key, Set<SSAValue> seen) { if (! (instantiatorArgs[0] instanceof Boolean)) { throw new IllegalArgumentException("Argument 0 to createInstance has to be boolean."); } if (! ((instantiatorArgs[1] == null) || (instantiatorArgs[1] instanceof VariableKey))) { throw new IllegalArgumentException("Argument 1 to createInstance has to be null or an instance of VariableKey"); } if (! ((instantiatorArgs[2] == null) || (instantiatorArgs[2] instanceof Set))) { throw new IllegalArgumentException("Argument 2 to createInstance has to be null or an instance of Set<? extends SSAValue>, " + "got: " + instantiatorArgs[2].getClass()); } final int currentDepth; { if (instantiatorArgs.length == 4) { currentDepth = (Integer) instantiatorArgs[3]; } else { currentDepth = 0; } } if (instantiatorArgs[2] != null) { final Set seen = (Set) instantiatorArgs[2]; if (! seen.isEmpty()) { final Object o = seen.iterator().next(); if (! (o instanceof SSAValue)) { throw new IllegalArgumentException("Argument 2 to createInstance has to be null or an instance of Set<? extends SSAValue>, " + "got Set<" + o.getClass() + ">"); } } } return createInstance(type, (Boolean) instantiatorArgs[0], (VariableKey) instantiatorArgs[1], (Set<? extends SSAValue>) instantiatorArgs[2], currentDepth).getNumber(); } }