1137 lines
46 KiB
Java
1137 lines
46 KiB
Java
/*******************************************************************************
|
|
* Copyright (c) 2002 - 2006 IBM Corporation.
|
|
* All rights reserved. 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
|
|
*
|
|
* Contributors:
|
|
* IBM Corporation - initial API and implementation
|
|
*******************************************************************************/
|
|
package com.ibm.wala.j2ee;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
import com.ibm.wala.analysis.typeInference.ConeType;
|
|
import com.ibm.wala.analysis.typeInference.PointType;
|
|
import com.ibm.wala.analysis.typeInference.TypeAbstraction;
|
|
import com.ibm.wala.classLoader.CallSiteReference;
|
|
import com.ibm.wala.classLoader.IClass;
|
|
import com.ibm.wala.classLoader.IMethod;
|
|
import com.ibm.wala.classLoader.Language;
|
|
import com.ibm.wala.classLoader.NewSiteReference;
|
|
import com.ibm.wala.classLoader.SyntheticMethod;
|
|
import com.ibm.wala.ipa.callgraph.AnalysisScope;
|
|
import com.ibm.wala.ipa.callgraph.CGNode;
|
|
import com.ibm.wala.ipa.callgraph.MethodTargetSelector;
|
|
import com.ibm.wala.ipa.callgraph.impl.FakeRootMethod;
|
|
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
|
import com.ibm.wala.ipa.summaries.BypassSyntheticClass;
|
|
import com.ibm.wala.ipa.summaries.MethodSummary;
|
|
import com.ibm.wala.ipa.summaries.SummarizedMethod;
|
|
import com.ibm.wala.j2ee.util.ReceiverTypeInference;
|
|
import com.ibm.wala.j2ee.util.ReceiverTypeInferenceCache;
|
|
import com.ibm.wala.shrikeBT.BytecodeConstants;
|
|
import com.ibm.wala.shrikeBT.IInvokeInstruction;
|
|
import com.ibm.wala.ssa.SSAFieldAccessInstruction;
|
|
import com.ibm.wala.ssa.SSAInstructionFactory;
|
|
import com.ibm.wala.ssa.SSAInvokeInstruction;
|
|
import com.ibm.wala.ssa.SSANewInstruction;
|
|
import com.ibm.wala.ssa.SSAReturnInstruction;
|
|
import com.ibm.wala.types.ClassLoaderReference;
|
|
import com.ibm.wala.types.Descriptor;
|
|
import com.ibm.wala.types.FieldReference;
|
|
import com.ibm.wala.types.MethodReference;
|
|
import com.ibm.wala.types.TypeName;
|
|
import com.ibm.wala.types.TypeReference;
|
|
import com.ibm.wala.util.collections.HashMapFactory;
|
|
import com.ibm.wala.util.debug.Assertions;
|
|
import com.ibm.wala.util.strings.Atom;
|
|
import com.ibm.wala.util.warnings.Warnings;
|
|
|
|
/**
|
|
* Special bypass rules divined from an EJB deployment descriptor.
|
|
*
|
|
* TODO: refactor this class using the delegation model
|
|
*/
|
|
public class J2EEMethodTargetSelector implements MethodTargetSelector, BytecodeConstants, EJBConstants {
|
|
static final boolean DEBUG = false;
|
|
|
|
private final static Atom addAtom = Atom.findOrCreateUnicodeAtom("add");
|
|
|
|
private final static Descriptor addDesc = Descriptor.findOrCreateUTF8("(Ljava/lang/Object;)Z");
|
|
|
|
private final static MethodReference addMethod = MethodReference.findOrCreate(TypeReference.JavaUtilCollection, addAtom, addDesc);
|
|
|
|
private final static Atom elementsAtom = Atom.findOrCreateUnicodeAtom("elements");
|
|
|
|
private final static Descriptor elementsDesc = Descriptor.findOrCreateUTF8("()Ljava/util/Enumeration");
|
|
|
|
private final static MethodReference elementsMethod = MethodReference.findOrCreate(TypeReference.JavaUtilVector, elementsAtom,
|
|
elementsDesc);
|
|
|
|
private final static MethodReference hashSetInit = MethodReference.findOrCreate(TypeReference.JavaUtilHashSet,
|
|
MethodReference.initAtom, MethodReference.defaultInitDesc);
|
|
|
|
private final static MethodReference vectorInit = MethodReference.findOrCreate(TypeReference.JavaUtilVector,
|
|
MethodReference.initAtom, MethodReference.defaultInitDesc);
|
|
|
|
private final static Atom JAVAX_EJB = J2EEUtil.EJB_HOME.getName().getPackage();
|
|
|
|
/**
|
|
* deployment information
|
|
*/
|
|
private final DeploymentMetaData deployment;
|
|
|
|
/**
|
|
* the governing class hierarchy
|
|
*/
|
|
private final IClassHierarchy cha;
|
|
|
|
/**
|
|
* A cache of synthetic methods generated so far. Mapping from MethodReference -> SyntheticMethods
|
|
*/
|
|
private final Map<MethodReference, SyntheticMethod> map = HashMapFactory.make();
|
|
|
|
/**
|
|
* A cache of TypeInference results
|
|
*/
|
|
private final ReceiverTypeInferenceCache typeInference;
|
|
|
|
/**
|
|
* Governing analysis scope
|
|
*/
|
|
private final AnalysisScope scope;
|
|
|
|
/**
|
|
* the method target selector to use if this one does not bypass it
|
|
*/
|
|
private final MethodTargetSelector parent;
|
|
|
|
private final SSAInstructionFactory insts;
|
|
|
|
/**
|
|
* A mapping for EJB entity contract method names
|
|
*
|
|
* TODO: split for Home vs. Remote interfaces, etc....
|
|
*/
|
|
private static final HashMap<Atom, Atom[]> entityContractMap = HashMapFactory.make(10);
|
|
static {
|
|
entityContractMap.put(CREATE, new Atom[] { EJB_CREATE, EJB_POST_CREATE });
|
|
entityContractMap.put(REMOVE, new Atom[] { EJB_REMOVE });
|
|
entityContractMap.put(GET_PRIMARY_KEY, new Atom[] { GET_PRIMARY_KEY });
|
|
entityContractMap.put(GET_EJB_META_DATA, new Atom[] { GET_EJB_META_DATA });
|
|
entityContractMap.put(GET_EJB_HOME, new Atom[] { GET_EJB_HOME });
|
|
entityContractMap.put(GET_HANDLE, new Atom[] { GET_HANDLE });
|
|
entityContractMap.put(IS_IDENTICAL, new Atom[] { IS_IDENTICAL });
|
|
}
|
|
|
|
/**
|
|
* A mapping from EJB entity contract method name to classes allocated by these methods.
|
|
*/
|
|
private static final HashMap<Atom, TypeReference> entityContractExceptionMap = HashMapFactory.make(5);
|
|
static {
|
|
entityContractExceptionMap.put(CREATE, CreateExceptionClass);
|
|
entityContractExceptionMap.put(REMOVE, RemoveExceptionClass);
|
|
}
|
|
|
|
public J2EEMethodTargetSelector(AnalysisScope scope, MethodTargetSelector parent, DeploymentMetaData deployment,
|
|
IClassHierarchy cha, ReceiverTypeInferenceCache typeInference) {
|
|
this.scope = scope;
|
|
this.deployment = deployment;
|
|
this.parent = parent;
|
|
this.cha = cha;
|
|
this.typeInference = typeInference;
|
|
this.insts = Language.JAVA.instructionFactory();
|
|
}
|
|
|
|
/**
|
|
* Handle a call to an entity's remote or local interface
|
|
*
|
|
* @param m a call to a remote or local interface
|
|
* @return a synthetic method which serves as a target implementation for a call to m, or null if there's a problem
|
|
*/
|
|
private SyntheticMethod hijackEntityInterface(MethodReference m) {
|
|
if (isJavaLangObjectMethod(m))
|
|
return null;
|
|
|
|
if (entityContractMap.containsKey(m.getName())) {
|
|
return findOrCreateEntityContractMethod(m);
|
|
}
|
|
|
|
TypeReference entityType = m.getDeclaringClass();
|
|
BeanMetaData bean = deployment.getBeanForInterface(entityType);
|
|
// resolve the method via the class hierarchy first
|
|
IMethod resolved = cha.resolveMethod(m);
|
|
if (resolved == null) {
|
|
Warnings.add(LoadFailure.create(m));
|
|
return null;
|
|
}
|
|
m = resolved.getReference();
|
|
SyntheticMethod S = map.get(m);
|
|
if (S == null) {
|
|
if (DEBUG) {
|
|
System.err.println(("EJBBypass: create Synthetic case A for " + m));
|
|
}
|
|
if (bean == null) {
|
|
assert false : "no bean bound for " + entityType;
|
|
}
|
|
IMethod ejbMethod = cha.resolveMethod(MethodReference.findOrCreate(bean.getEJBClass(), m.getName(), m.getDescriptor()));
|
|
|
|
assert ejbMethod != null : "Could not find method " + bean.getEJBClass() + " " + m.getName() + " " + m.getDescriptor();
|
|
|
|
MethodSummary summ = new MethodSummary(m);
|
|
int nextLocal = summ.getNumberOfParameters() + 1;
|
|
|
|
// 1. extract bean object from container object
|
|
// TODO: we pretend that the Entity is stateless, which it is not.
|
|
int beanVN = nextLocal++;
|
|
summ.addStatement(insts.GetInstruction(beanVN, J2EEContainerModel.getBeanFieldRef(bean)));
|
|
|
|
// 2. call corresponding method on bean object
|
|
int EXCEPTION_VN = nextLocal++;
|
|
int returnVN = nextLocal++;
|
|
|
|
int[] params = new int[summ.getNumberOfParameters()];
|
|
params[0] = beanVN;
|
|
for (int i = 1; i < params.length; i++) {
|
|
params[i] = i + 1;
|
|
}
|
|
|
|
MethodReference ref = ejbMethod.getReference();
|
|
CallSiteReference site = CallSiteReference.make(summ.getNextProgramCounter(), ref, IInvokeInstruction.Dispatch.VIRTUAL);
|
|
|
|
if (summ.getReturnType() != null) {
|
|
summ.addStatement(insts.InvokeInstruction(returnVN, params, EXCEPTION_VN, site));
|
|
summ.addStatement(insts.ReturnInstruction(returnVN, summ.getReturnType().isPrimitiveType()));
|
|
} else {
|
|
summ.addStatement(insts.InvokeInstruction(params, EXCEPTION_VN, site));
|
|
}
|
|
|
|
// 3. throw RemoteException if remote interface
|
|
if (deployment.isRemoteInterface(m.getDeclaringClass())) {
|
|
int xobj = nextLocal++;
|
|
summ.addStatement(insts.NewInstruction(xobj, NewSiteReference.make(summ.getNextProgramCounter(), RemoteExceptionClass)));
|
|
summ.addStatement(insts.ThrowInstruction(xobj));
|
|
}
|
|
|
|
int xobj = nextLocal++;
|
|
summ.addStatement(insts.NewInstruction(xobj, NewSiteReference.make(summ.getNextProgramCounter(), EJBExceptionClass)));
|
|
summ.addStatement(insts.ThrowInstruction(xobj));
|
|
|
|
IClass C = cha.lookupClass(m.getDeclaringClass());
|
|
S = new SummarizedEJBMethod(bean, m, summ, C);
|
|
map.put(m, S);
|
|
}
|
|
|
|
if (DEBUG) {
|
|
System.err.println(("EJBBypass: case A return " + S));
|
|
}
|
|
return S;
|
|
}
|
|
|
|
/**
|
|
* TODO: refactor extract common code with other hijack methods
|
|
*
|
|
* @param m
|
|
* @return a Synthetic method representing the a container-implemented method for the onMessage MDB entrypoint
|
|
*/
|
|
private SyntheticMethod hijackOnMessageEntrypoint(MethodReference m) {
|
|
|
|
TypeReference mdbType = m.getDeclaringClass();
|
|
BeanMetaData bean = deployment.getBeanMetaData(mdbType);
|
|
// resolve the method via the class hierarchy first
|
|
IMethod resolved = cha.resolveMethod(m);
|
|
if (resolved == null) {
|
|
Warnings.add(LoadFailure.create(m));
|
|
return null;
|
|
}
|
|
m = resolved.getReference();
|
|
SyntheticMethod S = map.get(m);
|
|
if (S == null) {
|
|
if (DEBUG) {
|
|
System.err.println(("EJBBypass: create Synthetic case A for " + m));
|
|
}
|
|
if (bean == null) {
|
|
assert false : "no bean bound for " + mdbType;
|
|
}
|
|
MethodSummary summ = new MethodSummary(m);
|
|
int nextLocal = summ.getNumberOfParameters() + 1;
|
|
|
|
// 1. extract bean object from container object
|
|
// TODO: we pretend that the Entity is stateless, which it is not.
|
|
int beanVN = nextLocal++;
|
|
summ.addStatement(insts.GetInstruction(beanVN, J2EEContainerModel.getBeanFieldRef(bean)));
|
|
|
|
// 2. call corresponding method on bean object
|
|
int EXCEPTION_VN = nextLocal++;
|
|
int returnVN = nextLocal++;
|
|
|
|
int[] params = new int[summ.getNumberOfParameters()];
|
|
params[0] = beanVN;
|
|
for (int i = 1; i < params.length; i++) {
|
|
params[i] = i + 1;
|
|
}
|
|
|
|
MethodReference ref = resolved.getReference();
|
|
CallSiteReference site = CallSiteReference.make(summ.getNextProgramCounter(), ref, IInvokeInstruction.Dispatch.VIRTUAL);
|
|
|
|
if (summ.getReturnType() != null) {
|
|
summ.addStatement(insts.InvokeInstruction(returnVN, params, EXCEPTION_VN, site));
|
|
summ.addStatement(insts.ReturnInstruction(returnVN, summ.getReturnType().isPrimitiveType()));
|
|
} else {
|
|
summ.addStatement(insts.InvokeInstruction(params, EXCEPTION_VN, site));
|
|
}
|
|
|
|
// 3. throw RemoteException if remote interface
|
|
if (deployment.isRemoteInterface(m.getDeclaringClass())) {
|
|
int xobj = nextLocal++;
|
|
summ.addStatement(insts.NewInstruction(xobj, NewSiteReference.make(summ.getNextProgramCounter(), RemoteExceptionClass)));
|
|
summ.addStatement(insts.ThrowInstruction(xobj));
|
|
}
|
|
|
|
int xobj = nextLocal++;
|
|
summ.addStatement(insts.NewInstruction(xobj, NewSiteReference.make(summ.getNextProgramCounter(), EJBExceptionClass)));
|
|
summ.addStatement(insts.ThrowInstruction(xobj));
|
|
|
|
IClass C = cha.lookupClass(m.getDeclaringClass());
|
|
S = new SummarizedEJBMethod(bean, m, summ, C);
|
|
map.put(m, S);
|
|
}
|
|
|
|
if (DEBUG) {
|
|
System.err.println(("EJBBypass: case A return " + S));
|
|
}
|
|
return S;
|
|
}
|
|
|
|
/**
|
|
* @param m
|
|
* @return a Synthetic method representing a servlet entrypoint to call
|
|
*/
|
|
private SyntheticMethod hijackServletEntrypoint(MethodReference m) {
|
|
|
|
SyntheticMethod S = map.get(m);
|
|
if (S == null) {
|
|
MethodSummary summ = new MethodSummary(m);
|
|
int nextLocal = summ.getNumberOfParameters() + 1;
|
|
|
|
int EXCEPTION_VN = nextLocal++;
|
|
int returnVN = nextLocal++;
|
|
|
|
// allocate a ServletConfig object
|
|
if (!m.getDeclaringClass().getClassLoader().equals(ClassLoaderReference.Primordial)) {
|
|
int servletConfigVN = nextLocal++;
|
|
NewSiteReference newConfig = NewSiteReference.make(summ.getNextProgramCounter(), ServletEntrypoints.WalaServletConfigModel);
|
|
summ.addStatement(insts.NewInstruction(servletConfigVN, newConfig));
|
|
|
|
// initialize the servlet
|
|
CallSiteReference site = CallSiteReference.make(summ.getNextProgramCounter(), ServletEntrypoints.servletInit,
|
|
IInvokeInstruction.Dispatch.INTERFACE);
|
|
int[] params = new int[2];
|
|
params[0] = 1; // "this" pointer
|
|
params[1] = servletConfigVN;
|
|
summ.addStatement(insts.InvokeInstruction(params, EXCEPTION_VN, site));
|
|
}
|
|
|
|
int[] params = new int[summ.getNumberOfParameters()];
|
|
params[0] = 1; // "this" pointer
|
|
for (int i = 1; i < params.length; i++) {
|
|
params[i] = i + 1;
|
|
}
|
|
// invoke the desired entrypoint
|
|
CallSiteReference site = CallSiteReference.make(summ.getNextProgramCounter(), m, IInvokeInstruction.Dispatch.VIRTUAL);
|
|
if (!summ.getReturnType().equals(TypeReference.Void)) {
|
|
summ.addStatement(insts.InvokeInstruction(returnVN, params, EXCEPTION_VN, site));
|
|
summ.addStatement(insts.ReturnInstruction(returnVN, summ.getReturnType().isPrimitiveType()));
|
|
} else {
|
|
summ.addStatement(insts.InvokeInstruction(params, EXCEPTION_VN, site));
|
|
}
|
|
|
|
IClass C = cha.lookupClass(m.getDeclaringClass());
|
|
S = new SummarizedMethod(m, summ, C);
|
|
map.put(m, S);
|
|
}
|
|
|
|
return S;
|
|
}
|
|
|
|
/**
|
|
* Handle a call to an entity's home or local home
|
|
*
|
|
* @param m a call to a home or local home interface
|
|
* @return a synthetic method which serves as a target implementation for a call to m
|
|
*/
|
|
private SyntheticMethod hijackHomeInterface(MethodReference m) {
|
|
if (isJavaLangObjectMethod(m))
|
|
return null;
|
|
|
|
TypeReference type = m.getDeclaringClass();
|
|
IClass C = cha.lookupClass(type);
|
|
BeanMetaData bean = deployment.getBeanForInterface(type);
|
|
assert bean != null : "null bean for " + type;
|
|
boolean local = deployment.isLocalHomeInterface(type);
|
|
|
|
if (deployment.isFinder(m)) {
|
|
MethodSummary summ = (local) ? new LocalHomeFinderSummary(m) : new RemoteFinderSummary(m);
|
|
SummarizedEJBMethod S = new SummarizedEJBMethod(bean, m, summ, C);
|
|
map.put(m, S);
|
|
return S;
|
|
} else if (entityContractMap.containsKey(m.getName())) {
|
|
return findOrCreateEntityContractMethod(m);
|
|
} else if (isHomeMethod(m, bean)) {
|
|
MethodSummary summ = new HomeMethodSummary(m, bean);
|
|
SummarizedEJBMethod S = new SummarizedEJBMethod(bean, m, summ, C);
|
|
map.put(m, S);
|
|
return S;
|
|
} else
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Handle a call to a CMP or CMR getter or setter
|
|
*
|
|
* @param m a call to a CMP or CMR getter of setter
|
|
* @return a synthetic method which serves as a target implementation for a call to m
|
|
*/
|
|
private SyntheticMethod hijackCMPBeanMethods(MethodReference m) {
|
|
|
|
TypeReference type = m.getDeclaringClass();
|
|
BeanMetaData bean = deployment.getBeanForInterface(type);
|
|
SyntheticMethod S = null;
|
|
if (deployment.isCMPGetter(m)) {
|
|
// get declaring instance from class hierarchy
|
|
m = cha.resolveMethod(m).getReference();
|
|
// return a summary for a CMP getter
|
|
S = map.get(m);
|
|
if (S == null) {
|
|
if (deployment.isCMRGetter(m)) {
|
|
CMRGetterSummary g = new CMRGetterSummary(m);
|
|
S = createSummarizedMethod(bean, m, g);
|
|
} else {
|
|
GetterSummary g = new GetterSummary(m);
|
|
S = createSummarizedMethod(bean, m, g);
|
|
}
|
|
}
|
|
return S;
|
|
} else if (deployment.isCMPSetter(m)) {
|
|
// get declaring instance from class hierarchy
|
|
m = cha.resolveMethod(m).getReference();
|
|
// return a summary for CMP setter
|
|
S = map.get(m);
|
|
if (S == null) {
|
|
if (deployment.isCMRSetter(m)) {
|
|
CMRSetterSummary s = new CMRSetterSummary(m);
|
|
S = createSummarizedMethod(bean, m, s);
|
|
} else {
|
|
SetterSummary s = new SetterSummary(m);
|
|
S = createSummarizedMethod(bean, m, s);
|
|
}
|
|
}
|
|
return S;
|
|
} else
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* If m is a special EJB-container generated method, return the IMethod that represents the modelled target of a call to m. Else,
|
|
* return null;
|
|
*/
|
|
private SyntheticMethod methodReferenceIntercept(MethodReference m) {
|
|
TypeReference type = m.getDeclaringClass();
|
|
if (DEBUG) {
|
|
System.err.println(("EJBBypass: intercept? " + m));
|
|
}
|
|
|
|
if (type.getClassLoader().equals(ClassLoaderReference.Primordial)) {
|
|
// small optimization: we know we will never hijack calls to the
|
|
// primordial loader
|
|
return null;
|
|
}
|
|
|
|
if (deployment.isLocalInterface(type) || deployment.isRemoteInterface(type)) {
|
|
return hijackEntityInterface(m);
|
|
} else if (deployment.isLocalHomeInterface(type) || deployment.isHomeInterface(type)) {
|
|
return hijackHomeInterface(m);
|
|
} else if (deployment.isContainerManaged(type)) {
|
|
return hijackCMPBeanMethods(m);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private boolean isJavaLangObjectMethod(MethodReference m) {
|
|
IClass object = cha.lookupClass(TypeReference.JavaLangObject);
|
|
IMethod declaredMethod = object.getMethod(m.getSelector());
|
|
return declaredMethod != null;
|
|
}
|
|
|
|
private SyntheticMethod createSummarizedMethod(BeanMetaData bean, MethodReference m, MethodSummary g) {
|
|
IClass C = cha.lookupClass(m.getDeclaringClass());
|
|
SyntheticMethod S = new SummarizedEJBMethod(bean, m, g, C);
|
|
map.put(m, S);
|
|
return S;
|
|
}
|
|
|
|
/**
|
|
* @param m
|
|
* @return a Synthetic method representing the a container-implemented method for the EJB Entity contract
|
|
*/
|
|
private SyntheticMethod findOrCreateEntityContractMethod(MethodReference m) {
|
|
if (DEBUG) {
|
|
System.err.println(("findOrCreateEntityContractMethod " + m));
|
|
}
|
|
SyntheticMethod S = map.get(m);
|
|
if (S == null) {
|
|
|
|
TypeReference rType = m.getReturnType();
|
|
TypeReference receiverType = m.getDeclaringClass();
|
|
IClass receiverClass = cha.lookupClass(receiverType);
|
|
BeanMetaData bean = deployment.getBeanForInterface(receiverType);
|
|
TypeReference ejbType = bean.getEJBClass();
|
|
IClass ejbClass = cha.lookupClass(ejbType);
|
|
|
|
MethodSummary summ = new MethodSummary(m);
|
|
int nextLocal = summ.getNumberOfParameters() + 1;
|
|
|
|
// get ejb object from pool
|
|
int alloc = nextLocal++;
|
|
summ.addStatement(insts.GetInstruction(alloc, J2EEContainerModel.getBeanFieldRef(bean)));
|
|
|
|
// create+return entity object, if appropriate
|
|
if (rType != TypeReference.Void) {
|
|
|
|
// create result object
|
|
int ret = nextLocal++;
|
|
summ.addStatement(insts.NewInstruction(ret, NewSiteReference.make(summ.getNextProgramCounter(), rType)));
|
|
|
|
// return it
|
|
summ.addStatement(insts.ReturnInstruction(ret, false));
|
|
}
|
|
|
|
// call contract methods
|
|
Atom[] names = entityContractMap.get(m.getName());
|
|
for (int i = 0; i < names.length; i++) {
|
|
Atom name = names[i];
|
|
MethodReference ref = makeEntityContractMethod(bean, m, ejbType, name);
|
|
CallSiteReference site = CallSiteReference.make(summ.getNextProgramCounter(), ref,
|
|
ejbClass.isInterface() ? IInvokeInstruction.Dispatch.INTERFACE : IInvokeInstruction.Dispatch.VIRTUAL);
|
|
if (cha.resolveMethod(ejbClass, ref.getSelector()) == null)
|
|
continue;
|
|
int[] params = new int[summ.getNumberOfParameters()];
|
|
// set up the dispatch to the bean object
|
|
params[0] = alloc;
|
|
for (int j = 1; j < params.length; j++) {
|
|
params[j] = j + 1;
|
|
}
|
|
// note that we reserve value number 2 to hold the exceptional result
|
|
// of the call.
|
|
if (!ref.getReturnType().equals(TypeReference.Void)) {
|
|
summ.addStatement(insts.InvokeInstruction(nextLocal++, params, nextLocal++, site));
|
|
} else {
|
|
summ.addStatement(insts.InvokeInstruction(params, nextLocal++, site));
|
|
}
|
|
}
|
|
|
|
final TypeReference t = entityContractExceptionMap.get(m.getName());
|
|
if (t != null) {
|
|
int ex = nextLocal++;
|
|
summ.addStatement(insts.NewInstruction(ex, NewSiteReference.make(summ.getNextProgramCounter(), t)));
|
|
summ.addStatement(insts.ThrowInstruction(ex));
|
|
}
|
|
|
|
if (deployment.isRemoteInterface(m.getDeclaringClass()) || deployment.isHomeInterface(m.getDeclaringClass())) {
|
|
int xobj = nextLocal++;
|
|
summ.addStatement(insts.NewInstruction(xobj, NewSiteReference.make(summ.getNextProgramCounter(), RemoteExceptionClass)));
|
|
summ.addStatement(insts.ThrowInstruction(xobj));
|
|
}
|
|
|
|
int ejbException = nextLocal++;
|
|
summ.addStatement(insts.NewInstruction(ejbException, NewSiteReference.make(summ.getNextProgramCounter(), EJBExceptionClass)));
|
|
summ.addStatement(insts.ThrowInstruction(ejbException));
|
|
|
|
S = new SummarizedMethod(m, summ, receiverClass);
|
|
map.put(m, S);
|
|
}
|
|
return S;
|
|
}
|
|
|
|
/**
|
|
* @param bean metadata regarding the EJB
|
|
* @param ifaceMethod name of a method called on an EJB interface
|
|
* @param ejbType concrete type the method should dispatch to
|
|
* @param methodName name of the bean method to dispatch to
|
|
* @return a method reference representing the dispatch target
|
|
*/
|
|
private MethodReference makeEntityContractMethod(BeanMetaData bean, MethodReference ifaceMethod, TypeReference ejbType,
|
|
Atom methodName) {
|
|
assert bean != null;
|
|
TypeName returnType = TypeReference.VoidName;
|
|
if (methodName.equals(EJB_CREATE) && bean.isContainerManagedEntity()) {
|
|
// ejbCreate returns the primary key type.
|
|
returnType = bean.getPrimaryKeyType().getName();
|
|
}
|
|
MethodReference ref = MethodReference.findOrCreate(ejbType, methodName, Descriptor.findOrCreate(ifaceMethod.getDescriptor()
|
|
.getParameters(), returnType));
|
|
return ref;
|
|
}
|
|
|
|
private class GetterSummary extends MethodSummary {
|
|
public GetterSummary(final MethodReference method) {
|
|
super(method);
|
|
FieldReference field = deployment.getCMPField(method);
|
|
int nextLocal = 2;
|
|
|
|
// the reference dispatched on is value number 1, and
|
|
// we store the result in value number nextLocal++
|
|
|
|
// TODO: we should make sure that finders populate all fields.
|
|
int result = nextLocal++;
|
|
SSAFieldAccessInstruction f = insts.GetInstruction(result, 1, field);
|
|
addStatement(f);
|
|
|
|
SSAReturnInstruction r = insts.ReturnInstruction(result, field.getFieldType().isPrimitiveType());
|
|
addStatement(r);
|
|
|
|
if (deployment.isRemoteInterface(method.getDeclaringClass())) {
|
|
int xobj = nextLocal++;
|
|
addStatement(insts.NewInstruction(xobj, NewSiteReference.make(getNextProgramCounter(), RemoteExceptionClass)));
|
|
addStatement(insts.ThrowInstruction(xobj));
|
|
}
|
|
int xobj = nextLocal++;
|
|
addStatement(insts.NewInstruction(xobj, NewSiteReference.make(getNextProgramCounter(), EJBExceptionClass)));
|
|
addStatement(insts.ThrowInstruction(xobj));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A synthetic model of a CMR getter method
|
|
*/
|
|
private class CMRGetterSummary extends MethodSummary {
|
|
public CMRGetterSummary(final MethodReference method) {
|
|
super(method);
|
|
FieldReference field = deployment.getCMPField(method);
|
|
int nextLocal = 2;
|
|
TypeReference T = field.getFieldType();
|
|
T = cha.lookupClass(T).getReference();
|
|
|
|
BeanMetaData getteeBean = deployment.getCMRBean(field);
|
|
TypeReference getteeType = getteeBean.getLocalInterface();
|
|
TypeReference getteeHomeType = getteeBean.getLocalHomeInterface();
|
|
TypeReference keyType = getteeBean.getPrimaryKeyType();
|
|
|
|
Atom finderName = Atom.findOrCreateUnicodeAtom("findByPrimaryKey");
|
|
Descriptor finderDesc = Descriptor.findOrCreate(new TypeName[] { keyType.getName() }, getteeType.getName());
|
|
IMethod finder = cha.resolveMethod(MethodReference.findOrCreate(getteeHomeType, finderName, finderDesc));
|
|
if (finder == null) {
|
|
// Here's a bad kludge ... it appears that the primary key type might be
|
|
// a subclass of the
|
|
// argument of the descriptor. TODO: figure out how this happens.
|
|
// temp fix: Try Ojbect instead ...
|
|
finderDesc = Descriptor.findOrCreate(new TypeName[] { TypeReference.JavaLangObject.getName() }, getteeType.getName());
|
|
finder = cha.resolveMethod(MethodReference.findOrCreate(getteeHomeType, finderName, finderDesc));
|
|
assert finder != null : "failed to find findByPrimaryKey for " + getteeType;
|
|
}
|
|
|
|
// allocate type of primary key
|
|
// TODO: should probably pretend the object has a field of this type
|
|
NewSiteReference nsr = NewSiteReference.make(getNextProgramCounter(), keyType);
|
|
int keyAlloc = nextLocal++;
|
|
addStatement(insts.NewInstruction(keyAlloc, nsr));
|
|
|
|
// allocate local home type
|
|
// TODO: this is fake; should model container providing home somehow
|
|
NewSiteReference lhnr = NewSiteReference.make(getNextProgramCounter(), getteeHomeType);
|
|
int localHomeAlloc = nextLocal++;
|
|
addStatement(insts.NewInstruction(localHomeAlloc, lhnr));
|
|
|
|
// call findByPrimaryKey
|
|
int fr = nextLocal++;
|
|
int ignoredExceptions = nextLocal++;
|
|
CallSiteReference fcsr = CallSiteReference.make(getNextProgramCounter(), finder.getReference(),
|
|
IInvokeInstruction.Dispatch.INTERFACE);
|
|
addStatement(insts.InvokeInstruction(fr, new int[] { localHomeAlloc, keyAlloc }, ignoredExceptions, fcsr));
|
|
|
|
// return result, as set if appropriate
|
|
if (T.equals(TypeReference.JavaUtilSet) || T.equals(TypeReference.JavaUtilCollection)) {
|
|
// assume HashSet is the type of the returned collection.
|
|
int setObj = nextLocal++;
|
|
NewSiteReference setRef = NewSiteReference.make(getNextProgramCounter(), TypeReference.JavaUtilHashSet);
|
|
SSANewInstruction n = insts.NewInstruction(setObj, setRef);
|
|
addStatement(n);
|
|
|
|
int initIgnoredExceptions = nextLocal++;
|
|
CallSiteReference initRef = CallSiteReference.make(getNextProgramCounter(), hashSetInit,
|
|
IInvokeInstruction.Dispatch.SPECIAL);
|
|
addStatement(insts.InvokeInstruction(new int[] { setObj }, initIgnoredExceptions, initRef));
|
|
|
|
int ignoredResult = nextLocal++;
|
|
int moreIgnoredExceptions = nextLocal++;
|
|
CallSiteReference addRef = CallSiteReference
|
|
.make(getNextProgramCounter(), addMethod, IInvokeInstruction.Dispatch.INTERFACE);
|
|
SSAInvokeInstruction addCall = insts.InvokeInstruction(ignoredResult, new int[] { setObj, fr }, moreIgnoredExceptions,
|
|
addRef);
|
|
addStatement(addCall);
|
|
|
|
SSAReturnInstruction r = insts.ReturnInstruction(setObj, false);
|
|
addStatement(r);
|
|
} else {
|
|
|
|
SSAReturnInstruction r = insts.ReturnInstruction(fr, false);
|
|
addStatement(r);
|
|
}
|
|
|
|
// put in a bogus getfield to pacify old clients that expect
|
|
// to see a getfield here. TODO: rewrite the old clients to
|
|
// avoid needing this instruction.
|
|
int ignore = nextLocal++;
|
|
SSAFieldAccessInstruction f = insts.GetInstruction(ignore, 1, field);
|
|
addStatement(f);
|
|
|
|
if (deployment.isRemoteInterface(method.getDeclaringClass())) {
|
|
int xobj = nextLocal++;
|
|
addStatement(insts.NewInstruction(xobj, NewSiteReference.make(getNextProgramCounter(), RemoteExceptionClass)));
|
|
addStatement(insts.ThrowInstruction(xobj));
|
|
}
|
|
int xobj = nextLocal++;
|
|
addStatement(insts.NewInstruction(xobj, NewSiteReference.make(getNextProgramCounter(), EJBExceptionClass)));
|
|
addStatement(insts.ThrowInstruction(xobj));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A synthetic model of a CMP setter method
|
|
*/
|
|
private class SetterSummary extends MethodSummary {
|
|
public SetterSummary(final MethodReference method) {
|
|
super(method);
|
|
// the reference dispatched on is value number 1, and
|
|
// the value stored is value number 2
|
|
SSAFieldAccessInstruction f = insts.PutInstruction(1, 2, deployment.getCMPField(method));
|
|
addStatement(f);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A synthetic model of a CMR setter method
|
|
*/
|
|
private class CMRSetterSummary extends MethodSummary {
|
|
public CMRSetterSummary(final MethodReference method) {
|
|
super(method);
|
|
// the reference dispatched on is value number 1, and
|
|
// the value stored is value number 2
|
|
FieldReference field = deployment.getCMPField(method);
|
|
TypeReference T = field.getFieldType();
|
|
int nextLocal = 3;
|
|
T = cha.lookupClass(T).getReference();
|
|
if (T.equals(TypeReference.JavaUtilSet) || T.equals(TypeReference.JavaUtilCollection)) {
|
|
// assume HashSet is the type of the returned collection.
|
|
int setObj = nextLocal++;
|
|
NewSiteReference setRef = NewSiteReference.make(getNextProgramCounter(), TypeReference.JavaUtilHashSet);
|
|
SSANewInstruction allocSet = insts.NewInstruction(setObj, setRef);
|
|
addStatement(allocSet);
|
|
|
|
int initIgnoredExceptions = nextLocal++;
|
|
CallSiteReference initRef = CallSiteReference.make(getNextProgramCounter(), hashSetInit,
|
|
IInvokeInstruction.Dispatch.SPECIAL);
|
|
addStatement(insts.InvokeInstruction(new int[] { setObj }, initIgnoredExceptions, initRef));
|
|
|
|
int ignoredResult = nextLocal++;
|
|
int moreIgnoredExceptions = nextLocal++;
|
|
CallSiteReference addRef = CallSiteReference
|
|
.make(getNextProgramCounter(), addMethod, IInvokeInstruction.Dispatch.INTERFACE);
|
|
SSAInvokeInstruction addCall = insts.InvokeInstruction(ignoredResult, new int[] { setObj, 2 }, moreIgnoredExceptions,
|
|
addRef);
|
|
addStatement(addCall);
|
|
|
|
SSAFieldAccessInstruction f2 = insts.PutInstruction(1, setObj, field);
|
|
addStatement(f2);
|
|
} else {
|
|
SSAFieldAccessInstruction f2 = insts.PutInstruction(1, 2, field);
|
|
addStatement(f2);
|
|
}
|
|
|
|
// create a bogus instance of the the bean on the other side
|
|
// of the relationship. TODO: is it OK to use the local interface?
|
|
// perhaps sometimes should use the remote interface ...
|
|
|
|
BeanMetaData otherBean = deployment.getCMRBean(field);
|
|
TypeReference otherType = otherBean.getLocalInterface();
|
|
NewSiteReference newRef = NewSiteReference.make(getNextProgramCounter(), otherType);
|
|
int otherInstance = nextLocal++;
|
|
SSANewInstruction n = insts.NewInstruction(otherInstance, newRef);
|
|
addStatement(n);
|
|
|
|
// model a putfield on the other instance
|
|
// TODO: for now it doesn't matter what we shove in this field ... since
|
|
// when we call the getter we'll return a synthetic answer.
|
|
// fix this if and when it becomes a problem.
|
|
FieldReference oppField = deployment.getOppositeField(field);
|
|
if (oppField == null) {
|
|
// this is a problem ... the field is not navigable .... need to do
|
|
// something better, like
|
|
// create a synthetic field.
|
|
Warnings.add(LoadFailure.create(field));
|
|
return;
|
|
}
|
|
TypeReference otherT = oppField.getFieldType();
|
|
otherT = cha.lookupClass(otherT).getReference();
|
|
if (otherT.equals(TypeReference.JavaUtilSet) || otherT.equals(TypeReference.JavaUtilCollection)) {
|
|
// assume HashSet is the type of the returned collection.
|
|
int setObj = nextLocal++;
|
|
NewSiteReference setRef = NewSiteReference.make(getNextProgramCounter(), TypeReference.JavaUtilHashSet);
|
|
SSANewInstruction allocSet = insts.NewInstruction(setObj, setRef);
|
|
addStatement(allocSet);
|
|
|
|
int initIgnoredExceptions = nextLocal++;
|
|
CallSiteReference initRef = CallSiteReference.make(getNextProgramCounter(), hashSetInit,
|
|
IInvokeInstruction.Dispatch.SPECIAL);
|
|
addStatement(insts.InvokeInstruction(new int[] { setObj }, initIgnoredExceptions, initRef));
|
|
|
|
int ignoredResult = nextLocal++;
|
|
int moreIgnoredExceptions = nextLocal++;
|
|
CallSiteReference addRef = CallSiteReference
|
|
.make(getNextProgramCounter(), addMethod, IInvokeInstruction.Dispatch.INTERFACE);
|
|
SSAInvokeInstruction addCall = insts.InvokeInstruction(ignoredResult, new int[] { setObj, 1 }, moreIgnoredExceptions,
|
|
addRef);
|
|
addStatement(addCall);
|
|
|
|
SSAFieldAccessInstruction f2 = insts.PutInstruction(otherInstance, setObj, oppField);
|
|
addStatement(f2);
|
|
} else {
|
|
SSAFieldAccessInstruction f2 = insts.PutInstruction(otherInstance, 1, oppField);
|
|
addStatement(f2);
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean isHomeMethod(MethodReference method, BeanMetaData bean) {
|
|
TypeReference ejbType = bean.getEJBClass();
|
|
IClass ejbClass = cha.lookupClass(ejbType);
|
|
|
|
Atom newName = Atom.findOrCreateUnicodeAtom("ejb" + method.getName().toString());
|
|
|
|
TypeReference rType = method.getReturnType();
|
|
|
|
// call approriate method
|
|
MethodReference ref = MethodReference.findOrCreate(ejbType, newName, Descriptor.findOrCreate(method.getDescriptor()
|
|
.getParameters(), rType.getName()));
|
|
|
|
return cha.resolveMethod(ejbClass, ref.getSelector()) != null;
|
|
}
|
|
|
|
/**
|
|
* model for a user-defined method on a home or local home interface
|
|
*/
|
|
private class HomeMethodSummary extends MethodSummary {
|
|
/**
|
|
* @param method the interface method summarized
|
|
*/
|
|
public HomeMethodSummary(MethodReference method, BeanMetaData bean) {
|
|
super(method);
|
|
TypeReference ejbType = bean.getEJBClass();
|
|
IClass ejbClass = cha.lookupClass(ejbType);
|
|
|
|
Atom newName = Atom.findOrCreateUnicodeAtom("ejb" + method.getName().toString());
|
|
int nextLocal = getNumberOfParameters() + 1;
|
|
|
|
// get ejb object from pool
|
|
int ejbObject = nextLocal++;
|
|
addStatement(insts.GetInstruction(ejbObject, J2EEContainerModel.getBeanFieldRef(bean)));
|
|
|
|
TypeReference rType = method.getReturnType();
|
|
|
|
// call approriate method
|
|
MethodReference ref = MethodReference.findOrCreate(ejbType, newName, Descriptor.findOrCreate(method.getDescriptor()
|
|
.getParameters(), rType.getName()));
|
|
CallSiteReference site = CallSiteReference.make(getNextProgramCounter(), ref, IInvokeInstruction.Dispatch.VIRTUAL);
|
|
if (cha.resolveMethod(ejbClass, ref.getSelector()) == null) {
|
|
return;
|
|
}
|
|
|
|
int[] params = new int[getNumberOfParameters()];
|
|
// set up the dispatch to the bean object
|
|
params[0] = ejbObject;
|
|
for (int j = 1; j < params.length; j++) {
|
|
params[j] = j + 1;
|
|
}
|
|
// note that we reserve a value number to hold the exceptional result
|
|
// of the call.
|
|
if (rType.equals(TypeReference.Void)) {
|
|
addStatement(insts.InvokeInstruction(params, nextLocal++, site));
|
|
} else {
|
|
int ret = nextLocal++;
|
|
addStatement(insts.InvokeInstruction(ret, params, nextLocal++, site));
|
|
addStatement(insts.ReturnInstruction(ret, rType.isPrimitiveType()));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// TODO!!! I think a finder really should may populate each field
|
|
// in the found object. Probably should create a Hydrate method
|
|
// summary for this.
|
|
private class LocalHomeFinderSummary extends MethodSummary {
|
|
protected int nextLocal;
|
|
|
|
public LocalHomeFinderSummary(final MethodReference method) {
|
|
super(method);
|
|
|
|
nextLocal = method.getNumberOfParameters() + 2;
|
|
TypeReference rType = cha.lookupClass(method.getReturnType()).getReference();
|
|
TypeReference homeType = method.getDeclaringClass();
|
|
TypeReference beanType = deployment.getFinderBeanType(method);
|
|
BeanMetaData beanData = deployment.getBeanMetaData(beanType);
|
|
TypeReference entType = (deployment.isLocalHomeInterface(homeType) ? beanData.getLocalInterface() : beanData
|
|
.getRemoteInterface());
|
|
|
|
// create interface object
|
|
int result2 = nextLocal++;
|
|
NewSiteReference ref2 = NewSiteReference.make(getNextProgramCounter(), entType);
|
|
SSANewInstruction a2 = insts.NewInstruction(result2, ref2);
|
|
addStatement(a2);
|
|
|
|
if (rType.equals(TypeReference.JavaUtilCollection) || rType.equals(TypeReference.JavaUtilSet)) {
|
|
// assume that the finder returns a HashSet
|
|
int result3 = nextLocal++;
|
|
NewSiteReference ref3 = NewSiteReference.make(getNextProgramCounter(), TypeReference.JavaUtilHashSet);
|
|
SSANewInstruction a3 = insts.NewInstruction(result3, ref3);
|
|
addStatement(a3);
|
|
|
|
int initIgnoredExceptions = nextLocal++;
|
|
CallSiteReference initRef = CallSiteReference.make(getNextProgramCounter(), hashSetInit,
|
|
IInvokeInstruction.Dispatch.SPECIAL);
|
|
addStatement(insts.InvokeInstruction(new int[] { result3 }, initIgnoredExceptions, initRef));
|
|
|
|
int ignoredResult = nextLocal++;
|
|
int ignoredExceptions = nextLocal++;
|
|
CallSiteReference addRef = CallSiteReference
|
|
.make(getNextProgramCounter(), addMethod, IInvokeInstruction.Dispatch.INTERFACE);
|
|
SSAInvokeInstruction addCall = insts.InvokeInstruction(ignoredResult, new int[] { result3, result2 }, ignoredExceptions,
|
|
addRef);
|
|
addStatement(addCall);
|
|
|
|
SSAReturnInstruction r = insts.ReturnInstruction(result3, false);
|
|
addStatement(r);
|
|
} else if (rType.equals(TypeReference.JavaUtilEnum)) {
|
|
int result3 = nextLocal++;
|
|
NewSiteReference ref3 = NewSiteReference.make(getNextProgramCounter(), TypeReference.JavaUtilVector);
|
|
SSANewInstruction a3 = insts.NewInstruction(result3, ref3);
|
|
addStatement(a3);
|
|
|
|
int initIgnoredExceptions = nextLocal++;
|
|
CallSiteReference initRef = CallSiteReference
|
|
.make(getNextProgramCounter(), vectorInit, IInvokeInstruction.Dispatch.SPECIAL);
|
|
addStatement(insts.InvokeInstruction(new int[] { result3 }, initIgnoredExceptions, initRef));
|
|
|
|
int ignoredResult = nextLocal++;
|
|
int ignoredExceptions = nextLocal++;
|
|
CallSiteReference addRef = CallSiteReference
|
|
.make(getNextProgramCounter(), addMethod, IInvokeInstruction.Dispatch.INTERFACE);
|
|
SSAInvokeInstruction addCall = insts.InvokeInstruction(ignoredResult, new int[] { result3, result2 }, ignoredExceptions,
|
|
addRef);
|
|
addStatement(addCall);
|
|
|
|
int result4 = nextLocal++;
|
|
int moreIgnoredExceptions = nextLocal++;
|
|
CallSiteReference elementsRef = CallSiteReference.make(getNextProgramCounter(), elementsMethod,
|
|
IInvokeInstruction.Dispatch.VIRTUAL);
|
|
SSAInvokeInstruction elementsCall = insts.InvokeInstruction(result4, new int[] { result3 }, moreIgnoredExceptions,
|
|
elementsRef);
|
|
addStatement(elementsCall);
|
|
|
|
SSAReturnInstruction r = insts.ReturnInstruction(result4, false);
|
|
addStatement(r);
|
|
} else {
|
|
int xobj = nextLocal++;
|
|
addStatement(insts.NewInstruction(xobj, NewSiteReference.make(getNextProgramCounter(), ObjectNotFoundExceptionClass)));
|
|
addStatement(insts.ThrowInstruction(xobj));
|
|
|
|
SSAReturnInstruction r = insts.ReturnInstruction(result2, false);
|
|
addStatement(r);
|
|
}
|
|
|
|
int xobj2 = nextLocal++;
|
|
addStatement(insts.NewInstruction(xobj2, NewSiteReference.make(getNextProgramCounter(), FinderExceptionClass)));
|
|
addStatement(insts.ThrowInstruction(xobj2));
|
|
|
|
int xobj3 = nextLocal++;
|
|
addStatement(insts.NewInstruction(xobj3, NewSiteReference.make(getNextProgramCounter(), EJBExceptionClass)));
|
|
addStatement(insts.ThrowInstruction(xobj3));
|
|
|
|
}
|
|
}
|
|
|
|
private class RemoteFinderSummary extends LocalHomeFinderSummary {
|
|
public RemoteFinderSummary(final MethodReference method) {
|
|
super(method);
|
|
|
|
int xobj = nextLocal++;
|
|
addStatement(insts.NewInstruction(xobj, NewSiteReference.make(getNextProgramCounter(), RemoteExceptionClass)));
|
|
addStatement(insts.ThrowInstruction(xobj));
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
* (non-Javadoc)
|
|
*
|
|
* @see com.ibm.wala.ipa.cha.MethodBypass#getBypass(com.ibm.wala.classLoader.MethodReference)
|
|
*/
|
|
public IMethod getCalleeTarget(CGNode N, CallSiteReference site, IClass receiver) {
|
|
MethodReference m = site.getDeclaredTarget();
|
|
|
|
// handle call to factory method for ActionForms
|
|
if (m.getDeclaringClass().equals(ActionFormFactoryMethod.factoryClassRef) && m.getName().equals(ActionFormFactoryMethod.name)
|
|
&& m.getDescriptor().equals(ActionFormFactoryMethod.descr)) {
|
|
return new ActionFormFactoryMethod(cha);
|
|
}
|
|
|
|
// If the declared target is something generic like EJBObject, perform
|
|
// type inference to try and resolve a more specific bean receiver for
|
|
// this call.
|
|
if (isEJBSuperInterface(m.getDeclaringClass())) {
|
|
IClass inferred = getReceiverClassFromTypeInference(N, site);
|
|
if (inferred != null) {
|
|
receiver = inferred;
|
|
}
|
|
}
|
|
|
|
IClass servlet = cha.lookupClass(ServletEntrypoints.Servlet);
|
|
// special logic for MDB onMessage entrypoints
|
|
if (FakeRootMethod.isFakeRootMethod(N.getMethod().getReference())) {
|
|
if (deployment.isMessageDriven(m.getDeclaringClass())) {
|
|
if (m.getName().equals(onMessageAtom) && m.getDescriptor().equals(onMessageDesc)) {
|
|
return hijackOnMessageEntrypoint(m);
|
|
}
|
|
}
|
|
// special logic for servlet entrypoints
|
|
else if (receiver != null && cha.implementsInterface(receiver, servlet)) {
|
|
IMethod resolved = cha.resolveMethod(receiver, m.getSelector());
|
|
if (!resolved.isInit() && !resolved.isClinit()) {
|
|
return hijackServletEntrypoint(m);
|
|
}
|
|
}
|
|
}
|
|
|
|
// encode the receiver type in the method reference m
|
|
m = specializeForReceiverType(receiver, m);
|
|
|
|
// first try to intercept the call before delegating
|
|
// [SJF]: I don't think this should be necessary ... let the delegate go ...
|
|
// we'll clean up later anyway.
|
|
// update ... it seems this is necessary to pass the AutoProfile
|
|
// ATKTest ... leave it in for now.
|
|
SyntheticMethod X = methodReferenceIntercept(m);
|
|
if (X != null) {
|
|
return X;
|
|
}
|
|
|
|
// now delegate
|
|
IMethod target = parent.getCalleeTarget(N, site, receiver);
|
|
if (target == null) {
|
|
return null;
|
|
}
|
|
|
|
// delegation is done ... try to intercept now.
|
|
X = methodReferenceIntercept(target.getReference());
|
|
if (X != null) {
|
|
return X;
|
|
} else {
|
|
return target;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param receiver
|
|
* @param m
|
|
* @return a method reference based on m, but encoding the receiver as the declared class ... or m is receiver is null
|
|
*/
|
|
private MethodReference specializeForReceiverType(IClass receiver, MethodReference m) {
|
|
// create a MethodReference m which encodes the receiver type.
|
|
if (receiver != null) {
|
|
if (receiver.getClassLoader().getReference().equals(scope.getSyntheticLoader())) {
|
|
if (receiver instanceof BypassSyntheticClass)
|
|
return MethodReference.findOrCreate(((BypassSyntheticClass) receiver).getRealType().getReference(), m.getName(), m
|
|
.getDescriptor());
|
|
else
|
|
return MethodReference.findOrCreate(receiver.getReference(), m.getName(), m.getDescriptor());
|
|
} else {
|
|
return MethodReference.findOrCreate(receiver.getReference(), m.getName(), m.getDescriptor());
|
|
}
|
|
}
|
|
return m;
|
|
}
|
|
|
|
/**
|
|
* @param N governing node
|
|
* @param site a call to something generic like EJBObject
|
|
* @return an estimate for the receiver class based on local type inference, or null if type inference doesn't help
|
|
*/
|
|
private IClass getReceiverClassFromTypeInference(CGNode N, CallSiteReference site) {
|
|
ReceiverTypeInference R = typeInference.findOrCreate(N);
|
|
TypeAbstraction type = null;
|
|
if (R != null) {
|
|
type = R.getReceiverType(site);
|
|
}
|
|
if (type == null) {
|
|
// Type inference failed; raise a severe warning
|
|
return null;
|
|
} else {
|
|
// Type inference succeeded; modify m to reflect the more specific
|
|
// receiver
|
|
if (type instanceof PointType) {
|
|
return ((PointType) type).getType();
|
|
} else if (type instanceof ConeType) {
|
|
return ((ConeType) type).getType();
|
|
} else {
|
|
Assertions.UNREACHABLE("Unexpected type" + type);
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param T
|
|
* @return true iff T is one of the EJB Superinterfaces defined in J2EEUtil
|
|
*/
|
|
private boolean isEJBSuperInterface(TypeReference T) {
|
|
TypeName tName = T.getName();
|
|
Atom pack = tName.getPackage();
|
|
if (pack == null || !pack.equals(JAVAX_EJB)) {
|
|
return false;
|
|
}
|
|
|
|
IClass klass = cha.lookupClass(T);
|
|
if (klass == null) {
|
|
return false;
|
|
} else {
|
|
TypeReference k = klass.getReference();
|
|
return (k.equals(J2EEUtil.EJB_HOME) || k.equals(J2EEUtil.EJB_LOCAL_HOME) || k.equals(J2EEUtil.EJB_LOCAL_OBJECT) || k
|
|
.equals(J2EEUtil.EJB_OBJECT));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* (non-Javadoc)
|
|
*
|
|
* @see com.ibm.wala.ipa.callgraph.MethodTargetSelector#mightReturnSyntheticMethod(com.ibm.wala.ipa.callgraph.CGNode,
|
|
* com.ibm.wala.classLoader.CallSiteReference)
|
|
*/
|
|
public boolean mightReturnSyntheticMethod(CGNode caller, CallSiteReference site) {
|
|
// TODO optimize this!
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* (non-Javadoc)
|
|
*
|
|
* @see com.ibm.wala.ipa.callgraph.MethodTargetSelector#mightReturnSyntheticMethod(com.ibm.wala.types.MethodReference)
|
|
*/
|
|
public boolean mightReturnSyntheticMethod(MethodReference declaredTarget) {
|
|
// TODO optimize this!
|
|
return true;
|
|
}
|
|
}
|