ClassNode.java 22.0 KB
Newer Older
S
Skylot 已提交
1 2
package jadx.core.dex.nodes;

3 4 5
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
6
import java.util.LinkedHashSet;
7 8
import java.util.List;
import java.util.Map;
9
import java.util.Set;
10
import java.util.function.BiConsumer;
11
import java.util.function.Consumer;
12
import java.util.stream.Collectors;
13

14
import org.jetbrains.annotations.NotNull;
15 16 17 18
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

S
Skylot 已提交
19
import jadx.api.DecompilationMode;
20
import jadx.api.ICodeCache;
S
Skylot 已提交
21
import jadx.api.ICodeInfo;
S
Skylot 已提交
22
import jadx.api.ICodeWriter;
S
Skylot 已提交
23
import jadx.api.JadxArgs;
24
import jadx.api.plugins.input.data.IClassData;
25 26
import jadx.api.plugins.input.data.IFieldData;
import jadx.api.plugins.input.data.IMethodData;
27
import jadx.api.plugins.input.data.annotations.EncodedValue;
S
Skylot 已提交
28 29 30 31 32 33
import jadx.api.plugins.input.data.attributes.JadxAttrType;
import jadx.api.plugins.input.data.attributes.types.AnnotationDefaultAttr;
import jadx.api.plugins.input.data.attributes.types.AnnotationDefaultClassAttr;
import jadx.api.plugins.input.data.attributes.types.InnerClassesAttr;
import jadx.api.plugins.input.data.attributes.types.InnerClsInfo;
import jadx.api.plugins.input.data.attributes.types.SourceFileAttr;
34
import jadx.api.plugins.input.data.impl.ListConsumer;
S
Skylot 已提交
35
import jadx.core.Consts;
S
Skylot 已提交
36
import jadx.core.ProcessClass;
37
import jadx.core.dex.attributes.AFlag;
38
import jadx.core.dex.attributes.AType;
39
import jadx.core.dex.attributes.nodes.InlinedAttr;
40
import jadx.core.dex.attributes.nodes.NotificationAttrNode;
S
Skylot 已提交
41 42 43 44 45 46
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;
1
13.beta2 已提交
47
import jadx.core.dex.instructions.args.LiteralArg;
48
import jadx.core.dex.nodes.utils.TypeUtils;
49
import jadx.core.utils.ListUtils;
50
import jadx.core.utils.Utils;
S
Skylot 已提交
51
import jadx.core.utils.exceptions.JadxRuntimeException;
S
Skylot 已提交
52

S
Skylot 已提交
53
import static jadx.core.dex.nodes.ProcessState.LOADED;
54
import static jadx.core.dex.nodes.ProcessState.NOT_LOADED;
55

56
public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeNode, Comparable<ClassNode> {
S
Skylot 已提交
57
	private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class);
S
Skylot 已提交
58

59
	private final RootNode root;
60
	private final IClassData clsData;
61

S
Skylot 已提交
62
	private final ClassInfo clsInfo;
63
	private AccessInfo accessFlags;
S
Skylot 已提交
64 65
	private ArgType superClass;
	private List<ArgType> interfaces;
66
	private List<ArgType> generics = Collections.emptyList();
S
Skylot 已提交
67

68 69
	private List<MethodNode> methods;
	private List<FieldNode> fields;
70
	private List<ClassNode> innerClasses = Collections.emptyList();
S
Skylot 已提交
71

72 73
	private List<ClassNode> inlinedClasses = Collections.emptyList();

74 75
	// store smali
	private String smali;
76 77
	// store parent for inner classes or 'this' otherwise
	private ClassNode parentClass;
S
Skylot 已提交
78

S
Skylot 已提交
79
	private volatile ProcessState state = ProcessState.NOT_LOADED;
80
	private LoadStage loadStage = LoadStage.NONE;
81

82 83 84
	/**
	 * Top level classes used in this class (only for top level classes, empty for inners)
	 */
S
Skylot 已提交
85
	private List<ClassNode> dependencies = Collections.emptyList();
