package com.laytonsmith.core;
import com.laytonsmith.core.compiler.FileOptions;
import com.laytonsmith.core.constructs.Construct;
import com.laytonsmith.core.constructs.Target;
import com.laytonsmith.core.exceptions.ConfigCompileException;
import com.laytonsmith.core.exceptions.ConfigRuntimeException;
import com.laytonsmith.core.functions.Function;
import java.util.List;
import java.util.Set;
/**
*
*
*/
public interface Optimizable extends Function {
/**
* This constant can be returned from an optimization method to indicate that
* the need for this node has been removed entirely, and it should be removed from the code tree
* or replaced with a no-op method. More than likely it will be replaced with a no-op
* method, so that the side effects are removed.
*/
public static final ParseTree REMOVE_ME = new ParseTree(null);
/**
* This constant can be returned from an optimization method to indicate that the
* first child of this method should be pulled up and replace this method.
* For instance, if you had func('arg') it would turn into 'arg'. If there are
* multiple children, you must handle that manually. This is simply a convenience, and
* could be accomplished in other ways.
*/
public static final ParseTree PULL_ME_UP = new ParseTree(null);
public enum OptimizationOption implements SimpleDocumentation {
/**
* If this function can be run at compile time if all the parameters of
* a function are constant, this optimization can be selected. This is a
* compile time optimization.
*/
CONSTANT_OFFLINE("If all the parameters of a function are constant, a function with this optimization"
+ " will be run at compile time, and that value stored, instead of it being run each time. For"
+ " instance, the add function is like this, which means that if you were to do add(2, 2), it would"
+ " simply replace that call with 4, at compile time, which makes it more efficient during runtime.", CHVersion.V3_3_1),
/**
* If the function will return void, and the effects of the function do
* not need to be ordered, this can be selected, and the function will
* be run on another thread. Note that this will only be valid if a
* function also can be run async. This is a runtime optimization.
*/
INSTANT_RETURN("Some functions can be run async, and there is no benefit for it to wait around for the operation to finish."
+ " For instance, using sys_out() does not need to wait for the IO to flush before returning control to the script.", CHVersion.V3_3_1),
/**
* If a function can do some amount of optimization at compile time, but
* can't simply run the exec() function directly, this can be selected,
* which will cause the function's optimize() method to be called. This
* is a compile time optimization.
*/
OPTIMIZE_CONSTANT("A function may be able to do some optimization if the parameters are constant, but it may be"
+ " a bit more complicated than simply running the function. Otherwise, this is exactly like " + CONSTANT_OFFLINE.getName(), CHVersion.V3_3_1),
/**
* If a function can do some amount of optimization at compile time,
* even if some of the parameters are dynamic, this can be selected,
* which will cause the function's optimizeDynamic() method to be called. This
* is a compile time optimization.
*/
OPTIMIZE_DYNAMIC("Some functions can do some amount of optimization or compilation checks, even if the function is sent dynamic"
+ " parameters. For instance, if(true, rand(), '1') can be changed simply to rand(), because the condition is hard coded"
+ " to be true. In this case, the compile tree is smaller, which makes it more efficient.", CHVersion.V3_3_1),
/**
* If, given the same parameters, the return of this function could be
* cached (that is, it is a const function) this can be selected. This
* does not guarantee that the results will be cached, since there is a
* memory trade off, and at large enough volumes, a time trade off too,
* with such a method, the system may decide to forego the cache, and
* simply re-call the method. However, given an often enough call, it
* should speed up execution in many cases. If the function returns an
* array, the array will be cloned before actually being returned.
*/
CACHE_RETURN("If a function is able to optimize out constant inputs, it can likely also cache the return value."
+ " If the engine determines that it is faster to cache the returned values vs re-running the function,"
+ " it may choose to do so. This is a runtime optimization, and is calculated by the engine itself to determine"
+ " which method is faster, so there is no guarantee that any optimization will occur, however, unless this"
+ " option is specified, it will certainly not.", CHVersion.V3_3_1),
/**
* If this function is terminal, that is, it will ALWAYS interrupt
* program flow, this can be selected. For instance, return() is an example. This is used
* during optimization, and to give compiler warnings.
*/
TERMINAL("If a function is \"terminal\", that is, it is guaranteed to have abnormal code flow (for instance,"
+ " return() or throw()) it is marked terminal, which is used by the compiler to issue warnings, in the"
+ " event you make some code unreachable by putting it under a terminal statement, and to optimize"
+ " by removing the unreachable code from the code tree.", CHVersion.V3_3_1),
/**
* If a function is "side effect free", that is, if the return value is unused, the function
* itself does nothing, then this optimization can be specified. This is mostly useful for cases
* where the value returns a check, but the check has been determined by the compiler to be unused,
* making the entire call itself unneeded, allowing the call itself to be removed from the tree.
*/
NO_SIDE_EFFECTS("If a function is \"side effect free\", that is, if the return value is unused, the function"
+ " itself does nothing, then this optimization can be specified. This is mostly useful for cases"
+ " where the value returns a check, but the check has been determined by the compiler to be unused,"
+ " making the entire call itself unneeded, allowing the call itself to be removed from the tree.", CHVersion.V3_3_1),
/**
* Some functions do want to do linking, but in a special, custom way. If this is specified, then
* the function will have the link() method called on it, in place of the default linking mechanism that
* the compiler provides.
*/
CUSTOM_LINK("Some functions do want to do linking, but in a special, custom way. If this is specified, then"
+ " the function will have the link() method called on it, in place of the default linking mechanism that"
+ " the compiler provides.", CHVersion.V3_3_1),
/**
* This is a priority optimization function, meaning it needs to be optimized before its children are.
* This is required when optimization of the children could cause different internal behavior, for instance
* if this function is expecting the precense of soem code element, but the child gets optimized out, this
* would cause an error, even though the user did in fact provide code in that section.
*/
PRIORITY_OPTIMIZATION("This is a priority optimization function, meaning it needs to be optimized before its children are."
+ " This is required when optimization of the children could cause different internal behavior, for instance"
+ " if this function is expecting the precense of soem code element, but the child gets optimized out, this"
+ " would cause an error, even though the user did in fact provide code in that section.", CHVersion.V3_3_1);
private final CHVersion since;
private final String docs;
private OptimizationOption(String docs, CHVersion since){
this.docs = docs;
this.since = since;
}
@Override
public String getName() {
return name();
}
@Override
public String docs() {
return docs;
}
@Override
public CHVersion since() {
return since;
}
}
/**
* Returns an array of optimization options that apply to this function.
*
* @return
*/
public Set<OptimizationOption> optimizationOptions();
/**
* This is called during compile time, if canOptimize returns true. It
* should return the construct to replace this function, if possible. If
* only type checking is being done, it may return null, in which case no
* changes will be made to the parse tree. During the optimization, it is
* also possible for a function to throw a ConfigCompileException. It may
* also throw a ConfigRuntimeException, which will be caught, and changed
* into a ConfigCompileException.
*
* @param t
* @param args
* @return
*/
public Construct optimize(Target t, Construct... args) throws ConfigRuntimeException, ConfigCompileException;
/**
* If the function indicates it can optimize dynamic values, this method is
* called. It may also throw a compile exception should the parameters be
* unacceptable. It may return null if no changes should be made (which is
* likely the default).
*
* @param t
* @param children The children that are being passed to this function
* @param fileOptions The file options for the top level function
* @return
*/
public ParseTree optimizeDynamic(Target t, List<ParseTree> children, FileOptions fileOptions) throws ConfigCompileException, ConfigRuntimeException;
/**
* Does custom linking in a given function. This is called if the {@link OptimizationOption#CUSTOM_LINK} option
* is specified. This is useful to override if some error checking should happen after all optimizations are done,
* across the entire AST, not just across this function and it's children. For instance, bind() uses this to provide
* a compile error if it isn't compiled out via an event_exists directive.
* @param t
* @param children
*/
public void link(Target t, List<ParseTree> children) throws ConfigCompileException;
}