Cache compile script data

This commit is contained in:
Shadowfacts 2017-10-07 15:36:22 -04:00
parent 7df6669bd0
commit a2c8cd6d4b
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
2 changed files with 88 additions and 26 deletions

View File

@ -1,10 +1,9 @@
package net.shadowfacts.ekt package net.shadowfacts.ekt
import java.io.File import org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineBase
import javax.script.ScriptContext import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
import javax.script.ScriptEngine import java.io.*
import javax.script.ScriptEngineManager import javax.script.*
import javax.script.SimpleScriptContext
import kotlin.concurrent.getOrSet import kotlin.concurrent.getOrSet
/** /**
@ -44,11 +43,11 @@ fun include(include: String, init: net.shadowfacts.ekt.EKT.DataProvider.() -> Un
_result.toString() _result.toString()
""" """
private val engine = ThreadLocal<ScriptEngine>() private val engine = ThreadLocal<KotlinJsr223JvmScriptEngineBase>()
fun render(env: TemplateEnvironment, template: String = env.template): String { fun render(env: TemplateEnvironment, template: String = env.template): String {
if (env.cacheDir != null && env.cacheFile.exists()) { if (env.cacheDir != null && env.cacheScript.exists()) {
return eval(env.cacheFile.readText(Charsets.UTF_8), env) return eval(env.cacheScript.readText(Charsets.UTF_8), env)
} }
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
@ -79,7 +78,7 @@ _result.toString()
val script = imports + scriptPrefix + template + scriptSuffix val script = imports + scriptPrefix + template + scriptSuffix
if (env.cacheDir != null) { if (env.cacheDir != null) {
env.cacheFile.apply { env.cacheScript.apply {
if (!parentFile.exists()) parentFile.mkdirs() if (!parentFile.exists()) parentFile.mkdirs()
if (!exists()) createNewFile() if (!exists()) createNewFile()
writeText(script, Charsets.UTF_8) writeText(script, Charsets.UTF_8)
@ -121,21 +120,58 @@ _result.toString()
return renderClasspath(name, path, "$path/includes", cacheDir, init) return renderClasspath(name, path, "$path/includes", cacheDir, init)
} }
internal fun eval(script: String, env: TemplateEnvironment): String { private fun eval(script: String, env: TemplateEnvironment): String {
val engine = engine.getOrSet { ScriptEngineManager().getEngineByExtension("kts") } val engine = engine.getOrSet { ScriptEngineManager().getEngineByExtension("kts") as KotlinJsr223JvmScriptEngineBase }
engine.context = SimpleScriptContext()
val bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE) val context = createContext(engine, env)
bindings.putAll(env.data)
bindings.put("_env", env) if (env.cacheDir != null) {
val cacheCompiled = env.cacheCompiled
val compiled = if (cacheCompiled.exists()) {
val fis = FileInputStream(cacheCompiled)
val ois = ObjectInputStream(fis)
val data = ois.readObject() as ReplCompileResult.CompiledClasses
ois.close()
fis.close()
KotlinJsr223JvmScriptEngineBase.CompiledKotlinScript(engine, engine.nextCodeLine(context, env.cacheScript.readText(Charsets.UTF_8)), data)
} else {
val compiled = engine.compile(script, context) as KotlinJsr223JvmScriptEngineBase.CompiledKotlinScript
val data = compiled.compiledData
val fos = FileOutputStream(cacheCompiled)
val oos = ObjectOutputStream(fos)
oos.writeObject(data)
oos.close()
fos.close()
compiled
}
return engine.eval(compiled, context) as String
} else {
return engine.eval(script, context) as String
}
}
private fun eval(script: KotlinJsr223JvmScriptEngineBase.CompiledKotlinScript, env: TemplateEnvironment): String {
return script.engine.eval(script, createContext(script.engine, env)) as String
}
private fun createContext(engine: ScriptEngine, env: TemplateEnvironment): ScriptContext {
val bindings = engine.createBindings().apply {
putAll(env.data)
put("_env", env)
}
val context = SimpleScriptContext().apply {
setBindings(bindings, ScriptContext.ENGINE_SCOPE)
}
// Hack to allow data to be accessed by name from template instead of via bindings map // Hack to allow data to be accessed by name from template instead of via bindings map
val unwrapBindings = env.data.keys.map { val unwrapBindings = env.data.keys.joinToString("\n") {
val type = env.data[it]!!.type val type = env.data[it]!!.type
"val $it = (bindings[\"$it\"] as net.shadowfacts.ekt.EKT.TypedValue).value as $type" "val $it = (bindings[\"$it\"] as net.shadowfacts.ekt.EKT.TypedValue).value as $type"
}.joinToString("\n") }
engine.eval(unwrapBindings) engine.eval(unwrapBindings, context)
return engine.eval(script) as String return context
} }
interface TemplateEnvironment { interface TemplateEnvironment {
@ -146,8 +182,10 @@ _result.toString()
val template: String val template: String
val include: String val include: String
val cacheFile: File val cacheScript: File
get() = File(cacheDir!!, "$name.kts") get() = File(cacheDir!!, "$name.kts")
val cacheCompiled: File
get() = File(cacheDir!!, "$name.kts.compiled")
fun createChild(name: String, data: Map<String, TypedValue>? = null): TemplateEnvironment fun createChild(name: String, data: Map<String, TypedValue>? = null): TemplateEnvironment

View File

@ -1,22 +1,46 @@
package net.shadowfacts.ekt package net.shadowfacts.ekt
import java.io.File import java.io.File
import java.lang.management.ManagementFactory
import kotlin.concurrent.thread import kotlin.concurrent.thread
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
fun main(args: Array<String>) { fun main(args: Array<String>) {
for (i in 0..2) { println(ManagementFactory.getRuntimeMXBean().name)
thread {
println("Calling from: $i") for (i in 0..99) {
println("Rendering $i")
render() render()
} }
}
println("done")
// thread {
// for (i in 0..30) {
// println("Thread 1 iteration $i")
// render()
// }
// }
//
// thread {
// for (i in 0..30) {
// println("Thread 2 iteration $i")
// render()
// }
// }
//
// thread {
// for (i in 0..30) {
// println("Thread 3 iteration $i")
// render()
// }
// }
} }
fun render(): String { fun render(): String {
return EKT.renderClasspath("template", "/templates") { return EKT.renderClasspath("template", "/templates", cacheDir = File("cache")) {
"list" to (listOf(1, 2, 3) asType "List<Int>") "list" to (listOf(1, 2, 3) asType "List<Int>")
} }
} }