diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4eef41407..6b599f3cc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -72,3 +72,4 @@ jobs: generate_release_notes: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + diff --git a/Wurstpack/wurstscript/grill.exe b/Wurstpack/wurstscript/grill.exe deleted file mode 100644 index 17545fed9..000000000 Binary files a/Wurstpack/wurstscript/grill.exe and /dev/null differ diff --git a/Wurstpack/wurstscript/wurstscript b/Wurstpack/wurstscript/wurstscript new file mode 100644 index 000000000..3423f0dd0 --- /dev/null +++ b/Wurstpack/wurstscript/wurstscript @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -euo pipefail + +RUNTIME="$HOME/.wurst/wurst-runtime/bin/java" + +# Resolve script dir (absolute) +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +JAR="$DIR/wurst-compiler/wurstscript.jar" +if [[ ! -f "$JAR" ]]; then + JAR="$DIR/../wurst-compiler/wurstscript.jar" +fi + +if [[ ! -f "$JAR" ]]; then + echo "[wurstscript] ERROR: Missing jar. Searched:" + echo " $DIR/wurst-compiler/wurstscript.jar" + echo " $DIR/../wurst-compiler/wurstscript.jar" + exit 2 +fi + +if [[ ! -x "$RUNTIME" ]]; then + echo "[wurstscript] ERROR: Bundled runtime not found or not executable at:" + echo " $RUNTIME" + echo "Please reinstall wurstscript via the VSCode extension." + exit 3 +fi + +exec "$RUNTIME" -jar "$JAR" "$@" diff --git a/Wurstpack/wurstscript/wurstscript.cmd b/Wurstpack/wurstscript/wurstscript.cmd new file mode 100644 index 000000000..9523637b7 --- /dev/null +++ b/Wurstpack/wurstscript/wurstscript.cmd @@ -0,0 +1,30 @@ +@echo off +setlocal EnableExtensions + +rem ---- shared slim runtime ---- +set "RUNTIME=%USERPROFILE%\.wurst\wurst-runtime\bin\java.exe" + +rem script directory (trailing backslash) +set "DIR=%~dp0" + +if not exist "%RUNTIME%" ( + echo [wurstscript] ERROR: Runtime not found: + echo(%RUNTIME% + echo Reinstall Wurstscript via the VSCode extension. + exit /b 1 +) + +rem ---- fixed jar location(s), no wildcards ---- +set "JAR=%DIR%wurst-compiler\wurstscript.jar" +if not exist "%JAR%" set "JAR=%DIR%..\wurst-compiler\wurstscript.jar" +if not exist "%JAR%" ( + echo [wurstscript] ERROR: Missing jar: + echo(%DIR%wurst-compiler\wurstscript.jar + echo or + echo(%DIR%..\wurst-compiler\wurstscript.jar + exit /b 1 +) + +rem Optional JVM flags via env var, e.g. set WURST_JAVA_OPTS=-Xmx1g +"%RUNTIME%" %WURST_JAVA_OPTS% -jar "%JAR%" %* +endlocal diff --git a/Wurstpack/wurstscript/wurstscript.exe b/Wurstpack/wurstscript/wurstscript.exe deleted file mode 100644 index 78bcb5101..000000000 Binary files a/Wurstpack/wurstscript/wurstscript.exe and /dev/null differ diff --git a/de.peeeq.wurstscript/build.gradle b/de.peeeq.wurstscript/build.gradle index 4f3a2f1d3..3a19b1cef 100644 --- a/de.peeeq.wurstscript/build.gradle +++ b/de.peeeq.wurstscript/build.gradle @@ -8,7 +8,6 @@ buildscript { plugins { id 'java' - id 'application' id 'antlr' id 'eclipse' id 'idea' @@ -27,9 +26,7 @@ import org.eclipse.jgit.lib.ObjectId import java.util.regex.Pattern -application { - mainClass = "de.peeeq.wurstio.Main" -} +ext.mainClassName = "de.peeeq.wurstio.Main" version = "1.8.1.0" @@ -229,7 +226,9 @@ shadowJar { archiveBaseName.set('wurstscript') archiveClassifier.set('') archiveVersion.set('') - manifest { attributes 'Main-Class': application.mainClass.get() } + manifest { + attributes 'Main-Class': mainClassName + } } def fatJar = shadowJar.archiveFile.map { it.asFile } diff --git a/de.peeeq.wurstscript/deploy.gradle b/de.peeeq.wurstscript/deploy.gradle index 0d00ed817..67bdcaeff 100644 --- a/de.peeeq.wurstscript/deploy.gradle +++ b/de.peeeq.wurstscript/deploy.gradle @@ -167,6 +167,16 @@ tasks.register("assembleSlimCompilerDist", Copy) { } } +tasks.named("assembleSlimCompilerDist", Copy) { t -> + if (os.isWindows()) { + from("../Wurstpack/wurstscript/wurstscript.cmd") { into(".") } + } else { + from("../Wurstpack/wurstscript/wurstscript") { + into(".") + } + } +} + // 4a) Package ZIP on Windows tasks.register("packageSlimCompilerDistZip", Zip) { description = "Packages slim dist as a ZIP archive (Windows)." 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 02e8e68dc..28ad77798 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 @@ -1568,8 +1568,51 @@ private void checkCall(StmtCall call) { call.attrCallSignature().checkSignatureCompatibility(call.attrFunctionSignature(), funcName, call); } + /** Error when a field in this class hides a field from a superclass. */ + private void checkFieldShadowing(ClassDef c) { + // Iterate only the fields *declared* in this class: + for (NameLink nl : c.attrNameLinks().values()) { + NameDef def = nl.getDef(); + // consider only variables that are declared in this class body + if (!(def instanceof GlobalVarDef)) continue; + if (nl.getDefinedIn() != c) continue; // not declared here + + String name = def.getName(); + + // Look up all visible declarations of the same name in this class scope: + ImmutableCollection all = c.attrNameLinks().get(name); + for (DefLink other : all) { + if (!(other instanceof NameLink)) continue; + if (other.getDefinedIn() == c) continue; // skip "self" (duplicates in same class handled elsewhere) + NameDef od = other.getDef(); + if (!(od instanceof GlobalVarDef)) continue; + + // Is the other definition a superclass' field? + StructureDef owner = od.attrNearestStructureDef(); + if (owner instanceof ClassDef) { + ClassDef superOwner = (ClassDef) owner; + if (isStrictSuperclassOf(superOwner, c)) { + // produce the requested error text + def.addError("Variable " + name + " in class " + c.getName() + + " hides variable " + name + " from superclass " + superOwner.getName()); + // one error per conflicting ancestor is enough + break; + } + } + } + } + } - // crude cap to avoid unbounded growth; tune as needed + private static boolean isStrictSuperclassOf(ClassDef sup, ClassDef sub) { + WurstTypeClass t = sub.attrTypC(); + WurstTypeClass cur = (t != null) ? t.extendedClass() : null; + while (cur != null) { + ClassDef cd = cur.getClassDef(); + if (cd == sup) return true; + cur = cur.extendedClass(); + } + return false; + } private static boolean isSubtypeCached(WurstType actual, WurstType expected, Annotation site) { if (actual == expected) return true; @@ -1796,12 +1839,15 @@ private void checkReturnInFunc(StmtReturn s, FunctionImplementation func) { private void visit(ClassDef classDef) { checkTypeName(classDef, classDef.getName()); - if (!(classDef.getExtendedClass() instanceof NoTypeExpr) && !(classDef.getExtendedClass().attrTyp() instanceof WurstTypeClass)) { + if (!(classDef.getExtendedClass() instanceof NoTypeExpr) + && !(classDef.getExtendedClass().attrTyp() instanceof WurstTypeClass)) { classDef.getExtendedClass().addError("Classes may only extend other classes."); } if (classDef.isInnerClass() && !classDef.attrIsStatic()) { classDef.addError("At the moment only static inner classes are supported."); } + + checkFieldShadowing(classDef); } private void checkTypeName(Element source, String name) { diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/BugTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/BugTests.java index e60d417fe..864fedda7 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/BugTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/BugTests.java @@ -1443,4 +1443,21 @@ public void minusRewrite() { } + @Test + public void duplicateNameInClassHierachy() { + testAssertErrorsLines(false, "Variable x in class B hides variable x from superclass A", + "package test", + "native testSuccess()", + "class A", + " int x", + "class B extends A", + " int x", + "init", + " let b = new B()", + " if b != null", + " testSuccess()", + "endpackage"); + } + + }