All Downloads are FREE. Search and download functionalities are using the official Maven repository.

soot.jimple.infoflow.methodSummary.taintWrappers.AccessPathFragment Maven / Gradle / Ivy

package soot.jimple.infoflow.methodSummary.taintWrappers;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import soot.SootField;
import soot.Type;
import soot.jimple.infoflow.data.AccessPath;
import soot.jimple.infoflow.data.ContainerContext;

/**
 * A portion of an access path
 * 
 * @author Steven Arzt
 *
 */
public class AccessPathFragment {

	private final String[] fields;
	private final String[] fieldTypes;
	private final ContainerContext[][] contexts;

	/**
	 * Creates a new instance of the {@link AccessPathFragment} class
	 * 
	 * @param fields     The names of the fields in this fragment of an access path
	 * @param fieldTypes The types of the given fields
	 */
	public AccessPathFragment(String[] fields, String[] fieldTypes) {
		this(fields, fieldTypes, new ContainerContext[fields == null ? 0 : fields.length][]);
	}

	/**
	 * Creates a new instance of the {@link AccessPathFragment} class
	 *
	 * @param fields     The names of the fields in this fragment of an access path
	 * @param fieldTypes The types of the given fields
	 * @param contexts   The contexts of the given fields
	 */
	public AccessPathFragment(String[] fields, String[] fieldTypes, ContainerContext[][] contexts) {
		this.fields = fields;
		this.fieldTypes = fieldTypes == null ? fieldsToTypes(fields) : fieldTypes;
		this.contexts = contexts;

		// Sanity check
		if (fields != null && fieldTypes != null && fields.length != fieldTypes.length)
			throw new RuntimeException("Access path array and type array must be of equal length");
		if (fields != null && fieldTypes != null && fields.length != contexts.length)
			throw new RuntimeException("Access path array and context array must be of equal length");
	}

	/**
	 * Creates a new instance of the {@link AccessPathFragment} class
	 * 
	 * @param fields     The fields in this fragment of an access path
	 * @param fieldTypes The types of the given fields
	 */
	public AccessPathFragment(SootField[] fields, Type[] fieldTypes, ContainerContext[][] contexts) {
		this(fieldArrayToStringArray(fields), typeArrayToStringArray(fieldTypes), contexts);
	}

	/**
	 * Creates a new instance of the {@link AccessPathFragment} class based on an
	 * existing access path
	 * 
	 * @param accessPath The original access path
	 */
	public AccessPathFragment(AccessPath accessPath) {
		this(accessPath.getFragmentCount() > 0 ? Arrays.stream(accessPath.getFragments())
				.map(f -> f.getField().toString()).collect(Collectors.toList()) : null,
				accessPath.getFragmentCount() > 0 ? Arrays.stream(accessPath.getFragments())
						.map(f -> f.getFieldType().toString()).collect(Collectors.toList()) : null,
				accessPath.getFragmentCount() > 0 ? Arrays.stream(accessPath.getFragments())
						.map(f -> f.getContext()).collect(Collectors.toList()) : null);
	}

	/**
	 * Creates a new instance of the {@link AccessPathFragment} class
	 * 
	 * @param fields     The fields in this fragment of an access path
	 * @param fieldTypes The types of the given fields
	 */
	public AccessPathFragment(List fields, List fieldTypes, List contexts) {
		this.fields = fields == null ? null : fields.toArray(new String[fields.size()]);
		this.fieldTypes = fieldTypes == null ? null : fieldTypes.toArray(new String[fieldTypes.size()]);
		this.contexts = contexts == null ? null : contexts.toArray(new ContainerContext[0][0]);
	}

	/**
	 * Converts an array of SootFields to an array of strings
	 * 
	 * @param fields The array of SootFields to convert
	 * @return The array of strings corresponding to the given array of SootFields
	 */
	private static String[] fieldArrayToStringArray(SootField[] fields) {
		if (fields == null)
			return null;
		String[] stringFields = new String[fields.length];
		for (int i = 0; i < fields.length; i++)
			stringFields[i] = fields[i].toString();
		return stringFields;
	}

