Skip to content

Commit

Permalink
= optimize the issue caused by R8(Proguard tools)
Browse files Browse the repository at this point in the history
  • Loading branch information
chenakam committed Nov 15, 2023
1 parent 01abb6c commit aa2e5b7
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 74 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ captures/

# Intellij
.idea/
.bsp/

# Keystore files
*.jks
55 changes: 55 additions & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
version = 2.7.3

preset = IntelliJ
align.preset = more # For pretty alignment.
maxColumn = 230 # For my wide 30" display.

# 注释不能使用';'号,虽然按 'command + /' 会出现分号。
assumeStandardLibraryStripMargin = true
align.stripMargin = true

continuationIndent.callSite = 2
continuationIndent.ctorSite = 4
continuationIndent.defnSite = 2
continuationIndent.extendSite = 4
continuationIndent.withSiteRelativeToExtends = 3

indentOperator.topLevelOnly = false
# indentOperator.exclude = "^(&&|\\|\\|)$"
# indentOperator.include = ".*"
indentOperator.preset = spray # default | spray (set include = "^.*=$", exclude = "^$")

align.preset = more # none | some | more | most
align.arrowEnumeratorGenerator = true
# align.openParenDefnSite = false

newlines.source = keep # classic | keep | fold | unfold
newlines.beforeMultiline = keep # classic | keep | fold | unfold
newlines.beforeMultilineDef = keep
newlines.topLevelStatements = [before] # [before, after]
# newlines.topLevelStatementsMinBreaks = 1 # 至少跨越多少行,才可以自动换行。

# optIn.configStyleArguments = true
# 方法调用的括号内,可见参数字符数量(包括逗号)。如果字符数 >= 这个数,则触发折行。
# runner.optimizer.forceConfigStyleOnOffset = 150
# runner.optimizer.forceConfigStyleMinArgCount = 1 # 最少参数数量
# optIn.breaksInsideChains = false
optIn.encloseClassicChains = true
optIn.forceBlankLineBeforeDocstring = false

rewrite.rules = [SortModifiers]
rewrite.rules = [SortImports]

trailingCommas = preserve # never | preserve | always | multiple

# verticalMultiline.atDefnSite = true
# verticalMultiline.arityThreshold = 100
# verticalMultiline.newlineAfterOpenParen = false

binPack.parentConstructors = OnelineIfPrimaryOneline

# // format: off
# val identity = Array(1, 0, 0,
# 0, 1, 0,
# 0, 0, 1)
# // format: on
25 changes: 14 additions & 11 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
// TODO: 导入项目时,应选择 bsp 项目(而不是 sbt 项目,否则无法`自动`链接各种源码)。
// 导入成功后,在 Terminal 运行:
// `sbt --java-home /Library/Java/JavaVirtualMachines/jdk-17-aarch64.jdk/Contents/Home`

name := baseDirectory.value.getName

organization := "hobby.wei.c"

version := "3.0.6"
version := "3.1.1"

scalaVersion := "2.12.17"
scalaVersion := "2.12.18"

crossScalaVersions := Seq(
"2.11.12",
"2.12.17",
"2.12.18",
"2.13.10"
)

