Skip to content

Commit b99685e

Browse files
committed
Make Il2Cpp.Object and Il2Cpp.ValueType lookup in parent class in case of static member
1 parent c1d0eca commit b99685e

File tree

4 files changed

+103
-33
lines changed

4 files changed

+103
-33
lines changed

src/structs/object.ts

+30-12
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,14 @@ namespace Il2Cpp {
7272
return Il2Cpp.exports.objectGetSize(this);
7373
}
7474

75-
/** Gets the field with the given name. */
75+
/** Gets the non-static field with the given name of the current class hierarchy. */
7676
field<T extends Il2Cpp.Field.Type>(name: string): Il2Cpp.Field<T> {
77-
return this.tryField(name) ?? raise(`couldn't find non-static field ${name} in class ${this.class.type.name}`);
77+
return this.tryField(name) ?? raise(`couldn't find non-static field ${name} in hierarchy of class ${this.class.type.name}`);
7878
}
7979

80-
/** Gets the method with the given name. */
80+
/** Gets the non-static method with the given name (and optionally parameter count) of the current class hierarchy. */
8181
method<T extends Il2Cpp.Method.ReturnType>(name: string, parameterCount: number = -1): Il2Cpp.Method<T> {
82-
return this.tryMethod<T>(name, parameterCount) ?? raise(`couldn't find non-static method ${name} in class ${this.class.type.name}`);
82+
return this.tryMethod<T>(name, parameterCount) ?? raise(`couldn't find non-static method ${name} in hierarchy of class ${this.class.type.name}`);
8383
}
8484

8585
/** Creates a reference to this object. */
@@ -92,21 +92,39 @@ namespace Il2Cpp {
9292
return new Il2Cpp.Method<T>(Il2Cpp.exports.objectGetVirtualMethod(this, method)).bind(this);
9393
}
9494

95-
/** Gets the field with the given name. */
95+
/** Gets the non-static field with the given name of the current class hierarchy, if it exists. */
9696
tryField<T extends Il2Cpp.Field.Type>(name: string): Il2Cpp.Field<T> | undefined {
9797
const field = this.class.tryField<T>(name);
98-
return field?.isStatic ? undefined : field?.bind(this);
98+
99+
if (field?.isStatic) {
100+
// classes cannot have static and non-static fields with the
101+
// same name, hence we can immediately check the parent
102+
for (const klass of this.class.hierarchy({ includeCurrent: false })) {
103+
for (const field of klass.fields) {
104+
if (field.name == name && !field.isStatic) {
105+
return field.bind(this) as Il2Cpp.Field<T>;
106+
}
107+
}
108+
}
109+
return undefined;
110+
}
111+
112+
return field?.bind(this);
99113
}
100114

101-
/** Gets the field with the given name. */
115+
/** Gets the non-static method with the given name (and optionally parameter count) of the current class hierarchy, if it exists. */
102116
tryMethod<T extends Il2Cpp.Method.ReturnType>(name: string, parameterCount: number = -1): Il2Cpp.Method<T> | undefined {
103-
let method = this.class.tryMethod<T>(name, parameterCount);
117+
const method = this.class.tryMethod<T>(name, parameterCount);
104118

105119
if (method?.isStatic) {
106-
method =
107-
(this.class.methods.find(
108-
_ => _.name == name && (parameterCount >= 0 ? _.parameterCount == parameterCount : true) && !_.isStatic
109-
) as Il2Cpp.Method<T>) ?? null;
120+
for (const klass of this.class.hierarchy()) {
121+
for (const method of klass.methods) {
122+
if (method.name == name && !method.isStatic && (parameterCount < 0 || method.parameterCount == parameterCount)) {
123+
return method.bind(this) as Il2Cpp.Method<T>;
124+
}
125+
}
126+
}
127+
return undefined;
110128
}
111129

112130
return method?.bind(this);

src/structs/value-type.ts

+34-8
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,50 @@ namespace Il2Cpp {
99
return new Il2Cpp.Object(Il2Cpp.exports.valueTypeBox(this.type.class, this));
1010
}
1111

12-
/** Gets the field with the given name. */
12+
/** Gets the non-static field with the given name of the current class hierarchy. */
1313
field<T extends Il2Cpp.Field.Type>(name: string): Il2Cpp.BoundField<T> {
14-
return this.type.class.field<T>(name).bind(this);
14+
return this.tryField(name) ?? raise(`couldn't find non-static field ${name} in hierarchy of class ${this.type.name}`);
1515
}
1616

17-
/** Gets the method with the given name. */
17+
/** Gets the non-static method with the given name (and optionally parameter count) of the current class hierarchy. */
1818
method<T extends Il2Cpp.Method.ReturnType>(name: string, parameterCount: number = -1): Il2Cpp.BoundMethod<T> {
19-
return this.type.class.method<T>(name, parameterCount).bind(this);
19+
return this.tryMethod<T>(name, parameterCount) ?? raise(`couldn't find non-static method ${name} in hierarchy of class ${this.type.name}`);
2020
}
2121

22-
/** Gets the field with the given name. */
22+
/** Gets the non-static field with the given name of the current class hierarchy, if it exists. */
2323
tryField<T extends Il2Cpp.Field.Type>(name: string): Il2Cpp.BoundField<T> | undefined {
24-
return this.type.class.tryField<T>(name)?.bind(this);
24+
const field = this.type.class.tryField<T>(name);
25+
26+
if (field?.isStatic) {
27+
for (const klass of this.type.class.hierarchy()) {
28+
for (const field of klass.fields) {
29+
if (field.name == name && !field.isStatic) {
30+
return field.bind(this) as Il2Cpp.Field<T>;
31+
}
32+
}
33+
}
34+
return undefined;
35+
}
36+
37+
return field?.bind(this);
2538
}
2639

27-
/** Gets the field with the given name. */
40+
/** Gets the non-static method with the given name (and optionally parameter count) of the current class hierarchy, if it exists. */
2841
tryMethod<T extends Il2Cpp.Method.ReturnType>(name: string, parameterCount: number = -1): Il2Cpp.BoundMethod<T> | undefined {
29-
return this.type.class.tryMethod<T>(name, parameterCount)?.bind(this);
42+
const method = this.type.class.tryMethod<T>(name, parameterCount);
43+
44+
if (method?.isStatic) {
45+
for (const klass of this.type.class.hierarchy()) {
46+
for (const method of klass.methods) {
47+
if (method.name == name && !method.isStatic && (parameterCount < 0 || method.parameterCount == parameterCount)) {
48+
return method.bind(this) as Il2Cpp.Method<T>;
49+
}
50+
}
51+
}
52+
return undefined;
53+
}
54+
55+
return method?.bind(this);
3056
}
3157

3258
/** */

test/GameAssembly.cs

+20
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@ public class Il2CppObjectTest
201201
{
202202
public static int F;
203203

204+
public int G;
205+
204206
public static int A(int a)
205207
{
206208
return 0;
@@ -232,6 +234,24 @@ public Il2CppObjectTest D()
232234
return this;
233235
}
234236
}
237+
238+
public class MemberLookupTest : Il2CppObjectTest {
239+
public new static int G;
240+
241+
public int H;
242+
243+
public new static void C()
244+
{
245+
}
246+
247+
public static void D()
248+
{
249+
}
250+
251+
public void D(int a)
252+
{
253+
}
254+
}
235255
}
236256

