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
205 changes: 205 additions & 0 deletions BUGS-TO-REPORT.md

Large diffs are not rendered by default.

46 changes: 29 additions & 17 deletions WIP.Build.md
Original file line number Diff line number Diff line change
Expand Up @@ -485,13 +485,17 @@ the bytes of the [help add-in's project file](#the-help-add-ins-project-file).

**Which folders under `docs/` are outputs comes from one list.** A build given
`--dest docs/_site-basepath` writes `_site-basepath-offline` and
`_site-basepath-pdf` as well, so naming output folders one at a time misses some
and reads them as sources. The script skips the top-level folders that
`isOutputTree` in [lib/markdown-files.mjs](lib/markdown-files.mjs) names --- the
prefix list (`_site`, `_serve`, `_pdf`) the markdown walk uses --- and keeps only
`.git` and `node_modules` as names of its own. Serve mode prepares `_serve` alone
(`prepDest` in `builder/tbdocs.mjs`), so it leaves no `_serve-offline` or
`_serve-pdf` beside it.
`_site-basepath-pdf` as well, unless `--no-offline` and `--no-pdf` skip them, so
naming output folders one at a time misses some and reads them as sources. The
script skips the top-level folders that `isOutputTree` in
[lib/markdown-files.mjs](lib/markdown-files.mjs) names --- the prefix list
(`_site`, `_serve`, `_pdf`) the markdown walk uses --- and keeps only `.git` and
`node_modules` as names of its own. `prepDest` in `builder/tbdocs.mjs` prepares
only the trees whose passes run (`destinationTrees` in `builder/write.mjs`): a
build removes a skipped sibling rather than leaving it empty, and serve mode
prepares `_serve` alone, so it leaves no `_serve-offline` or `_serve-pdf` beside
it. `check_cli.mjs` probes the rule, because nothing else would notice two empty
folders beside a `--no-offline --no-pdf` build.

**Files the build writes into a source folder are not sources.**
`page-baseline.json` and `symbol-baseline.json` (`IGNORED_FILES`) are written
Expand Down Expand Up @@ -610,20 +614,27 @@ and symbol guards share, and the second in
that added a page. The script's sources are "the inputs that decide the built
bytes", and a baseline decides none of them. `dot` and `vendorAssets` write into
`docs/` and escape this only because they run early.
- **Neither CI nor `--serve` may write.** A CI run that rewrote the file would
record the drop it was asked to catch, so there a missing baseline is an error
rather than a first run. `--serve` rebuilds on every save under `docs/`, so a
page half-deleted in an editor would lower the baseline and a half-added one
would raise it.
- **Neither CI nor `--serve` may write, nor a build into another `--dest`.** A
CI run that rewrote the file would record the drop it was asked to catch, so
there a missing baseline is an error rather than a first run. `--serve`
rebuilds on every save under `docs/`, so a page half-deleted in an editor
would lower the baseline and a half-added one would raise it. A build into a
`--dest` other than `docs/_site` is a private copy: agents building side by
side into `.claude/<name>-site` would each rewrite the committed file, and
every other checkout would have to undo it after every build. Such a build
compares and prints `page baseline not raised: ...` for a rise; a fall still
fails it. `baselineWrites` decides all three, and the symbol baseline and the
add-in's index copy follow it too. `--update-page-baseline` writes whatever
the destination.

**Mark a build failed through `failBuild`, never by assigning
`process.exitCode`.** `failBuild` is the one place `runBuild` sets the found-problems
exit code (1), so no later step can clear it; a refused command line or a crash
exits 2 instead.

`scripts/check_page_baseline.mjs` is the gate on the gate, in `test.bat` and
both CI workflows: eleven probes against a scratch baseline, no browser, no
built tree. **It is not optional bookkeeping** --- the guard is silent on a
both CI workflows: seventeen probes against a scratch baseline, no browser, no
built tree, six of them on who may write. **It is not optional bookkeeping** --- the guard is silent on a
healthy tree, so a green build is exactly what a guard that has stopped working
produces. One probe replays the loss of 37 pages; the two that look redundant
(foreign source root, missing baseline under CI) each guard one of the rules
Expand Down Expand Up @@ -691,8 +702,8 @@ Three decisions about the build side, each with the alternative it rules out:
**The drift guard is the page-count guard's shape applied to URLs**
([builder/symbol-baseline.mjs](builder/symbol-baseline.mjs)): `builder/symbol-baseline.json`
lists every URL the index has published, one to a line. A build that loses one fails and
names it; a build that adds one rewrites the list; CI, `--serve` and `--dry-run` never
write; a source root other than `docs` is skipped; `--update-symbol-baseline` records a
names it; a build that adds one rewrites the list; CI, `--serve`, `--dry-run` and a build
into another `--dest` never write; a source root other than `docs` is skipped; `--update-symbol-baseline` records a
removal. It exists because **an anchor has no `redirect_from:`** --- a reworded member
heading moves its id, an installed add-in keeps the old URL, and the link check only
follows links made inside the site, so nothing else would notice. The failure message
Expand All @@ -701,7 +712,8 @@ leads with the usual fix, pinning the old id on the reworded heading.
**The help add-in's copy of the index** ([builder/addin-index.mjs](builder/addin-index.mjs)):
`add-in/Resources/SYMBOLS/symbols.json` is committed so that the add-in builds from its
tree as it is, and a build of `docs` rewrites it whenever it differs from the index the
build wrote, under the guards' write rules (not CI, not `--serve`, not `--dry-run`). A
build wrote, under the guards' write rules (not CI, not `--serve`, not `--dry-run`, not
another `--dest`). A
difference is reported, never a failure: a stale copy is an older index, not a broken one.
The comparison is byte for byte, which is why `.gitattributes` checks `add-in/Resources/`
out `-text`: a CRLF checkout of the copy would differ from the LF the build writes on every
Expand Down
6 changes: 3 additions & 3 deletions WIP.md