86 87 88 89
	/**
	 * Top level classes needed for code generation stage
	 */
	private List<ClassNode> codegenDeps = Collections.emptyList();
90 91 92
	/**
	 * Classes which uses this class
	 */
93
	private List<ClassNode> useIn = Collections.emptyList();
94 95 96
	/**
	 * Methods which uses this class (by instructions only, definition is excluded)
	 */
97
	private List<MethodNode> useInMth = Collections.emptyList();
98

S
Skylot 已提交
99 100 101
	// cache maps
	private Map<MethodInfo, MethodNode> mthInfoMap = Collections.emptyMap();

102 103 104
	public ClassNode(RootNode root, IClassData cls) {
		this.root = root;
		this.clsInfo = ClassInfo.fromType(root, ArgType.object(cls.getType()));
105
		this.clsData = cls.copy();
106
		initialLoad(clsData);
107 108
	}

S
Skylot 已提交
109
	private void initialLoad(IClassData cls) {
S
Skylot 已提交
110
		try {
111 112 113
			addAttrs(cls.getAttributes());
			this.accessFlags = new AccessInfo(getAccessFlags(cls), AFType.CLASS);
			this.superClass = checkSuperType(cls);
114
			this.interfaces = Utils.collectionMap(cls.getInterfacesTypes(), ArgType::object);
S
Skylot 已提交
115

116 117 118
			ListConsumer<IFieldData, FieldNode> fieldsConsumer = new ListConsumer<>(fld -> FieldNode.build(this, fld));
			ListConsumer<IMethodData, MethodNode> methodsConsumer = new ListConsumer<>(mth -> MethodNode.build(this, mth));
			cls.visitFieldsAndMethods(fieldsConsumer, methodsConsumer);
119 120 121 122
			if (this.fields != null && this.methods != null) {
				// TODO: temporary solution for restore usage info in reloaded methods and fields
				restoreUsageData(this.fields, this.methods, fieldsConsumer.getResult(), methodsConsumer.getResult());
			}
123 124
			this.fields = fieldsConsumer.getResult();
			this.methods = methodsConsumer.getResult();
S
Skylot 已提交
125

S
Skylot 已提交
126 127
			initStaticValues(fields);
			processAttributes(this);
S
Skylot 已提交
128
			buildCache();
129 130 131 132 133

			// TODO: implement module attribute parsing
			if (this.accessFlags.isModuleInfo()) {
				this.addWarnComment("Modules not supported yet");
			}
S
Skylot 已提交
134
		} catch (Exception e) {
135
			throw new JadxRuntimeException("Error decode class: " + clsInfo, e);
S
Skylot 已提交
136 137 138
		}
	}

139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
	private void restoreUsageData(List<FieldNode> oldFields, List<MethodNode> oldMethods,
			List<FieldNode> newFields, List<MethodNode> newMethods) {
		Map<FieldInfo, FieldNode> oldFieldMap = Utils.groupBy(oldFields, FieldNode::getFieldInfo);
		for (FieldNode newField : newFields) {
			FieldNode oldField = oldFieldMap.get(newField.getFieldInfo());
			if (oldField != null) {
				newField.setUseIn(oldField.getUseIn());
			}
		}
		Map<MethodInfo, MethodNode> oldMethodsMap = Utils.groupBy(oldMethods, MethodNode::getMethodInfo);
		for (MethodNode newMethod : newMethods) {
			MethodNode oldMethod = oldMethodsMap.get(newMethod.getMethodInfo());
			if (oldMethod != null) {
				newMethod.setUseIn(oldMethod.getUseIn());
			}
		}
	}

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
	private ArgType checkSuperType(IClassData cls) {
		String superType = cls.getSuperType();
		if (superType == null) {
			if (clsInfo.getType().getObject().equals(Consts.CLASS_OBJECT)) {
				// java.lang.Object don't have super class
				return null;
			}
			if (this.accessFlags.isModuleInfo()) {
				// module-info also don't have super class
				return null;
			}
			throw new JadxRuntimeException("No super class in " + clsInfo.getType());
		}
		return ArgType.object(superType);
	}

173 174 175 176 177 178
	public void updateGenericClsData(ArgType superClass, List<ArgType> interfaces, List<ArgType> generics) {
		this.superClass = superClass;
		this.interfaces = interfaces;
		this.generics = generics;
	}

S
Skylot 已提交
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
	private static void processAttributes(ClassNode cls) {
		// move AnnotationDefault from cls to methods (dex specific)
		AnnotationDefaultClassAttr defAttr = cls.get(JadxAttrType.ANNOTATION_DEFAULT_CLASS);
		if (defAttr != null) {
			cls.remove(JadxAttrType.ANNOTATION_DEFAULT_CLASS);
			for (Map.Entry<String, EncodedValue> entry : defAttr.getValues().entrySet()) {
				MethodNode mth = cls.searchMethodByShortName(entry.getKey());
				if (mth != null) {
					mth.addAttr(new AnnotationDefaultAttr(entry.getValue()));
				} else {
					cls.addWarnComment("Method from annotation default annotation not found: " + entry.getKey());
				}
			}
		}

		// check source file attribute
		if (!cls.checkSourceFilenameAttr()) {
			cls.remove(JadxAttrType.SOURCE_FILE);
		}
	}

	private int getAccessFlags(IClassData cls) {
		InnerClassesAttr innerClassesAttr = get(JadxAttrType.INNER_CLASSES);
		if (innerClassesAttr != null) {
			InnerClsInfo innerClsInfo = innerClassesAttr.getMap().get(cls.getType());
			if (innerClsInfo != null) {
				return innerClsInfo.getAccessFlags();
			}
207
		}
S
Skylot 已提交
208
		return cls.getAccessFlags();
209 210
	}

211
	public static ClassNode addSyntheticClass(RootNode root, String name, int accessFlags) {
212 213 214 215 216 217 218 219 220 221
		ClassInfo clsInfo = ClassInfo.fromName(root, name);
		ClassNode existCls = root.resolveClass(clsInfo);
		if (existCls != null) {
			throw new JadxRuntimeException("Class already exist: " + name);
		}
		return addSyntheticClass(root, clsInfo, accessFlags);
	}

	public static ClassNode addSyntheticClass(RootNode root, ClassInfo clsInfo, int accessFlags) {
		ClassNode cls = new ClassNode(root, clsInfo, accessFlags);
222 223 224 225 226 227 228
		cls.add(AFlag.SYNTHETIC);
		cls.setState(ProcessState.PROCESS_COMPLETE);
		root.addClassNode(cls);
		return cls;
	}

	// Create empty class
229
	private ClassNode(RootNode root, ClassInfo clsInfo, int accessFlags) {
230
		this.root = root;
231
		this.clsData = null;
232
		this.clsInfo = clsInfo;
233 234 235 236
		this.interfaces = new ArrayList<>();
		this.methods = new ArrayList<>();
		this.fields = new ArrayList<>();
		this.accessFlags = new AccessInfo(accessFlags, AFType.CLASS);
237 238 239
		this.parentClass = this;
	}

S
Skylot 已提交
240
	private void initStaticValues(List<FieldNode> fields) {
241 242
		if (fields.isEmpty()) {
			return;
S
Skylot 已提交
243
		}
244
		List<FieldNode> staticFields = fields.stream().filter(FieldNode::isStatic).collect(Collectors.toList());
S
Skylot 已提交
245
		for (FieldNode f : staticFields) {
S
Skylot 已提交
246
			if (f.getAccessFlags().isFinal() && f.get(JadxAttrType.CONSTANT_VALUE) == null) {
247
				// incorrect initialization will be removed if assign found in constructor
S
Skylot 已提交
248
				f.addAttr(EncodedValue.NULL);
S
Skylot 已提交
249 250
			}
		}
251 252 253 254 255
		try {
			// process const fields
			root().getConstValues().processConstFields(this, staticFields);
		} catch (Exception e) {
			this.addWarnComment("Failed to load initial values for static fields", e);
256
		}
S
Skylot 已提交
257 258
	}

S
Skylot 已提交
259 260 261 262
	private boolean checkSourceFilenameAttr() {
		SourceFileAttr sourceFileAttr = get(JadxAttrType.SOURCE_FILE);
		if (sourceFileAttr == null) {
			return true;
263
		}
S
Skylot 已提交
264
		String fileName = sourceFileAttr.getFileName();
265 266 267
		if (fileName.endsWith(".java")) {
			fileName = fileName.substring(0, fileName.length() - 5);
		}
268
		if (fileName.isEmpty() || fileName.equals("SourceFile")) {
S
Skylot 已提交
269
			return false;
270 271 272 273
		}
		if (clsInfo != null) {
			String name = clsInfo.getShortName();
			if (fileName.equals(name)) {
S
Skylot 已提交
274 275 276 277 278 279 280 281 282
				return false;
			}
			ClassInfo parentCls = clsInfo.getParentClass();
			while (parentCls != null) {
				String parentName = parentCls.getShortName();
				if (parentName.equals(fileName) || parentName.startsWith(fileName + '$')) {
					return false;
				}
				parentCls = parentCls.getParentClass();
283
			}
284
			if (fileName.contains("$") && fileName.endsWith('$' + name)) {
S
Skylot 已提交
285 286 287 288
				return false;
			}
			if (name.contains("$") && name.startsWith(fileName)) {
				return false;
S
Skylot 已提交
289
			}
290
		}
S
Skylot 已提交
291
		return true;
292 293
	}

294 295 296 297
	public boolean checkProcessed() {
		return getTopParentClass().getState().isProcessComplete();
	}

298
	public void ensureProcessed() {
299 300
		if (!checkProcessed()) {
			ClassNode topParentClass = getTopParentClass();
301
			throw new JadxRuntimeException("Expected class to be processed at this point,"
302
					+ " class: " + topParentClass + ", state: " + topParentClass.getState());
303
		}
S
Skylot 已提交
304 305
	}

306 307 308 309
	public ICodeInfo decompile() {
		return decompile(true);
	}

S
Skylot 已提交
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
	/**
	 * WARNING: Slow operation! Use with caution!
	 */
	public ICodeInfo decompileWithMode(DecompilationMode mode) {
		DecompilationMode baseMode = root.getArgs().getDecompilationMode();
		if (mode == baseMode) {
			return decompile(true);
		}
		JadxArgs args = root.getArgs();
		try {
			unload();
			args.setDecompilationMode(mode);
			ProcessClass process = new ProcessClass(args);
			process.initPasses(root);
			return process.generateCode(this);
		} finally {
			args.setDecompilationMode(baseMode);
		}
	}

330 331 332 333
	public ICodeInfo getCode() {
		return decompile(true);
	}

334 335
	public ICodeInfo reloadCode() {
		add(AFlag.CLASS_DEEP_RELOAD);
336 337 338
		return decompile(false);
	}

339 340 341 342 343 344 345 346 347
	public void unloadCode() {
		if (state == NOT_LOADED) {
			return;
		}
		add(AFlag.CLASS_UNLOADED);
		unloadFromCache();
		deepUnload();
	}

S
Skylot 已提交
348
	public void deepUnload() {
349
		if (clsData == null) {
S
Skylot 已提交
350 351 352
			// manually added class
			return;
		}
353
		unload();
354
		clearAttributes();
355
		root().getConstValues().removeForClass(this);
356
		initialLoad(clsData);
357

358
		innerClasses.forEach(ClassNode::deepUnload);
359 360
	}

361 362 363 364 365 366 367 368
	private void unloadFromCache() {
		if (isInner()) {
			return;
		}
		ICodeCache codeCache = root().getCodeCache();
		codeCache.remove(getRawName());
	}

