Skip to content

Commit

Permalink
✨ Clinit
Browse files Browse the repository at this point in the history
  • Loading branch information
yhs0602 committed May 15, 2024
1 parent e609eed commit 49ba31f
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/main/kotlin/vm/classloader/DexClassLoader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class DexClassLoader(
)
loadedTypes[typeId] = loadedType
loadedMockedClasses.add(mockedClass.clazz)
loadedType.callClInit()
return loadedType
}

Expand Down
26 changes: 26 additions & 0 deletions src/main/kotlin/vm/classloader/DexDefinedType.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.yhs0602.vm.classloader

import com.yhs0602.dex.ParsedClass
import com.yhs0602.vm.Environment
import com.yhs0602.vm.MethodWrapper
import net.bytebuddy.ByteBuddy
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy
Expand All @@ -26,6 +27,8 @@ class DexDefinedType(
override val clazz by lazy {
makeClazz()
}
private var clinit: MethodTableEntry? = null
private var hasCalledClinit = false

init {
// populate v-table and i-table of the class
Expand Down Expand Up @@ -55,6 +58,9 @@ class DexDefinedType(
)
if (method.accessFlags.isStatic) {
staticMethods[methodTableEntry] = MethodWrapper.Encoded(method)
if (methodId.name == "<clinit>") {
clinit = methodTableEntry
}
} else if (method.accessFlags.isConstructor) {
constructors[methodTableEntry] = MethodWrapper.Encoded(method)
} else {
Expand All @@ -81,6 +87,26 @@ class DexDefinedType(
}
}

override fun callClInit() {
if (hasCalledClinit) {
return
}
clinit?.let {
val clInitMethod = getMethod(it)
if (clInitMethod !is MethodWrapper.Encoded) {
throw IllegalStateException("clinit method is not encoded")
}
hasCalledClinit = true
clInitMethod.execute(
arrayOf(),
Environment.getInstance(),
clInitMethod.encodedMethod.codeItem ?: throw IllegalStateException("clinit method has no code"),
true,
0
)
}
}

private fun makeClazz(): Class<*> {
// use bytebuddy to create a class
var builder = ByteBuddy()
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/vm/classloader/MockedType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class MockedType(
override val methods = mutableMapOf<MethodTableEntry, MethodWrapper>()
override val constructors = mutableMapOf<MethodTableEntry, MethodWrapper>()
override val staticMethods = mutableMapOf<MethodTableEntry, MethodWrapper>()
override fun callClInit() {} // no clinit for Mocked type, as it is already initialized

init {
// populate v-table and i-table of the class
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/vm/classloader/ObjectType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ data object ObjectType : Type() {
override val staticMethods = mutableMapOf<MethodTableEntry, MethodWrapper>()
override val constructors = mutableMapOf<MethodTableEntry, MethodWrapper>()
override val clazz: Class<*> = java.lang.Object::class.java
override fun callClInit() {} // no clinit for Object

init {
// populate v-table and i-table of Object
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/vm/classloader/PrimitiveType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class Primitive(override val descriptor: String) : Type() {
override val methods = emptyMap<MethodTableEntry, MethodWrapper>()
override val constructors = emptyMap<MethodTableEntry, MethodWrapper>()
override val staticMethods = emptyMap<MethodTableEntry, MethodWrapper>()
override fun callClInit() {} // no clinit for primitive types

override val clazz: Class<*> by lazy {
when (descriptor) {
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/vm/classloader/Type.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ sealed class Type {
abstract val staticMethods: Map<MethodTableEntry, MethodWrapper>
abstract val constructors: Map<MethodTableEntry, MethodWrapper>
abstract val clazz: Class<*>
abstract fun callClInit()

// Use this method to get the method entry in the virtual table
fun getVirtualMethodEntry(givenEntry: MethodTableEntry): MethodTableEntry {
Expand Down

0 comments on commit 49ba31f

Please sign in to comment.