Skip to content
Merged
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
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Two incompatible generic systems coexist. The syntax of the type parameter picks
* **All existing tests must continue to pass.** The authoritative behavior is defined by the existing test suite.
* **Test-driven**: new behavior requires tests showing failure before the change and success after, in the existing test style. Bug fixes start with a failing repro.
* **Minimal, well-scoped edits**: small local patches; no large refactors (renames, structural moves, mass rewrites), no altered language semantics without tests demonstrating the intended outcome, no new external dependencies unless requested.
* **Generated code**: never modify files generated by `:gen`; change the `.parseq` specs or grammars and regenerate (`./gradlew :gen`). Adding a node type to a `.parseq` sum type breaks every exhaustive matcher at compile time — fix those compile errors first. Visitors with defaults, `instanceof` chains and `default` switch arms do not break, so also grep for the sibling node types.
* **Generated code**: never modify files generated by `:gen`; change the `.parseq` specs or grammars and regenerate (`./gradlew :gen`). Adding a node type to a `.parseq` sum type breaks every exhaustive matcher at compile time; fix those compile errors first. Visitors with defaults, `instanceof` chains and `default` switch arms do not break, so also grep for the sibling node types.

---

Expand Down Expand Up @@ -323,7 +323,7 @@ Rules for backend work:
### Reference semantics for arithmetic

* Integer/real division and modulo semantics are centralized: `WurstOperator.moduloInteger` is Blizzard.j's ModuloInteger (truncated remainder, plus divisor if negative), `WurstOperator.moduloReal` is ModuloReal's formula itself (`a - R2I(a / b) * b` in float, plus divisor if negative, not the exact remainder), and Jass `div` truncates toward zero.
* The Lua helpers (`__wurst_intDiv`, `__wurst_modInt`, `__wurst_modReal`, built in `LuaNativeLowering`), the interpreter's `MathProvider` mocks, and constant folding (`SimpleRewrites`) must all stay consistent with those helpers — never reimplement div/mod locally. A real modulo on Lua is Blizzard.j's `ModuloReal` formula, as on Jass, never `math.fmod` or Lua's float `%`, which differ from Jass in the game ([`docs/WC3_RUNTIME.md`](docs/WC3_RUNTIME.md) "Numbers").
* The Lua helpers (`__wurst_intDiv`, `__wurst_modInt`, `__wurst_modReal`, built in `LuaNativeLowering`), the interpreter's `MathProvider` mocks, and constant folding (`SimpleRewrites`) must all stay consistent with those helpers; never reimplement div/mod locally. A real modulo on Lua is Blizzard.j's `ModuloReal` formula, as on Jass, never `math.fmod` or Lua's float `%`, which differ from Jass in the game ([`docs/WC3_RUNTIME.md`](docs/WC3_RUNTIME.md) "Numbers").
* Jass `==` on reals allows a difference of 0.001 (`WurstOperator.jassRealEquals`), while `!=` and the orderings are exact, so on the Jass target `not (a == b)` is not `a != b` for reals. Lua compares exactly, by design: the tolerance would cost every real comparison. Both interpreters use that helper; the optimiser must not swap one for the other or fold nearly equal reals on Jass.
* The game reads Jass real literals inexactly and computes Lua reals with a 24-bit mantissa (measurements: [`docs/WC3_RUNTIME.md`](docs/WC3_RUNTIME.md) "Numbers"). So on both targets real folding (`SimpleRewrites.foldRealExactly`) reads and writes only exact literals and folds an operation only when its exact result is one. Never fold in double or float, and never treat one `R2I` result as proof of a precision. The test runtime's Lua computes in double, so a test that runs the emitted Lua does not show the game's rounding.

Expand Down
4 changes: 2 additions & 2 deletions BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ Notes; finished-work narrative does not.
save first, or refuse unsaved files, is open.

- **Reuse translation work on unchanged map runs.** After checked-model reuse, Survival Chaos Reborn
takes 4.5–5.2 s for an unoptimised warm RunMap: AST→IM 1.3–1.4 s, required Jass lowering 1.3–1.7 s,
compile-time work 0.9–1.0 s, map injection 0.4–0.6 s. Whole-project typechecking is now skipped.
takes 4.5 to 5.2 s for an unoptimised warm RunMap: AST→IM 1.3 to 1.4 s, required Jass lowering 1.3 to 1.7 s,
compile-time work 0.9 to 1.0 s, map injection 0.4 to 0.6 s. Whole-project typechecking is now skipped.
Investigate retaining a pristine IM snapshot keyed by model/config/target state, then copying it
before mutable compile-time/backend passes; compare snapshot cost with translation and test edits,
configuration changes, cancellation and both targets against fresh builds. This needs an explicit
Expand Down
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,12 @@
from inside a constructor. A generic object stays erased there and only the paths needing a concrete
type are specialised, so the concrete type has to reach those paths rather than the object: a
specialised method is bound to the class its objects are allocated from, a call which names its target
— `super.m()` is one — takes the instantiation from the class its receiver is used as, and a function
(such as `super.m()`) takes the instantiation from the class its receiver is used as, and a function
of a generic class is matched against that class's type variables rather than being read as having
none of its own. A specialisation nothing allocates is no longer emitted at all.

