<commit_before>/*******************************************************************************
 * Copyright (c) 2012 Secure Software Engineering Group at EC SPRIDE.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors: <NAME>, <NAME>, <NAME>, <NAME>, and others.
 ******************************************************************************/
package soot.jimple.infoflow.data;

import java.util.Arrays;
import java.util.Collection;
import java.util.Set;

import soot.ArrayType;
import soot.Local;
import soot.RefType;
import soot.Scene;
import soot.SootField;
import soot.Type;
import soot.Value;
import soot.jimple.ArrayRef;
import soot.jimple.FieldRef;
import soot.jimple.InstanceFieldRef;
import soot.jimple.StaticFieldRef;
import soot.jimple.infoflow.Infoflow;
import soot.jimple.infoflow.collect.ConcurrentHashSet;
import soot.jimple.infoflow.collect.MyConcurrentHashMap;

/**
 * This class represents the taint, containing a base value and a list of fields
 * (length is bounded by Infoflow.ACCESSPATHLENGTH)
 */
public class AccessPath implements Cloneable {
	
	// ATTENTION: This class *must* be immutable!
	/*
	 * tainted value, is not null for non-static values
	 */
	private final Local value;
	/**
	 * list of fields, either they are based on a concrete @value or they indicate a static field
	 */
	private final SootField[] fields;
	
	private final Type baseType;
	private final Type[] fieldTypes;
	
	private final boolean taintSubFields;
	private final boolean cutOffApproximation;
	
	private int hashCode = 0;
	
	/**
	 * Specialized pair class for field bases
	 * 
	 * @author <NAME>
	 *
	 */
	public static class BasePair {
		
		private final SootField[] fields;
		private final Type[] types;
		private int hashCode = 0;
		
		public BasePair(SootField[] fields, Type[] types) {
			this.fields = fields;
			this.types = types;
			
			// Check whether this base makes sense
			if (fields == null || fields.length == 0)
				throw new RuntimeException("A base must contain at least one field");
		}
		
		public SootField[] getFields() {
			return this.fields;
		}
		
		public Type[] getTypes()  {
			return this.types;
		}
		
		@Override
		public int hashCode() {
			if (hashCode == 0) {			
				final int prime = 31;
				int result = 1;
				result = prime * result + Arrays.hashCode(fields);
				result = prime * result + Arrays.hashCode(types);
				hashCode = result;
			}
			return hashCode;
		}
		
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			BasePair other = (BasePair) obj;
			if (!Arrays.equals(fields, other.fields))
				return false;
			if (!Arrays.equals(types, other.types))
				return false;
			return true;
		}
		
