-
Notifications
You must be signed in to change notification settings - Fork 180
Expand file tree
/
Copy pathMain.scala
More file actions
127 lines (113 loc) · 4.92 KB
/
Main.scala
File metadata and controls
127 lines (113 loc) · 4.92 KB
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
package io.kaitai.struct
import io.kaitai.struct.format.{ClassSpec, ClassSpecs, MetaSpec}
import io.kaitai.struct.languages.{GoCompiler, NimCompiler, RustCompiler}
import io.kaitai.struct.languages.components.LanguageCompilerStatic
import io.kaitai.struct.precompile._
import io.kaitai.struct.problems.CompilationProblem
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
object Main {
/**
* Takes a freshly made [[format.ClassSpecs]] container with a single .ksy loaded
* into it, launches recursive loading of imports into this container,
* and then runs precompilation on every class that happens to be there
* after imports.
* @param specs [[format.ClassSpecs]] container with first type loaded into it
* @param config runtime configuration to be passed to precompile step
* @return a future that will resolve when both imports and precompilations
* are complete; modifies given container by loading extra classes
* into it and modifying classes itself by precompilation step
*/
def importAndPrecompile(specs: ClassSpecs, config: RuntimeConfig): Future[Iterable[CompilationProblem]] = {
new LoadImports(specs).processClass(specs.firstSpec, LoadImports.BasePath).map { (allSpecs) =>
Log.importOps.info(() => s"imports done, got: ${specs.keys} (async=$allSpecs)")
precompile(specs, config)
}
}
/**
* Runs precompilation steps on every type in the given [[format.ClassSpecs]] collection,
* using provided configuration. See individual precompile steps invocations for more
* in-depth description of what each step includes.
*
* @param specs [[format.ClassSpecs]] container with all types loaded into it
* @param config runtime configuration to be passed to precompile steps
* @return a list of compilation problems encountered during precompilation steps
*/
def precompile(specs: ClassSpecs, conf: RuntimeConfig): Iterable[CompilationProblem] = {
new CalculateFullNamesAndSetSurroundingType(specs).run()
val resolveTypeProblems = specs.flatMap { case (_, topClass) =>
val config = updateConfigFromMeta(conf, topClass.meta)
new ResolveTypes(specs, topClass, config.opaqueTypes).run()
}
// For now, bail out early in case we have any type resolution problems
// TODO: try to recover and do some more passes even in face of these
if (resolveTypeProblems.nonEmpty) {
return resolveTypeProblems
}
var passes = Seq(
new ParentTypes(specs),
new DeriveValueInstanceTypes(specs),
new CalculateSeqSizes(specs),
new TypeValidator(specs),
// Warnings
new StyleCheckIds(specs),
new CanonicalizeEncodingNames(specs),
)
resolveTypeProblems ++ passes.flatMap(_.run())
}
/**
* Compiles a single [[format.ClassSpec]] into a single target language using
* provided configuration.
* @param specs bundle of class specifications (used to search to references there)
* @param spec class specification to compile
* @param lang specifies which language compiler will be used
* @param conf runtime compiler configuration
* @return a container that contains all compiled files and results
*/
def compile(specs: ClassSpecs, spec: ClassSpec, lang: LanguageCompilerStatic, conf: RuntimeConfig): CompileLog.SpecSuccess = {
val config = updateConfigFromMeta(conf, spec.meta)
val cc = lang match {
case GraphvizClassCompiler =>
new GraphvizClassCompiler(specs, spec)
case GoCompiler =>
new GoClassCompiler(specs, spec, config)
case RustCompiler =>
new RustClassCompiler(specs, spec, config)
case ConstructClassCompiler =>
new ConstructClassCompiler(specs, spec)
case NimCompiler =>
new NimClassCompiler(specs, spec, config)
case HtmlClassCompiler =>
new HtmlClassCompiler(specs, spec)
case _ =>
new ClassCompiler(specs, spec, config, lang)
}
cc.compile
}
/**
* Updates runtime configuration with "enforcement" options that came from a source file itself.
* Currently used to enforce:
* * debug when "ks-debug: true" is specified in top-level "meta" key
* * zero copy stream usage when "ks-zero-copy-stream" is specified
*
* @param config original runtime configuration
* @param meta meta spec for top-level type
* @return updated runtime configuration with applied enforcements
*/
private def updateConfigFromMeta(config: RuntimeConfig, meta: MetaSpec): RuntimeConfig = {
val config1 = if (meta.forceDebug) {
config.copy(autoRead = false, readStoresPos = true)
} else {
config
}
val config2 = meta.zeroCopySubstream match {
case Some(value) => config1.copy(zeroCopySubstream = value)
case None => config1
}
val config3 = meta.opaqueTypes match {
case Some(value) => config2.copy(opaqueTypes = value)
case None => config2
}
config3
}
}