Large diffs are not rendered by default.

Binary file not shown.
6 changes: 6 additions & 0 deletions bugs/class-array-field-element-assignment-crash/repro.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"mode": "compile",
"expect": {
"exit": 4
}
}
61 changes: 61 additions & 0 deletions bugs/class-array-field-element-assignment-crash/src/Settings
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"configuration.inherits": "Defaults",
"project.appTitle": "ClassArrayFieldElementAssignmentCrash",
"project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}",
"project.buildType": "Standard EXE",
"project.description": "Reproduces: Assigning to an element of a public array field of a class crashes the compiler",
"project.exportPathIsV2": true,
"project.id": "{D299DBED-CBF8-45C4-B380-6B47C584A6E3}",
"project.name": "ClassArrayFieldElementAssignmentCrash",
"project.optionExplicit": true,
"project.references": [
{
"id": "{00020430-0000-0000-C000-000000000046}",
"lcid": 0,
"name": "OLE Automation",
"path32": "C:\\Windows\\SysWOW64\\stdole2.tlb",
"path64": "C:\\Windows\\System32\\stdole2.tlb",
"symbolId": "stdole",
"versionMajor": 2,
"versionMinor": 0
},
{
"id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}",
"isCompilerPackage": true,
"licence": "MIT",
"name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)",
"path32": "",
"path64": "",
"publisher": "TWINBASIC-COMPILER",
"symbolId": "VB",
"versionBuild": 0,
"versionMajor": 0,
"versionMinor": 0,
"versionRevision": 31
},
{
"id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}",
"isCompilerPackage": true,
"licence": "MIT",
"name": "[COMPILER PACKAGE] twinBASIC - App global class object",
"path32": "",
"path64": "",
"publisher": "TWINBASIC-COMPILER",
"symbolId": "AppGlobalClassProject",
"versionBuild": 0,
"versionMajor": 1,
"versionMinor": 0,
"versionRevision": 0
}
],
"project.settingsVersion": 1,
"project.startupObject": "Sub Main",
"project.warnings": {
"errors": [],
"hints": [],
"ignored": [],
"info": [],
"warnings": []
},
"runtime.useUnicodeStandardLibrary": true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
' An assignment to an element of a public array field of a class, written outside the class
' (h.Values(1) = 5), crashes the compiler. The declaration of the field alone does not, and
' nor does an assignment inside the class, or the read of an element, which is refused
' (TB5033). VB6 refuses the declaration (the VB6 side is in vb6/).