		@Override
		public String toString() {
			return Arrays.toString(fields);
		}
		
	}
	
	private static MyConcurrentHashMap<Type, Set<BasePair>> baseRegister
			= new MyConcurrentHashMap<Type, Set<BasePair>>();

	/**
	 * The empty access path denotes a code region depending on a tainted
	 * conditional. If a function is called inside the region, there is no
	 * tainted value inside the callee, but there is taint - modeled by
	 * the empty access path.
	 */
	private static final AccessPath emptyAccessPath = new AccessPath();
	
	private AccessPath() {
		this.value = null;
		this.fields = null;
		this.baseType = null;
		this.fieldTypes = null;
		this.taintSubFields = true;
		this.cutOffApproximation = false;
	}
	
	public AccessPath(Value val, boolean taintSubFields){
		this(val, (SootField[]) null, null, (Type[]) null, taintSubFields);
	}
	
	public AccessPath(Value val, SootField[] appendingFields, boolean taintSubFields){
		this(val, appendingFields, null, (Type[]) null, taintSubFields);
	}
	
	public AccessPath(Value val, SootField[] appendingFields, Type valType,
			Type[] appendingFieldTypes, boolean taintSubFields) {
		this(val, appendingFields, valType, appendingFieldTypes, taintSubFields, false, true);
	}
	
	public AccessPath(Value val, SootField[] appendingFields, Type valType,
			Type[] appendingFieldTypes, boolean taintSubFields,
			boolean cutFirstField, boolean reduceBases){
		// Make sure that the base object is valid
		assert (val == null && appendingFields != null && appendingFields.length > 0)
		 	|| canContainValue(val);
		
		// Initialize the field type information if necessary
		if (appendingFields != null && appendingFieldTypes == null) {
			appendingFieldTypes = new Type[appendingFields.length];
			for (int i = 0; i < appendingFields.length; i++)
				appendingFieldTypes[i] = appendingFields[i].getType();
		}
		
		SootField[] fields;
		Type[] fieldTypes;
		
		// Get the base object, field and type
		if(val instanceof FieldRef) {
			FieldRef ref = (FieldRef) val;

			// Set the base value and type if we have one
			if (val instanceof InstanceFieldRef) {
				InstanceFieldRef iref = (InstanceFieldRef) val;
				this.value = (Local) iref.getBase();
				this.baseType = value.getType();
			}
			else {
				this.value = null;
				this.baseType = null;
			}
			
			// Handle the fields
			fields = new SootField[(appendingFields == null ? 0 : appendingFields.length) + 1];
			fields[0] = ref.getField();
			if (appendingFields != null)
				System.arraycopy(appendingFields, 0, fields, 1, appendingFields.length);
			
			fieldTypes = new Type[(appendingFieldTypes == null ? 0 : appendingFieldTypes.length) + 1];
			fieldTypes[0] = valType != null ? valType : fields[0].getType();
			if (appendingFieldTypes != null)
				System.arraycopy(appendingFieldTypes, 0, fieldTypes, 1, appendingFieldTypes.length);
		}
		else if (val instanceof ArrayRef) {
			ArrayRef ref = (ArrayRef) val;
			this.value = (Local) ref.getBase();
			this.baseType = valType == null ? value.getType() : valType;
			
			fields = appendingFields;
			fieldTypes = appendingFieldTypes;
		}
		else {
			this.value = (Local) val;
			this.baseType = valType == null ? (this.value == null ? null : value.getType()) : valType;
			
			fields = appendingFields;
			fieldTypes = appendingFieldTypes;
		}
		
		// If we don't want to track fields at all, we can cut the field
		// processing short
		if (Infoflow.getAccessPathLength() == 0) {
			fields = null;
			fieldTypes = null;
		}
		
		// Cut the first field if requested
		if (cutFirstField && fields != null && fields.length > 0) {
			SootField[] newFields = new SootField[fields.length - 1];
			Type[] newTypes = new Type[newFields.length];
			System.arraycopy(fields, 1, newFields, 0, newFields.length);
			System.arraycopy(fieldTypes, 1, newTypes, 0, newTypes.length);
			fields = newFields.length > 0 ? newFields : null;
			fieldTypes = newTypes.length > 0 ? newTypes : null;
		}
		
		// Make sure that only heap objects may have fields
		assert this.value == null
				|| this.value.getType() instanceof RefType 
				|| this.value.getType() instanceof ArrayType
				|| fields == null || fields.length == 0;
		
		// Make sure that the actual types are always as precise as the declared ones
		if (fields != null)
			for (int i = 0; i < fields.length; i++)
				if (fields[i].getType() != fieldTypes[i]
						&& Scene.v().getFastHierarchy().canStoreType(fields[i].getType(), fieldTypes[i]))
					fieldTypes[i] = fields[i].getType();
		
		// We can always merge a.inner.this$0.c to a.c. We do this first so that
		// we don't create recursive bases for stuff we don't need anyway.
		if (fields != null) {
			for (int i = 0; i < fields.length; i++) {
				// Is this a reference to an outer class?
				if (fields[i].getName().startsWith("this$")) {
					// Get the name of the outer class
					String outerClassName = ((RefType) fields[i].getType()).getClassName();
					{
						int outerClassIdx = Integer.parseInt(fields[i].getName().substring(5));
						String newOuterClassName = "";
						for (int j = 0; j < outerClassIdx; j++) {
							newOuterClassName = outerClassName.substring(0, outerClassName.indexOf("$"));
							outerClassName = outerClassName.substring(outerClassName.indexOf("$") + 1);
						}
						outerClassName = newOuterClassName;
					}
					
					// Check the base object
					int startIdx = -1;
					if (this.value != null
							&& this.value.getType() instanceof RefType
							&& ((RefType) this.value.getType()).getClassName().equals(outerClassName)) {<commit_msg>one more fix for finding outer class names<commit_after>/*******************************************************************************
 * Copyright (c) 2012 Secure Software Engineering Group at EC SPRIDE.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors: <NAME>, <NAME>, <NAME>, <NAME>, and others.
 ******************************************************************************/
package soot.jimple.infoflow.data;

import java.util.Arrays;
import java.util.Collection;
import java.util.Set;

import soot.ArrayType;
import soot.Local;
import soot.RefType;
import soot.Scene;
import soot.SootField;
import soot.Type;
import soot.Value;
import soot.jimple.ArrayRef;
import soot.jimple.FieldRef;
import soot.jimple.InstanceFieldRef;
import soot.jimple.StaticFieldRef;
import soot.jimple.infoflow.Infoflow;
import soot.jimple.infoflow.collect.ConcurrentHashSet;
import soot.jimple.infoflow.collect.MyConcurrentHashMap;

/**
 * This class represents the taint, containing a base value and a list of fields
 * (length is bounded by Infoflow.ACCESSPATHLENGTH)
 */
public class AccessPath implements Cloneable {
	
	// ATTENTION: This class *must* be immutable!
	/*
	 * tainted value, is not null for non-static values
	 */
	private final Local value;
	/**
	 * list of fields, either they are based on a concrete @value or they indicate a static field
	 */
	private final SootField[] fields;
	
	private final Type baseType;
	private final Type[] fieldTypes;
	
	private final boolean taintSubFields;
	private final boolean cutOffApproximation;
	
	private int hashCode = 0;
	
	/**
	 * Specialized pair class for field bases
	 * 
	 * @author <NAME>
	 *
	 */
	public static class BasePair {
		
		private final SootField[] fields;
		private final Type[] types;
		private int hashCode = 0;
		
		public BasePair(SootField[] fields, Type[] types) {
			this.fields = fields;
			this.types = types;
			
			// Check whether this base makes sense
			if (fields == null || fields.length == 0)
				throw new RuntimeException("A base must contain at least one field");
		}
		
		public SootField[] getFields() {
			return this.fields;
		}
		
		public Type[] getTypes()  {
			return this.types;
		}
		
		@Override
		public int hashCode() {
			if (hashCode == 0) {			
				final int prime = 31;
				int result = 1;
				result = prime * result + Arrays.hashCode(fields);
				result = prime * result + Arrays.hashCode(types);
				hashCode = result;
			}
			return hashCode;
		}
		
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			BasePair other = (BasePair) obj;
			if (!Arrays.equals(fields, other.fields))
				return false;
			if (!Arrays.equals(types, other.types))
				return false;
			return true;
		}
		
		@Override
		public String toString() {
			return Arrays.toString(fields);
		}
		
	}
	
	private static MyConcurrentHashMap<Type, Set<BasePair>> baseRegister
			= new MyConcurrentHashMap<Type, Set<BasePair>>();

	/**
	 * The empty access path denotes a code region depending on a tainted
	 * conditional. If a function is called inside the region, there is no
	 * tainted value inside the callee, but there is taint - modeled by
	 * the empty access path.
	 */
	private static final AccessPath emptyAccessPath = new AccessPath();
	
	private AccessPath() {
		this.value = null;
		this.fields = null;
		this.baseType = null;
		this.fieldTypes = null;
		this.taintSubFields = true;
		this.cutOffApproximation = false;
	}
	
	public AccessPath(Value val, boolean taintSubFields){
		this(val, (SootField[]) null, null, (Type[]) null, taintSubFields);
	}
	
	public AccessPath(Value val, SootField[] appendingFields, boolean taintSubFields){
		this(val, appendingFields, null, (Type[]) null, taintSubFields);
	}
	
	public AccessPath(Value val, SootField[] appendingFields, Type valType,
			Type[] appendingFieldTypes, boolean taintSubFields) {
		this(val, appendingFields, valType, appendingFieldTypes, taintSubFields, false, true);
	}
	
	public AccessPath(Value val, SootField[] appendingFields, Type valType,
			Type[] appendingFieldTypes, boolean taintSubFields,
			boolean cutFirstField, boolean reduceBases){
		// Make sure that the base object is valid
		assert (val == null && appendingFields != null && appendingFields.length > 0)
		 	|| canContainValue(val);
		
		// Initialize the field type information if necessary
		if (appendingFields != null && appendingFieldTypes == null) {
			appendingFieldTypes = new Type[appendingFields.length];
			for (int i = 0; i < appendingFields.length; i++)
				appendingFieldTypes[i] = appendingFields[i].getType();
		}
		
		SootField[] fields;
		Type[] fieldTypes;
		
		// Get the base object, field and type
		if(val instanceof FieldRef) {
			FieldRef ref = (FieldRef) val;

			// Set the base value and type if we have one
			if (val instanceof InstanceFieldRef) {
				InstanceFieldRef iref = (InstanceFieldRef) val;
				this.value = (Local) iref.getBase();
				this.baseType = value.getType();
			}
			else {
				this.value = null;
				this.baseType = null;
			}
			
			// Handle the fields
			fields = new SootField[(appendingFields == null ? 0 : appendingFields.length) + 1];
			fields[0] = ref.getField();
			if (appendingFields != null)
				System.arraycopy(appendingFields, 0, fields, 1, appendingFields.length);
			
			fieldTypes = new Type[(appendingFieldTypes == null ? 0 : appendingFieldTypes.length) + 1];
			fieldTypes[0] = valType != null ? valType : fields[0].getType();
			if (appendingFieldTypes != null)
				System.arraycopy(appendingFieldTypes, 0, fieldTypes, 1, appendingFieldTypes.length);
		}
		else if (val instanceof ArrayRef) {
			ArrayRef ref = (ArrayRef) val;
			this.value = (Local) ref.getBase();
			this.baseType = valType == null ? value.getType() : valType;
			
			fields = appendingFields;
			fieldTypes = appendingFieldTypes;
		}
		else {
			this.value = (Local) val;
			this.baseType = valType == null ? (this.value == null ? null : value.getType()) : valType;
			
			fields = appendingFields;
			fieldTypes = appendingFieldTypes;
		}
		
		// If we don't want to track fields at all, we can cut the field
		// processing short
		if (Infoflow.getAccessPathLength() == 0) {
			fields = null;
			fieldTypes = null;
		}
		
		// Cut the first field if requested
		if (cutFirstField && fields != null && fields.length > 0) {
			SootField[] newFields = new SootField[fields.length - 1];
			Type[] newTypes = new Type[newFields.length];
			System.arraycopy(fields, 1, newFields, 0, newFields.length);
			System.arraycopy(fieldTypes, 1, newTypes, 0, newTypes.length);
			fields = newFields.length > 0 ? newFields : null;
			fieldTypes = newTypes.length > 0 ? newTypes : null;
		}
		
		// Make sure that only heap objects may have fields
		assert this.value == null
				|| this.value.getType() instanceof RefType 
				|| this.value.getType() instanceof ArrayType
				|| fields == null || fields.length == 0;
		
		// Make sure that the actual types are always as precise as the declared ones
		if (fields != null)
			for (int i = 0; i < fields.length; i++)
				if (fields[i].getType() != fieldTypes[i]
						&& Scene.v().getFastHierarchy().canStoreType(fields[i].getType(), fieldTypes[i]))
					fieldTypes[i] = fields[i].getType();
		
		// We can always merge a.inner.this$0.c to a.c. We do this first so that
		// we don't create recursive bases for stuff we don't need anyway.
		if (fields != null) {
			for (int i = 0; i < fields.length; i++) {
				// Is this a reference to an outer class?
				if (fields[i].getName().startsWith("this$")) {
					// Get the name of the outer class
					String outerClassName = ((RefType) fields[i].getType()).getClassName();
					
					// Check the base object
					int startIdx = -1;
					if (this.value != null
							&& this.value.getType() instanceof RefType
							&& ((RefType) this.value.getType()).getClassName().equals(outerClassName)) {
{'fraction_non_alphanumeric': 0.13101348931598425, 'fraction_numerical': 0.005252477020413036, 'mean_word_length': 3.58037178786222, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1552751', 'n_tokens_mistral': 5861, 'n_tokens_neox': 4946, 'n_words': 1579}