369
	private synchronized ICodeInfo decompile(boolean searchInCache) {
370 371 372
		if (isInner()) {
			return ICodeInfo.EMPTY;
		}
373
		ICodeCache codeCache = root().getCodeCache();
374
		String clsRawName = getRawName();
375 376
		if (searchInCache) {
			ICodeInfo code = codeCache.get(clsRawName);
377
			if (code != null && code != ICodeInfo.EMPTY) {
378 379
				return code;
			}
S
Skylot 已提交
380
		}
S
Skylot 已提交
381
		ICodeInfo codeInfo = root.getProcessClasses().generateCode(this);
382
		codeCache.add(clsRawName, codeInfo);
S
Skylot 已提交
383 384 385
		return codeInfo;
	}

386 387 388 389 390 391 392 393 394 395 396
	@Nullable
	public ICodeInfo getCodeFromCache() {
		ICodeCache codeCache = root().getCodeCache();
		String clsRawName = getRawName();
		ICodeInfo codeInfo = codeCache.get(clsRawName);
		if (codeInfo == ICodeInfo.EMPTY) {
			return null;
		}
		return codeInfo;
	}

S
Skylot 已提交
397
	@Override
S
Skylot 已提交
398
	public void load() {
S
Skylot 已提交
399
		for (MethodNode mth : getMethods()) {
S
Skylot 已提交
400 401
			try {
				mth.load();
402
			} catch (Exception e) {
403
				mth.addError("Method load error", e);
S
Skylot 已提交
404
			}
S
Skylot 已提交
405 406 407 408
		}
		for (ClassNode innerCls : getInnerClasses()) {
			innerCls.load();
		}
S
Skylot 已提交
409
		setState(LOADED);
S
Skylot 已提交
410 411 412 413
	}

	@Override
	public void unload() {
414 415 416
		if (state == NOT_LOADED) {
			return;
		}
417 418 419 420
		methods.forEach(MethodNode::unload);
		innerClasses.forEach(ClassNode::unload);
		fields.forEach(FieldNode::unloadAttributes);
		unloadAttributes();
421
		setState(NOT_LOADED);
422
		this.loadStage = LoadStage.NONE;
423
		this.smali = null;
S
Skylot 已提交
424 425
	}

S
Skylot 已提交
426
	private void buildCache() {
S
Skylot 已提交
427
		mthInfoMap = new HashMap<>(methods.size());
S
Skylot 已提交
428 429 430 431 432
		for (MethodNode mth : methods) {
			mthInfoMap.put(mth.getMethodInfo(), mth);
		}
	}

S
Skylot 已提交
433 434
	@Nullable
	public ArgType getSuperClass() {
S
Skylot 已提交
435 436 437
		return superClass;
	}

S
Skylot 已提交
438
	public List<ArgType> getInterfaces() {
S
Skylot 已提交
439 440 441
		return interfaces;
	}

442
	public List<ArgType> getGenericTypeParameters() {
443
		return generics;
444 445
	}

446 447 448 449 450 451 452 453
	public ArgType getType() {
		ArgType clsType = clsInfo.getType();
		if (Utils.notEmpty(generics)) {
			return ArgType.generic(clsType, generics);
		}
		return clsType;
	}

S
Skylot 已提交
454 455 456 457 458 459 460 461
	public List<MethodNode> getMethods() {
		return methods;
	}

	public List<FieldNode> getFields() {
		return fields;
	}

462 463 464 465
	public void addField(FieldNode fld) {
		fields.add(fld);
	}

466 467 468 469
	public FieldNode getConstField(Object obj) {
		return getConstField(obj, true);
	}

470
	@Nullable
471
	public FieldNode getConstField(Object obj, boolean searchGlobal) {
472
		return root().getConstValues().getConstField(this, obj, searchGlobal);
473 474
	}

475
	@Nullable
1
13.beta2 已提交
476
	public FieldNode getConstFieldByLiteralArg(LiteralArg arg) {
477
		return root().getConstValues().getConstFieldByLiteralArg(this, arg);
1
13.beta2 已提交
478 479
	}

480
	public FieldNode searchField(FieldInfo field) {
S
Skylot 已提交
481
		for (FieldNode f : fields) {
482
			if (f.getFieldInfo().equals(field)) {
S
Skylot 已提交
483
				return f;
S
Skylot 已提交
484
			}
S
Skylot 已提交
485 486 487 488
		}
		return null;
	}

