From b82dc396391a3d3456a2752f8be3b97ec27d2471 Mon Sep 17 00:00:00 2001 From: Frotty Date: Wed, 22 Nov 2023 16:41:40 +0100 Subject: [PATCH] More performance improvements & fixes (#1081) * only extract map script if map changed * some more parallelization * update wc3libs to fix loading screen issues * only apply loading screen background when not null --- de.peeeq.wurstscript/build.gradle | 2 +- .../main/java/de/peeeq/wurstio/TimeTaker.java | 19 +++ .../peeeq/wurstio/WurstCompilerJassImpl.java | 46 ++++-- .../ReflectionBasedNativeProvider.java | 77 +++++----- .../ReflectionNativeProvider.java | 6 +- .../languageserver/ProjectConfigBuilder.java | 24 ++-- .../languageserver/requests/MapRequest.java | 72 ++++++---- .../languageserver/requests/RunMap.java | 1 + .../attributes/names/NameResolution.java | 3 +- .../imtranslation/CyclicFunctionRemover.java | 136 ++++++++++-------- .../translation/imtranslation/Flatten.java | 32 ++--- .../imtranslation/ImTranslator.java | 6 +- .../imtranslation/MultiArrayEliminator.java | 2 +- .../validation/WurstValidator.java | 30 ++-- 14 files changed, 261 insertions(+), 195 deletions(-) diff --git a/de.peeeq.wurstscript/build.gradle b/de.peeeq.wurstscript/build.gradle index 05a1c2003..7ed0635f3 100644 --- a/de.peeeq.wurstscript/build.gradle +++ b/de.peeeq.wurstscript/build.gradle @@ -108,7 +108,7 @@ dependencies { implementation group: 'com.github.inwc3', name: 'jmpq3', version: '264c54cfc8' // Water's wc3 libs - implementation 'com.github.inwc3:wc3libs:ad25440d15' + implementation 'com.github.inwc3:wc3libs:01fb9e23bf' // The setup tool for wurst.build handling implementation 'com.github.wurstscript:wurstsetup:475cc7fae8' diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/TimeTaker.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/TimeTaker.java index 95ee20038..df5de9986 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/TimeTaker.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/TimeTaker.java @@ -2,6 +2,8 @@ import de.peeeq.wurstscript.utils.Utils; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Supplier; @@ -53,6 +55,8 @@ class Recording implements TimeTaker { private long currentPhaseStart; private Map accumulatedTimes = new LinkedHashMap<>(); + private Long startTime = 0L; + public T measure(String name, Supplier f) { name = withNesting(name); nesting++; @@ -76,6 +80,9 @@ private void reportDuration(String name, long duration) { @Override public void beginPhase(String description) { + if (accumulatedTimes.isEmpty()) { + this.startTime = System.currentTimeMillis(); + } if (currentPhaseDescription != null) { endPhase(); } @@ -101,9 +108,21 @@ public void endPhase() { public void printReport() { System.out.println("#############################"); System.out.println("Run times:"); + for (Map.Entry e : accumulatedTimes.entrySet()) { System.out.println(e.getKey() + ": " + e.getValue() + "ms"); } + + System.out.println("Total runtime: " + (System.currentTimeMillis() - startTime) + "ms"); + System.out.println("GC time: " + getGarbageCollectionTime() + "ms"); + } + + private static long getGarbageCollectionTime() { + long collectionTime = 0; + for (GarbageCollectorMXBean garbageCollectorMXBean : ManagementFactory.getGarbageCollectorMXBeans()) { + collectionTime += garbageCollectorMXBean.getCollectionTime(); + } + return collectionTime; } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java index db26ef4af..7d30873dc 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java @@ -421,25 +421,31 @@ public JassProg transformProgToJass() { beginPhase(2, "Eliminate generics"); new EliminateGenerics(imTranslator2, imProg2).transform(); printDebugImProg("./test-output/im " + stage++ + "_genericsEliminated.im"); - + timeTaker.endPhase(); // eliminate classes beginPhase(2, "translate classes"); new EliminateClasses(imTranslator2, imProg2, !runArgs.isUncheckedDispatch()).eliminateClasses(); imTranslator2.assertProperties(); printDebugImProg("./test-output/im " + stage++ + "_classesEliminated.im"); + timeTaker.endPhase(); new VarargEliminator(imProg2).run(); printDebugImProg("./test-output/im " + stage++ + "_varargEliminated.im"); imTranslator2.assertProperties(); + + timeTaker.endPhase(); + if (runArgs.isNoDebugMessages()) { beginPhase(3, "remove debug messages"); DebugMessageRemover.removeDebugMessages(imProg2); + timeTaker.endPhase(); } else { // debug: add stacktraces if (runArgs.isIncludeStacktraces()) { beginPhase(4, "add stack traces"); new StackTraceInjector2(imProg2, imTranslator2).transform(timeTaker); + timeTaker.endPhase(); } } imTranslator2.assertProperties(); @@ -453,49 +459,60 @@ public JassProg transformProgToJass() { imTranslator2.assertProperties(); printDebugImProg("./test-output/im " + stage++ + "_afterinline.im"); + timeTaker.endPhase(); } // eliminate tuples beginPhase(6, "eliminate tuples"); - getImProg().flatten(imTranslator2); - EliminateTuples.eliminateTuplesProg(getImProg(), imTranslator2); + timeTaker.measure("flatten", () -> getImProg().flatten(imTranslator2)); + timeTaker.measure("kill tuples", () -> EliminateTuples.eliminateTuplesProg(getImProg(), imTranslator2)); getImTranslator().assertProperties(AssertProperty.NOTUPLES); printDebugImProg("./test-output/im " + stage++ + "_withouttuples.im"); + timeTaker.endPhase(); + + beginPhase(7, "eliminate multi arrays"); new MultiArrayEliminator(imProg2, imTranslator2, runArgs.isIncludeStacktraces() && !runArgs.isNoDebugMessages()).run(); printDebugImProg("./test-output/im " + stage++ + "_withoutmultiarrays.im"); imTranslator2.assertProperties(); + timeTaker.endPhase(); - beginPhase(7, "remove func refs"); + beginPhase(8, "remove func refs"); new FuncRefRemover(imProg2, imTranslator2).run(); + timeTaker.endPhase(); // remove cycles: - beginPhase(8, "remove cyclic functions"); + beginPhase(9, "remove cyclic functions"); new CyclicFunctionRemover(imTranslator2, imProg2, timeTaker).work(); printDebugImProg("./test-output/im " + stage++ + "_nocyc.im"); + timeTaker.endPhase(); // flatten - beginPhase(9, "flatten"); + beginPhase(10, "flatten"); getImProg().flatten(imTranslator2); getImTranslator().assertProperties(AssertProperty.NOTUPLES, AssertProperty.FLAT); printDebugImProg("./test-output/im " + stage++ + "_flat.im"); + timeTaker.endPhase(); if (runArgs.isLocalOptimizations()) { - beginPhase(10, "local optimizations"); + beginPhase(11, "local optimizations"); optimizer.localOptimizations(); + timeTaker.endPhase(); } printDebugImProg("./test-output/im " + stage++ + "_afterlocalopts.im"); if (runArgs.isNullsetting()) { - beginPhase(11, "null setting"); + beginPhase(12, "null setting"); optimizer.doNullsetting(); printDebugImProg("./test-output/im " + stage++ + "_afternullsetting.im"); + timeTaker.endPhase(); } + beginPhase(13, "flatten"); optimizer.removeGarbage(); imProg.flatten(imTranslator); @@ -504,12 +521,13 @@ public JassProg transformProgToJass() { imProg.flatten(imTranslator); printDebugImProg("./test-output/im " + stage++ + "_afterremoveGarbage1.im"); + timeTaker.endPhase(); if (runArgs.isHotStartmap() || runArgs.isHotReload()) { addJassHotCodeReloadCode(); } if (runArgs.isOptimize()) { - beginPhase(12, "froptimize"); + beginPhase(13, "froptimize"); optimizer.optimize(); optimizer.removeGarbage(); @@ -521,7 +539,7 @@ public JassProg transformProgToJass() { // translate flattened intermediate lang to jass: - beginPhase(13, "translate to jass"); + beginPhase(14, "translate to jass"); getImTranslator().calculateCallRelationsAndUsedVariables(); ImToJassTranslator translator = new ImToJassTranslator(getImProg(), getImTranslator().getCalledFunctions(), getImTranslator().getMainFunc(), getImTranslator().getConfFunc()); @@ -841,11 +859,13 @@ public LuaCompilationUnit transformProgToLua() { if (runArgs.isNoDebugMessages()) { beginPhase(3, "remove debug messages"); DebugMessageRemover.removeDebugMessages(imProg); + timeTaker.endPhase(); } else { // debug: add stacktraces if (runArgs.isIncludeStacktraces()) { beginPhase(4, "add stack traces"); new StackTraceInjector2(imProg, imTranslator).transform(timeTaker); + timeTaker.endPhase(); } } ImTranslator imTranslator2 = getImTranslator(); @@ -858,6 +878,7 @@ public LuaCompilationUnit transformProgToLua() { imTranslator2.assertProperties(); printDebugImProg("./test-output/lua/im " + stage++ + "_afterinline.im"); + timeTaker.endPhase(); } // eliminate local types @@ -867,11 +888,12 @@ public LuaCompilationUnit transformProgToLua() { optimizer.removeGarbage(); imProg.flatten(imTranslator); - + timeTaker.endPhase(); stage = 10; if (runArgs.isLocalOptimizations()) { beginPhase(10, "local optimizations"); optimizer.localOptimizations(); + timeTaker.endPhase(); } printDebugImProg("./test-output/lua/im " + stage++ + "_afterlocalopts.im"); @@ -893,11 +915,13 @@ public LuaCompilationUnit transformProgToLua() { optimizer.removeGarbage(); imProg.flatten(imTranslator); printDebugImProg("./test-output/lua/im " + stage++ + "_afteroptimize.im"); + timeTaker.endPhase(); } beginPhase(13, "translate to lua"); LuaTranslator luaTranslator = new LuaTranslator(imProg, imTranslator); LuaCompilationUnit luaCode = luaTranslator.translate(); ImAttrType.setWurstClassType(TypesHelper.imInt()); + timeTaker.endPhase(); return luaCode; } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/ReflectionBasedNativeProvider.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/ReflectionBasedNativeProvider.java index 4bcc4e397..de12c9368 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/ReflectionBasedNativeProvider.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/ReflectionBasedNativeProvider.java @@ -4,61 +4,60 @@ import de.peeeq.wurstscript.intermediatelang.ILconst; import de.peeeq.wurstscript.intermediatelang.interpreter.NativesProvider; import de.peeeq.wurstscript.intermediatelang.interpreter.NoSuchNativeException; +import de.peeeq.wurstscript.utils.Pair; import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.HashMap; import java.util.stream.Collectors; public abstract class ReflectionBasedNativeProvider implements NativesProvider { protected PrintStream outStream = System.err; - @Override - public ILconst invoke(String funcname, ILconst[] args) throws NoSuchNativeException { - Method candidate = null; - nextMethod: + private final HashMap, Method> methodMap = new HashMap<>(); + + public ReflectionBasedNativeProvider() { for (Method method : this.getClass().getMethods()) { - if (method.getName().equals(funcname)) { - // this is a candidate as it has the correct name - candidate = method; - Object r; - try { - if (args.length != method.getParameterTypes().length) { - continue; - } - int i = 0; - for (Class paramType : method.getParameterTypes()) { - if (!paramType.isAssignableFrom(args[i].getClass())) { - continue nextMethod; - } - i++; - } - r = method.invoke(this, (Object[]) args); - } catch (IllegalAccessException | IllegalArgumentException e) { - WLogger.severe(e); - throw new Error(e); - } catch (InvocationTargetException e) { - if (e.getCause() instanceof InterpreterException) { - throw (InterpreterException) e.getCause(); - } if (e.getCause() instanceof Error) { - throw (Error) e.getCause(); - } - throw new Error(e.getCause()); - } - return (ILconst) r; + Pair keyPair = Pair.create(method.getName(), method.getParameterTypes().length); + if (methodMap.containsKey(keyPair)) { + throw new RuntimeException("native entry already exists"); } + methodMap.put(keyPair, method); } - String msg = "Calling method " + funcname + "(" + + } + + @Override + public ILconst invoke(String funcname, ILconst[] args) throws NoSuchNativeException { + Method method = methodMap.get(Pair.create(funcname, args.length)); + if (method == null) { + String msg = "Calling method " + funcname + "(" + 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(", ")) + ")?"; + 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); } - throw new NoSuchNativeException(msg); + try { + return (ILconst) method.invoke(this, (Object[]) args); + } catch (IllegalAccessException | IllegalArgumentException e) { + WLogger.severe(e); + throw new Error(e); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof InterpreterException) { + throw (InterpreterException) e.getCause(); + } + if (e.getCause() instanceof Error) { + throw (Error) e.getCause(); + } + throw new Error(e.getCause()); + } + } @Override diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/ReflectionNativeProvider.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/ReflectionNativeProvider.java index e93730bcb..754381cd1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/ReflectionNativeProvider.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/ReflectionNativeProvider.java @@ -15,7 +15,7 @@ import java.util.HashMap; public class ReflectionNativeProvider implements NativesProvider { - private HashMap methodMap = new HashMap<>(); + private final HashMap methodMap = new HashMap<>(); public ReflectionNativeProvider(AbstractInterpreter interpreter) { addProvider(new GamecacheProvider(interpreter)); @@ -70,10 +70,6 @@ private void addProvider(Provider provider) { @Override public ILconst invoke(String funcname, ILconst[] args) throws NoSuchNativeException { - String msg = "Calling method " + funcname + "(" + - Utils.printSep(", ", args) + ")"; - WLogger.trace(msg); - NativeJassFunction candidate = methodMap.get(funcname); if (candidate == null) { throw new NoSuchNativeException(""); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/ProjectConfigBuilder.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/ProjectConfigBuilder.java index 245dbf63d..4dcf20047 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/ProjectConfigBuilder.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/ProjectConfigBuilder.java @@ -56,7 +56,7 @@ public static MapRequest.CompilationResult apply(WurstProjectConfigData projectC } else { GameVersion version = GameVersion.VERSION_1_32; System.out.println( - "Failed to determine installed game version. Falling back to " + version.toString() + "Failed to determine installed game version. Falling back to " + version ); w3I.injectConfigsInJassScript(inputStream, sw, version); } @@ -66,7 +66,7 @@ public static MapRequest.CompilationResult apply(WurstProjectConfigData projectC result.w3i = new File(buildDir, "war3map.w3i"); if (runArgs.isLua()) { w3I.setScriptLang(W3I.ScriptLang.LUA); - w3I.setFileVersion(W3I.EncodingFormat.W3I_0x1C.getVersion()); + w3I.setFileVersion(W3I.EncodingFormat.W3I_0x1F.getVersion()); } w3I.write(result.w3i); Files.write(scriptBytes, result.script); @@ -106,10 +106,10 @@ private static W3I prepareW3I(WurstProjectConfigData projectConfig, MpqEditor mp } applyScenarioData(w3I, buildMapData); - if (buildMapData.getPlayers().size() > 0) { + if (!buildMapData.getPlayers().isEmpty()) { applyPlayers(projectConfig, w3I); } - if (buildMapData.getForces().size() > 0) { + if (!buildMapData.getForces().isEmpty()) { applyForces(projectConfig, w3I); } applyOptionFlags(projectConfig, w3I); @@ -139,16 +139,16 @@ private static void applyScenarioData(W3I w3I, WurstProjectBuildMapData buildMap } } - private static void applyLoadingScreen(W3I w3I, WurstProjectBuildLoadingScreenData loadingScreen) { - if (StringUtils.isNotBlank(loadingScreen.getModel())) { - w3I.getLoadingScreen().setBackground(new LoadingScreenBackground.CustomBackground(new File(loadingScreen.getModel()))); - } else { - w3I.getLoadingScreen().setBackground(LoadingScreenBackground.PresetBackground.findByName(loadingScreen.getBackground())); + private static void applyLoadingScreen(W3I w3I, WurstProjectBuildLoadingScreenData loadingScreenData) { + if (StringUtils.isNotBlank(loadingScreenData.getModel())) { + w3I.getLoadingScreen().setBackground(new LoadingScreenBackground.CustomBackground(new File(loadingScreenData.getModel()))); + } else if (StringUtils.isNotBlank(loadingScreenData.getBackground())) { + w3I.getLoadingScreen().setBackground(LoadingScreenBackground.PresetBackground.findByName(loadingScreenData.getBackground())); } - w3I.getLoadingScreen().setTitle(loadingScreen.getTitle()); - w3I.getLoadingScreen().setSubtitle(loadingScreen.getSubTitle()); - w3I.getLoadingScreen().setText(loadingScreen.getText()); + w3I.getLoadingScreen().setTitle(loadingScreenData.getTitle()); + w3I.getLoadingScreen().setSubtitle(loadingScreenData.getSubTitle()); + w3I.getLoadingScreen().setText(loadingScreenData.getText()); } private static void applyForces(WurstProjectConfigData projectConfig, W3I w3I) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/MapRequest.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/MapRequest.java index 09bf28891..e7508bb44 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/MapRequest.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/MapRequest.java @@ -287,8 +287,6 @@ protected File compileScript(WurstGui gui, ModelManager modelManager, List testMap, WurstProjectConfigData projectConfigData, File buildDir, boolean isProd) throws Exception { if (testMap.isPresent() && testMap.get().exists()) { boolean deleteOk = testMap.get().delete(); @@ -338,7 +337,9 @@ protected CompilationResult compileScript(ModelManager modelManager, WurstGui gu throw new RequestFailedException(MessageType.Error, "Could not find war3map.j file"); } } else { + timeTaker.beginPhase("load map script"); File scriptFile = loadMapScript(testMap, modelManager, gui); + timeTaker.endPhase(); result = applyProjectConfig(gui, testMap, buildDir, projectConfigData, scriptFile); } @@ -360,6 +361,8 @@ protected CompilationResult compileScript(ModelManager modelManager, WurstGui gu return result; } + private static Long lastMapModified = 0L; + private File loadMapScript(Optional mapCopy, ModelManager modelManager, WurstGui gui) throws Exception { File scriptFile = new File(new File(workspaceRoot.getFile(), "wurst"), "war3map.j"); // If runargs are no extract, either use existing or throw error @@ -373,41 +376,48 @@ private File loadMapScript(Optional mapCopy, ModelManager modelManager, Wu "RunArg noExtractMapScript is set but no mapscript is provided inside the wurst folder"); } } - WLogger.info("extracting mapscript"); - byte[] extractedScript = null; - try (@Nullable MpqEditor mpqEditor = MpqEditorFactory.getEditor(mapCopy, true)) { - if (mpqEditor.hasFile("war3map.j")) { - extractedScript = mpqEditor.extractFile("war3map.j"); - } - } - if (extractedScript == null) { - if (scriptFile.exists()) { - String msg = "No war3map.j in map file, using old extracted file"; - WLogger.info(msg); - } else { - CompileError err = new CompileError(new WPos(mapCopy.toString(), new LineOffsets(), 0, 0), - "No war3map.j found in map file."); - gui.showInfoMessage(err.getMessage()); - WLogger.severe(err); + long mapLastModified = mapCopy.get().lastModified(); + if (mapLastModified > lastMapModified) { + lastMapModified = mapLastModified; + WLogger.info("extracting mapscript"); + byte[] extractedScript = null; + try (@Nullable MpqEditor mpqEditor = MpqEditorFactory.getEditor(mapCopy, true)) { + if (mpqEditor.hasFile("war3map.j")) { + extractedScript = mpqEditor.extractFile("war3map.j"); + } } - } else if (new String(extractedScript, StandardCharsets.UTF_8).startsWith(JassPrinter.WURST_COMMENT_RAW)) { - WLogger.info("map has already been compiled with wurst"); - // file generated by wurst, do not use - if (scriptFile.exists()) { - String msg = "Cannot use war3map.j from map file, because it already was compiled with wurst. " + "Using war3map.j from Wurst directory instead."; - WLogger.info(msg); + if (extractedScript == null) { + if (scriptFile.exists()) { + String msg = "No war3map.j in map file, using old extracted file"; + WLogger.info(msg); + } else { + CompileError err = new CompileError(new WPos(mapCopy.toString(), new LineOffsets(), 0, 0), + "No war3map.j found in map file."); + gui.showInfoMessage(err.getMessage()); + WLogger.severe(err); + } + } else if (new String(extractedScript, StandardCharsets.UTF_8).startsWith(JassPrinter.WURST_COMMENT_RAW)) { + WLogger.info("map has already been compiled with wurst"); + // file generated by wurst, do not use + if (scriptFile.exists()) { + String msg = "Cannot use war3map.j from map file, because it already was compiled with wurst. " + "Using war3map.j from Wurst directory instead."; + WLogger.info(msg); + } else { + CompileError err = new CompileError(new WPos(mapCopy.toString(), new LineOffsets(), 0, 0), + "Cannot use war3map.j from map file, because it already was compiled with wurst. " + "Please add war3map.j to the wurst directory."); + gui.showInfoMessage(err.getMessage()); + WLogger.severe(err); + } } else { - CompileError err = new CompileError(new WPos(mapCopy.toString(), new LineOffsets(), 0, 0), - "Cannot use war3map.j from map file, because it already was compiled with wurst. " + "Please add war3map.j to the wurst directory."); - gui.showInfoMessage(err.getMessage()); - WLogger.severe(err); + WLogger.info("new map, use extracted"); + // write mapfile from map to workspace + Files.write(extractedScript, scriptFile); } } else { - WLogger.info("new map, use extracted"); - // write mapfile from map to workspace - Files.write(extractedScript, scriptFile); + System.out.println("Map not modified, not extracting script"); } + return scriptFile; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/RunMap.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/RunMap.java index 12259a201..3380ded67 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/RunMap.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/RunMap.java @@ -146,6 +146,7 @@ public Object execute(ModelManager modelManager) throws IOException { gui.sendProgress("running " + cmd); Runtime.getRuntime().exec(cmd.toArray(new String[0])); + timeTaker.endPhase(); timeTaker.printReport(); } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameResolution.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameResolution.java index d0d3429b9..14d650572 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameResolution.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameResolution.java @@ -223,8 +223,7 @@ public static DefLink matchDefLinkReceiver(DefLink n, WurstType receiverType, El if (showErrors) { if (n.getVisibility() == Visibility.PRIVATE_OTHER) { node.addError(Utils.printElement(n.getDef()) + " is private and cannot be used here."); - } - if (n.getVisibility() == Visibility.PROTECTED_OTHER) { + } else if (n.getVisibility() == Visibility.PROTECTED_OTHER) { node.addError(Utils.printElement(n.getDef()) + " is protected and cannot be used here."); } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/CyclicFunctionRemover.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/CyclicFunctionRemover.java index 5c0bfff8c..ac8fc1f4f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/CyclicFunctionRemover.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/CyclicFunctionRemover.java @@ -90,7 +90,11 @@ private void removeCycle(List funcs, Set funcSet) { stmts = elseBlock; } - replaceCalls(funcs, funcSet, newFunc, oldToNewVar, prog); + Map funcToIndex = new HashMap<>(); + for (int i = 0; i < funcs.size(); i++) { + funcToIndex.put(funcs.get(i), i); + } + replaceCalls(funcSet, funcToIndex, newFunc, oldToNewVar, prog); for (ImFunction e : Lists.newArrayList(tr.getCalledFunctions().keys())) { Collection called = tr.getCalledFunctions().get(e); @@ -120,78 +124,94 @@ private void replaceVars(Element e, Map oldToNewVar) { } - private void replaceCalls(List funcs, Set funcSet, ImFunction newFunc, Map oldToNewVar, Element e) { - // process children - for (int i = 0; i < e.size(); i++) { - replaceCalls(funcs, funcSet, newFunc, oldToNewVar, e.get(i)); - } + private void replaceCalls(Set funcSet, Map funcToIndex, ImFunction newFunc, Map oldToNewVar, Element e) { + List relevant = new ArrayList<>(); + e.accept(new Element.DefaultVisitor() { + @Override + public void visit(ImFuncRef imFuncRef) { + super.visit(imFuncRef); + relevant.add(imFuncRef); + } + @Override + public void visit(ImFunctionCall imFunctionCall) { + super.visit(imFunctionCall); + relevant.add(imFunctionCall); + } + }); + relevant.parallelStream().forEach(relevantElem -> { + if (relevantElem instanceof ImFuncRef) { + replaceImFuncRef(funcSet, funcToIndex, newFunc, oldToNewVar, (ImFuncRef) relevantElem); + } else if (relevantElem instanceof ImFunctionCall) { + replaceImFunctionCall(funcSet, funcToIndex, newFunc, oldToNewVar, (ImFunctionCall) relevantElem); + } + }); + } - if (e instanceof ImFuncRef) { - ImFuncRef fr = (ImFuncRef) e; - ImFunction f = fr.getFunc(); - if (funcSet.contains(f)) { + private void replaceImFuncRef(Set funcSet, Map funcToIndex, ImFunction newFunc, Map oldToNewVar, ImFuncRef e) { + ImFuncRef fr = e; + ImFunction f = fr.getFunc(); + if (funcSet.contains(f)) { - ImFunction proxyFunc = JassIm.ImFunction(f.attrTrace(), f.getName() + "_proxy", JassIm.ImTypeVars(), f.getParameters().copy(), (ImType) f.getReturnType().copy(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); - prog.getFunctions().add(proxyFunc); + ImFunction proxyFunc = JassIm.ImFunction(f.attrTrace(), f.getName() + "_proxy", JassIm.ImTypeVars(), f.getParameters().copy(), (ImType) f.getReturnType().copy(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); + prog.getFunctions().add(proxyFunc); - ImExprs arguments = JassIm.ImExprs(); - for (ImVar p : proxyFunc.getParameters()) { - arguments.add(JassIm.ImVarAccess(p)); - } + ImExprs arguments = JassIm.ImExprs(); + for (ImVar p : proxyFunc.getParameters()) { + arguments.add(JassIm.ImVarAccess(p)); + } - ImFunctionCall call = JassIm.ImFunctionCall(fr.attrTrace(), f, JassIm.ImTypeArguments(), arguments, true, CallType.NORMAL); + ImFunctionCall call = JassIm.ImFunctionCall(fr.attrTrace(), f, JassIm.ImTypeArguments(), arguments, true, CallType.NORMAL); - if (f.getReturnType() instanceof ImVoid) { - proxyFunc.getBody().add(call); - } else { - proxyFunc.getBody().add(JassIm.ImReturn(proxyFunc.getTrace(), call)); - } - // rewrite the proxy call: - replaceCalls(funcs, funcSet, newFunc, oldToNewVar, call); - // change the funcref to use the proxy - fr.setFunc(proxyFunc); + if (f.getReturnType() instanceof ImVoid) { + proxyFunc.getBody().add(call); + } else { + proxyFunc.getBody().add(JassIm.ImReturn(proxyFunc.getTrace(), call)); } - } else if (e instanceof ImFunctionCall) { - ImFunctionCall fc = (ImFunctionCall) e; - ImFunction oldFunc = fc.getFunc(); - if (funcSet.contains(oldFunc)) { - - ImExprs arguments = JassIm.ImExprs(); - - // first argument is the choice index - arguments.add(JassIm.ImIntVal(funcs.indexOf(oldFunc))); - - // now for the actual arguments - List oldArgs = fc.getArguments().removeAll(); - int pos = 0; - for (int i = 1; i < newFunc.getParameters().size(); i++) { - ImVar p = newFunc.getParameters().get(i); - if (pos < oldArgs.size() && oldToNewVar.get(oldFunc.getParameters().get(pos)) == p) { - arguments.add(oldArgs.get(pos)); - pos++; - } else { - // use default value - arguments.add(tr.getDefaultValueForJassType(p.getType())); - } - } + // rewrite the proxy call: + replaceCalls(funcSet, funcToIndex, newFunc, oldToNewVar, call); + // change the funcref to use the proxy + fr.setFunc(proxyFunc); + } + } + + private void replaceImFunctionCall(Set funcSet, Map funcToIndex, ImFunction newFunc, Map oldToNewVar, ImFunctionCall e) { + ImFunctionCall fc = e; + ImFunction oldFunc = fc.getFunc(); + if (funcSet.contains(oldFunc)) { + ImExprs arguments = JassIm.ImExprs(); - ImFunctionCall newCall = JassIm.ImFunctionCall(fc.getTrace(), newFunc, JassIm.ImTypeArguments(), arguments, true, CallType.NORMAL); + // first argument is the choice index + arguments.add(JassIm.ImIntVal(funcToIndex.get(oldFunc))); - Element ret; - if (oldFunc.getReturnType() instanceof ImVoid) { - ret = newCall; + // now for the actual arguments + List oldArgs = fc.getArguments().removeAll(); + int pos = 0; + for (int i = 1; i < newFunc.getParameters().size(); i++) { + ImVar p = newFunc.getParameters().get(i); + if (pos < oldArgs.size() && oldToNewVar.get(oldFunc.getParameters().get(pos)) == p) { + arguments.add(oldArgs.get(pos)); + pos++; } else { - // if there is a return value, use the temporary return value - ret = JassIm.ImStatementExpr(JassIm.ImStmts(newCall), JassIm.ImVarAccess(getTempReturnVar(oldFunc.getReturnType()))); + // use default value + arguments.add(tr.getDefaultValueForJassType(p.getType())); } - fc.replaceBy(ret); - } - } + ImFunctionCall newCall = JassIm.ImFunctionCall(fc.getTrace(), newFunc, JassIm.ImTypeArguments(), arguments, true, CallType.NORMAL); + + Element ret; + if (oldFunc.getReturnType() instanceof ImVoid) { + ret = newCall; + } else { + // if there is a return value, use the temporary return value + ret = JassIm.ImStatementExpr(JassIm.ImStmts(newCall), JassIm.ImVarAccess(getTempReturnVar(oldFunc.getReturnType()))); + } + fc.replaceBy(ret); + + } } private void replaceReturn(Element e, ImType returnType) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java index b33f60357..9ae415b57 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java @@ -32,7 +32,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; + import static de.peeeq.wurstscript.jassIm.JassIm.*; + /** * flattening expressions and statements * after flattening there will be no more StatementExprs @@ -204,8 +206,8 @@ private static void exprToStatements(List result, Element e, ImTranslato flattenStatementsInto(result, e2.getStatements(), t, f); exprToStatements(result, e2, t, f); } else if (e instanceof ImOperatorCall && - (((ImOperatorCall) e).getOp() == WurstOperator.AND - || ((ImOperatorCall) e).getOp() == WurstOperator.OR)) { + (((ImOperatorCall) e).getOp() == WurstOperator.AND + || ((ImOperatorCall) e).getOp() == WurstOperator.OR)) { // short circuiting operators have to be handled in a special way: // we translate them to if statements when necessary ImOperatorCall oc = (ImOperatorCall) e; @@ -257,16 +259,16 @@ public static Result flatten(ImIf s, ImTranslator t, ImFunction f) { Result cond = s.getCondition().flatten(t, f); List stmts = Lists.newArrayList(cond.stmts); stmts.add( - JassIm.ImIf(s.getTrace(), cond.expr, - flattenStatements(s.getThenBlock(), t, f), - flattenStatements(s.getElseBlock(), t, f))); + JassIm.ImIf(s.getTrace(), cond.expr, + flattenStatements(s.getThenBlock(), t, f), + flattenStatements(s.getElseBlock(), t, f))); return new Result(stmts); } public static Result flatten(ImLoop s, ImTranslator t, ImFunction f) { return new Result(Collections.singletonList( - JassIm.ImLoop(s.getTrace(), flattenStatements(s.getBody(), t, f)))); + JassIm.ImLoop(s.getTrace(), flattenStatements(s.getBody(), t, f)))); } public static Result flatten(ImReturn s, ImTranslator t, ImFunction f) { @@ -440,14 +442,9 @@ public static void flattenFunc(ImFunction f, ImTranslator translator) { } public static void flattenProg(ImProg imProg, ImTranslator translator) { - for (ImFunction f : imProg.getFunctions()) { - f.flatten(translator); - } - for (ImClass c : imProg.getClasses()) { - for (ImFunction f : c.getFunctions()) { - f.flatten(translator); - } - } + imProg.getFunctions().parallelStream().forEach(f -> f.flatten(translator)); + imProg.getClasses().parallelStream().forEach(c -> + c.getFunctions().parallelStream().forEach(f -> f.flatten(translator))); translator.assertProperties(AssertProperty.FLAT); } @@ -481,7 +478,7 @@ private static MultiResult flattenExprs(ImTranslator t, ImFunction f, List= withStmts) { + || i >= withStmts) { newExprs.add(r.expr); } else { ImVar tempVar = JassIm.ImVar(e.attrTrace(), r.expr.attrTyp(), "temp", false); @@ -512,7 +509,7 @@ private static MultiResultL flattenExprsL(ImTranslator t, ImFunction f, List= withStmts) { + || i >= withStmts) { newExprs.add(r.getExpr()); } else { ImVar tempVar = JassIm.ImVar(e.attrTrace(), r.expr.attrTyp(), "temp", false); @@ -553,9 +550,8 @@ public static Result flatten(ImGetStackTrace e, ImTranslator translator, public static Result flatten(ImVarargLoop s, ImTranslator translator, ImFunction f) { return new Result(Collections.singletonList( - JassIm.ImVarargLoop(s.getTrace(), flattenStatements(s.getBody(), translator, f), s.getLoopVar()))); + JassIm.ImVarargLoop(s.getTrace(), flattenStatements(s.getBody(), translator, f), s.getLoopVar()))); } - } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index 2ebc36510..f7d2ed018 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -1547,12 +1547,12 @@ public void assertProperties(Set properties, Element e) { if (e instanceof ElementWithVar) { checkVar(((ElementWithVar) e).getVar(), properties); } - properties.forEach(p -> p.check(e)); + properties.parallelStream().forEach(p -> p.check(e)); if (properties.contains(AssertProperty.NOTUPLES)) { - + // TODO ? } if (properties.contains(AssertProperty.FLAT)) { - + // TODO ? } for (int i = 0; i < e.size(); i++) { Element child = e.get(i); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/MultiArrayEliminator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/MultiArrayEliminator.java index 0fee1baac..6c3593f7e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/MultiArrayEliminator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/MultiArrayEliminator.java @@ -82,8 +82,8 @@ public void run() { } replaceVars(prog, getSetMap); - prog.getGlobals().addAll(newVars); prog.getGlobals().removeAll(oldVars); + prog.getGlobals().addAll(newVars); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java index c95c5ca89..9baaf1594 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java @@ -59,6 +59,8 @@ public void validate(Collection toCheck) { prog.getErrorHandler().setProgress("Checking wurst types", ProgressHelper.getValidatorPercent(visitedFunctions, functionCount)); + + for (CompilationUnit cu : toCheck) { walkTree(cu); } @@ -281,10 +283,10 @@ private void check(Element e) { checkConstructorsUnique((ClassOrModule) e); if (e instanceof CompilationUnit) checkPackageName((CompilationUnit) e); - if (e instanceof ConstructorDef) + if (e instanceof ConstructorDef) { checkConstructor((ConstructorDef) e); - if (e instanceof ConstructorDef) checkConstructorSuperCall((ConstructorDef) e); + } if (e instanceof ExprBinary) visit((ExprBinary) e); if (e instanceof ExprClosure) @@ -295,20 +297,20 @@ private void check(Element e) { checkIntVal((ExprIntVal) e); if (e instanceof ExprFuncRef) checkFuncRef((ExprFuncRef) e); - if (e instanceof ExprFunctionCall) + if (e instanceof ExprFunctionCall) { checkBannedFunctions((ExprFunctionCall) e); - if (e instanceof ExprFunctionCall) visit((ExprFunctionCall) e); + } if (e instanceof ExprMemberMethod) visit((ExprMemberMethod) e); if (e instanceof ExprMemberVar) checkMemberVar((ExprMemberVar) e); if (e instanceof ExprMemberArrayVar) checkMemberArrayVar((ExprMemberArrayVar) e); - if (e instanceof ExprNewObject) + if (e instanceof ExprNewObject) { checkNewObj((ExprNewObject) e); - if (e instanceof ExprNewObject) visit((ExprNewObject) e); + } if (e instanceof ExprNull) checkExprNull((ExprNull) e); if (e instanceof ExprVarAccess) @@ -331,22 +333,22 @@ private void check(Element e) { checkTypeBinding((HasTypeArgs) e); if (e instanceof InterfaceDef) checkInterfaceDef((InterfaceDef) e); - if (e instanceof LocalVarDef) + if (e instanceof LocalVarDef) { checkLocalShadowing((LocalVarDef) e); - if (e instanceof LocalVarDef) visit((LocalVarDef) e); + } if (e instanceof Modifiers) visit((Modifiers) e); if (e instanceof ModuleDef) visit((ModuleDef) e); - if (e instanceof NameDef) + if (e instanceof NameDef) { nameDefsMustNotBeNamedAfterJassNativeTypes((NameDef) e); - if (e instanceof NameDef) checkConfigOverride((NameDef) e); - if (e instanceof NameRef) + } + if (e instanceof NameRef) { checkImplicitParameter((NameRef) e); - if (e instanceof NameRef) checkNameRef((NameRef) e); + } if (e instanceof StmtCall) checkCall((StmtCall) e); if (e instanceof ExprDestroy) @@ -375,10 +377,10 @@ private void check(Element e) { visit((WImport) e); if (e instanceof WPackage) checkPackage((WPackage) e); - if (e instanceof WParameter) + if (e instanceof WParameter) { checkParameter((WParameter) e); - if (e instanceof WParameter) visit((WParameter) e); + } if (e instanceof WScope) checkForDuplicateNames((WScope) e); if (e instanceof WStatement)