237257
enum Enum

test/agent/src/index.ts

+19-13
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ rpc.exports = {
7474
},
7575

7676
"Il2Cpp.Image::classCount"() {
77-
assert(Il2Cpp.domain.assembly("GameAssembly").image.classes.length).is(30);
78-
assert(Il2Cpp.domain.assembly("GameAssembly").image.classCount).is(30);
77+
assert(Il2Cpp.domain.assembly("GameAssembly").image.classes.length).is(31);
78+
assert(Il2Cpp.domain.assembly("GameAssembly").image.classCount).is(31);
7979
},
8080

8181
"Il2Cpp.Class::image"() {
@@ -253,23 +253,29 @@ rpc.exports = {
253253
},
254254

255255
"Il2Cpp.Object field lookup ignores static fields"() {
256-
const Class = Il2Cpp.domain.assembly("GameAssembly").image.class("Il2CppObjectTest");
256+
const T = Il2Cpp.domain.assembly("GameAssembly").image.class("Il2CppObjectTest").nested("MemberLookupTest");
257257

258-
assert(Class.new().tryField("F")).is(undefined);
259-
assert(() => Class.new().field("F")).throws("couldn't find non-static field F in class Il2CppObjectTest");
258+
assert(T.new().tryField("F")).is(undefined);
259+
assert(() => T.new().field("F")).throws("couldn't find non-static field F in hierarchy of class Il2CppObjectTest.MemberLookupTest");
260+
261+
assert(T.new().tryField("H")).not(undefined);
262+
assert(T.new().tryField("G")?.isStatic).is(false);
260263
},
261264

262265
"Il2Cpp.Object method lookup ignores static methods"() {
263-
const Class = Il2Cpp.domain.assembly("GameAssembly").image.class("Il2CppObjectTest");
266+
const T = Il2Cpp.domain.assembly("GameAssembly").image.class("Il2CppObjectTest");
267+
268+
assert(T.new().tryMethod("A")).not(undefined);
269+
assert(T.new().tryMethod("B")).is(undefined);
270+
assert(T.new().tryMethod("C", 1)).is(undefined);
271+
assert(T.new().tryMethod("C")).not(undefined);
272+
assert(T.new().tryMethod("C", 0)).not(undefined);
264273

265-
assert(Class.new().tryMethod("A")).not(undefined);
266-
assert(Class.new().tryMethod("B")).is(undefined);
267-
assert(Class.new().tryMethod("C", 1)).is(undefined);
268-
assert(Class.new().tryMethod("C")).not(undefined);
269-
assert(Class.new().tryMethod("C", 0)).not(undefined);
274+
assert(T.new().method("A").invoke(NULL)).is(1);
275+
assert(() => T.new().method("B")).throws("couldn't find non-static method B in hierarchy of class Il2CppObjectTest");
270276

271-
assert(Class.new().method("A").invoke(NULL)).is(1);
272-
assert(() => Class.new().method("B")).throws("couldn't find non-static method B in class Il2CppObjectTest");
277+
assert(T.nested("MemberLookupTest").new().tryMethod("C")?.isStatic).is(false);
278+
assert(T.nested("MemberLookupTest").new().tryMethod("D")?.isStatic).is(false);
273279
},
274280

275281
"Every enum base type matches its 'value__' field type"() {

0 commit comments

Comments
 (0)