Expand All @@ -17,17 +21,17 @@ crossScalaVersions := Seq(
//scalacOptions += "-Xexperimental"

// 解决生成文档报错导致 jitpack.io 出错的问题。
publishArtifact in packageDoc := false
packageDoc / publishArtifact := false

// 独立使用本库的话,应该启用下面的设置。
//lazy val scalaSettings = Seq(
// scalaVersion := "2.12.17"
// scalaVersion := "2.12.xx"
//)
//
//lazy val root = Project(id = "reflow", base = file("."))
// .dependsOn(/*lang*/)
// .settings(scalaSettings,
// aggregate in update := false
// update / aggregate := false
// )

exportJars := true
Expand All @@ -40,10 +44,9 @@ resolvers += "jitpack" at "https://jitpack.io"
libraryDependencies ++= Seq(
// 如果要用 jitpack 打包的话就加上,打完了再注掉。
// 如果独立使用本库,应该启用本依赖。
"com.github.bdo-cash" % "annoguard" % "v1.0.6",
"com.github.bdo-cash" % "annoguard" % "v2.0.0",
// 如果用 jitpack 打包 2.12.12, 这个包的引入也必须是 2.12.12。
//"com.github.bdo-cash" % "scala-lang" % "138bff0c11",
"com.github.bdo-cash" % "scala-lang" % "1096f23b08", // scala 2.12
"com.github.bdo-cash" % "scala-lang" % "60a29ca4c7", // scala 2.12

// ScalaTest 的标准引用
"junit" % "junit" % "[4.12,)" % Test,
Expand All @@ -53,14 +56,14 @@ libraryDependencies ++= Seq(
// 如果项目要独立编译,请同时启用这部分。
// Macro Settings
///*
resolvers += Resolver.sonatypeRepo("releases")
resolvers ++= Resolver.sonatypeOssRepos("releases")
libraryDependencies ++= (CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, x)) if x < 13 =>
addCompilerPlugin("org.scalamacros" % "paradise" % "[2.1.0,)" cross CrossVersion.full)
Seq("org.scala-lang" % "scala-compiler" % scalaVersion.value)
case _ => Seq("org.scala-lang" % "scala-reflect" % scalaVersion.value)
})
scalacOptions in Compile ++= (CrossVersion.partialVersion(scalaVersion.value) match {
Compile / scalacOptions ++= (CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, x)) if x >= 13 => Seq("-Ymacro-annotations")
case _ => Nil
})
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=0.13.18
sbt.version=1.9.1
73 changes: 50 additions & 23 deletions src/main/java/hobby/wei/c/tool/Reflect.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@
import java.util.Locale;