	/**
	 * Converts an array of Soot Types to an array of strings
	 * 
	 * @param fields The array of Soot Types to convert
	 * @return The array of strings corresponding to the given array of Soot Types
	 */
	private static String[] typeArrayToStringArray(Type[] types) {
		if (types == null)
			return null;
		String[] stringTypes = new String[types.length];
		for (int i = 0; i < types.length; i++)
			stringTypes[i] = types[i].toString();
		return stringTypes;
	}

	private static String[] fieldsToTypes(String[] fields) {
		if (fields == null)
			return null;

		String[] types = new String[fields.length];
		for (int i = 0; i < fields.length; i++) {
			String f = fields[i];
			types[i] = f.substring(f.indexOf(":") + 2, f.lastIndexOf(" "));
		}
		return types;
	}

	/**
	 * Gets the number of fields in this access path fragments
	 * 
	 * @return The length of this access path fragments
	 */
	public int length() {
		return fields == null ? 0 : fields.length;
	}

	/**
	 * Gets the names of the fields in this access path fragment
	 * 
	 * @return The names of the fields in this access path fragment
	 */
	public String[] getFields() {
		return fields;
	}

	/**
	 * Gets the types of the fields in this access path fragment
	 * 
	 * @return The types of the fields in this access path fragment
	 */
	public String[] getFieldTypes() {
		return fieldTypes;
	}

	public ContainerContext[][] getContexts() {
		return contexts;
	}

	/**
	 * Gets the name of the field at the given index
	 *
	 * @param idx The field index
	 * @return The name of the field at the given index
	 */
	public ContainerContext[] getContext(int idx) {
		if (contexts == null || idx < 0 || idx >= contexts.length)
			return null;
		return contexts[idx];
	}

	/**
	 * Gets the name of the last field in this access path fragment
	 * 
	 * @return The name of the last field in this access path fragment
	 */
	public String getLastFieldName() {
		if (fields == null || fields.length == 0)
			return null;
		return fields[fields.length - 1];
	}

	/**
	 * Gets the name of the first field in this access path fragment
	 * 
	 * @return The name of the first field in this access path fragment
	 */
	public String getFirstFieldName() {
		if (fields == null || fields.length == 0)
			return null;
		return fields[0];
	}

	/**
	 * Gets the type of the last field in this access path fragment
	 * 
	 * @return The type of the last field in this access path fragment
	 */
	public String getLastFieldType() {
		if (fieldTypes == null || fieldTypes.length == 0)
			return null;
		return fieldTypes[fieldTypes.length - 1];
	}

	/**
	 * Gets the type of the first field in this access path fragment
	 * 
	 * @return The type of the first field in this access path fragment
	 */
	public String getFirstFieldType() {
		if (fieldTypes == null || fieldTypes.length == 0)
			return null;
		return fieldTypes[0];
	}

	public ContainerContext[] getFirstFieldContext() {
		if (contexts == null || contexts.length == 0)
			return null;
		return contexts[0];
	}

	/**
	 * Gets whether this access path fragment is empty
	 * 
	 * @return true if this access path fragment is empty, false otherwise
	 */
	public boolean isEmpty() {
		return this.fields == null || this.fields.length == 0;
	}