489 490 491 492 493 494 495 496 497
	public FieldNode searchFieldByNameAndType(FieldInfo field) {
		for (FieldNode f : fields) {
			if (f.getFieldInfo().equalsNameAndType(field)) {
				return f;
			}
		}
		return null;
	}

S
Skylot 已提交
498
	public FieldNode searchFieldByName(String name) {
S
Skylot 已提交
499
		for (FieldNode f : fields) {
S
Skylot 已提交
500
			if (f.getName().equals(name)) {
S
Skylot 已提交
501
				return f;
S
Skylot 已提交
502
			}
S
Skylot 已提交
503 504 505 506
		}
		return null;
	}

507 508 509 510 511 512 513 514 515
	public FieldNode searchFieldByShortId(String shortId) {
		for (FieldNode f : fields) {
			if (f.getFieldInfo().getShortId().equals(shortId)) {
				return f;
			}
		}
		return null;
	}

S
Skylot 已提交
516
	public MethodNode searchMethod(MethodInfo mth) {
S
Skylot 已提交
517
		return mthInfoMap.get(mth);
S
Skylot 已提交
518 519
	}

520
	public MethodNode searchMethodByShortId(String shortId) {
S
Skylot 已提交
521
		for (MethodNode m : methods) {
S
Skylot 已提交
522
			if (m.getMethodInfo().getShortId().equals(shortId)) {
S
Skylot 已提交
523
				return m;
S
Skylot 已提交
524
			}
S
Skylot 已提交
525 526 527 528
		}
		return null;
	}

529 530
	/**
	 * Return first method by original short name
531 532
	 * Note: methods are not unique by name (class can have several methods with same name but different
	 * signature)
533 534 535 536 537 538 539 540 541 542 543
	 */
	@Nullable
	public MethodNode searchMethodByShortName(String name) {
		for (MethodNode m : methods) {
			if (m.getMethodInfo().getName().equals(name)) {
				return m;
			}
		}
		return null;
	}

544
	public ClassNode getParentClass() {
545 546 547 548 549 550 551 552 553
		return parentClass;
	}

	public void updateParentClass() {
		if (clsInfo.isInner()) {
			ClassNode parent = root.resolveClass(clsInfo.getParentClass());
			if (parent != null) {
				parentClass = parent;
				return;
554 555
			}
		}
556
		parentClass = this;
557 558
	}

559 560
	public ClassNode getTopParentClass() {
		ClassNode parent = getParentClass();
561
		return parent == this ? this : parent.getTopParentClass();
562 563
	}

564 565 566 567 568 569 570 571 572 573
	public void visitParentClasses(Consumer<ClassNode> consumer) {
		ClassNode currentCls = this;
		ClassNode parentCls = currentCls.getParentClass();
		while (parentCls != currentCls) {
			consumer.accept(parentCls);
			currentCls = parentCls;
			parentCls = currentCls.getParentClass();
		}
	}

574 575 576 577 578 579 580 581 582 583 584 585 586
	public void visitSuperTypes(BiConsumer<ArgType, ArgType> consumer) {
		TypeUtils typeUtils = root.getTypeUtils();
		ArgType thisType = this.getType();
		if (!superClass.equals(ArgType.OBJECT)) {
			consumer.accept(thisType, superClass);
			typeUtils.visitSuperTypes(superClass, consumer);
		}
		for (ArgType iface : interfaces) {
			consumer.accept(thisType, iface);
			typeUtils.visitSuperTypes(iface, consumer);
		}
	}

587 588 589 590 591 592 593 594 595 596 597
	public boolean hasNotGeneratedParent() {
		if (contains(AFlag.DONT_GENERATE)) {
			return true;
		}
		ClassNode parent = getParentClass();
		if (parent == this) {
			return false;
		}
		return parent.hasNotGeneratedParent();
	}