Class Holder
Public Values(1 To 3) As Long
End Class

Module Startup

Public Sub Main()
Dim h As New Holder
h.Values(1) = 5
End Sub

End Module
25 changes: 25 additions & 0 deletions bugs/class-array-field-element-assignment-crash/vb6/Holder.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "Holder"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

' VB6 refuses this declaration when it builds the project:
' "Constants, fixed-length strings, arrays, user-defined types and Declare statements not
' allowed as Public members of object modules". The line is commented out so that the
' project builds; the message is the one VB6 prints for the line left in.
'Public Values(1 To 3) As Long
Private mValues(1 To 3) As Long

Public Property Get Values(ByVal Index As Long) As Long
Values = mValues(Index)
End Property

Public Property Let Values(ByVal Index As Long, ByVal Value As Long)
mValues(Index) = Value
End Property
20 changes: 20 additions & 0 deletions bugs/class-array-field-element-assignment-crash/vb6/Module1.bas
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Attribute VB_Name = "Module1"
Option Explicit

' What VB6 does with a public array in a class: it refuses the declaration (see Holder.cls),
' so the project here uses a property with an index in its place, and assigns through it.
' Build and run it with: node scripts/bug_repro.mjs vb6 class-array-field-element-assignment-crash

Sub Main()
On Error GoTo Fail
Open App.Path & "\out.txt" For Output As #9
Dim h As New Holder
h.Values(1) = 5
Print #9, "h.Values(1) after the assignment: " & h.Values(1)
Close #9
Exit Sub
Fail:
On Error Resume Next
Print #9, "unhandled error " & Err.Number & ": " & Err.Description
Close #9
End Sub
9 changes: 9 additions & 0 deletions bugs/class-array-field-element-assignment-crash/vb6/Probe.vbp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Type=Exe
Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\Windows\SysWOW64\stdole2.tlb#OLE Automation
Module=Module1; Module1.bas
Class=Holder; Holder.cls
Startup="Sub Main"
ExeName32="Probe.exe"
Name="Probe"
CompilationType=0
OptimizationType=0
Binary file not shown.
17 changes: 17 additions & 0 deletions bugs/empty-parentheses-after-value/repro.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"mode": "run",
"expect": {
"exit": 0,
"output": [
"^Asc\\(s\\)\\(\\) \\[Long\\]: result=\\[97\\] error 0$",
"^Len\\(s\\)\\(\\) \\[Long\\]: result=\\[3\\] error 0$",
"^Twice\\(3\\)\\(\\) \\[Long\\]: result=\\[6\\] error 0$",
"^n\\(\\) \\[Long variable\\]: result=\\[5\\] error 0$",
"^s\\(\\) \\[String variable\\]: result=\\[abc\\] error 0$",
"^Chr\\$\\(65\\)\\(\\) \\[String\\]: result=\\[A\\] error 0$",
"^Chr\\(65\\)\\(\\) \\[Variant\\]: result=\\[\\] error -2147467259$",
"^VarLong\\(\\)\\(\\) \\[Variant\\]: result=\\[\\] error -2147467259$",
"^v\\(\\) \\[Variant variable\\]: result=\\[\\] error -2147467259$"
]
}
}
61 changes: 61 additions & 0 deletions bugs/empty-parentheses-after-value/src/Settings
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"configuration.inherits": "Defaults",
"project.appTitle": "EmptyParenthesesAfterValue",
"project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}",
"project.buildType": "Standard EXE",
"project.description": "Reproduces: Empty parentheses after a function result or a variable are accepted on a typed value and fail at run time on a Variant",
"project.exportPathIsV2": true,
"project.id": "{3A4CB941-FB0E-43CD-806A-6A875AA63D14}",
"project.name": "EmptyParenthesesAfterValue",
"project.optionExplicit": true,
"project.references": [
{
"id": "{00020430-0000-0000-C000-000000000046}",
"lcid": 0,
"name": "OLE Automation",
"path32": "C:\\Windows\\SysWOW64\\stdole2.tlb",
"path64": "C:\\Windows\\System32\\stdole2.tlb",
"symbolId": "stdole",
"versionMajor": 2,
"versionMinor": 0
},
{
"id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}",
"isCompilerPackage": true,
"licence": "MIT",
"name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)",
"path32": "",
"path64": "",
"publisher": "TWINBASIC-COMPILER",
"symbolId": "VB",
"versionBuild": 0,
"versionMajor": 0,
"versionMinor": 0,
"versionRevision": 31
},
{
"id": "{C192FB39-64CA-4D9B-B477-A5502F48EFCC}",
"isCompilerPackage": true,
"licence": "MIT",
"name": "[COMPILER PACKAGE] twinBASIC - App global class object",
"path32": "",
"path64": "",
"publisher": "TWINBASIC-COMPILER",
"symbolId": "AppGlobalClassProject",
"versionBuild": 0,
"versionMajor": 1,
"versionMinor": 0,
"versionRevision": 0
}
],
"project.settingsVersion": 1,
"project.startupObject": "Sub Main",
"project.warnings": {
"errors": [],
"hints": [],
"ignored": [],
"info": [],
"warnings": []
},
"runtime.useUnicodeStandardLibrary": true
}
54 changes: 54 additions & 0 deletions bugs/empty-parentheses-after-value/src/Sources/Startup.twin
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
' Empty parentheses after a function result or a variable, as in Num = Asc(c)() or
' G = Chr(Num)(). twinBASIC compiles every line below. Where the value is of a type other
' than Variant, the parentheses are ignored and the value is used (Asc(c)() is Asc(c)).
' Where it is a Variant, the parentheses fail when the line runs, with error -2147467259,
' "Unspecified error". VB6 refuses the first group when it compiles ("Type mismatch",
' "Syntax error" or "Expected array") and raises error 9, "Subscript out of range", for the
' second (the VB6 side is in vb6/).

