diff --git a/.github/workflows/deploy_dev.yml b/.github/workflows/deploy_dev.yml
index f27c5de9..02bdb867 100644
--- a/.github/workflows/deploy_dev.yml
+++ b/.github/workflows/deploy_dev.yml
@@ -13,8 +13,6 @@ jobs:
strategy:
matrix:
include:
- - java_version: 11
- java_distribution: "temurin"
- java_version: 17
java_distribution: "temurin"
- java_version: 17
@@ -25,7 +23,7 @@ jobs:
name: Run Unit Tests on Java ${{ matrix.java_distribution }} ${{ matrix.java_version }}
steps:
- uses: actions/checkout@v3
- - name: set up JDK 11
+ - name: set up JDK
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java_version }}
@@ -34,47 +32,36 @@ jobs:
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Test with Gradle
- run: ./gradlew test
+ run: ./gradlew check
- name: Upload test artifacts
if: always()
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
- name: test-report
+ name: test-report-${{ matrix.java_distribution }}-${{ matrix.java_version }}
path: |
./**/build/reports
./**/build/test-results
- dorny_report:
- name: Prepare Dorny Test Report
- needs: tests
- runs-on: ubuntu-latest
- if: ${{ always() }}
- steps:
- - uses: actions/checkout@v3
- - name: Download Test Artifact
- uses: actions/download-artifact@v3
- with:
- name: test-report
-
- - name: Publish Dorny Test Report
- uses: dorny/test-reporter@v1
- with:
- name: Dorny JUnit Test Report
- path: '**/build/test-results/**/*.xml'
- reporter: java-junit
- list-tests: 'all'
-
mikepenz_report:
name: Prepare Mikepenz Test Report
needs: tests
runs-on: ubuntu-latest
if: ${{ always() }}
+ strategy:
+ matrix:
+ include:
+ - java_version: 17
+ java_distribution: "temurin"
+ - java_version: 17
+ java_distribution: "oracle"
+ - java_version: 17
+ java_distribution: "corretto"
steps:
- uses: actions/checkout@v3
- name: Download Test Artifact
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
- name: test-report
+ name: test-report-${{ matrix.java_distribution }}-${{ matrix.java_version }}
- name: Publish Test Report
uses: mikepenz/action-junit-report@v3
diff --git a/.github/workflows/deploy_pull_request.yml b/.github/workflows/deploy_pull_request.yml
index ab8cee27..86ad7d5b 100644
--- a/.github/workflows/deploy_pull_request.yml
+++ b/.github/workflows/deploy_pull_request.yml
@@ -9,8 +9,6 @@ jobs:
strategy:
matrix:
include:
- - java_version: 11
- java_distribution: "temurin"
- java_version: 17
java_distribution: "temurin"
- java_version: 17
@@ -21,7 +19,7 @@ jobs:
name: Run Unit Tests on Java ${{ matrix.java_distribution }} ${{ matrix.java_version }}
steps:
- uses: actions/checkout@v3
- - name: set up JDK 11
+ - name: set up JDK
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java_version }}
@@ -30,47 +28,36 @@ jobs:
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Test with Gradle
- run: ./gradlew test
+ run: ./gradlew check
- name: Upload test artifacts
if: always()
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
- name: test-report
+ name: test-report-${{ matrix.java_distribution }}-${{ matrix.java_version }}
path: |
./**/build/reports
./**/build/test-results
- dorny_report:
- name: Prepare Dorny Test Report
- needs: tests
- runs-on: ubuntu-latest
- if: ${{ always() }}
- steps:
- - uses: actions/checkout@v3
- - name: Download Test Artifact
- uses: actions/download-artifact@v3
- with:
- name: test-report
-
- - name: Publish Dorny Test Report
- uses: dorny/test-reporter@v1
- with:
- name: Dorny JUnit Test Report
- path: '**/build/test-results/**/*.xml'
- reporter: java-junit
- list-tests: 'all'
-
mikepenz_report:
name: Prepare Mikepenz Test Report
needs: tests
runs-on: ubuntu-latest
if: ${{ always() }}
+ strategy:
+ matrix:
+ include:
+ - java_version: 17
+ java_distribution: "temurin"
+ - java_version: 17
+ java_distribution: "oracle"
+ - java_version: 17
+ java_distribution: "corretto"
steps:
- uses: actions/checkout@v3
- name: Download Test Artifact
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
- name: test-report
+ name: test-report-${{ matrix.java_distribution }}-${{ matrix.java_version }}
- name: Publish Test Report
uses: mikepenz/action-junit-report@v3
diff --git a/.github/workflows/deploy_release.yml b/.github/workflows/deploy_release.yml
index 1971eb2b..91702d81 100644
--- a/.github/workflows/deploy_release.yml
+++ b/.github/workflows/deploy_release.yml
@@ -23,7 +23,7 @@ jobs:
cmd: "gh_release_diff -l -d --summary -w changes.md"
- name: Upload changes artifacts
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: changes
path: |
@@ -36,10 +36,10 @@ jobs:
if: ${{ always() }}
steps:
- uses: actions/checkout@v3
- - name: set up JDK 11
+ - name: set up JDK
uses: actions/setup-java@v3
with:
- java-version: '11'
+ java-version: '17'
distribution: 'temurin'
cache: gradle
- name: Grant execute permission for gradlew
@@ -49,7 +49,7 @@ jobs:
run: ./gradlew publishToMavenLocal
- name: Upload changes artifacts
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: artifacts
path: |
diff --git a/.gitignore b/.gitignore
index 835e8dca..306705a1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
.gradle
.idea
-build
\ No newline at end of file
+build
+.kotlin
+kotlin-js-store
\ No newline at end of file
diff --git a/android_lib/build.gradle b/android_lib/build.gradle
deleted file mode 100644
index 90b4418e..00000000
--- a/android_lib/build.gradle
+++ /dev/null
@@ -1,52 +0,0 @@
-plugins {
- id 'com.android.library'
-}
-
-apply from: '../jitpack.gradle'
-
-android {
- namespace 'com.github.klee0kai.stone'
- compileSdk 33
-
- defaultConfig {
- minSdk 21
- targetSdk 33
-
- consumerProguardFiles "consumer-rules.pro"
- }
-
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_11
- targetCompatibility JavaVersion.VERSION_11
- }
- publishing {
- singleVariant("release") {
- withSourcesJar()
- withJavadocJar()
- }
- }
-}
-
-publishing {
- publications {
- maven(MavenPublication) {
- afterEvaluate {
- from components.release
- }
- }
- }
-}
-
-
-dependencies {
- api project(':stone_lib')
-
- implementation 'androidx.appcompat:appcompat:1.6.1'
-}
\ No newline at end of file
diff --git a/android_lib/consumer-rules.pro b/android_lib/consumer-rules.pro
deleted file mode 100644
index e69de29b..00000000
diff --git a/android_lib/proguard-rules.pro b/android_lib/proguard-rules.pro
deleted file mode 100644
index 481bb434..00000000
--- a/android_lib/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/android_lib/src/main/AndroidManifest.xml b/android_lib/src/main/AndroidManifest.xml
deleted file mode 100644
index 325bfcc5..00000000
--- a/android_lib/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/android_lib/src/main/java/com/github/klee0kai/stone/AndroidStone.java b/android_lib/src/main/java/com/github/klee0kai/stone/AndroidStone.java
deleted file mode 100644
index a7793a55..00000000
--- a/android_lib/src/main/java/com/github/klee0kai/stone/AndroidStone.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.github.klee0kai.stone;
-
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.DefaultLifecycleObserver;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import com.github.klee0kai.stone.lifecycle.StoneLifeCycleOwner;
-import org.jetbrains.annotations.NotNull;
-
-public class AndroidStone {
-
- private static final long PROTECT_TIME_MILLIS = 500;
-
- public static StoneLifeCycleOwner lifeCycleOwner(LifecycleOwner owner, long protectTimeMillis) {
- return lifeCycleOwner(owner.getLifecycle(), protectTimeMillis);
- }
-
- public static StoneLifeCycleOwner lifeCycleOwner(Lifecycle lifecycle, long protectTimeMillis) {
- return listener -> lifecycle.addObserver(new DefaultLifecycleObserver() {
- @Override
- public void onPause(@NonNull @NotNull LifecycleOwner owner1) {
- DefaultLifecycleObserver.super.onPause(owner1);
- listener.protectForInjected(protectTimeMillis);
- }
- });
- }
-
- public static StoneLifeCycleOwner lifeCycleOwner(LifecycleOwner owner) {
- return lifeCycleOwner(owner.getLifecycle(), PROTECT_TIME_MILLIS);
- }
-
- public static StoneLifeCycleOwner lifeCycleOwner(Lifecycle lifecycle) {
- return lifeCycleOwner(lifecycle, PROTECT_TIME_MILLIS);
- }
-
-
-}
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index 77949b39..00000000
--- a/build.gradle
+++ /dev/null
@@ -1,18 +0,0 @@
-buildscript {
- repositories {
- gradlePluginPortal()
- mavenCentral()
- google()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:7.2.2'
- }
-}
-
-allprojects {
- group 'com.github.klee0kai.stone'
- version '1.0.7'
-}
-
-
-
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 00000000..bd73b8bf
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,6 @@
+plugins {
+ // We declare plugins without application. We avoid possible version conflicts.
+ alias(libs.plugins.kotlin.multiplatform) apply false
+ alias(libs.plugins.kotlin.jvm) apply false
+ alias(libs.plugins.android.library) apply false
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 01b80d70..bfe48ae2 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -17,3 +17,5 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
+
+kotlin.mpp.enableCInteropCommonization=false
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 41dfb879..d706aba6 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/inject_multiplatform/build.gradle.kts b/inject_multiplatform/build.gradle.kts
new file mode 100644
index 00000000..3bc13cd6
--- /dev/null
+++ b/inject_multiplatform/build.gradle.kts
@@ -0,0 +1,78 @@
+plugins {
+ alias(libs.plugins.kotlin.multiplatform)
+ alias(libs.plugins.publish.stone)
+ alias(libs.plugins.publish.maven)
+}
+
+group = "com.github.klee0kai.stone"
+version = libs.versions.stone.get()
+
+kotlin {
+ jvm()
+ js(IR) {
+ browser()
+ nodejs()
+ }
+
+ linuxX64()
+ mingwX64()
+ wasmJs {
+ browser()
+ nodejs()
+ }
+
+ sourceSets {
+ commonMain.dependencies {
+ api(libs.kotlinx.coroutines)
+ }
+ jvmMain.dependencies {
+ api(libs.java.inject)
+ }
+
+ }
+}
+
+val isMac = System.getProperty("os.name").contains("Mac")
+if (isMac) kotlin {
+ macosX64()
+ macosArm64()
+ iosX64()
+ iosArm64()
+ iosSimulatorArm64()
+
+ sourceSets {
+ val commonMain by getting
+ val commonTest by getting
+ val iosX64Main by getting
+ val iosArm64Main by getting
+ val iosSimulatorArm64Main by getting
+
+ val iosMain by creating {
+ dependsOn(commonMain)
+ iosX64Main.dependsOn(this)
+ iosArm64Main.dependsOn(this)
+ iosSimulatorArm64Main.dependsOn(this)
+ }
+
+
+ val iosX64Test by getting
+ val iosArm64Test by getting
+ val iosSimulatorArm64Test by getting
+ val iosTest by creating {
+ dependsOn(commonTest)
+ iosX64Test.dependsOn(this)
+ iosArm64Test.dependsOn(this)
+ iosSimulatorArm64Test.dependsOn(this)
+ }
+ }
+}
+
+publishing {
+ publications.withType().configureEach {
+ pom {
+ name.set("Stone")
+ description.set("Library DI designed on weak references.")
+ }
+ }
+}
+
diff --git a/inject_multiplatform/src/commonMain/kotlin/com/github/klee0kai/stone/Inject.kt b/inject_multiplatform/src/commonMain/kotlin/com/github/klee0kai/stone/Inject.kt
new file mode 100644
index 00000000..459a95ab
--- /dev/null
+++ b/inject_multiplatform/src/commonMain/kotlin/com/github/klee0kai/stone/Inject.kt
@@ -0,0 +1,11 @@
+package com.github.klee0kai.stone
+
+/**
+ * Identifies injectable constructors, methods, and fields. May apply to static
+ * as well as instance members. An injectable member may have any access
+ * modifier (private, package-private, protected, public). Constructors are
+ * injected first, followed by fields, and then methods. Fields and methods
+ * in superclasses are injected before those in subclasses. Ordering of
+ * injection among fields and among methods in the same class is not specified.
+ */
+annotation class Inject
\ No newline at end of file
diff --git a/inject_multiplatform/src/commonMain/kotlin/com/github/klee0kai/stone/Named.kt b/inject_multiplatform/src/commonMain/kotlin/com/github/klee0kai/stone/Named.kt
new file mode 100644
index 00000000..730404c4
--- /dev/null
+++ b/inject_multiplatform/src/commonMain/kotlin/com/github/klee0kai/stone/Named.kt
@@ -0,0 +1,23 @@
+package com.github.klee0kai.stone
+
+
+/**
+ * String-based [Qualifier].
+ *
+ * Example usage:
+ *
+ * ```
+ * class Car {
+ * @Inject
+ * @Named("driver")
+ * var driverSeat : DriverSeat
+ *
+ * @Inject
+ * @Named("passenger")
+ * var passengerSeat: Seat
+ * }
+ * ```
+ */
+annotation class Named(
+ val value: String
+)
\ No newline at end of file
diff --git a/inject_multiplatform/src/commonMain/kotlin/com/github/klee0kai/stone/Provider.kt b/inject_multiplatform/src/commonMain/kotlin/com/github/klee0kai/stone/Provider.kt
new file mode 100644
index 00000000..71eb948d
--- /dev/null
+++ b/inject_multiplatform/src/commonMain/kotlin/com/github/klee0kai/stone/Provider.kt
@@ -0,0 +1,10 @@
+package com.github.klee0kai.stone
+
+/**
+ * Provides instances of [T]. Typically implemented by an injector. For
+ * any type [T] that can be injected, you can also inject
+ * `Provider`.
+ */
+fun interface Provider {
+ fun get(): T
+}
\ No newline at end of file
diff --git a/inject_multiplatform/src/commonMain/kotlin/com/github/klee0kai/stone/Qualifier.kt b/inject_multiplatform/src/commonMain/kotlin/com/github/klee0kai/stone/Qualifier.kt
new file mode 100644
index 00000000..2667f044
--- /dev/null
+++ b/inject_multiplatform/src/commonMain/kotlin/com/github/klee0kai/stone/Qualifier.kt
@@ -0,0 +1,32 @@
+package com.github.klee0kai.stone
+
+/**
+ * Identifies qualifier annotations. Anyone can define a new qualifier. A
+ * qualifier annotation:
+ *
+ * - is annotated with `@Qualifier`, `@Retention(RUNTIME)`,
+ * and typically `@Documented`.
+ * - can have attributes.
+ * - may be part of the public API, much like the dependency type, but
+ * unlike implementation types which needn't be part of the public
+ * API.
+ * - may have restricted usage if annotated with {@code @Target}. While
+ * this specification covers applying qualifiers to fields and
+ * parameters only, some injector configurations might use qualifier
+ * annotations in other places (on methods or classes for example).
+ *
+ *
+ * For example:
+ *
+ * ```
+ * @Documented
+ * @Retention(RUNTIME)
+ * @Qualifier
+ * annotation class Leather(
+ * val color: Color
+ * )
+ * ```
+ *
+ * @see [Named]
+ */
+annotation class Qualifier
\ No newline at end of file
diff --git a/inject_multiplatform/src/commonMain/kotlin/com/github/klee0kai/stone/Scope.kt b/inject_multiplatform/src/commonMain/kotlin/com/github/klee0kai/stone/Scope.kt
new file mode 100644
index 00000000..097c44c6
--- /dev/null
+++ b/inject_multiplatform/src/commonMain/kotlin/com/github/klee0kai/stone/Scope.kt
@@ -0,0 +1,14 @@
+package com.github.klee0kai.stone
+
+/**
+ * Identifies scope annotations. A scope annotation applies to a class
+ * containing an injectable constructor and governs how the injector reuses
+ * instances of the type. By default, if no scope annotation is present, the
+ * injector creates an instance (by injecting the type's constructor), uses
+ * the instance for one injection, and then forgets it. If a scope annotation
+ * is present, the injector may retain the instance for possible reuse in a
+ * later injection. If multiple threads can access a scoped instance, its
+ * implementation should be thread safe. The implementation of the scope
+ * itself is left up to the injector.
+ */
+annotation class Scope
\ No newline at end of file
diff --git a/jitpack.gradle b/jitpack.gradle
deleted file mode 100644
index 7dbacd9f..00000000
--- a/jitpack.gradle
+++ /dev/null
@@ -1,38 +0,0 @@
-apply plugin: 'maven-publish'
-
-publishing {
- publications {
- maven(MavenPublication) {
- groupId project.group
- artifactId project.name
- version project.version
-
- pom {
- name = 'Stone'
- description = 'Library DI designed on weak references.'
- url = 'https://github.com/klee0kai/stone'
-
- licenses {
- license {
- name = 'GNU GENERAL PUBLIC LICENSE, Version 3'
- url = 'https://github.com/klee0kai/stone/blob/dev/LICENCE.md'
- }
- }
-
- developers {
- developer {
- id = 'klee0kai'
- name = 'Andrey Kuzubov'
- email = 'klee0kai@gmail.com'
- }
- }
-
- }
-
- }
- }
-}
-java {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
-}
\ No newline at end of file
diff --git a/jitpack.yml b/jitpack.yml
new file mode 100644
index 00000000..d14c8f94
--- /dev/null
+++ b/jitpack.yml
@@ -0,0 +1,5 @@
+jdk:
+ - openjdk17
+install:
+ - echo "publish"
+ - ./gradlew publishToMavenLocal
diff --git a/kotlin_lib/build.gradle b/kotlin_lib/build.gradle
deleted file mode 100644
index 917356d0..00000000
--- a/kotlin_lib/build.gradle
+++ /dev/null
@@ -1,34 +0,0 @@
-plugins {
- id "org.jetbrains.kotlin.jvm" version '1.7.0'
- id "org.jetbrains.kotlin.kapt" version "1.7.21"
-}
-
-apply from: '../jitpack.gradle'
-
-java {
- withSourcesJar()
- withJavadocJar()
-}
-
-publishing {
- publications {
- maven(MavenPublication) {
- from components.java
- }
- }
-}
-
-compileKotlin {
- kotlinOptions.jvmTarget = JavaVersion.VERSION_11
-}
-
-compileTestKotlin {
- kotlinOptions.jvmTarget = JavaVersion.VERSION_11
-}
-
-
-dependencies {
- api(project(":stone_lib"))
-}
-
-
diff --git a/kotlin_lib/src/main/kotlin/com/github/klee0kai/stone/KotlinWrappersStone.kt b/kotlin_lib/src/main/kotlin/com/github/klee0kai/stone/KotlinWrappersStone.kt
deleted file mode 100644
index 768f7de7..00000000
--- a/kotlin_lib/src/main/kotlin/com/github/klee0kai/stone/KotlinWrappersStone.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.github.klee0kai.stone
-
-import com.github.klee0kai.stone.annotations.wrappers.WrappersCreator
-import com.github.klee0kai.stone.wrappers.creators.ProviderWrapper
-import javax.inject.Provider
-
-@WrappersCreator(
- wrappers = [
- Lazy::class
- ]
-)
-open class KotlinWrappersStone : ProviderWrapper {
-
- override fun wrap(wrapperCl: Class, originalProvider: Provider): Wr? {
- return when {
- wrapperCl == Lazy::class.java -> lazy { originalProvider.get() } as Wr
- else -> null
- }
- }
-
-}
\ No newline at end of file
diff --git a/kotlin_lib/src/main/kotlin/com/github/klee0kai/stone/type/wrappers/WrappersExt.kt b/kotlin_lib/src/main/kotlin/com/github/klee0kai/stone/type/wrappers/WrappersExt.kt
deleted file mode 100644
index cb7ab055..00000000
--- a/kotlin_lib/src/main/kotlin/com/github/klee0kai/stone/type/wrappers/WrappersExt.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.github.klee0kai.stone.type.wrappers
-
-import com.github.klee0kai.stone.wrappers.Ref
-import java.lang.ref.SoftReference
-import java.lang.ref.WeakReference
-import kotlin.reflect.KProperty
-
-operator fun Ref.getValue(t: Any?, property: KProperty<*>): T = get()
-
-operator fun SoftReference.getValue(t: Any?, property: KProperty<*>): T? = get()
-
-operator fun WeakReference.getValue(t: Any?, property: KProperty<*>): T? = get()
-
diff --git a/libs.versions.toml b/libs.versions.toml
new file mode 100644
index 00000000..155ce337
--- /dev/null
+++ b/libs.versions.toml
@@ -0,0 +1,61 @@
+[versions]
+
+stone = "2.0.2_alpha"
+
+kotlin = "2.2.21"
+ksp = "2.3.2"
+agp = "8.12.3"
+
+javax-inject = "1"
+kotlinx-coroutines = "1.10.1"
+jetbrain-immutable = "0.3.7"
+jetbrain-coroutines = "1.10.1"
+kotlinx-atomicfu = "0.29.0"
+
+kotlinpoet = "2.0.0"
+
+junit = "6.0.0"
+junit-launcher = "6.0.0"
+testing-compile = "0.23.0"
+
+
+[libraries]
+
+
+jetbrain-immutable = { group = "org.jetbrains.kotlinx", name = "kotlinx-collections-immutable", version.ref = "jetbrain-immutable" }
+jetbrain-coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "jetbrain-coroutines" }
+
+# https://mvnrepository.com/artifact/javax.inject/javax.inject
+java-inject = { group = "javax.inject", name = "javax.inject", version.ref = "javax-inject" }
+
+kotlinx-coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
+kotlinx-atomicfu = { group = "org.jetbrains.kotlinx", name = "atomicfu", version.ref = "kotlinx-atomicfu" }
+
+ksp = { group = "com.google.devtools.ksp", name = "symbol-processing-api", version.ref = "ksp" }
+
+kotlinpoet = { group = "com.squareup", name = "kotlinpoet", version.ref = "kotlinpoet" }
+kotlinpoet-ksp = { group = "com.squareup", name = "kotlinpoet-ksp", version.ref = "kotlinpoet" }
+
+kotlin-compiler = { group = "org.jetbrains.kotlin", name = "kotlin-compiler-embeddable", version.ref = "kotlin" }
+
+jupiter-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit" }
+jupiter-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit" }
+jupiter-launcher = { group = "org.junit.platform", name = "junit-platform-launcher", version.ref = "junit-launcher" }
+
+testing-kapt-compile = { group = "com.google.testing.compile", name = "compile-testing", version.ref = "testing-compile" }
+
+[bundles]
+
+junit = ["jupiter-api", "jupiter-engine", "jupiter-launcher"]
+kotlinpoet = ["kotlinpoet", "kotlinpoet-ksp"]
+kotlin = ["jetbrain-coroutines", "jetbrain-immutable"]
+
+[plugins]
+
+kotlin-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
+kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
+kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
+android-library = { id = "com.android.library", version.ref = "agp" }
+
+publish-maven = { id = "org.gradle.maven-publish" }
+publish-stone = { id = "com.github.klee0kai.stone.publish" }
diff --git a/android_lib/.gitignore b/plugin_publish/.gitignore
similarity index 100%
rename from android_lib/.gitignore
rename to plugin_publish/.gitignore
diff --git a/plugin_publish/build.gradle.kts b/plugin_publish/build.gradle.kts
new file mode 100644
index 00000000..db3880da
--- /dev/null
+++ b/plugin_publish/build.gradle.kts
@@ -0,0 +1,11 @@
+plugins {
+ `kotlin-dsl`
+ `java-gradle-plugin`
+}
+
+gradlePlugin {
+ plugins.register("stone-publish") {
+ id = "com.github.klee0kai.stone.publish"
+ implementationClass = "com.github.klee0kai.stone.publish.StonePublishPlugin"
+ }
+}
diff --git a/plugin_publish/settings.gradle.kts b/plugin_publish/settings.gradle.kts
new file mode 100644
index 00000000..7b5865f8
--- /dev/null
+++ b/plugin_publish/settings.gradle.kts
@@ -0,0 +1,20 @@
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ }
+}
+
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+ versionCatalogs {
+ create("libs") {
+ from(files("../libs.versions.toml"))
+ }
+ }
+}
diff --git a/plugin_publish/src/main/kotlin/com/github/klee0kai/stone/publish/MavenPublishExt.kt b/plugin_publish/src/main/kotlin/com/github/klee0kai/stone/publish/MavenPublishExt.kt
new file mode 100644
index 00000000..6834955b
--- /dev/null
+++ b/plugin_publish/src/main/kotlin/com/github/klee0kai/stone/publish/MavenPublishExt.kt
@@ -0,0 +1,30 @@
+package com.github.klee0kai.stone.publish
+
+import org.gradle.api.Project
+import org.gradle.api.publish.PublishingExtension
+import org.gradle.api.publish.maven.MavenPublication
+import org.gradle.kotlin.dsl.withType
+
+
+fun PublishingExtension.stoneToMaven(project: Project) {
+ publications.withType().configureEach {
+ version = project.version.toString()
+
+ pom {
+ url.set("https://github.com/klee0kai/stone")
+ licenses {
+ license {
+ name.set("GNU General Public License, Version 3")
+ url.set("https://github.com/klee0kai/stone/blob/dev/LICENCE.md")
+ }
+ }
+ developers {
+ developer {
+ id.set("klee0kai")
+ name.set("Andrei Kuzubov")
+ email.set("klee0kai@gmail.com")
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugin_publish/src/main/kotlin/com/github/klee0kai/stone/publish/StonePublishPlugin.kt b/plugin_publish/src/main/kotlin/com/github/klee0kai/stone/publish/StonePublishPlugin.kt
new file mode 100644
index 00000000..15db3637
--- /dev/null
+++ b/plugin_publish/src/main/kotlin/com/github/klee0kai/stone/publish/StonePublishPlugin.kt
@@ -0,0 +1,22 @@
+package com.github.klee0kai.stone.publish
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.publish.PublishingExtension
+import org.gradle.api.publish.plugins.PublishingPlugin
+
+/**
+ * apply MavenPublishPlugin with configs
+ */
+class StonePublishPlugin : Plugin {
+
+ override fun apply(project: Project) {
+ project.pluginManager.apply(PublishingPlugin::class.java)
+
+ project.afterEvaluate {
+ project.extensions.configure(PublishingExtension::class.java) {
+ stoneToMaven(project)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/readme.md b/readme.md
index f1c83d5e..00df9c54 100644
--- a/readme.md
+++ b/readme.md
@@ -37,8 +37,8 @@ In the project module, import dependencies
```kotlin
dependencies {
// stone
- implementation("com.github.klee0kai.stone:stone_lib:1.0.7")
- kapt("com.github.klee0kai.stone:stone_processor:1.0.7")
+ implementation("com.github.klee0kai.stone:stone_multiplatform:2.0.0_alpha")
+ ksp("com.github.klee0kai.stone:stone_ksp:2.0.0_alpha")
}
```
@@ -64,7 +64,7 @@ interface PlanetsComponent {
And further use.
```kotlin
-val DI: PlanetsComponent = Stone.createComponent(PlanetsComponent::class.java)
+val DI: PlanetsComponent = PlanetsComponentStoneComponent()
fun main(args: Array) {
val earth = DI.planets().earth()
}
@@ -115,14 +115,9 @@ or to implement initialization in coroutine scopes.
Documentation with examples
- [Kotlin](https://github.com/klee0kai/stone/wiki/kotlin_start)
- - [Java](https://github.com/klee0kai/stone/wiki/java_start)
-
-## Find this library useful?
-Support it by joining __[stargazers](https://github.com/klee0kai/stone/stargazers)__ for this repository. :star:
-Also, __[follow me](https://github.com/klee0kai)__ on GitHub for more libraries!
## License
```
-Copyright (c) 2024 Andrey Kuzubov
+Copyright (c) 2022 Andrey Kuzubov
```
diff --git a/settings.gradle b/settings.gradle
deleted file mode 100644
index f0f0000d..00000000
--- a/settings.gradle
+++ /dev/null
@@ -1,39 +0,0 @@
-pluginManagement {
- repositories {
- gradlePluginPortal()
- google()
- mavenCentral()
- }
-}
-dependencyResolutionManagement {
- repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
- repositories {
- mavenLocal()
- google()
- mavenCentral()
- }
-}
-
-rootProject.name = 'Stone'
-include 'stone_lib'
-include 'stone_processor'
-include 'android_lib'
-include 'kotlin_lib'
-
-include 'wiki_java'
-include 'wiki_kotlin'
-include 'tests'
-include 'tests_ext'
-include 'tests_kotlin'
-include 'tests_wraps'
-include 'tests_wraps_kotlin'
-include 'tests_compile'
-include ':test_feature:companies:consulting'
-include ':test_feature:hr:api'
-include ':test_feature:hr:impl'
-include ':test_feature:planning:api'
-include ':test_feature:planning:impl'
-include ':test_feature:finance:api'
-include ':test_feature:finance:impl'
-include ':test_feature_core_deps'
-
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 00000000..97fc81db
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,43 @@
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ }
+}
+
+dependencyResolutionManagement {
+ repositories {
+ google()
+ mavenCentral()
+ }
+ versionCatalogs {
+ create("libs") {
+ from(files("libs.versions.toml"))
+ }
+ }
+}
+
+rootProject.name = "Stone"
+includeBuild("plugin_publish")
+include("weakref_multiplatform")
+include("inject_multiplatform")
+include(":stone_multiplatform")
+
+include(":wiki_kotlin")
+include(":tests")
+include(":tests_ext")
+include(":tests_kotlin")
+include(":tests_wraps_kotlin")
+// waiting to k2 support https://github.com/tschuchortdev/kotlin-compile-testing/issues/411
+//include(":tests_compile")
+include(":test_feature:companies:consulting")
+include(":test_feature:hr:api")
+include(":test_feature:hr:impl")
+include(":test_feature:planning:api")
+include(":test_feature:planning:impl")
+include(":test_feature:finance:api")
+include(":test_feature:finance:impl")
+include(":test_feature_core_deps")
+
+include("stone_ksp")
\ No newline at end of file
diff --git a/stone_ksp/build.gradle.kts b/stone_ksp/build.gradle.kts
new file mode 100644
index 00000000..446e4085
--- /dev/null
+++ b/stone_ksp/build.gradle.kts
@@ -0,0 +1,35 @@
+plugins {
+ alias(libs.plugins.kotlin.multiplatform)
+ alias(libs.plugins.publish.stone)
+ alias(libs.plugins.publish.maven)
+}
+
+group = "com.github.klee0kai.stone"
+version = libs.versions.stone.get()
+
+
+kotlin {
+ jvm()
+
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ implementation(project(":stone_multiplatform"))
+ implementation(libs.bundles.kotlin)
+ implementation(libs.bundles.kotlinpoet)
+ implementation(libs.ksp)
+ implementation(libs.kotlin.compiler)
+ }
+ }
+ }
+}
+
+publishing {
+ publications.withType().configureEach {
+ pom {
+ name.set("Stone")
+ description.set("Library DI designed on weak references.")
+ }
+ }
+}
+
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/Processor.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/Processor.kt
new file mode 100644
index 00000000..437f47dd
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/Processor.kt
@@ -0,0 +1,190 @@
+package com.github.klee0kai.thekey.stone.ksp
+
+import com.github.klee0kai.thekey.stone.ksp.coroutines.LaunchConductor
+import com.github.klee0kai.thekey.stone.ksp.exceptions.StoneException
+import com.github.klee0kai.thekey.stone.ksp.exceptions.wrapKsNoteInfo
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.GenSpec
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.filter
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.forceProcess
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.nowTakeOnly
+import com.github.klee0kai.thekey.stone.ksp.target.component.GenComponentProcessor
+import com.github.klee0kai.thekey.stone.ksp.target.hiddenmodule.GenHiddenModuleCacheControlProcessor
+import com.github.klee0kai.thekey.stone.ksp.target.hiddenmodule.GenHiddenModuleProcessor
+import com.github.klee0kai.thekey.stone.ksp.target.module.GenModuleCacheControlProcessor
+import com.github.klee0kai.thekey.stone.ksp.target.module.GenModuleFactoryProcessor
+import com.github.klee0kai.thekey.stone.ksp.target.module.GenModuleProcessor
+import com.github.klee0kai.thekey.stone.ksp.target.provider.GenProviderProcessor
+import com.github.klee0kai.thekey.stone.ksp.target.wrapper.GenWrappersSupportProcessor
+import com.google.devtools.ksp.containingFile
+import com.google.devtools.ksp.processing.CodeGenerator
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.squareup.kotlinpoet.ksp.writeTo
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import java.util.concurrent.ConcurrentLinkedQueue
+import java.util.concurrent.atomic.AtomicInteger
+import kotlin.math.max
+import kotlin.math.min
+
+
+class Processor(
+ private val options: Map,
+ private val logger: KSPLogger,
+ private val codeGenerator: CodeGenerator,
+) : SymbolProcessor {
+
+ companion object {
+ const val PROJECT_URL = "https://github.com/klee0kai/stone"
+
+ var oneRunSymbolsCount = 4
+ private set
+
+ var multithread = false
+ private set
+
+ var debug = false
+ private set
+
+ var debugPkgFilter: String? = null
+ private set
+ }
+
+ init {
+ oneRunSymbolsCount = options["oneRunSymbolsCount"]?.toInt()
+ ?: max(Runtime.getRuntime().availableProcessors(), 4)
+
+ multithread = options["multithread"]?.toBoolean() ?: false
+ debug = options["debug"]?.toBoolean() ?: false
+ debugPkgFilter = options["debugPkgFilter"]
+
+ // force changes
+// debug = true
+// debugPkgFilter = "com.github.klee0kai.test.car.di.cachecontrol.gc"
+ }
+
+
+ val dispatcher by lazy { if (multithread) Dispatchers.Default else Dispatchers.Unconfined }
+ val targetProcessors = arrayOf(
+ GenProviderProcessor(),
+ GenModuleFactoryProcessor(),
+ GenModuleCacheControlProcessor(),
+ GenModuleProcessor(),
+ GenHiddenModuleProcessor(),
+ GenHiddenModuleCacheControlProcessor(),
+ GenWrappersSupportProcessor(),
+ GenComponentProcessor(),
+ )
+
+ override fun process(
+ resolver: Resolver
+ ): List = runBlocking(dispatcher) {
+
+ val processSymbolsCounter = AtomicInteger(0)
+ val findSymbolsMutex = Mutex()
+ val globalSymbolsForProcessing = ConcurrentLinkedQueue()
+ val globalSymbolsForReprocessing = ConcurrentLinkedQueue()
+ val genSpecs = ConcurrentLinkedQueue()
+
+ val launchConductor = LaunchConductor()
+
+ val generateCodeJob = launch {
+ targetProcessors.forEach { processor ->
+ launch {
+ var symbols = launchConductor.finishTogether {
+ var symbols = findSymbolsMutex.withLock { processor.findSymbolsToProcess(resolver) }
+
+ if (debug && debugPkgFilter != null) {
+ symbols = symbols
+ .filter {
+ it.containingFile?.packageName
+ ?.asString()?.startsWith(debugPkgFilter!!) ?: true
+ }
+ }
+
+ var takeSymbolsCount = 0
+ processSymbolsCounter.updateAndGet { totalCount ->
+ takeSymbolsCount = min(
+ symbols.symbolsForProcessing.size,
+ oneRunSymbolsCount - totalCount
+ )
+
+ takeSymbolsCount = max(takeSymbolsCount, 0)
+ totalCount + takeSymbolsCount
+ }
+
+
+ // skip to next run
+ symbols = symbols.nowTakeOnly(takeSymbolsCount)
+
+ globalSymbolsForProcessing.addAll(symbols.symbolsForProcessing)
+
+ symbols
+ }
+
+ symbols = symbols.forceProcess { it in globalSymbolsForProcessing }
+
+
+ globalSymbolsForReprocessing.addAll(symbols.symbolsForReprocessing)
+
+ try {
+ genSpecs.addAll(
+ symbols.symbolsForProcessing
+ .mapNotNull { targetSymbol ->
+ wrapKsNoteInfo(targetSymbol) {
+ processor.process(
+ validSymbol = targetSymbol,
+ resolver = resolver,
+ options = options,
+ logger = logger,
+ )
+ }
+ }
+ )
+ } catch (e: StoneException) {
+ logger.error(
+ message = e.message ?: "",
+ symbol = e.findLastErrorElement(),
+ )
+ throw e
+ }
+
+ }
+ }
+ }
+
+ // join generate code
+ // we provide separate file recording
+ // with symbol resolution so that the processor can link the input and output of generation
+ generateCodeJob.join()
+
+ try {
+ genSpecs.forEach { genSpec ->
+ wrapKsNoteInfo(genSpec.dependencies.originatingFiles.firstOrNull()) {
+ genSpec?.fileSpec?.writeTo(
+ codeGenerator = codeGenerator,
+ dependencies = genSpec.dependencies
+ )
+ }
+ }
+ } catch (e: StoneException) {
+ logger.error(e.toString(), e.findLastErrorElement())
+ }
+ globalSymbolsForReprocessing.toList()
+ }
+
+ override fun finish() {
+ super.finish()
+ }
+
+ override fun onError() {
+ super.onError()
+ }
+
+
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/StoneProcessorProvider.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/StoneProcessorProvider.kt
new file mode 100644
index 00000000..8271c8bf
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/StoneProcessorProvider.kt
@@ -0,0 +1,17 @@
+package com.github.klee0kai.thekey.stone.ksp
+
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
+import com.google.devtools.ksp.processing.SymbolProcessorProvider
+
+class StoneProcessorProvider : SymbolProcessorProvider {
+
+ override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
+ return Processor(
+ options = environment.options,
+ logger = environment.logger,
+ codeGenerator = environment.codeGenerator,
+ )
+ }
+
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/coroutines/LaunchConductor.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/coroutines/LaunchConductor.kt
new file mode 100644
index 00000000..217a6e27
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/coroutines/LaunchConductor.kt
@@ -0,0 +1,23 @@
+package com.github.klee0kai.thekey.stone.ksp.coroutines
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.update
+
+class LaunchConductor {
+
+ private val parallelWorkCounter = MutableStateFlow(0)
+
+ suspend fun finishTogether(
+ suspendFun: suspend () -> T,
+ ): T {
+ parallelWorkCounter.update { it + 1 }
+ try {
+ return suspendFun()
+ } finally {
+ parallelWorkCounter.update { it - 1 }
+ parallelWorkCounter.first { it <= 0 }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/ClassNotFoundStoneException.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/ClassNotFoundStoneException.kt
new file mode 100644
index 00000000..61dd1371
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/ClassNotFoundStoneException.kt
@@ -0,0 +1,13 @@
+package com.github.klee0kai.thekey.stone.ksp.exceptions
+
+import com.google.devtools.ksp.symbol.KSNode
+
+class ClassNotFoundStoneException(
+ message: String? = null,
+ cause: Throwable? = null,
+ element: KSNode? = null,
+) : StoneException(
+ message = message,
+ cause = cause,
+ element = element,
+)
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/ImplementMethodStoneException.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/ImplementMethodStoneException.kt
new file mode 100644
index 00000000..b93ba1ac
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/ImplementMethodStoneException.kt
@@ -0,0 +1,13 @@
+package com.github.klee0kai.thekey.stone.ksp.exceptions
+
+import com.google.devtools.ksp.symbol.KSNode
+
+class ImplementMethodStoneException(
+ message: String? = null,
+ cause: Throwable? = null,
+ element: KSNode? = null,
+) : StoneException(
+ message = message,
+ cause = cause,
+ element = element,
+)
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/IncorrectSignatureException.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/IncorrectSignatureException.kt
new file mode 100644
index 00000000..bf62e497
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/IncorrectSignatureException.kt
@@ -0,0 +1,13 @@
+package com.github.klee0kai.thekey.stone.ksp.exceptions
+
+import com.google.devtools.ksp.symbol.KSNode
+
+class IncorrectSignatureException(
+ message: String? = null,
+ cause: Throwable? = null,
+ element: KSNode? = null,
+) : StoneException(
+ message = message,
+ cause = cause,
+ element = element,
+)
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/ObjectNotProvidedException.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/ObjectNotProvidedException.kt
new file mode 100644
index 00000000..dd145aac
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/ObjectNotProvidedException.kt
@@ -0,0 +1,13 @@
+package com.github.klee0kai.thekey.stone.ksp.exceptions
+
+import com.google.devtools.ksp.symbol.KSNode
+
+class ObjectNotProvidedException(
+ message: String? = null,
+ cause: Throwable? = null,
+ element: KSNode? = null,
+) : StoneException(
+ message = message,
+ cause = cause,
+ element = element,
+)
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/PrimitiveTypeNonSupportedStoneException.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/PrimitiveTypeNonSupportedStoneException.kt
new file mode 100644
index 00000000..4ca8f6db
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/PrimitiveTypeNonSupportedStoneException.kt
@@ -0,0 +1,13 @@
+package com.github.klee0kai.thekey.stone.ksp.exceptions
+
+import com.google.devtools.ksp.symbol.KSNode
+
+class PrimitiveTypeNonSupportedStoneException(
+ message: String? = null,
+ cause: Throwable? = null,
+ element: KSNode? = null,
+) : StoneException(
+ message = message,
+ cause = cause,
+ element = element,
+)
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/RecursiveProviding.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/RecursiveProviding.kt
new file mode 100644
index 00000000..47705b92
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/RecursiveProviding.kt
@@ -0,0 +1,13 @@
+package com.github.klee0kai.thekey.stone.ksp.exceptions
+
+import com.google.devtools.ksp.symbol.KSNode
+
+class RecursiveProviding(
+ message: String? = null,
+ cause: Throwable? = null,
+ element: KSNode? = null,
+) : StoneException(
+ message = message,
+ cause = cause,
+ element = element,
+)
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/StoneException.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/StoneException.kt
new file mode 100644
index 00000000..968460ea
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/StoneException.kt
@@ -0,0 +1,24 @@
+package com.github.klee0kai.thekey.stone.ksp.exceptions
+
+import com.google.devtools.ksp.symbol.KSNode
+import com.google.devtools.ksp.symbol.NonExistLocation
+
+
+open class StoneException(
+ message: String? = null,
+ cause: Throwable? = null,
+ val element: KSNode? = null,
+) : IllegalStateException(message, cause) {
+
+ fun findLastErrorElement(): KSNode? {
+ var sourceElement: KSNode? = null
+ if (cause is StoneException) {
+ sourceElement = (cause as StoneException).findLastErrorElement()
+ }
+ if (sourceElement == null || sourceElement.location is NonExistLocation) {
+ sourceElement = element
+ }
+ return sourceElement
+ }
+
+}
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/WrapExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/WrapExt.kt
new file mode 100644
index 00000000..9c69bc76
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/exceptions/WrapExt.kt
@@ -0,0 +1,40 @@
+package com.github.klee0kai.thekey.stone.ksp.exceptions
+
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.google.devtools.ksp.symbol.KSNode
+import com.google.devtools.ksp.symbol.KSType
+
+inline fun T.wrapKsNoteInfo(
+ ksNode: KSNode?,
+ block: T.() -> R,
+): R {
+ return try {
+ block()
+ } catch (e: Throwable) {
+ throw StoneException(
+ message = "${e.message}. At ${ksNode?.location} ",
+ element = ksNode,
+ cause = e,
+ )
+ }
+}
+
+fun Sequence.forEachFun(
+ action: (index: Int, KSFunctionDeclaration) -> Unit
+) {
+ forEachIndexed { idx, func ->
+ wrapKsNoteInfo(func) {
+ action(idx, func)
+ }
+ }
+}
+
+fun Sequence.forEachType(
+ action: (index: Int, KSType) -> Unit
+) {
+ forEachIndexed { idx, type ->
+ wrapKsNoteInfo(type.declaration) {
+ action(idx, type)
+ }
+ }
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/ComponentDeclarationExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/ComponentDeclarationExt.kt
new file mode 100644
index 00000000..8897ceb7
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/ComponentDeclarationExt.kt
@@ -0,0 +1,105 @@
+@file:OptIn(KspExperimental::class)
+
+package com.github.klee0kai.thekey.stone.ksp.helpers
+
+import com.github.klee0kai.stone.annotations.component.*
+import com.github.klee0kai.stone.annotations.qualifier.IgnoreQualifier
+import com.github.klee0kai.stone.lifecycle.StoneLifeCycleOwner
+import com.github.klee0kai.stone.Named
+import com.github.klee0kai.stone.Qualifier
+import com.github.klee0kai.thekey.stone.ksp.helpers.annotations.findComponentAnnotation
+import com.github.klee0kai.thekey.stone.ksp.ksp.isAnyType
+import com.github.klee0kai.thekey.stone.ksp.ksp.isChildOf
+import com.github.klee0kai.thekey.stone.ksp.ksp.isType
+import com.github.klee0kai.thekey.stone.ksp.ksp.resolveNotNullable
+import com.google.devtools.ksp.KspExperimental
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.*
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.asClassName
+import com.github.klee0kai.stone.Scope as StoneScope
+
+fun Resolver.findComponentForModuleOrDep(
+ moduleCl: ClassName,
+): Sequence {
+ return getSymbolsWithAnnotation(Component::class.asClassName().canonicalName)
+ .filterIsInstance()
+ .filter { componentCl ->
+ componentCl.getAllFunctions().any {
+ it.returnType?.resolve()?.declaration?.isType(moduleCl) ?: false
+ }
+ }
+}
+
+val KSClassDeclaration.allParentDeclarations: Sequence
+ get() = (sequenceOf(this)
+ + superTypes.mapNotNull { it.resolveNotNullable().declaration as? KSClassDeclaration })
+
+val KSClassDeclaration.allIdentifierTypes: Sequence
+ get() = allParentDeclarations
+ .flatMap { it.findComponentAnnotation() }
+ .flatMap { it.identifiers }
+
+fun List.identifierParameters(
+ allIdentifierTypes: List,
+) = filter { it.type.resolveNotNullable() in allIdentifierTypes }
+
+fun List.notIdentifierParameters(
+ allIdentifierTypes: List,
+) = filter { it.type.resolveNotNullable() !in allIdentifierTypes }
+
+fun List.lifeCycleParameter() = firstOrNull {
+ (it.type.resolveNotNullable().declaration as? KSClassDeclaration)
+ ?.isChildOf(StoneLifeCycleOwner::class.asClassName()) == true
+}
+
+
+val KSClassDeclaration.wrapperProviders: Sequence
+ get() {
+ val componentCl = this@wrapperProviders
+ val allParentsSequence = (sequenceOf(componentCl) + superTypes)
+ return allParentsSequence
+ .flatMap { it.findComponentAnnotation() }
+ .flatMap { it.wrapperProviders }
+ }
+
+val KSAnnotated.scopeAnnotations: Sequence
+ get() {
+ val standardScopeAnnotations = listOf(
+ GcAllScope::class, GcWeakScope::class,
+ GcSoftScope::class, GcStrongScope::class
+ )
+
+ return annotations.filter { funAnnotation ->
+ standardScopeAnnotations.any { funAnnotation.annotationType.resolveNotNullable().declaration.isType(it) }
+ || funAnnotation.annotationType.resolveNotNullable().declaration.annotations.any { annotationOfAnnotation ->
+ annotationOfAnnotation.annotationType.resolveNotNullable().declaration.isAnyType(
+ GcScopeAnnotation::class,
+ StoneScope::class,
+ javax.inject.Scope::class
+ )
+ }
+ }
+ }
+
+val KSAnnotated.qualifierAnnotations: Sequence
+ get() {
+ val standardQualifierAnnotations = listOf(
+ Named::class,
+ javax.inject.Named::class,
+ IgnoreQualifier::class,
+ )
+
+ return annotations.filter { funAnnotation ->
+ standardQualifierAnnotations.any { funAnnotation.annotationType.resolveNotNullable().declaration.isType(it) }
+ || funAnnotation.annotationType.resolveNotNullable().declaration.annotations.any { annotationOfAnnotation ->
+ annotationOfAnnotation.annotationType.resolveNotNullable().declaration.isAnyType(
+ Qualifier::class,
+ javax.inject.Qualifier::class
+ )
+ }
+ }
+ }
+
+
+
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/StoneFileNamesExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/StoneFileNamesExt.kt
new file mode 100644
index 00000000..8fabad5e
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/StoneFileNamesExt.kt
@@ -0,0 +1,58 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers
+
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.squareup.kotlinpoet.ClassName
+
+val String.componentClName: String get() = "${this}StoneComponent"
+
+val String.factoryClName: String get() = "${this}_FStone"
+
+val String.moduleClName: String get() = "${this}_MStone"
+
+val String.cacheControlClName: String get() = "${this}_CCMStone"
+
+val String.hiddenModuleClName: String get() = "${this}_HMStone"
+
+val String.wrapperClName: String get() = "${this}_TWStone"
+
+val KSFunctionDeclaration.cacheControlMethodName get() = simpleName.asString().cacheControlMethodName
+val String.cacheControlMethodName get() = "__" + this + "_cache"
+
+val KSClassDeclaration.componentStoneClName
+ get() = ClassName(
+ packageName.asString(),
+ simpleName.getShortName().componentClName,
+ )
+
+val KSClassDeclaration.factoryStoneClName
+ get() = ClassName(
+ packageName.asString(),
+ simpleName.getShortName().factoryClName,
+ )
+
+val KSClassDeclaration.moduleStoneClName
+ get() = ClassName(
+ packageName.asString(),
+ simpleName.getShortName().moduleClName,
+ )
+
+
+val ClassName.cacheControlStoneClName
+ get() = ClassName(
+ packageName,
+ simpleName.cacheControlClName,
+ )
+
+
+val KSClassDeclaration.hiddenModuleStoneClName
+ get() = ClassName(
+ packageName.asString(),
+ simpleName.getShortName().hiddenModuleClName,
+ )
+
+val KSClassDeclaration.wrapperStoneClName
+ get() = ClassName(
+ packageName.asString(),
+ simpleName.getShortName().wrapperClName,
+ )
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/TypeHelpersExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/TypeHelpersExt.kt
new file mode 100644
index 00000000..eeaf8493
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/TypeHelpersExt.kt
@@ -0,0 +1,24 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers
+
+import com.github.klee0kai.thekey.stone.ksp.ksp.isAnyType
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSType
+
+fun KSType.isListType(
+): Boolean {
+ if (
+ declaration.isAnyType(
+ Iterable::class,
+ List::class,
+ MutableList::class,
+ Collection::class,
+ )
+ ) {
+ return true
+ }
+
+
+ return (this.declaration as? KSClassDeclaration)
+ ?.superTypes
+ ?.any { it.resolve().isListType() } == true
+}
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/annotations/ComponentAnnMirror.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/annotations/ComponentAnnMirror.kt
new file mode 100644
index 00000000..c5d9920c
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/annotations/ComponentAnnMirror.kt
@@ -0,0 +1,99 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.annotations
+
+import com.github.klee0kai.stone.annotations.component.*
+import com.github.klee0kai.stone.annotations.dependencies.Dependencies
+import com.github.klee0kai.stone.annotations.module.BindInstance
+import com.github.klee0kai.stone.annotations.module.Module
+import com.github.klee0kai.stone.annotations.module.Provide
+import com.github.klee0kai.stone.annotations.wrappers.WrappersHelper
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSAnnotation
+import com.google.devtools.ksp.symbol.KSType
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.asClassName
+import com.squareup.kotlinpoet.ksp.toTypeName
+
+class ComponentAnnMirror(
+ val identifiers: List,
+ val wrapperProviders: List,
+ val wrapperHelpers: List,
+)
+
+fun KSAnnotated.annotations(
+ className: ClassName,
+): Sequence = annotations
+ .filter { it.annotationType.resolve().toTypeName() == className }
+
+
+fun KSAnnotated.anyAnnotation(
+ vararg classNames: ClassName,
+): Sequence = annotations
+ .filter { it.annotationType.resolve().toTypeName() in classNames }
+
+
+fun KSAnnotated.stoneControlAnnotations(
+): Sequence = anyAnnotation(
+ Component::class.asClassName(),
+ WrappersHelper::class.asClassName(),
+ Module::class.asClassName(),
+ Dependencies::class.asClassName(),
+ ExtendOf::class.asClassName(),
+ ModuleOriginFactory::class.asClassName(),
+ ProtectInjected::class.asClassName(),
+ RunGc::class.asClassName(),
+ SwitchCache::class.asClassName(),
+ Init::class.asClassName(),
+ BindInstance::class.asClassName(),
+ Provide::class.asClassName(),
+
+ )
+
+
+fun KSAnnotated.hasOnlyAnnotation(
+ className: ClassName,
+): Boolean {
+ if (annotations.count() != 1) return false
+ return annotations.first().annotationType.resolve().toTypeName() == className
+}
+
+fun KSAnnotated.hasOnlyStoneControlAnnotation(
+ vararg classNames: ClassName,
+): Boolean {
+ if (stoneControlAnnotations().count() > classNames.size) return false
+ return stoneControlAnnotations().all { it.annotationType.resolve().toTypeName() in classNames }
+}
+
+
+fun KSType.annotations(
+ className: ClassName,
+): Sequence = annotations
+ .filter { it.annotationType.resolve().toTypeName() == className }
+
+
+@Suppress("UNCHECKED_CAST")
+fun KSAnnotated.findComponentAnnotation(
+): Sequence = annotations
+ .filter { it.annotationType.resolve().toTypeName() == Component::class.asClassName() }
+ .map { compAnn ->
+ val identifiers = compAnn.arguments
+ .firstOrNull { it.name?.asString() == "identifiers" }
+ ?.value as? List
+ ?: emptyList()
+
+ val wrapperProviders = compAnn.arguments
+ .firstOrNull { it.name?.asString() == "wrapperProviders" }
+ ?.value as? List
+ ?: emptyList()
+
+ val wrapperHelpers = compAnn.arguments
+ .firstOrNull { it.name?.asString() == "wrapperHelpers" }
+ ?.value as? List
+ ?: emptyList()
+
+ ComponentAnnMirror(
+ identifiers = identifiers,
+ wrapperProviders = wrapperProviders,
+ wrapperHelpers = wrapperHelpers,
+ )
+ }
+
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/InvokeCall.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/InvokeCall.kt
new file mode 100644
index 00000000..0147daeb
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/InvokeCall.kt
@@ -0,0 +1,240 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.invokecall
+
+import com.github.klee0kai.stone.__hidden__.provide.ProvideBuilder
+import com.github.klee0kai.thekey.stone.ksp.helpers.invokecall.model.FieldDetail
+import com.github.klee0kai.thekey.stone.ksp.helpers.invokecall.model.MethodDetail
+import com.github.klee0kai.thekey.stone.ksp.helpers.invokecall.model.QualifierAnn
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrap.WrapHelper
+import com.github.klee0kai.thekey.stone.ksp.poet.codeBlock
+import com.github.klee0kai.thekey.stone.ksp.utils.LocalFieldName
+import com.squareup.kotlinpoet.CodeBlock
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.TypeName
+import com.squareup.kotlinpoet.asClassName
+import java.util.*
+
+/**
+ * Invoke sequence or call sequence.
+ * Like someMethod1(arg1,arg2).someMethod2(arg3).someMethod3()
+ *
+ *
+ * Describe method names and using arguments for code generation.
+ * Arguments for chaining are reused by type.
+ */
+class InvokeCall(
+ var wrapHelper: WrapHelper,
+ val invokeSequenceVariants: List>,
+ val qualifierAnnotations: Set,
+ val flags: InvokeProvideFlags = InvokeProvideFlags(),
+) {
+
+ companion object;
+
+ fun bestSequence(): List = invokeSequenceVariants[0]
+
+ /**
+ * Using arguments in invoke sequence
+ *
+ * @return collection of all argument's types
+ */
+ fun argDeps(): Set {
+ val argsTypes = HashSet()
+ for (invokeSequence in invokeSequenceVariants) {
+ for (m in invokeSequence) {
+ argsTypes.addAll(
+ m.args.map { arg ->
+ ProvideDep(
+ methodName = null,
+ typeName = wrapHelper.listWrapTypeIfNeed(arg.type),
+ qualifierAnns = arg.qualifierAnns.toSet(),
+ )
+ })
+ }
+ }
+ return argsTypes
+ }
+
+ /**
+ * Providing type in this invoke sequence
+ *
+ * @return return type
+ */
+ fun resultType(): TypeName = wrapHelper.nonWrappedType(rawReturnType())
+
+ fun rawReturnType(): TypeName {
+ val invokeSequence = bestSequence()
+ return invokeSequence[invokeSequence.size - 1].returnType
+ }
+
+ fun best() = InvokeCall.fromSequence(
+ wrapHelper,
+ callSequence = bestSequence(),
+ qualifierAnnotations = qualifierAnnotations,
+ )
+
+ /**
+ * Generate invoke code bloke
+ *
+ * @param envFields predefined arguments in generated code
+ * @param argGen argument generator, if non found in envFields
+ * @return new code block without semicolon
+ */
+ fun invokeCode(
+ envFields: List,
+ argGen: (FieldDetail) -> CodeBlock? = { null },
+ ): CodeBlock {
+ val argGens = LinkedList<(FieldDetail) -> CodeBlock?>()
+ argGens.add(unwrapArgument(envFields))
+ argGens.add(argGen)
+
+ val invokeBuilder = CodeBlock.builder()
+ var invokeCount = 0
+ for (m in bestSequence()) {
+ var argCount = 0
+ val argsCodeBuilder = CodeBlock.builder()
+ for (arg in m.args) {
+ if (argCount++ > 0) argsCodeBuilder.add(",")
+ val argCode = argGens.firstNotNullOfOrNull { it.invoke(arg) }
+ argsCodeBuilder.add(argCode ?: CodeBlock.of("null"))
+ }
+
+ if (invokeCount++ > 0) invokeBuilder.add(".")
+ if (m.isProperty) {
+ invokeBuilder.add("%L", m.methodName)
+ } else {
+ invokeBuilder.add("%L(%L)", m.methodName, argsCodeBuilder.build())
+ }
+ }
+
+ return invokeBuilder.build()
+ }
+
+ fun invokeAllToList(
+ declaredFields: List,
+ ): CodeBlock = codeBlock {
+ val provType = List::class.asClassName().parameterizedBy(resultType())
+ val listFieldName: String = LocalFieldName.genLocalFieldName()
+ add(
+ "%T{ %L -> \n",
+ ProvideBuilder::class.asClassName().parameterizedBy(resultType()),
+ listFieldName,
+ )
+ for (sequence in invokeSequenceVariants) {
+ val invokeCall = InvokeCall.fromSequence(wrapHelper, sequence, qualifierAnnotations = qualifierAnnotations)
+ val seqCodeBlock = invokeCall.invokeCode(declaredFields)
+
+ if (wrapHelper.isList(invokeCall.rawReturnType())) {
+ add(
+ "%L.addAll( %L ?: emptyList() );\n",
+ listFieldName,
+ wrapHelper.transform(
+ invokeCall.rawReturnType(),
+ provType.copy(nullable = true),
+ seqCodeBlock
+ )
+ )
+ } else {
+ add(
+ "%L.add( %L );\n",
+ listFieldName,
+ wrapHelper.transform(
+ invokeCall.rawReturnType(),
+ resultType().copy(nullable = true),
+ seqCodeBlock
+ )
+ )
+ }
+ }
+
+ add(" }.all();\n")
+ }
+
+
+ private fun unwrapArgument(
+ envFields: List,
+ ): (FieldDetail) -> CodeBlock? {
+ return { arg ->
+ val isWannaList = wrapHelper.isList(arg.type)
+ val typeFields = envFields.filter { f ->
+ wrapHelper.nonWrappedType(f.type) == wrapHelper.nonWrappedType(arg.type)
+ }
+ var field = if (isWannaList) typeFields.lastOrNull { f ->
+ wrapHelper.isList(f.type) && (arg.qualifierAnns == f.qualifierAnns)
+ } else null
+
+ if (field == null) {
+ //non list
+ field = typeFields.firstOrNull { f ->
+ (arg.qualifierAnns == f.qualifierAnns)
+ }
+ }
+
+ if (field != null) {
+ wrapHelper.transform(field.type, arg.type, CodeBlock.of(field.name))
+ } else {
+ null
+ }
+ }
+ }
+
+ override fun toString(): String {
+ val builder = StringBuilder()
+ if (invokeSequenceVariants.size <= 1) {
+ for (qualifierAnn in qualifierAnnotations) {
+ builder.append(qualifierAnn.toString())
+ .append(" ")
+ }
+ }
+ var variantIndx = 0
+ for (variant in invokeSequenceVariants) {
+ if (variantIndx++ > 0) builder.append(";\n")
+ var secIndx = 0
+ for (m in variant) {
+ if (secIndx++ > 0) builder.append(".")
+ builder.append(m.methodName)
+ .append("(")
+ .append(m.args.joinToString(", ") { it.type.toString() })
+ .append(")")
+ }
+ }
+ return builder.toString()
+ }
+
+
+ override fun equals(o: Any?): Boolean {
+ if (this === o) return true
+ if (o == null || javaClass != o.javaClass) return false
+ val that = o as InvokeCall
+ return invokeSequenceVariants == that.invokeSequenceVariants
+ }
+
+ override fun hashCode(): Int {
+ return Objects.hash(invokeSequenceVariants)
+ }
+}
+
+
+fun InvokeCall.Companion.fromSequence(
+ wrapHelper: WrapHelper,
+ callSequence: List,
+ qualifierAnnotations: Set? = null,
+ flags: InvokeProvideFlags = InvokeProvideFlags(),
+) = InvokeCall(
+ wrapHelper = wrapHelper,
+ invokeSequenceVariants = listOf(callSequence),
+ flags = flags,
+ qualifierAnnotations = qualifierAnnotations ?: callSequence.lastOrNull()?.qualifierAnns ?: emptySet()
+
+)
+
+
+fun InvokeCall.Companion.fromVariants(
+ wrapHelper: WrapHelper,
+ variants: List,
+ qualifierAnnotations: Set,
+) = InvokeCall(
+ wrapHelper = wrapHelper,
+ invokeSequenceVariants = variants.flatMap { it.invokeSequenceVariants },
+ flags = variants.fold(InvokeProvideFlags()) { acc, value -> acc.merge(value.flags) },
+ qualifierAnnotations = qualifierAnnotations,
+)
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/InvokeProvideFlags.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/InvokeProvideFlags.kt
new file mode 100644
index 00000000..ce9eefd2
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/InvokeProvideFlags.kt
@@ -0,0 +1,14 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.invokecall
+
+data class InvokeProvideFlags(
+ val provideObjectCached: Boolean = false,
+ val provideBindInstance: Boolean = false,
+)
+
+
+fun InvokeProvideFlags.merge(
+ invokeProvideFlags: InvokeProvideFlags,
+) = InvokeProvideFlags(
+ provideObjectCached = provideObjectCached || invokeProvideFlags.provideObjectCached,
+ provideBindInstance = provideBindInstance || invokeProvideFlags.provideBindInstance,
+)
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/ModulesGraph.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/ModulesGraph.kt
new file mode 100644
index 00000000..fc3e3dd4
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/ModulesGraph.kt
@@ -0,0 +1,444 @@
+@file:OptIn(KspExperimental::class)
+
+package com.github.klee0kai.thekey.stone.ksp.helpers.invokecall
+
+import com.github.klee0kai.stone.__hidden__.CacheAction
+import com.github.klee0kai.stone.__hidden__.provide.ProvideBuilder
+import com.github.klee0kai.stone.annotations.module.BindInstance
+import com.github.klee0kai.stone.annotations.module.Provide
+import com.github.klee0kai.stone.annotations.qualifier.IgnoreQualifier
+import com.github.klee0kai.stone.weakref.Ref
+import com.github.klee0kai.thekey.stone.ksp.exceptions.*
+import com.github.klee0kai.thekey.stone.ksp.helpers.cacheControlMethodName
+import com.github.klee0kai.thekey.stone.ksp.helpers.hiddenModuleStoneClName
+import com.github.klee0kai.thekey.stone.ksp.helpers.identifierParameters
+import com.github.klee0kai.thekey.stone.ksp.helpers.invokecall.model.*
+import com.github.klee0kai.thekey.stone.ksp.helpers.qualifierAnnotations
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrap.WrapHelper
+import com.github.klee0kai.thekey.stone.ksp.ksp.cacheProtected
+import com.github.klee0kai.thekey.stone.ksp.ksp.getAllMethods
+import com.github.klee0kai.thekey.stone.ksp.ksp.isNotPrimitive
+import com.github.klee0kai.thekey.stone.ksp.ksp.resolveNotNullable
+import com.github.klee0kai.thekey.stone.ksp.target.component.*
+import com.github.klee0kai.thekey.stone.ksp.utils.LocalFieldName.genLocalFieldName
+import com.github.klee0kai.thekey.stone.ksp.utils.RecursiveDetector
+import com.github.klee0kai.thekey.stone.ksp.utils.removeDoubles
+import com.google.devtools.ksp.KspExperimental
+import com.google.devtools.ksp.getAnnotationsByType
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSType
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.CodeBlock
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.TypeName
+import com.squareup.kotlinpoet.asClassName
+import com.squareup.kotlinpoet.ksp.toTypeName
+import java.util.*
+
+class ModulesGraph(
+ val wrapHelper: WrapHelper,
+ val identifierTypes: List,
+) {
+
+ private val provideTypeCodes = HashMap>()
+ private val cacheControlTypeCodes = HashMap>()
+
+
+ fun collectFromComponent(
+ componentCl: KSClassDeclaration,
+ ) {
+ val componentsAllMethods = componentCl
+ .getAllMethods(includeObjectMethods = false, allowDoubles = false, "")
+ val hiddenModuleProvideMethod = MethodDetail(
+ methodName = GenComponentProcessor.hiddenModuleFieldName,
+ returnType = componentCl.hiddenModuleStoneClName,
+ isProperty = true,
+ )
+
+ componentsAllMethods
+ .filter { it.isModuleProvideMethod || it.isDepsProvideMethod }
+ .forEachFun { _, moduleProvideMethod ->
+
+ val module = moduleProvideMethod.returnType?.resolveNotNullable()
+ ?.declaration as? KSClassDeclaration ?: return@forEachFun
+
+ for (m in module.getAllMethods(includeObjectMethods = false, allowDoubles = true, "")) {
+ if (m.returnType?.resolveNotNullable()?.isNotPrimitive == false) continue
+
+ val returnType = m.returnType?.resolveNotNullable()?.toTypeName() ?: continue
+ val provTypeName = wrapHelper.nonWrappedType(returnType)
+ val isCached = m.getAnnotationsByType(Provide::class)
+ .firstOrNull()?.cacheProtected !in listOf(Provide.CacheType.Factory, null)
+ val isBindInstance = m.getAnnotationsByType(BindInstance::class).firstOrNull() != null
+
+ provideTypeCodes.putIfAbsent(provTypeName, HashSet())
+ provideTypeCodes[provTypeName]?.add(
+ InvokeCall.fromSequence(
+ wrapHelper = wrapHelper,
+ callSequence = listOf(moduleProvideMethod.toMethodDetail(), m.toMethodDetail()),
+ flags = InvokeProvideFlags(
+ provideObjectCached = isCached,
+ provideBindInstance = isBindInstance,
+ ),
+ )
+ )
+
+ val cacheControlMethod = MethodDetail(
+ methodName = m.cacheControlMethodName,
+ returnType = wrapHelper.listWrapTypeIfNeed(returnType),
+ qualifierAnns = m.qualifierAnnotations.map { it.toQualifierAnn() }.toSet(),
+ args = buildList {
+ add(FieldDetail.simple("__action", CacheAction::class.asClassName()))
+ addAll(m.parameters.identifierParameters(identifierTypes).map { it.toFieldDetail() })
+ },
+ )
+
+ cacheControlTypeCodes.putIfAbsent(provTypeName, HashSet())
+ cacheControlTypeCodes[provTypeName]?.add(
+ InvokeCall.fromSequence(
+ wrapHelper = wrapHelper,
+ callSequence = listOf(moduleProvideMethod.toMethodDetail(), cacheControlMethod)
+ )
+ )
+ }
+ }
+
+
+ componentsAllMethods
+ .filter { it.isBindInstanceMethod == BindInstanceType.BindInstanceAndProvide }
+ .forEachFun { _, componentMethod ->
+ val returnType = componentMethod.returnType?.resolve()?.toTypeName() ?: return@forEachFun
+ val nonInModules = provideInvokesWithDeps(
+ ProvideDep(
+ componentMethod.simpleName.asString(),
+ wrapHelper.listWrapTypeIfNeed(returnType),
+ componentMethod.qualifierAnnotations.map { it.toQualifierAnn() }.toSet()
+ )
+ ) == null
+ if (!nonInModules) return@forEachFun
+
+ val provTypeName = wrapHelper.nonWrappedType(returnType)
+ val isCached = componentMethod.getAnnotationsByType(Provide::class)
+ .firstOrNull()?.cacheProtected !in listOf(Provide.CacheType.Factory, null)
+ val isBindInstance = componentMethod.getAnnotationsByType(BindInstance::class).firstOrNull() != null
+
+ provideTypeCodes.putIfAbsent(provTypeName, HashSet())
+ provideTypeCodes[provTypeName]?.add(
+ InvokeCall.fromSequence(
+ wrapHelper = wrapHelper,
+ callSequence = listOf(hiddenModuleProvideMethod, componentMethod.toMethodDetail()),
+ flags = InvokeProvideFlags(
+ provideObjectCached = isCached,
+ provideBindInstance = isBindInstance,
+ ),
+ )
+ )
+
+ val cacheControlMethod = MethodDetail(
+ methodName = componentMethod.cacheControlMethodName,
+ returnType = wrapHelper.listWrapTypeIfNeed(returnType),
+ qualifierAnns = componentMethod.qualifierAnnotations.map { it.toQualifierAnn() }.toSet(),
+ args = buildList {
+ add(FieldDetail.simple("__action", CacheAction::class.asClassName()))
+ addAll(
+ componentMethod.parameters.identifierParameters(identifierTypes)
+ .map { it.toFieldDetail() })
+ },
+ )
+
+ cacheControlTypeCodes.putIfAbsent(provTypeName, HashSet())
+ cacheControlTypeCodes[provTypeName]
+ ?.add(
+ InvokeCall.fromSequence(
+ wrapHelper = wrapHelper,
+ callSequence = listOf(hiddenModuleProvideMethod, cacheControlMethod)
+ )
+ )
+ }
+ }
+
+
+ fun codeProvideType(
+ methodName: String?,
+ returnType: TypeName,
+ qualifierAnns: Set,
+ declaredFields: List
+ ): CodeBlock? {
+ val isWrappedReturn = wrapHelper.isSupport(returnType)
+ val providingType = if (isWrappedReturn) wrapHelper.nonWrappedType(returnType) else returnType
+ val provideDeps = HashSet()
+ provideDeps.add(
+ ProvideDep(
+ methodName = methodName,
+ typeName = wrapHelper.listWrapTypeIfNeed(returnType),
+ qualifierAnns = qualifierAnns,
+ )
+ )
+ val provideTypeInvokes = provideInvokesWithDeps(provideDeps.iterator().next())
+ if (provideTypeInvokes.isNullOrEmpty()) return null
+
+ for (provideTypeInvoke in provideTypeInvokes) provideDeps.addAll(provideTypeInvoke.argDeps())
+ if (provideTypeInvokes.size == 1 && !wrapHelper.isList(returnType)) {
+ val invokeCall = provideTypeInvokes.first().best()
+ return wrapHelper.transform(
+ invokeCall.rawReturnType(),
+ returnType,
+ invokeCall.invokeCode(declaredFields)
+ )
+ }
+
+ val provideBuilder = ProvideBuilder::class.asClassName().parameterizedBy(providingType)
+ val provideBuilderList = Collection::class.asClassName().parameterizedBy(providingType)
+
+ val listFieldName = genLocalFieldName()
+ val localVariables = LinkedList(declaredFields)
+
+ val codeBlock = CodeBlock.builder()
+ codeBlock.add("%T{ %L -> \n", provideBuilder, listFieldName)
+
+ for (inv in provideTypeInvokes) {
+ val isCacheProvide = inv.flags.provideObjectCached
+ val isSingleDepRequired = provideDeps.any {
+ wrapHelper.nonWrappedType(it.typeName) == wrapHelper.nonWrappedType(inv.resultType())
+ && !wrapHelper.isList(it.typeName)
+ }
+ val isListDepRequired = provideDeps.any {
+ wrapHelper.nonWrappedType(it.typeName) == wrapHelper.nonWrappedType(inv.resultType())
+ && wrapHelper.isList(it.typeName)
+ }
+ var singleDepField = FieldDetail(
+ name = genLocalFieldName(),
+ type = inv.resultType(),
+ qualifierAnns = inv.qualifierAnnotations
+ )
+ val listDepField = FieldDetail(
+ name = genLocalFieldName(),
+ type = Ref::class.asClassName().parameterizedBy(
+ List::class.asClassName().parameterizedBy(
+ inv.resultType()
+ )
+ ),
+ qualifierAnns = inv.qualifierAnnotations
+ )
+
+ if (isSingleDepRequired) {
+ if (isCacheProvide) {
+ codeBlock.add("// 1 ${inv.qualifierAnnotations.joinToString(",") { it.logString() }} \n")
+ codeBlock.add("val %L = ", singleDepField.name)
+ .add(
+ wrapHelper.transform(
+ inv.best().rawReturnType(),
+ inv.resultType(),
+ inv.best().invokeCode(localVariables)
+ )
+ )
+ .addStatement("")
+
+
+ localVariables.add(singleDepField)
+ } else {
+ singleDepField = singleDepField
+ .copy(type = Ref::class.asClassName().parameterizedBy(inv.resultType()))
+
+ codeBlock.add("// 2 ${inv.qualifierAnnotations.joinToString(",") { it.logString() }} \n")
+ codeBlock.add("val %L = %T{ ", singleDepField.name, Ref::class)
+ .add(
+ wrapHelper.transform(
+ inv.best().rawReturnType(),
+ inv.resultType(),
+ inv.best().invokeCode(localVariables),
+ )
+ )
+ .addStatement(" } ")
+
+ localVariables.add(singleDepField)
+ }
+ }
+
+ if (isListDepRequired) {
+ codeBlock.add("// 3 ${inv.qualifierAnnotations.joinToString(",") { it.logString() }} \n")
+ codeBlock.add("val %L = %T{ ", listDepField.name, listDepField.type)
+ .add(inv.invokeAllToList(localVariables))
+ .addStatement(" } ")
+
+ localVariables.add(listDepField)
+ }
+
+
+ if (inv.resultType().copy(nullable = false) == providingType.copy(nullable = false)) {
+ if (wrapHelper.isList(returnType)) {
+ codeBlock.add(
+ "%L.addAll( %L ?: emptyList() )\n",
+ listFieldName,
+ wrapHelper.transform(
+ listDepField.type,
+ provideBuilderList.copy(nullable = true),
+ CodeBlock.of(listDepField.name)
+ )
+ )
+ } else {
+ codeBlock.add(
+ "%L.add( %L )\n",
+ listFieldName,
+ wrapHelper.transform(
+ singleDepField.type,
+ providingType.copy(nullable = true),
+ CodeBlock.of(singleDepField.name)
+ )
+ )
+ }
+ if (!wrapHelper.isList(returnType)) break
+ }
+ }
+
+ codeBlock.add(" }")
+ if (wrapHelper.isList(returnType)) {
+ codeBlock.add(".all() ")
+
+ return wrapHelper.transform(
+ List::class.asClassName().parameterizedBy(providingType),
+ returnType,
+ codeBlock.build()
+ )
+
+ } else {
+ codeBlock.add(if (providingType.isNullable) ".firstOrNull()" else ".first() ")
+
+ return wrapHelper.transform(
+ providingType,
+ returnType,
+ codeBlock.build()
+ )
+ }
+ }
+
+
+ fun provideInvokesWithDeps(provideDep: ProvideDep): List? {
+ var provideTypeInvokes = LinkedList()
+ var needProvideDeps = LinkedList()
+ val needProvideDepsRecursiveDetector = RecursiveDetector()
+ needProvideDeps.add(provideDep)
+ var loopCount = 0
+
+ // provide dependencies while not provide all
+ while (!needProvideDeps.isEmpty()) {
+ val rawDep = needProvideDeps.pollFirst()
+ val dep = wrapHelper.nonWrappedType(rawDep.typeName)
+ val invokeCall = provideTypeInvokeCall(
+ provideTypeCodes,
+ dep,
+ rawDep.qualifierAnns,
+ rawDep.methodName,
+ wrapHelper.isList(rawDep.typeName)
+ )
+ if (invokeCall == null) {
+ if (provideDep == rawDep) return null
+ throw ObjectNotProvidedException("Error provide type ${rawDep.typeName}")
+ }
+
+ val isBindInstanceInvoke = invokeCall.flags.provideBindInstance
+ val newDeps = invokeCall.argDeps().filter {
+ if (isBindInstanceInvoke && rawDep.typeName == it.typeName) {
+ // bind instance case. Argument and return type are equals
+ return@filter false
+ }
+ // qualifies not need to provide
+ val argNonWrapped = wrapHelper.nonWrappedType(it.typeName)
+ argNonWrapped is ClassName && !identifierTypes.any { it.toTypeName() == argNonWrapped }
+ }
+ needProvideDeps.addAll(newDeps)
+
+ needProvideDeps = LinkedList(needProvideDeps.removeDoubles { a, b -> a == b })
+
+ val recursiveDetected = !newDeps.isEmpty()
+ && needProvideDepsRecursiveDetector.next(needProvideDeps.hashCode())
+ if (recursiveDetected) {
+ throw RecursiveProviding("Error provide type ${provideDep.typeName}. Recursive providing detected.")
+ }
+
+ if (loopCount++ > MAX_PROVIDE_RESOLVE_COUNT) {
+ throw StoneException(
+ "Error provide type ${provideDep.typeName}. " +
+ "Long providing loop for type. Stone library Error. "
+ )
+ }
+
+ provideTypeInvokes.add(invokeCall)
+ }
+
+ if (provideTypeInvokes.filter { it.resultType() == provideDep.typeName }
+ .groupBy { it.qualifierAnnotations }.size > 1) {
+ throw StoneException("internal arch error")
+ }
+ provideTypeInvokes = LinkedList(
+ provideTypeInvokes.removeDoubles { it1, it2 ->
+ it1.resultType() == it2.resultType()
+ && it1.qualifierAnnotations == it2.qualifierAnnotations
+ }
+ )
+ provideTypeInvokes.reverse()
+ return provideTypeInvokes
+ }
+
+ /**
+ * Generate cache control method invoke. Clean refs, change ref type and other
+ *
+ * @param provideMethodName predefined method name
+ * @param typeName the name of the type whose cache needs to be changed
+ * @return cache control invoke call
+ */
+ fun invokeControlCacheForType(
+ provideMethodName: String?,
+ typeName: TypeName,
+ qualifierAnns: Set
+ ): InvokeCall? = provideTypeInvokeCall(
+ cacheControlTypeCodes,
+ typeName,
+ qualifierAnns,
+ provideMethodName?.cacheControlMethodName,
+ false
+ )
+
+
+ private fun provideTypeInvokeCall(
+ provideTypeCodes: Map>,
+ typeName: TypeName,
+ qualifierAnns: Set,
+ provideMethodName: String?,
+ listVariants: Boolean,
+ ): InvokeCall? {
+ val invokeCalls = provideTypeCodes.getOrDefault(typeName, null)
+ if (invokeCalls == null || invokeCalls.isEmpty()) return null
+
+ var filtered = invokeCalls.toList()
+ if (qualifierAnns.none { it.typeName == IgnoreQualifier::class.asClassName() }) {
+ filtered = invokeCalls.filter { it.qualifierAnnotations == qualifierAnns }
+ }
+
+ filtered = if (provideMethodName != null) {
+ filtered.filter { provideMethodName == it.bestSequence().last().methodName }
+ } else filtered
+
+ if (!listVariants && filtered.size > 1) {
+ throw IncorrectSignatureException(
+ "Error provide type $typeName " +
+ ": is bound multi times.\n " +
+ filtered.joinToString(" and ")
+ )
+ }
+ return if (!filtered.isEmpty()) {
+ InvokeCall.fromVariants(
+ wrapHelper,
+ variants = filtered.toList(),
+ qualifierAnnotations = qualifierAnns,
+ )
+ } else {
+ null
+ }
+ }
+
+ companion object {
+ const val MAX_PROVIDE_RESOLVE_COUNT: Int = 10000
+ }
+
+}
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/ProvideDep.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/ProvideDep.kt
new file mode 100644
index 00000000..c23719eb
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/ProvideDep.kt
@@ -0,0 +1,13 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.invokecall
+
+import com.github.klee0kai.thekey.stone.ksp.helpers.invokecall.model.QualifierAnn
+import com.squareup.kotlinpoet.TypeName
+
+data class ProvideDep(
+ val methodName: String?,
+ val typeName: TypeName,
+ val qualifierAnns: Set,
+) {
+ companion object;
+}
+
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/model/FieldDetail.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/model/FieldDetail.kt
new file mode 100644
index 00000000..f424f7f0
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/model/FieldDetail.kt
@@ -0,0 +1,24 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.invokecall.model
+
+import com.github.klee0kai.thekey.stone.ksp.helpers.qualifierAnnotations
+import com.github.klee0kai.thekey.stone.ksp.ksp.resolveNotNullable
+import com.google.devtools.ksp.symbol.KSValueParameter
+import com.squareup.kotlinpoet.TypeName
+import com.squareup.kotlinpoet.ksp.toTypeName
+
+data class FieldDetail(
+ val name: String,
+ val type: TypeName,
+ val qualifierAnns: Set = emptySet(),
+) {
+ companion object;
+}
+
+fun FieldDetail.Companion.simple(name: String, type: TypeName) = FieldDetail(name, type)
+
+fun KSValueParameter.toFieldDetail() = FieldDetail(
+ name = name?.asString() ?: "it",
+ type = type.resolveNotNullable().toTypeName().copy(nullable = false),
+ qualifierAnns = qualifierAnnotations.map { it.toQualifierAnn() }.toSet(),
+)
+
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/model/MethodDetail.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/model/MethodDetail.kt
new file mode 100644
index 00000000..7a66e1ef
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/model/MethodDetail.kt
@@ -0,0 +1,25 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.invokecall.model
+
+import com.github.klee0kai.thekey.stone.ksp.helpers.qualifierAnnotations
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.squareup.kotlinpoet.TypeName
+import com.squareup.kotlinpoet.asClassName
+import com.squareup.kotlinpoet.ksp.toTypeName
+
+data class MethodDetail(
+ val methodName: String,
+ val returnType: TypeName,
+ val args: List = emptyList(),
+ val qualifierAnns: Set = emptySet(),
+ val isProperty: Boolean = false,
+) {
+ companion object;
+}
+
+fun KSFunctionDeclaration.toMethodDetail() = MethodDetail(
+ methodName = simpleName.asString(),
+ returnType = returnType?.resolve()?.toTypeName() ?: Unit::class.asClassName(),
+ args = parameters.map { it.toFieldDetail() },
+ qualifierAnns = qualifierAnnotations.map { it.toQualifierAnn() }.toSet(),
+)
+
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/model/QualifierAnn.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/model/QualifierAnn.kt
new file mode 100644
index 00000000..1ecd8d77
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/invokecall/model/QualifierAnn.kt
@@ -0,0 +1,35 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.invokecall.model
+
+import com.github.klee0kai.stone.annotations.qualifier.IgnoreQualifier
+import com.github.klee0kai.thekey.stone.ksp.ksp.resolveAlias
+import com.google.devtools.ksp.symbol.KSAnnotation
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.TypeName
+import com.squareup.kotlinpoet.asTypeName
+import com.squareup.kotlinpoet.ksp.toClassName
+
+data class QualifierAnn(
+ var typeName: TypeName,
+ var values: Map = mapOf(),
+) {
+ companion object;
+}
+
+fun QualifierAnn.Companion.ignoreQualifier(
+) = QualifierAnn(
+ typeName = IgnoreQualifier::class.asTypeName(),
+ values = emptyMap(),
+)
+
+
+fun KSAnnotation.toQualifierAnn(
+) = QualifierAnn(
+ typeName = annotationType.resolveAlias().toClassName(),
+ values = arguments.map { it.name?.asString()!! to it.value }.groupBy { it.first },
+)
+
+fun QualifierAnn.logString(): String {
+ val type = (typeName as? ClassName)?.simpleName ?: typeName
+ val value = values.values.joinToString(",")
+ return "${type}( $values )"
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/itemholder/ItemCacheType.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/itemholder/ItemCacheType.kt
new file mode 100644
index 00000000..9e63fb5b
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/itemholder/ItemCacheType.kt
@@ -0,0 +1,53 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.itemholder
+
+import com.github.klee0kai.stone.__hidden__.types.holders.StoneRefType
+import com.github.klee0kai.stone.annotations.component.GcSoftScope
+import com.github.klee0kai.stone.annotations.component.GcStrongScope
+import com.github.klee0kai.stone.annotations.component.GcWeakScope
+import com.github.klee0kai.stone.annotations.module.BindInstance
+import com.github.klee0kai.stone.annotations.module.Provide
+import com.squareup.kotlinpoet.asClassName
+
+enum class ItemCacheType {
+ Strong, Soft, Weak;
+
+ val gcScopeClassName
+ get() = when (this) {
+ Weak -> GcWeakScope::class
+ Strong -> GcStrongScope::class
+ Soft -> GcSoftScope::class
+ }.asClassName()
+}
+
+fun ItemCacheType.toRefTypeSingle(
+
+): StoneRefType = when (this) {
+ ItemCacheType.Strong -> StoneRefType.StrongObject
+ ItemCacheType.Soft -> StoneRefType.SoftObject
+ ItemCacheType.Weak -> StoneRefType.WeakObject
+}
+
+fun ItemCacheType.toRefTypeList(
+
+): StoneRefType = when (this) {
+ ItemCacheType.Strong -> StoneRefType.ListObject
+ ItemCacheType.Soft -> StoneRefType.ListSoftObject
+ ItemCacheType.Weak -> StoneRefType.ListWeakObject
+}
+
+
+fun BindInstance.CacheType.toItemCacheType(
+): ItemCacheType = when (this) {
+ BindInstance.CacheType.Weak -> ItemCacheType.Weak
+ BindInstance.CacheType.Soft -> ItemCacheType.Soft
+ BindInstance.CacheType.Strong -> ItemCacheType.Strong
+}
+
+fun Provide.CacheType.toItemCacheType(
+): ItemCacheType? = when (this) {
+ Provide.CacheType.Factory -> null
+ Provide.CacheType.Weak -> ItemCacheType.Weak
+ Provide.CacheType.Soft -> ItemCacheType.Soft
+ Provide.CacheType.Strong -> ItemCacheType.Strong
+}
+
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/itemholder/ItemHolderCodeHelper.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/itemholder/ItemHolderCodeHelper.kt
new file mode 100644
index 00000000..b7458c1c
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/itemholder/ItemHolderCodeHelper.kt
@@ -0,0 +1,69 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.itemholder
+
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrap.WrapHelper
+import com.google.devtools.ksp.symbol.KSValueParameter
+import com.squareup.kotlinpoet.CodeBlock
+import com.squareup.kotlinpoet.TypeName
+import com.squareup.kotlinpoet.TypeSpec
+
+interface ItemHolderCodeHelper {
+ companion object;
+
+ val fieldName: String
+
+ fun TypeSpec.Builder.genCacheField()
+
+ fun codeGetCachedValue(): CodeBlock
+
+ fun codeSetCachedValue(
+ value: CodeBlock,
+ onlyIfNull: Boolean,
+ ): CodeBlock
+
+ fun statementSwitchRef(
+ paramsCode: CodeBlock,
+ ): CodeBlock
+
+ fun clearNullsStatement(): CodeBlock
+
+}
+
+fun ItemHolderCodeHelper.Companion.of(
+ fieldName: String,
+ returnType: TypeName,
+ idArguments: List,
+ cacheType: ItemCacheType,
+ wrapHelper: WrapHelper,
+): ItemHolderCodeHelper {
+
+ return when {
+ idArguments.isEmpty() -> SingleItemHolderCodeHelper(
+ fieldName = fieldName,
+ returnType = returnType,
+ nonWrappedReturnType = wrapHelper.nonWrappedType(returnType),
+ itemCacheType = cacheType,
+ isListCaching = wrapHelper.isList(returnType),
+ defRefType = if (wrapHelper.isList(returnType)) cacheType.toRefTypeList() else cacheType.toRefTypeSingle(),
+ )
+
+ idArguments.size == 1 -> SimpleMapItemHolderCodeHelper(
+ fieldName = fieldName,
+ returnType = returnType,
+ nonWrappedReturnType = wrapHelper.nonWrappedType(returnType),
+ itemCacheType = cacheType,
+ isListCaching = wrapHelper.isList(returnType),
+ defRefType = if (wrapHelper.isList(returnType)) cacheType.toRefTypeList() else cacheType.toRefTypeSingle(),
+ keyParam = idArguments.first(),
+ )
+
+ else -> MultiKeyMapItemHolderCodeHelper(
+ fieldName = fieldName,
+ returnType = returnType,
+ nonWrappedReturnType = wrapHelper.nonWrappedType(returnType),
+ itemCacheType = cacheType,
+ isListCaching = wrapHelper.isList(returnType),
+ defRefType = if (wrapHelper.isList(returnType)) cacheType.toRefTypeList() else cacheType.toRefTypeSingle(),
+ keyArguments = idArguments,
+ )
+ }
+}
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/itemholder/MultiKeyMapItemHolderCodeHelper.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/itemholder/MultiKeyMapItemHolderCodeHelper.kt
new file mode 100644
index 00000000..de5d189c
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/itemholder/MultiKeyMapItemHolderCodeHelper.kt
@@ -0,0 +1,72 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.itemholder
+
+import com.github.klee0kai.stone.__hidden__.types.MultiKey
+import com.github.klee0kai.stone.__hidden__.types.holders.MapItemHolder
+import com.github.klee0kai.stone.__hidden__.types.holders.StoneRefType
+import com.github.klee0kai.thekey.stone.ksp.poet.codeBlock
+import com.github.klee0kai.thekey.stone.ksp.poet.genProperty
+import com.google.devtools.ksp.symbol.KSValueParameter
+import com.squareup.kotlinpoet.*
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+
+class MultiKeyMapItemHolderCodeHelper(
+ override val fieldName: String,
+ val returnType: TypeName,
+ val nonWrappedReturnType: TypeName,
+ val itemCacheType: ItemCacheType,
+ val isListCaching: Boolean,
+ val defRefType: StoneRefType,
+ val keyArguments: List,
+) : ItemHolderCodeHelper {
+
+ override fun TypeSpec.Builder.genCacheField() {
+ val cacheType = MapItemHolder::class.asClassName()
+ .parameterizedBy(
+ MultiKey::class.asClassName(),
+ nonWrappedReturnType,
+ )
+
+ genProperty(fieldName, cacheType) {
+ addModifiers(KModifier.PRIVATE)
+ initializer("%T(%T.%L)", cacheType, StoneRefType::class, defRefType)
+ }
+ }
+
+
+ override fun codeGetCachedValue(
+ ) = codeBlock {
+ val getMethod = if (isListCaching) "getList" else "get"
+ add(
+ "%L.%L(key = %T(%L))",
+ fieldName, getMethod,
+ MultiKey::class.asClassName(), keyArguments.joinToString(",") { it.name!!.asString() },
+ )
+ }
+
+ override fun codeSetCachedValue(
+ value: CodeBlock,
+ onlyIfNull: Boolean
+ ) = codeBlock {
+ val setMethod = if (isListCaching) "setList" else "set"
+ add(
+ "%L.%L(key = %T(%L), onlyIfNull = %L ){ ",
+ fieldName, setMethod,
+ MultiKey::class.asClassName(), keyArguments.joinToString(",") { it.name!!.asString() },
+ onlyIfNull
+ )
+ add(value)
+ add("}")
+ }
+
+ override fun statementSwitchRef(
+ paramsCode: CodeBlock,
+ ): CodeBlock = CodeBlock.builder()
+ .addStatement("%L.switchCache(%L)", fieldName, paramsCode)
+ .build()
+
+
+ override fun clearNullsStatement(): CodeBlock = CodeBlock.Builder()
+ .addStatement("%L.clearNulls()", fieldName)
+ .build()
+
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/itemholder/SimpleMapItemHolderCodeHelper.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/itemholder/SimpleMapItemHolderCodeHelper.kt
new file mode 100644
index 00000000..4f35fdb6
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/itemholder/SimpleMapItemHolderCodeHelper.kt
@@ -0,0 +1,68 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.itemholder
+
+import com.github.klee0kai.stone.__hidden__.types.holders.MapItemHolder
+import com.github.klee0kai.stone.__hidden__.types.holders.StoneRefType
+import com.github.klee0kai.thekey.stone.ksp.poet.codeBlock
+import com.github.klee0kai.thekey.stone.ksp.poet.genProperty
+import com.google.devtools.ksp.symbol.KSValueParameter
+import com.squareup.kotlinpoet.*
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.ksp.toTypeName
+
+class SimpleMapItemHolderCodeHelper(
+ override val fieldName: String,
+ val returnType: TypeName,
+ val nonWrappedReturnType: TypeName,
+ val itemCacheType: ItemCacheType,
+ val isListCaching: Boolean,
+ val defRefType: StoneRefType,
+ val keyParam: KSValueParameter,
+) : ItemHolderCodeHelper {
+
+ override fun TypeSpec.Builder.genCacheField() {
+ val cacheType = MapItemHolder::class.asClassName()
+ .parameterizedBy(
+ keyParam.type.resolve().toTypeName(),
+ nonWrappedReturnType,
+ )
+
+ genProperty(fieldName, cacheType) {
+ addModifiers(KModifier.PRIVATE)
+ initializer("%T(%T.%L)", cacheType, StoneRefType::class, defRefType)
+ }
+ }
+
+ override fun codeGetCachedValue(
+ ) = codeBlock {
+ val getMethod = if (isListCaching) "getList" else "get"
+ add(
+ "%L.%L(key = %L)",
+ fieldName, getMethod, keyParam.name!!.asString()
+ )
+ }
+
+ override fun codeSetCachedValue(
+ value: CodeBlock,
+ onlyIfNull: Boolean
+ ) = codeBlock {
+ val setMethod = if (isListCaching) "setList" else "set"
+ add(
+ "%L.%L(key = %L, onlyIfNull = %L ){ ",
+ fieldName, setMethod, keyParam.name!!.asString(), onlyIfNull
+ )
+ add(value)
+ add("}")
+ }
+
+ override fun statementSwitchRef(
+ paramsCode: CodeBlock,
+ ): CodeBlock = CodeBlock.builder()
+ .addStatement("%L.switchCache(%L)", fieldName, paramsCode)
+ .build()
+
+ override fun clearNullsStatement(): CodeBlock = CodeBlock.Builder()
+ .addStatement("%L.clearNulls()", fieldName)
+ .build()
+
+
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/itemholder/SingleItemHolderCodeHelper.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/itemholder/SingleItemHolderCodeHelper.kt
new file mode 100644
index 00000000..c142a12d
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/itemholder/SingleItemHolderCodeHelper.kt
@@ -0,0 +1,53 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.itemholder
+
+import com.github.klee0kai.stone.__hidden__.types.holders.SingleItemHolder
+import com.github.klee0kai.stone.__hidden__.types.holders.StoneRefType
+import com.github.klee0kai.thekey.stone.ksp.poet.codeBlock
+import com.github.klee0kai.thekey.stone.ksp.poet.genProperty
+import com.squareup.kotlinpoet.*
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+
+class SingleItemHolderCodeHelper(
+ override val fieldName: String,
+ val returnType: TypeName,
+ val nonWrappedReturnType: TypeName,
+ val itemCacheType: ItemCacheType,
+ val isListCaching: Boolean,
+ val defRefType: StoneRefType,
+) : ItemHolderCodeHelper {
+
+ override fun TypeSpec.Builder.genCacheField() {
+ val cacheType = SingleItemHolder::class.asClassName()
+ .parameterizedBy(nonWrappedReturnType)
+
+ genProperty(fieldName, cacheType) {
+ addModifiers(KModifier.PRIVATE)
+ initializer("%T(%T.%L)", cacheType, StoneRefType::class, defRefType)
+ }
+ }
+
+ override fun codeGetCachedValue(
+ ) = codeBlock {
+ val getMethod = if (isListCaching) "getList" else "get"
+ add("%L.%L()", fieldName, getMethod)
+ }
+
+ override fun codeSetCachedValue(
+ value: CodeBlock,
+ onlyIfNull: Boolean
+ ) = codeBlock {
+ val setMethod = if (isListCaching) "setList" else "set"
+ add("%L.%L(onlyIfNull = %L ){ ", fieldName, setMethod, onlyIfNull)
+ add(value)
+ add(" }")
+ }
+
+ override fun statementSwitchRef(
+ paramsCode: CodeBlock,
+ ): CodeBlock = CodeBlock.builder()
+ .addStatement("%L.switchCache(%L)", fieldName, paramsCode)
+ .build()
+
+ override fun clearNullsStatement(): CodeBlock = CodeBlock.of("")
+
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/wrap/ClassNameUtils.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/wrap/ClassNameUtils.kt
new file mode 100644
index 00000000..3b4c874f
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/wrap/ClassNameUtils.kt
@@ -0,0 +1,38 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.wrap
+
+import com.squareup.kotlinpoet.ParameterizedTypeName
+import com.squareup.kotlinpoet.TypeName
+import com.squareup.kotlinpoet.WildcardTypeName
+
+object ClassNameUtils {
+
+ fun rawTypeOf(typeName: TypeName): TypeName {
+ if (typeName is ParameterizedTypeName) {
+ return rawTypeOf((typeName).rawType)
+ }
+ if (typeName is WildcardTypeName) {
+ val upperBounds = typeName.outTypes
+ if (upperBounds.isNotEmpty()) {
+ return rawTypeOf(upperBounds.first())
+ }
+ }
+ return typeName.copy(nullable = false)
+ }
+
+
+ fun noWildCardType(type: TypeName): TypeName {
+ if (type is WildcardTypeName) {
+ val upperBounds = type.outTypes
+ return if (!upperBounds.isEmpty()) {
+ noWildCardType(upperBounds.first())
+ } else {
+ type
+ }
+ }
+ return type
+ }
+
+}
+
+
+fun TypeName.rawType(): TypeName = ClassNameUtils.rawTypeOf(this)
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/wrap/FormatInList.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/wrap/FormatInList.kt
new file mode 100644
index 00000000..13ee5573
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/wrap/FormatInList.kt
@@ -0,0 +1,18 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.wrap
+
+import com.squareup.kotlinpoet.CodeBlock
+import com.squareup.kotlinpoet.TypeName
+
+fun interface FormatInList {
+
+ /**
+ * @param itemTransformFun format each type
+ * @return
+ */
+ fun formatCode(
+ originalListType: TypeName,
+ or: CodeBlock,
+ itemTransformFun: FormatSimple,
+ ): CodeBlock
+
+}
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/wrap/FormatSimple.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/wrap/FormatSimple.kt
new file mode 100644
index 00000000..2324b215
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/wrap/FormatSimple.kt
@@ -0,0 +1,52 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.wrap
+
+import com.squareup.kotlinpoet.CodeBlock
+
+
+fun interface FormatSimple {
+
+ /**
+ * @param code code witch return original type
+ * @return code witch return wanna type
+ */
+ fun formatCode(
+ or: CodeBlock,
+ ): CodeBlock
+
+
+}
+
+
+fun interface UnwrapFun {
+
+ /**
+ * @param code code witch return original type
+ * @return code witch return wanna type
+ */
+ fun formatCode(
+ or: CodeBlock,
+ srcNullable: Boolean,
+ targetNullable: Boolean,
+ ): CodeBlock
+
+
+}
+
+
+fun interface WrapFun {
+
+ /**
+ * @param code code witch return original type
+ * @return code witch return wanna type
+ */
+ fun formatCode(
+ or: CodeBlock,
+ srcNullable: Boolean,
+ targetNullable: Boolean,
+ targetArgTypeNullable: Boolean,
+ ): CodeBlock
+
+
+}
+
+
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/wrap/WrapHelper.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/wrap/WrapHelper.kt
new file mode 100644
index 00000000..5c334bfe
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/wrap/WrapHelper.kt
@@ -0,0 +1,380 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.wrap
+
+import com.github.klee0kai.stone.Provider
+import com.github.klee0kai.stone.weakref.Ref
+import com.github.klee0kai.stone.weakref.SoftRef
+import com.github.klee0kai.stone.weakref.WeakRef
+import com.github.klee0kai.stone.wrappers.AsyncCoroutineProvide
+import com.github.klee0kai.stone.wrappers.FantomAsyncProvide
+import com.github.klee0kai.stone.wrappers.LazyProvide
+import com.github.klee0kai.stone.wrappers.PhantomProvide
+import com.github.klee0kai.thekey.stone.ksp.exceptions.StoneException
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrap.ClassNameUtils.noWildCardType
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrap.ClassNameUtils.rawTypeOf
+import com.github.klee0kai.thekey.stone.ksp.poet.codeBlock
+import com.squareup.kotlinpoet.*
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import java.lang.ref.Reference
+import java.lang.ref.SoftReference
+import java.lang.ref.WeakReference
+import java.util.*
+
+
+class WrapHelper {
+
+ private var wrapTypes = HashMap()
+
+ init {
+ std()
+ }
+
+ fun support(wrapType: WrapType) {
+ wrapTypes.putIfAbsent(wrapType.typeName, wrapType)
+ }
+
+ fun isSupport(typeName: TypeName): Boolean = wrapTypes.containsKey(rawTypeOf(typeName))
+
+ fun isNonCachingWrapper(typeName: TypeName): Boolean {
+ for (t in allParamTypes(typeName)) {
+ val wrapType = wrapTypes.get(rawTypeOf(t))
+ if (wrapType != null && wrapType.isNoCachingWrapper) return true
+ }
+ return false
+ }
+
+ fun isList(typeName: TypeName): Boolean = allParamTypes(typeName).any {
+ val wrapType = wrapTypes.get(rawTypeOf(it))
+ wrapType != null && wrapType.isList
+ }
+
+ fun paramType(typeName: TypeName): TypeName {
+ if (typeName is ParameterizedTypeName) {
+ if (isSupport(typeName.rawType) && !typeName.typeArguments.isEmpty()) {
+ return typeName.typeArguments.first()
+ }
+ }
+ return typeName
+ }
+
+ /**
+ * com.github.klee0kai.stone.wrappers.LazyProvide -> com.github.klee0kai.test.tech.components.Battery
+ * ? extends java.lang.ref.WeakReference -> com.github.klee0kai.test.car.model.Window
+ */
+ fun nonWrappedType(typeName: TypeName): TypeName {
+ if (typeName is ParameterizedTypeName) {
+ if (isSupport(typeName.rawType) && !typeName.typeArguments.isEmpty()) {
+ return nonWrappedType(typeName.typeArguments.first())
+ }
+ }
+ if (typeName is WildcardTypeName) {
+ if (!typeName.outTypes.isEmpty()) return nonWrappedType(typeName.outTypes.first())
+ }
+ return typeName.copy(nullable = false)
+ }
+
+ /**
+ * java.util.List>> ->
+ * java.util.List>
+ */
+ fun listWrapTypeIfNeed(typeName: TypeName): TypeName {
+ if (isList(typeName)) return List::class.asClassName().parameterizedBy(nonWrappedType(typeName))
+ return nonWrappedType(typeName)
+ }
+
+ fun allParamTypes(typeName: TypeName): List {
+ var typeName = noWildCardType(typeName)
+ val allParams = LinkedList()
+ while (true) {
+ allParams.add(typeName)
+ val paramType = typeName as? ParameterizedTypeName
+ if (paramType == null || paramType.typeArguments.isEmpty()) break
+ typeName = noWildCardType(paramType.typeArguments[0])
+ }
+ return allParams
+ }
+
+
+ fun transform(
+ providingType: TypeName,
+ wannaType: TypeName,
+ code: CodeBlock
+ ): CodeBlock {
+ if (providingType == wannaType) return code
+
+ var codeBuilder = CodeBlock.builder().add(code)
+ val wrapPathNames = LinkedList(allParamTypes(wannaType))
+ val unwrapPathNames = LinkedList(allParamTypes(providingType))
+ wrapPathNames.reverse()
+
+ while (!wrapPathNames.isEmpty() && !unwrapPathNames.isEmpty()
+ && unwrapPathNames.last().rawType() == wrapPathNames.first().rawType()
+ ) {
+ unwrapPathNames.pollLast()
+ wrapPathNames.pollFirst()
+ }
+
+ val wrapTypeFormat: (TypeName) -> WrapType = { it: TypeName ->
+ wrapTypes[it.rawType()]
+ ?: throw StoneException(message = "Type Transform non support $providingType -> $wannaType")
+ }
+
+ val unwrapPath = LinkedList(unwrapPathNames.map { wrapTypeFormat(it).copy(typeName = it) })
+ val wrapPath = LinkedList(wrapPathNames.map { wrapTypeFormat(it).copy(typeName = it) })
+
+ var currentNullable = providingType.isNullable
+
+ while (!unwrapPath.isEmpty()) {
+ val unwrapType = unwrapPath.first()
+ if (unwrapType.isList) {
+ val wrapListIndex = wrapPath.indexOfFirst { it.isList }
+ if (wrapListIndex >= 0) {
+ val unWrapItemType = paramType(unwrapPathNames[0])
+ val wrapItemType = paramType(wrapPathNames[wrapListIndex])
+ val wrapListType = wrapPath[wrapListIndex]
+
+ codeBuilder = wrapListType.inListFormat!!.formatCode(
+ originalListType = unwrapType.typeName,
+ or = codeBuilder.build(),
+ itemTransformFun = { listItemCode ->
+ transform(unWrapItemType, wrapItemType, listItemCode)
+ }
+ ).toBuilder()
+
+ for (i in 0..wrapListIndex) {
+ wrapPath.pollFirst()
+ wrapPathNames.pollFirst()
+ }
+ unwrapPath.clear()
+ unwrapPathNames.clear()
+ break
+ }
+ }
+ unwrapPath.pollFirst()
+ unwrapPathNames.pollFirst()
+
+ currentNullable = (
+ wrapPath.firstOrNull()?.typeName?.let { paramType(it) }
+ ?: wannaType)
+ .isNullable
+
+ codeBuilder = unwrapType.unwrap.formatCode(
+ or = codeBuilder.build(),
+ srcNullable = unwrapType.typeName.isNullable,
+ targetNullable = currentNullable
+ ).toBuilder()
+ }
+
+ while (!wrapPath.isEmpty()) {
+ val wrapType = wrapPath.first()
+
+ wrapPath.pollFirst()
+ wrapPathNames.pollFirst()
+
+ codeBuilder = wrapType.wrap.formatCode(
+ codeBuilder.build(),
+ srcNullable = currentNullable,
+ targetNullable = wrapType.typeName.isNullable,
+ targetArgTypeNullable = paramType(wrapType.typeName).isNullable,
+ ).toBuilder()
+
+ currentNullable = wrapType.typeName.isNullable
+ }
+
+ if (currentNullable && !wannaType.isNullable) {
+ codeBuilder.add("!!")
+ }
+
+ return codeBuilder.build()
+ }
+
+ private fun std() {
+ for (cl in listOf(
+ WeakReference::class,
+ SoftReference::class,
+ Reference::class,
+ )) {
+ val wrapper = cl.asClassName()
+ val creator = if (cl != Reference::class) wrapper else WeakReference::class.asClassName()
+
+ val wrapType = WrapType(
+ typeName = wrapper,
+ isNoCachingWrapper = false,
+ wrap = { or, srcNullable, targetNullable, argTypeNullable ->
+ codeBlock {
+ when {
+ !srcNullable -> add("%T( %L )", creator, or)
+ targetNullable -> add("%L?.let{ %T( it ) }", or, creator)
+ else -> add("%T( %L!! )", creator, or)
+ }
+ }
+ },
+ unwrap = { or, srcNullable, targetNullable ->
+ codeBlock {
+ when {
+ targetNullable -> add("%L?.get()", or)
+ else -> add("%L!!.get()!!", or)
+ }
+ }
+ }
+ )
+ support(wrapType)
+ }
+
+
+ for (cl in listOf(
+ WeakRef::class,
+ SoftRef::class,
+ )) {
+ val creator = cl.asClassName()
+
+ val wrapType = WrapType(
+ typeName = creator,
+ isNoCachingWrapper = false,
+ wrap = { or, srcNullable, targetNullable, argTypeNullable ->
+ codeBlock {
+ when {
+ !srcNullable -> add("%T( %L )", creator, or)
+ targetNullable -> add("%L?.let{ %T( it ) }", or, creator)
+ else -> add("%T( %L!! )", creator, or)
+ }
+ }
+ },
+ unwrap = { or, srcNullable, targetNullable ->
+ codeBlock {
+ when {
+ targetNullable -> add("%L?.get()", or)
+ else -> add("%L!!.get()!!", or)
+ }
+ }
+ }
+ )
+ support(wrapType)
+ }
+
+ for (cl in listOf(
+ Lazy::class,
+ )) {
+ val wrapper = cl.asClassName()
+ val wrapType = WrapType(
+ typeName = wrapper,
+ isNoCachingWrapper = false,
+ wrap = { or, srcNullable, targetNullable, argTypeNullable ->
+ codeBlock {
+ when {
+ !srcNullable || argTypeNullable -> add("lazy{ %L } ", or)
+ else -> add("lazy{ %L!! } ", or)
+ }
+ }
+ },
+ unwrap = { or, srcNullable, targetNullable ->
+ codeBlock {
+ when {
+ targetNullable -> add("%L?.value", or)
+ !srcNullable -> add("%L.value", or)
+ else -> add("%L!!.value", or)
+ }
+ }
+ },
+ )
+ support(wrapType)
+ }
+
+ for (cl in listOf(
+ PhantomProvide::class,
+ Ref::class,
+ Provider::class,
+ javax.inject.Provider::class,
+ LazyProvide::class,
+ AsyncCoroutineProvide::class,
+ FantomAsyncProvide::class,
+ )) {
+ val isNoCachingWrapper = cl != LazyProvide::class && cl != AsyncCoroutineProvide::class
+
+ val wrapper = cl.asClassName()
+ val wrapType = WrapType(
+ typeName = wrapper,
+ isNoCachingWrapper = isNoCachingWrapper,
+ wrap = { or, srcNullable, targetNullable, argTypeNullable ->
+ codeBlock {
+ when {
+ !srcNullable || argTypeNullable -> add("%T{ %L }", wrapper, or)
+ else -> add("%T{ %L!! } ", wrapper, or)
+ }
+ }
+ },
+ unwrap = { or, srcNullable, targetNullable ->
+ codeBlock {
+ when {
+ targetNullable -> add("%L?.get()", or)
+ !srcNullable -> add("%L.get()", or)
+ else -> add("%L!!.get()!!", or)
+ }
+ }
+ },
+ )
+ support(wrapType)
+ }
+
+
+
+ for (cl in listOf(
+ LinkedList::class,
+ ArrayList::class,
+ List::class,
+ Collection::class,
+ )) {
+ val wrapper = cl.asClassName()
+ val constructor = when (cl) {
+ LinkedList::class, ArrayList::class -> wrapper
+ else -> null
+ }
+
+ val wrapType = WrapType(
+ typeName = wrapper,
+ isNoCachingWrapper = false,
+ wrap = { or, srcNullable, targetNullable, argTypeNullable ->
+ codeBlock {
+ when {
+ constructor != null && targetNullable -> add("%L?.let { %T(it) }", or, constructor)
+ targetNullable -> add("%L?.let { listOfNotNull( it ) }", or)
+ constructor != null && argTypeNullable -> add("%L!!.let { %T(it) }", or, constructor)
+ constructor != null -> add("%L!!.let { %T(it!!) }", or, constructor)
+ else -> add("listOfNotNull( %L )", or)
+ }
+
+ }
+ },
+ unwrap = { or, srcNullable, targetNullable ->
+ codeBlock {
+ when {
+ targetNullable -> add("%L?.firstOrNull()", or)
+ !srcNullable -> add("%L.first()", or)
+ else -> add("%L!!.first()", or)
+ }
+ }
+ },
+ inListFormat = { originalListType, originalListCode, itemTransformFun ->
+ codeBlock {
+ val itemTransform = itemTransformFun.formatCode(CodeBlock.of("it"))
+ if (itemTransform.toString() == "it") {
+ //no transforms
+ add(originalListCode)
+ } else {
+ add("( %L?.map{ it -> %L } ?: emptyList() )", originalListCode, itemTransform)
+ }
+
+ if (wrapper.rawType() != originalListType.rawType()) {
+ if (constructor != null) {
+ add("!!.let { %T(it) }", constructor)
+ } else {
+ add("!!.toList()")
+ }
+ }
+ }
+ },
+ )
+
+ support(wrapType)
+ }
+ }
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/wrap/WrapType.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/wrap/WrapType.kt
new file mode 100644
index 00000000..f4e01da7
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/helpers/wrap/WrapType.kt
@@ -0,0 +1,14 @@
+package com.github.klee0kai.thekey.stone.ksp.helpers.wrap
+
+import com.squareup.kotlinpoet.TypeName
+
+data class WrapType(
+ val typeName: TypeName,
+ val unwrap: UnwrapFun,
+ val wrap: WrapFun,
+ val isNoCachingWrapper: Boolean = true,
+ val inListFormat: FormatInList? = null,
+) {
+ val isList: Boolean get() = inListFormat != null
+}
+
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/AnnotationProtectedExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/AnnotationProtectedExt.kt
new file mode 100644
index 00000000..0b4b87b3
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/AnnotationProtectedExt.kt
@@ -0,0 +1,44 @@
+package com.github.klee0kai.thekey.stone.ksp.ksp
+
+import com.github.klee0kai.stone.annotations.component.ProtectInjected
+import com.github.klee0kai.stone.annotations.component.SwitchCache
+import com.github.klee0kai.stone.annotations.module.BindInstance
+import com.github.klee0kai.stone.annotations.module.Module
+import com.github.klee0kai.stone.annotations.module.Provide
+
+/**
+ * fix crash at :tests:kspKotlinWasmJs
+ */
+val Provide.cacheProtected get() = runCatching { cache }.getOrNull() ?: Provide.CacheType.Factory
+
+/**
+ * fix crash at :tests:kspKotlinWasmJs
+ */
+val Provide.provideWrapperProtected get() = runCatching { provideWrapper }.getOrNull() ?: Nothing::class
+
+/**
+ * fix crash at :tests:kspKotlinWasmJs
+ */
+val BindInstance.cacheProtected get() = runCatching { cache }.getOrNull() ?: BindInstance.CacheType.Soft
+
+
+/**
+ * fix crash at :tests:kspKotlinWasmJs
+ */
+val SwitchCache.cacheProtected get() = runCatching { cache }.getOrNull() ?: SwitchCache.CacheType.Default
+
+/**
+ * fix crash at :tests:kspKotlinWasmJs
+ */
+val SwitchCache.timeMillisProtected get() = runCatching { timeMillis }.getOrNull() ?: -1
+
+
+/**
+ * fix crash at :tests:kspKotlinWasmJs
+ */
+val ProtectInjected.timeMillisProtected get() = runCatching { timeMillis }.getOrNull() ?: 5000L
+
+/**
+ * fix crash at :tests:kspKotlinWasmJs
+ */
+val Module.genProviderNameProtected get() = runCatching { genProviderName }.getOrNull()
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/FileLocationExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/FileLocationExt.kt
new file mode 100644
index 00000000..9fba373b
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/FileLocationExt.kt
@@ -0,0 +1,32 @@
+package com.github.klee0kai.thekey.stone.ksp.ksp
+
+import com.google.devtools.ksp.symbol.FileLocation
+import com.google.devtools.ksp.symbol.Location
+import java.io.File
+
+fun Location.findText(
+ count: Int = Int.MAX_VALUE,
+): String {
+ if (this is FileLocation) {
+ val file = File(filePath)
+ val lines = file.readLines()
+
+ return lines
+ .drop(lineNumber - 1)
+ .take(count)
+ .joinToString("\n")
+ }
+ return ""
+}
+
+
+fun Location.fileText(): String {
+ if (this is FileLocation) {
+ val file = File(filePath)
+ val lines = file.readLines()
+
+ return lines.joinToString("\n")
+ }
+ return ""
+}
+
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/KSClassDeclarationExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/KSClassDeclarationExt.kt
new file mode 100644
index 00000000..ce07eb96
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/KSClassDeclarationExt.kt
@@ -0,0 +1,118 @@
+package com.github.klee0kai.thekey.stone.ksp.ksp
+
+import com.github.klee0kai.thekey.stone.ksp.utils.removeDoubles
+import com.github.klee0kai.thekey.stone.ksp.utils.then
+import com.google.devtools.ksp.getAllSuperTypes
+import com.google.devtools.ksp.getDeclaredFunctions
+import com.google.devtools.ksp.symbol.*
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.ksp.toClassName
+import com.squareup.kotlinpoet.ksp.toClassNameOrNull
+import kotlin.reflect.KClass
+
+
+fun KSTypeReference.resolveAlias(): KSType = resolve().unwrapAlias()
+
+fun KSTypeReference.resolveNotNullable(): KSType = resolve().makeNotNullable().unwrapAlias()
+
+fun KSType.unwrapAlias(): KSType {
+ var current: KSType = this
+ if (current.declaration is KSTypeAlias) {
+ val alias = current.declaration as KSTypeAlias
+ current = alias.type.resolve().replace(current.arguments)
+ }
+ return current
+}
+
+
+fun KSClassDeclaration.findConstructor(
+ parameters: List,
+): KSFunctionDeclaration? = getDeclaredFunctions()
+ .filter { function ->
+ function.simpleName.asString() == ""
+ && function.parameters.all { it.type.resolveNotNullable() in parameters || it.hasDefault }
+ }.maxByOrNull { it.parameters.size }
+
+fun KSDeclaration.isAnyType(
+ vararg cl: KClass<*>,
+) = cl.any { isType(it) }
+
+fun KSDeclaration.isType(cl: KClass<*>): Boolean = qualifiedName?.asString() == cl.qualifiedName.toString()
+
+fun KSDeclaration.isType(cl: ClassName): Boolean = qualifiedName?.asString() == cl.toString()
+
+fun KSClassDeclaration.getAllMethods(
+ includeObjectMethods: Boolean = false,
+ allowDoubles: Boolean = false,
+ vararg exceptNames: String = emptyArray(),
+): Sequence = sequence {
+ val cl = this@getAllMethods
+ if (!includeObjectMethods && cl.qualifiedName?.asString() in listOf(
+ Object::class.qualifiedName,
+ Any::class.qualifiedName
+ )
+ ) {
+ return@sequence
+ }
+
+ val allMethods = mutableListOf()
+ allMethods.addAll(getDeclaredFunctions())
+
+ getAllSuperTypes().forEach { superType ->
+ allMethods.addAll(
+ (superType.declaration as KSClassDeclaration)
+ .getAllMethods(
+ includeObjectMethods = includeObjectMethods,
+ allowDoubles = allowDoubles,
+ exceptNames = exceptNames,
+ )
+ )
+ }
+
+ yieldAll(
+ allMethods
+ .filter { it.simpleName.asString() !in exceptNames }
+ .then(!allowDoubles) {
+ removeDoubles { it1, it2 ->
+ it1.isSameMethods(it2)
+ }
+ }
+ )
+}
+
+
+fun KSClassDeclaration.isChildOf(
+ parentType: ClassName,
+): Boolean {
+ if (toClassName() == parentType) return true
+ superTypes.forEach { type ->
+ if (type.resolve().toClassNameOrNull() == type) return true
+ if ((type.resolve().declaration as? KSClassDeclaration)?.isChildOf(parentType) == true) return true
+ }
+ return false
+}
+
+val KSType.isUnit: Boolean get() = declaration.qualifiedName?.asString() == "kotlin.Unit"
+
+val KSType.isNotPrimitive: Boolean
+ get() {
+ return declaration.qualifiedName?.asString() !in setOf(
+ "java.lang.Boolean",
+ "java.lang.Byte",
+ "java.lang.Short",
+ "java.lang.Integer",
+ "java.lang.Long",
+ "java.lang.Character",
+ "java.lang.Float",
+ "java.lang.Double",
+ "kotlin.Boolean",
+ "kotlin.Byte",
+ "kotlin.Short",
+ "kotlin.Int",
+ "kotlin.Long",
+ "kotlin.Char",
+ "kotlin.Float",
+ "kotlin.Double",
+ "kotlin.Unit",
+ )
+ }
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/KSFunctionDeclarationExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/KSFunctionDeclarationExt.kt
new file mode 100644
index 00000000..f546e830
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/KSFunctionDeclarationExt.kt
@@ -0,0 +1,46 @@
+package com.github.klee0kai.thekey.stone.ksp.ksp
+
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.google.devtools.ksp.symbol.KSValueParameter
+
+fun KSFunctionDeclaration.isSameMethods(
+ other: KSFunctionDeclaration,
+): Boolean {
+ if (simpleName != other.simpleName
+ || parameters.size != other.parameters.size
+ ) {
+ return false
+ }
+ for (idx in parameters.indices) {
+ if (parameters[idx].type.resolve() != other.parameters[idx].type.resolve()) {
+ return false
+ }
+ }
+
+ return true
+}
+
+fun KSFunctionDeclaration.joinInvokeArguments(
+ availableVariables: List,
+): String {
+ return parameters.mapNotNull { parameter ->
+ val availableVariable = availableVariables
+ .firstOrNull { it.type.resolveNotNullable() == parameter.type.resolveNotNullable() }
+ if (availableVariable != null) {
+ val notNullablePostFix = if (availableVariable.type.resolve().isMarkedNullable
+ && !parameter.type.resolve().isMarkedNullable
+ ) {
+ "!!"
+ } else {
+ ""
+ }
+
+ "${parameter.name!!.asString()} = ${availableVariable.name!!.asString()}${notNullablePostFix}"
+ } else {
+ null
+ }
+ }.joinToString(", ")
+}
+
+fun KSFunctionDeclaration.isClassReturn(
+): Boolean = returnType?.resolve()?.isNotPrimitive ?: false
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/arch/GenSpec.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/arch/GenSpec.kt
new file mode 100644
index 00000000..159da457
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/arch/GenSpec.kt
@@ -0,0 +1,9 @@
+package com.github.klee0kai.thekey.stone.ksp.ksp.arch
+
+import com.google.devtools.ksp.processing.Dependencies
+import com.squareup.kotlinpoet.FileSpec
+
+data class GenSpec(
+ val fileSpec: FileSpec,
+ val dependencies: Dependencies,
+)
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/arch/SymbolsToProcess.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/arch/SymbolsToProcess.kt
new file mode 100644
index 00000000..1f29ef01
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/arch/SymbolsToProcess.kt
@@ -0,0 +1,36 @@
+package com.github.klee0kai.thekey.stone.ksp.ksp.arch
+
+import com.google.devtools.ksp.symbol.KSAnnotated
+
+data class SymbolsToProcess(
+ val symbolsForProcessing: List,
+ val symbolsForReprocessing: List,
+)
+
+fun SymbolsToProcess.nowTakeOnly(
+ takeSymbolsCount: Int,
+): SymbolsToProcess {
+ return copy(
+ symbolsForProcessing = symbolsForProcessing.take(takeSymbolsCount),
+ symbolsForReprocessing = symbolsForReprocessing + symbolsForProcessing.drop(takeSymbolsCount),
+ )
+}
+
+fun SymbolsToProcess.filter(
+ filter: (KSAnnotated) -> Boolean,
+): SymbolsToProcess {
+ return copy(
+ symbolsForProcessing = symbolsForProcessing.filter(filter),
+ symbolsForReprocessing = symbolsForReprocessing.filter(filter),
+ )
+}
+
+fun SymbolsToProcess.forceProcess(
+ filter: (KSAnnotated) -> Boolean = { false },
+): SymbolsToProcess {
+ val symbolsForProcessing = (symbolsForProcessing + symbolsForReprocessing.filter(filter)).toSet()
+ return copy(
+ symbolsForProcessing = symbolsForProcessing.toList(),
+ symbolsForReprocessing = symbolsForReprocessing.filter { it !in symbolsForProcessing }
+ )
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/arch/TargetFileProcessor.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/arch/TargetFileProcessor.kt
new file mode 100644
index 00000000..a200b8be
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/ksp/arch/TargetFileProcessor.kt
@@ -0,0 +1,20 @@
+package com.github.klee0kai.thekey.stone.ksp.ksp.arch
+
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.KSAnnotated
+
+interface TargetFileProcessor {
+
+ suspend fun findSymbolsToProcess(
+ resolver: Resolver,
+ ): SymbolsToProcess
+
+ suspend fun process(
+ validSymbol: KSAnnotated,
+ resolver: Resolver,
+ options: Map,
+ logger: KSPLogger,
+ ): GenSpec?
+
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/AnnotationExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/AnnotationExt.kt
new file mode 100644
index 00000000..b1e9b0ec
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/AnnotationExt.kt
@@ -0,0 +1,40 @@
+package com.github.klee0kai.thekey.stone.ksp.poet
+
+import com.google.devtools.ksp.symbol.KSAnnotation
+import com.google.devtools.ksp.symbol.KSType
+import com.squareup.kotlinpoet.AnnotationSpec
+import com.squareup.kotlinpoet.FunSpec
+import com.squareup.kotlinpoet.ksp.toClassName
+import com.squareup.kotlinpoet.ksp.toTypeName
+
+fun FunSpec.Builder.addAnnotation(
+ ksAnnotation: KSAnnotation,
+): FunSpec.Builder {
+ val typeName = ksAnnotation.annotationType.resolve().toClassName()
+
+ val annotationBuilder = AnnotationSpec.builder(typeName)
+
+ for (arg in ksAnnotation.arguments) {
+ val name = arg.name?.asString() ?: continue
+ val value = arg.value
+
+ // value может быть: primitive, enum, class, array
+ annotationBuilder.addMember("%L = %L", name, formatAnnotationValue(value))
+ }
+
+ return this.addAnnotation(annotationBuilder.build())
+}
+
+fun formatAnnotationValue(
+ value: Any?,
+): String = when (value) {
+ is String -> "\"${value}\""
+ is KSAnnotation -> {
+ val type = value.annotationType.resolve().toTypeName()
+ "@$type"
+ }
+
+ is KSType -> value.toTypeName().toString() + "::class"
+ is List<*> -> value.joinToString(", ", "{", "}") { formatAnnotationValue(it) }
+ else -> value.toString()
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/CodeBlockExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/CodeBlockExt.kt
new file mode 100644
index 00000000..86487e21
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/CodeBlockExt.kt
@@ -0,0 +1,8 @@
+package com.github.klee0kai.thekey.stone.ksp.poet
+
+import com.squareup.kotlinpoet.CodeBlock
+
+@PoetDsl
+fun codeBlock(
+ block: CodeBlock.Builder.() -> Unit,
+) = CodeBlock.builder().apply(block).build()
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/FileSpecDsl.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/FileSpecDsl.kt
new file mode 100644
index 00000000..bfcc3c7e
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/FileSpecDsl.kt
@@ -0,0 +1,79 @@
+package com.github.klee0kai.thekey.stone.ksp.poet
+
+import com.squareup.kotlinpoet.*
+
+@PoetDsl
+fun genFileSpec(
+ packageName: String,
+ fileName: String,
+ block: FileSpec.Builder.() -> Unit,
+): FileSpec {
+ return FileSpec.builder(packageName, fileName)
+ .also(block)
+ .build()
+}
+
+@PoetDsl
+fun FileSpec.Builder.genProperty(
+ name: String,
+ type: TypeName,
+ vararg modifiers: KModifier,
+ block: PropertySpec.Builder.() -> Unit = {}
+) {
+ addProperty(
+ PropertySpec.builder(name, type, *modifiers)
+ .apply(block)
+ .build()
+ )
+}
+
+@PoetDsl
+fun FileSpec.Builder.genClass(
+ className: ClassName,
+ block: TypeSpec.Builder.() -> Unit = {},
+) {
+ addType(
+ TypeSpec.classBuilder(className)
+ .apply(block)
+ .build()
+ )
+}
+
+
+@PoetDsl
+fun FileSpec.Builder.genObject(
+ className: ClassName,
+ block: TypeSpec.Builder.() -> Unit = {},
+) {
+ addType(
+ TypeSpec.objectBuilder(className)
+ .apply(block)
+ .build()
+ )
+}
+
+@PoetDsl
+fun FileSpec.Builder.genInterface(
+ className: ClassName,
+ block: TypeSpec.Builder.() -> Unit = {},
+) {
+ addType(
+ TypeSpec.interfaceBuilder(className)
+ .apply(block)
+ .build()
+ )
+}
+
+@PoetDsl
+fun FileSpec.Builder.genFun(
+ name: String,
+ block: FunSpec.Builder.() -> Unit = {},
+) {
+ addFunction(
+ FunSpec.builder(name)
+ .apply(block)
+ .build()
+ )
+}
+
+
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/FunSpecDsl.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/FunSpecDsl.kt
new file mode 100644
index 00000000..75c53e90
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/FunSpecDsl.kt
@@ -0,0 +1,27 @@
+package com.github.klee0kai.thekey.stone.ksp.poet
+
+import com.squareup.kotlinpoet.CodeBlock
+import com.squareup.kotlinpoet.FunSpec
+
+@PoetDsl
+fun CodeBlock.Builder.controlFlow(
+ controlFlow: String,
+ vararg args: Any,
+ block: CodeBlock.Builder.() -> Unit,
+) = apply {
+ beginControlFlow(controlFlow, *args)
+ .add(CodeBlock.builder().apply(block).build())
+ .endControlFlow()
+ .add("\n")
+}
+
+@PoetDsl
+fun FunSpec.Builder.controlFlow(
+ controlFlow: String,
+ vararg args: Any,
+ block: CodeBlock.Builder.() -> Unit,
+) = apply {
+ beginControlFlow(controlFlow, *args)
+ .addCode(CodeBlock.builder().apply(block).build())
+ .endControlFlow()
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/NamingExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/NamingExt.kt
new file mode 100644
index 00000000..563ac3fa
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/NamingExt.kt
@@ -0,0 +1,7 @@
+package com.github.klee0kai.thekey.stone.ksp.poet
+
+val String.stonePackageName: String
+ get() {
+ return if (endsWith(".stone")) this
+ else "$this.stone"
+ }
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/PoetExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/PoetExt.kt
new file mode 100644
index 00000000..bbce7438
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/PoetExt.kt
@@ -0,0 +1,30 @@
+package com.github.klee0kai.thekey.stone.ksp.poet
+
+import com.github.klee0kai.thekey.stone.ksp.Processor
+import com.squareup.kotlinpoet.CodeBlock
+import com.squareup.kotlinpoet.FileSpec
+import com.squareup.kotlinpoet.ParameterSpec
+import com.squareup.kotlinpoet.PropertySpec
+
+@DslMarker
+annotation class PoetDsl
+
+fun FileSpec.Builder.genLibComment() {
+ addFileComment("Generated by Stone Library\n")
+ addFileComment("Project " + Processor.PROJECT_URL + "\n")
+ addFileComment("Copyright (c) 2022 Andrey Kuzubov")
+}
+
+
+fun PropertySpec.asParameter(): ParameterSpec = ParameterSpec.builder(name, type).build()
+
+fun PropertySpec.Builder.initFromConstructor(): PropertySpec.Builder = apply { initializer(build().name) }
+
+fun Collection.toCodeBlock(): CodeBlock {
+ val blocks = this
+ return CodeBlock.builder().apply {
+ blocks.forEach {
+ add(it)
+ }
+ }.build()
+}
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/PropertySpecDsl.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/PropertySpecDsl.kt
new file mode 100644
index 00000000..2bc38c11
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/PropertySpecDsl.kt
@@ -0,0 +1,29 @@
+package com.github.klee0kai.thekey.stone.ksp.poet
+
+import com.squareup.kotlinpoet.FunSpec
+import com.squareup.kotlinpoet.PropertySpec
+
+@PoetDsl
+fun PropertySpec.Builder.genGetter(
+ block: FunSpec.Builder.() -> Unit = {}
+): FunSpec {
+ return FunSpec.getterBuilder()
+ .apply(block)
+ .build()
+ .also {
+ getter(it)
+ }
+}
+
+
+@PoetDsl
+fun PropertySpec.Builder.genSetter(
+ block: FunSpec.Builder.() -> Unit = {}
+): FunSpec {
+ return FunSpec.setterBuilder()
+ .apply(block)
+ .build()
+ .also {
+ setter(it)
+ }
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/TypeSpecDsl.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/TypeSpecDsl.kt
new file mode 100644
index 00000000..aec3c014
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/poet/TypeSpecDsl.kt
@@ -0,0 +1,107 @@
+package com.github.klee0kai.thekey.stone.ksp.poet
+
+import com.github.klee0kai.thekey.stone.ksp.exceptions.wrapKsNoteInfo
+import com.github.klee0kai.thekey.stone.ksp.target.declareSameParameters
+import com.github.klee0kai.thekey.stone.ksp.target.isSuspend
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.squareup.kotlinpoet.*
+import com.squareup.kotlinpoet.ksp.toTypeName
+
+@PoetDsl
+fun TypeSpec.Builder.genProperty(
+ name: String,
+ type: TypeName,
+ vararg modifiers: KModifier,
+ block: PropertySpec.Builder.() -> Unit = {}
+): PropertySpec {
+ return PropertySpec.builder(name, type, *modifiers)
+ .apply(block)
+ .build()
+ .also {
+ addProperty(it)
+ }
+}
+
+@PoetDsl
+fun TypeSpec.Builder.genClass(
+ className: ClassName,
+ block: TypeSpec.Builder.() -> Unit = {},
+) {
+ addType(
+ TypeSpec.classBuilder(className)
+ .apply(block)
+ .build()
+ )
+}
+
+@PoetDsl
+fun TypeSpec.Builder.genObject(
+ className: ClassName,
+ block: TypeSpec.Builder.() -> Unit = {},
+) {
+ addType(
+ TypeSpec.objectBuilder(className)
+ .apply(block)
+ .build()
+ )
+}
+
+@PoetDsl
+fun TypeSpec.Builder.genInterface(
+ className: ClassName,
+ block: TypeSpec.Builder.() -> Unit = {},
+) {
+ addType(
+ TypeSpec.interfaceBuilder(className)
+ .apply(block)
+ .build()
+ )
+}
+
+@PoetDsl
+fun TypeSpec.Builder.genFun(
+ name: String,
+ block: FunSpec.Builder.() -> Unit = {},
+) {
+ addFunction(
+ FunSpec.builder(name)
+ .apply(block)
+ .build()
+ )
+}
+
+@PoetDsl
+fun TypeSpec.Builder.genOverrideFun(
+ func: KSFunctionDeclaration,
+ block: FunSpec.Builder.() -> Unit = {},
+) {
+ genFun(func.simpleName.asString()) {
+ addModifiers(KModifier.OVERRIDE)
+ if (func.isSuspend) addModifiers(KModifier.SUSPEND)
+ declareSameParameters(func)
+ func.returnType?.resolve()?.toTypeName()?.let { returns(it) }
+ wrapKsNoteInfo(func) { block() }
+ }
+}
+
+@PoetDsl
+fun TypeSpec.Builder.genPrimaryConstructor(
+ block: FunSpec.Builder.() -> Unit = {},
+) {
+ primaryConstructor(
+ FunSpec.constructorBuilder()
+ .apply(block)
+ .build()
+ )
+}
+
+@PoetDsl
+fun TypeSpec.Builder.genConstructor(
+ block: FunSpec.Builder.() -> Unit = {},
+) {
+ addFunction(
+ FunSpec.constructorBuilder()
+ .apply(block)
+ .build()
+ )
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/property/Property.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/property/Property.kt
new file mode 100644
index 00000000..ab53b2e6
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/property/Property.kt
@@ -0,0 +1,71 @@
+package com.github.klee0kai.thekey.stone.ksp.property
+
+import kotlin.reflect.KProperty
+
+class Property(
+ initValue: T
+) {
+ companion object;
+
+ private val subscriptions = mutableListOf<(T) -> Unit>()
+
+ var value: T = initValue
+ set(value) {
+ field = value
+ subscriptions.forEach { it.invoke(value) }
+ }
+
+ private val updateThisValueSubscription: (T) -> Unit = { value = it }
+
+ var source: Property? = null
+ set(value) {
+ field?.unsubscribeChanges(updateThisValueSubscription)
+ field = value
+ field?.subscribeChanges(updateThisValueSubscription)
+ }
+
+ fun subscribeChanges(
+ block: (T) -> Unit,
+ ) {
+ if (block === updateThisValueSubscription) {
+ return
+ }
+ subscriptions += block
+ block(value)
+ }
+
+ fun unsubscribeChanges(block: (T) -> Unit) {
+ subscriptions -= block
+ }
+
+ operator fun getValue(
+ thisRef: Any?,
+ property: KProperty<*>,
+ ): T = value
+
+ operator fun setValue(
+ thisRef: Any?,
+ property: KProperty<*>,
+ newValue: T,
+ ) {
+ value = newValue
+ }
+
+ operator fun setValue(
+ thisRef: Any?,
+ property: KProperty<*>,
+ newValue: Property,
+ ) {
+ source = newValue
+ }
+
+}
+
+fun Property.map(
+ transform: (T) -> R,
+): Property {
+ val prop = Property(transform(value))
+ subscribeChanges { prop.value = transform(it) }
+ return prop
+}
+
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/psi/InMemoryPsiParser.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/psi/InMemoryPsiParser.kt
new file mode 100644
index 00000000..a472d2fa
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/psi/InMemoryPsiParser.kt
@@ -0,0 +1,48 @@
+package com.github.klee0kai.thekey.stone.ksp.psi
+
+import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import org.jetbrains.kotlin.com.intellij.openapi.Disposable
+import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.psi.KtPsiFactory
+import java.lang.AutoCloseable
+
+class InMemoryPsiParser(
+ val fileCode: String,
+) : AutoCloseable {
+ val disposable: Disposable = Disposer.newDisposable()
+
+ val configuration = CompilerConfiguration()
+ val environment = KotlinCoreEnvironment.createForProduction(
+ disposable,
+ configuration,
+ EnvironmentConfigFiles.JVM_CONFIG_FILES
+ )
+
+ val project = environment.project
+ val psiFactory = KtPsiFactory(project, markGenerated = false)
+
+ val ktFile = psiFactory.createFile(fileCode)
+
+ val document = ktFile.containingFile.viewProvider.document
+
+ override fun close() {
+ Disposer.dispose(disposable)
+ }
+
+}
+
+
+fun PsiElement.fileNumber(): Int {
+ val document = containingFile.viewProvider.document
+
+ if (document != null) {
+ val line = document.getLineNumber(textOffset)
+ val column = textOffset - document.getLineStartOffset(line)
+
+ return column + 1
+ }
+ return -1
+}
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/psi/PsiKspExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/psi/PsiKspExt.kt
new file mode 100644
index 00000000..e91092ce
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/psi/PsiKspExt.kt
@@ -0,0 +1,15 @@
+package com.github.klee0kai.thekey.stone.ksp.psi
+
+import com.google.devtools.ksp.symbol.FileLocation
+import com.google.devtools.ksp.symbol.KSNode
+import org.jetbrains.kotlin.com.intellij.psi.impl.PsiElementBase
+
+fun PsiElementBase.isSamePlace(otherElement: KSNode): Boolean {
+ val document = containingFile.containingFile.viewProvider.document
+ val kspElementLine = ((otherElement.location as? FileLocation)?.lineNumber ?: 1) - 1
+
+ val psiFunLineStart = document.getLineNumber(textRange.startOffset)
+ val psiFunLineEnd = document.getLineNumber(textRange.endOffset)
+
+ return kspElementLine in psiFunLineStart..psiFunLineEnd
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/CommonPoetExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/CommonPoetExt.kt
new file mode 100644
index 00000000..cb5ea061
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/CommonPoetExt.kt
@@ -0,0 +1,42 @@
+package com.github.klee0kai.thekey.stone.ksp.target
+
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.google.devtools.ksp.symbol.Modifier
+import com.squareup.kotlinpoet.*
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.ksp.toTypeName
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Job
+
+val KSFunctionDeclaration.isSuspend: Boolean get() = modifiers.contains(Modifier.SUSPEND)
+
+val KSFunctionDeclaration.asyncReturnType: TypeName?
+ get() {
+ val returnType = returnType?.resolve()?.toTypeName()
+ return when {
+ returnType != null && !isSuspend -> returnType
+ returnType != null && returnType != Unit::class.asClassName() ->
+ Deferred::class.asClassName().parameterizedBy(returnType)
+
+ isSuspend -> Job::class.asClassName()
+ else -> returnType
+ }
+ }
+
+fun FunSpec.Builder.declareSameParameters(
+ function: KSFunctionDeclaration,
+) = apply {
+ function.returnType?.resolve()?.toTypeName()?.let { returns(it) }
+ function.extensionReceiver?.resolve()?.toTypeName()?.let { receiver(it) }
+
+ function.parameters.forEach { param ->
+ addParameter(
+ ParameterSpec.builder(
+ name = param.name?.asString() ?: "",
+ type = param.type.resolve().toTypeName(),
+ ).apply {
+ if (param.isVararg) addModifiers(KModifier.VARARG)
+ }.build()
+ )
+ }
+}
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/component/ComponentGraphExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/component/ComponentGraphExt.kt
new file mode 100644
index 00000000..97b4b8ca
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/component/ComponentGraphExt.kt
@@ -0,0 +1,75 @@
+package com.github.klee0kai.thekey.stone.ksp.target.component
+
+import com.github.klee0kai.thekey.stone.ksp.exceptions.forEachFun
+import com.github.klee0kai.thekey.stone.ksp.helpers.allIdentifierTypes
+import com.github.klee0kai.thekey.stone.ksp.helpers.allParentDeclarations
+import com.github.klee0kai.thekey.stone.ksp.helpers.annotations.findComponentAnnotation
+import com.github.klee0kai.thekey.stone.ksp.helpers.invokecall.ModulesGraph
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrap.WrapHelper
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrap.WrapType
+import com.github.klee0kai.thekey.stone.ksp.ksp.getAllMethods
+import com.github.klee0kai.thekey.stone.ksp.ksp.resolveNotNullable
+import com.github.klee0kai.thekey.stone.ksp.poet.codeBlock
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.squareup.kotlinpoet.ksp.toClassName
+
+fun KSClassDeclaration.collectWrapHelper(
+): WrapHelper {
+ val wrapHelper = WrapHelper()
+ val wrapperHelpers = allParentDeclarations
+ .filter { it.findComponentAnnotation().any() }
+ .flatMap { parentComponentCl ->
+ parentComponentCl.findComponentAnnotation().firstOrNull()?.wrapperHelpers ?: emptyList()
+ }
+
+ wrapperHelpers.forEachIndexed { _, wrapperHelperCl ->
+ val wrapperHelperClDec = wrapperHelperCl.declaration as? KSClassDeclaration ?: return@forEachIndexed
+ val methods = wrapperHelperClDec.getAllMethods(false, false, "")
+ methods.forEachFun { funIdx, m ->
+ val inputType = (m.parameters.firstOrNull()
+ ?.type?.resolveNotNullable()
+ ?.declaration as? KSClassDeclaration)
+ ?.toClassName() ?: return@forEachFun
+
+ val outputType = (m.returnType?.resolveNotNullable()
+ ?.declaration as? KSClassDeclaration)
+ ?.toClassName() ?: return@forEachFun
+
+ wrapHelper.support(
+ WrapType(
+ isNoCachingWrapper = false,
+ typeName = outputType,
+ wrap = { or, srcNullable, targetNullable, argTypeNullable ->
+ codeBlock {
+ add(
+ "%T.%L{ %L!! }",
+ wrapperHelperClDec.toClassName(),
+ m.simpleName.asString(),
+ or,
+ )
+ }
+ },
+ unwrap = { or, srcNullable, targetNullable ->
+ codeBlock {
+
+ }
+ }
+ )
+ )
+ }
+ }
+
+ return wrapHelper
+}
+
+
+fun KSClassDeclaration.collectComponentGraph(
+
+): ModulesGraph {
+ val modulesGraph = ModulesGraph(
+ wrapHelper = collectWrapHelper(),
+ identifierTypes = allIdentifierTypes.toList(),
+ )
+ modulesGraph.collectFromComponent(this)
+ return modulesGraph
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/component/ComponentsMethodsExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/component/ComponentsMethodsExt.kt
new file mode 100644
index 00000000..c1ffa260
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/component/ComponentsMethodsExt.kt
@@ -0,0 +1,308 @@
+package com.github.klee0kai.thekey.stone.ksp.target.component
+
+import com.github.klee0kai.stone.annotations.component.*
+import com.github.klee0kai.stone.annotations.dependencies.Dependencies
+import com.github.klee0kai.stone.annotations.module.BindInstance
+import com.github.klee0kai.stone.annotations.module.Module
+import com.github.klee0kai.thekey.stone.ksp.exceptions.IncorrectSignatureException
+import com.github.klee0kai.thekey.stone.ksp.helpers.annotations.annotations
+import com.github.klee0kai.thekey.stone.ksp.helpers.annotations.anyAnnotation
+import com.github.klee0kai.thekey.stone.ksp.helpers.annotations.hasOnlyAnnotation
+import com.github.klee0kai.thekey.stone.ksp.helpers.annotations.stoneControlAnnotations
+import com.github.klee0kai.thekey.stone.ksp.helpers.scopeAnnotations
+import com.github.klee0kai.thekey.stone.ksp.ksp.isClassReturn
+import com.github.klee0kai.thekey.stone.ksp.ksp.isNotPrimitive
+import com.github.klee0kai.thekey.stone.ksp.ksp.isUnit
+import com.github.klee0kai.thekey.stone.ksp.ksp.resolveNotNullable
+import com.github.klee0kai.thekey.stone.ksp.target.module.GenModuleProcessor
+import com.google.devtools.ksp.getAllSuperTypes
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.squareup.kotlinpoet.asClassName
+import com.squareup.kotlinpoet.ksp.toClassName
+import com.squareup.kotlinpoet.ksp.toTypeName
+
+enum class BindInstanceType {
+ BindInstance,
+ BindInstanceAndProvide
+}
+
+val KSFunctionDeclaration.isModuleFactoryProvideMethod: Boolean
+ get() {
+ if (!isProvideMethodSimple) return false
+ val moduleCl = returnType?.resolve()?.declaration as? KSClassDeclaration ?: return false
+ if (!moduleCl.annotations(Module::class.asClassName()).any()) return false
+ if (!annotations(ModuleOriginFactory::class.asClassName()).any()) return false
+
+ if (parameters.isNotEmpty()) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must no have arguments",
+ element = this,
+ )
+ }
+ if (!hasOnlyAnnotation(ModuleOriginFactory::class.asClassName())) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must have only one annotation ${ModuleOriginFactory::class.simpleName}",
+ element = this,
+ )
+ }
+ checkMethodNameBusy()
+
+ return true
+ }
+
+val KSFunctionDeclaration.isModuleProvideMethod: Boolean
+ get() {
+ if (!isProvideMethodSimple) return false
+ val moduleCl = returnType?.resolve()?.declaration as? KSClassDeclaration ?: return false
+ if (!moduleCl.annotations(Module::class.asClassName()).any()) return false
+ if (annotations(ModuleOriginFactory::class.asClassName()).any()) return false
+
+ if (parameters.isNotEmpty()) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must no have arguments",
+ element = this,
+ )
+ }
+ if (annotations.any()) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must no have annotations",
+ element = this,
+ )
+ }
+ checkMethodNameBusy()
+
+ return true
+ }
+
+val KSFunctionDeclaration.isDepsProvideMethod: Boolean
+ get() {
+ if (!isProvideMethodSimple) return false
+ val depCl = returnType?.resolve()?.declaration as? KSClassDeclaration ?: return false
+ if (!depCl.annotations(Dependencies::class.asClassName()).any()) return false
+ if (parameters.isNotEmpty()) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must no have arguments",
+ element = this,
+ )
+ }
+ if (annotations.any()) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must no have annotations",
+ element = this,
+ )
+ }
+ checkMethodNameBusy()
+ return true
+ }
+
+val KSFunctionDeclaration.isObjectProvideMethod: Boolean
+ get() = isProvideMethodSimple
+ && !isModuleProvideMethod
+ && !isDepsProvideMethod
+ && !isModuleFactoryProvideMethod
+ && stoneControlAnnotations().none()
+
+val KSFunctionDeclaration.isModuleInitMethod: Boolean
+ get() {
+ if (!annotations(Init::class.asClassName()).any()) return false
+
+ if (!hasOnlyAnnotation(Init::class.asClassName())) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must have only one annotation ${Init::class.simpleName}",
+ element = this,
+ )
+ }
+ if (returnType?.resolve()?.isUnit == false) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must return unit",
+ element = this,
+ )
+ }
+
+ parameters.forEach {
+ val clDeclaration = it.type
+ .resolve()
+ .declaration as? KSClassDeclaration
+ ?: throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must have only one parameter of Dependency or Module instance",
+ element = this,
+ )
+
+
+ if (!clDeclaration.anyAnnotation(Module::class.asClassName(), Dependencies::class.asClassName()).any()) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must have only one parameter of Dependency or Module instance",
+ element = this,
+ )
+ }
+ }
+ checkMethodNameBusy()
+ return true
+ }
+
+fun KSFunctionDeclaration.isExtOfMethod(
+ clOwner: KSClassDeclaration,
+): Boolean {
+ if (!annotations(ExtendOf::class.asClassName()).any()) return false
+
+ if (!hasOnlyAnnotation(ExtendOf::class.asClassName())) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must have only one annotation ${ExtendOf::class.simpleName}",
+ element = this,
+ )
+ }
+ if (parameters.size != 1) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must have only one parameter of Component instance",
+ element = this,
+ )
+ }
+ if (returnType?.resolve()?.isUnit == false) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must return unit",
+ element = this,
+ )
+ }
+ val argumentType = parameters.firstOrNull()?.type
+ ?.resolveNotNullable()?.declaration as? KSClassDeclaration
+ ?: throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must have only one parameter of Component instance",
+ element = this,
+ )
+
+ if (!argumentType.annotations(Component::class.asClassName()).any()) {
+ throw IncorrectSignatureException(
+ message = "${argumentType.simpleName.asString()} must have @Component annotation",
+ element = this,
+ )
+ }
+ if (!clOwner.getAllSuperTypes().any { parent -> parent.toTypeName() == argumentType.toClassName() }) {
+ throw IncorrectSignatureException(
+ message = "The argument for the method ${simpleName.asString()} must be the parent class of the class ${clOwner.toClassName()}. " +
+ "The class ${argumentType.toClassName()} is not a parent to the class ${clOwner.toClassName()}.",
+ element = this,
+ )
+ }
+
+ checkMethodNameBusy()
+ return true
+}
+
+
+val KSFunctionDeclaration.isBindInstanceMethod: BindInstanceType?
+ get() {
+ if (!annotations(BindInstance::class.asClassName()).any()) return null
+
+ if (parameters.size != 1) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must have only one parameter of binding type",
+ element = this,
+ )
+ }
+ checkMethodNameBusy()
+ val parameterIsNotPrimitive = parameters.first().type.resolveNotNullable().isNotPrimitive
+
+ when {
+ parameterIsNotPrimitive
+ && returnType?.resolveNotNullable()?.toTypeName() == parameters.first().type.resolveNotNullable().toTypeName() -> {
+ return BindInstanceType.BindInstanceAndProvide
+ }
+
+ parameterIsNotPrimitive -> {
+ return BindInstanceType.BindInstance
+ }
+
+
+ }
+
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} has incorrect signature",
+ element = this,
+ )
+ }
+
+val KSFunctionDeclaration.isGcMethod: Boolean
+ get() {
+ if (!annotations(RunGc::class.asClassName()).any()) return false
+ if (!scopeAnnotations.any()) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must use GC scope annotation",
+ element = this,
+ )
+ }
+
+ if (returnType?.resolve()?.isUnit == false) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must have return type is Unit",
+ element = this,
+ )
+ }
+
+ checkMethodNameBusy()
+ return true
+ }
+
+
+val KSFunctionDeclaration.isSwitchCacheMethod: Boolean
+ get() {
+ if (!annotations(SwitchCache::class.asClassName()).any()) return false
+
+ if (parameters.isNotEmpty()) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must no have arguments",
+ element = this,
+ )
+ }
+ if (returnType?.resolve()?.isUnit == false) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must have return type is Unit",
+ element = this,
+ )
+ }
+ checkMethodNameBusy()
+ return true
+ }
+
+val KSFunctionDeclaration.isInjectMethod: Boolean
+ get() {
+ if (annotations.any()) return false
+ if (returnType?.resolve()?.isUnit == false) return false
+ if (parameters.isEmpty()) return false
+ checkMethodNameBusy()
+ return true
+ }
+
+val KSFunctionDeclaration.isProtectInjectedMethod: Boolean
+ get() {
+ if (!annotations(ProtectInjected::class.asClassName()).any()) return false
+ if (returnType?.resolve()?.isUnit == false) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must have return type is Unit",
+ element = this,
+ )
+ }
+ if (parameters.size != 1) {
+ throw IncorrectSignatureException(
+ message = "${simpleName.asString()} must have only one parameter",
+ element = this,
+ )
+ }
+ checkMethodNameBusy()
+ return true
+ }
+
+
+fun KSFunctionDeclaration.checkMethodNameBusy() {
+ val reserved =
+ simpleName.asString() in GenModuleProcessor.allReserveMethodNames + GenComponentProcessor.allReserveMethodNames
+ if (reserved) {
+ throw IncorrectSignatureException(
+ message = "Function name ${simpleName.asString()} is reserved by stone library",
+ element = this,
+ )
+ }
+}
+
+
+private val KSFunctionDeclaration.isProvideMethodSimple get() = isClassReturn()
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/component/GenComponentProcessor.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/component/GenComponentProcessor.kt
new file mode 100644
index 00000000..a894571a
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/component/GenComponentProcessor.kt
@@ -0,0 +1,848 @@
+@file:OptIn(KspExperimental::class)
+
+package com.github.klee0kai.thekey.stone.ksp.target.component
+
+import com.github.klee0kai.stone.__hidden__.*
+import com.github.klee0kai.stone.__hidden__.collections.RefCollection
+import com.github.klee0kai.stone.__hidden__.types.WeakList
+import com.github.klee0kai.stone.__hidden__.types.holders.TimeHolder
+import com.github.klee0kai.stone.annotations.component.Component
+import com.github.klee0kai.stone.annotations.component.ProtectInjected
+import com.github.klee0kai.stone.annotations.component.SwitchCache
+import com.github.klee0kai.stone.annotations.dependencies.Dependencies
+import com.github.klee0kai.stone.annotations.module.Module
+import com.github.klee0kai.stone.Inject
+import com.github.klee0kai.stone.weakref.Memory
+import com.github.klee0kai.thekey.stone.ksp.exceptions.IncorrectSignatureException
+import com.github.klee0kai.thekey.stone.ksp.exceptions.ObjectNotProvidedException
+import com.github.klee0kai.thekey.stone.ksp.exceptions.forEachFun
+import com.github.klee0kai.thekey.stone.ksp.helpers.*
+import com.github.klee0kai.thekey.stone.ksp.helpers.annotations.anyAnnotation
+import com.github.klee0kai.thekey.stone.ksp.helpers.invokecall.ModulesGraph
+import com.github.klee0kai.thekey.stone.ksp.helpers.invokecall.model.toFieldDetail
+import com.github.klee0kai.thekey.stone.ksp.helpers.invokecall.model.toQualifierAnn
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrap.WrapHelper
+import com.github.klee0kai.thekey.stone.ksp.ksp.*
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.GenSpec
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.SymbolsToProcess
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.TargetFileProcessor
+import com.github.klee0kai.thekey.stone.ksp.poet.*
+import com.github.klee0kai.thekey.stone.ksp.target.module.GenModuleProcessor
+import com.google.devtools.ksp.KspExperimental
+import com.google.devtools.ksp.containingFile
+import com.google.devtools.ksp.getAnnotationsByType
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.ClassKind
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.squareup.kotlinpoet.*
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.ksp.toClassName
+import com.squareup.kotlinpoet.ksp.toTypeName
+import kotlinx.coroutines.CoroutineScope
+import kotlin.reflect.KClass
+import com.google.devtools.ksp.processing.Dependencies as KspDependencies
+
+class GenComponentProcessor : TargetFileProcessor {
+
+ companion object {
+ val refCollectionGlFieldName = "__refCollection"
+ val scopeFieldName = "__scope"
+ val hiddenModuleFieldName = "__hiddenModule"
+ val relatedComponentsListFieldName = "__related"
+ val protectRecursiveField = "__protectRecursive"
+ val eachModuleMethodName = "__eachModule"
+ val initMethodName = "__init"
+ val initDepsMethodName = "__initDependencies"
+ val bindMethodName = "__bind"
+ val extOfMethodName = "__extOf"
+
+ val allReserveMethodNames = listOf(
+ refCollectionGlFieldName,
+ scopeFieldName,
+ hiddenModuleFieldName,
+ relatedComponentsListFieldName,
+ protectRecursiveField,
+ eachModuleMethodName,
+ initMethodName,
+ initDepsMethodName,
+ bindMethodName,
+ extOfMethodName,
+ )
+ }
+
+ private class DelayedCodeBlocks(
+ val initDepsMethodBody: CodeBlock.Builder = CodeBlock.builder(),
+ )
+
+ override suspend fun findSymbolsToProcess(
+ resolver: Resolver,
+ ) = SymbolsToProcess(
+ symbolsForProcessing = resolver
+ .getSymbolsWithAnnotation(Component::class.asClassName().canonicalName)
+ .toList(),
+ symbolsForReprocessing = emptyList(),
+ )
+
+ override suspend fun process(
+ validSymbol: KSAnnotated,
+ resolver: Resolver,
+ options: Map,
+ logger: KSPLogger
+ ): GenSpec? {
+
+ val fileOwner = validSymbol.containingFile ?: return null
+ val componentCl = validSymbol as? KSClassDeclaration ?: return null
+
+ val wrapHelper = componentCl.collectWrapHelper()
+ val modulesGraph = componentCl.collectComponentGraph()
+ val delayedCodeBlocks = DelayedCodeBlocks()
+
+ val genComponentClassName = componentCl.componentStoneClName
+
+ val fileSpec = genFileSpec(genComponentClassName.packageName, genComponentClassName.simpleName) {
+ genLibComment()
+
+ genClass(genComponentClassName) {
+ if (componentCl.classKind == ClassKind.INTERFACE) {
+ addSuperinterface(componentCl.toClassName())
+ } else {
+ superclass(componentCl.toClassName())
+ }
+ addSuperinterface(IPrivateComponent::class.asClassName())
+
+ val componentsAllMethods = componentCl
+ .getAllMethods(includeObjectMethods = false, allowDoubles = false, "")
+
+ componentsAllMethods.forEachFun { _, m ->
+ when {
+ m.isModuleProvideMethod -> {
+ val moduleCl = m.returnType?.resolve()?.declaration as? KSClassDeclaration
+ ?: throw IncorrectSignatureException(
+ message = "wrong return type. Must by Module type",
+ element = m,
+ )
+ genProperty(m.simpleName.asString(), moduleCl.moduleStoneClName) {
+ addModifiers(KModifier.PRIVATE)
+ initializer("%T()", moduleCl.moduleStoneClName)
+ }
+ genOverrideFun(m) {
+ returns(moduleCl.moduleStoneClName)
+ addStatement("return %L", m.simpleName.asString())
+ }
+ }
+
+ m.isModuleFactoryProvideMethod -> {
+ val providingModuleFun = componentsAllMethods
+ .filter { it.isModuleProvideMethod }
+ .firstOrNull {
+ it.returnType?.resolve()?.toTypeName() == m.returnType?.resolve()?.toTypeName()
+ }
+ ?: throw IncorrectSignatureException(
+ message = "Component must also have providing module simple method with same type",
+ element = m.returnType,
+ )
+ genOverrideFun(m) {
+ addStatement(
+ "return %L.%L",
+ providingModuleFun.simpleName.asString(),
+ GenModuleProcessor.factoryFieldName
+ )
+ }
+ }
+
+ m.isDepsProvideMethod -> {
+ val depType = m.returnType!!.resolveNotNullable().toClassName()
+ genProperty(m.simpleName.asString(), depType.copy(nullable = true)) {
+ addModifiers(KModifier.PRIVATE)
+ mutable(true)
+ initializer("null")
+ }
+ genOverrideFun(m) {
+ returns(depType)
+ addStatement("return %L!!", m.simpleName.asString())
+ }
+
+ delayedCodeBlocks.initDepsMethodBody.addStatement(
+ "if (m is %T) this.%L = m", depType, m.simpleName.asString(),
+ )
+ }
+
+ m.isModuleInitMethod -> {
+ genOverrideFun(m) {
+ m.parameters.forEach { param ->
+ val paramType = param.type.resolve()
+ .declaration as? KSClassDeclaration
+ ?: throw IncorrectSignatureException(
+ message = "wrong return type. Must by Module type",
+ element = param,
+ )
+ when {
+ paramType.anyAnnotation(
+ Module::class.asClassName(),
+ Component::class.asClassName()
+ ).any() -> {
+ addStatement(
+ "%L?.let{ %L( %L ) }",
+ param.name!!.asString(),
+ initMethodName,
+ param.name!!.asString()
+ )
+ }
+
+ paramType.anyAnnotation(
+ Dependencies::class.asClassName(),
+ ).any() -> {
+ addStatement(
+ "%L?.let{ %L( %L ) }",
+ param.name!!.asString(),
+ initDepsMethodName,
+ param.name!!.asString()
+ )
+ }
+
+ else -> {
+ throw IncorrectSignatureException(
+ message = "wrong return type. Must by Module type",
+ element = param,
+ )
+ }
+ }
+
+ }
+ }
+ }
+
+ m.isExtOfMethod(componentCl) -> {
+ genOverrideFun(m) {
+ addCode(
+ "(%L as? %T)?.let{ %L(it) }",
+ parameters.first().name,
+ IPrivateComponent::class.asClassName(),
+ extOfMethodName
+ )
+ }
+ }
+
+ m.isObjectProvideMethod -> {
+ genProvideObjMethod(
+ componentCl = componentCl,
+ method = m,
+ modulesGraph = modulesGraph,
+ )
+ }
+
+ m.isBindInstanceMethod != null -> {
+ genBindInstanceMethod(
+ componentCl = componentCl,
+ method = m,
+ modulesGraph = modulesGraph,
+ wrapHelper = wrapHelper,
+ )
+ }
+
+ m.isGcMethod -> {
+ genGcMthod(
+ componentCl = componentCl,
+ method = m,
+ wrapHelper = wrapHelper,
+ )
+ }
+
+ m.isSwitchCacheMethod -> {
+ genSwitchRefMethod(
+ componentCl = componentCl,
+ method = m,
+ )
+ }
+
+ m.isInjectMethod -> {
+ genInjectMethod(
+ componentCl = componentCl,
+ method = m,
+ wrapHelper = wrapHelper,
+ modulesGraph = modulesGraph,
+ )
+ }
+
+ m.isProtectInjectedMethod -> {
+ genProtectInjected(
+ componentCl = componentCl,
+ method = m,
+ wrapHelper = wrapHelper,
+ )
+ }
+
+ m.isAbstract -> {
+ throw IncorrectSignatureException(
+ message = "What is purpose for Method ${m.simpleName.asString()}. " +
+ "Declared in ${componentCl.simpleName.asString()} ",
+ element = m,
+ )
+ }
+ }
+ }
+
+ genIComponentMethods(
+ componentCl = componentCl,
+ delayedCodeBlocks = delayedCodeBlocks,
+
+ )
+ }
+ }
+
+ return GenSpec(
+ fileSpec = fileSpec,
+ // https://kotlinlang.org/docs/ksp-incremental.html
+ dependencies = KspDependencies(aggregating = false, fileOwner),
+ )
+ }
+
+ private fun TypeSpec.Builder.genProvideObjMethod(
+ componentCl: KSClassDeclaration,
+ method: KSFunctionDeclaration,
+ modulesGraph: ModulesGraph,
+ ) {
+ val returnType = method.returnType?.resolveAlias()?.toTypeName() ?: return
+
+ val codeBlock = modulesGraph.codeProvideType(
+ methodName = null,
+ returnType = returnType,
+ qualifierAnns = method.qualifierAnnotations.map { it.toQualifierAnn() }.toSet(),
+ declaredFields = method.parameters.map { it.toFieldDetail() },
+ )
+
+ if (codeBlock == null) {
+ throw ObjectNotProvidedException(
+ message = "Error provide type ${returnType}. " +
+ "Required in ${componentCl.toClassName()}.${method.simpleName.asString()}",
+ element = method,
+ )
+ }
+
+ genOverrideFun(method) {
+ addStatement("return %L", codeBlock)
+ }
+
+ }
+
+
+ private fun TypeSpec.Builder.genBindInstanceMethod(
+ componentCl: KSClassDeclaration,
+ method: KSFunctionDeclaration,
+ modulesGraph: ModulesGraph,
+ wrapHelper: WrapHelper,
+ ) {
+ val returnType = method.returnType?.resolveAlias()?.toTypeName() ?: return
+ val identifierTypes = componentCl.allIdentifierTypes.toList()
+
+ val setValueArg = method.parameters.firstOrNull { it.type.resolveNotNullable() !in identifierTypes }
+ ?: throw IncorrectSignatureException(
+ message = "Bind instance method must have bind instance arcgument",
+ element = method,
+ )
+
+ val nonWrappedBindType = wrapHelper.nonWrappedType(setValueArg.type.resolve().toTypeName())
+ val isProvideMethod = wrapHelper
+ .nonWrappedType(method.returnType!!.resolve().toTypeName()) == nonWrappedBindType
+ val hidingProvideName = if (isProvideMethod) method.simpleName.asString() else null
+
+ // bind object declared in module
+ val cacheControlInvoke = modulesGraph.invokeControlCacheForType(
+ hidingProvideName,
+ nonWrappedBindType,
+ method.qualifierAnnotations.map { it.toQualifierAnn() }.toSet()
+ )
+
+ val isListCache = wrapHelper.isList(cacheControlInvoke!!.rawReturnType())
+ val cacheControlType = if (isListCache) {
+ List::class.asClassName()
+ .parameterizedBy(nonWrappedBindType.copy(nullable = true))
+ .copy(nullable = true)
+ } else {
+ nonWrappedBindType.copy(nullable = true)
+ }
+
+ // bind object declared in module
+ genOverrideFun(method) {
+ addCode(
+ cacheControlInvoke.invokeCode(
+ envFields = method.parameters.map { it.toFieldDetail() },
+ argGen = { _ ->
+ codeBlock {
+ add("%T.setValueAction(", CacheAction::class)
+ add(
+ wrapHelper.transform(
+ setValueArg.type.resolve().toTypeName(),
+ cacheControlType,
+ CodeBlock.of(setValueArg.name!!.asString())
+ )
+ )
+ add(")")
+ }
+ }
+ )
+ )
+ addStatement("")
+
+
+ addStatement(
+ "%L{ module -> module.%L( %L ); } ",
+ eachModuleMethodName,
+ GenModuleProcessor.updateBindInstancesFrom,
+ cacheControlInvoke.bestSequence().first().methodName,
+ )
+ addStatement("")
+
+ if (isProvideMethod) {
+ addCode("return ")
+ addCode(
+ modulesGraph.codeProvideType(
+ hidingProvideName,
+ returnType,
+ method.qualifierAnnotations.map { it.toQualifierAnn() }.toSet(),
+ method.parameters.map { it.toFieldDetail() },
+ )!!
+ )
+ addCode("\n")
+ }
+ }
+
+ }
+
+
+ private fun TypeSpec.Builder.genInjectMethod(
+ componentCl: KSClassDeclaration,
+ method: KSFunctionDeclaration,
+ wrapHelper: WrapHelper,
+ modulesGraph: ModulesGraph,
+ ) {
+ val identifierTypes = componentCl.allIdentifierTypes.toList()
+ val lifeCycleOwnerArg = method.parameters.lifeCycleParameter()
+ val injectableArguments = method.parameters.notIdentifierParameters(identifierTypes)
+ if (injectableArguments.isEmpty()) {
+ throw IncorrectSignatureException(
+ message = "No injectable parameter at ${method.simpleName.asString()}",
+ element = method,
+ )
+ }
+
+ genOverrideFun(method) {
+ for (injectableField in injectableArguments) {
+ val injectableCl = injectableField.type.resolveNotNullable().declaration as? KSClassDeclaration
+ ?: throw IncorrectSignatureException(
+ message = "parameter must be a class",
+ element = injectableField,
+ )
+
+
+ for (injectField in injectableCl.getAllProperties()) {
+ if (!injectField.anyAnnotation(
+ Inject::class.asClassName(),
+ javax.inject.Inject::class.asClassName()
+ ).any()
+ ) continue
+
+ val provideCode = modulesGraph.codeProvideType(
+ methodName = null,
+ returnType = injectField.type.resolveAlias().toTypeName(),
+ qualifierAnns = injectField.qualifierAnnotations.map { it.toQualifierAnn() }.toSet(),
+ declaredFields = method.parameters.map { it.toFieldDetail() },
+ )
+
+ if (provideCode == null) {
+ wrapHelper.nonWrappedType(injectField.type.resolveNotNullable().toTypeName())
+ throw ObjectNotProvidedException(
+ message = "Error provide type ${injectField.type.resolveNotNullable().toTypeName()}. " +
+ "Required in ${injectableCl.toClassName()}.${injectField.simpleName.asString()}",
+ element = method,
+ )
+ }
+
+ addCode(
+ "%L?.%L = ",
+ injectableField.name!!.asString(),
+ injectField.simpleName.asString(),
+ )
+ addCode(provideCode)
+ addStatement("")
+ }
+
+ for (injectMethod in injectableCl.getAllMethods(false, false, "")) {
+ if (!injectMethod.anyAnnotation(
+ Inject::class.asClassName(),
+ javax.inject.Inject::class.asClassName()
+ ).any()
+ ) continue
+ val providingArgsCode = CodeBlock.builder()
+ for (injectField in injectMethod.parameters) {
+ val provideCode = modulesGraph.codeProvideType(
+ null,
+ injectField.type.resolveAlias().toTypeName(),
+ injectField.qualifierAnnotations.map { it.toQualifierAnn() }.toSet(),
+ method.parameters.map { it.toFieldDetail() },
+ )
+
+ if (provideCode == null) {
+ throw ObjectNotProvidedException(
+ message = "Error provide type ${injectField.type.resolveAlias().toTypeName()}. " +
+ "Required in ${injectableCl.toClassName()}.${injectMethod.simpleName.asString()}",
+ element = method,
+ )
+ }
+
+ if (!providingArgsCode.isEmpty()) providingArgsCode.add(", ")
+ providingArgsCode.add(provideCode)
+ }
+
+ addCode("%L?.%L( ", injectableField.name!!.asString(), injectMethod.simpleName.asString())
+ addCode(providingArgsCode.build())
+ addStatement(")");
+ }
+
+ }
+
+
+ //protect by lifecycle owner
+ for (injectableField in injectableArguments) {
+ val injectableCl = injectableField.type.resolveNotNullable().declaration as? KSClassDeclaration
+ ?: throw IncorrectSignatureException(
+ message = "parameter must be a class",
+ element = injectableField,
+ )
+
+
+ val subscrCode = CodeBlock.builder()
+ var emptyCode = true
+ if (lifeCycleOwnerArg != null) {
+ subscrCode.beginControlFlow(
+ "%L?.subscribe{ timeMillis -> ",
+ lifeCycleOwnerArg.name!!.asString(),
+ )
+ for (injectField in injectableCl.getAllProperties()) {
+ if (!injectField.anyAnnotation(
+ Inject::class.asClassName(),
+ javax.inject.Inject::class.asClassName()
+ ).any()
+ ) continue
+ if (wrapHelper.isNonCachingWrapper(injectField.type.resolveNotNullable().toClassName())) {
+ //nothing to protect
+ continue
+ }
+
+ emptyCode = false
+ subscrCode.addStatement(
+ "%L.add( %T( %L, %L?.%L , timeMillis) )",
+ refCollectionGlFieldName,
+ TimeHolder::class.asClassName(),
+ scopeFieldName,
+ injectableField.name!!.asString(),
+ injectField.simpleName.asString()
+ )
+ }
+
+ subscrCode
+ .endControlFlow()
+
+ if (!emptyCode) addCode(subscrCode.build())
+ }
+ }
+ }
+ }
+
+ private fun TypeSpec.Builder.genProtectInjected(
+ componentCl: KSClassDeclaration,
+ method: KSFunctionDeclaration,
+ wrapHelper: WrapHelper,
+ ) {
+ val protectTimeMillis = method.getAnnotationsByType(ProtectInjected::class)
+ .firstOrNull()?.timeMillisProtected
+ ?: throw IncorrectSignatureException(
+ message = "Use ProtectInjected annotation at method ${componentCl.simpleName.asString()}.${method.simpleName.asString()}",
+ element = method,
+ )
+ val identifierTypes = componentCl.allIdentifierTypes.toList()
+ val injectableArguments = method.parameters.notIdentifierParameters(identifierTypes)
+ if (injectableArguments.isEmpty()) {
+ throw IncorrectSignatureException(
+ message = "No injectable parameter at ${method.simpleName.asString()}",
+ element = method,
+ )
+ }
+
+
+ genOverrideFun(method) {
+ for (injectableField in injectableArguments) {
+ val injectableCl = injectableField.type.resolveNotNullable().declaration as? KSClassDeclaration
+ ?: throw IncorrectSignatureException(
+ message = "parameter must be a class",
+ element = injectableField,
+ )
+
+
+ for (injectField in injectableCl.getAllProperties()) {
+ if (!injectField.anyAnnotation(
+ Inject::class.asClassName(),
+ javax.inject.Inject::class.asClassName()
+ ).any()
+ ) continue
+
+ if (wrapHelper.isNonCachingWrapper(injectField.type.resolveNotNullable().toTypeName())) {
+ //nothing to protect
+ continue
+ }
+
+ addStatement(
+ "%L.add( %T( %L, %L?.%L , %L ) )",
+ refCollectionGlFieldName,
+ TimeHolder::class.asClassName(),
+ scopeFieldName,
+ injectableField.name!!.asString(),
+ injectField.simpleName.asString(),
+ protectTimeMillis,
+ )
+ }
+ }
+ }
+ }
+
+ private fun TypeSpec.Builder.genGcMthod(
+ componentCl: KSClassDeclaration,
+ method: KSFunctionDeclaration,
+ wrapHelper: WrapHelper,
+ ) {
+
+ val scopesCode = codeBlock {
+ method.scopeAnnotations.forEachIndexed { index, annotation ->
+ if (index > 0) add(", ")
+ add("%T::class", annotation.annotationType.resolve().toTypeName())
+ }
+ }
+
+
+ genOverrideFun(method) {
+ addStatement(
+ "val scopes = setOf<%T>( %L )",
+ KClass::class.asClassName().parameterizedBy(STAR), scopesCode
+ )
+ addStatement("val toWeak = %T.toWeak()", SwitchCacheParam::class)
+ addStatement("val toDef = %T.toDef()", SwitchCacheParam::class)
+
+ addStatement(
+ "%L{ m -> m.%L(scopes, toWeak) } ",
+ eachModuleMethodName,
+ GenModuleProcessor.switchRefMethodName,
+ )
+
+ addStatement("%T.gc()", Memory::class)
+ addStatement("%L.clearNulls()", relatedComponentsListFieldName)
+ addStatement(
+
+ "%L{ m -> m.__clearNulls(); m.%L(scopes, toDef); }",
+ eachModuleMethodName, GenModuleProcessor.switchRefMethodName,
+ )
+
+ addStatement("%L.clearNulls()", refCollectionGlFieldName);
+ }
+ }
+
+ private fun TypeSpec.Builder.genSwitchRefMethod(
+ componentCl: KSClassDeclaration,
+ method: KSFunctionDeclaration,
+ ) {
+ val switchCacheAnn = method.getAnnotationsByType(SwitchCache::class).first()
+ val scopesCode = codeBlock {
+ method.scopeAnnotations.forEachIndexed { index, annotation ->
+ if (index > 0) add(", ")
+ add("%T::class", annotation.annotationType.resolve().toTypeName())
+ }
+ }
+
+ genOverrideFun(method) {
+ addStatement(
+ "val scopes = setOf<%T>( %L )",
+ KClass::class.asClassName().parameterizedBy(STAR), scopesCode
+ )
+ addStatement(
+ "val switchCacheParams = %T( %T.%L , %L )",
+ SwitchCacheParam::class,
+ SwitchCache.CacheType::class, switchCacheAnn.cacheProtected.name,
+ switchCacheAnn.timeMillisProtected,
+ )
+
+ addStatement(
+ "%L{ m -> m.%L(scopes, switchCacheParams) } ",
+ eachModuleMethodName, GenModuleProcessor.switchRefMethodName,
+ )
+ }
+ }
+
+ private fun TypeSpec.Builder.genIComponentMethods(
+ componentCl: KSClassDeclaration,
+ delayedCodeBlocks: DelayedCodeBlocks,
+ ) {
+ val relatedListType = WeakList::class.asClassName().parameterizedBy(IPrivateComponent::class.asClassName())
+ genProperty(
+ name = relatedComponentsListFieldName,
+ type = relatedListType,
+ ) {
+ addModifiers(KModifier.PRIVATE)
+ initializer("%T()", relatedListType)
+ }
+ genProperty(
+ name = protectRecursiveField,
+ type = BOOLEAN,
+ ) {
+ addModifiers(KModifier.PRIVATE)
+ mutable(true)
+ initializer("false")
+ }
+ genProperty(
+ name = refCollectionGlFieldName,
+ type = RefCollection::class.asClassName().parameterizedBy(ANY),
+ ) {
+ addModifiers(KModifier.PRIVATE)
+ mutable(true)
+ initializer("%T()", RefCollection::class.asClassName().parameterizedBy(ANY))
+ }
+
+ genProperty(
+ name = scopeFieldName,
+ type = CoroutineScope::class.asClassName(),
+ ) {
+ addModifiers(KModifier.PRIVATE)
+ mutable(true)
+ initializer(" %T.stoneCoroutineScope ", StoneScope::class)
+ }
+
+ genProperty(
+ name = hiddenModuleFieldName,
+ type = componentCl.hiddenModuleStoneClName,
+ ) {
+ addModifiers(KModifier.OVERRIDE)
+ initializer("%T()", componentCl.hiddenModuleStoneClName)
+ }
+
+ genFun(initMethodName) {
+ addModifiers(KModifier.OVERRIDE)
+ addParameter("modules", Any::class, KModifier.VARARG)
+
+ beginControlFlow("for (m in modules)")
+ beginControlFlow("if (m is %T)", IPrivateComponent::class.asClassName())
+ addComment("related component")
+ addStatement("%L.add(m)", relatedComponentsListFieldName)
+ endControlFlow()
+ beginControlFlow("else")
+ addComment("init modules")
+ addStatement(
+ "%L{ module -> module.%L(m) } ",
+ eachModuleMethodName, GenModuleProcessor.initMethodName
+ )
+ endControlFlow()
+ endControlFlow()
+ }
+
+ genFun(initDepsMethodName) {
+ addModifiers(KModifier.OVERRIDE)
+ addParameter("deps", Any::class, KModifier.VARARG)
+ beginControlFlow("for (m in deps)")
+ addComment("init dependencies")
+ addCode(delayedCodeBlocks.initDepsMethodBody.build())
+ endControlFlow()
+ }
+
+ genFun(bindMethodName) {
+ addModifiers(KModifier.OVERRIDE)
+ addParameter("objects", Any::class, KModifier.VARARG)
+ beginControlFlow("for (ob in objects)")
+ addStatement(
+ "%L{ m -> m.%L(ob) }",
+ eachModuleMethodName, GenModuleProcessor.bindMethodName
+ )
+ endControlFlow()
+ }
+
+ genFun(extOfMethodName) {
+ addModifiers(KModifier.OVERRIDE)
+ addParameter("c", IPrivateComponent::class)
+
+ for (proto in componentCl.allParentDeclarations) {
+ if (proto.toClassName() == IPrivateComponent::class.asClassName()) continue
+ val provideModuleMethods = proto
+ .getAllMethods(includeObjectMethods = false, allowDoubles = false)
+ .filter { it.isModuleProvideMethod }
+ .toList()
+
+ val bindInstanceAndProvideMethods = proto
+ .getAllMethods(includeObjectMethods = false, allowDoubles = false)
+ .filter { it.isBindInstanceMethod == BindInstanceType.BindInstanceAndProvide }
+ .toList()
+
+ if (provideModuleMethods.isEmpty() && bindInstanceAndProvideMethods.isEmpty()) continue
+
+ beginControlFlow("if (c is %T)", proto.toClassName())
+ addStatement("val protoComponent = c as %T", proto.toClassName())
+
+ for (provideModule in provideModuleMethods) {
+ addStatement(
+ "%L().%L( protoComponent.%L() as %T )",
+ provideModule.simpleName.asString(),
+ GenModuleProcessor.initCachesFromMethodName,
+ provideModule.simpleName.asString(), IModule::class.asClassName(),
+ )
+ }
+
+ addStatement("// bind instance methods ignore nullability checks ")
+ for (bindInstMethod in bindInstanceAndProvideMethods) {
+ addStatement(
+ "runCatching{ %L(protoComponent.%L(null)) }",
+ bindInstMethod.simpleName.asString(), bindInstMethod.simpleName.asString(),
+ )
+ }
+ addStatement(
+ "c.%L( %L ) ",
+ initMethodName,
+ provideModuleMethods.joinToString { "${it.simpleName.asString()}()" },
+ )
+ addStatement("c.%L(this)", initMethodName)
+ endControlFlow()
+ }
+
+ addStatement("%L.add(c)", relatedComponentsListFieldName)
+ }
+
+ genFun(eachModuleMethodName) {
+ addModifiers(KModifier.OVERRIDE)
+ addParameter(
+ "callback", LambdaTypeName.get(
+ receiver = null,
+ parameters = listOf(ParameterSpec.builder("it", IModule::class.asClassName()).build()),
+ returnType = UNIT,
+ )
+ )
+ addStatement("if (%L) return ", protectRecursiveField)
+ addStatement("%L = true", protectRecursiveField)
+
+ val provideModuleMethods = componentCl
+ .getAllMethods(includeObjectMethods = false, allowDoubles = false)
+ .filter { it.isModuleProvideMethod }
+ .toList()
+
+ for (module in provideModuleMethods) {
+ addStatement("callback(%L())", module.simpleName.asString())
+ }
+ addStatement("callback(%L)", hiddenModuleFieldName);
+
+ beginControlFlow("for (c in %L.toList())", relatedComponentsListFieldName)
+ addStatement("c.%L(callback)", eachModuleMethodName)
+ endControlFlow();
+
+ addStatement("%L = false", protectRecursiveField);
+ }
+
+
+ }
+
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/hiddenmodule/GenHiddenModuleCacheControlProcessor.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/hiddenmodule/GenHiddenModuleCacheControlProcessor.kt
new file mode 100644
index 00000000..9d1490dc
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/hiddenmodule/GenHiddenModuleCacheControlProcessor.kt
@@ -0,0 +1,114 @@
+@file:OptIn(KspExperimental::class)
+
+package com.github.klee0kai.thekey.stone.ksp.target.hiddenmodule
+
+import com.github.klee0kai.stone.__hidden__.CacheAction
+import com.github.klee0kai.stone.__hidden__.SwitchCacheParam
+import com.github.klee0kai.stone.annotations.component.Component
+import com.github.klee0kai.stone.annotations.module.BindInstance
+import com.github.klee0kai.thekey.stone.ksp.exceptions.forEachFun
+import com.github.klee0kai.thekey.stone.ksp.helpers.*
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.GenSpec
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.SymbolsToProcess
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.TargetFileProcessor
+import com.github.klee0kai.thekey.stone.ksp.ksp.getAllMethods
+import com.github.klee0kai.thekey.stone.ksp.poet.*
+import com.github.klee0kai.thekey.stone.ksp.target.module.GenModuleProcessor
+import com.github.klee0kai.thekey.stone.ksp.target.component.BindInstanceType
+import com.github.klee0kai.thekey.stone.ksp.target.component.isBindInstanceMethod
+import com.google.devtools.ksp.KspExperimental
+import com.google.devtools.ksp.containingFile
+import com.google.devtools.ksp.getAnnotationsByType
+import com.google.devtools.ksp.processing.Dependencies
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.STAR
+import com.squareup.kotlinpoet.asClassName
+import com.squareup.kotlinpoet.ksp.toTypeName
+import kotlin.reflect.KClass
+
+class GenHiddenModuleCacheControlProcessor : TargetFileProcessor {
+
+ override suspend fun findSymbolsToProcess(
+ resolver: Resolver,
+ ) = SymbolsToProcess(
+ symbolsForProcessing = resolver
+ .getSymbolsWithAnnotation(Component::class.asClassName().canonicalName)
+ .toList(),
+ symbolsForReprocessing = emptyList(),
+ )
+
+
+ override suspend fun process(
+ validSymbol: KSAnnotated,
+ resolver: Resolver,
+ options: Map,
+ logger: KSPLogger
+ ): GenSpec? {
+ val fileOwner = validSymbol.containingFile ?: return null
+ val componentCl = validSymbol as? KSClassDeclaration ?: return null
+
+ val genHiddenModuleCl = componentCl.hiddenModuleStoneClName
+ val identifierTypes = componentCl.allIdentifierTypes.toList()
+
+ val genCacheControlClassName = genHiddenModuleCl.cacheControlStoneClName
+ val fileSpec = genFileSpec(genHiddenModuleCl.packageName, genCacheControlClassName.simpleName) {
+ genLibComment()
+
+ genInterface(genCacheControlClassName) {
+
+ genFun(GenModuleProcessor.bindMethodName) {
+ modifiers.add(KModifier.ABSTRACT)
+ addParameter("or", Any::class)
+ returns(Boolean::class)
+ }
+
+ genFun(GenModuleProcessor.switchRefMethodName) {
+ modifiers.add(KModifier.ABSTRACT)
+ addParameter(
+ "scopes",
+ Set::class.asClassName()
+ .parameterizedBy(
+ KClass::class.asClassName()
+ .parameterizedBy(STAR)
+ )
+ )
+ addParameter("__params", SwitchCacheParam::class)
+ }
+
+ val functions = validSymbol.getAllMethods(false, false, "")
+ functions.forEachFun { _, function ->
+ val idArguments = function.parameters.identifierParameters(identifierTypes)
+ val bindAnn = function.getAnnotationsByType(BindInstance::class).firstOrNull()
+
+ if (bindAnn == null || function.isBindInstanceMethod != BindInstanceType.BindInstanceAndProvide)
+ return@forEachFun
+
+ genOverrideFun(function) {
+ modifiers.remove(KModifier.OVERRIDE)
+ modifiers.add(KModifier.ABSTRACT)
+ }
+ genFun(function.cacheControlMethodName) {
+ modifiers.add(KModifier.ABSTRACT)
+ returns(returnType = function.returnType!!.resolve().toTypeName().copy(nullable = true))
+ addParameter("__action", CacheAction::class)
+ idArguments.forEach {
+ addParameter(it.name!!.asString(), it.type.resolve().toTypeName())
+ }
+ }
+ }
+
+ }
+ }
+
+ return GenSpec(
+ fileSpec = fileSpec,
+ // https://kotlinlang.org/docs/ksp-incremental.html
+ dependencies = Dependencies(aggregating = false, fileOwner),
+ )
+ }
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/hiddenmodule/GenHiddenModuleProcessor.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/hiddenmodule/GenHiddenModuleProcessor.kt
new file mode 100644
index 00000000..77e112ea
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/hiddenmodule/GenHiddenModuleProcessor.kt
@@ -0,0 +1,433 @@
+@file:OptIn(KspExperimental::class)
+
+package com.github.klee0kai.thekey.stone.ksp.target.hiddenmodule
+
+import com.github.klee0kai.stone.__hidden__.CacheAction
+import com.github.klee0kai.stone.__hidden__.IModule
+import com.github.klee0kai.stone.__hidden__.SwitchCacheParam
+import com.github.klee0kai.stone.__hidden__.types.holders.SingleItemHolder
+import com.github.klee0kai.stone.__hidden__.types.holders.StoneRefType
+import com.github.klee0kai.stone.annotations.component.Component
+import com.github.klee0kai.stone.annotations.component.GcAllScope
+import com.github.klee0kai.stone.annotations.module.BindInstance
+import com.github.klee0kai.thekey.stone.ksp.exceptions.forEachFun
+import com.github.klee0kai.thekey.stone.ksp.helpers.*
+import com.github.klee0kai.thekey.stone.ksp.helpers.annotations.findComponentAnnotation
+import com.github.klee0kai.thekey.stone.ksp.helpers.itemholder.ItemHolderCodeHelper
+import com.github.klee0kai.thekey.stone.ksp.helpers.itemholder.of
+import com.github.klee0kai.thekey.stone.ksp.helpers.itemholder.toItemCacheType
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrap.ClassNameUtils.rawTypeOf
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrap.WrapHelper
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.GenSpec
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.SymbolsToProcess
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.TargetFileProcessor
+import com.github.klee0kai.thekey.stone.ksp.ksp.cacheProtected
+import com.github.klee0kai.thekey.stone.ksp.ksp.getAllMethods
+import com.github.klee0kai.thekey.stone.ksp.ksp.resolveNotNullable
+import com.github.klee0kai.thekey.stone.ksp.poet.*
+import com.github.klee0kai.thekey.stone.ksp.target.component.BindInstanceType
+import com.github.klee0kai.thekey.stone.ksp.target.component.collectWrapHelper
+import com.github.klee0kai.thekey.stone.ksp.target.component.isBindInstanceMethod
+import com.github.klee0kai.thekey.stone.ksp.target.module.GenModuleProcessor.Companion.appliedLocalFieldName
+import com.github.klee0kai.thekey.stone.ksp.target.module.GenModuleProcessor.Companion.bindMethodName
+import com.github.klee0kai.thekey.stone.ksp.target.module.GenModuleProcessor.Companion.clearNullsMethodName
+import com.github.klee0kai.thekey.stone.ksp.target.module.GenModuleProcessor.Companion.factoryFieldName
+import com.github.klee0kai.thekey.stone.ksp.target.module.GenModuleProcessor.Companion.initCachesFromMethodName
+import com.github.klee0kai.thekey.stone.ksp.target.module.GenModuleProcessor.Companion.initMethodName
+import com.github.klee0kai.thekey.stone.ksp.target.module.GenModuleProcessor.Companion.overridedModuleFieldName
+import com.github.klee0kai.thekey.stone.ksp.target.module.GenModuleProcessor.Companion.switchRefMethodName
+import com.github.klee0kai.thekey.stone.ksp.target.module.GenModuleProcessor.Companion.updateBindInstancesFrom
+import com.google.devtools.ksp.KspExperimental
+import com.google.devtools.ksp.containingFile
+import com.google.devtools.ksp.getAnnotationsByType
+import com.google.devtools.ksp.processing.Dependencies
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.google.devtools.ksp.symbol.KSValueParameter
+import com.squareup.kotlinpoet.*
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.ksp.toTypeName
+import kotlin.reflect.KClass
+
+class GenHiddenModuleProcessor : TargetFileProcessor {
+
+
+ private class DelayedCodeBlocks(
+ val bindMethodBody: CodeBlock.Builder = CodeBlock.builder(),
+ val clearNullsMethodBody: CodeBlock.Builder = CodeBlock.builder(),
+ val switchRefStatementBuilders: MutableMap, CodeBlock.Builder> = mutableMapOf()
+ )
+
+ override suspend fun findSymbolsToProcess(
+ resolver: Resolver,
+ ) = SymbolsToProcess(
+ symbolsForProcessing = resolver
+ .getSymbolsWithAnnotation(Component::class.asClassName().canonicalName)
+ .toList(),
+ symbolsForReprocessing = emptyList(),
+ )
+
+
+ override suspend fun process(
+ validSymbol: KSAnnotated,
+ resolver: Resolver,
+ options: Map,
+ logger: KSPLogger
+ ): GenSpec? {
+ val fileOwner = validSymbol.containingFile ?: return null
+ val componentCl = validSymbol as? KSClassDeclaration ?: return null
+
+ val genHiddenModuleCl = componentCl.hiddenModuleStoneClName
+ val identifierTypes = componentCl.allIdentifierTypes.toList()
+ val wrapHelper = componentCl.collectWrapHelper()
+ val codeBlocks = DelayedCodeBlocks()
+
+ val fileSpec = genFileSpec(genHiddenModuleCl.packageName, genHiddenModuleCl.simpleName) {
+ genLibComment()
+
+ addImport("com.github.klee0kai.stone.__hidden__.coroutines", "syncIfAvailable")
+
+ genClass(genHiddenModuleCl) {
+ addSuperinterface(IModule::class)
+ componentCl.allParentDeclarations
+ .filter { it.findComponentAnnotation().any() }
+ .forEach { parentComponentCl -> addSuperinterface(parentComponentCl.hiddenModuleStoneClName.cacheControlStoneClName) }
+
+
+ val functions = validSymbol.getAllMethods(false, false, "")
+ functions.forEachFun { funIdx, function ->
+ val itemHolderIdx = funIdx + 1
+ val bindAnn = function.getAnnotationsByType(BindInstance::class).firstOrNull()
+ val idArguments = function.parameters.identifierParameters(identifierTypes)
+
+ val returnType = function.returnType?.resolve()?.toTypeName() ?: return@forEachFun
+ val nonWrappedType = wrapHelper.nonWrappedType(returnType)
+ val gcScopes = (function.scopeAnnotations
+ .map { it.annotationType.resolve().toTypeName() }
+ .toSet() + GcAllScope::class.asClassName()).toMutableSet()
+
+ if (bindAnn == null || function.isBindInstanceMethod != BindInstanceType.BindInstanceAndProvide)
+ return@forEachFun
+
+ val itemHolderCodeHelper = ItemHolderCodeHelper.of(
+ fieldName = "${function.simpleName.asString()}$itemHolderIdx",
+ returnType = returnType,
+ idArguments = idArguments,
+ cacheType = bindAnn.cacheProtected.toItemCacheType(),
+ wrapHelper = wrapHelper,
+ )
+ gcScopes += bindAnn.cacheProtected.toItemCacheType().gcScopeClassName
+ codeBlocks.switchRefStatementBuilders.getOrPut(gcScopes) { CodeBlock.builder() }
+ .add(itemHolderCodeHelper.statementSwitchRef(CodeBlock.of("__params")))
+
+ codeBlocks.clearNullsMethodBody.add(itemHolderCodeHelper.clearNullsStatement())
+
+
+ with(itemHolderCodeHelper) {
+ genCacheField()
+
+ codeBlocks.bindMethodBody.apply {
+ add(
+ "if (or::class == %T::class) {\n",
+ rawTypeOf(nonWrappedType),
+ )
+ add(codeSetCachedValue(CodeBlock.of("or as? %T", nonWrappedType), false))
+ add("\n")
+ add("%L = true\n", appliedLocalFieldName)
+ add("}\n")
+ }
+ }
+
+ genBindInstance(
+ function = function,
+ idArguments = idArguments,
+ itemHolderCodeHelper = itemHolderCodeHelper,
+ wrapHelper = wrapHelper,
+ )
+ genCacheControlFun(
+ function = function,
+ idArguments = idArguments,
+ itemHolderCodeHelper = itemHolderCodeHelper,
+ wrapHelper = wrapHelper,
+ )
+ }
+
+ genIModelMethods(
+ componentCl = componentCl,
+ codeBlocks = codeBlocks,
+ )
+ }
+
+ }
+
+ return GenSpec(
+ fileSpec = fileSpec,
+ // https://kotlinlang.org/docs/ksp-incremental.html
+ dependencies = Dependencies(aggregating = false, fileOwner),
+ )
+ }
+
+ private fun TypeSpec.Builder.genBindInstance(
+ function: KSFunctionDeclaration,
+ idArguments: List,
+ itemHolderCodeHelper: ItemHolderCodeHelper,
+ wrapHelper: WrapHelper,
+ ) {
+ val returnType = function.returnType?.resolve()?.toTypeName() ?: return
+ val setValueArg = function.parameters.firstOrNull {
+ it.type.resolveNotNullable().toTypeName() == function.returnType?.resolveNotNullable()?.toTypeName()
+ }
+
+ genOverrideFun(function) {
+ beginControlFlow("return syncIfAvailable(%L.mutex)", itemHolderCodeHelper.fieldName)
+ addStatement(
+ "val cached = %L.get()?.%L( %T.getValueAction, %L ) ",
+ overridedModuleFieldName,
+ function.cacheControlMethodName,
+ CacheAction::class.asClassName(),
+ idArguments.joinToString(", ") { it.name!!.asString() },
+ )
+ addCode("if ( cached != null ) return@syncIfAvailable ")
+ addCode(
+ wrapHelper.transform(
+ providingType = wrapHelper.listWrapTypeIfNeed(returnType),
+ wannaType = returnType,
+ code = codeBlock { add("cached") },
+ )
+ )
+ addStatement("")
+
+ if (setValueArg != null) {
+ beginControlFlow("if (%L != null)", setValueArg.name!!.asString())
+ addCode(
+ itemHolderCodeHelper.codeSetCachedValue(
+ value = CodeBlock.of("%L", setValueArg.name!!.asString()),
+ onlyIfNull = false
+ )
+ )
+ endControlFlow();
+ }
+
+
+ addCode("return@syncIfAvailable ")
+ addCode(
+ wrapHelper.transform(
+ wrapHelper.listWrapTypeIfNeed(returnType),
+ returnType,
+ itemHolderCodeHelper.codeGetCachedValue(),
+ )
+ )
+ addStatement(" as %T", returnType)
+ endControlFlow()
+ }
+ }
+
+
+ private fun TypeSpec.Builder.genCacheControlFun(
+ function: KSFunctionDeclaration,
+ idArguments: List,
+ itemHolderCodeHelper: ItemHolderCodeHelper,
+ wrapHelper: WrapHelper,
+ ) {
+ val returnType = function.returnType?.resolve()?.toTypeName() ?: return
+ val cacheControlType = wrapHelper.listWrapTypeIfNeed(returnType)
+ genFun(function.cacheControlMethodName) {
+ modifiers.add(KModifier.OVERRIDE)
+ returns(returnType.copy(nullable = true))
+ addParameter("__action", CacheAction::class)
+ idArguments.forEach {
+ addParameter(it.name!!.asString(), it.type.resolve().toTypeName())
+ }
+
+ beginControlFlow("return syncIfAvailable(%L.mutex)", itemHolderCodeHelper.fieldName)
+ addStatement(
+ "%L.get()?.%L( __action, %L ) ",
+ overridedModuleFieldName,
+ function.cacheControlMethodName,
+ idArguments.joinToString(", ") { it.name!!.asString() },
+ )
+ beginControlFlow("when (__action.type) {")
+ addStatement("%T.GET_VALUE -> Unit", CacheAction.ActionType::class)
+ //set value
+ beginControlFlow("%T.SET_VALUE ->", CacheAction.ActionType::class)
+ addCode("if ( __action.value != null ) ")
+ addCode(
+ codeBlock = itemHolderCodeHelper.codeSetCachedValue(
+ CodeBlock.of("__action.value as? %T", cacheControlType),
+ onlyIfNull = false,
+ )
+ )
+ endControlFlow()
+ //set if null value
+ beginControlFlow("%T.SET_IF_NULL ->", CacheAction.ActionType::class)
+ addCode("if ( __action.value != null ) ")
+ addCode(
+ codeBlock = itemHolderCodeHelper.codeSetCachedValue(
+ CodeBlock.of("__action.value as? %T", cacheControlType),
+ onlyIfNull = true,
+ )
+ )
+ endControlFlow()
+ // switch cache type
+ beginControlFlow("%T.SWITCH_CACHE ->", CacheAction.ActionType::class)
+ addCode(codeBlock = itemHolderCodeHelper.statementSwitchRef(CodeBlock.of("__action.swCacheParams!!")))
+ endControlFlow()
+
+ addStatement("null -> Unit")
+ endControlFlow()
+
+ addCode("return@syncIfAvailable ")
+ addCode(codeBlock = itemHolderCodeHelper.codeGetCachedValue())
+ addStatement("")
+ endControlFlow()
+ }
+ }
+
+ private fun TypeSpec.Builder.genIModelMethods(
+ componentCl: KSClassDeclaration,
+
+ codeBlocks: DelayedCodeBlocks,
+ ) {
+ genProperty(
+ name = factoryFieldName,
+ type = ANY.copy(nullable = true),
+ ) {
+ addModifiers(KModifier.OVERRIDE)
+ initializer("null")
+ }
+
+ val cacheControlHolder = SingleItemHolder::class.asClassName()
+ .parameterizedBy(componentCl.hiddenModuleStoneClName.cacheControlStoneClName)
+ genProperty(
+ name = overridedModuleFieldName,
+ type = cacheControlHolder,
+ ) {
+ mutable(true)
+ initializer("%T(%T.WeakObject)", cacheControlHolder, StoneRefType::class)
+ }
+
+ genFun(initMethodName) {
+ addModifiers(KModifier.OVERRIDE)
+ returns(BOOLEAN)
+ addParameter("or", Any::class)
+ addStatement("if (or === this) return false")
+ addStatement("var %L = false", appliedLocalFieldName)
+
+ addStatement("return %L", appliedLocalFieldName)
+ }
+
+
+
+ genFun(bindMethodName) {
+ addModifiers(KModifier.OVERRIDE)
+ addParameter("or", Any::class)
+ returns(BOOLEAN)
+ addStatement("var %L = false", appliedLocalFieldName)
+ addCode(codeBlocks.bindMethodBody.build())
+ addStatement("return %L", appliedLocalFieldName)
+ }
+
+ genFun(switchRefMethodName) {
+ addModifiers(KModifier.OVERRIDE)
+ addParameter(
+ "scopes",
+ Set::class.asClassName()
+ .parameterizedBy(
+ KClass::class.asClassName()
+ .parameterizedBy(STAR)
+ )
+ )
+ addParameter("__params", SwitchCacheParam::class)
+
+ codeBlocks.switchRefStatementBuilders.forEach { (key, value) ->
+ addCode("if (listOf(")
+ key.forEachIndexed { idx, scope ->
+ if (idx > 0) addCode(", ")
+ addCode("%T::class", scope)
+ }
+ beginControlFlow(").containsAll(scopes))")
+ addCode(value.build())
+ endControlFlow()
+ }
+ }
+
+ genFun(initCachesFromMethodName) {
+ addModifiers(KModifier.OVERRIDE)
+ addParameter("m", IModule::class)
+ addStatement("if (m == this) return")
+
+ componentCl.allParentDeclarations
+ .filter { it.findComponentAnnotation().any() }
+ .forEach { parentComponentCl ->
+ val cacheControlCl = parentComponentCl.hiddenModuleStoneClName.cacheControlStoneClName
+ beginControlFlow("if ( m is %T )", cacheControlCl)
+ addStatement("val module = m as %T", cacheControlCl)
+
+ parentComponentCl.getAllMethods(
+ includeObjectMethods = false,
+ allowDoubles = false,
+ exceptNames = arrayOf(""),
+ ).forEach { protoProvideMethod ->
+ if (protoProvideMethod.isBindInstanceMethod != BindInstanceType.BindInstanceAndProvide) {
+ return@forEach
+ }
+
+ val cacheControlMethod = protoProvideMethod.cacheControlMethodName
+
+ addStatement(
+ "%L( %T.setIfNullValueAction( module.%L( %T.getValueAction ) ) )",
+ cacheControlMethod, CacheAction::class,
+ cacheControlMethod, CacheAction::class,
+ )
+ }
+
+ addStatement("return")
+ endControlFlow()
+ }
+
+ }
+
+ genFun(updateBindInstancesFrom) {
+ addModifiers(KModifier.OVERRIDE)
+ addParameter("m", IModule::class)
+ addStatement("if (m == this) return")
+
+ componentCl.allParentDeclarations
+ .filter { it.findComponentAnnotation().any() }
+ .forEach { parentComponentCl ->
+ val cacheControlCl = parentComponentCl.hiddenModuleStoneClName.cacheControlStoneClName
+ beginControlFlow("if ( m is %T )", cacheControlCl)
+ addStatement("val module = m as %T", cacheControlCl)
+ parentComponentCl.getAllMethods(
+ includeObjectMethods = false,
+ allowDoubles = false,
+ exceptNames = arrayOf(""),
+ ).forEach { protoProvideMethod ->
+ if (protoProvideMethod.isBindInstanceMethod != BindInstanceType.BindInstanceAndProvide) {
+ return@forEach
+ }
+
+ val cacheControlMethod = protoProvideMethod.cacheControlMethodName
+
+ addStatement(
+ "%L( %T.setValueAction( module.%L( %T.getValueAction ) ) )",
+ cacheControlMethod, CacheAction::class,
+ cacheControlMethod, CacheAction::class,
+ )
+ }
+ addStatement("return")
+ endControlFlow()
+ }
+
+ }
+
+ genFun(clearNullsMethodName) {
+ addModifiers(KModifier.OVERRIDE)
+ addCode(codeBlocks.clearNullsMethodBody.build())
+ }
+ }
+
+}
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/module/GenModuleCacheControlProcessor.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/module/GenModuleCacheControlProcessor.kt
new file mode 100644
index 00000000..b465f292
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/module/GenModuleCacheControlProcessor.kt
@@ -0,0 +1,117 @@
+@file:OptIn(KspExperimental::class)
+
+package com.github.klee0kai.thekey.stone.ksp.target.module
+
+import com.github.klee0kai.stone.__hidden__.CacheAction
+import com.github.klee0kai.stone.__hidden__.SwitchCacheParam
+import com.github.klee0kai.stone.annotations.module.Module
+import com.github.klee0kai.thekey.stone.ksp.exceptions.forEachFun
+import com.github.klee0kai.thekey.stone.ksp.helpers.*
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrap.WrapHelper
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.GenSpec
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.SymbolsToProcess
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.TargetFileProcessor
+import com.github.klee0kai.thekey.stone.ksp.ksp.getAllMethods
+import com.github.klee0kai.thekey.stone.ksp.ksp.resolveNotNullable
+import com.github.klee0kai.thekey.stone.ksp.poet.*
+import com.github.klee0kai.thekey.stone.ksp.target.component.collectWrapHelper
+import com.google.devtools.ksp.KspExperimental
+import com.google.devtools.ksp.containingFile
+import com.google.devtools.ksp.processing.Dependencies
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.STAR
+import com.squareup.kotlinpoet.asClassName
+import com.squareup.kotlinpoet.ksp.toClassName
+import com.squareup.kotlinpoet.ksp.toTypeName
+import kotlin.reflect.KClass
+
+class GenModuleCacheControlProcessor : TargetFileProcessor {
+
+ override suspend fun findSymbolsToProcess(
+ resolver: Resolver,
+ ) = SymbolsToProcess(
+ symbolsForProcessing = resolver
+ .getSymbolsWithAnnotation(Module::class.asClassName().canonicalName)
+ .toList(),
+ symbolsForReprocessing = emptyList(),
+ )
+
+ override suspend fun process(
+ validSymbol: KSAnnotated,
+ resolver: Resolver,
+ options: Map,
+ logger: KSPLogger
+ ): GenSpec? {
+ val fileOwner = validSymbol.containingFile ?: return null
+ val moduleCl = validSymbol as? KSClassDeclaration ?: return null
+
+ val componentCl = resolver.findComponentForModuleOrDep(moduleCl.toClassName())
+ .firstOrNull()
+ val wrapHelper = componentCl?.collectWrapHelper() ?: WrapHelper()
+
+ val identifierTypes = componentCl
+ ?.allIdentifierTypes?.toList()
+ ?: emptyList()
+
+ val genCacheControlClassName = moduleCl.toClassName().cacheControlStoneClName
+ val fileSpec = genFileSpec(
+ packageName = genCacheControlClassName.packageName,
+ fileName = genCacheControlClassName.simpleName
+ ) {
+ genLibComment()
+
+ genInterface(genCacheControlClassName) {
+
+ genFun(GenModuleProcessor.bindMethodName) {
+ modifiers.add(KModifier.ABSTRACT)
+ addParameter("or", Any::class)
+ returns(Boolean::class)
+ }
+
+ genFun(GenModuleProcessor.switchRefMethodName) {
+ modifiers.add(KModifier.ABSTRACT)
+ addParameter(
+ "scopes",
+ Set::class.asClassName()
+ .parameterizedBy(
+ KClass::class.asClassName()
+ .parameterizedBy(STAR)
+ )
+ )
+ addParameter("__params", SwitchCacheParam::class)
+ }
+
+ val methods = validSymbol.getAllMethods(includeObjectMethods = false, allowDoubles = false, "")
+ methods.forEachFun { _, function ->
+ val idArguments = function.parameters.identifierParameters(identifierTypes)
+ val returnType = function.returnType?.resolveNotNullable()?.toTypeName() ?: return@forEachFun
+ val cacheControlType = wrapHelper.listWrapTypeIfNeed(returnType).copy(nullable = true)
+
+ genOverrideFun(function) {
+ modifiers.remove(KModifier.OVERRIDE)
+ modifiers.add(KModifier.ABSTRACT)
+ }
+ genFun(function.cacheControlMethodName) {
+ modifiers.add(KModifier.ABSTRACT)
+ returns(returnType = cacheControlType)
+ addParameter("__action", CacheAction::class)
+ idArguments.forEach {
+ addParameter(it.name!!.asString(), it.type.resolve().toTypeName())
+ }
+ }
+ }
+ }
+ }
+
+ return GenSpec(
+ fileSpec = fileSpec,
+ // https://kotlinlang.org/docs/ksp-incremental.html
+ dependencies = Dependencies(aggregating = false, fileOwner),
+ )
+ }
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/module/GenModuleFactoryProcessor.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/module/GenModuleFactoryProcessor.kt
new file mode 100644
index 00000000..c0ed2806
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/module/GenModuleFactoryProcessor.kt
@@ -0,0 +1,141 @@
+@file:OptIn(KspExperimental::class)
+
+package com.github.klee0kai.thekey.stone.ksp.target.module
+
+import com.github.klee0kai.stone.__hidden__.IModuleFactory
+import com.github.klee0kai.stone.annotations.module.BindInstance
+import com.github.klee0kai.stone.annotations.module.Module
+import com.github.klee0kai.thekey.stone.ksp.exceptions.forEachFun
+import com.github.klee0kai.thekey.stone.ksp.helpers.factoryStoneClName
+import com.github.klee0kai.thekey.stone.ksp.helpers.findComponentForModuleOrDep
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrap.WrapHelper
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrap.rawType
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.GenSpec
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.SymbolsToProcess
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.TargetFileProcessor
+import com.github.klee0kai.thekey.stone.ksp.ksp.findConstructor
+import com.github.klee0kai.thekey.stone.ksp.ksp.getAllMethods
+import com.github.klee0kai.thekey.stone.ksp.ksp.joinInvokeArguments
+import com.github.klee0kai.thekey.stone.ksp.ksp.resolveNotNullable
+import com.github.klee0kai.thekey.stone.ksp.poet.genClass
+import com.github.klee0kai.thekey.stone.ksp.poet.genFileSpec
+import com.github.klee0kai.thekey.stone.ksp.poet.genLibComment
+import com.github.klee0kai.thekey.stone.ksp.poet.genOverrideFun
+import com.github.klee0kai.thekey.stone.ksp.target.component.collectWrapHelper
+import com.google.devtools.ksp.KspExperimental
+import com.google.devtools.ksp.containingFile
+import com.google.devtools.ksp.getAnnotationsByType
+import com.google.devtools.ksp.getClassDeclarationByName
+import com.google.devtools.ksp.processing.Dependencies
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.ClassKind
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.Modifier
+import com.squareup.kotlinpoet.CodeBlock
+import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.asClassName
+import com.squareup.kotlinpoet.ksp.toClassName
+import com.squareup.kotlinpoet.ksp.toTypeName
+
+class GenModuleFactoryProcessor : TargetFileProcessor {
+
+ override suspend fun findSymbolsToProcess(
+ resolver: Resolver,
+ ) = SymbolsToProcess(
+ symbolsForProcessing = resolver
+ .getSymbolsWithAnnotation(Module::class.asClassName().canonicalName)
+ .toList(),
+ symbolsForReprocessing = emptyList(),
+ )
+
+ override suspend fun process(
+ validSymbol: KSAnnotated,
+ resolver: Resolver,
+ options: Map,
+ logger: KSPLogger
+ ): GenSpec? {
+
+ val fileOwner = validSymbol.containingFile ?: return null
+ val moduleCl = validSymbol as? KSClassDeclaration ?: return null
+
+ val componentCl = resolver.findComponentForModuleOrDep(moduleCl.toClassName())
+ .firstOrNull()
+
+ val wrapHelper = componentCl?.collectWrapHelper() ?: WrapHelper()
+
+ val genFactoryClassName = moduleCl.factoryStoneClName
+ val fileSpec = genFileSpec(genFactoryClassName.packageName, fileName = genFactoryClassName.simpleName) {
+ genLibComment()
+
+ genClass(genFactoryClassName) {
+ if (moduleCl.classKind == ClassKind.INTERFACE) {
+ addSuperinterface(moduleCl.toClassName())
+ } else {
+ superclass(moduleCl.toClassName())
+ }
+ addSuperinterface(IModuleFactory::class)
+ addModifiers(KModifier.OPEN)
+
+ validSymbol.getAllMethods(false, false, "")
+ .forEachFun { _, function ->
+ if (!function.modifiers.contains(Modifier.ABSTRACT) && moduleCl.classKind != ClassKind.INTERFACE) return@forEachFun
+ val returnType = function.returnType?.toTypeName() ?: return@forEachFun
+ val nonWrappedType = wrapHelper.nonWrappedType(returnType)
+ val nonWrappedClDec = resolver.getClassDeclarationByName(nonWrappedType.rawType().toString())
+
+ val bindInstanceAnn = function.getAnnotationsByType(BindInstance::class)
+ .firstOrNull()
+
+ val constructorFun by lazy {
+ nonWrappedClDec?.findConstructor(
+ parameters = function.parameters.map { it.type.resolveNotNullable() })
+ }
+
+ genOverrideFun(function) {
+ when {
+ bindInstanceAnn != null -> {
+ addStatement(
+ "throw %T(%S)",
+ NotImplementedError::class.asClassName(),
+ "Object generation is not available for bind instance methods"
+ )
+ }
+
+ constructorFun != null -> {
+ addCode(
+ "return %L",
+ wrapHelper.transform(
+ providingType = nonWrappedType,
+ wannaType = returnType,
+ CodeBlock.of(
+ "%T( %L )",
+ nonWrappedType,
+ constructorFun!!.joinInvokeArguments(function.parameters),
+ )
+ )
+ )
+ }
+
+ else -> {
+ addStatement(
+ "return super.%L( %L )",
+ function.simpleName.asString(),
+ function.parameters.joinToString(", ") { it.name?.asString() ?: "it" },
+ )
+ }
+ }
+ }
+ }
+
+ }
+ }
+
+ return GenSpec(
+ fileSpec = fileSpec,
+ // https://kotlinlang.org/docs/ksp-incremental.html
+ dependencies = Dependencies(aggregating = false, fileOwner),
+ )
+ }
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/module/GenModuleProcessor.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/module/GenModuleProcessor.kt
new file mode 100644
index 00000000..f85903e8
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/module/GenModuleProcessor.kt
@@ -0,0 +1,612 @@
+@file:OptIn(KspExperimental::class)
+
+package com.github.klee0kai.thekey.stone.ksp.target.module
+
+import com.github.klee0kai.stone.__hidden__.CacheAction
+import com.github.klee0kai.stone.__hidden__.IModule
+import com.github.klee0kai.stone.__hidden__.SwitchCacheParam
+import com.github.klee0kai.stone.__hidden__.types.holders.SingleItemHolder
+import com.github.klee0kai.stone.__hidden__.types.holders.StoneRefType
+import com.github.klee0kai.stone.annotations.component.GcAllScope
+import com.github.klee0kai.stone.annotations.module.BindInstance
+import com.github.klee0kai.stone.annotations.module.Module
+import com.github.klee0kai.stone.annotations.module.Provide
+import com.github.klee0kai.stone.weakref.Ref
+import com.github.klee0kai.thekey.stone.ksp.exceptions.forEachFun
+import com.github.klee0kai.thekey.stone.ksp.helpers.*
+import com.github.klee0kai.thekey.stone.ksp.helpers.annotations.annotations
+import com.github.klee0kai.thekey.stone.ksp.helpers.annotations.anyAnnotation
+import com.github.klee0kai.thekey.stone.ksp.helpers.itemholder.ItemHolderCodeHelper
+import com.github.klee0kai.thekey.stone.ksp.helpers.itemholder.of
+import com.github.klee0kai.thekey.stone.ksp.helpers.itemholder.toItemCacheType
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrap.ClassNameUtils.rawTypeOf
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrap.WrapHelper
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.GenSpec
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.SymbolsToProcess
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.TargetFileProcessor
+import com.github.klee0kai.thekey.stone.ksp.ksp.cacheProtected
+import com.github.klee0kai.thekey.stone.ksp.ksp.getAllMethods
+import com.github.klee0kai.thekey.stone.ksp.ksp.resolveNotNullable
+import com.github.klee0kai.thekey.stone.ksp.poet.*
+import com.github.klee0kai.thekey.stone.ksp.target.component.collectWrapHelper
+import com.google.devtools.ksp.KspExperimental
+import com.google.devtools.ksp.containingFile
+import com.google.devtools.ksp.getAllSuperTypes
+import com.google.devtools.ksp.getAnnotationsByType
+import com.google.devtools.ksp.processing.Dependencies
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.*
+import com.squareup.kotlinpoet.*
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.ksp.toClassName
+import com.squareup.kotlinpoet.ksp.toTypeName
+import kotlin.reflect.KClass
+
+class GenModuleProcessor : TargetFileProcessor {
+
+ companion object {
+ const val overridedModuleFieldName: String = "overridedModule"
+ const val factoryFieldName: String = "factory"
+ const val appliedLocalFieldName: String = "applied"
+ const val initMethodName: String = "__init"
+ const val initCachesFromMethodName: String = "__initCachesFrom"
+ const val updateBindInstancesFrom: String = "__updateBindInstancesFrom"
+ const val bindMethodName: String = "__bind"
+ const val switchRefMethodName: String = "__switchRef"
+ const val clearNullsMethodName: String = "__clearNulls"
+
+ val allReserveMethodNames = listOf(
+ initMethodName,
+ initCachesFromMethodName,
+ updateBindInstancesFrom,
+ bindMethodName,
+ switchRefMethodName,
+ clearNullsMethodName,
+ )
+ }
+
+ private class DelayedCodeBlocks(
+ val bindMethodBody: CodeBlock.Builder = CodeBlock.builder(),
+ val clearNullsMethodBody: CodeBlock.Builder = CodeBlock.builder(),
+ val switchRefStatementBuilders: MutableMap, CodeBlock.Builder> = mutableMapOf()
+ )
+
+ override suspend fun findSymbolsToProcess(
+ resolver: Resolver,
+ ) = SymbolsToProcess(
+ symbolsForProcessing = resolver
+ .getSymbolsWithAnnotation(Module::class.asClassName().canonicalName)
+ .toList(),
+ symbolsForReprocessing = emptyList(),
+ )
+
+ override suspend fun process(
+ validSymbol: KSAnnotated,
+ resolver: Resolver,
+ options: Map,
+ logger: KSPLogger
+ ): GenSpec? {
+ val fileOwner = validSymbol.containingFile ?: return null
+ val moduleCl = validSymbol as? KSClassDeclaration ?: return null
+
+ val moduleAnn = moduleCl.getAnnotationsByType(Module::class)
+ .firstOrNull() ?: return null
+
+ val componentCl = resolver.findComponentForModuleOrDep(moduleCl.toClassName())
+ .firstOrNull()
+
+ val wrapHelper = componentCl?.collectWrapHelper() ?: WrapHelper()
+
+ val identifierTypes = componentCl
+ ?.allIdentifierTypes?.toList()
+ ?: emptyList()
+
+ val genModuleClassName = moduleCl.moduleStoneClName
+
+ val fileSpec = genFileSpec(genModuleClassName.packageName, genModuleClassName.simpleName) {
+ genLibComment()
+
+ addImport("com.github.klee0kai.stone.__hidden__.coroutines", "syncIfAvailable")
+
+ genClass(genModuleClassName) {
+ if (moduleCl.classKind == ClassKind.INTERFACE) {
+ addSuperinterface(moduleCl.toClassName())
+ } else {
+ superclass(moduleCl.toClassName())
+ }
+ addSuperinterface(IModule::class)
+
+ moduleCl.allParentDeclarations
+ .filter { it.getAnnotationsByType(Module::class).any() }
+ .forEach { parentModuleCl -> addSuperinterface(parentModuleCl.toClassName().cacheControlStoneClName) }
+
+ addModifiers(KModifier.OPEN)
+ val codeBlocks = DelayedCodeBlocks()
+
+ validSymbol.getAllMethods(false, false, "")
+ .forEachFun { funIdx, function ->
+ val holderIdx = funIdx + 1
+ val bindAnn = function.getAnnotationsByType(BindInstance::class).firstOrNull()
+ val provideAnn = function.getAnnotationsByType(Provide::class).firstOrNull()
+ val idArguments = function.parameters.identifierParameters(identifierTypes)
+
+ val returnType = function.returnType?.resolve()?.toTypeName() ?: return@forEachFun
+ val nonWrappedType = wrapHelper.nonWrappedType(returnType)
+ val isListReturnType = wrapHelper.isList(returnType)
+ val gcScopes = (function.scopeAnnotations
+ .map { it.annotationType.resolve().toTypeName() }
+ .toSet() + GcAllScope::class.asClassName()).toMutableSet()
+
+
+ when {
+ bindAnn != null -> {
+ val itemHolderCodeHelper = ItemHolderCodeHelper.of(
+ fieldName = "${function.simpleName.asString()}$holderIdx",
+ returnType = returnType,
+ idArguments = idArguments,
+ cacheType = bindAnn.cacheProtected.toItemCacheType(),
+ wrapHelper = wrapHelper,
+ )
+ gcScopes += bindAnn.cacheProtected.toItemCacheType().gcScopeClassName
+ codeBlocks.switchRefStatementBuilders.getOrPut(gcScopes) { CodeBlock.builder() }
+ .add(itemHolderCodeHelper.statementSwitchRef(CodeBlock.of("__params")))
+
+ codeBlocks.clearNullsMethodBody.add(itemHolderCodeHelper.clearNullsStatement())
+
+ with(itemHolderCodeHelper) {
+ genCacheField()
+
+ if (!isListReturnType) {
+ codeBlocks.bindMethodBody.controlFlow(
+ "if (or::class == %T::class) {",
+ rawTypeOf(nonWrappedType)
+ ) {
+ add(codeSetCachedValue(CodeBlock.of("or as? %T", nonWrappedType), false))
+ addStatement("")
+ addStatement("%L = true\n", appliedLocalFieldName)
+ }
+ }
+ }
+
+ genBindInstance(
+ function = function,
+ idArguments = idArguments,
+ itemHolderCodeHelper = itemHolderCodeHelper,
+ wrapHelper = wrapHelper,
+ )
+ genCacheControlFun(
+ function = function,
+ idArguments = idArguments,
+ itemHolderCodeHelper = itemHolderCodeHelper,
+ wrapHelper = wrapHelper,
+ )
+
+
+ }
+
+ provideAnn == null || provideAnn.cacheProtected == Provide.CacheType.Factory -> {
+ genOverrideFun(function) {
+ addStatement(
+ "return %L.%L(%L)", factoryFieldName,
+ function.simpleName.asString(),
+ function.parameters.joinToString(", ") { it.name!!.asString() })
+ }
+ genFun(function.cacheControlMethodName) {
+ modifiers.add(KModifier.OVERRIDE)
+ val cacheControlType = function.returnType?.resolveNotNullable()
+ ?.toTypeName()
+ ?.let { wrapHelper.listWrapTypeIfNeed(it).copy(nullable = true) }
+ cacheControlType?.let { returns(cacheControlType) }
+ addParameter("__action", CacheAction::class)
+ idArguments.forEach {
+ addParameter(it.name!!.asString(), it.type.resolve().toTypeName())
+ }
+ addStatement("return null")
+ }
+ }
+
+ else -> {
+ val itemHolderCodeHelper = ItemHolderCodeHelper.of(
+ fieldName = "${function.simpleName.asString()}$holderIdx",
+ returnType = returnType,
+ idArguments = idArguments,
+ cacheType = provideAnn.cacheProtected.toItemCacheType() ?: return@forEachFun,
+ wrapHelper = wrapHelper,
+ )
+ gcScopes += provideAnn.cacheProtected.toItemCacheType()!!.gcScopeClassName
+ codeBlocks.switchRefStatementBuilders.getOrPut(gcScopes) { CodeBlock.builder() }
+ .add(itemHolderCodeHelper.statementSwitchRef(CodeBlock.of("__params")))
+ codeBlocks.clearNullsMethodBody.add(itemHolderCodeHelper.clearNullsStatement())
+ with(itemHolderCodeHelper) {
+ genCacheField()
+ }
+ genProvideCachedFun(
+ function = function,
+ idArguments = idArguments,
+ itemHolderCodeHelper = itemHolderCodeHelper,
+ wrapHelper = wrapHelper,
+ )
+ genCacheControlFun(
+ function = function,
+ idArguments = idArguments,
+ itemHolderCodeHelper = itemHolderCodeHelper,
+ wrapHelper = wrapHelper,
+ )
+ }
+ }
+
+ }
+
+
+ genIModelMethods(
+ moduleCl = moduleCl,
+ identifierTypes = identifierTypes,
+ codeBlocks = codeBlocks,
+ )
+ }
+ }
+
+ return GenSpec(
+ fileSpec = fileSpec,
+ // https://kotlinlang.org/docs/ksp-incremental.html
+ dependencies = Dependencies(aggregating = false, fileOwner),
+ )
+ }
+
+
+ private fun TypeSpec.Builder.genBindInstance(
+ function: KSFunctionDeclaration,
+ idArguments: List,
+ itemHolderCodeHelper: ItemHolderCodeHelper,
+ wrapHelper: WrapHelper,
+ ) {
+ val returnType = function.returnType?.resolve()?.toTypeName() ?: return
+ val setValueArg = function.parameters.firstOrNull {
+ it.type.resolveNotNullable().toTypeName() == function.returnType?.resolveNotNullable()?.toTypeName()
+ }
+
+ genOverrideFun(function) {
+ beginControlFlow("return syncIfAvailable(%L.mutex)", itemHolderCodeHelper.fieldName)
+ addStatement(
+ "val cached = %L.get()?.%L( %T.getValueAction, %L ) ",
+ overridedModuleFieldName,
+ function.cacheControlMethodName,
+ CacheAction::class.asClassName(),
+ idArguments.joinToString(", ") { it.name!!.asString() },
+ )
+ addCode("if ( cached != null ) return@syncIfAvailable ")
+ addCode(
+ wrapHelper.transform(
+ providingType = wrapHelper.listWrapTypeIfNeed(returnType),
+ wannaType = returnType,
+ code = codeBlock { add("cached") },
+ )
+ )
+ addStatement("")
+
+ if (setValueArg != null) {
+ beginControlFlow("if (%L != null)", setValueArg.name!!.asString())
+ addCode(
+ itemHolderCodeHelper.codeSetCachedValue(
+ value = CodeBlock.of("%L", setValueArg.name!!.asString()),
+ onlyIfNull = false
+ )
+ )
+ endControlFlow();
+ }
+
+ addCode("return@syncIfAvailable ")
+ addCode(
+ wrapHelper.transform(
+ wrapHelper.listWrapTypeIfNeed(returnType).copy(nullable = true),
+ returnType,
+ itemHolderCodeHelper.codeGetCachedValue(),
+ )
+ )
+ endControlFlow()
+ }
+ }
+
+ private fun TypeSpec.Builder.genProvideCachedFun(
+ function: KSFunctionDeclaration,
+ idArguments: List,
+ itemHolderCodeHelper: ItemHolderCodeHelper,
+ wrapHelper: WrapHelper,
+ ) {
+ val returnType = function.returnType?.resolve()?.toTypeName() ?: return
+ genOverrideFun(function) {
+ beginControlFlow("return syncIfAvailable(%L.mutex)", itemHolderCodeHelper.fieldName)
+ addStatement(
+ "val cached = %L.get()?.%L( %T.getValueAction, %L ) ",
+ overridedModuleFieldName,
+ function.cacheControlMethodName,
+ CacheAction::class.asClassName(),
+ idArguments.joinToString(", ") { it.name!!.asString() },
+ )
+ addCode("if (cached != null ) return@syncIfAvailable ")
+ addCode(
+ wrapHelper.transform(
+ wrapHelper.listWrapTypeIfNeed(returnType),
+ returnType,
+ CodeBlock.of("cached"),
+ )
+ )
+ addStatement("")
+
+ // set value if null
+ val argStrList = function.parameters.joinToString(", ") { it.name!!.asString() }
+ addCode("val creator = %T{ ", Ref::class.asClassName().parameterizedBy(returnType))
+ addCode(
+ "%L.get()?.%L(%L)",
+ overridedModuleFieldName, function.simpleName.asString(), argStrList,
+ )
+ addCode(" ?: ")
+ addCode("%L.%L(%L)", factoryFieldName, function.simpleName.asString(), argStrList)
+ addCode("}\n")
+ addCode(
+ itemHolderCodeHelper.codeSetCachedValue(
+ wrapHelper.transform(
+ returnType,
+ wrapHelper.listWrapTypeIfNeed(returnType),
+ CodeBlock.of("creator.get()"),
+ ),
+ onlyIfNull = true,
+ )
+ )
+ addCode("\n")
+ addCode("return@syncIfAvailable ")
+ addCode(
+ wrapHelper.transform(
+ wrapHelper.listWrapTypeIfNeed(returnType).copy(nullable = true),
+ returnType,
+ itemHolderCodeHelper.codeGetCachedValue(),
+ )
+ )
+ endControlFlow()
+ }
+ }
+
+ private fun TypeSpec.Builder.genCacheControlFun(
+ function: KSFunctionDeclaration,
+ idArguments: List,
+ itemHolderCodeHelper: ItemHolderCodeHelper,
+ wrapHelper: WrapHelper,
+ ) {
+ val returnType = function.returnType?.resolveNotNullable()?.toTypeName() ?: return
+ val cacheControlType = wrapHelper.listWrapTypeIfNeed(returnType).copy(nullable = true)
+ genFun(function.cacheControlMethodName) {
+ modifiers.add(KModifier.OVERRIDE)
+ returns(cacheControlType.copy(nullable = true))
+ addParameter("__action", CacheAction::class)
+ idArguments.forEach {
+ addParameter(it.name!!.asString(), it.type.resolve().toTypeName())
+ }
+
+ beginControlFlow("return syncIfAvailable(%L.mutex)", itemHolderCodeHelper.fieldName)
+ addStatement(
+ "%L.get()?.%L( __action, %L ) ",
+ overridedModuleFieldName,
+ function.cacheControlMethodName,
+ idArguments.joinToString(", ") { it.name!!.asString() },
+ )
+ beginControlFlow("when (__action.type) {")
+ addStatement("%T.GET_VALUE -> Unit", CacheAction.ActionType::class)
+ //set value
+ beginControlFlow("%T.SET_VALUE ->", CacheAction.ActionType::class)
+ addCode("if ( __action.value != null ) ")
+ addCode(
+ codeBlock = itemHolderCodeHelper.codeSetCachedValue(
+ CodeBlock.of("__action.value as? %T", cacheControlType),
+ onlyIfNull = false
+ )
+ )
+ endControlFlow()
+ //set if null value
+ beginControlFlow("%T.SET_IF_NULL ->", CacheAction.ActionType::class)
+ addCode("if ( __action.value != null ) ")
+ addCode(
+ codeBlock = itemHolderCodeHelper.codeSetCachedValue(
+ CodeBlock.of("__action.value as? %T", cacheControlType),
+ onlyIfNull = true
+ )
+ )
+ endControlFlow()
+ // switch cache type
+ beginControlFlow("%T.SWITCH_CACHE ->", CacheAction.ActionType::class)
+ addCode(codeBlock = itemHolderCodeHelper.statementSwitchRef(CodeBlock.of("__action.swCacheParams!!")))
+ endControlFlow()
+
+ addStatement("null -> Unit")
+ endControlFlow()
+
+ addCode("return@syncIfAvailable ")
+ addCode(codeBlock = itemHolderCodeHelper.codeGetCachedValue())
+ endControlFlow()
+ }
+ }
+
+ private fun TypeSpec.Builder.genIModelMethods(
+ moduleCl: KSClassDeclaration,
+ identifierTypes: List,
+ codeBlocks: DelayedCodeBlocks,
+ ) {
+
+ genProperty(
+ name = factoryFieldName,
+ type = moduleCl.toClassName(),
+ ) {
+ addModifiers(KModifier.OVERRIDE)
+ mutable(true)
+ initializer("%T()", moduleCl.factoryStoneClName)
+ }
+
+ val cacheControlHolder = SingleItemHolder::class.asClassName()
+ .parameterizedBy(moduleCl.toClassName().cacheControlStoneClName)
+ genProperty(
+ name = overridedModuleFieldName,
+ type = cacheControlHolder,
+ ) {
+ mutable(true)
+ initializer("%T(%T.WeakObject)", cacheControlHolder, StoneRefType::class)
+ }
+
+ genFun(initMethodName) {
+ addModifiers(KModifier.OVERRIDE)
+ returns(BOOLEAN)
+ addParameter("or", Any::class)
+ addStatement("if (or === this) return false")
+ addStatement("var %L = false", appliedLocalFieldName)
+
+ // check module class
+ beginControlFlow(
+ "if ( (or is %T) ) ",
+ moduleCl.toClassName().cacheControlStoneClName,
+ )
+ addStatement(
+ "%L.set(onlyIfNull = false) { or }",
+ overridedModuleFieldName,
+ )
+ addStatement(
+ "%L = (or as %T).%L as %T",
+ factoryFieldName,
+ IModule::class.asClassName(),
+ factoryFieldName,
+ moduleCl.toClassName(),
+ )
+ addStatement("%L = true", appliedLocalFieldName)
+ endControlFlow() // check factory class
+ beginControlFlow("else if (or is %T) ", moduleCl.toClassName())
+ addStatement(
+ "%L = or as %T",
+ factoryFieldName,
+ moduleCl.toClassName(),
+
+ )
+ addStatement("%L = true", appliedLocalFieldName)
+ endControlFlow() // get module factory by module class
+
+ addStatement("return %L", appliedLocalFieldName)
+ }
+
+
+
+ genFun(bindMethodName) {
+ addModifiers(KModifier.OVERRIDE)
+ addParameter("or", Any::class)
+ returns(BOOLEAN)
+ addStatement("%L.get()?.%L(or)", overridedModuleFieldName, bindMethodName)
+
+ addStatement("var %L = false", appliedLocalFieldName)
+ addCode(codeBlocks.bindMethodBody.build())
+ addStatement("return %L", appliedLocalFieldName)
+ }
+
+ genFun(switchRefMethodName) {
+ addModifiers(KModifier.OVERRIDE)
+ addParameter(
+ "scopes",
+ Set::class.asClassName()
+ .parameterizedBy(
+ KClass::class.asClassName()
+ .parameterizedBy(STAR)
+ )
+ )
+ addParameter("__params", SwitchCacheParam::class)
+
+ codeBlocks.switchRefStatementBuilders.forEach { (key, value) ->
+ addCode("if (listOf(")
+ key.forEachIndexed { idx, scope ->
+ if (idx > 0) addCode(", ")
+ addCode("%T::class", scope)
+ }
+ beginControlFlow(").containsAll(scopes))")
+ addCode(value.build())
+ endControlFlow()
+ }
+ }
+
+
+ genFun(initCachesFromMethodName) {
+ addModifiers(KModifier.OVERRIDE)
+ addParameter("m", IModule::class)
+ addStatement("if (m == this) return")
+ (sequenceOf(moduleCl) + moduleCl.getAllSuperTypes().map { it.declaration })
+ .filter { it.annotations(Module::class.asClassName()).any() }
+ .mapNotNull { it as? KSClassDeclaration }
+ .forEach { cl ->
+ val cacheControlCl = cl.toClassName().cacheControlStoneClName
+ beginControlFlow("if ( m is %T )", cacheControlCl)
+ addStatement("val module = m as %T", cacheControlCl)
+
+ cl.getAllMethods(
+ includeObjectMethods = false,
+ allowDoubles = false,
+ exceptNames = arrayOf(""),
+ ).forEach { protoProvideMethod ->
+ val cacheControlMethod = protoProvideMethod.cacheControlMethodName
+ val idArguments = protoProvideMethod.parameters.identifierParameters(identifierTypes)
+ if (idArguments.isNotEmpty()) {
+ // TODO https://github.com/klee0kai/stone/issues/42
+ return@forEach
+ }
+
+ addStatement(
+ "%L( %T.setIfNullValueAction( module.%L( %T.getValueAction ) ) )",
+ cacheControlMethod, CacheAction::class,
+ cacheControlMethod, CacheAction::class,
+ );
+ }
+ endControlFlow()
+ }
+ }
+
+ genFun(updateBindInstancesFrom) {
+ addModifiers(KModifier.OVERRIDE)
+ addParameter("m", IModule::class)
+ addStatement("if (m == this) return")
+ moduleCl.getAllSuperTypes()
+
+ (sequenceOf(moduleCl) + moduleCl.getAllSuperTypes().map { it.declaration })
+ .filter { it.annotations(Module::class.asClassName()).any() }
+ .mapNotNull { it as? KSClassDeclaration }
+ .forEach { cl ->
+ val cacheControlCl = cl.toClassName().cacheControlStoneClName
+ beginControlFlow("if ( m is %T )", cacheControlCl)
+ addStatement("val module = m as %T", cacheControlCl)
+
+ cl.getAllMethods(
+ includeObjectMethods = false,
+ allowDoubles = false,
+ exceptNames = arrayOf(""),
+ ).forEach { protoProvideMethod ->
+ if (protoProvideMethod.anyAnnotation(BindInstance::class.asClassName()).none()) {
+ return@forEach
+ }
+
+ val cacheControlMethod = protoProvideMethod.cacheControlMethodName
+ val idArguments = protoProvideMethod.parameters.identifierParameters(identifierTypes)
+ if (idArguments.isNotEmpty()) {
+ // TODO https://github.com/klee0kai/stone/issues/42
+ return@forEach
+ }
+
+ addStatement(
+ "%L( %T.setValueAction( module.%L( %T.getValueAction ) ) )",
+ cacheControlMethod, CacheAction::class,
+ cacheControlMethod, CacheAction::class,
+ );
+ }
+ endControlFlow()
+ }
+ }
+
+ genFun(clearNullsMethodName) {
+ addModifiers(KModifier.OVERRIDE)
+ addCode(codeBlocks.clearNullsMethodBody.build())
+ }
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/provider/GenProviderProcessor.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/provider/GenProviderProcessor.kt
new file mode 100644
index 00000000..a6fb7561
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/provider/GenProviderProcessor.kt
@@ -0,0 +1,152 @@
+@file:OptIn(KspExperimental::class)
+
+package com.github.klee0kai.thekey.stone.ksp.target.provider
+
+import com.github.klee0kai.stone.annotations.module.Module
+import com.github.klee0kai.stone.annotations.module.Provide
+import com.github.klee0kai.thekey.stone.ksp.exceptions.forEachFun
+import com.github.klee0kai.thekey.stone.ksp.helpers.allIdentifierTypes
+import com.github.klee0kai.thekey.stone.ksp.helpers.findComponentForModuleOrDep
+import com.github.klee0kai.thekey.stone.ksp.helpers.identifierParameters
+import com.github.klee0kai.thekey.stone.ksp.helpers.qualifierAnnotations
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.GenSpec
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.SymbolsToProcess
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.TargetFileProcessor
+import com.github.klee0kai.thekey.stone.ksp.ksp.fileText
+import com.github.klee0kai.thekey.stone.ksp.ksp.genProviderNameProtected
+import com.github.klee0kai.thekey.stone.ksp.ksp.getAllMethods
+import com.github.klee0kai.thekey.stone.ksp.ksp.provideWrapperProtected
+import com.github.klee0kai.thekey.stone.ksp.poet.*
+import com.github.klee0kai.thekey.stone.ksp.psi.InMemoryPsiParser
+import com.github.klee0kai.thekey.stone.ksp.psi.isSamePlace
+import com.github.klee0kai.thekey.stone.ksp.target.isSuspend
+import com.google.devtools.ksp.KspExperimental
+import com.google.devtools.ksp.containingFile
+import com.google.devtools.ksp.getAnnotationsByType
+import com.google.devtools.ksp.processing.Dependencies
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.ParameterSpec
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.asClassName
+import com.squareup.kotlinpoet.ksp.toClassName
+import com.squareup.kotlinpoet.ksp.toTypeName
+import org.jetbrains.kotlin.psi.KtClass
+import org.jetbrains.kotlin.psi.KtNamedFunction
+
+class GenProviderProcessor : TargetFileProcessor {
+
+ override suspend fun findSymbolsToProcess(
+ resolver: Resolver,
+ ) = SymbolsToProcess(
+ symbolsForProcessing = resolver
+ .getSymbolsWithAnnotation(Module::class.asClassName().canonicalName)
+ .toList(),
+ symbolsForReprocessing = emptyList(),
+ )
+
+ override suspend fun process(
+ validSymbol: KSAnnotated,
+ resolver: Resolver,
+ options: Map,
+ logger: KSPLogger
+ ): GenSpec? {
+ val fileOwner = validSymbol.containingFile ?: return null
+ val moduleCl = validSymbol as? KSClassDeclaration ?: return null
+
+ val moduleAnn = moduleCl.getAnnotationsByType(Module::class)
+ .firstOrNull() ?: return null
+
+ val componentCl = resolver.findComponentForModuleOrDep(moduleCl.toClassName())
+ .firstOrNull()
+
+ val identifierTypes = componentCl
+ ?.allIdentifierTypes?.toList()
+ ?: emptyList()
+
+ if (moduleAnn.genProviderNameProtected.isNullOrBlank()) return null
+
+
+ val genProvideClName = ClassName(
+ moduleCl.packageName.asString(),
+ moduleAnn.genProviderName,
+ )
+
+ val filePsi = InMemoryPsiParser(moduleCl.location.fileText())
+
+ val fileSpec = genFileSpec(moduleCl.packageName.asString(), genProvideClName.simpleName) {
+ genLibComment()
+
+ genInterface(genProvideClName) {
+ validSymbol.getAllMethods(false, false, "")
+ .forEachFun { funIdx, func ->
+ val idArguments = func.parameters.identifierParameters(identifierTypes)
+ val returnType = func.returnType?.resolve()?.toTypeName() ?: return@forEachFun
+
+
+ val provideWrapper = func.getAnnotationsByType(Provide::class)
+ .firstOrNull()
+ ?.provideWrapperProtected
+ ?.takeIf { it !in listOf(Nothing::class, Void::class) }
+
+ val providingType = provideWrapper?.asClassName()?.parameterizedBy(returnType)
+ ?: returnType
+
+ genFun(func.simpleName.asString()) {
+ if (func.isSuspend) addModifiers(KModifier.SUSPEND)
+ val psiFunc by lazy {
+ filePsi.ktFile.declarations
+ .filterIsInstance()
+ .firstOrNull()
+ ?.declarations
+ ?.filterIsInstance()
+ ?.firstOrNull { it.isSamePlace(func) }
+ }
+ addModifiers(KModifier.ABSTRACT)
+ returns(providingType)
+ func.qualifierAnnotations.forEach {
+ addAnnotation(it)
+ }
+
+ idArguments.forEach { param ->
+ addParameter(
+ ParameterSpec.builder(
+ name = param.name?.asString() ?: "",
+ type = param.type.resolve().toTypeName(),
+ ).apply {
+ if (param.isVararg) addModifiers(KModifier.VARARG)
+
+ if (param.hasDefault && psiFunc != null) {
+ val psiParam = psiFunc
+ ?.valueParameters
+ ?.firstOrNull { it.name == param.name?.asString() }
+
+ if (!psiParam?.defaultValue?.text.isNullOrBlank()) {
+ defaultValue("%L", psiParam.defaultValue?.text)
+ }
+ }
+ }.build()
+ )
+ }
+ }
+ }
+
+
+ }
+ }
+
+
+ filePsi.close()
+
+ return GenSpec(
+ fileSpec = fileSpec,
+ // https://kotlinlang.org/docs/ksp-incremental.html
+ dependencies = Dependencies(aggregating = false, fileOwner),
+ )
+ }
+
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/wrapper/GenWrappersSupportProcessor.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/wrapper/GenWrappersSupportProcessor.kt
new file mode 100644
index 00000000..8ce49fe6
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/target/wrapper/GenWrappersSupportProcessor.kt
@@ -0,0 +1,80 @@
+package com.github.klee0kai.thekey.stone.ksp.target.wrapper
+
+import com.github.klee0kai.stone.annotations.component.Component
+import com.github.klee0kai.thekey.stone.ksp.exceptions.forEachType
+import com.github.klee0kai.thekey.stone.ksp.helpers.allParentDeclarations
+import com.github.klee0kai.thekey.stone.ksp.helpers.annotations.findComponentAnnotation
+import com.github.klee0kai.thekey.stone.ksp.helpers.wrapperStoneClName
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.GenSpec
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.SymbolsToProcess
+import com.github.klee0kai.thekey.stone.ksp.ksp.arch.TargetFileProcessor
+import com.github.klee0kai.thekey.stone.ksp.poet.genFileSpec
+import com.github.klee0kai.thekey.stone.ksp.poet.genLibComment
+import com.github.klee0kai.thekey.stone.ksp.poet.genObject
+import com.github.klee0kai.thekey.stone.ksp.poet.genProperty
+import com.google.devtools.ksp.containingFile
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.squareup.kotlinpoet.asClassName
+import com.squareup.kotlinpoet.ksp.toClassName
+import com.google.devtools.ksp.processing.Dependencies as KspDependencies
+
+
+class GenWrappersSupportProcessor : TargetFileProcessor {
+
+ companion object {
+ val provideWrappersGlFieldPrefixName = "__wrapperCreator"
+
+
+ val allReserveMethodNames = listOf(
+ provideWrappersGlFieldPrefixName,
+ )
+ }
+
+ override suspend fun findSymbolsToProcess(
+ resolver: Resolver
+ ) = SymbolsToProcess(
+ symbolsForProcessing = resolver
+ .getSymbolsWithAnnotation(Component::class.asClassName().canonicalName)
+ .toList(),
+ symbolsForReprocessing = emptyList(),
+ )
+
+ override suspend fun process(
+ validSymbol: KSAnnotated,
+ resolver: Resolver,
+ options: Map,
+ logger: KSPLogger
+ ): GenSpec? {
+ val fileOwner = validSymbol.containingFile ?: return null
+ val componentCl = validSymbol as? KSClassDeclaration ?: return null
+ val wrapperProviders = componentCl.allParentDeclarations
+ .filter { it.findComponentAnnotation().any() }
+ .flatMap { parentComponentCl ->
+ parentComponentCl.findComponentAnnotation().firstOrNull()?.wrapperProviders ?: emptyList()
+ }
+
+ val wrapperCreatorClName = componentCl.wrapperStoneClName
+ val fileSpec = genFileSpec(wrapperCreatorClName.packageName, wrapperCreatorClName.simpleName) {
+ genLibComment()
+
+ genObject(wrapperCreatorClName) {
+ wrapperProviders.forEachType { index, provideWrappersCl ->
+ val name = provideWrappersGlFieldPrefixName + index
+
+ genProperty(name, provideWrappersCl.toClassName()) {
+ initializer("%T()", provideWrappersCl.toClassName())
+ }
+ }
+ }
+ }
+
+ return GenSpec(
+ fileSpec = fileSpec,
+ // https://kotlinlang.org/docs/ksp-incremental.html
+ dependencies = KspDependencies(aggregating = false, fileOwner),
+ )
+ }
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/utils/CollectionExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/utils/CollectionExt.kt
new file mode 100644
index 00000000..c74c1b83
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/utils/CollectionExt.kt
@@ -0,0 +1,15 @@
+package com.github.klee0kai.thekey.stone.ksp.utils
+
+import java.util.*
+
+
+fun List.removeDoubles(
+ compare: (T, T) -> Boolean,
+): List {
+ val out = LinkedList()
+ for (item in this) {
+ val contains = out.any { compare.invoke(item, it) }
+ if (!contains) out.add(item)
+ }
+ return out.toList()
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/utils/CommonExt.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/utils/CommonExt.kt
new file mode 100644
index 00000000..a6151ea0
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/utils/CommonExt.kt
@@ -0,0 +1,10 @@
+package com.github.klee0kai.thekey.stone.ksp.utils
+
+inline fun T.then(
+ condition: Boolean,
+ block: T.() -> T,
+) = if (condition) {
+ block()
+} else {
+ this
+}
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/utils/LocalFieldName.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/utils/LocalFieldName.kt
new file mode 100644
index 00000000..80c94406
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/utils/LocalFieldName.kt
@@ -0,0 +1,6 @@
+package com.github.klee0kai.thekey.stone.ksp.utils
+
+object LocalFieldName {
+ private var localVariableIndx: Long = 0
+ fun genLocalFieldName() = "lc" + localVariableIndx++
+}
\ No newline at end of file
diff --git a/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/utils/RecursiveDetector.kt b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/utils/RecursiveDetector.kt
new file mode 100644
index 00000000..850111af
--- /dev/null
+++ b/stone_ksp/src/commonMain/kotlin/com/github/klee0kai/thekey/stone/ksp/utils/RecursiveDetector.kt
@@ -0,0 +1,35 @@
+package com.github.klee0kai.thekey.stone.ksp.utils
+
+import java.util.*
+
+/**
+ * A tortoise is chasing the hare.
+ * If the sequence is looped, then the hare will meet the tortoise on the next circle
+ *
+ * @param sequence type
+ */
+class RecursiveDetector {
+
+ private val race = LinkedList()
+ private var hareStep: Long = 0
+
+
+ /**
+ * Do next step.
+ *
+ * @param next next item in sequence
+ * @return true if sequence is cycled
+ */
+ fun next(next: T?): Boolean {
+ race.add(next)
+ if (hareStep++ % 2 == 0L) {
+ race.pollFirst()
+ }
+ if (race.size <= 2) return false
+
+ val hare = next
+ val turtle = race.first()
+ return hare == turtle
+ }
+
+}
diff --git a/stone_ksp/src/commonMain/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/stone_ksp/src/commonMain/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..0745d06f
--- /dev/null
+++ b/stone_ksp/src/commonMain/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+com.github.klee0kai.thekey.stone.ksp.StoneProcessorProvider
\ No newline at end of file
diff --git a/stone_lib/build.gradle b/stone_lib/build.gradle
deleted file mode 100644
index ca085034..00000000
--- a/stone_lib/build.gradle
+++ /dev/null
@@ -1,43 +0,0 @@
-plugins {
- id 'java'
-}
-
-apply from: '../jitpack.gradle'
-
-java {
- withSourcesJar()
- withJavadocJar()
-}
-
-javadoc {
- exclude("com/github/klee0kai/stone/closed/")
-}
-
-publishing {
- publications {
- maven(MavenPublication) {
- from components.java
- }
- }
-}
-
-dependencies {
-
- // https://mvnrepository.com/artifact/javax.inject/javax.inject
- apiElements 'javax.inject:javax.inject:1'
- implementation 'javax.inject:javax.inject:1'
-
-
- testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.1'
- testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.1'
-}
-
-test {
- useJUnitPlatform()
-}
-
-
-java {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
-}
\ No newline at end of file
diff --git a/stone_lib/src/main/java/com/github/klee0kai/stone/Stone.java b/stone_lib/src/main/java/com/github/klee0kai/stone/Stone.java
deleted file mode 100644
index 29eb1d44..00000000
--- a/stone_lib/src/main/java/com/github/klee0kai/stone/Stone.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.github.klee0kai.stone;
-
-import java.lang.reflect.InvocationTargetException;
-
-public class Stone {
-
- /**
- * Create a new Stone component
- */
- public static T createComponent(Class component) {
- try {
- Class> gennedClass = Class.forName(component.getCanonicalName() + "StoneComponent");
- return (T) gennedClass.getConstructors()[0].newInstance();
- } catch (ClassNotFoundException | InstantiationException | IllegalAccessException |
- InvocationTargetException e) {
- throw new RuntimeException(e);
- }
- }
-
-
-
-}
diff --git a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/IModule.java b/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/IModule.java
deleted file mode 100644
index 95201ebd..00000000
--- a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/IModule.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.github.klee0kai.stone._hidden_;
-
-import com.github.klee0kai.stone._hidden_.types.SwitchCacheParam;
-
-import java.util.Set;
-
-/**
- * Stone Private class
- */
-public interface IModule {
-
- /**
- * Init module
- *
- * @param ob can be:
- * - a factory instance
- * - a factory class
- */
- boolean __init(Object ob);
-
- /**
- * Init caches from module prototype.
- * using in extOf method
- */
- void __initCachesFrom(IModule module);
-
- /**
- * Update values of bindInstance variables
- *
- * @param module related module, source to update
- */
- void __updateBindInstancesFrom(IModule module);
-
- /**
- * bind instance objects
- *
- * @param object - An instance of bindable objects
- */
- boolean __bind(Object object);
-
- /**
- * get component's factory
- *
- * @return
- */
- Object __getFactory();
-
- /**
- * Switch cache type for scope
- */
- void __switchRef(Set scopes, SwitchCacheParam param);
-
- /**
- * Clear null refs.
- * Useful after gc
- */
- void __clearNulls();
-
-
-}
diff --git a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/IModuleFactory.java b/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/IModuleFactory.java
deleted file mode 100644
index 6fcb4bf4..00000000
--- a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/IModuleFactory.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.github.klee0kai.stone._hidden_;
-
-/**
- * Stone Private class
- */
-public interface IModuleFactory {
-
-}
diff --git a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/IPrivateComponent.java b/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/IPrivateComponent.java
deleted file mode 100644
index 6c1abc51..00000000
--- a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/IPrivateComponent.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.github.klee0kai.stone._hidden_;
-
-
-import com.github.klee0kai.stone._hidden_.types.StoneCallback;
-
-/**
- * Private Stone class
- * Each Stone component implement this interface.
- */
-public interface IPrivateComponent {
-
- /**
- * init modules.
- *
- * @param modules can be:
- * - a factory instance
- * - a factory class
- * @deprecated Create init method with module type as argument
- */
- void __init(Object... modules);
-
- /**
- * init dependencies
- *
- * @param dependencies - An instance of dependencies
- * @deprecated Create init method with dependency type as argument
- */
- void __initDependencies(Object... dependencies);
-
- /**
- * bind instance objects
- *
- * @param objects - An instance of bindable objects
- */
- void __bind(Object... objects);
-
- /**
- * this component extends of other
- */
- void __extOf(IPrivateComponent components);
-
- /**
- * hidden module
- */
- IModule __hidden();
-
- /**
- *
- * @param callback
- */
- void __eachModule(StoneCallback callback);
-
-}
diff --git a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/provide/ProvideBuilder.java b/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/provide/ProvideBuilder.java
deleted file mode 100644
index a8a834e8..00000000
--- a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/provide/ProvideBuilder.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.github.klee0kai.stone._hidden_.provide;
-
-import java.util.List;
-
-public class ProvideBuilder {
-
- public interface ProvideBody {
- void provide(ProvideConsumer consumer);
- }
-
- private ProvideBody provideBody;
-
- public ProvideBuilder(ProvideBody provideBody) {
- this.provideBody = provideBody;
- }
-
- public T first() {
- ProvideConsumer consumer = new ProvideConsumer<>();
- provideBody.provide(consumer);
- return consumer.getFirst();
- }
-
- public List all() {
- ProvideConsumer consumer = new ProvideConsumer<>();
- provideBody.provide(consumer);
- return consumer.getList();
- }
-}
diff --git a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/provide/ProvideConsumer.java b/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/provide/ProvideConsumer.java
deleted file mode 100644
index 2288bf2f..00000000
--- a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/provide/ProvideConsumer.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.github.klee0kai.stone._hidden_.provide;
-
-import com.github.klee0kai.stone._hidden_.types.ListUtils;
-
-import java.util.Collection;
-import java.util.LinkedList;
-
-public class ProvideConsumer {
-
- private LinkedList list = new LinkedList<>();
-
- public void addAll(Collection collection) {
- if (collection != null) {
- list.addAll(ListUtils.filter(collection, (i, it) -> it != null));
- }
- }
-
- public void add(T element) {
- if (element != null) {
- list.add(element);
- }
- }
-
- public LinkedList getList() {
- return list;
- }
-
- public T getFirst() {
- return list.isEmpty() ? null : list.getFirst();
- }
-
-
-}
diff --git a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/types/CacheAction.java b/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/types/CacheAction.java
deleted file mode 100644
index 4f85c0b3..00000000
--- a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/types/CacheAction.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package com.github.klee0kai.stone._hidden_.types;
-
-import com.github.klee0kai.stone.annotations.component.SwitchCache;
-
-/**
- * Stone Private class
- */
-public class CacheAction {
-
- public final ActionType type;
- public final SwitchCacheParam swCacheParams;
- public final Object value;
-
- public CacheAction(ActionType type, SwitchCacheParam swCacheParams, Object value) {
- this.type = type;
- this.swCacheParams = swCacheParams;
- this.value = value;
- }
-
-
- public static CacheAction getValueAction() {
- return new CacheAction(ActionType.GET_VALUE, null, null);
- }
-
- public static CacheAction setValueAction(Object value) {
- return new CacheAction(ActionType.SET_VALUE, null, value);
- }
-
- public static CacheAction setIfNullValueAction(Object value) {
- return new CacheAction(ActionType.SET_IF_NULL, null, value);
- }
-
- public static CacheAction switchCacheValueAction(SwitchCacheParam param) {
- return new CacheAction(ActionType.SWITCH_CACHE, param, null);
- }
-
- public static CacheAction switchCacheToDefAction() {
- return new CacheAction(ActionType.SWITCH_CACHE,
- new SwitchCacheParam(SwitchCache.CacheType.Default, 0, null),
- null
- );
- }
-
- public boolean isGetAction() {
- return type == ActionType.GET_VALUE;
- }
-
- public boolean isSetAction() {
- return type == ActionType.SET_VALUE;
- }
-
- public boolean isSetIfNullAction() {
- return type == ActionType.SET_IF_NULL;
- }
-
- public boolean isSwitchCacheAction() {
- return type == ActionType.SWITCH_CACHE;
- }
-
-
- public enum ActionType {
- GET_VALUE,
- SET_VALUE,
- SET_IF_NULL,
- SWITCH_CACHE,
- }
-
-}
-
-
diff --git a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/types/ListUtils.java b/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/types/ListUtils.java
deleted file mode 100644
index 6a43c8a6..00000000
--- a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/types/ListUtils.java
+++ /dev/null
@@ -1,154 +0,0 @@
-package com.github.klee0kai.stone._hidden_.types;
-
-import java.util.*;
-
-/**
- * Stone Private class
- */
-public class ListUtils {
-
- public interface IFormat {
- Tout format(Tin it);
- }
-
- public interface ICompare {
- int compare(T it1, T it2);
- }
-
-
- public interface IFilter {
- boolean filter(int inx, T it);
- }
-
- public interface IEq {
- boolean eq(T it1, T it2);
- }
-
- public static List format(Iterable list, IFormat format) {
- if (list == null) return null;
- LinkedList touts = new LinkedList<>();
- if (list != null) for (Tin it : list) {
- touts.add(format.format(it));
- }
- return touts;
- }
-
- public static boolean contains(List list, IFilter filter) {
- int idx = 0;
- if (list != null) for (T it : list) {
- if (filter.filter(idx++, it))
- return true;
- }
- return false;
- }
-
- public static T first(Iterable list, IFilter filter) {
- int idx = 0;
- if (list != null) for (T it : list) {
- if (filter == null || filter.filter(idx++, it))
- return it;
- }
- return null;
- }
-
- public static T first(Iterable list) {
- return first(list, null);
- }
-
- public static int indexOf(List list, IFilter filter) {
- int idx = 0;
- if (list != null) for (T it : list) {
- if (filter.filter(idx, it))
- return idx;
- else idx++;
- }
- return -1;
- }
-
- public static Tout firstNotNull(List list, IFormat format) {
- if (list != null) for (Tin it : list) {
- Tout out = format.format(it);
- if (out != null) return out;
- }
- return null;
- }
-
- public static LinkedList filter(Collection list, IFilter filter) {
- if (list == null) return null;
- LinkedList touts = new LinkedList<>();
- int idx = 0;
- if (list != null) for (T it : list) {
- if (filter.filter(idx++, it))
- touts.add(it);
- }
- return touts;
- }
-
- public static void orderedAdd(List list, T item, ICompare compare) {
- ListIterator itr = list.listIterator();
- while (true) {
- if (!itr.hasNext()) {
- itr.add(item);
- return;
- }
-
- T elementInList = itr.next();
- if (compare.compare(elementInList, item) > 0) {
- itr.previous();
- itr.add(item);
- return;
- }
- }
- }
-
- public static Set setOf(List list, T... items) {
- Set set = new HashSet<>();
- set.addAll(list);
- set.addAll(Arrays.asList(items));
- return set;
- }
-
- public static boolean endWith(List parentList, List childList) {
- if (parentList == null || childList == null) return false;
- if (childList.size() > parentList.size()) return false;
- int pSt = parentList.size() - childList.size();
-
- Iterator ch = childList.listIterator();
- Iterator p = parentList.listIterator(pSt);
- while (ch.hasNext() && p.hasNext()) {
- if (!Objects.equals(p.next(), ch.next()))
- return false;
- }
- return !ch.hasNext() && !p.hasNext();
- }
-
- public static LinkedList removeDoubles(List list, IEq eqHelper) {
- if (list == null) return null;
- LinkedList out = new LinkedList<>();
- for (T item : list) {
- boolean contains = ListUtils.contains(out, (i, it) -> eqHelper.eq(item, it));
- if (!contains) out.add(item);
- }
- return out;
- }
-
- public static LinkedList removeDoublesRight(List list, IEq eqHelper) {
- if (list == null) return null;
- LinkedList out = new LinkedList<>();
- for (T item : list) {
- out = ListUtils.filter(out, (i, it) -> !eqHelper.eq(item, it));
- out.add(item);
- }
- return out;
- }
-
- public static boolean listAreSame(List list1, List list2, IEq eqHelper) {
- if (list1 == list2) return true;
- if ((list2 == null) != (list1 == null)) return false;
- if (list1.size() != list2.size()) return false;
- for (T it1 : list1) if (!list2.contains(it1)) return false;
- return true;
- }
-
-
-}
diff --git a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/types/MultiKey.java b/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/types/MultiKey.java
deleted file mode 100644
index 027c1560..00000000
--- a/stone_lib/src/main/java/com/github/klee0kai/stone/_hidden_/types/MultiKey.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.github.klee0kai.stone._hidden_.types;
-
-import java.util.*;
-
-/**
- * Stone Private class
- */
-
-public class MultiKey {
-
- private final List