WALA/com.ibm.wala.core/src/com/ibm/wala/classLoader/JavaLanguage.java

722 lines
26 KiB
Java

/*******************************************************************************
* Copyright (c) 2009 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.classLoader;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import com.ibm.wala.analysis.typeInference.JavaPrimitiveType;
import com.ibm.wala.analysis.typeInference.PrimitiveType;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.ConstantInstruction;
import com.ibm.wala.shrikeBT.ConstantInstruction.ClassToken;
import com.ibm.wala.shrikeBT.Constants;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;
import com.ibm.wala.shrikeBT.IComparisonInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.shrikeBT.Instruction;
import com.ibm.wala.shrikeCT.ConstantPoolParser.ReferenceToken;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.SSAAddressOfInstruction;
import com.ibm.wala.ssa.SSAArrayLengthInstruction;
import com.ibm.wala.ssa.SSAArrayLoadInstruction;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSABinaryOpInstruction;
import com.ibm.wala.ssa.SSACheckCastInstruction;
import com.ibm.wala.ssa.SSAComparisonInstruction;
import com.ibm.wala.ssa.SSAConditionalBranchInstruction;
import com.ibm.wala.ssa.SSAConversionInstruction;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAGotoInstruction;
import com.ibm.wala.ssa.SSAInstanceofInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSALoadIndirectInstruction;
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
import com.ibm.wala.ssa.SSAMonitorInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.ssa.SSAStoreIndirectInstruction;
import com.ibm.wala.ssa.SSASwitchInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
import com.ibm.wala.ssa.SSAUnaryOpInstruction;
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.Selector;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.shrike.Exceptions.MethodResolutionFailure;
import com.ibm.wala.util.shrike.ShrikeUtil;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.warnings.Warnings;
/**
* The implementation of {@link Language} which defines Java semantics.
*
*/
public class JavaLanguage extends LanguageImpl implements BytecodeLanguage, Constants {
public static class JavaInstructionFactory implements SSAInstructionFactory {
@Override
public SSAArrayLengthInstruction ArrayLengthInstruction(int iindex, int result, int arrayref) {
return new SSAArrayLengthInstruction(iindex, result, arrayref) {
@Override
public Collection<TypeReference> getExceptionTypes() {
return getNullPointerException();
}
};
}
@Override
public SSAArrayLoadInstruction ArrayLoadInstruction(int iindex, int result, int arrayref, int index, TypeReference declaredType) {
return new SSAArrayLoadInstruction(iindex, result, arrayref, index, declaredType) {
@Override
public Collection<TypeReference> getExceptionTypes() {
return getArrayAccessExceptions();
}
};
}
@Override
public SSAArrayStoreInstruction ArrayStoreInstruction(int iindex, int arrayref, int index, int value, TypeReference declaredType) {
return new SSAArrayStoreInstruction(iindex, arrayref, index, value, declaredType) {
@Override
public Collection<TypeReference> getExceptionTypes() {
if (typeIsPrimitive()) {
return getArrayAccessExceptions();
} else {
return getAaStoreExceptions();
}
}
};
}
@Override
public SSABinaryOpInstruction BinaryOpInstruction(int iindex, IBinaryOpInstruction.IOperator operator, boolean overflow, boolean unsigned,
int result, int val1, int val2, boolean mayBeInteger) {
assert !overflow;
// assert (!unsigned) : "BinaryOpInstuction: unsigned disallowed! iIndex: " + iindex + ", operation: " + val1 + " " + operator.toString() + " " + val2 ;
return new SSABinaryOpInstruction(iindex, operator, result, val1, val2, mayBeInteger) {
@Override
public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
return insts.BinaryOpInstruction(iindex, getOperator(), false, false, defs == null || defs.length == 0 ? getDef(0) : defs[0],
uses == null ? getUse(0) : uses[0], uses == null ? getUse(1) : uses[1], mayBeIntegerOp());
}
@Override
public Collection<TypeReference> getExceptionTypes() {
if (isPEI()) {
return getArithmeticException();
} else {
return Collections.emptySet();
}
}
};
}
@Override
public SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, int[] typeValues, boolean isPEI) {
throw new UnsupportedOperationException();
}
@Override
public SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, TypeReference[] types, boolean isPEI) {
assert types.length == 1;
assert isPEI;
return new SSACheckCastInstruction(iindex, result, val, types, true) {
@Override
public Collection<TypeReference> getExceptionTypes() {
return getClassCastException();
}
};
}
@Override
public SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, int typeValue, boolean isPEI) {
assert isPEI;
return CheckCastInstruction(iindex, result, val, new int[]{ typeValue }, true);
}
@Override
public SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, TypeReference type, boolean isPEI) {
assert isPEI;
return CheckCastInstruction(iindex, result, val, new TypeReference[]{ type }, true);
}
@Override
public SSAComparisonInstruction ComparisonInstruction(int iindex, IComparisonInstruction.Operator operator, int result, int val1, int val2) {
return new SSAComparisonInstruction(iindex, operator, result, val1, val2);
}
@Override
public SSAConditionalBranchInstruction ConditionalBranchInstruction(int iindex, IConditionalBranchInstruction.IOperator operator,
TypeReference type, int val1, int val2, int target) {
return new SSAConditionalBranchInstruction(iindex, operator, type, val1, val2, target);
}
@Override
public SSAConversionInstruction ConversionInstruction(int iindex, int result, int val, TypeReference fromType, TypeReference toType,
boolean overflow) {
assert !overflow;
return new SSAConversionInstruction(iindex, result, val, fromType, toType) {
@Override
public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
if (uses != null && uses.length == 0) {
throw new IllegalArgumentException("(uses != null) and (uses.length == 0)");
}
return insts.ConversionInstruction(iindex, defs == null || defs.length == 0 ? getDef(0) : defs[0], uses == null ? getUse(0)
: uses[0], getFromType(), getToType(), false);
}
};
}
@Override
public SSAGetCaughtExceptionInstruction GetCaughtExceptionInstruction(int iindex, int bbNumber, int exceptionValueNumber) {
return new SSAGetCaughtExceptionInstruction(iindex, bbNumber, exceptionValueNumber);
}
@Override
public SSAGetInstruction GetInstruction(int iindex, int result, FieldReference field) {
return new SSAGetInstruction(iindex, result, field) {
};
}
@Override
public SSAGetInstruction GetInstruction(int iindex, int result, int ref, FieldReference field) {
return new SSAGetInstruction(iindex, result, ref, field) {
@Override
public Collection<TypeReference> getExceptionTypes() {
return getNullPointerException();
}
};
}
@Override
public SSAGotoInstruction GotoInstruction(int iindex, int target) {
return new SSAGotoInstruction(iindex, target);
}
@Override
public SSAInstanceofInstruction InstanceofInstruction(int iindex, int result, int ref, TypeReference checkedType) {
return new SSAInstanceofInstruction(iindex, result, ref, checkedType);
}
@Override
public SSAInvokeInstruction InvokeInstruction(int iindex, int result, int[] params, int exception, CallSiteReference site) {
return new SSAInvokeInstruction(iindex, result, params, exception, site) {
@Override
public Collection<TypeReference> getExceptionTypes() {
if (!isStatic()) {
return getNullPointerException();
} else {
return Collections.emptySet();
}
}
};
}
@Override
public SSAInvokeInstruction InvokeInstruction(int iindex, int[] params, int exception, CallSiteReference site) {
return new SSAInvokeInstruction(iindex, params, exception, site) {
@Override
public Collection<TypeReference> getExceptionTypes() {
if (!isStatic()) {
return getNullPointerException();
} else {
return Collections.emptySet();
}
}
};
}
@Override
public SSAMonitorInstruction MonitorInstruction(int iindex, int ref, boolean isEnter) {
return new SSAMonitorInstruction(iindex, ref, isEnter) {
@Override
public Collection<TypeReference> getExceptionTypes() {
return getNullPointerException();
}
};
}
@Override
public SSANewInstruction NewInstruction(int iindex, int result, NewSiteReference site) {
return new SSANewInstruction(iindex, result, site) {
@Override
public Collection<TypeReference> getExceptionTypes() {
if (getNewSite().getDeclaredType().isArrayType()) {
return getNewArrayExceptions();
} else {
return getNewScalarExceptions();
}
}
};
}
@Override
public SSAPhiInstruction PhiInstruction(int iindex, int result, int[] params) throws IllegalArgumentException {
return new SSAPhiInstruction(iindex, result, params) {
};
}
@Override
public SSAPutInstruction PutInstruction(int iindex, int ref, int value, FieldReference field) {
return new SSAPutInstruction(iindex, ref, value, field) {
@Override
public Collection<TypeReference> getExceptionTypes() {
return getNullPointerException();
}
};
}
@Override
public SSAPutInstruction PutInstruction(int iindex, int value, FieldReference field) {
return new SSAPutInstruction(iindex, value, field) {
};
}
@Override
public SSAReturnInstruction ReturnInstruction(int iindex) {
return new SSAReturnInstruction(iindex);
}
@Override
public SSAReturnInstruction ReturnInstruction(int iindex, int result, boolean isPrimitive) {
return new SSAReturnInstruction(iindex, result, isPrimitive);
}
@Override
public SSASwitchInstruction SwitchInstruction(int iindex, int val, int defaultLabel, int[] casesAndLabels) {
return new SSASwitchInstruction(iindex, val, defaultLabel, casesAndLabels);
}
@Override
public SSAThrowInstruction ThrowInstruction(int iindex, int exception) {
return new SSAThrowInstruction(iindex, exception) {
@Override
public Collection<TypeReference> getExceptionTypes() {
return getNullPointerException();
}
};
}
@Override
public SSAUnaryOpInstruction UnaryOpInstruction(int iindex, IUnaryOpInstruction.IOperator operator, int result, int val) {
return new SSAUnaryOpInstruction(iindex, operator, result, val);
}
@Override
public SSALoadMetadataInstruction LoadMetadataInstruction(int iindex, int lval, TypeReference entityType, Object token) {
return new SSALoadMetadataInstruction(iindex, lval, entityType, token) {
@Override
public Collection<TypeReference> getExceptionTypes() {
return loadClassExceptions;
}
};
}
@Override
public SSANewInstruction NewInstruction(int iindex, int result, NewSiteReference site, int[] params) {
return new SSANewInstruction(iindex, result, site, params) {
@Override
public Collection<TypeReference> getExceptionTypes() {
return getNewArrayExceptions();
}
};
}
@Override
public SSAPiInstruction PiInstruction(int iindex, int result, int val, int piBlock, int successorBlock, SSAInstruction cause) {
return new SSAPiInstruction(iindex, result, val, piBlock, successorBlock, cause);
}
@Override
public SSAAddressOfInstruction AddressOfInstruction(int iindex, int lval, int local, TypeReference pointeeType) {
throw new UnsupportedOperationException();
}
@Override
public SSAAddressOfInstruction AddressOfInstruction(int iindex, int lval, int local, int indexVal, TypeReference pointeeType) {
throw new UnsupportedOperationException();
}
@Override
public SSAAddressOfInstruction AddressOfInstruction(int iindex, int lval, int local, FieldReference field, TypeReference pointeeType) {
throw new UnsupportedOperationException();
}
@Override
public SSALoadIndirectInstruction LoadIndirectInstruction(int iindex, int lval, TypeReference t, int addressVal) {
throw new UnsupportedOperationException();
}
@Override
public SSAStoreIndirectInstruction StoreIndirectInstruction(int iindex, int addressVal, int rval, TypeReference pointeeType) {
throw new UnsupportedOperationException();
}
}
private static final Collection<TypeReference> arrayAccessExceptions = Collections.unmodifiableCollection(Arrays
.asList(new TypeReference[] { TypeReference.JavaLangNullPointerException,
TypeReference.JavaLangArrayIndexOutOfBoundsException }));
private static final Collection<TypeReference> aaStoreExceptions = Collections.unmodifiableCollection(Arrays
.asList(new TypeReference[] { TypeReference.JavaLangNullPointerException,
TypeReference.JavaLangArrayIndexOutOfBoundsException, TypeReference.JavaLangArrayStoreException }));
private static final Collection<TypeReference> newScalarExceptions = Collections.unmodifiableCollection(Arrays
.asList(new TypeReference[] { TypeReference.JavaLangExceptionInInitializerError, TypeReference.JavaLangOutOfMemoryError }));
private static final Collection<TypeReference> newArrayExceptions = Collections.unmodifiableCollection(Arrays
.asList(new TypeReference[] { TypeReference.JavaLangOutOfMemoryError, TypeReference.JavaLangNegativeArraySizeException }));
private static final Collection<TypeReference> exceptionInInitializerError = Collections
.singleton(TypeReference.JavaLangExceptionInInitializerError);
private static final Collection<TypeReference> nullPointerException = Collections
.singleton(TypeReference.JavaLangNullPointerException);
private static final Collection<TypeReference> arithmeticException = Collections
.singleton(TypeReference.JavaLangArithmeticException);
private static final Collection<TypeReference> classCastException = Collections
.singleton(TypeReference.JavaLangClassCastException);
private static final Collection<TypeReference> classNotFoundException = Collections
.singleton(TypeReference.JavaLangClassNotFoundException);
private static final Collection<TypeReference> loadClassExceptions = Collections
.singleton(TypeReference.JavaLangClassNotFoundException);
public static Collection<TypeReference> getAaStoreExceptions() {
return aaStoreExceptions;
}
public static Collection<TypeReference> getArithmeticException() {
return arithmeticException;
}
public static Collection<TypeReference> getArrayAccessExceptions() {
return arrayAccessExceptions;
}
public static Collection<TypeReference> getClassCastException() {
return classCastException;
}
public static Collection<TypeReference> getClassNotFoundException() {
return classNotFoundException;
}
public static Collection<TypeReference> getNewArrayExceptions() {
return newArrayExceptions;
}
public static Collection<TypeReference> getNewScalarExceptions() {
return newScalarExceptions;
}
public static Collection<TypeReference> getNullPointerException() {
return nullPointerException;
}
public static Collection<TypeReference> getExceptionInInitializerError() {
return exceptionInInitializerError;
}
@Override
public Atom getName() {
return ClassLoaderReference.Java;
}
@Override
public TypeReference getRootType() {
return TypeReference.JavaLangObject;
}
@Override
public TypeReference getThrowableType() {
return TypeReference.JavaLangThrowable;
}
@Override
public TypeReference getConstantType(Object o) {
if (o == null) {
// TODO: do we really want null here instead of TypeReference.Null?
// lots of code seems to depend on this being null.
return null;
} else if (o instanceof Boolean) {
return TypeReference.Boolean;
} else if (o instanceof Long) {
return TypeReference.Long;
} else if (o instanceof Double) {
return TypeReference.Double;
} else if (o instanceof Float) {
return TypeReference.Float;
} else if (o instanceof Number) {
return TypeReference.Int;
} else if (o instanceof String) {
return TypeReference.JavaLangString;
} else if (o instanceof ClassToken || o instanceof TypeReference) {
return TypeReference.JavaLangClass;
} else if (o instanceof IMethod) {
IMethod m = (IMethod) o;
return m.isInit() ? TypeReference.JavaLangReflectConstructor : TypeReference.JavaLangReflectMethod;
} else if (o instanceof MethodHandle || o instanceof ReferenceToken) {
return TypeReference.JavaLangInvokeMethodHandle;
} else if (o instanceof MethodType) {
return TypeReference.JavaLangInvokeMethodType;
} else {
assert false : "unknown constant " + o + ": " + o.getClass();
return null;
}
}
@Override
public boolean isNullType(TypeReference type) {
return type == null || type == TypeReference.Null;
}
@Override
public TypeReference[] getArrayInterfaces() {
return new TypeReference[] { TypeReference.JavaIoSerializable, TypeReference.JavaLangCloneable };
}
@Override
public TypeName lookupPrimitiveType(String name) {
throw new UnsupportedOperationException();
}
/**
* @return Collection<TypeReference>, set of exception types a call to a declared target might throw.
* @throws InvalidClassFileException
* @throws IllegalArgumentException if target is null
* @throws IllegalArgumentException if cha is null
*/
@Override
public Collection<TypeReference> inferInvokeExceptions(MethodReference target, IClassHierarchy cha)
throws InvalidClassFileException {
if (cha == null) {
throw new IllegalArgumentException("cha is null");
}
if (target == null) {
throw new IllegalArgumentException("target is null");
}
ArrayList<TypeReference> set = new ArrayList<TypeReference>(cha.getJavaLangRuntimeExceptionTypes());
set.addAll(cha.getJavaLangErrorTypes());
IClass klass = cha.lookupClass(target.getDeclaringClass());
if (klass == null) {
Warnings.add(MethodResolutionFailure.moderate(target));
}
if (klass != null) {
IMethod M = klass.getMethod(target.getSelector());
if (M == null) {
Warnings.add(MethodResolutionFailure.severe(target));
} else {
TypeReference[] exceptionTypes = M.getDeclaredExceptions();
if (exceptionTypes != null) {
set.addAll(Arrays.asList(exceptionTypes));
}
}
}
return set;
}
/**
* @param pei a potentially-excepting instruction
* @return the exception types that pei may throw, independent of the class hierarchy. null if none.
*
* Notes
* <ul>
* <li>this method will <em>NOT</em> return the exception type explicitly thrown by an athrow
* <li>this method will <em>NOT</em> return the exception types that a called method may throw
* <li>this method ignores OutOfMemoryError
* <li>this method ignores linkage errors
* <li>this method ignores IllegalMonitorState exceptions
* </ul>
*
* @throws IllegalArgumentException if pei is null
*/
@Override
public Collection<TypeReference> getImplicitExceptionTypes(IInstruction pei) {
if (pei == null) {
throw new IllegalArgumentException("pei is null");
}
switch (((Instruction) pei).getOpcode()) {
case OP_iaload:
case OP_laload:
case OP_faload:
case OP_daload:
case OP_aaload:
case OP_baload:
case OP_caload:
case OP_saload:
case OP_iastore:
case OP_lastore:
case OP_fastore:
case OP_dastore:
case OP_bastore:
case OP_castore:
case OP_sastore:
return getArrayAccessExceptions();
case OP_aastore:
return getAaStoreExceptions();
case OP_getfield:
case OP_putfield:
case OP_invokevirtual:
case OP_invokespecial:
case OP_invokeinterface:
return getNullPointerException();
case OP_idiv:
case OP_irem:
case OP_ldiv:
case OP_lrem:
return getArithmeticException();
case OP_new:
return newScalarExceptions;
case OP_newarray:
case OP_anewarray:
case OP_multianewarray:
return newArrayExceptions;
case OP_arraylength:
return getNullPointerException();
case OP_athrow:
// N.B: the caller must handle the explicitly-thrown exception
return getNullPointerException();
case OP_checkcast:
return getClassCastException();
case OP_monitorenter:
case OP_monitorexit:
// we're currently ignoring MonitorStateExceptions, since J2EE stuff
// should be
// logically single-threaded
return getNullPointerException();
case OP_ldc_w:
if (((ConstantInstruction) pei).getType().equals(TYPE_Class))
return getClassNotFoundException();
else
return null;
case OP_getstatic:
case OP_putstatic:
return getExceptionInInitializerError();
default:
return Collections.emptySet();
}
}
@Override
public SSAInstructionFactory instructionFactory() {
return javaShrikeFactory;
}
private final static SSAInstructionFactory javaShrikeFactory = new JavaInstructionFactory();
@Override
public boolean isDoubleType(TypeReference type) {
return type == TypeReference.Double;
}
@Override
public boolean isFloatType(TypeReference type) {
return type == TypeReference.Float;
}
@Override
public boolean isIntType(TypeReference type) {
return type == TypeReference.Int;
}
@Override
public boolean isLongType(TypeReference type) {
return type == TypeReference.Long;
}
@Override
public boolean isVoidType(TypeReference type) {
return type == TypeReference.Void;
}
@Override
public boolean isMetadataType(TypeReference type) {
return type == TypeReference.JavaLangClass ||
type == TypeReference.JavaLangInvokeMethodHandle ||
type == TypeReference.JavaLangInvokeMethodType;
}
@Override
public boolean isStringType(TypeReference type) {
return type == TypeReference.JavaLangString;
}
@Override
public boolean isBooleanType(TypeReference type) {
return type == TypeReference.Boolean;
}
@Override
public boolean isCharType(TypeReference type) {
return type == TypeReference.Char;
}
@Override
public Object getMetadataToken(Object value) {
if (value instanceof ClassToken) {
return ShrikeUtil.makeTypeReference(ClassLoaderReference.Application, ((ClassToken) value).getTypeName());
} else if (value instanceof ReferenceToken) {
ReferenceToken tok = (ReferenceToken)value;
TypeReference cls = ShrikeUtil.makeTypeReference(ClassLoaderReference.Application, "L" + tok.getClassName());
return MethodReference.findOrCreate(cls, new Selector(Atom.findOrCreateUnicodeAtom(tok.getElementName()), Descriptor.findOrCreateUTF8(tok.getDescriptor())));
} else if (value instanceof MethodHandle || value instanceof MethodType) {
return value;
} else {
assert value instanceof TypeReference;
return value;
}
}
@Override
public TypeReference getPointerType(TypeReference pointee) throws UnsupportedOperationException {
throw new UnsupportedOperationException("Java does not permit explicit pointers");
}
@Override
public TypeReference getStringType() {
return TypeReference.JavaLangString;
}
{
JavaPrimitiveType.init();
}
@Override
@SuppressWarnings("static-access")
public PrimitiveType getPrimitive(TypeReference reference) {
return JavaPrimitiveType.getPrimitive(reference);
}
}