|
@@ -369,24 +369,26 @@ rebuild_compiler () {
|
|
|
return 0
|
|
return 0
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-# 1. the original bug: `off MOD 100H`, always disp8. Restores exactly the code
|
|
|
|
|
-# that was there before EmBpDisp existed. t28's [BP+128] read becomes
|
|
|
|
|
-# [BP-128], which is the failure this whole check is named after.
|
|
|
|
|
|
|
+# 1. the original bug: `off MOD 100H`, always disp8. Restores the code that
|
|
|
|
|
+# was there before EmBpDisp existed. t28's [BP+128] read becomes [BP-128],
|
|
|
|
|
+# which is the failure this whole check is named after.
|
|
|
|
|
+#
|
|
|
|
|
+# regf is KEPT. It is the other half of EmBpDisp's job - the ModR/M reg
|
|
|
|
|
+# field, which the DI forms (regf 7) need - and it was added after this
|
|
|
|
|
+# case was written. Hard-coding 46H here again would break the DI forms at
|
|
|
|
|
+# the same time and turn one mutation into two faults; the check would
|
|
|
|
|
+# still go red, but for a reason the label does not name.
|
|
|
python3 - "$SAVED_C" <<'PYEOF'
|
|
python3 - "$SAVED_C" <<'PYEOF'
|
|
|
import sys
|
|
import sys
|
|
|
p = 'Compiler.mod'
|
|
p = 'Compiler.mod'
|
|
|
s = open(p).read()
|
|
s = open(p).read()
|
|
|
-old = """BEGIN
|
|
|
|
|
- IF off <= 127 THEN
|
|
|
|
|
- Ebyte (46H) ; Ebyte (VAL (BYTE, off))
|
|
|
|
|
|
|
+old = """ IF off <= 127 THEN
|
|
|
|
|
+ Ebyte (VAL (BYTE, 40H + regf * 8 + 6H)) ; Ebyte (VAL (BYTE, off))
|
|
|
ELSE
|
|
ELSE
|
|
|
- Ebyte (86H) ; Eword (off)
|
|
|
|
|
|
|
+ Ebyte (VAL (BYTE, 80H + regf * 8 + 6H)) ; Eword (off)
|
|
|
END
|
|
END
|
|
|
END EmBpDisp ;"""
|
|
END EmBpDisp ;"""
|
|
|
-new = """VAR disp : CARDINAL ;
|
|
|
|
|
-BEGIN
|
|
|
|
|
- disp := off MOD 100H ;
|
|
|
|
|
- Ebyte (46H) ; Ebyte (VAL (BYTE, disp))
|
|
|
|
|
|
|
+new = """ Ebyte (VAL (BYTE, 40H + regf * 8 + 6H)) ; Ebyte (VAL (BYTE, off MOD 100H))
|
|
|
END EmBpDisp ;"""
|
|
END EmBpDisp ;"""
|
|
|
assert old in s, "EmBpDisp body not found -- update this mutation"
|
|
assert old in s, "EmBpDisp body not found -- update this mutation"
|
|
|
open(p, 'w').write(s.replace(old, new))
|
|
open(p, 'w').write(s.replace(old, new))
|
|
@@ -409,11 +411,11 @@ python3 - <<'PYEOF'
|
|
|
p = 'Compiler.mod'
|
|
p = 'Compiler.mod'
|
|
|
s = open(p).read()
|
|
s = open(p).read()
|
|
|
old = """ IF off <= 127 THEN
|
|
old = """ IF off <= 127 THEN
|
|
|
- Ebyte (46H) ; Ebyte (VAL (BYTE, off))
|
|
|
|
|
|
|
+ Ebyte (VAL (BYTE, 40H + regf * 8 + 6H)) ; Ebyte (VAL (BYTE, off))
|
|
|
ELSE
|
|
ELSE
|
|
|
- Ebyte (86H) ; Eword (off)
|
|
|
|
|
|
|
+ Ebyte (VAL (BYTE, 80H + regf * 8 + 6H)) ; Eword (off)
|
|
|
END"""
|
|
END"""
|
|
|
-new = """ Ebyte (86H) ; Eword (off)"""
|
|
|
|
|
|
|
+new = """ Ebyte (VAL (BYTE, 80H + regf * 8 + 6H)) ; Eword (off)"""
|
|
|
assert old in s, "EmBpDisp branch not found -- update this mutation"
|
|
assert old in s, "EmBpDisp branch not found -- update this mutation"
|
|
|
open(p, 'w').write(s.replace(old, new))
|
|
open(p, 'w').write(s.replace(old, new))
|
|
|
PYEOF
|
|
PYEOF
|
|
@@ -785,21 +787,23 @@ cp "$SAVED_C" Compiler.mod
|
|
|
if python3 - <<'PYX'
|
|
if python3 - <<'PYX'
|
|
|
p = 'Compiler.mod'
|
|
p = 'Compiler.mod'
|
|
|
s = open(p).read()
|
|
s = open(p).read()
|
|
|
-old = """BEGIN
|
|
|
|
|
- IF left.kind = 2 THEN
|
|
|
|
|
- EmPushAx () ;
|
|
|
|
|
- left.kind := 4 (* 4 = on the top of the stack *)
|
|
|
|
|
- END
|
|
|
|
|
-END SaveLeft ;"""
|
|
|
|
|
-new = """BEGIN
|
|
|
|
|
- (* MUTATION: the park is unreachable, so nothing survives the parse *)
|
|
|
|
|
- IF left.kind = 99 THEN
|
|
|
|
|
- EmPushAx () ;
|
|
|
|
|
- left.kind := 4
|
|
|
|
|
- END
|
|
|
|
|
-END SaveLeft ;"""
|
|
|
|
|
-assert s.count(old) == 1, 'SaveLeft body found %d times -- update this mutation' % s.count(old)
|
|
|
|
|
-open(p, 'w').write(s.replace(old, new))
|
|
|
|
|
|
|
+# Both guards are matched, and each must be unique. `IF left.kind = 2 THEN'
|
|
|
|
|
+# reads like it can only be SaveLeft's, but LoadAtom and friends also branch
|
|
|
|
|
+# on kind, so an unasserted replace could disable one of THEM instead and the
|
|
|
|
|
+# case would still go red - for a reason the label does not name.
|
|
|
|
|
+#
|
|
|
|
|
+# The kind-5 ("a string element whose address is in DI") guard did not exist
|
|
|
|
|
+# when this case was written; it arrived with `s[i]`. Making only the kind-2
|
|
|
|
|
+# park unreachable would leave that one alive, so the mutation would no longer
|
|
|
|
|
+# be "SaveLeft parks nothing" - it would be a half-disabled SaveLeft, and the
|
|
|
|
|
+# case would be testing something narrower than it says.
|
|
|
|
|
+g2 = ' IF left.kind = 2 THEN\n'
|
|
|
|
|
+g5 = ' ELSIF left.kind = 5 THEN\n'
|
|
|
|
|
+assert s.count(g2) == 1, 'the kind-2 guard found %d times -- update this mutation' % s.count(g2)
|
|
|
|
|
+assert s.count(g5) == 1, 'the kind-5 guard found %d times -- update this mutation' % s.count(g5)
|
|
|
|
|
+s = s.replace(g2, ' IF left.kind = 99 THEN\n')
|
|
|
|
|
+s = s.replace(g5, ' ELSIF left.kind = 99 THEN\n')
|
|
|
|
|
+open(p, 'w').write(s)
|
|
|
PYX
|
|
PYX
|
|
|
then
|
|
then
|
|
|
if rebuild_compiler; then
|
|
if rebuild_compiler; then
|
|
@@ -1043,19 +1047,26 @@ cp "$SAVED_C" Compiler.mod
|
|
|
# and only rt_exec.py can see a wrstr that reads no length at all.
|
|
# and only rt_exec.py can see a wrstr that reads no length at all.
|
|
|
|
|
|
|
|
# 1. Assignment's string arm is gone, so `s := 'hello'' trips the old
|
|
# 1. Assignment's string arm is gone, so `s := 'hello'' trips the old
|
|
|
-# `size > 2' refusal again. The arm is spliced out by its boundaries, not
|
|
|
|
|
-# by its text, so it cannot silently stop matching when the comment inside
|
|
|
|
|
-# is touched.
|
|
|
|
|
|
|
+# `size > 2' refusal again. The arm is spliced between its guard and the
|
|
|
|
|
+# `size > 2' check that follows it, so a comment inside cannot break the
|
|
|
|
|
+# match - but the GUARD is matched by text, and it has to be the whole
|
|
|
|
|
+# guard. It grew an `AND (r.cls = TString)' when `s[i] := c' made a
|
|
|
|
|
+# TString destination reach a second arm, and ParseSub grew a look-alike
|
|
|
|
|
+# `IF symtab [r.idx].cls = TString THEN' for `s[i]'. The shorter anchor
|
|
|
|
|
+# therefore now finds ParseSub's, some 800 lines earlier, and the splice
|
|
|
|
|
+# below deletes everything between the two - which does not test the
|
|
|
|
|
+# refusal, it tests whether the compiler still compiles. Hence the assert
|
|
|
|
|
+# and the spelled-out guard.
|
|
|
cp "$SAVED_C" Compiler.mod
|
|
cp "$SAVED_C" Compiler.mod
|
|
|
if python3 - <<'PYX'
|
|
if python3 - <<'PYX'
|
|
|
p = 'Compiler.mod'
|
|
p = 'Compiler.mod'
|
|
|
s = open(p).read()
|
|
s = open(p).read()
|
|
|
-a = s.index(' IF symtab [r.idx].cls = TString THEN')
|
|
|
|
|
|
|
+g = ' IF (symtab [r.idx].cls = TString) AND (r.cls = TString) THEN\n'
|
|
|
|
|
+assert s.count(g) == 1, 'the string-assignment guard found %d times' % s.count(g)
|
|
|
|
|
+a = s.index(g)
|
|
|
m = ' RETURN\n END ;\n IF symtab [r.idx].size > 2 THEN\n'
|
|
m = ' RETURN\n END ;\n IF symtab [r.idx].size > 2 THEN\n'
|
|
|
i = s.index(m, a)
|
|
i = s.index(m, a)
|
|
|
-new = (' IF symtab [r.idx].cls = TString THEN\n'
|
|
|
|
|
- ' Err (ENoLib) ; (* MUTATION: the arm is refused *)\n'
|
|
|
|
|
- ' RETURN\n')
|
|
|
|
|
|
|
+new = g + ' Err (ENoLib) ; (* MUTATION: the arm is refused *)\n RETURN\n'
|
|
|
open(p, 'w').write(s[:a] + new + s[i + len(' RETURN\n'):])
|
|
open(p, 'w').write(s[:a] + new + s[i + len(' RETURN\n'):])
|
|
|
PYX
|
|
PYX
|
|
|
then
|
|
then
|