One shape remains unsupported on Lua: a method combining its own type parameters with those of the
generic class owning it, though it is no longer rejected outright — the arity check it tripped over
generic class owning it, though it is no longer rejected outright: the arity check it tripped over
counted the class's type arguments against a call that had only supplied the method's.

- Added new pseudo-natives for debugging memory leaks:
Expand Down
2 changes: 1 addition & 1 deletion de.peeeq.wurstscript/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ tasks.named('compileJava') { it.dependsOn('gen') }
* Cores decide how much parallelism is useful; memory decides how much is survivable. Each worker
* is a JVM with the heap set below, and the Gradle daemon holds its own on top, so a machine with
* many cores and little memory has to be counted the other way. Measured on eight cores: serial
* 13m11s, four forks 8m39s, eight forks 7m03s — more forks kept winning even though each test runs
* 13m11s, four forks 8m39s, eight forks 7m03s; more forks kept winning even though each test runs
* slower under the contention, so this errs towards cores where memory allows.
*/
int testForkCount() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ public boolean sendErrorReport(Throwable t, String sourcecode) {
wr.writeBytes(data);
wr.flush();
wr.close();
// String output = CharStreams.toString(new InputStreamReader(connection.getInputStream()));

// Get response data.
InputStream input = connection.getInputStream();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,12 +492,6 @@ public JassProg transformProgToJass() {

timeTaker.endPhase();

if (runArgs.isNoDebugMessages()) {
beginPhase(3, "remove debug messages");
DebugMessageRemover.removeDebugMessages(imProg2);
timeTaker.endPhase();
}

imTranslator2.assertProperties();

ImOptimizer optimizer = new ImOptimizer(timeTaker, imTranslator2);
Expand Down Expand Up @@ -1003,17 +997,10 @@ public LuaCompilationUnit transformProgToLua() {
LuaNativeLowering.lowerKeyedTables(imProg, getImTranslator());
timeTaker.endPhase();

if (runArgs.isNoDebugMessages()) {
beginPhase(3, "remove debug messages");
DebugMessageRemover.removeDebugMessages(imProg);
if (!runArgs.isNoDebugMessages() && runArgs.isIncludeStacktraces()) {
beginPhase(4, "add stack traces");
new StackTraceInjector2(imProg, imTranslator).transform(timeTaker);
timeTaker.endPhase();
} else {
// debug: add stacktraces
if (runArgs.isIncludeStacktraces()) {
beginPhase(4, "add stack traces");
new StackTraceInjector2(imProg, imTranslator).transform(timeTaker);
timeTaker.endPhase();
}
}
// Same position as on Jass: after stack traces, before lowering and inlining. Calls with a
// static argument count go to fixed-arity copies, so the emitted Lua packs no table and the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ public class WurstErrorWindow extends javax.swing.JFrame {
private javax.swing.JLabel currentStatus;
private javax.swing.JTextArea errorDetailsPanel;
private DefaultListModel<CompileError> errorListModel;
// private javax.swing.JList errorList;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JScrollPane jScrollPane2;
private javax.swing.JScrollPane jScrollPane3;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ public ILconstInt sqlite_prepare(ILconstInt connection, ILconstString query) {
* re-executes it. Called after a parameter is (re)bound: without this, binding new
* values after a step (but before a sqlite_reset) would be silently ignored and the
* new values never queried/written. Note this also closes any open result set, so a
* column must be read before the next bind — the contract is bind, step, read, then
* column must be read before the next bind; the contract is bind, step, read, then
* (re)bind and step again.
*/
private void markStatementForReexecution(int handle) {
Expand Down Expand Up @@ -345,7 +345,7 @@ public ILconstInt sqlite_column_count(ILconstInt statement) {

// Column indices are 0-based, mirroring the SQLite C API (sqlite3_column_*); the +1
// converts to JDBC's 1-based ResultSet indexing. Note this asymmetry with the 1-based
// bind indices above — both match the C API.
// bind indices above; both match the C API.
public ILconstInt sqlite_column_int(ILconstInt statement, ILconstInt index) {
ResultSet rs = sqliteResultSets.get(statement.getVal());
if (rs == null) throw new InterpreterException("No result set for statement handle: " + statement.getVal());
Expand Down Expand Up @@ -411,7 +411,7 @@ public void sqlite_clear_bindings(ILconstInt statement) {
try {
stmt.clearParameters();
// Clearing bindings mutates the parameters (all become NULL), so it must
// invalidate any prior execution just like sqlite_bind_* — otherwise a
// invalidate any prior execution just like sqlite_bind_*; otherwise a
// step after clearing on an already-executed statement would return false
// and silently skip re-running with the now-NULL parameters.
markStatementForReexecution(statement.getVal());
Expand Down Expand Up @@ -439,7 +439,7 @@ public void sqlite_close(ILconstInt connection) {
if (conn == null) throw new InterpreterException("Invalid SQLite connection handle: " + connection.getVal());
// Finalize every statement belonging to this connection, then always close the
// connection itself. A failure finalizing one statement must not abort the loop or
// skip conn.close() — otherwise the connection (already removed from the map above)
// skip conn.close(); otherwise the connection (already removed from the map above)
// would leak permanently, unreachable even by closeAllSqliteResources().
List<String> errors = new ArrayList<>();
for (int statement : sqliteStatementConnections.entrySet().stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ private static boolean tryExportWithWrapper(Appendable out, ObjectFileType fileT
}
out.append(formatWrapperValue(m, info)).append(")\n");
} else {
// No wrapper method for this field — emit as a commented raw call so the
// No wrapper method for this field; emit as a commented raw call so the
// user can see what needs to be handled and add it manually.
out.append("\t// TODO no wrapper: ");
appendRawMod(out, m, fileType);
Expand Down Expand Up @@ -716,7 +716,7 @@ private static boolean hasUnsupportedMappedWrapperField(List<ObjMod.Obj.Mod> mod
/**
* Returns the lookup key used to match a mod to a {@link StdlibObjectMappings.FieldMethodInfo}.
* Uses the mod's actual dataPtr when it is an {@link ObjMod.Obj.ExtendedMod}, regardless of
* whether the file type reports {@code usesLevels()} — some types (e.g. buffs) store their
* whether the file type reports {@code usesLevels()}; some types (e.g. buffs) store their
* data as extended mods even though the file type flag is false.
*/
static String fieldKey(ObjMod.Obj.Mod m, ObjectFileType fileType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,40 +190,6 @@ public void case_JassStmtCall(JassStmtCall jassStmtCall) {
});


// if (s instanceof ILif) {
// translateStatementIf(localVarMap, (ILif)s);
// } else if (s instanceof ILsetVar) {
// translateIlcopy( localVarMap, (ILsetVar)s);
// } else if (s instanceof IlsetConst) {
// translateIlsetConst( localVarMap, (IlsetConst)s);
// } else if (s instanceof ILsetBinary) {
// translateIlbinary( localVarMap, (ILsetBinary)s);
// } else if (s instanceof IlsetUnary) {
// translateIlunary(localVarMap, (IlsetUnary) s);
// } else if (s instanceof ILreturn) {
// translateReturn(localVarMap, (ILreturn) s);
// } else if (s instanceof ILloop) {
// translateLoop(localVarMap, (ILloop) s);
// } else if (s instanceof ILexitwhen) {
// translateExitwhen(localVarMap, (ILexitwhen) s);
// } else if (s instanceof ILfunctionCall) {
// translateFunctionCall(localVarMap, (ILfunctionCall) s);
// } else if (s instanceof IlbuildinFunctionCall) {
// translateBuildinFunction(localVarMap, (IlbuildinFunctionCall) s);
// } else if (s instanceof ILsetBinaryCR) {
// translateILsetBinaryCR(localVarMap, (ILsetBinaryCR) s);
// } else if (s instanceof ILsetBinaryCL) {
// translateILsetBinaryCL(localVarMap, (ILsetBinaryCL) s);
// } else if (s instanceof ILarraySetVar) {
// translateILarraySetVar(localVarMap, (ILarraySetVar) s);
// } else if (s instanceof ILsetVarArray) {
// translateILsetVarArray(localVarMap, (ILsetVarArray) s);
// } else if (s instanceof ILerror) {
// throw new InterpreterException("IL execution error: " + lookupVarValue(localVarMap, ((ILerror) s).msg));
// } else {
//
// throw new InterpreterException("not implemented " + s);
// }
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ public ILconst invoke(String funcname, ILconst[] args) throws NoSuchNativeExcept
Arrays.stream(args).map(Object::toString).collect(Collectors.joining(", ")) + ")";
msg += "\nwith types " + funcname + "(" +
Arrays.stream(args).map(o -> o.getClass().getSimpleName()).collect(Collectors.joining(", ")) + ")";
// if (candidate != null) {
// msg += "\nDid you mean " + funcname + "(" +
// Arrays.stream(candidate.getParameterTypes()).map(Class::getSimpleName).collect(Collectors.joining(", ")) + ")?";
// }
throw new NoSuchNativeException(msg);
}
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,11 @@ public void TriggerRegisterPlayerChatEvent(IlConstHandle trigger, IlConstHandle
exactMatchOnly) {
TriggerMock triggerMock = (TriggerMock) trigger.getObj();
// TODO
// triggerMock.registerEvent();
}

public void TriggerRegisterVariableEvent(IlConstHandle trigger, ILconstString varName, IlConstHandle opcode, ILconstReal limitval) {
TriggerMock triggerMock = (TriggerMock) trigger.getObj();
// TODO
// triggerMock.registerEvent();
}

public IlConstHandle Condition(ILconstFuncRef code) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ public void reportBuildDiagnostics(List<CompileError> diagnostics) {
private final File projectPath;
// dependency project folders discovered in _build/dependencies
private final Set<File> dependencies = Sets.newLinkedHashSet();
// private WurstGui gui = new WurstGuiLogger();
private final List<Consumer<PublishDiagnosticsParams>> onCompilationResultListeners = new ArrayList<>();
// compile errors for each file
private final Map<WFile, List<CompileError>> parseErrors = new LinkedHashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1071,7 +1071,6 @@ private CompletionItem makeFunctionCompletion(FuncLink f) {
completion.setSortText(ratingToString(rating));
completionTypes.put(completion, f.getReturnType());
// TODO use call signature instead for generics
// completion.set

if (!isBeforeParenthesis()) {
addParamSnippet(replacementString, f, completion);
Expand Down Expand Up @@ -1148,9 +1147,6 @@ private String getFunctionDescriptionShort(FunctionDefinition f) {

private CompletionItem makeConstructorCompletion(ClassDef c, ConstructorDef constr) {
String replacementString = c.getName();
// if (!isBeforeParenthesis()) {
// replacementString += "()";
// }

CompletionItem completion = new CompletionItem(c.getName());
completion.setKind(CompletionItemKind.Constructor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -895,7 +895,7 @@ protected File executeBuildMapPipeline(ModelManager modelManager, WurstGui gui,
gui.sendProgress("Finalizing map");
try (MpqEditor mpq = MpqEditorFactory.getEditor(Optional.of(outputMapFile))) {
if (mpq != null) {
// Strip internal Wurst cache files — they are dev-only metadata and should
// Strip internal Wurst cache files; they are dev-only metadata and should
// not be present in the distributed map.
if (mpq.hasFile("wurst_cache_manifest.txt")) mpq.deleteFile("wurst_cache_manifest.txt");
if (mpq.hasFile("wurst_object_cache.txt")) mpq.deleteFile("wurst_object_cache.txt");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ public void debug(String format, Object... args) {
public void info(String msg) {
logger.info(msg);
if (System.currentTimeMillis() - startTime > 100) { // Wait 250 mseconds
// System.out.println("Info: " + msg);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ public static FunctionSignature calculate(StmtCall fc) {
// ---- DEBUG: what did we pick, and what did we bind? ----
if (fc instanceof FunctionCall) {
WLogger.trace(() -> "[IMPLCONV] call=" + name(fc) + " args=" + at);
// WLogger.trace("[IMPLCONV] pickedSig=" + sig);
WLogger.trace(() -> "[IMPLCONV] mapping=" + sig.getMapping()
+ " unbound=" + sig.getMapping().printUnboundTypeVars());
if (fc instanceof ExprMemberMethodDot emmd) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,6 @@ public static WurstType calculate(TypeExprThis node) {
scope = node;
}
return AttrExprType.caclulateThistype(scope, false, "thistype");
// ClassOrModule n = node.attrNearestClassOrModule();
// if (n == null) {
// node.addError("'thistype' can only be used in classes and modules.");
// return WurstTypeUnknown.instance();
// }
// return n.match(new ClassOrModule.Matcher<WurstType>() {
//
// @Override
// public WurstType case_ClassDef(ClassDef classDef) {
// WurstTypeClass t = (WurstTypeClass) classDef.attrTyp();
// t.gett
// }
//
// @Override
// public WurstType case_ModuleDef(ModuleDef moduleDef) {
// return new WurstTypeModule(moduleDef, true);
// }
//
// });
}

public static WurstType calculate(NoTypeExpr optType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ public static WurstType calculate(WShortParameter p) {
// we have to look up the expected type of the lambda
ExprClosure parentClosure = (ExprClosure) p.getParent().getParent();
int paramIndex = parentClosure.getShortParameters().indexOf(p);
// WurstType expectedTyp = parentClosure.attrExpectedTypRaw();
// return WurstTypeInfer.instance();
WurstType expectedTyp = parentClosure.attrExpectedTyp();
return getParameterTypeFromClosureType(p, paramIndex, expectedTyp, true);
}
Expand Down
Loading
Loading