Pruefung auf doppelte Funktionsnamen — und ein zweiter Fund
Die Kollision von heute war weder fuer den Browser noch fuer einen Syntaxpruefer sichtbar: Zwei Funktionen desselben Namens sind erlaubtes JavaScript, die spaetere gewinnt lautlos. Auch die Oberflaechen-Tests schwiegen -- sie pruefen, ob Kacheln DA sind, nicht ob sinnvoller Text darin steht. pruef-namenskollision.mjs durchsucht jetzt alle 110 Dateien mit eigenem JavaScript. Mit Gegenprobe, damit die Pruefung nicht selbst kaputtgehen und dabei "sauber" melden kann. ZWEITER FUND, AELTER ALS MEIN FEHLER Sie meldete sofort eine weitere Kollision: tageSeit stand zweimal in verwaltung.html. Beide rechneten dasselbe, mit einem Unterschied -- bei fehlendem Datum gab die eine null zurueck, die andere 0. Die spaetere (mit 0) gewann. Damit war die frueher definierte wirkungslos, und mit ihr die Pruefungen "if (t === null) return ''" in altersText und dringlichkeit: Ohne Datum stand dort "seit heute" statt gar nichts. Entfernt wurde die spaetere. Die beiden verbliebenen Aufrufer vertragen null genauso wie 0 -- nachgeprueft, nicht angenommen: Math.max(x, null) ergibt x, und null >= 7 ist falsch, gleich wie bei 0. 110 Dateien jetzt sauber. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -838,7 +838,7 @@
|
||||
Stand haben. Die Nummer wird zusammen mit CACHE_NAME in sw.js
|
||||
hochgezaehlt -- beide gehoeren zusammen. */
|
||||
navigator.serviceWorker
|
||||
.register("/webdesign/sw.js?v=57", { scope: "/webdesign/" })
|
||||
.register("/webdesign/sw.js?v=58", { scope: "/webdesign/" })
|
||||
.catch(function () { /* still schlucken: ohne SW funktioniert alles weiter */ });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/* =====================================================================
|
||||
Sucht doppelt vergebene Funktionsnamen.
|
||||
|
||||
ANLASS
|
||||
|
||||
Am 26.08.2026 brachte neuer Code eine Hilfsfunktion namens "kachel"
|
||||
mit. Weiter oben in derselben Datei gab es seit langem eine andere
|
||||
Funktion desselben Namens, die die Kacheln der Uebersicht baut.
|
||||
|
||||
JavaScript kennt keine Ueberladung: Die spaetere Definition gewinnt.
|
||||
Ohne Warnung, ohne Fehler, ohne dass irgendetwas anschlaegt. Die
|
||||
Uebersicht zeigte danach in jeder Kachel "[object Object]".
|
||||
|
||||
WARUM KEIN ANDERES WERKZEUG DAS FINDET
|
||||
|
||||
- Der Browser meldet nichts: Zwei Funktionen desselben Namens sind
|
||||
erlaubtes JavaScript.
|
||||
- "node --check" meldet nichts: syntaktisch einwandfrei.
|
||||
- Die Oberflaechen-Tests meldeten nichts: Sie pruefen, ob Kacheln DA
|
||||
sind, nicht ob sinnvoller Text darin steht.
|
||||
- Am tueckischsten: Der neue Code selbst funktionierte einwandfrei.
|
||||
Der Schaden trat an einer ganz anderen Stelle auf, und nichts
|
||||
deutete auf die Ursache hin.
|
||||
|
||||
Diese Pruefung kostet Sekundenbruchteile und haette den Fehler
|
||||
verhindert, bevor er ausgeliefert wurde.
|
||||
===================================================================== */
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
let ok = 0, fehl = 0;
|
||||
const pruefe = (name, gut, info) => {
|
||||
if (gut) { ok++; console.log(" ok " + name + (info ? " -> " + info : "")); }
|
||||
else { fehl++; console.log(" FEHL " + name + (info ? " -> " + info : "")); }
|
||||
};
|
||||
|
||||
const WURZEL = process.cwd();
|
||||
|
||||
/* Alle Dateien mit eigenem JavaScript: die grossen Inline-Bloecke der
|
||||
Seiten und die Skriptdateien. */
|
||||
const dateien = [
|
||||
...fs.readdirSync(WURZEL).filter((f) => f.endsWith(".html")).map((f) => f),
|
||||
...fs.readdirSync(path.join(WURZEL, "webdesign"))
|
||||
.filter((f) => f.endsWith(".html")).map((f) => "webdesign/" + f),
|
||||
...fs.readdirSync(path.join(WURZEL, "assets/js"))
|
||||
.filter((f) => f.endsWith(".js")).map((f) => "assets/js/" + f),
|
||||
];
|
||||
|
||||
console.log(`Durchsucht: ${dateien.length} Dateien\n`);
|
||||
|
||||
const betroffen = [];
|
||||
|
||||
for (const rel of dateien) {
|
||||
const quelle = fs.readFileSync(path.join(WURZEL, rel), "utf8");
|
||||
|
||||
/* Nur Deklarationen am Zeilenanfang (mit Einrueckung). Damit bleiben
|
||||
Methoden in Objekten, verschachtelte Hilfsfunktionen in anderen
|
||||
Funktionen und Zeichenketten aussen vor -- die kollidieren nicht
|
||||
miteinander, weil sie in eigenen Gueltigkeitsbereichen liegen. */
|
||||
const muster = /^[ \t]*function\s+([A-Za-z_$][\w$]*)\s*\(/gm;
|
||||
const gesehen = new Map();
|
||||
let t;
|
||||
while ((t = muster.exec(quelle)) !== null) {
|
||||
const name = t[1];
|
||||
const zeile = quelle.slice(0, t.index).split("\n").length;
|
||||
if (!gesehen.has(name)) gesehen.set(name, []);
|
||||
gesehen.get(name).push(zeile);
|
||||
}
|
||||
|
||||
for (const [name, zeilen] of gesehen) {
|
||||
if (zeilen.length > 1) {
|
||||
betroffen.push({ datei: rel, name, zeilen });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pruefe("kein Funktionsname ist doppelt vergeben", betroffen.length === 0,
|
||||
betroffen.length ? betroffen.length + " Kollision(en)" : dateien.length + " Dateien sauber");
|
||||
|
||||
for (const b of betroffen) {
|
||||
console.log(`\n ${b.datei}`);
|
||||
console.log(` "${b.name}" steht in Zeile ${b.zeilen.join(" und ")}`);
|
||||
console.log(` Die spaetere gewinnt — die fruehere ist damit wirkungslos.`);
|
||||
}
|
||||
|
||||
/* Gegenprobe: Die Pruefung muss auch wirklich etwas finden koennen.
|
||||
Ohne diesen Abschnitt koennte das Muster kaputt sein und immer
|
||||
"sauber" melden. */
|
||||
console.log("\nGEGENPROBE");
|
||||
const beispiel = `
|
||||
function tuwas(a) { return a; }
|
||||
var x = 1;
|
||||
function tuwas(a, b) { return b; }
|
||||
`;
|
||||
const m2 = /^[ \t]*function\s+([A-Za-z_$][\w$]*)\s*\(/gm;
|
||||
const treffer = [...beispiel.matchAll(m2)].map((x) => x[1]);
|
||||
pruefe(" findet eine eingebaute Kollision",
|
||||
treffer.filter((n) => n === "tuwas").length === 2,
|
||||
treffer.join(", "));
|
||||
|
||||
console.log(`\n===== ${ok} bestanden, ${fehl} fehlgeschlagen =====`);
|
||||
process.exit(fehl ? 1 : 0);
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
+1
-1
@@ -34,7 +34,7 @@
|
||||
Start alle alten Zwischenspeicher weg. Muss bei jeder Änderung an den
|
||||
Dateien unten hochgezählt werden, sonst hängen Nutzer auf einem alten
|
||||
Stand fest. */
|
||||
const CACHE_NAME = "dogfather-webdesign-v57";
|
||||
const CACHE_NAME = "dogfather-webdesign-v58";
|
||||
|
||||
/* Bausteine, die die Oberfläche zum Anzeigen braucht. Bewusst KEINE
|
||||
HTML-Datei in dieser Liste. */
|
||||
|
||||
+19
-12
@@ -3223,18 +3223,25 @@
|
||||
}
|
||||
|
||||
|
||||
/* Ganze Tage seit einem Zeitpunkt. Bewusst ueber den Kalendertag
|
||||
gerechnet, nicht ueber 24-Stunden-Bloecke: eine Anfrage von gestern
|
||||
23:50 Uhr ist umgangssprachlich "gestern", nicht "vor 0 Tagen". */
|
||||
function tageSeit(zeit) {
|
||||
if (!zeit) return 0;
|
||||
var dann = new Date(zeit);
|
||||
if (isNaN(dann.getTime())) return 0;
|
||||
var a = new Date(dann.getFullYear(), dann.getMonth(), dann.getDate());
|
||||
var heute = new Date();
|
||||
var b = new Date(heute.getFullYear(), heute.getMonth(), heute.getDate());
|
||||
return Math.round((b - a) / 86400000);
|
||||
}
|
||||
/* ⚠️ HIER STAND EINE ZWEITE FUNKTION tageSeit (entfernt 26.08.2026).
|
||||
|
||||
Sie rechnete dasselbe wie die weiter oben, unterschied sich aber in
|
||||
einem Punkt: Bei fehlendem oder unlesbarem Datum gab sie 0 zurueck,
|
||||
die obere gibt null.
|
||||
|
||||
Weil in JavaScript die spaetere Definition gewinnt, war die obere
|
||||
seit ihrer Einfuehrung wirkungslos -- und mit ihr die Pruefungen
|
||||
"if (t === null) return ''" in altersText und dringlichkeit. Ohne
|
||||
Datum stand dort seither "seit heute" statt gar nichts.
|
||||
|
||||
Gefunden hat das keine Fehlermeldung, sondern pruef-namenskollision.mjs,
|
||||
entstanden am selben Tag aus genau demselben Fehler an anderer
|
||||
Stelle. Zwei Funktionen desselben Namens sind erlaubtes JavaScript;
|
||||
kein Browser und kein Syntaxpruefer schlaegt an.
|
||||
|
||||
Die beiden verbliebenen Aufrufer weiter unten vertragen null
|
||||
genauso wie 0 -- geprueft: Math.max(x, null) ergibt x, und
|
||||
null >= 7 ist falsch, gleich wie bei 0. */
|
||||
|
||||
function zeitText(tage) {
|
||||
if (tage <= 0) return "heute";
|
||||
|
||||
Reference in New Issue
Block a user