Skip to content

Commit 514d364

Browse files
committed
Initial commit
0 parents  commit 514d364

File tree

207 files changed

+6054
-0
lines changed

Some content is hidden

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

207 files changed

+6054
-0
lines changed

.editorconfig

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
root = true
2+
3+
[*]
4+
indent_size = 2
5+
max_line_length = 80

.gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.idea
2+
.gradle
3+
build
4+
out
5+
*.iml
6+
testdata
7+
composite_build.local
8+
*.hprof

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 Benny Huo
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# 《深入实践 Kotlin 元编程》随书源码
2+
3+
## 环境
4+
5+
* JDK 17
6+
* IntelliJ IDEA 2022.3.2
7+
* Kotlin 1.8.10
8+
9+
## 说明
10+
11+
本书绝大多数代码清单的内容都可以在这里找到。
12+
13+
书中限于篇幅,通常会对一些较长的代码做出省略,这里会尽可能地给出可以直接运行的程序。
14+
15+
为了方便读者找到对应的代码,我会将代码清单的编号作为包名,例如 com.bennyhuo.list4_20,表示代码清单 4-20 的代码在这个包中。如果包名不适合添加编号信息,我也会在代码前面用一行注释写出清单的编号,例如 `// 4-20`,表示以下代码对应于书中的代码清单 4-20。
16+
17+
当然,也有部分代码清单的代码属于某个开源项目,例如 KotlinTuples,书中对应的源码将不在这里列出,读者可以按照下面给出的链接找到对应的项目去阅读其中的源码。
18+
19+
如果遇到问题,也欢迎提 issue,我有空的时候会尽量帮你分析。
20+
21+
## 章节列表
22+
23+
### [第一章 元编程概述](chapter1)
24+
25+
* [JDK 源码编译环境配置 - 视频](https://www.bilibili.com/video/BV1Qj411P7eZ/)
26+
* [Jetpack Compose 环境配置 - 视频](TODO)
27+
28+
### [第二章 元数据简介](chapter2)
29+
30+
### [第三章 运行时的反射](chapter3)
31+
32+
相关开源项目:
33+
34+
* [Retrofit](https://github.com/square/retrofit)
35+
* [KotlinDeepCopy](https://github.com/bennyhuo/KotlinDeepCopy)
36+
* [JVM 反射实现](https://github.com/bennyhuo/KotlinDeepCopy/blob/master/reflect-impl/src/main/kotlin/com/bennyhuo/kotlin/deepcopy/reflect/DeepCopy.kt)
37+
* [JS 反射实现](https://github.com/bennyhuo/KotlinDeepCopy/blob/master/reflect-impl-js/src/main/kotlin/com/bennyhuo/kotlin/deepcopy/DeepCopy.kt)
38+
39+
### [第四章 源代码的生成](chapter4)
40+
41+
相关开源项目:
42+
43+
* [KotlinTuples](https://github.com/bennyhuo/KotlinTuples)
44+
* [Anko-Legacy/anko-asm-parser](https://github.com/bennyhuo/anko-legacy)
45+
* [Detekt](https://github.com/detekt/detekt)
46+
* [JavaPoet](https://github.com/square/javapoet)
47+
* [KotlinPoet](https://github.com/square/kotlinpoet)
48+
49+
### [第五章 编译时的符号处理](chapter5)
50+
51+
相关开源项目:
52+
53+
* [symbol-processing-module-support](https://github.com/bennyhuo/symbol-processing-module-support)
54+
* [KotlinDeepCopy](https://github.com/bennyhuo/KotlinDeepCopy)
55+
* [APT](https://github.com/bennyhuo/KotlinDeepCopy/tree/master/compiler/compiler-apt)
56+
* [KSP](https://github.com/bennyhuo/KotlinDeepCopy/tree/master/compiler/compiler-ksp)
57+
58+
### [第六章 程序静态分析](chapter6)
59+
60+
* 使用 detekt 扫描程序,可以运行 :chapter6:app:detektMain 来查看效果。注意,运行所在的工作目录需要为 chapter6/app。
61+
* 体验 dataclass-inspection 的效果,可以运行 :chapter6:dataclass-inspection:runIde 来运行一个调试用的 IntelliJ。
62+
63+
### 第七章 编译器插件
64+
65+
* allopen
66+
* [编译器插件源码](https://github.com/JetBrains/kotlin/tree/master/plugins/allopen)
67+
* [IntelliJ 插件源码](https://github.com/JetBrains/intellij-community/tree/master/plugins/kotlin/compiler-plugins/allopen)
68+
* [TrimIndent](https://github.com/bennyhuo/Kotlin-Trim-Indent)
69+
* [DeepCopy](https://github.com/bennyhuo/KotlinDeepCopy/tree/master/kcp-impl)
70+
* [KAPT](https://github.com/jetbrains/kotlin/tree/master/plugins/kapt3)
71+
* [KSP](https://github.com/google/ksp)
72+
73+
### 第八章 元程序的开发和调试
74+
75+
本章主要介绍了常见的单元测试框架的用法,涉及到的框架如下:
76+
77+
* [tschuchortdev/kotlin-compile-testing](https://github.com/tschuchortdev/kotlin-compile-testing):Kotlin 编译器测试框架,原仓库
78+
* [ZacSweers/kotlin-compile-testing](https://github.com/ZacSweers/kotlin-compile-testing):Kotlin 编译器测试框架,Fork 仓库,由 moshi 的编译器插件作者/Kotlin 社区非常活跃的开发者维护,版本更新更快
79+
* [bennyhuo/kotlin-compile-testing-extensions](https://github.com/bennyhuo/kotlin-compile-testing-extensions):Kotlin 编译器测试框架的扩展,简化了测试任务的集成流程,提供了较为灵活的结果检查机制
80+
81+
所有单测 case 的编写方法可以参见 DeepCopy 项目的单测:
82+
* [KAPT/KSP](https://github.com/bennyhuo/KotlinDeepCopy/tree/master/test-common/src/test/kotlin/com/benyhuo/kotlin/deepcopy/compiler)
83+
* [KCP](https://github.com/bennyhuo/KotlinDeepCopy/tree/master/kcp-impl/compiler-kcp/src/test/kotlin/com/bennyhuo/kotlin/kcp/deepcopy/compiler)
84+
85+
### 第九章 Jetpack Compose 的编译时处理
86+
87+
本章源码在 AOSP 中,读者可以直接在 AOSP 在线源代码阅读平台上直接阅读这部分源码:[androidx-main:compose](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/compiler/),也可以按照本书第一章提供的方法下载源码阅读。
88+
89+
### [第十章 AtomicFU 的编译产物处理](chapter10)
90+
91+
本章涉及到的 AtomicFU 的源码可以参见我 Fork 的版本:[kotlinx-atomicfu](https://github.com/bennyhuo/kotlinx-atomicfu)

build.gradle.kts

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
plugins {
2+
kotlin("jvm") version Version.kotlin apply false
3+
kotlin("android") version Version.kotlin apply false
4+
kotlin("multiplatform") version Version.kotlin apply false
5+
kotlin("plugin.serialization") version Version.kotlin apply false
6+
kotlin("plugin.allopen") version Version.kotlin apply false
7+
8+
id("com.google.devtools.ksp") version Version.ksp apply false
9+
id("org.jetbrains.dokka") version Version.dokka apply false
10+
11+
id("com.android.application") version Version.android apply false
12+
id("com.android.library") version Version.android apply false
13+
}
14+
15+
group = "com.bennyhuo.kotlin"
16+
version = "1.0-SNAPSHOT"
17+
18+
allprojects {
19+
repositories {
20+
maven("https://mirrors.tencent.com/nexus/repository/maven-public/")
21+
mavenCentral()
22+
23+
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
24+
maven("https://oss.sonatype.org/content/repositories/snapshots/")
25+
}
26+
}

buildSrc/build.gradle.kts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
plugins {
2+
`kotlin-dsl`
3+
}
4+
5+
repositories {
6+
maven("https://mirrors.tencent.com/nexus/repository/maven-public/")
7+
}

buildSrc/src/main/kotlin/Version.kt

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
object Version {
2+
const val kotlin = "1.8.10"
3+
const val kotlinCoroutine = ""
4+
const val kotlinxMetadataJvm = "0.6.0"
5+
6+
const val dokka = "1.7.10"
7+
8+
const val android = "7.2.0"
9+
10+
const val ksp = "1.8.10-1.0.9"
11+
12+
const val javaPoet = "1.13.0"
13+
const val kotlinPoet = "1.12.0"
14+
15+
const val grammarAntlr = "1.8.0-SNAPSHOT"
16+
const val kotlinCodeAnalyzer = "1.8.0-SNAPSHOT"
17+
18+
const val detekt = "1.22.0"
19+
}

chapter1/build.gradle.kts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
plugins {
2+
kotlin("jvm")
3+
}
4+
5+
dependencies {
6+
implementation("com.squareup.okhttp3:okhttp:4.10.0")
7+
implementation("com.squareup.moshi:moshi:1.14.0")
8+
implementation("com.squareup.moshi:moshi-kotlin:1.14.0")
9+
10+
implementation("com.squareup.retrofit2:retrofit:2.9.0")
11+
implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
12+
implementation("com.squareup.moshi:moshi-kotlin:1.13.0")
13+
14+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.2")
15+
}

chapter1/gradle.properties

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
kotlinx.atomicfu.enableIrTransformation=true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.bennyhuo.list1_1;
2+
3+
// 1-1
4+
public class User {
5+
private long id;
6+
private String name;
7+
private int age;
8+
9+
public long getId() {
10+
return id;
11+
}
12+
13+
public void setId(long id) {
14+
this.id = id;
15+
}
16+
17+
public String getName() {
18+
return name;
19+
}
20+
21+
public void setName(String name) {
22+
this.name = name;
23+
}
24+
25+
public int getAge() {
26+
return age;
27+
}
28+
29+
public void setAge(int age) {
30+
this.age = age;
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# 1-12
2+
cd $ws
3+
git clone https://github.com/JetBrains/kotlin.git
4+
5+
# 1-13
6+
git config core.longpaths true
7+
8+
# 1-14
9+
kotlin.build.isObsoleteJdkOverrideEnabled=true
10+
11+
# 1-15
12+
./gradlew dist
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# 1-16
2+
cd $ws
3+
git clone https://github.com/JetBrains/intellij-community.git
4+
git checkout 222.4345
5+
6+
# 1-17
7+
cd $ws/intellij-community
8+
git clone git://git.jetbrains.org/idea/android.git android
9+
cd android
10+
git checkout 222.4345
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# 1-19
2+
test -d ~/bin || mkdir ~/bin
3+
curl https://storage.googleapis.com/git-repo-downloads/repo \
4+
> ~/bin/repo && chmod 700 ~/bin/repo
5+
6+
# 1-20
7+
export PATH=~/bin:$PATH
8+
9+
# 1-21
10+
function repo() {
11+
command python3 ~/bin/repo $@
12+
}
13+
14+
# 1-22
15+
cd $ws
16+
mkdir androidx-main && cd androidx-main
17+
# 初始化仓库
18+
repo init -u https://android.googlesource.com/platform/manifest \
19+
-b androidx-main --partial-clone --clone-filter=blob:limit=10M
20+
# 下载源码
21+
repo sync -c -j8
22+
23+
# 1-23
24+
git config --global merge.renameLimit 999999
25+
git config --global diff.renameLimit 999999
26+
27+
# 1-24
28+
cd $ws/androidx-main/frameworks/support
29+
ANDROIDX_PROJECTS=COMPOSE ./gradlew studio
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.bennyhuo.list1_2
2+
3+
// 1-2
4+
class User(var id: Long, var name: String, var age: Int)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package com.bennyhuo.list1_3
2+
3+
import com.squareup.moshi.Moshi
4+
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
5+
import okhttp3.Call
6+
import okhttp3.Callback
7+
import okhttp3.OkHttpClient
8+
import okhttp3.Request
9+
import okhttp3.Response
10+
import java.io.IOException
11+
import kotlin.coroutines.resume
12+
import kotlin.coroutines.resumeWithException
13+
import kotlin.coroutines.suspendCoroutine
14+
15+
// 1-3
16+
class GitHubApi {
17+
18+
private val endPoint = "https://api.github.com"
19+
private val client = OkHttpClient()
20+
private val moshi = Moshi.Builder().addLast(KotlinJsonAdapterFactory()).build()
21+
22+
fun getUser(login: String): GitUser? {
23+
val request: Request = Request.Builder()
24+
.url("$endPoint/users/$login").get().build()
25+
return client.newCall(request).execute().use { response ->
26+
response.body?.string()?.let {
27+
moshi.adapter(GitUser::class.java).fromJson(it)
28+
}
29+
}
30+
}
31+
32+
//region 1-4
33+
fun getUser(
34+
login: String,
35+
onSuccess: (GitUser?) -> Unit,
36+
onFailure: (Throwable) -> Unit
37+
) {
38+
val request: Request = Request.Builder()
39+
.url("$endPoint/users/$login").get().build()
40+
client.newCall(request).enqueue(object : Callback {
41+
override fun onFailure(call: Call, e: IOException) {
42+
onFailure(e)
43+
}
44+
45+
override fun onResponse(call: Call, response: Response) {
46+
try {
47+
val user = response.body?.string()?.let {
48+
moshi.adapter(GitUser::class.java).fromJson(it)
49+
}
50+
onSuccess(user)
51+
} catch (e: Exception) {
52+
onFailure(e)
53+
}
54+
}
55+
})
56+
}
57+
58+
suspend fun getUserAsync(login: String): GitUser? =
59+
suspendCoroutine { continuation ->
60+
getUser(login, onSuccess = {
61+
continuation.resume(it)
62+
}, onFailure = {
63+
continuation.resumeWithException(it)
64+
})
65+
}
66+
//endregion
67+
68+
fun getRepository(userLogin: String, repoName: String): GitRepo? {
69+
val request: Request = Request.Builder()
70+
.url("$endPoint/repos/$userLogin/$repoName").get().build()
71+
return client.newCall(request).execute().use { response ->
72+
response.body?.string()?.let {
73+
moshi.adapter(GitRepo::class.java).fromJson(it)
74+
}
75+
}
76+
}
77+
78+
}
79+
80+
81+
82+
suspend fun main() {
83+
val api = GitHubApi()
84+
val user = api.getUser("bennyhuo")
85+
println(user)
86+
87+
val repo = api.getRepository("bennyhuo", "KotlinDeepCopy")
88+
println(repo)
89+
90+
val user2 = api.getUserAsync("bennyhuo")
91+
println(user2)
92+
}

0 commit comments

Comments
 (0)