	/**
	 * Append the given access path fragment to this one
	 * 
	 * @param toAppend The access path fragment to append to this one
	 * @return The concatenated access path fragment containing all elements from
	 *         this fragment followed by those from the given fragment
	 */
	public AccessPathFragment append(AccessPathFragment toAppend) {
		// If only one of the two operands contains actual data, we simply take that
		// object and don't need to append anything
		if (toAppend == null || toAppend.isEmpty()) {
			if (this.isEmpty())
				return null;
			return this;
		}
		if (this.isEmpty())
			return toAppend;

		String[] toAppendFields = toAppend.getFields();
		String[] toAppendFieldTypes = toAppend.getFieldTypes();
		ContainerContext[][] toAppendContexts = toAppend.getContexts();

		String[] appendedFields = new String[fields.length + toAppendFields.length];
		System.arraycopy(fields, 0, appendedFields, 0, fields.length);
		System.arraycopy(toAppendFields, 0, appendedFields, fields.length, toAppendFields.length);

		String[] appendedTypes = new String[fieldTypes.length + toAppendFieldTypes.length];
		System.arraycopy(fieldTypes, 0, appendedTypes, 0, fieldTypes.length);
		System.arraycopy(toAppendFieldTypes, 0, appendedTypes, fieldTypes.length, toAppendFieldTypes.length);

		ContainerContext[][] appendedContexts = new ContainerContext[contexts.length + toAppendContexts.length][];
		System.arraycopy(contexts, 0, appendedContexts, 0, contexts.length);
		System.arraycopy(toAppendContexts, 0, appendedContexts, contexts.length, toAppendContexts.length);

		return new AccessPathFragment(appendedFields, appendedTypes, appendedContexts);
	}

	/**
	 * Derives a new access path fragment by replacing the field type at the given
	 * index with the given type
	 * 
	 * @param idx       The index at which to change the field type
	 * @param fieldType The new field type
	 * @return The new access path fragment with the updated field type
	 */
	public AccessPathFragment updateFieldType(int idx, String fieldType) {
		String[] newFieldTypes = Arrays.copyOf(fieldTypes, fieldTypes.length);
		newFieldTypes[idx] = fieldType;
		return new AccessPathFragment(fields, newFieldTypes, contexts);
	}

	/**
	 * Gets the name of the field at the given index
	 * 
	 * @param idx The field index
	 * @return The name of the field at the given index
	 */
	public String getField(int idx) {
		if (idx < 0 || idx >= fields.length)
			return null;
		return fields[idx];
	}

	/**
	 * Gets the prefix of this access path with the given length
	 * 
	 * @param length The length to which this access path shall be cut
	 * @return This access path cut to the given length
	 */
	public AccessPathFragment prefix(int length) {
		if (length < 0)
			return this;
		if (length() <= length)
			return this;

		String[] newFields = new String[length];
		String[] newFieldTypes = new String[length];
		ContainerContext[][] newContexts = new ContainerContext[length][];

		System.arraycopy(fields, 0, newFields, 0, length);
		System.arraycopy(fieldTypes, 0, newFieldTypes, 0, length);
		System.arraycopy(fieldTypes, 0, newFieldTypes, 0, length);
		return new AccessPathFragment(newFields, newFieldTypes, newContexts);
	}

	@Override
	public String toString() {
		return fields == null ? "" : Arrays.toString(fields);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + Arrays.hashCode(fieldTypes);
		result = prime * result + Arrays.hashCode(fields);
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		AccessPathFragment other = (AccessPathFragment) obj;
		if (!Arrays.equals(fieldTypes, other.fieldTypes))
			return false;
		if (!Arrays.equals(fields, other.fields))
			return false;
		return true;
	}

	/**
	 * Obtains the string representation of the given access path fragment
	 * 
	 * @param accessPath The access path fragment for which to get the string
	 *                   representation
	 * @return The string representation of the given access path fragment
	 */
	public static String toString(AccessPathFragment accessPath) {
		return accessPath.fields == null ? "" : Arrays.toString(accessPath.fields);
	}

	/**
	 * Appends the given suffix to the given access path
	 * 
	 * @param accessPath The base access path, which will become the first part of
	 *                   the result
	 * @param suffix     The access path to append, which will become the second
	 *                   part of the result
	 * @return The concatenated access path
	 */
	public static AccessPathFragment append(AccessPathFragment accessPath, AccessPathFragment suffix) {
		if (accessPath == null)
			return suffix;
		if (suffix == null)
			return accessPath;
		return accessPath.append(suffix);
	}

	public AccessPathFragment addContext(ContainerContext[] ctxt) {
		ContainerContext[][] contexts = new ContainerContext[fields.length][];;
		System.arraycopy(this.contexts, 1, contexts, 1, this.contexts.length - 1);
		contexts[0] = ctxt;
		return new AccessPathFragment(fields, fieldTypes, contexts);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy