-
Notifications
You must be signed in to change notification settings - Fork 21
/
Shader.scala
149 lines (137 loc) · 4.97 KB
/
Shader.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package com.eed3si9n.jarjarabrams
import java.nio.file.{ Files, Path, StandardOpenOption }
import com.eed3si9n.jarjar.{ JJProcessor, _ }
import com.eed3si9n.jarjar.util.EntryStruct
import Zip.createDirectories
import scala.collection.JavaConverters._
object Shader {
def shadeFile(
rules: Seq[ShadeRule],
inputJar: Path,
outputJar: Path,
verbose: Boolean,
skipManifest: Boolean,
resetTimestamp: Boolean,
warnOnDuplicateClass: Boolean
): Unit = {
val shader = bytecodeShader(rules, verbose, skipManifest)
Zip.transformJarFile(inputJar, outputJar, resetTimestamp, warnOnDuplicateClass) { struct0 =>
shader(struct0.data, struct0.name).map {
case (shadedBytes, shadedName) =>
Zip.entryStruct(shadedName, struct0.time, shadedBytes, struct0.skipTransform)
}
}
}
def makeMappings(dir: Path): List[(Path, String)] =
Files.walk(dir).iterator().asScala.toList.flatMap { x =>
if (x == dir) None
else Some(x -> dir.relativize(x).toString)
}
def shadeDirectory(
rules: Seq[ShadeRule],
dir: Path,
mappings: Seq[(Path, String)],
verbose: Boolean
): Unit = shadeDirectory(rules, dir, mappings, verbose, skipManifest = true)
def shadeDirectory(
rules: Seq[ShadeRule],
dir: Path,
mappings: Seq[(Path, String)],
verbose: Boolean,
skipManifest: Boolean
): Unit =
if (rules.isEmpty) ()
else {
val shader = bytecodeShader(rules, verbose, skipManifest)
for {
(path, name) <- mappings
if !Files.isDirectory(path)
bytes = Files.readAllBytes(path)
_ = Files.delete(path)
(shadedBytes, shadedName) <- shader(bytes, name)
out = dir.resolve(shadedName)
_ = createDirectories(out.getParent)
_ = Files.write(out, shadedBytes, StandardOpenOption.CREATE)
} yield ()
Files.walk(dir).iterator().asScala.toList.foreach { x =>
if (x == dir) ()
else Zip.resetModifiedTime(x)
}
}
def bytecodeShader(
rules: Seq[ShadeRule],
verbose: Boolean,
skipManifest: Boolean
): (Array[Byte], String) => Option[(Array[Byte], String)] =
if (rules.isEmpty)(bytes, mapping) => Some(bytes -> mapping)
else {
val jjrules = rules.flatMap { r =>
r.shadePattern match {
case ShadePattern.Rename(patterns) =>
patterns.map {
case (from, to) =>
val jrule = new Rule()
jrule.setPattern(from)
jrule.setResult(to)
jrule
}
case ShadePattern.Zap(patterns) =>
patterns.map { pattern =>
val jrule = new Zap()
jrule.setPattern(pattern)
jrule
}
case ShadePattern.Keep(patterns) =>
patterns.map { pattern =>
val jrule = new Keep()
jrule.setPattern(pattern)
jrule
}
case _ => Nil
}
}
val proc = new JJProcessor(
patterns = jjrules,
verbose = verbose,
skipManifest = skipManifest,
misplacedClassStrategy = null
)
val excludes = proc.getExcludes
(bytes, mapping) =>
/*
jarjar MisplacedClassProcessor class transforms byte[] to a class using org.objectweb.asm.ClassReader.getClassName
which always translates class names containing '.' into '/', regardless of OS platform.
We need to transform any windows file paths in order for jarjar to match them properly and not omit them.
*/
val sanitizedMapping = if (mapping.contains('\\')) mapping.replace('\\', '/') else mapping
val entry = new EntryStruct
entry.data = bytes
entry.name = sanitizedMapping
entry.time = -1
entry.skipTransform = false
if (!excludes.contains(entry.name) && proc.process(entry))
Some(entry.data -> entry.name)
else
None
}
def parseRulesFile(rulesFile: Path): List[ShadeRule] =
RulesFileParser.parse(rulesFile.toFile()).asScala.map(toShadeRule).toList
def toShadeRule(rule: PatternElement): ShadeRule =
rule match {
case r: Rule =>
ShadeRule(ShadeRule.rename((r.getPattern, r.getResult)), Vector(ShadeTarget.inAll))
case r: Keep => ShadeRule(ShadeRule.keep((r.getPattern)), Vector(ShadeTarget.inAll))
case r: Zap => ShadeRule(ShadeRule.zap((r.getPattern)), Vector(ShadeTarget.inAll))
}
}
sealed trait ShadePattern {
def inAll: ShadeRule = ShadeRule(this, Vector(ShadeTarget.inAll))
def inProject: ShadeRule = ShadeRule(this, Vector(ShadeTarget.inProject))
def inModuleCoordinates(moduleId: ModuleCoordinate*): ShadeRule =
ShadeRule(this, moduleId.toVector map ShadeTarget.inModuleCoordinate)
}
object ShadePattern {
case class Rename(patterns: List[(String, String)]) extends ShadePattern
case class Zap(patterns: List[String]) extends ShadePattern
case class Keep(patterns: List[String]) extends ShadePattern
}