<commit_before>package jadx.core.dex.nodes;

import jadx.core.Consts;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.annotations.Annotation;
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
import jadx.core.dex.attributes.nodes.LineAttrNode;
import jadx.core.dex.attributes.nodes.SourceFileAttr;
import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.info.AccessInfo.AFType;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.LiteralArg;
import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.nodes.parser.AnnotationsParser;
import jadx.core.dex.nodes.parser.FieldValueAttr;
import jadx.core.dex.nodes.parser.SignatureParser;
import jadx.core.dex.nodes.parser.StaticValuesParser;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxRuntimeException;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.android.dex.ClassData;
import com.android.dex.ClassData.Field;
import com.android.dex.ClassData.Method;
import com.android.dex.ClassDef;
import com.android.dx.rop.code.AccessFlags;

public class ClassNode extends LineAttrNode implements ILoadable {
	private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class);

	private final DexNode dex;
	private final ClassInfo clsInfo;
	private final AccessInfo accessFlags;
	private ArgType superClass;
	private List<ArgType> interfaces;
	private Map<ArgType, List<ArgType>> genericMap;

	private final List<MethodNode> methods;
	private final List<FieldNode> fields;
	private Map<Object, FieldNode> constFields = Collections.emptyMap();
	private List<ClassNode> innerClasses = Collections.emptyList();

	// store decompiled code
	private CodeWriter code;
	// store parent for inner classes or 'this' otherwise
	private ClassNode parentClass;

	private ProcessState state = ProcessState.NOT_LOADED;
	private final Set<ClassNode> dependencies = new HashSet<ClassNode>();

	public ClassNode(DexNode dex, ClassDef cls) throws DecodeException {
		this.dex = dex;
		this.clsInfo = ClassInfo.fromDex(dex, cls.getTypeIndex());
		try {
			if (cls.getSupertypeIndex() == DexNode.NO_INDEX) {
				this.superClass = null;
			} else {
				this.superClass = dex.getType(cls.getSupertypeIndex());
			}
			this.interfaces = new ArrayList<ArgType>(cls.getInterfaces().length);
			for (short interfaceIdx : cls.getInterfaces()) {
				this.interfaces.add(dex.getType(interfaceIdx));
			}
			if (cls.getClassDataOffset() != 0) {
				ClassData clsData = dex.readClassData(cls);
				int mthsCount = clsData.getDirectMethods().length + clsData.getVirtualMethods().length;
				int fieldsCount = clsData.getStaticFields().length + clsData.getInstanceFields().length;

				methods = new ArrayList<MethodNode>(mthsCount);
				fields = new ArrayList<FieldNode>(fieldsCount);

				for (Method mth : clsData.getDirectMethods()) {
					methods.add(new MethodNode(this, mth));
				}
				for (Method mth : clsData.getVirtualMethods()) {
					methods.add(new MethodNode(this, mth));
				}

				for (Field f : clsData.getStaticFields()) {
					fields.add(new FieldNode(this, f));
				}
				loadStaticValues(cls, fields);
				for (Field f : clsData.getInstanceFields()) {
					fields.add(new FieldNode(this, f));
				}
			} else {
				methods = Collections.emptyList();
				fields = Collections.emptyList();
			}

			loadAnnotations(cls);

			parseClassSignature();
			setFieldsTypesFromSignature();

			int sfIdx = cls.getSourceFileIndex();
			if (sfIdx != DexNode.NO_INDEX) {
				String fileName = dex.getString(sfIdx);
				if (clsInfo != null
						&& !clsInfo.getFullName().contains(fileName.replace(".java", ""))
						&& !fileName.equals("SourceFile")
						&& !fileName.equals("\"")) {
					this.addAttr(new SourceFileAttr(fileName));
					LOG.debug("Class '{}' compiled from '{}'", this, fileName);
				}
			}

			// restore original access flags from dalvik annotation if present
			int accFlagsValue;
			Annotation a = getAnnotation(Consts.DALVIK_INNER_CLASS);
			if (a != null) {
				accFlagsValue = (Integer) a.getValues().get("accessFlags");
			} else {
				accFlagsValue = cls.getAccessFlags();
			}
			this.accessFlags = new AccessInfo(accFlagsValue, AFType.CLASS);

		} catch (Exception e) {
			throw new DecodeException("Error decode class: " + clsInfo, e);
		}
	}

	// empty synthetic class
	public ClassNode(DexNode dex, ClassInfo clsInfo) {
		this.dex = dex;
		this.clsInfo = clsInfo;
		this.interfaces = Collections.emptyList();
		this.methods = Collections.emptyList();
		this.fields = Collections.emptyList();
		this.accessFlags = new AccessInfo(AccessFlags.ACC_PUBLIC | AccessFlags.ACC_SYNTHETIC, AFType.CLASS);
		this.parentClass = this;
	}

	private void loadAnnotations(ClassDef cls) {
		int offset = cls.getAnnotationsOffset();
		if (offset != 0) {
			try {
				new AnnotationsParser(this).parse(offset);
			} catch (DecodeException e) {
				LOG.error("Error parsing annotations in {}", this, e);
			}
		}
	}

	private void loadStaticValues(ClassDef cls, List<FieldNode> staticFields) throws DecodeException {
		for (FieldNode f : staticFields) {
			if (f.getAccessFlags().isFinal()) {
				f.addAttr(new FieldValueAttr(null));
			}
		}

		int offset = cls.getStaticValuesOffset();
		if (offset != 0) {
			StaticValuesParser parser = new StaticValuesParser(dex, dex.openSection(offset));
			int count = parser.processFields(staticFields);
			constFields = new LinkedHashMap<Object, FieldNode>(count);
			for (FieldNode f : staticFields) {
				AccessInfo accFlags = f.getAccessFlags();
				if (accFlags.isStatic() && accFlags.isFinal()) {
					FieldValueAttr fv = f.get(AType.FIELD_VALUE);
					if (fv != null && fv.getValue() != null) {
						if (accFlags.isPublic()) {
							dex.getConstFields().put(fv.getValue(), f);
						}
						constFields.put(fv.getValue(), f);
					}
				}<commit_msg>core: skip annotations parsing if error occurs (#57)<commit_after>package jadx.core.dex.nodes;

import jadx.core.Consts;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.annotations.Annotation;
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
import jadx.core.dex.attributes.nodes.LineAttrNode;
import jadx.core.dex.attributes.nodes.SourceFileAttr;
import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.info.AccessInfo.AFType;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.LiteralArg;
import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.nodes.parser.AnnotationsParser;
import jadx.core.dex.nodes.parser.FieldValueAttr;
import jadx.core.dex.nodes.parser.SignatureParser;
import jadx.core.dex.nodes.parser.StaticValuesParser;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxRuntimeException;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.android.dex.ClassData;
import com.android.dex.ClassData.Field;
import com.android.dex.ClassData.Method;
import com.android.dex.ClassDef;
import com.android.dx.rop.code.AccessFlags;

public class ClassNode extends LineAttrNode implements ILoadable {
	private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class);

	private final DexNode dex;
	private final ClassInfo clsInfo;
	private final AccessInfo accessFlags;
	private ArgType superClass;
	private List<ArgType> interfaces;
	private Map<ArgType, List<ArgType>> genericMap;

	private final List<MethodNode> methods;
	private final List<FieldNode> fields;
	private Map<Object, FieldNode> constFields = Collections.emptyMap();
	private List<ClassNode> innerClasses = Collections.emptyList();

	// store decompiled code
	private CodeWriter code;
	// store parent for inner classes or 'this' otherwise
	private ClassNode parentClass;

	private ProcessState state = ProcessState.NOT_LOADED;
	private final Set<ClassNode> dependencies = new HashSet<ClassNode>();

	public ClassNode(DexNode dex, ClassDef cls) throws DecodeException {
		this.dex = dex;
		this.clsInfo = ClassInfo.fromDex(dex, cls.getTypeIndex());
		try {
			if (cls.getSupertypeIndex() == DexNode.NO_INDEX) {
				this.superClass = null;
			} else {
				this.superClass = dex.getType(cls.getSupertypeIndex());
			}
			this.interfaces = new ArrayList<ArgType>(cls.getInterfaces().length);
			for (short interfaceIdx : cls.getInterfaces()) {
				this.interfaces.add(dex.getType(interfaceIdx));
			}
			if (cls.getClassDataOffset() != 0) {
				ClassData clsData = dex.readClassData(cls);
				int mthsCount = clsData.getDirectMethods().length + clsData.getVirtualMethods().length;
				int fieldsCount = clsData.getStaticFields().length + clsData.getInstanceFields().length;

				methods = new ArrayList<MethodNode>(mthsCount);
				fields = new ArrayList<FieldNode>(fieldsCount);

				for (Method mth : clsData.getDirectMethods()) {
					methods.add(new MethodNode(this, mth));
				}
				for (Method mth : clsData.getVirtualMethods()) {
					methods.add(new MethodNode(this, mth));
				}

				for (Field f : clsData.getStaticFields()) {
					fields.add(new FieldNode(this, f));
				}
				loadStaticValues(cls, fields);
				for (Field f : clsData.getInstanceFields()) {
					fields.add(new FieldNode(this, f));
				}
			} else {
				methods = Collections.emptyList();
				fields = Collections.emptyList();
			}

			loadAnnotations(cls);

			parseClassSignature();
			setFieldsTypesFromSignature();

			int sfIdx = cls.getSourceFileIndex();
			if (sfIdx != DexNode.NO_INDEX) {
				String fileName = dex.getString(sfIdx);
				if (clsInfo != null
						&& !clsInfo.getFullName().contains(fileName.replace(".java", ""))
						&& !fileName.equals("SourceFile")
						&& !fileName.equals("\"")) {
					this.addAttr(new SourceFileAttr(fileName));
					LOG.debug("Class '{}' compiled from '{}'", this, fileName);
				}
			}

			// restore original access flags from dalvik annotation if present
			int accFlagsValue;
			Annotation a = getAnnotation(Consts.DALVIK_INNER_CLASS);
			if (a != null) {
				accFlagsValue = (Integer) a.getValues().get("accessFlags");
			} else {
				accFlagsValue = cls.getAccessFlags();
			}
			this.accessFlags = new AccessInfo(accFlagsValue, AFType.CLASS);

		} catch (Exception e) {
			throw new DecodeException("Error decode class: " + clsInfo, e);
		}
	}

	// empty synthetic class
	public ClassNode(DexNode dex, ClassInfo clsInfo) {
		this.dex = dex;
		this.clsInfo = clsInfo;
		this.interfaces = Collections.emptyList();
		this.methods = Collections.emptyList();
		this.fields = Collections.emptyList();
		this.accessFlags = new AccessInfo(AccessFlags.ACC_PUBLIC | AccessFlags.ACC_SYNTHETIC, AFType.CLASS);
		this.parentClass = this;
	}

	private void loadAnnotations(ClassDef cls) {
		int offset = cls.getAnnotationsOffset();
		if (offset != 0) {
			try {
				new AnnotationsParser(this).parse(offset);
			} catch (Exception e) {
				LOG.error("Error parsing annotations in {}", this, e);
			}
		}
	}

	private void loadStaticValues(ClassDef cls, List<FieldNode> staticFields) throws DecodeException {
		for (FieldNode f : staticFields) {
			if (f.getAccessFlags().isFinal()) {
				f.addAttr(new FieldValueAttr(null));
			}
		}

		int offset = cls.getStaticValuesOffset();
		if (offset != 0) {
			StaticValuesParser parser = new StaticValuesParser(dex, dex.openSection(offset));
			int count = parser.processFields(staticFields);
			constFields = new LinkedHashMap<Object, FieldNode>(count);
			for (FieldNode f : staticFields) {
				AccessInfo accFlags = f.getAccessFlags();
				if (accFlags.isStatic() && accFlags.isFinal()) {
					FieldValueAttr fv = f.get(AType.FIELD_VALUE);
					if (fv != null && fv.getValue() != null) {
						if (accFlags.isPublic()) {
							dex.getConstFields().put(fv.getValue(), f);
						}
						constFields.put(fv.getValue(), f);
					}
				}
{'fraction_non_alphanumeric': 0.12390220745312129, 'fraction_numerical': 0.0009494422027059103, 'mean_word_length': 4.42024013722127, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '4330347', 'n_tokens_mistral': 4524, 'n_tokens_neox': 4009, 'n_words': 819}