/**
* 摘自Gson源码
* 摘自`Gson`源码
*
* @author Wei Chou([email protected])
* @version 1.0, 02/07/2016
* @version 1.0, 02/07/2016;
* 1.1, 同步至 {@code Gson 2.10.1}
*/
public class Reflect {
public static Type[] getSuperclassTypeParameter(Class<?> subclass, boolean checkTypeVariable) {
Expand All @@ -38,35 +39,62 @@ public static Type[] getSuperclassTypeParameter(Class<?> subclass, boolean check
} else {
final Type[] types = ((ParameterizedType) superclass).getActualTypeArguments();
if (checkTypeVariable) {
for (Type t : types) {
if (t instanceof TypeVariable) {
throwClassIllegal(subclass);
}
for (Type tpe : types) {
checkTypeVariable(tpe);
}
}
return types;
}
}

public static Class<?> getRawType(Type type) {
if (type instanceof Class) {
return (Class) type;
} else if (type instanceof ParameterizedType) {
return (Class) ((ParameterizedType) type).getRawType();
} else if (type instanceof GenericArrayType) {
return Array.newInstance(getRawType(((GenericArrayType) type).getGenericComponentType()), 0).getClass();
} else if (type instanceof TypeVariable) {
/**
* 借鉴 {@code Gson 2.10.1}, 实测与上面效果一样(问题不在 scala/java, 而在 Android, 与 R8 有关,已找到原因)。
*/
public static Type[] getSuperclassTypeParameterSafe(Class<?> subclass, boolean checkTypeVariable) {
final Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof ParameterizedType) {
final Type[] types = ((ParameterizedType) superclass).getActualTypeArguments();
if (checkTypeVariable) {
for (Type tpe : types) {
checkTypeVariable(tpe);
}
}
return types;
}
return throwClassIllegal(subclass);
}

public static void checkTypeVariable(Type tpe) {
// `TypeVariable`表示`T`占位符而不是具体类型,如`Xx<T>`而不是`Xx<java.util.ArrayList<?>>`。
// 区别于`WildcardType`。
if (tpe instanceof TypeVariable) {
// ((TypeVariable<?>)tpe).getBounds(); // 表示获取`T`参数表示的具体类,如:
// `java.util.ArrayList<?>`,这需要递归,具体看在第几层。
throwTypeIllegal(tpe);
}
}

public static Class<?> getRawType(Type tpe) {
if (tpe instanceof Class) {
return (Class<?>) tpe;
} else if (tpe instanceof ParameterizedType) {
return (Class<?>) ((ParameterizedType) tpe).getRawType();
} else if (tpe instanceof GenericArrayType) {
return Array.newInstance(getRawType(((GenericArrayType) tpe).getGenericComponentType()), 0).getClass();
} else if (tpe instanceof TypeVariable) {
// we could use the variable's bounds, but that won't work if there are multiple.
// having a raw type that's more general than necessary is okay
return Object.class;
} else if (type instanceof WildcardType) {
return getRawType(((WildcardType) type).getUpperBounds()[0]);
} else if (tpe instanceof WildcardType) {
return getRawType(((WildcardType) tpe).getUpperBounds()[0]);
} else {
return throwTypeIllegal(type);
return throwTypeIllegal(tpe);
}
}

public static Type[] getSubTypes(Type type) {
if (type instanceof ParameterizedType) {
return ((ParameterizedType) type).getActualTypeArguments();
public static Type[] getSubTypes(Type tpe) {
if (tpe instanceof ParameterizedType) {
return ((ParameterizedType) tpe).getActualTypeArguments();
} else {
return EMPTY_TYPES;
}
Expand All @@ -77,10 +105,9 @@ private static Type[] throwClassIllegal(Class<?> subclass) {
Locale.CHINA, "请确保类\"%s\"是泛型类的[匿名]子类。", subclass.getName()));
}

private static Class<?> throwTypeIllegal(Type type) {
private static Class<?> throwTypeIllegal(Type tpe) {
throw new IllegalArgumentException(String.format(
// Locale.CHINA, "不支持的类型\"%s\", 仅支持Class, ParameterizedType, or GenericArrayType.", type));
Locale.CHINA, "不支持的类型\"%s\", 请确认泛型参数是否正确。", type));
Locale.CHINA, "不支持的类型\"%s\", 请确认泛型参数是否正确。", tpe));
}

private static final Type[] EMPTY_TYPES = new Type[0];
Expand Down
64 changes: 25 additions & 39 deletions src/main/scala/hobby/wei/c/reflow/KvTpe.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,49 +19,43 @@ package hobby.wei.c.reflow
import java.lang.reflect.Type
import hobby.chenai.nakam.lang.J2S.NonNull
import hobby.chenai.nakam.lang.TypeBring.AsIs
import hobby.wei.c.anno.proguard.{Keep$$e, KeepC$$e}
import hobby.wei.c.tool.Reflect

import scala.collection._

/**
* 定义key及value类型。value类型由泛型指定
* 注意: 本类的子类必须在运行时创建, 即匿名子类, 否则泛型信息可能在Proguard时被删除, 从而导致解析失败。
* 定义`key`及`value`类型。`value`类型由泛型指定
* 注意: 本类的子类必须在运行时创建, 即匿名子类, 否则泛型信息可能在`Proguard`时被删除, 从而导致解析失败。
*
* @author Wei Chou([email protected])
* @version 1.0, 21/07/2016
*/
abstract class KvTpe[T <: AnyRef] private[reflow](_key: String, _clazz: Class[_], _index: Int = 0, _raw: Boolean = false) extends Equals {
@Keep$$e @KeepC$$e
abstract class KvTpe[T <: AnyRef] private[reflow] (_key: String, _clazz: Class[_], _index: Int = 0, _raw: Boolean = false) extends Equals {
protected def this(_key: String) = this(_key, null)

final val key: String = _key.ensuring(_.nonEmpty)
/**
* 泛型参数的类型, 类似于这种结构: java.util.List<java.util.List<int[]>>。
*/
final val tpe: Type = (if (_raw) _clazz else Reflect.getSuperclassTypeParameter(if (_clazz.isNull) this.getClass else _clazz, true)(_index)).ensuring(_.nonNull)
/**
* 第一级泛型参数的Class表示。
*/

/** 泛型参数的类型, 类似于这种结构: `java.util.List<java.util.List<int[]>>`。 */
final val tpe: Type = (if (_raw) _clazz else Reflect.getSuperclassTypeParameterSafe(if (_clazz.isNull) this.getClass else _clazz, true)(_index)).ensuring(_.nonNull)

/** 第一级泛型参数的`Class`表示。 */
private lazy val rawType: Class[_ >: T] = Reflect.getRawType(tpe).as[Class[_ >: T]]
/**
* 第一级泛型参数的子泛型参数, 可能不存在。作用或结构与tpe类似。
*/

/** 第一级泛型参数的子泛型参数, 可能不存在。作用或结构与`tpe`类似。 */
private lazy val subTypes: Array[Type] = Reflect.getSubTypes(tpe)

/**
* 走这个方法作类型转换, 确保value类型与定义的一致性。
* @return 返回Task在执行时当前key对应值的目标类型。
*/
/** 走这个方法作类型转换, 确保`value`类型与定义的一致性。 */
def asType(value: Any): T = value.as[T]

def putValue(map: mutable.Map[String, Any], value: Any): Boolean = putValue(map, value, ignoreTpeDiff = false)

/**
* 将输出值按指定类型(作类型检查)插入Map。
*
* @param map 输出到的Map。
* @param value 要输出的值。
* @param ignoreTpeDiff 如果value参数类型不匹配,是否忽略。
* @return true成功,else失败。
/** 将输出值按指定类型(作类型检查)插入`Map`。
* @param map 输出到的`Map`
* @param value 要输出的值
* @param ignoreTpeDiff 如果`value`参数类型不匹配,是否忽略。
* @return `true`成功,`else`失败。
*/
def putValue(map: mutable.Map[String, Any], value: Any, ignoreTpeDiff: Boolean): Boolean = {
val v = requireSameType(value, ignoreTpeDiff)
Expand All @@ -71,15 +65,13 @@ abstract class KvTpe[T <: AnyRef] private[reflow](_key: String, _clazz: Class[_]
} else false
}

/**
* 走这个方法取值, 确保value类型与定义的一致性。
*
* @param map Task的输入参数。
* @return 返回Task在执行时当前key对应值的目标类型。
/** 走这个方法取值, 确保`value`类型与定义的一致性。
* @param map `Task`的输入参数
* @return 返回`Task`在执行时当前`key`对应值的目标类型
*/
def takeValue(map: Map[String, Any]): Option[T] = {
// 强制类型转换比较宽松, 只会检查对象类型, 而不会检查泛型。
// 但是由于value值对象无法获得泛型类型, 因此这里不再作泛型检查。也避免了性能问题。
// 但是由于`value`值对象无法获得泛型类型, 因此这里不再作泛型检查。也避免了性能问题。
map.get(key).as[Option[T]]
}

Expand All @@ -90,17 +82,11 @@ abstract class KvTpe[T <: AnyRef] private[reflow](_key: String, _clazz: Class[_]
if (ignoreDiffType) null
else Assist.Throws.typeNotMatch(this, clazz)
} else value
// 数值对象通常已经失去了泛型参数, 因此不作检查
// 数值对象通常已经失去了泛型参数, 因此不作检查
} else value
}

/**
* 参数的value类型是否与本value类型相同或者子类型。
* 同{Class#isAssignableFrom(Class)}
*
* @param key
* @return
*/
/** 参数的`value`类型是否与本`value`类型相同或者子类型。同`Class#isAssignableFrom(Class)`。 */
def isAssignableFrom(key: KvTpe[_ <: AnyRef]): Boolean = {
if (!rawType.isAssignableFrom(key.rawType)) false
else if (subTypes.length != key.subTypes.length) false
Expand All @@ -109,7 +95,7 @@ abstract class KvTpe[T <: AnyRef] private[reflow](_key: String, _clazz: Class[_]

override def equals(any: scala.Any) = any match {
case that: KvTpe[_] if that.canEqual(this) => that.key == this.key && that.tpe == this.tpe
case _ => false
case _ => false
}

override def canEqual(that: Any) = that.isInstanceOf[KvTpe[_]]
Expand Down

0 comments on commit aa2e5b7

Please sign in to comment.