Merge pull request #1307 from gnembon/strict_and_null_initialization

Strict config and null initialization
This commit is contained in:
gnembon authored and GitHub committed 2022-02-01 21:52:42 +01:00
commit 1432f06438
7 files changed
+152 -3

No files matched your search

+5 -1
View File
@@ -40,8 +40,12 @@ apps `__on_close()` will execute once per app, and with `'player'` scoped apps,
### App config via `__config()` function
If an app defines `__config` method, and that method returns a map, it will be used to apply custom settings
for this app. Currently the following options are supported:
for this app. Currently, the following options are supported:
* `'strict'` : if `true`, any use of an uninitialized variable will result in program failure. Defaults to `false` if
not specified. With `'strict'`you have to assign an initial value to any variable before using it. It is very useful
to use this setting for app debugging and for beginner programmers. Explicit initialization is not required for your
code to work, but mistakes may result from improper assumptions about initial variable values of `null`.
* `'scope'`: default scope for global variables for the app, Default is `'player'`, which means that globals and defined
functions will be unique for each player so that apps for each player will run in isolation. This is useful in
tool-like applications, where behaviour of things is always from a player's perspective. With player scope the initial run
@@ -100,6 +100,7 @@ public class CarpetScriptHost extends ScriptHost
else if (parent != null)
{
persistenceRequired = ((CarpetScriptHost)parent).persistenceRequired;
strict = parent.strict;
}
appConfig = config;
appArgTypes = argTypes;
@@ -306,6 +307,7 @@ public class CarpetScriptHost extends ScriptHost
Map<Value, Value> config = ((MapValue) ret).getMap();
setPerPlayer(config.getOrDefault(new StringValue("scope"), new StringValue("player")).getString().equalsIgnoreCase("player"));
persistenceRequired = config.getOrDefault(new StringValue("stay_loaded"), Value.TRUE).getBoolean();
strict = config.getOrDefault(StringValue.of("strict"), Value.FALSE).getBoolean();
// check requires
Value loadRequirements = config.get(new StringValue("requires"));
if (loadRequirements instanceof FunctionValue)
+1 -1
View File
@@ -738,7 +738,7 @@ public class Expression
}
var = c.host.getGlobalVariable(module, name);
if (var != null) return var;
var = (_c, _t ) -> Value.ZERO.reboundedTo(name);
var = (_c, _t ) -> _c.host.strict ? Value.UNDEF.reboundedTo(name) : Value.NULL.reboundedTo(name);
setAnyVariable(c, name, var);
return var;
}
@@ -36,6 +36,7 @@ public abstract class ScriptHost
private final Map<Value, ThreadPoolExecutor> executorServices = new HashMap<>();
private final Map<Value, Object> locks = new ConcurrentHashMap<>();
protected boolean inTermination = false;
public boolean strict;
private final Set<String> deprecations = new HashSet<>();
@@ -118,6 +119,7 @@ public abstract class ScriptHost
this.main = code;
this.perUser = perUser;
this.user = null;
this.strict = false;
ModuleData moduleData = new ModuleData(code);
initializeModuleGlobals(moduleData);
this.moduleData.put(code, moduleData);
@@ -32,7 +32,7 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
{
return new NullValue();
}
private NullValue() {super(0);}
protected NullValue() {super(0);}
@Override
public boolean equals(final Object o)
@@ -0,0 +1,140 @@
package carpet.script.value;
import carpet.script.exception.InternalExpressionException;
import com.google.gson.JsonElement;
import net.minecraft.nbt.Tag;
public class UndefValue extends NullValue {
public static final UndefValue UNDEF = new UndefValue();
private RuntimeException getError() {
return new InternalExpressionException("variable "+boundVariable+" was used before initialization under 'strict' app config");
}
@Override
public String getString()
{
throw getError();
}
@Override
public String getPrettyString()
{
return "undefined";
}
@Override
public boolean getBoolean()
{
throw getError();
}
@Override
public Value clone()
{
return new UndefValue();
}
@Override
public boolean equals(final Object o)
{
throw getError();
}
@Override
public Value slice(long fromDesc, Long toDesc) {
throw getError();
}
@Override
public NumericValue opposite() {
throw getError();
}
@Override
public int length() {
throw getError();
}
@Override
public int compareTo(Value o)
{
throw getError();
}
@Override
public Value in(Value value) {
throw getError();
}
@Override
public String getTypeString()
{
return "undef";
}
@Override
public int hashCode()
{
throw getError();
}
@Override
public Tag toTag(boolean force)
{
throw getError();
}
@Override
public Value split(Value delimiter) {
throw getError();
}
@Override
public JsonElement toJson()
{
throw getError();
}
@Override
public boolean isNull() {
throw getError();
}
@Override
public Value add(Value v) {
throw getError();
}
@Override
public Value subtract(Value v)
{
throw getError();
}
@Override
public Value multiply(Value v)
{
throw getError();
}
@Override
public Value divide(Value v)
{
throw getError();
}
@Override
public double readDoubleNumber()
{
throw getError();
}
@Override
public long readInteger()
{
throw getError();
}
}
@@ -21,6 +21,7 @@ public abstract class Value implements Comparable<Value>, Cloneable
public static NumericValue ONE = new NumericValue(1);
public static NullValue NULL = NullValue.NULL;
public static UndefValue UNDEF = UndefValue.UNDEF;
public String boundVariable;