Skip to content

Commit

Permalink
FIXScriptUtilでのランタイム依存をIScriptRuntimeでなくす
Browse files Browse the repository at this point in the history
  • Loading branch information
anatawa12 committed Jun 3, 2020
1 parent 4042cc8 commit ce6a43a
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 119 deletions.
21 changes: 14 additions & 7 deletions src/main/java/com/anatawa12/fixRtm/FixRtm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import com.anatawa12.fixRtm.asm.config.MainConfig.changeTestTrainTextureEnabled
import com.anatawa12.fixRtm.io.FIXFileLoader
import com.anatawa12.fixRtm.network.NetworkHandler
import com.anatawa12.fixRtm.rtm.modelpack.modelset.dummies.*
import com.anatawa12.fixRtm.scripting.ExecutedScriptCache
import com.anatawa12.fixRtm.scripting.loadFIXScriptUtil
import com.anatawa12.fixRtm.scripting.rhino.ExecutedScriptCache
import com.anatawa12.fixRtm.scripting.rhino.PrimitiveJavaHelper
import com.anatawa12.fixRtm.scripting.rhino.RhinoHooks
import com.anatawa12.fixRtm.scripting.loadFIXScriptUtil
import jp.ngt.ngtlib.NGTCore
import jp.ngt.rtm.RTMCore
import net.minecraft.block.Block
Expand Down Expand Up @@ -45,12 +45,19 @@ object FixRtm {
@Mod.EventHandler
fun construct(e: FMLConstructionEvent) {
NativeJavaObject.canConvert(0, Object::class.java)// load
RhinoHooks.load()// load
FIXFileLoader.load() // init
if (MainConfig.useOurScripting) {
ExecutedScriptCache.load()// init
loadFIXScriptUtil()// init
PrimitiveJavaHelper.load()// init
when (MainConfig.scriptingMode) {
MainConfig.ScriptingMode.CacheWithRhino -> {
loadFIXScriptUtil()// init
ExecutedScriptCache.load()// init
PrimitiveJavaHelper.load()// init
RhinoHooks.load()// load
}
MainConfig.ScriptingMode.BetterWithNashorn -> {
}
MainConfig.ScriptingMode.UseRtmNormal -> {
// nop
}
}
if (e.side == Side.CLIENT) registerGenerators()
}
Expand Down
128 changes: 18 additions & 110 deletions src/main/java/com/anatawa12/fixRtm/scripting/FIXScriptUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,139 +2,47 @@

package com.anatawa12.fixRtm.scripting

import com.anatawa12.fixRtm.caching.ModelPackBasedCache
import com.anatawa12.fixRtm.fixCacheDir
import com.anatawa12.fixRtm.asm.config.MainConfig
import com.anatawa12.fixRtm.io.FIXFileLoader
import com.anatawa12.fixRtm.io.FIXModelPack
import com.anatawa12.fixRtm.scripting.rhino.FIXRhinoScriptEngine
import com.anatawa12.fixRtm.scripting.rhino.ImportScriptRhinoFunctionImpl
import com.anatawa12.fixRtm.scripting.rhino.ScriptCompiledClassCache
import com.anatawa12.fixRtm.scripting.rhino.usingContext
import com.anatawa12.fixRtm.utils.DigestUtils
import com.anatawa12.fixRtm.scripting.rhino.RhinoScriptRuntimeImpl
import jp.ngt.rtm.modelpack.ModelPackManager
import net.minecraft.util.ResourceLocation
import org.mozilla.javascript.ImporterTopLevel
import org.mozilla.javascript.ScriptableObject
import org.mozilla.javascript.TopLevel
import javax.script.ScriptEngine

val baseScope = usingContext {
val scope = TopLevel()

it.initStandardObjects(scope)

ImportScriptRhinoFunctionImpl.init(scope)

scope.sealObject()

scope
val scriptRuntime: IScriptRuntime<*, *> = when (MainConfig.scriptingMode) {
MainConfig.ScriptingMode.CacheWithRhino -> RhinoScriptRuntimeImpl
MainConfig.ScriptingMode.BetterWithNashorn -> TODO()
MainConfig.ScriptingMode.UseRtmNormal -> IScriptRuntime.AssertingRuntime
}

fun loadFIXScriptUtil() {}

private fun makeNewScope(): ScriptableObject = usingContext {
it.applicationClassLoader = ScriptCompiledClassCache.Loader

val scope = ImporterTopLevel(it, false)
scope.prototype = baseScope
return scope
}

fun makeNewScopeWithCache(cache: ExecutedScript): ScriptableObject? {
usingContext {
ScriptCompiledClassCache.initContext(it)
return cache.getScope(baseScope)
}
}
@Suppress("unused")
fun ModelPackManager.getScriptAndDoScript(fileName: String): ScriptEngine
= com.anatawa12.fixRtm.scripting.getScriptAndDoScript(fileName)

fun makeExecutedScript(dependencies: Map<String, ByteArray>, scope: ScriptableObject): ExecutedScript {
usingContext {
ScriptCompiledClassCache.initContext(it)
return ExecutedScript(dependencies, scope, baseScope)
}
@Suppress("unused")
fun getScriptAndDoScript(fileName: String): ScriptEngine {
return getScriptAndDoScript(scriptRuntime, fileName)
}

@Suppress("unused")
fun ModelPackManager.getScriptAndDoScript(fileName: String): ScriptEngine {
fun <CompiledScript, Engine : ScriptEngine> getScriptAndDoScript(runtime: IScriptRuntime<CompiledScript, Engine>, fileName: String): ScriptEngine {
val filePath = ResourceLocation(fileName)
val resource = FIXFileLoader.getResource(filePath)
val scriptStr = resource.inputStream.reader().use { it.readText() }
val dependencies = makeDependenciesData(ScriptImporter.getAllDependenceScripts(filePath, scriptStr))
val dependencies = ScriptImporter.getAllDependenceScripts(filePath, scriptStr)

// first, try cache
getScriptAndDoScriptByCache(filePath, resource.pack, dependencies)?.let { scope ->
val engine = FIXRhinoScriptEngine()
engine.scope = scope
return engine
}
runtime.getCachedEngine(filePath, resource, dependencies)?.let { return it }

// then evalute
val engine = FIXRhinoScriptEngine()
usingContext { cx ->
val scope = makeNewScope()

val script = ImportScriptRhinoFunctionImpl.makeScript(filePath, scriptStr, resource.pack)

script.exec(cx, scope)
val script = runtime.compile(filePath, scriptStr, resource.pack)

engine.scope = scope
}
val engine = runtime.exec(script)

// add to cache

ExecutedScriptCache.add(resource.pack, filePath, makeExecutedScript(dependencies, engine.scope))
runtime.cache(resource.pack, filePath, dependencies, engine)

return engine
}

fun getScriptAndDoScriptByCache(filePath: ResourceLocation, pack: FIXModelPack, dependencies: Map<String, ByteArray>): ScriptableObject? {
val cache = ExecutedScriptCache.getScript(pack, filePath) ?: return null

// verify cache
if (cache.dependencies.keys != dependencies.keys) return null

for ((name, hash) in dependencies) {
if (!cache.dependencies[name]!!.contentEquals(hash)) return null
}

// load cache

val newScope = makeNewScopeWithCache(cache)
if (newScope == null) {
ExecutedScriptCache.discord(pack, filePath)
}
return newScope
}

fun makeDependenciesData(dependencies: Map<ResourceLocation, String>): Map<String, ByteArray> {
val data = mutableMapOf<String, ByteArray>()
for ((name, script) in dependencies) {
data[name.toString()] = DigestUtils.sha1(script)
}
return data
}

@Suppress("unused")
fun getScriptAndDoScript(fileName: String): ScriptEngine = ModelPackManager.INSTANCE.getScriptAndDoScript(fileName)

object ExecutedScriptCache {
private val cache = ModelPackBasedCache(
fixCacheDir.resolve("excluded-script"),
0x0000 to ExecutedScript.Serializer
)

fun getScript(pack: FIXModelPack, filePath: ResourceLocation): ExecutedScript? {
return cache.get(pack, DigestUtils.sha1Hex(filePath.toString()), ExecutedScript.Serializer)
}

fun add(pack: FIXModelPack, filePath: ResourceLocation, executedScript: ExecutedScript) {
cache.put(pack, DigestUtils.sha1Hex(filePath.toString()), executedScript)
}

fun discord(pack: FIXModelPack, filePath: ResourceLocation) {
cache.discord(pack, DigestUtils.sha1Hex(filePath.toString()))
}

fun load() {}
}

53 changes: 53 additions & 0 deletions src/main/java/com/anatawa12/fixRtm/scripting/IScriptRuntime.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.anatawa12.fixRtm.scripting

import com.anatawa12.fixRtm.io.FIXModelPack
import com.anatawa12.fixRtm.io.FIXResource
import net.minecraft.util.ResourceLocation
import javax.script.ScriptEngine

interface IScriptRuntime<CompiledScript, Engine : ScriptEngine> {
/**
* @param filePath the path of script
* @param resource [FIXResource] of the script
* @param dependencies all dependency script name and body
* @return cached [Engine] if found, returns null if cache is not found or not supported.
*/
fun getCachedEngine(filePath: ResourceLocation, resource: FIXResource, dependencies: Map<ResourceLocation, String>): Engine?

/**
* @param location the path of script
* @param script body of script
* @param pack model pack of the script
* @param engine the engine for which runtime compile.
*/
fun compile(location: ResourceLocation, script: String, pack: FIXModelPack? = null, engine: Engine? = null): CompiledScript {
val preprocessed = ScriptImporter.preprocessScript(script)
val name = if (pack != null) "$location(${pack.file.name})" else "$location"
return compile(preprocessed, name, engine = engine)
}

/**
* @param script body of script
* @param fileName name of the script
* @param engine the engine for which runtime compile.
*/
fun compile(script: String, fileName: String, engine: Engine? = null): CompiledScript

fun exec(script: CompiledScript): Engine

fun cache(pack: FIXModelPack, filePath: ResourceLocation, dependencies: Map<ResourceLocation, String>, engine: Engine)

object AssertingRuntime : IScriptRuntime<Nothing, Nothing> {
override fun getCachedEngine(filePath: ResourceLocation, resource: FIXResource, dependencies: Map<ResourceLocation, String>): Nothing?
= throw AssertionError("IScriptRuntime should be never used")

override fun compile(script: String, fileName: String, engine: Nothing?): Nothing
= throw AssertionError("IScriptRuntime should be never used")

override fun exec(script: Nothing): Nothing
= throw AssertionError("IScriptRuntime should be never used")

override fun cache(pack: FIXModelPack, filePath: ResourceLocation, dependencies: Map<ResourceLocation, String>, engine: Nothing)
= throw AssertionError("IScriptRuntime should be never used")
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.anatawa12.fixRtm.scripting.rhino

import com.anatawa12.fixRtm.scripting.baseScope
import org.mozilla.javascript.*

@Suppress("FunctionName")
Expand All @@ -22,7 +21,7 @@ object PrimitiveJavaHelper {
return wrapFunctionForString(stringObject.get(name, start))
}

private val stringObject = usingContext { NativeJavaObject(baseScope, "", null) }
private val stringObject = usingContext { NativeJavaObject(RhinoScriptRuntimeImpl.baseScope, "", null) }

private fun wrapFunctionForString(scriptable: Any?): Any? {
if (scriptable is NativeJavaMethod)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package com.anatawa12.fixRtm.scripting.rhino

import com.anatawa12.fixRtm.caching.ModelPackBasedCache
import com.anatawa12.fixRtm.fixCacheDir
import com.anatawa12.fixRtm.io.FIXModelPack
import com.anatawa12.fixRtm.io.FIXResource
import com.anatawa12.fixRtm.scripting.ExecutedScript
import com.anatawa12.fixRtm.scripting.IScriptRuntime
import com.anatawa12.fixRtm.utils.DigestUtils
import net.minecraft.util.ResourceLocation
import org.mozilla.javascript.ImporterTopLevel
import org.mozilla.javascript.Script
import org.mozilla.javascript.ScriptableObject
import org.mozilla.javascript.TopLevel

object RhinoScriptRuntimeImpl : IScriptRuntime<Script, FIXRhinoScriptEngine> {
override fun getCachedEngine(filePath: ResourceLocation, resource: FIXResource, dependencies: Map<ResourceLocation, String>): FIXRhinoScriptEngine? {
return getScriptAndDoScriptByCache(filePath, resource.pack, makeDependenciesData(dependencies))
?.let { scope -> FIXRhinoScriptEngine().also { it.scope = scope } }
}

override fun compile(script: String, fileName: String, engine: FIXRhinoScriptEngine?): Script =usingContext { cx ->
return ScriptCompiledClassCache.compile(script, fileName)
}

override fun exec(script: Script): FIXRhinoScriptEngine {
val engine = FIXRhinoScriptEngine()
usingContext { cx ->
val scope = makeNewScope()

script.exec(cx, scope)

engine.scope = scope
}

return engine
}

override fun cache(pack: FIXModelPack, filePath: ResourceLocation, dependencies: Map<ResourceLocation, String>, engine: FIXRhinoScriptEngine) {
ExecutedScriptCache.add(pack, filePath, makeExecutedScript(makeDependenciesData(dependencies), engine.scope))
}

val baseScope = usingContext {
val scope = TopLevel()

it.initStandardObjects(scope)

ImportScriptRhinoFunctionImpl.init(scope)

scope.sealObject()

scope
}

////////////////////////////////////////////////////////////////

private fun makeNewScope(): ScriptableObject = usingContext {
ScriptCompiledClassCache.initContext(it)

val scope = ImporterTopLevel(it, false)
scope.prototype = baseScope
return scope
}

private fun makeNewScopeWithCache(cache: ExecutedScript): ScriptableObject? = usingContext {
ScriptCompiledClassCache.initContext(it)

return cache.getScope(baseScope)
}

private fun makeExecutedScript(dependencies: Map<String, ByteArray>, scope: ScriptableObject): ExecutedScript = usingContext {
ScriptCompiledClassCache.initContext(it)

return ExecutedScript(dependencies, scope, baseScope)
}

private fun getScriptAndDoScriptByCache(filePath: ResourceLocation, pack: FIXModelPack, dependencies: Map<String, ByteArray>): ScriptableObject? {
val cache = ExecutedScriptCache.getScript(pack, filePath) ?: return null

// verify cache
if (cache.dependencies.keys != dependencies.keys) return null

for ((name, hash) in dependencies) {
if (!cache.dependencies[name]!!.contentEquals(hash)) return null
}

// load cache

val newScope = makeNewScopeWithCache(cache)
if (newScope == null) {
ExecutedScriptCache.discord(pack, filePath)
}
return newScope
}

private fun makeDependenciesData(dependencies: Map<ResourceLocation, String>): Map<String, ByteArray> {
val data = mutableMapOf<String, ByteArray>()
for ((name, script) in dependencies) {
data[name.toString()] = DigestUtils.sha1(script)
}
return data
}
}

object ExecutedScriptCache {
private val cache = ModelPackBasedCache(
fixCacheDir.resolve("excluded-script"),
0x0000 to ExecutedScript.Serializer
)

fun getScript(pack: FIXModelPack, filePath: ResourceLocation): ExecutedScript? {
return cache.get(pack, DigestUtils.sha1Hex(filePath.toString()), ExecutedScript.Serializer)
}

fun add(pack: FIXModelPack, filePath: ResourceLocation, executedScript: ExecutedScript) {
cache.put(pack, DigestUtils.sha1Hex(filePath.toString()), executedScript)
}

fun discord(pack: FIXModelPack, filePath: ResourceLocation) {
cache.discord(pack, DigestUtils.sha1Hex(filePath.toString()))
}

fun load() {}
}


0 comments on commit ce6a43a

Please sign in to comment.