S
Skylot 已提交
598 599 600 601
	public List<ClassNode> getInnerClasses() {
		return innerClasses;
	}

602 603 604 605
	public List<ClassNode> getInlinedClasses() {
		return inlinedClasses;
	}

606
	/**
607
	 * Get all inner and inlined classes recursively
608
	 *
609 610
	 * @param resultClassesSet
	 *                         all identified inner and inlined classes are added to this set
611
	 */
612 613 614 615 616 617 618 619 620 621
	public void getInnerAndInlinedClassesRecursive(Set<ClassNode> resultClassesSet) {
		for (ClassNode innerCls : innerClasses) {
			if (resultClassesSet.add(innerCls)) {
				innerCls.getInnerAndInlinedClassesRecursive(resultClassesSet);
			}
		}
		for (ClassNode inlinedCls : inlinedClasses) {
			if (resultClassesSet.add(inlinedCls)) {
				inlinedCls.getInnerAndInlinedClassesRecursive(resultClassesSet);
			}
622 623 624
		}
	}

S
Skylot 已提交
625
	public void addInnerClass(ClassNode cls) {
626 627 628
		if (innerClasses.isEmpty()) {
			innerClasses = new ArrayList<>(5);
		}
S
Skylot 已提交
629
		innerClasses.add(cls);
630
		cls.parentClass = this;
S
Skylot 已提交
631 632
	}

633 634 635 636
	public void addInlinedClass(ClassNode cls) {
		if (inlinedClasses.isEmpty()) {
			inlinedClasses = new ArrayList<>(5);
		}
637
		cls.addAttr(new InlinedAttr(this));
638 639 640
		inlinedClasses.add(cls);
	}

641
	public boolean isEnum() {
S
Skylot 已提交
642 643 644
		return getAccessFlags().isEnum()
				&& getSuperClass() != null
				&& getSuperClass().getObject().equals(ArgType.ENUM.getObject());
645 646
	}

S
Skylot 已提交
647
	public boolean isAnonymous() {
648
		return contains(AType.ANONYMOUS_CLASS);
649 650
	}

651
	public boolean isInner() {
652
		return parentClass != this;
653 654
	}

655 656 657 658
	public boolean isTopClass() {
		return parentClass == this;
	}

659 660
	@Nullable
	public MethodNode getClassInitMth() {
661
		return searchMethodByShortId("<clinit>()V");
662 663 664
	}

	@Nullable
S
Skylot 已提交
665 666
	public MethodNode getDefaultConstructor() {
		for (MethodNode mth : methods) {
667
			if (mth.isDefaultConstructor()) {
S
Skylot 已提交
668
				return mth;
S
Skylot 已提交
669 670
			}
		}
S
Skylot 已提交
671
		return null;
S
Skylot 已提交
672 673
	}

674
	@Override
S
Skylot 已提交
675 676 677 678
	public AccessInfo getAccessFlags() {
		return accessFlags;
	}

679 680 681 682 683
	@Override
	public void setAccessFlags(AccessInfo accessFlags) {
		this.accessFlags = accessFlags;
	}

684 685
	@Override
	public RootNode root() {
686
		return root;
687 688
	}

689 690 691 692 693
	@Override
	public String typeName() {
		return "class";
	}

S
Skylot 已提交
694 695 696 697 698 699 700
	public String getRawName() {
		return clsInfo.getRawName();
	}

	/**
	 * Internal class info (don't use in code generation and external api).
	 */
S
Skylot 已提交
701 702 703 704 705
	public ClassInfo getClassInfo() {
		return clsInfo;
	}

	public String getShortName() {
706
		return clsInfo.getAliasShortName();
S
Skylot 已提交
707 708 709
	}

	public String getFullName() {
710
		return clsInfo.getAliasFullName();
S
Skylot 已提交
711 712 713
	}

	public String getPackage() {
714
		return clsInfo.getAliasPkg();
715 716
	}

