Uncaught exception handler is not shared
LadyCailinBot opened this issue ยท 1 comments
CMDHELPER-3162 - Reported by PseudoKnight
When setting the uncaught exception handlers, aliases do not use it because they create an entirely new environment every time.
Neither solution was correct. I'm thinking for now we need to have a new environment created on reload, clone that for main.ms, also store it in aliascore so that any time an alias is run it can clone it. That way any uncaught exception handlers defined in main.ms (or other scripts) can be referenced in aliases. Also, unlike my previous PR, this would not affect future systems that add separate distinct environments.
I would also like to discuss the possibility of not executing auto_includes every time an alias is run. Unless there's some dynamic code in an auto_include, which I would think would be bad practice, it wouldn't affect behavior if we just used the procs we got when we execute on reload. This would even further reduce alias run overhead, but would need to pass it by LC.
Comment by LadyCailin
So, I'm going to basically just start from scratch here, and describe what the Environment should do and what I expect it to be able to do in the future, and then we can use that to figure out what we need to change.
An Environment has 3 main requirements.
-
It should be generic, meaning that new environment features should be able to be added, without the core needing to change (hence the Environment vs EnvironmentImpl). The environment can pass along multiple sub environments, which, if a function doesn't know about them, that's ok, it is transparently passed through, but doesn't need to touch it. (Side note: The only exception is GlobalEnv, which should always be available, though I've gone back and forth about whether or not to special case it by giving it a direct accessor in Environment. However, since Environment is also used at compile time to pass CompilerEnvironment but not GlobalEnv, I still don't think it would make sense to special case GlobalEnv.)
-
The environment contains two types of values. Startup values (things that are set at script startup, and generally remain unchanged throughout runtime, but may vary from script to script), and runtime values (things that are actively changed as the script is run). In many ways, these runtime values act like a heap, and the things that are set at script startup act like the stack.
The environments must be cloneable, because at runtime, closures need to branch off of the current environment, but keep their own. However, there might be some cases where it only makes sense to clone certain fields for closure branches, and keep others common.
- Snapins should have totally separate environments, which should not be allowed to interact with each other.
Given these three things, it looks like we need to have 3 distinct "clone" operations. One, where all environments are generated per snap in (where we consider the way it works today as a single, monolithic snap in), a clone operation per execution unit (which may inherit some core things, but things like variables, etc, are reset) and finally a clone (or maybe "branch" is a better term here, to use a git analogy) for closures, where only some values are actually cloned, but others stay the same.
So, perhaps the best course of action should be to create two methods in EnvironmentImpl (keeping in mind that a sub environment can provide a constructor that may or may not require specific inputs for initial construction. This acts as the third "clone".)
interface EnvironmentImpl {
/**
* Clones the environment in such a way that it can be used for a new execution environment.
* This will clear out any values that should not be shared across execution units, such as
* variable tables and the like, but will keep things that are shared, such as DaemonManagers
* and uncaught exception handlers.
*/
EnvironmentImpl cloneForExecutionUnit();
/**
* Clones the environment entirely. This essentially acts as a branch, where the
* environment is intact for the most part, but separate from the old one, and changes
* are independent of the old environment.
* @param branchStrategy (An enum.) The branching strategy to use. When branching occurs, this
* can happen in different contexts, and those contexts may change what exactly we expect from
* the environment at the end of the day. Of course, the function that is creating the
* branch can simply modify the environment after the fact to change it to suit those
* particular needs, but it may be that the particular strategy will be cloning something
* needlessly, because it's going to simply be cleared out after the fact.
* (For instance iclosure vs closure.) Each EnvironmentImpl must actively consider each
* BranchingStrategy, and if it encounters an unknown BranchingStrategy, it must throw an
* exception. If the BranchingStrategy passed in does not apply, no exception need be thrown,
* so long as it does not simply ignore unknown ones.
*/
EnvironmentImpl cloneForBranch(BranchStrategy branchStrategy);
}
As for the auto_include.ms change, I think that's perfectly acceptable, but we need to ensure (at compile time) a function is not doing dynamic stuff in auto_include.ms, we can't just assume they are not. We should be able to easily enough do this though, as we can create a new OptimizationOption that states that this function is not a dynamic one, and then ensure that at the top level, only those functions are included in the file.