Module Startup

Private Function Twice(ByVal n As Long) As Long
Twice = n * 2
End Function

Private Function VarLong() As Variant
VarLong = 7&
End Function

Private Sub Report(ByVal Label As String, ByVal Result As Variant)
Debug.Print Label & ": result=[" & Result & "] error " & Err.Number
Err.Clear
End Sub

Public Sub Main()
Dim s As String = "abc"
Dim n As Long = 5
Dim v As Variant = 6
Dim t As Variant
On Error Resume Next

' A value that is not a Variant: the parentheses are ignored.
t = Empty: t = Asc(s)()
Report "Asc(s)() [Long]", t
t = Empty: t = Len(s)()
Report "Len(s)() [Long]", t
t = Empty: t = Twice(3)()
Report "Twice(3)() [Long]", t
t = Empty: t = n()
Report "n() [Long variable]", t
t = Empty: t = s()
Report "s() [String variable]", t
t = Empty: t = Chr$(65)()
Report "Chr$(65)() [String]", t

' A Variant: the parentheses raise an error.
t = Empty: t = Chr(65)()
Report "Chr(65)() [Variant]", t
t = Empty: t = VarLong()()
Report "VarLong()() [Variant]", t
t = Empty: t = v()
Report "v() [Variant variable]", t
End Sub

End Module
Loading
Loading