S
Skylot 已提交
717
	public String getDisassembledCode() {
718
		if (smali == null) {
719
			StringBuilder sb = new StringBuilder();
S
Skylot 已提交
720 721
			getDisassembledCode(sb);
			sb.append(ICodeWriter.NL);
722 723 724
			Set<ClassNode> allInlinedClasses = new LinkedHashSet<>();
			getInnerAndInlinedClassesRecursive(allInlinedClasses);
			for (ClassNode innerClass : allInlinedClasses) {
S
Skylot 已提交
725 726
				innerClass.getDisassembledCode(sb);
				sb.append(ICodeWriter.NL);
727
			}
728
			smali = sb.toString();
729
		}
730 731 732
		return smali;
	}

S
Skylot 已提交
733 734
	protected void getDisassembledCode(StringBuilder sb) {
		if (clsData == null) {
735 736
			sb.append(String.format("###### Class %s is created by jadx", getFullName()));
			return;
737
		}
738
		sb.append(String.format("###### Class %s (%s)", getFullName(), getRawName()));
S
Skylot 已提交
739 740
		sb.append(ICodeWriter.NL);
		sb.append(clsData.getDisassembledCode());
741 742
	}

743 744
	public IClassData getClsData() {
		return clsData;
745 746
	}

747 748 749 750 751 752 753 754
	public ProcessState getState() {
		return state;
	}

	public void setState(ProcessState state) {
		this.state = state;
	}

755 756 757 758
	public LoadStage getLoadStage() {
		return loadStage;
	}

759 760
	public void setLoadStage(LoadStage loadStage) {
		this.loadStage = loadStage;
761 762 763 764 765
	}

	public void reloadAtCodegenStage() {
		ClassNode topCls = this.getTopParentClass();
		if (topCls.getLoadStage() == LoadStage.CODEGEN_STAGE) {
766
			throw new JadxRuntimeException("Class not yet loaded at codegen stage: " + topCls);
767 768 769 770
		}
		topCls.add(AFlag.RELOAD_AT_CODEGEN_STAGE);
	}

S
Skylot 已提交
771
	public List<ClassNode> getDependencies() {
772 773 774
		return dependencies;
	}

S
Skylot 已提交
775 776 777 778
	public void setDependencies(List<ClassNode> dependencies) {
		this.dependencies = dependencies;
	}

779 780 781 782
	public void removeDependency(ClassNode dep) {
		this.dependencies = ListUtils.safeRemoveAndTrim(this.dependencies, dep);
	}

783 784 785 786 787 788 789 790
	public List<ClassNode> getCodegenDeps() {
		return codegenDeps;
	}

	public void setCodegenDeps(List<ClassNode> codegenDeps) {
		this.codegenDeps = codegenDeps;
	}

791 792 793 794
	public void addCodegenDep(ClassNode dep) {
		this.codegenDeps = ListUtils.safeAdd(this.codegenDeps, dep);
	}

795 796 797 798
	public int getTotalDepsCount() {
		return dependencies.size() + codegenDeps.size();
	}

799 800 801 802 803 804 805 806 807 808
	public List<ClassNode> getUseIn() {
		return useIn;
	}

	public void setUseIn(List<ClassNode> useIn) {
		this.useIn = useIn;
	}

	public List<MethodNode> getUseInMth() {
		return useInMth;
809 810
	}

811 812
	public void setUseInMth(List<MethodNode> useInMth) {
		this.useInMth = useInMth;
813 814
	}

815
	@Override
816 817
	public String getInputFileName() {
		return clsData == null ? "synthetic" : clsData.getInputFileName();
818 819
	}

820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
	@Override
	public int hashCode() {
		return clsInfo.hashCode();
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (o instanceof ClassNode) {
			ClassNode other = (ClassNode) o;
			return clsInfo.equals(other.clsInfo);
		}
		return false;
	}

837 838 839 840 841
	@Override
	public int compareTo(@NotNull ClassNode o) {
		return this.getFullName().compareTo(o.getFullName());
	}

S
Skylot 已提交
842 843
	@Override
	public String toString() {
S
Skylot 已提交
844
		return clsInfo.getFullName();
S
Skylot 已提交
845 846
	}
}