Skip to content

Commit 30d5cf7

Browse files
Yuh Shin Ongfacebook-github-bot
Yuh Shin Ong
authored andcommitted
More complete class interval computation
Summary: Changes the class interval computation to support internal classes deriving from external classes. Specifically, as documented in the comments: ``` // Theoretically, all classes are rooted in java.lang.Object. In practice, // internal classes inheriting from external classes will not be reachable // from Object. Treat the class hierarchy as a forest of trees, with roots // being either direct children of Object or internal classes deriving // directly from external classes. ``` Previously, internal classes that derived from external classes had no intervals (top). Reviewed By: anwesht Differential Revision: D71085091 fbshipit-source-id: 156d790af70bf5b7c0383a70997c385d1d34c01b
1 parent 236914d commit 30d5cf7

File tree

120 files changed

+2343
-2312
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

120 files changed

+2343
-2312
lines changed

source/ClassIntervals.cpp

+41-10
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,16 @@ void dfs_on_hierarchy(
3232
mt_assert(children != class_hierarchy.end());
3333

3434
for (const auto* child : children->second) {
35+
mt_assert(
36+
dfs_order <
37+
std::numeric_limits<int32_t>::max()); // Ensure no overflows.
3538
++dfs_order;
36-
mt_assert(dfs_order > 0); // Ensure no overflows.
3739
dfs_on_hierarchy(class_hierarchy, child, dfs_order, result);
3840
}
3941

42+
mt_assert(
43+
dfs_order < std::numeric_limits<int32_t>::max()); // Ensure no overflows.
4044
++dfs_order;
41-
mt_assert(dfs_order > 0); // Ensure no overflows.
4245

4346
// Each node should only be visited once since multiple inheritance is not
4447
// supported by Java/Kotlin.
@@ -53,22 +56,50 @@ ClassIntervals::ClassIntervals(
5356
const Options& options,
5457
const DexStoresVector& stores)
5558
: top_(Interval::top()) {
59+
// Theoretically, all classes are rooted in java.lang.Object. In practice,
60+
// internal classes inheriting from external classes will not be reachable
61+
// from Object. Treat the class hierarchy as a forest of trees, with roots
62+
// being either direct children of Object or internal classes deriving
63+
// directly from external classes.
5664
ClassHierarchy class_hierarchy;
65+
const auto* object_root = type::java_lang_Object();
66+
67+
// Use a deterministic ordering for the roots so that integration tests always
68+
// return the same interval for the same class.
69+
std::set<const DexType*, dextypes_comparator> roots;
70+
5771
for (const auto& scope : DexStoreClassesIterator(stores)) {
5872
auto store_hierarchy = build_type_hierarchy(scope);
5973
for (const auto& [parent, children] : store_hierarchy) {
60-
class_hierarchy[parent].insert(children.begin(), children.end());
74+
if (parent == object_root) {
75+
roots.insert(children.begin(), children.end());
76+
} else {
77+
const auto* parent_class = type_class(parent);
78+
if (parent_class == nullptr) {
79+
// DexType exists but not DexClass, this is an external class.
80+
continue;
81+
}
82+
83+
class_hierarchy[parent].insert(children.begin(), children.end());
84+
85+
const auto* super_class = type_class(parent_class->get_super_class());
86+
if (super_class == nullptr) {
87+
// DexClass does not exist for for the super class. This is a root
88+
// (derives directly from an external class).
89+
roots.insert(parent_class->get_type());
90+
}
91+
}
6192
}
6293
}
6394

64-
// Assuming the code is known, all classes will be rooted in java.lang.Object.
65-
// Optimization: Divide the single hierarchy into multiple, with roots at
66-
// direct children of Object, then compute in parallel. Need to make sure
67-
// dfs_order does not intersect between different trees.
68-
const auto* root = type::java_lang_Object();
69-
7095
std::int32_t dfs_order = MIN_INTERVAL;
71-
dfs_on_hierarchy(class_hierarchy, root, dfs_order, class_intervals_);
96+
for (const auto* root : roots) {
97+
dfs_on_hierarchy(class_hierarchy, root, dfs_order, class_intervals_);
98+
mt_assert(
99+
dfs_order <
100+
std::numeric_limits<int32_t>::max()); // Ensure no overflows.
101+
++dfs_order;
102+
}
72103

73104
if (options.dump_class_intervals()) {
74105
auto class_intervals_path = options.class_intervals_output_path();

source/ClassIntervals.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ class ClassIntervals final {
6060
/**
6161
* Returns the most precisely known interval of the given type.
6262
* This is generally the computed type, but can be the open interval, such as
63-
* when class interval computation is disabled, or for non-class types.
63+
* when class interval computation is disabled, or for non-class types, or
64+
* java.lang.Object.
6465
*/
6566
const Interval& get_interval(const DexType* type) const;
6667

source/tests/ClassIntervalsTest.cpp

+10-11
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,16 @@ TEST_F(ClassIntervalsTest, IntervalComputation) {
3737
Scope scope;
3838

3939
// Construct simple class hierarchy, rooted in BaseA and BaseB.
40-
// Note that Object() is still the root of everything.
4140

42-
// BaseA -> DerivedA1
43-
// -> DerivedA2
41+
// BaseA [0,5] -> DerivedA1 [1,2]
42+
// -> DerivedA2 [3,4]
4443
const auto* a = marianatrench::redex::create_class(scope, "LBaseA;");
4544
const auto* a1 =
4645
marianatrench::redex::create_class(scope, "LDerivedA1;", a->get_type());
4746
const auto* a2 =
4847
marianatrench::redex::create_class(scope, "LDerivedA2;", a->get_type());
4948

50-
// BaseB -> DerivedB1 -> DerivedB1_1
49+
// BaseB [6,11] -> DerivedB1 [7,10] -> DerivedB1_1 [8,9]
5150
const auto* b = marianatrench::redex::create_class(scope, "LBaseB;");
5251
const auto* b1 =
5352
marianatrench::redex::create_class(scope, "LDerivedB1;", b->get_type());
@@ -57,26 +56,26 @@ TEST_F(ClassIntervalsTest, IntervalComputation) {
5756
auto context = test_context(scope);
5857

5958
auto interval_a = context.class_intervals->get_interval(a->get_type());
60-
EXPECT_EQ(ClassIntervals::Interval::finite(1, 6), interval_a);
59+
EXPECT_EQ(ClassIntervals::Interval::finite(0, 5), interval_a);
6160

6261
auto interval_a1 = context.class_intervals->get_interval(a1->get_type());
63-
EXPECT_EQ(ClassIntervals::Interval::finite(2, 3), interval_a1);
62+
EXPECT_EQ(ClassIntervals::Interval::finite(1, 2), interval_a1);
6463

6564
auto interval_a2 = context.class_intervals->get_interval(a2->get_type());
66-
EXPECT_EQ(ClassIntervals::Interval::finite(4, 5), interval_a2);
65+
EXPECT_EQ(ClassIntervals::Interval::finite(3, 4), interval_a2);
6766

6867
auto interval_b = context.class_intervals->get_interval(b->get_type());
69-
EXPECT_EQ(ClassIntervals::Interval::finite(7, 12), interval_b);
68+
EXPECT_EQ(ClassIntervals::Interval::finite(6, 11), interval_b);
7069

7170
auto interval_b1 = context.class_intervals->get_interval(b1->get_type());
72-
EXPECT_EQ(ClassIntervals::Interval::finite(8, 11), interval_b1);
71+
EXPECT_EQ(ClassIntervals::Interval::finite(7, 10), interval_b1);
7372

7473
auto interval_b1_1 = context.class_intervals->get_interval(b1_1->get_type());
75-
EXPECT_EQ(ClassIntervals::Interval::finite(9, 10), interval_b1_1);
74+
EXPECT_EQ(ClassIntervals::Interval::finite(8, 9), interval_b1_1);
7675

7776
auto interval_object =
7877
context.class_intervals->get_interval(type::java_lang_Object());
79-
EXPECT_EQ(ClassIntervals::Interval::finite(0, 13), interval_object);
78+
EXPECT_EQ(ClassIntervals::Interval::top(), interval_object);
8079
}
8180

8281
TEST_F(ClassIntervalsTest, ClassIntervalSerializationDeserialization) {

0 commit comments

Comments
 (0)