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
2 changes: 1 addition & 1 deletion Wurstpack/wurstscript/grill
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ if [[ ! -x "$RUNTIME" ]]; then
exit 3
fi

exec "$RUNTIME" -Dfile.encoding=UTF-8 -jar "$JAR" "$@"
exec "$RUNTIME" -Dfile.encoding=UTF-8 --enable-native-access=ALL-UNNAMED -jar "$JAR" "$@"
2 changes: 1 addition & 1 deletion Wurstpack/wurstscript/grill.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ if not exist "%JAVA%" (
goto :restore
)

"%JAVA%" -Dfile.encoding=UTF-8 -jar "%GRILL_JAR%" %*
"%JAVA%" -Dfile.encoding=UTF-8 --enable-native-access=ALL-UNNAMED -jar "%GRILL_JAR%" %*

:restore
rem Restore previous code page if we captured it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ public ILconstTuple createObjectDefinition(ILconstString fileType, ILconstInt ne
String objIdString = ObjectHelper.objectIdIntToString(newUnitId.getVal());
boolean isMeleeOverride = newUnitId.getVal() == deriveFrom.getVal();

if (!isMeleeOverride && dataStore.getObjs().containsKey(ObjId.valueOf(objIdString))) {
globalState.compilationError("Object definition with id " + objIdString + " already exists.");
if (!isMeleeOverride && !globalState.registerCreatedObjectDefinition(fileType.getVal(), objIdString)) {
globalState.compilationError("Object definition with id " + objIdString + " is defined more than once.");
}
ObjMod.Obj objDef = newDefFromFiletype(dataStore, deriveFrom.getVal(), newUnitId.getVal(), isMeleeOverride);
if (!isMeleeOverride) {
Expand All @@ -59,10 +59,15 @@ private ObjMod.Obj newDefFromFiletype(ObjMod<? extends ObjMod.Obj> dataStore, in
if (isMeleeOverride) {
ObjId id = ObjId.valueOf(ObjectHelper.objectIdIntToString(newId));
// same id => modify melee/original definition table
ObjMod.Obj existing = dataStore.getObjs().get(id);
if (existing != null) {
return existing;
}
return dataStore.addObj(id, null);
}
ObjId baseIdS = ObjId.valueOf(ObjectHelper.objectIdIntToString(base));
ObjId newIdS = ObjId.valueOf(ObjectHelper.objectIdIntToString(newId));
dataStore.removeObj(newIdS);
return dataStore.addObj(newIdS, baseIdS);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class ProgramStateIO extends ProgramState {
private @Nullable final MpqEditor mpqEditor;
private final Map<ObjectFileType, ObjMod<? extends ObjMod.Obj>> dataStoreMap = Maps.newLinkedHashMap();
private final Map<ObjectFileType, String> dataStoreHashes = Maps.newLinkedHashMap();
private final Map<String, Set<String>> createdObjectDefinitionIds = Maps.newLinkedHashMap();
private int id = 0;
private final Map<String, ObjMod.Obj> objDefinitions = Maps.newLinkedHashMap();
private PrintStream outStream = System.err;
Expand Down Expand Up @@ -368,6 +369,11 @@ ObjMod.Obj getObjectDefinition(String key) {
return objDefinitions.get(key);
}

boolean registerCreatedObjectDefinition(String fileExtension, String objId) {
Set<String> ids = createdObjectDefinitionIds.computeIfAbsent(fileExtension, k -> new LinkedHashSet<>());
return ids.add(objId);
}

/**
* Calculate hash of an object file's contents
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public Changes removeCompilationUnit(WFile resource) {
toRemove.add(compilationUnit);
}
}
GlobalCaches.clearLookupCacheFor(toRemove);
model2.removeAll(toRemove);
}

Expand Down Expand Up @@ -419,6 +420,7 @@ private void updateModel(CompilationUnit cu, WurstGui gui) {
Set<String> oldPackages = providedPackages(c);
Set<CompilationUnit> mustUpdate = calculateCUsToUpdate(Collections.singletonList(cu), oldPackages, model2);

GlobalCaches.clearLookupCacheFor(Collections.singletonList(c));
clearCompilationUnits(mustUpdate);
// replace old compilationunit with new one:
it.set(cu);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import de.peeeq.wurstscript.attributes.ErrorHandler;
import de.peeeq.wurstscript.attributes.names.DesugarArrayLength;
import de.peeeq.wurstscript.gui.WurstGui;
import de.peeeq.wurstscript.validation.GlobalCaches;
import de.peeeq.wurstscript.validation.TRVEHelper;
import de.peeeq.wurstscript.validation.WurstValidator;

Expand Down Expand Up @@ -34,6 +35,7 @@ public void checkProg(WurstModel root, Collection<CompilationUnit> toCheck) {
if (errorHandler.getErrorCount() > 0) return;

attachErrorHandler(root);
clearGlobalCaches(root, toCheck);

expandModules(root);

Expand All @@ -50,6 +52,14 @@ public void checkProg(WurstModel root, Collection<CompilationUnit> toCheck) {
validator.validate(toCheck);
}

private void clearGlobalCaches(WurstModel root, Collection<CompilationUnit> toCheck) {
if (toCheck == root || toCheck.size() >= root.size()) {
GlobalCaches.clearAll();
} else {
GlobalCaches.clearLookupCacheFor(toCheck);
}
}

private void attachErrorHandler(WurstModel root) {
for (CompilationUnit cu : root) {
cu.getCuInfo().setCuErrorHandler(errorHandler);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ public static ImmutableMultimap<String, TypeLink> exportedTypeNameLinks(WPackage
return result.build();
}


private static void addExportedTypeNameLinks(Builder<String, TypeLink> result, WPackage p, Set<WPackage> alreadyImported) {
if (alreadyImported.contains(p)) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,6 @@ public static ImmutableMultimap<String, DefLink> calculate(WPackage p) {
return result.build();
}


public static ImmutableMultimap<String, DefLink> calculate(WEntities wEntities) {
ImmutableMultimap.Builder<String, DefLink> result = ImmutableSetMultimap.builder();
for (WEntity e : wEntities) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@
import de.peeeq.wurstscript.intermediatelang.interpreter.LocalState;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

// Expose static fields only if you already have them there; otherwise, just clear via dedicated methods.
Expand Down Expand Up @@ -146,6 +150,28 @@ public static void clearAll() {
HasAnnotation.clearCaches();
}

/**
* Clears lookup entries whose query element belongs to one of the changed roots.
* This keeps editor validation from throwing away hot lookup data for unchanged files.
*/
public static void clearLookupCacheFor(Collection<? extends Element> roots) {
if (roots.isEmpty()) {
return;
}
Set<Element> affected = java.util.Collections.newSetFromMap(new IdentityHashMap<>());
ArrayDeque<Element> todo = new ArrayDeque<>(roots);
while (!todo.isEmpty()) {
Element e = todo.removeLast();
if (!affected.add(e)) {
continue;
}
for (int i = 0; i < e.size(); i++) {
todo.add(e.get(i));
}
}
lookupCache.keySet().removeIf(key -> affected.contains(key.element));
Comment thread
Frotty marked this conversation as resolved.
}

public enum LookupType {
FUNC, VAR, TYPE, PACKAGE, MEMBER_FUNC, MEMBER_VAR
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,10 @@ public WurstValidator(WurstModel root) {

public void validate(Collection<CompilationUnit> toCheck) {
try {
functionCount = countFunctions();
functionCount = countFunctions(toCheck);
visitedFunctions = 0;
heavyFunctions.clear();
heavyBlocks.clear();
GlobalCaches.clearAll();

lightValidation(toCheck);

Expand Down Expand Up @@ -123,11 +122,6 @@ private void lightValidation(Collection<CompilationUnit> toCheck) {
for (CompilationUnit cu : toCheck) {
walkTree(cu);
}

// Build CFG once for heavy phase (enables reachability/prev/next attrs)
for (CompilationUnit cu : toCheck) {
computeFlowAttributes(cu);
}
}

/** Visit only statements under root and run checkReachability where applicable. */
Expand Down Expand Up @@ -1174,16 +1168,19 @@ private void checkIntVal(ExprIntVal e) {
// check range? ...
}

private int countFunctions() {
private int countFunctions(Collection<CompilationUnit> toCheck) {
final int[] functionCount = new int[1];
prog.accept(new WurstModel.DefaultVisitor() {
Element.DefaultVisitor visitor = new Element.DefaultVisitor() {

@Override
public void visit(FuncDef f) {
super.visit(f);
functionCount[0]++;
}
});
};
for (CompilationUnit cu : toCheck) {
cu.accept(visitor);
}
return functionCount[0];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,34 @@
package tests.wurstscript.tests;

import de.peeeq.wurstio.intermediateLang.interpreter.CompiletimeNatives;
import de.peeeq.wurstio.intermediateLang.interpreter.ProgramStateIO;
import de.peeeq.wurstio.objectreader.ObjectHelper;
import de.peeeq.wurstscript.ast.Ast;
import de.peeeq.wurstscript.ast.Element;
import de.peeeq.wurstscript.gui.WurstGuiLogger;
import de.peeeq.wurstscript.intermediatelang.ILconstInt;
import de.peeeq.wurstscript.intermediatelang.ILconstString;
import de.peeeq.wurstscript.jassIm.ImProg;
import de.peeeq.wurstscript.jassIm.JassIm;
import net.moonlightflower.wc3libs.bin.ObjMod;
import net.moonlightflower.wc3libs.bin.app.objMod.W3A;
import net.moonlightflower.wc3libs.bin.app.objMod.W3U;
import net.moonlightflower.wc3libs.dataTypes.DataType;
import net.moonlightflower.wc3libs.dataTypes.app.War3String;
import net.moonlightflower.wc3libs.dataTypes.app.War3Real;
import net.moonlightflower.wc3libs.misc.MetaFieldId;
import net.moonlightflower.wc3libs.misc.ObjId;
import org.testng.annotations.Test;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;

public class CompiletimeNativesTest {
Expand Down Expand Up @@ -104,4 +115,97 @@ public void differentIdsObjectDefinitionUsesCustomTable() throws Exception {
assertEquals(w3u.getOrigObjs().size(), 0);
assertEquals(w3u.getCustomObjs().size(), 1);
}

@Test
public void differentIdsObjectDefinitionOverwritesExistingMapObject() throws Exception {
CompiletimeNatives natives = new CompiletimeNatives(null, null, false);
W3U w3u = new W3U();
int hfoo = ObjectHelper.objectIdStringToInt("hfoo");
int hf01 = ObjectHelper.objectIdStringToInt("hf01");
W3U.Obj existing = w3u.addObj(ObjId.valueOf("hf01"), ObjId.valueOf("hpea"));
existing.addMod(new ObjMod.Obj.Mod(MetaFieldId.valueOf("unam"), ObjMod.ValType.STRING, War3String.valueOf("old map object")));

Method newDefFromFiletype = CompiletimeNatives.class.getDeclaredMethod(
"newDefFromFiletype",
ObjMod.class,
int.class,
int.class,
boolean.class
);
newDefFromFiletype.setAccessible(true);

ObjMod.Obj obj = (ObjMod.Obj) newDefFromFiletype.invoke(natives, w3u, hfoo, hf01, false);

assertEquals(w3u.getCustomObjs().size(), 1, "Existing map object should be replaced instead of duplicated");
assertEquals(obj.getId().getVal(), "hf01");
assertEquals(obj.getBaseId().getVal(), "hfoo");
assertEquals(obj.getMods().size(), 0, "Overwritten map object mods should not leak into the Wurst-created object");
}

@Test
public void duplicateCodeObjectDefinitionsReportError() {
WurstGuiLogger gui = new WurstGuiLogger();
ProgramStateIO state = new ProgramStateIO(Optional.empty(), null, gui, emptyProg(), true);
CompiletimeNatives natives = new CompiletimeNatives(state, null, false);
int hfoo = ObjectHelper.objectIdStringToInt("hfoo");
int hf01 = ObjectHelper.objectIdStringToInt("hf01");

natives.createObjectDefinition(new ILconstString("w3u"), new ILconstInt(hf01), new ILconstInt(hfoo));
natives.createObjectDefinition(new ILconstString("w3u"), new ILconstInt(hf01), new ILconstInt(hfoo));

assertEquals(gui.getErrorCount(), 1);
assertTrue(gui.getErrors().contains("Object definition with id hf01 is defined more than once."));
}

@Test
public void createObjectDefinitionDoesNotReportExistingMapObjectAsError() throws Exception {
WurstGuiLogger gui = new WurstGuiLogger();
ProgramStateIO state = new ProgramStateIO(Optional.empty(), null, gui, emptyProg(), true);
CompiletimeNatives natives = new CompiletimeNatives(state, null, false);
int hfoo = ObjectHelper.objectIdStringToInt("hfoo");
int hf01 = ObjectHelper.objectIdStringToInt("hf01");

Method getDataStore = ProgramStateIO.class.getDeclaredMethod("getDataStore", String.class);
getDataStore.setAccessible(true);
W3U w3u = (W3U) getDataStore.invoke(state, "w3u");
W3U.Obj existing = w3u.addObj(ObjId.valueOf("hf01"), ObjId.valueOf("hpea"));
existing.addMod(new ObjMod.Obj.Mod(MetaFieldId.valueOf("unam"), ObjMod.ValType.STRING, War3String.valueOf("old map object")));

natives.createObjectDefinition(new ILconstString("w3u"), new ILconstInt(hf01), new ILconstInt(hfoo));

assertEquals(gui.getErrorCount(), 0);
assertEquals(w3u.getCustomObjs().size(), 1);
ObjMod.Obj obj = w3u.getCustomObjs().get(0);
assertEquals(obj.getId().getVal(), "hf01");
assertEquals(obj.getBaseId().getVal(), "hfoo");
assertFalse(obj.getMods().stream().anyMatch(m -> m.getId().getVal().equals("unam")));
}

@Test
public void sameIdObjectDefinitionsMergeModsWithoutDuplicateError() throws Exception {
WurstGuiLogger gui = new WurstGuiLogger();
ProgramStateIO state = new ProgramStateIO(Optional.empty(), null, gui, emptyProg(), true);
CompiletimeNatives natives = new CompiletimeNatives(state, null, false);
int hfoo = ObjectHelper.objectIdStringToInt("hfoo");

var first = natives.createObjectDefinition(new ILconstString("w3u"), new ILconstInt(hfoo), new ILconstInt(hfoo));
natives.ObjectDefinition_setString(first, new ILconstString("unam"), new ILconstString("first"));
var second = natives.createObjectDefinition(new ILconstString("w3u"), new ILconstInt(hfoo), new ILconstInt(hfoo));
natives.ObjectDefinition_setString(second, new ILconstString("utip"), new ILconstString("second"));

Method getDataStore = ProgramStateIO.class.getDeclaredMethod("getDataStore", String.class);
getDataStore.setAccessible(true);
W3U w3u = (W3U) getDataStore.invoke(state, "w3u");

assertEquals(gui.getErrorCount(), 0);
assertEquals(w3u.getOrigObjs().size(), 1);
ObjMod.Obj obj = w3u.getOrigObjs().get(0);
assertTrue(obj.getMods().stream().anyMatch(m -> m.getId().getVal().equals("unam")));
assertTrue(obj.getMods().stream().anyMatch(m -> m.getId().getVal().equals("utip")));
}

private ImProg emptyProg() {
Element trace = Ast.NoExpr();
return JassIm.ImProg(trace, JassIm.ImVars(), JassIm.ImFunctions(), JassIm.ImMethods(), JassIm.ImClasses(), JassIm.ImTypeClassFuncs(), new HashMap<>());
}
}
Loading
Loading