Skip to content
Permalink
Browse files
Re-use environment for compilation (#1209)
* Re-use environment for compilation

This is an optimization which uses the same environment to compile multiple msa scripts, rather than creating a new environment every time. Environment creation reads and parses a few files, so this is an expensive operation.

* Create StaticRuntimeEnv for static final functionality

- Create StaticRuntimeEnv and move functionality from GlobalEnv to this class if it is static and final accross compile units.
- Replace reflection in RandomTests test with Mockito, as this allows for the accessed field to be made final like it should.

* Use a fresh environment per script

Clone the environment for every (msa) script. Since StaticRuntimeEnv returns itself when cloned, its contents act like a cache.

* Move test code out of production code

Use Mockito to mock unused environment objects instead.

* Use cheaper newline replacement in read()

There's no need to replace "\n" with "\n".
  • Loading branch information
Pieter12345 committed Jun 14, 2020
1 parent ca0531d commit 675fab075c675bf7f4cd6257972501c7a64e3540
Show file tree
Hide file tree
Showing 32 changed files with 294 additions and 252 deletions.
@@ -13,6 +13,7 @@
import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.environments.GlobalEnv;
import com.laytonsmith.core.environments.RuntimeMode;
import com.laytonsmith.core.environments.StaticRuntimeEnv;
import com.laytonsmith.core.exceptions.CancelCommandException;
import com.laytonsmith.core.exceptions.ConfigCompileException;
import com.laytonsmith.core.exceptions.ConfigCompileGroupException;
@@ -139,15 +140,17 @@ public void textLine(MCPlayer p, String line) {
*/
public void execute(String script, final MCPlayer p) throws ConfigCompileException, ConfigCompileGroupException {
TokenStream stream = MethodScriptCompiler.lex(script, null, new File("Interpreter"), true);
GlobalEnv gEnv = new GlobalEnv(plugin.executionQueue, plugin.profiler, plugin.persistenceNetwork,
GlobalEnv gEnv = new GlobalEnv(plugin.executionQueue,
CommandHelperFileLocations.getDefault().getConfigDirectory(),
plugin.profiles, new TaskManagerImpl(), EnumSet.of(RuntimeMode.EMBEDDED, RuntimeMode.INTERPRETER));
EnumSet.of(RuntimeMode.EMBEDDED, RuntimeMode.INTERPRETER));
StaticRuntimeEnv staticRuntimeEnv = new StaticRuntimeEnv(plugin.profiler,
plugin.persistenceNetwork, plugin.profiles, new TaskManagerImpl());
gEnv.SetDynamicScriptingMode(true);
CommandHelperEnvironment cEnv = new CommandHelperEnvironment();
cEnv.SetPlayer(p);
CompilerEnvironment compilerEnv = new CompilerEnvironment();
compilerEnv.setLogCompilerWarnings(false);
Environment env = Environment.createEnvironment(gEnv, cEnv, compilerEnv);
Environment env = Environment.createEnvironment(gEnv, staticRuntimeEnv, cEnv, compilerEnv);
ParseTree tree = MethodScriptCompiler.compile(stream, env, env.getEnvClasses());
final boolean isInterpeterMode = interpreterMode.remove(p.getName());
try {
@@ -16,6 +16,7 @@
import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.environments.GlobalEnv;
import com.laytonsmith.core.environments.RuntimeMode;
import com.laytonsmith.core.environments.StaticRuntimeEnv;
import com.laytonsmith.core.events.EventUtils;
import com.laytonsmith.core.exceptions.CancelCommandException;
import com.laytonsmith.core.exceptions.ConfigCompileException;
@@ -132,16 +133,17 @@ public boolean alias(String command, final MCCommandSender sender) {
Static.getLogger().log(Level.INFO, "Running alias on " + sender.getName() + " ---> " + command);
}

GlobalEnv gEnv = new GlobalEnv(parent.executionQueue, parent.profiler, parent.persistenceNetwork,
MethodScriptFileLocations.getDefault().getConfigDirectory(),
parent.profiles, new TaskManagerImpl(), EnumSet.of(RuntimeMode.EMBEDDED));
GlobalEnv gEnv = new GlobalEnv(parent.executionQueue,
MethodScriptFileLocations.getDefault().getConfigDirectory(), EnumSet.of(RuntimeMode.EMBEDDED));
StaticRuntimeEnv staticRuntimeEnv = new StaticRuntimeEnv(parent.profiler,
parent.persistenceNetwork, parent.profiles, new TaskManagerImpl());
CommandHelperEnvironment cEnv = new CommandHelperEnvironment();
cEnv.SetCommandSender(sender);
cEnv.SetCommand(command);
Environment env = Environment.createEnvironment(gEnv, cEnv, compilerEnv);
Environment env = Environment.createEnvironment(gEnv, staticRuntimeEnv, cEnv, compilerEnv);

this.addPlayerReference(sender);
ProfilePoint alias = gEnv.GetProfiler().start("Alias - \"" + command + "\"", LogLevel.ERROR);
ProfilePoint alias = staticRuntimeEnv.GetProfiler().start("Alias - \"" + command + "\"", LogLevel.ERROR);
try {
script.run(script.getVariables(command), env, output -> {
// If this is a macro, we need to run the output as a command
@@ -263,9 +265,10 @@ public final void reload(MCPlayer player, String[] settings, boolean firstLoad)
MSLog.GetLogger().e(MSLog.Tags.GENERAL, ex.getMessage(), Target.UNKNOWN);
return;
}
GlobalEnv gEnv = new GlobalEnv(parent.executionQueue, parent.profiler, parent.persistenceNetwork,
MethodScriptFileLocations.getDefault().getConfigDirectory(),
parent.profiles, new TaskManagerImpl(), EnumSet.of(RuntimeMode.EMBEDDED));
GlobalEnv gEnv = new GlobalEnv(parent.executionQueue,
MethodScriptFileLocations.getDefault().getConfigDirectory(), EnumSet.of(RuntimeMode.EMBEDDED));
StaticRuntimeEnv staticRuntimeEnv = new StaticRuntimeEnv(parent.profiler,
parent.persistenceNetwork, parent.profiles, new TaskManagerImpl());
gEnv.SetLabel(Static.GLOBAL_PERMISSION);
if(options.reloadExecutionQueue()) {
ProfilePoint stoppingExecutionQueue = parent.profiler.start("Stopping execution queues", LogLevel.VERBOSE);
@@ -276,7 +279,7 @@ public final void reload(MCPlayer player, String[] settings, boolean firstLoad)
}
}
CommandHelperEnvironment cEnv = new CommandHelperEnvironment();
Environment env = Environment.createEnvironment(gEnv, cEnv, compilerEnv);
Environment env = Environment.createEnvironment(gEnv, staticRuntimeEnv, cEnv, compilerEnv);
if(options.reloadGlobals()) {
ProfilePoint clearingGlobals = parent.profiler.start("Clearing globals", LogLevel.VERBOSE);
try {
@@ -385,7 +388,7 @@ public final void reload(MCPlayer player, String[] settings, boolean firstLoad)
}
ProfilePoint compilerMSA = parent.profiler.start("Compilation of MSA files in Local Packages", LogLevel.VERBOSE);
try {
localPackages.compileMSA(scripts, player, env.getEnvClasses());
localPackages.compileMSA(scripts, player, env, env.getEnvClasses());
} finally {
compilerMSA.stop();
}
@@ -671,7 +674,7 @@ public void appendMS(String s, File path) {
}

public void compileMSA(List<Script> scripts, MCPlayer player,
Set<Class<? extends Environment.EnvironmentImpl>> envs) {
Environment env, Set<Class<? extends Environment.EnvironmentImpl>> envs) {
for(FileInfo fi : msa) {
List<Script> tempScripts;
try {
@@ -685,7 +688,7 @@ public void compileMSA(List<Script> scripts, MCPlayer player,
for(Script s : tempScripts) {
try {
try {
s.compile();
s.compile(env.clone());
s.checkAmbiguous(scripts);
scripts.add(s);
} catch (ConfigCompileException e) {
@@ -696,6 +699,8 @@ public void compileMSA(List<Script> scripts, MCPlayer player,
ConfigRuntimeException.HandleUncaughtException(e, "Compile error in script."
+ " Compilation will attempt to continue, however.", player);
}
} catch (CloneNotSupportedException e) {
throw new Error("Environment wasn't clonable, while it should be.", e);
}
} catch (RuntimeException ee) {
throw new RuntimeException("While processing a script, "
@@ -26,6 +26,7 @@
import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.environments.GlobalEnv;
import com.laytonsmith.core.environments.InvalidEnvironmentException;
import com.laytonsmith.core.environments.StaticRuntimeEnv;
import com.laytonsmith.core.exceptions.CRE.AbstractCREException;
import com.laytonsmith.core.exceptions.CRE.CRECastException;
import com.laytonsmith.core.exceptions.CRE.CREInsufficientPermissionException;
@@ -307,7 +308,7 @@ public Mixed eval(ParseTree c, final Environment env) throws CancelCommandExcept
if(p == null) {
throw new CREInvalidProcedureException("Unknown procedure \"" + m.val() + "\"", m.getTarget());
}
ProfilePoint pp = env.getEnv(GlobalEnv.class).GetProfiler().start(m.val() + " execution", LogLevel.INFO);
ProfilePoint pp = env.getEnv(StaticRuntimeEnv.class).GetProfiler().start(m.val() + " execution", LogLevel.INFO);
Mixed ret;
try {
if(debugOutput) {
@@ -355,9 +356,10 @@ public Mixed eval(ParseTree c, final Environment env) throws CancelCommandExcept
}
if(f.useSpecialExec()) {
ProfilePoint p = null;
if(f.shouldProfile() && env.getEnv(GlobalEnv.class).GetProfiler() != null
&& env.getEnv(GlobalEnv.class).GetProfiler().isLoggable(f.profileAt())) {
p = env.getEnv(GlobalEnv.class).GetProfiler().start(f.profileMessageS(c.getChildren()), f.profileAt());
if(f.shouldProfile() && env.getEnv(StaticRuntimeEnv.class).GetProfiler() != null
&& env.getEnv(StaticRuntimeEnv.class).GetProfiler().isLoggable(f.profileAt())) {
p = env.getEnv(StaticRuntimeEnv.class)
.GetProfiler().start(f.profileMessageS(c.getChildren()), f.profileAt());
}
Mixed ret;
try {
@@ -399,9 +401,9 @@ public Mixed eval(ParseTree c, final Environment env) throws CancelCommandExcept
//It takes a moment to generate the toString of some things, so lets not do it
//if we actually aren't going to profile
ProfilePoint p = null;
if(f.shouldProfile() && env.getEnv(GlobalEnv.class).GetProfiler() != null
&& env.getEnv(GlobalEnv.class).GetProfiler().isLoggable(f.profileAt())) {
p = env.getEnv(GlobalEnv.class).GetProfiler().start(f.profileMessage(ca), f.profileAt());
if(f.shouldProfile() && env.getEnv(StaticRuntimeEnv.class).GetProfiler() != null
&& env.getEnv(StaticRuntimeEnv.class).GetProfiler().isLoggable(f.profileAt())) {
p = env.getEnv(StaticRuntimeEnv.class).GetProfiler().start(f.profileMessage(ca), f.profileAt());
}
Mixed ret;
try {
@@ -672,11 +674,11 @@ public List<Variable> getVariables(String command) {
return vars;
}

public Script compile() throws ConfigCompileException, ConfigCompileGroupException {
public Script compile(Environment env) throws ConfigCompileException, ConfigCompileGroupException {
try {
verifyLeft();
compileLeft();
compileRight();
compileRight(env);
} catch (ConfigCompileException e) {
compilerError = true;
throw e;
@@ -885,7 +887,7 @@ private boolean compileLeft() {
return true;
}

public void compileRight() throws ConfigCompileException, ConfigCompileGroupException {
public void compileRight(Environment env) throws ConfigCompileException, ConfigCompileGroupException {
List<Token> temp = new ArrayList<>();
right = new ArrayList<>();
for(Token t : fullRight) {
@@ -903,7 +905,7 @@ public void compileRight() throws ConfigCompileException, ConfigCompileGroupExce
cright = new ArrayList<>();
for(List<Token> l : right) {
StaticAnalysis analysis = new StaticAnalysis(true);
cright.add(MethodScriptCompiler.compile(new TokenStream(l, fileOptions), null, envs, analysis));
cright.add(MethodScriptCompiler.compile(new TokenStream(l, fileOptions), env, envs, analysis));
}
}

@@ -47,6 +47,7 @@
import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.environments.GlobalEnv;
import com.laytonsmith.core.environments.RuntimeMode;
import com.laytonsmith.core.environments.StaticRuntimeEnv;
import com.laytonsmith.core.exceptions.CRE.CREBadEntityException;
import com.laytonsmith.core.exceptions.CRE.CRECastException;
import com.laytonsmith.core.exceptions.CRE.CREFormatException;
@@ -1329,10 +1330,12 @@ public static Environment GenerateStandaloneEnvironment(
PersistenceNetwork persistenceNetwork = new PersistenceNetworkImpl(MethodScriptFileLocations.getDefault().getPersistenceConfig(),
new URI(URLEncoder.encode("sqlite://" + new File(platformFolder, "persistence.db").getCanonicalPath().replace('\\', '/'), "UTF-8")), options);
GlobalEnv gEnv = new GlobalEnv(new MethodScriptExecutionQueue("MethodScriptExecutionQueue", "default"),
new Profiler(MethodScriptFileLocations.getDefault().getProfilerConfigFile()), persistenceNetwork,
platformFolder, profiles, new TaskManagerImpl(), runtimeModes);
platformFolder, runtimeModes);
gEnv.SetLabel(GLOBAL_PERMISSION);
return Environment.createEnvironment(gEnv, new CompilerEnvironment());
StaticRuntimeEnv staticRuntimeEnv = new StaticRuntimeEnv(
new Profiler(MethodScriptFileLocations.getDefault().getProfilerConfigFile()),
persistenceNetwork, profiles, new TaskManagerImpl());
return Environment.createEnvironment(gEnv, staticRuntimeEnv, new CompilerEnvironment());
}

/**
@@ -592,8 +592,7 @@ private void compileIncludesLinkCycles(Set<IncludeReference> handledRefs, Set<In
if(includeAnalysis == null) {
includeAnalysis = new StaticAnalysis(false);
IncludeCache.get(file, env, envs, includeAnalysis, includeRef.getTarget());
assert IncludeCache.getStaticAnalysis(file) != null
: "Failed to cache include analysis.";
assert IncludeCache.getStaticAnalysis(file) != null : "Failed to cache include analysis.";
}
} catch (CREException e) {

0 comments on commit 675fab0

Please sign in to comment.