Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,10 @@ public static boolean neverNil(ImExpr e, ImTranslator translator) {
return f == translator.luaRawConcatFunc
|| f == translator.luaRawOrEmptyFunc
|| f == translator.luaRawToStringFunc
|| (f.isNative() && STRING_NATIVES_NEVER_NIL.contains(f.getName()));
|| (f.isNative() && STRING_NATIVES_NEVER_NIL.contains(f.getName()))
// a string load of the hashtable emulation answers "" when nothing is stored
|| (LuaHashtable.op(translator, f) instanceof LuaHashtable.Op op
&& op.kind() == LuaHashtable.Kind.LOAD && op.values() == LuaHashtable.Values.STR);
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
* <ul>
* <li>InitHashtable is the table constructor;</li>
* <li>a load is {@code (h.T[p] or __wurst_htEmpty)[c]}, then the default the helper answers: {@code or 0},
* {@code or 0.0}, {@code == true}, and nothing for a string or a handle, which are nil when absent;</li>
* {@code or 0.0}, {@code == true}, {@code or ""} (a null string is {@code ""} on Lua), and nothing for a
* handle, which is nil when absent;</li>
* <li>a HaveSaved test is the same read {@code ~= nil};</li>
* <li>a save is {@code (h.T[p] or __wurst_htNewChild(h.T, p))[c] = v};</li>
* <li>a remove is {@code if h.T[p] then h.T[p][c] = nil end}. Not through the empty child: in Lua 5.3
Expand Down Expand Up @@ -64,7 +65,8 @@ static LuaExpr expression(ImFunctionCall call, LuaHashtable.Op op, LuaTranslator
case INT -> LuaAst.LuaExprBinary(value, LuaAst.LuaOpOr(), LuaAst.LuaExprIntVal("0"));
case REAL -> LuaAst.LuaExprBinary(value, LuaAst.LuaOpOr(), LuaAst.LuaExprRealVal("0.0"));
case BOOL -> LuaAst.LuaExprBinary(value, LuaAst.LuaOpEquals(), LuaAst.LuaExprBoolVal(true));
case STR, HANDLE -> value;
case STR -> LuaAst.LuaExprBinary(value, LuaAst.LuaOpOr(), LuaAst.LuaExprStringVal(""));
case HANDLE -> value;
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,8 +379,9 @@ public class LuaNatives {
// true only for a stored true, as LoadBoolean answers false when nothing is stored
addHashtableLoad(withWurstPrefix("LoadBoolean"), "__wurst_ht_bool", "false", "x[c] == true");
addHashtableLoad(withWurstPrefix("LoadReal"), "__wurst_ht_real", "0.0", "x[c] or 0.0");
// nil when absent, as the string and handle loads have always answered
addHashtableLoad(withWurstPrefix("LoadStr"), "__wurst_ht_str", "nil", "x[c]");
// "" when absent: a null string is "" on Lua (EliminateLocalTypes), so a missing string compares equal to null
addHashtableLoad(withWurstPrefix("LoadStr"), "__wurst_ht_str", "\"\"", "x[c] or \"\"");
// nil when absent, which is the null handle
addHashtableLoad(withWurstPrefix(HASHTABLE_HANDLE_LOAD_NAMES), "__wurst_ht_handle", "nil", "x[c]");

addHashtableEntryOperation(withWurstPrefix("HaveSavedInteger"), "__wurst_ht_int", "return x ~= nil and x[c] ~= nil");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,9 @@ public void hashtableOperationsArePrintedWhereTheyAreCalled() throws IOException
"\\(\\(\\w+\\.__wurst_ht_int\\[1\\] or __wurst_htEmpty\\)\\[2\\] ~= nil\\)", init);
assertMatches("a handle load answers nil when absent, as the helper did",
"\\(\\(\\w+\\.__wurst_ht_handle\\[1\\] or __wurst_htEmpty\\)\\[2\\] == nil\\)", init);
assertMatches("so does a string load, which the concatenation guards",
assertMatches("a string load answers \"\", the null string on Lua, when absent",
"\\(\\w+\\.__wurst_ht_str\\[1\\] or __wurst_htEmpty\\)\\[2\\] or \"\"\\)", init);
assertFalse("so the concatenation adds no guard of its own:\n" + init, init.contains("or \"\") or \"\")"));
assertMatches("a remove never writes the shared empty child",
"if (\\w+)\\.__wurst_ht_int\\[1\\] then\\s+\\1\\.__wurst_ht_int\\[1\\]\\[2\\] = nil\\s+end", init);
for (String subtable : new String[]{"int", "bool", "real", "str", "handle"}) {
Expand All @@ -117,7 +118,8 @@ public void hashtableOperationsArePrintedWhereTheyAreCalled() throws IOException
"init",
" let h = InitHashtable()",
" check(LoadInteger(h, 1, 1) == 0 and LoadReal(h, 1, 1) == 0. and not LoadBoolean(h, 1, 1)"
+ " and \"x\" + LoadStr(h, 1, 1) == \"x\" and LoadTimerHandle(h, 1, 1) == null, \"an absent parent key reads defaults\")",
+ " and \"x\" + LoadStr(h, 1, 1) == \"x\" and LoadStr(h, 1, 1) == null and LoadTimerHandle(h, 1, 1) == null,"
+ " \"an absent parent key reads defaults\")",
" check(not HaveSavedInteger(h, 1, 1) and not HaveSavedReal(h, 1, 1) and not HaveSavedBoolean(h, 1, 1)"
+ " and not HaveSavedString(h, 1, 1) and not HaveSavedHandle(h, 1, 1), \"an absent parent key holds nothing\")",
" SaveInteger(h, 1, 1, 11)",
Expand All @@ -132,6 +134,7 @@ public void hashtableOperationsArePrintedWhereTheyAreCalled() throws IOException
" check(LoadReal(h, 1, 1) == 2.5 and HaveSavedReal(h, 1, 1), \"real\")",
" SaveStr(h, 1, 1, \"s\")",
" check(LoadStr(h, 1, 1) == \"s\" and HaveSavedString(h, 1, 1), \"string\")",
" check(LoadStr(h, 1, 2) == null and not HaveSavedString(h, 1, 2), \"an absent child key reads the null string\")",
" let t = CreateTimer()",
" SaveTimerHandle(h, 1, 1, t)",
" check(LoadTimerHandle(h, 1, 1) == t and HaveSavedHandle(h, 1, 1), \"handle\")",
Expand Down Expand Up @@ -195,10 +198,12 @@ public void anOperandWithAnEffectKeepsTheCallOfTheHelper() throws IOException {
" SaveInteger(h, 1, 6, 7)",
" if LoadInteger(h, key(\"P\", 1), key(\"C\", 2)) != 3 or not HaveSavedInteger(h, 1, key(\"H\", 4))",
" testFail(\"stored through the helper\")",
" if LoadStr(h, key(\"L\", 1), 9) != null",
" testFail(\"an absent string through the helper is the null string\")",
" RemoveSavedInteger(h, key(\"r\", 9), key(\"s\", 2))",
" RemoveSavedInteger(h, key(\"R\", 1), key(\"S\", 2))",
" FlushChildHashtable(h, key(\"f\", 1))",
" if trace != \"pcvwPCHrsRSf\"",
" if trace != \"pcvwPCHLrsRSf\"",
" testFail(\"operands evaluated as \" + trace)",
" if HaveSavedInteger(h, 1, 2) or HaveSavedInteger(h, 1, 4) or LoadInteger(h, 9, 2) != 0",
" testFail(\"removed and flushed\")",
Expand All @@ -207,8 +212,8 @@ public void anOperandWithAnEffectKeepsTheCallOfTheHelper() throws IOException {
String compiled = compiled("anOperandWithAnEffectKeepsTheCallOfTheHelper");
LuaTranslator.assertNoLeakedHashtableNativeCalls(compiled);
String init = functionBody(compiled, "init_Test");
for (String helper : new String[]{"SaveInteger", "LoadInteger", "HaveSavedInteger", "RemoveSavedInteger",
"FlushChildHashtable"}) {
for (String helper : new String[]{"SaveInteger", "LoadInteger", "HaveSavedInteger", "LoadStr",
"RemoveSavedInteger", "FlushChildHashtable"}) {
assertTrue("an operation with an effect in an operand calls its helper: " + helper + "\n" + init,
init.contains("__wurst_" + helper + "("));
assertTrue("a helper which is called is defined: " + helper + "\n" + compiled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ public void hashtableLoadsUseTypeSpecificBuckets() {
assertTrue(loadStr.contains("h.__wurst_ht_str"));
assertTrue(loadHandle.contains("h.__wurst_ht_handle"));
assertTrue(loadInt, loadInt.contains("return x[c] or 0"));
assertTrue("a missing string is nil: " + loadStr, loadStr.contains("return nil") && loadStr.contains("return x[c]\n"));
assertTrue("a missing string is \"\", the null string on Lua: " + loadStr,
loadStr.contains("return \"\"") && loadStr.contains("return x[c] or \"\"\n"));
assertTrue("a missing handle is nil: " + loadHandle,
loadHandle.contains("return nil") && loadHandle.contains("return x[c]\n"));
assertTrue(loadBool, loadBool.contains("return x[c] == true"));
assertFalse("no subtable is ever missing: " + loadInt, loadInt.contains("t == nil"));
}
Expand Down
Loading