diff --git a/.gitignore b/.gitignore index 62cb1f9b0..0ce436d08 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,10 @@ indexer/.packages/ # docs/assets/fonts/ -- only the archives are throwaway. /.font-cache/ +# The help add-in's documentation archive is built by scripts/build_help_archive.mjs +# from docs/_site-offline, and is too large to commit. +/add-in/Resources/HELP/ + # A bug reproducer's zip is only for attaching to its GitHub issue (see # BUGS-TO-REPORT.md); the .twinproj beside it is committed. /bugs/**/*.zip diff --git a/BUGS-TO-REPORT.md b/BUGS-TO-REPORT.md index 7cc82db9e..f30e3634f 100644 --- a/BUGS-TO-REPORT.md +++ b/BUGS-TO-REPORT.md @@ -127,11 +127,15 @@ node scripts/impexp.mjs import bugs//.twinproj bugs//src --ove `bugs//repro.json` says how to ask the compiler about the entry, and is committed with the reproducer: a `mode` of `compile`, `build`, `run`, `cli` (the compiler executable's own -command line) or `manual`, and what a reproduction looks like in `expect`, such as the exit -code of `tbbuild`, the diagnostic codes, or a regular expression the output must match. -`verify` reads it for every entry and reports `reproduces`, `NO LONGER REPRODUCES` (the bug -may be fixed on this build) or `manual`, which prints the `steps` it holds. It needs a -twinBASIC install, and is run by a person, never by a gate or by CI. +command line), `lane` (a lane of `ide-test.bat` or `addin-test.bat` and the tests in it that +assert the bug), `probe` (a script under `scripts/`) or `manual`, and what a reproduction +looks like in `expect`, such as the exit code of `tbbuild`, the diagnostic codes, or a +regular expression the output must match. A bug that needs the IDE operated gets a lane +test rather than `manual` steps, so that no entry needs a person at the IDE; `steps` still +holds the recipe for the issue. `verify` reads it for every entry and reports `reproduces`, +`NO LONGER REPRODUCES` (the bug may be fixed on this build) or `manual`, which prints the +`steps` it holds. It needs a twinBASIC install, and is run by a person, never by a gate or +by CI. **A VB6 comparison is a project of its own in `vb6/`**, made by `new "" --with-vb6` from the template in `test/repro-templates/vb6/`. By convention `Probe.vbp` builds `Probe.exe`, @@ -209,7 +213,7 @@ What does not reproduce it: So it takes at least one existing entry and at least one slot with nothing in it, which looks like each slot being read with the previous slot's value as its default. Once a duplicate is there, the next opened project keeps it: 18 copies of one project became 20 after one more open, with the new project on top. Both variants of step 2 (deleted slots, empty strings) were measured on BETA 995, ending with 21 values and 3 distinct projects. - + --- @@ -257,7 +261,7 @@ So exit 0 on a project with errors happens only for an error in code nothing cal Silence on stdout and stderr: measured on BETA 983 first, and on BETA 995 by redirecting the standard handles of the process to a file. The same redirection captures the compiler executable's own output (`twinBASIC_win32.exe settings ` wrote the whole `Settings` file), so the capture works. - + --- @@ -814,7 +818,7 @@ Each build writes its type library and succeeds, however many IDEs share the `TE **Additional context** Severity: low; the build passes when repeated, and an IDE run by a person rarely builds at the same moment as another. It affects tools that build several projects at once. The compiler imports `GetTempFileNameW`, and its messages show that it writes the type library to a file and reads it back (`[TYPELIB] failed to read in generated type library file` is the message beside this one). A temporary file name that two processes both use would explain the counts; that is an inference, not observed. The temp folder is empty after the builds, so whatever is written there is deleted. - + --- @@ -867,7 +871,7 @@ What was tried: - An interface with three `[PreserveSig]` members declared in `IUnknown`'s order, `QueryInterface`, `AddRef` and `Release`, behaves the same way: `Set` succeeds, and the calls go to the wrong slots (one `AddRef` returned 0, and the next call crashed). - `Interface IUnk Extends stdole.IUnknown` with that identifier compiles, and `u.AddRef` is then reported as `TB5027 Unrecognized member 'AddRef' on type 'IUnk'`, as it is for `stdole.IUnknown` itself, which has no members that twinBASIC code can call. - + --- @@ -1085,7 +1089,7 @@ Severity: low for a program that passes all the arguments; for one that does not Related and also different from VB6: `Err.Raise 65536` is accepted, and `Err.Number` is 65536, where VB6 raises error 5. `Err.Raise 0` raises error 5 in both. An explicit empty string for the source or the description gives an empty `Source` or `Description`, in VB6 as well. `Err.HelpContext` is 0 in twinBASIC, and VB6 sets it to 1000000 plus the number for a `Raise` without one (`1000005` for 5). - + --- @@ -1122,7 +1126,7 @@ The two symptoms are one bug because the object is the same: `ObjPtr` of what `G What does not reproduce it: a fresh thread (`GetErrorInfo` returns `S_FALSE`); a caller that makes the call through an interface whose methods are declared `[PreserveSig]` and reads the slot itself, where the first `GetErrorInfo` returns the values and the second returns `S_FALSE`; the second `GetErrorInfo` after the object has been read from the slot (it returns `S_FALSE`, so `GetErrorInfo` does empty the slot). Severity: low. The slot is not a documented twinBASIC interface, but a program that reads error information with the COM functions, or a library that does, sees values that change under it, and a program cannot rely on the description of a failure that came with no error information. - + --- @@ -1167,7 +1171,7 @@ What was tried: an object that is not a sink gives an ordinary error (`E_NOINTER VB6 crashes too. The same call on the connection point of a VB6 class with an event, made through `DispCallFunc` (the VB6 project is attached as `advise-nothing-crashes-vb6.zip`), ends the exe with an access violation (`0xC0000005` in `MSVBVM60.DLL`, in the Application event log) after it writes `before`. A fix therefore costs no compatibility: no program depends on the crash. - + --- @@ -1212,7 +1216,7 @@ before Unadvise 0 Severity: low. A client that releases a connection twice, or with a wrong cookie, is told it worked. `Advise` with a sink that does not answer `QueryInterface` for the outgoing interface fails with `E_NOINTERFACE` (`&H80004002`), where the Windows SDK page for [IConnectionPoint::Advise](https://learn.microsoft.com/en-us/windows/win32/api/ocidl/nf-ocidl-iconnectionpoint-advise) says the method "must return CONNECT_E_CANNOTCONNECT" (`&H80040201`). VB6 returns `E_NOINTERFACE` too, so that is not reported here; the reproducer prints it as a control. A sink that is an ordinary twinBASIC class gives the same `E_NOINTERFACE` whether it has no members or implements `IDispatch` (the outgoing interface's identifier is generated for each build, so a twinBASIC class cannot answer for it). Nothing is connected afterwards, and no event reaches the sink. Calling `Unadvise` a second time with the cookie of the failed `Advise`, which is 0, also returns without an error. - + --- @@ -1417,7 +1421,7 @@ The same difference exists for the high end of the index range: VB6 draws `&H800 The change is `TranslateColor2` in the VB package's `Graphics.twin`, which `Line`, `Circle` and `PSet` call on `Color`: for a top byte of `&H80` it returns the system colour when the value minus `&H80000000` is 0 to 30 and 0 otherwise, and for any other value it returns the low 24 bits. - + --- @@ -1428,7 +1432,7 @@ Assigning a value that is not a colour to a colour property does not leave the p **To Reproduce** Steps to reproduce the behavior: -1. Open `forecolor-invalid-value-stored.twinproj` (attached as `forecolor-invalid-value-stored.zip`). Its `Sub Main` loads `Form1`, which holds a PictureBox, a Label, a TextBox, a CommandButton and a user control, UC1, whose `RunCases` assigns its own colour properties. For each property tried it sets `vbGreen`, then assigns `-1` (and `&H8000001F` to the form's `ForeColor`) under `On Error Resume Next` and prints the error number and the value read back. The Printer lines need a default printer; nothing is printed. +1. Open `forecolor-invalid-value-stored.twinproj` (attached as `forecolor-invalid-value-stored.zip`). Its `Sub Main` loads `Form1`, which holds a PictureBox, a Label, a TextBox, a CommandButton and a user control, UC1, whose `RunCases` assigns its own colour properties; it then loads `MyMDIForm`, an MDIForm, assigns the same values to its `BackColor`, shows it and assigns them again. For each property tried it sets `vbGreen`, then assigns `-1` (and `&H8000001F` to the form's `ForeColor`) under `On Error Resume Next` and prints the error number and the value read back. The Printer lines need a default printer; nothing is printed. 2. Run it (F5) and read the DEBUG CONSOLE: ``` Form.ForeColor = -1: Err 5, reads FFFFFFFF @@ -1448,6 +1452,10 @@ Steps to reproduce the behavior: UserControl.ForeColor = -1: Err 5, reads FFFFFFFF UserControl.BackColor = -1: Err 0, reads FFFFFFFF UserControl.FillColor = -1: Err 0, reads FFFFFFFF + MDIForm.BackColor = -1: Err 0, reads FFFFFFFF + MDIForm.BackColor = &H8000001F: Err 0, reads 8000001F + MDIForm (shown).BackColor = -1: Err 5, reads FFFFFFFF + MDIForm (shown).BackColor = &H8000001F: Err 5, reads 8000001F ``` **Expected behavior** @@ -1470,6 +1478,10 @@ Printer.FillColor = -1: Err 380, reads FF00 UserControl.ForeColor = -1: Err 380, reads FF00 UserControl.BackColor = -1: Err 380, reads FF00 UserControl.FillColor = -1: Err 380, reads FF00 +MDIForm.BackColor = -1: Err 380, reads FF00 +MDIForm.BackColor = &H8000001F: Err 380, reads FF00 +MDIForm (shown).BackColor = -1: Err 380, reads FF00 +MDIForm (shown).BackColor = &H8000001F: Err 380, reads FF00 ``` `FF00` is `vbGreen`, which each property held before the assignment. A program that sets a colour from user input and handles the error then keeps the colour it had. @@ -1482,7 +1494,7 @@ Severity: low. `ForeColor` raises the error on the surfaces and the other proper What was tried: the same lines on BETA 983 print the same, so this is not a change of BETA 984. A value that is a valid colour, such as `&H100FF00` or `&H20000FF`, is accepted by `ForeColor` with no error, as in VB6. Besides the attached project, each of `-1`, `&H8000001F` and `&HFF0000FF` was assigned to every colour property of a UserControl (placed on the form), CheckBox, Frame, ListBox, Shape, ComboBox, DirListBox, DriveListBox, FileListBox, OptionButton and OLE in a project of its own, on BETA 995 and 983 and in VB6: the results are the same as above, error 5 for the UserControl's `ForeColor` and none for the rest in twinBASIC, error 380 and the old value kept in VB6. The twinBASIC-only controls behave the same (`QRCode.ForeColor` raises error 5, `CheckMark.BackColor` and `MultiFrame.BackColor` raise nothing), and so do `CommandButton.ForeColor` and the Data control's colours, which VB6 has no counterpart or no loadable project for. The package source explains the pattern: the colour properties are plain stored fields (`Public ForeColor As OLE_COLOR` and so on, in `GraphicsBase` for Form, PictureBox, UserControl, PropertyPage and Report, and declared again in each control), with nothing that validates the value; the error 5 comes from the `ForeColor` change handler of `GraphicsBase`, which runs after the value is stored. - + --- @@ -1517,72 +1529,7 @@ Severity: low to medium. Typing into the box was not tried; the gap is the assig What was tried: BETA 983 prints the same two lines. Assigning the same long text a second time raises no `Change` event, in twinBASIC and in VB6. - - ---- - -## `LSet` on a `Long` variable compiles, and the build fails with a codegen error - -**Describe the bug** -`LSet` is defined for a string or a user-defined type. A statement `LSet n = "ab"` with `n` declared `As Long` is accepted by the compiler without a diagnostic, and building the project then fails: the linker reports a code-generation error at the statement, and no exe is produced. Run from the IDE (F5), the project stops the same way. Observed in a build of the reproducer project. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `lset-long-codegen-error.twinproj` (attached as `lset-long-codegen-error.zip`). Its one source file, `Startup.twin`, is a `Sub Main` with `Dim n As Long` and `LSet n = "ab"`. -2. Compile it: no errors, warnings or hints. -3. Build it (Build, or F5) and read the DEBUG CONSOLE: - ``` - [BUILD] Starting... - [LINKER] compilation (codegen) error detected in 'Startup.Mainrootmain' at line #5 - [LINKER] FAILED due to compilation errors 'LsetLongCodegenError_win32.exe' - [BUILD] failed - ``` - -**Expected behavior** -A compile error on the `LSet` line, as for the neighbouring wrong operands: `LSet s = a` with `a` a user-defined type and `RSet a = b` with user-defined types are refused with TB5001 (*unable to convert type ... to String*), and `LSet a = s` with a string source and a user-defined type destination is refused with TB5249. VB6 refuses this statement at compile time with *LSet allowed only on strings and user-defined types* (attached as `lset-long-codegen-error-vb6.zip`, which does not build). - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -Severity: low. The error is reported, so nothing runs wrongly, but it points at no cause, and the compile step that the IDE shows while typing says the project is fine. - -Tried with a `Long` destination only. The same on BETA 983. - - - ---- - -## `LSet` and `RSet` on an element of a `Variant` that holds an array change nothing - -**Describe the bug** -`LSet v(0) = "zz"` and `RSet v(1) = "zz"` do nothing when `v` is a `Variant` that holds an array, whether the array came from `Array(...)` or from `ReDim v(0 To 1)` on the `Variant`. The statements raise no error, and the element keeps its value. The same statements on an element of an array declared `Dim w(0 To 1) As Variant` work. Observed in a run of the reproducer project. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `lset-variant-array-element-noop.twinproj` (attached as `lset-variant-array-element-noop.zip`). Its one source file, `Startup.twin`, has a `Sub Main` that assigns `v = Array("0123456789", "abcde")`, runs `LSet v(0) = "zz"` and `RSet v(1) = "zz"`, and does the same to the elements of `Dim w(0 To 1) As Variant`. -2. Run it (F5) and read the DEBUG CONSOLE: - ``` - LSet v(0), v = Array(...): [0123456789] - RSet v(1), v = Array(...): [abcde] - LSet w(0), w(0 To 1) As Variant: [zz ] - RSet w(1), w(0 To 1) As Variant: [ zz] - ``` - -**Expected behavior** -The elements of `v` change as those of `w` do: `[zz ]` and `[ zz]`. VB6 does so (attached as `lset-variant-array-element-noop-vb6.zip`). - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -Severity: low to medium. A statement that has no effect and no error is easy to miss. - -Tried: `ReDim v2(0 To 1)` on a `Variant`, then `LSet v2(0) = "zz"`, does the same. The `w` elements and `Dim a(0 To 2) As String` elements work. On BETA 983 the `v` lines read the same, and the `w` line for `LSet` reads `[zz23456789]`, because `LSet` did not yet fill with spaces there (fixed in BETA 984, which is a separate matter). - - + --- @@ -1704,3 +1651,652 @@ In the default mode, MODERN, the console asks for the list after every key and a Severity: moderate. `Print`, `Call` and `Set` begin lines that are often typed in the console, and each becomes a line the console refuses. The only ways round it are Escape before the space and another IntelliSense mode. + +--- + +## A `[RunAfterBuild]` Sub does not run, and nothing says so, when a module is named like the project + +**Describe the bug** +When a module has the same name as the project, a `[RunAfterBuild]` Sub does not run, and nothing says so. The build succeeds and the exe is written. The DEBUG CONSOLE's last line is `[BUILD] Executing '..'...`, and nothing follows it: no message box, no entry in the error panel, no diagnostic, and the status bar is as it is after a run that works. The names are compared without regard to letter case, so `ProbeWS` and `probews` clash as well. Even an empty module with the project's name, beside the module that holds the Sub, stops it. Observed in the DEBUG CONSOLE after pressing Build. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `module-named-like-project.twinproj` (attached as `module-named-like-project.zip`). The project is named `ModuleNamedLikeProject` and has one module of that name: + ``` + Module ModuleNamedLikeProject + Public Sub Main() + End Sub + [RunAfterBuild] + Public Sub Hello() + Debug.Cls + Debug.Print "RunAfterBuild ran" + End Sub + End Module + ``` +2. Build the project. +3. Read the DEBUG CONSOLE. It ends at `[BUILD] Executing 'ModuleNamedLikeProject.ModuleNamedLikeProject.Hello'...`. `RunAfterBuild ran` is never printed. +4. Rename the module, to `Runner` for example, and build again. The console is cleared and holds `RunAfterBuild ran`. + +**Expected behavior** +The Sub runs whatever the modules are called, as it does once the module is renamed. If a module may not have the project's name, the compiler should refuse it with an error, as VB6 does, and not build the project and skip the Sub in silence. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 997 + +**Additional context** +Also on BETA 995, identically. + +VB6 refuses a project whose module has the project's name. Its build stops with `Name conflicts with existing module, project, or object library`, for a module that holds `Sub Main` (the project is attached as `module-named-like-project-vb6.zip`) and for an empty module of that name beside the module that holds `Sub Main` (checked, not attached). So VB6 reports the clash, where twinBASIC accepts it and skips the Sub. + +What does not reproduce it: with any other module name, such as `Runner`, the Sub runs. A project with no module of the project's name runs it, whichever module holds it. + +What reproduces it besides the plain case: the clash in another letter case, such as a project `ProbeWS` with a module `probews`; and an empty module with the project's name in a project whose `[RunAfterBuild]` Sub is in a different module, `Runner`, where the console ends at `[BUILD] Executing 'ProbeWS.Runner.'...`, which names an unambiguous Sub. So the cause is the module's name, not a call that cannot be told apart. + +Severity: moderate. A `[RunAfterBuild]` Sub runs after the exe is built, for example to sign it, and here it is skipped while the build reports success. Nothing points at the module's name as the cause, so the project's owner has to find it by renaming modules. + + + +--- + +## A `[RunAfterBuild]` Sub does not run, and nothing says so, when its module holds a procedure named like the module + +**Describe the bug** +When a module holds a procedure with the same name as the module, a `[RunAfterBuild]` Sub in that module does not run, and nothing says so. The build succeeds and the exe is written. The DEBUG CONSOLE's last line is `[BUILD] Executing '..'...`, and nothing follows it: no message box, no entry in the error panel, no diagnostic. Not even the first statement of the Sub runs. The names are compared without regard to letter case, so `Runner` and `runner` clash. A Sub, a Function and a `Private` Sub all cause it, and the procedure does not have to be called. Observed in the DEBUG CONSOLE after pressing Build. + +This is a sibling of "A `[RunAfterBuild]` Sub does not run, and nothing says so, when a module is named like the project". There the module and the project share a name; here the project's name plays no part, and the clash is inside one module. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `sub-named-like-module.twinproj` (attached as `sub-named-like-module.zip`). The project is named `SubNamedLikeModule`. It has a `Startup` module with an empty `Sub Main`, and this module: + ``` + Module Probe + [RunAfterBuild] + Public Sub Hello() + Debug.Cls + Debug.Print "RunAfterBuild ran" + End Sub + + Public Sub Probe() + End Sub + End Module + ``` +2. Build the project. +3. Read the DEBUG CONSOLE. It ends at `[BUILD] Executing 'SubNamedLikeModule.Probe.Hello'...`. `RunAfterBuild ran` is never printed. +4. Rename `Sub Probe` to `NotProbe`, or move it to another module, and build again. The console is cleared and holds `RunAfterBuild ran`. + +**Expected behavior** +The Sub runs whatever the other procedures in its module are called, as it does once `Probe` is renamed. If a procedure may not have its module's name, the compiler should refuse it with an error, and not build the project and skip the Sub in silence. Writing `Probe.Probe` in code is already refused, with TB5027. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 997 + +**Additional context** +Also on BETA 983, identically, so this is not a recent regression. + +VB6 accepts the name. A project named `SubNamedLikeModule` with a standard module `Probe` (`Attribute VB_Name = "Probe"`) that holds `Public Sub Probe()` builds, and `Sub Main` calls `Probe` and it runs (the project is attached as `sub-named-like-module-vb6.zip`; it printed `Sub Probe ran, in a module named Probe`). VB6 has no `[RunAfterBuild]`, so what is checked is that the module and the Sub can coexist. + +What does not reproduce it: a procedure with another name, such as `NotProbe`. A procedure named like a module, when the module that holds it is a different one from the module that holds the `[RunAfterBuild]` Sub (a `Runner` module holding the Sub, and an `Other` module holding `Sub Other`). + +What reproduces it besides the plain case: a `Function Probe`, a `Private Sub Probe`, and the clash in another letter case (`Module Runner` with `Sub runner`). The failure needs no help from any tool: the Sub is the one marked `[RunAfterBuild]`, and its body never starts. + +Severity: moderate. A `[RunAfterBuild]` Sub runs after the exe is built, for example to sign it, and here it is skipped while the build reports success. Nothing points at the clash as the cause, so the project's owner has to find it by renaming procedures. + + + +--- + +## A declaration that names two calling conventions compiles without a diagnostic + +**Describe the bug** +An API declaration, a procedure, a delegate or an interface member can name its calling convention after its name, and the compiler accepts two conventions on one declaration, in either order, with no error or warning. The call then uses one of them. In every pair tried on a `Declare`, `CDecl` is dropped and the other keyword is used, whichever comes first. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `two-calling-conventions-accepted.twinproj` (attached as `two-calling-conventions-accepted.zip`). Its `Startup.twin` declares `RtlUlongByteSwap`, which `ntdll` exports as fastcall on a 32-bit build (its one argument in ECX), under several conventions: + ``` + Private Declare PtrSafe Function SwapFast FastCall Lib "ntdll" Alias "RtlUlongByteSwap" (ByVal Source As Long) As Long + Private Declare PtrSafe Function SwapCdecl CDecl Lib "ntdll" Alias "RtlUlongByteSwap" (ByVal Source As Long) As Long + Private Declare PtrSafe Function SwapFastCdecl FastCall CDecl Lib "ntdll" Alias "RtlUlongByteSwap" (ByVal Source As Long) As Long + Private Declare PtrSafe Function SwapCdeclFast CDecl FastCall Lib "ntdll" Alias "RtlUlongByteSwap" (ByVal Source As Long) As Long + Private Declare PtrSafe Function SwapThisCdecl ThisCall CDecl Lib "ntdll" Alias "RtlUlongByteSwap" (ByVal Source As Long) As Long + Private Declare PtrSafe Function SwapCdeclThis CDecl ThisCall Lib "ntdll" Alias "RtlUlongByteSwap" (ByVal Source As Long) As Long + ``` + It also holds a delegate (`CDecl FastCall`), a procedure in a module (`CDecl ThisCall`) and an interface member (`ThisCall CDecl`), each naming two conventions. `Sub Main` prints the result of each `Declare` for `&H11223344`. +2. Compile it. The Problems panel reports 0 errors and 0 warnings. +3. Run it (win32). The DEBUG CONSOLE prints: + ``` + FastCall: 44332211 + FastCall CDecl: 44332211 + CDecl FastCall: 44332211 + ThisCall CDecl: 44332211 + CDecl ThisCall: 44332211 + CDecl: 1EEC6F00 + ``` + The last value changes from run to run: called as cdecl, the function swaps whatever ECX held. Every pair is called as fastcall or thiscall, which pass this one argument the same way. + +**Expected behavior** +A declaration has one calling convention, so the second keyword is refused with an error that names it. As it is, nothing says which of the two the call uses, and when one of them is `CDecl`, it is dropped without a word. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 997 + +**Additional context** +Also on BETA 995, identically apart from the cdecl value. + +Only the `Declare` pairs are observed at run time. The delegate, the procedure and the interface member are only compiled, so which convention they use is not known. Not tried: `FastCall` with `ThisCall`, which this function cannot tell apart, and a 64-bit build, where the conventions have no effect. + +Severity: low. It takes a mistake to trigger it, but the mistake goes unreported, and a declaration meant as cdecl that names a second convention by accident calls the function the wrong way. + + + +--- + +## `raiseEvent` in a tool window's own HTML throws a TypeError, and the add-in's listener is not called + +**Describe the bug** +An add-in sets a tool window's HTML with an inline handler that calls `raiseEvent`, as the list-view items of Sample 15 do, and registers a listener for the event with `AddEventListener` on the parent element. Clicking the element does not call the listener, and the add-in is told nothing. The IDE's page throws `TypeError: Cannot read properties of null (reading 'rootEventHandler')`: `raiseEvent` goes up through `parentNode` looking for an element that has `rootEventHandler`, and in plain tool-window HTML no element has one, so it goes past the document. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `raiseevent-plain-html-typeerror.twinproj` (attached as `raiseevent-plain-html-typeerror.zip`). It is an add-in, and its tool window is built like this: + ``` + With Pane.RootDomElement.ChildDomElements.Add("box", "div") + .AddEventListener("myEvent", AddressOf MyEvent) + .Properties.innerHTML = _ + "[raise] " & _ + "[direct]" + End With + ``` + `MyEvent` prints `myEvent from ` to the DEBUG CONSOLE. +2. Build it (win32). Copy `Build\RaiseeventPlainHtmlTypeerror_win32.dll` into `%APPDATA%\twinBASIC\addins\win32\`, restart the IDE and open any project. +3. Click the toolbar button **raiseEvent repro**. A tool window opens with **[raise]** and **[direct]**. +4. Click **[raise]**. Nothing is printed, and the IDE's page has the uncaught `TypeError` above. +5. Click **[direct]**. The DEBUG CONSOLE prints `myEvent from direct`. + +**Expected behavior** +Step 4 prints `myEvent from raise`: `raiseEvent` calls the listener the div registered, as calling it directly does in step 5. If `raiseEvent` is meant only for list views and Monaco widgets, it stops at the document without throwing. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 997 + +**Additional context** +Also on BETA 995. + +The workaround is step 5's: an inline handler that calls the function `AddEventListener` stores on the element under the event's name. + +Severity: low. A workaround exists, but nothing tells the add-in's author why the listener is not called: the exception shows only in the IDE's own page. + + + +--- + +## Setting a property whose name starts with `on` through `HtmlElementProperties` does nothing, and raises no error + +**Describe the bug** +An add-in that sets an event-handler property of a tool-window element through `HtmlElementProperties`, such as `.Properties.onclick = "..."`, gets no error, and the element has no handler: a click does nothing, and the element has neither an `onclick` property nor an `onclick` attribute. The same handler written inline in `innerHTML` runs. The IDE page's property setter skips any property whose last name starts with `on`, with an empty branch (`if(a.indexOf("on")==0){}`), and reports success. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `on-property-ignored.twinproj` (attached as `on-property-ignored.zip`). It is an add-in, and its tool window is built like this: + ``` + With .Add("property", "div") + .Properties.innerText = "[property]" + On Error Resume Next + .Properties.onclick = "this.innerText = 'clicked'" + Host.DebugConsole.PrintText "onclick set, error " & Err.Number + On Error GoTo 0 + End With + With .Add("inline", "div") + .Properties.innerHTML = "[inline]" + End With + ``` +2. Build it (win32). Copy `Build\OnPropertyIgnored_win32.dll` into `%APPDATA%\twinBASIC\addins\win32\`, restart the IDE and open any project. +3. Click the toolbar button **on property repro**. A tool window opens with **[property]** and **[inline]**, and the DEBUG CONSOLE prints `onclick set, error 0`. +4. Click **[property]**. Nothing happens. +5. Click **[inline]**. Its text changes to `clicked`. + +**Expected behavior** +Either the property is set, as every other property is, and step 4 changes the text; or, if handler properties are refused on purpose, the assignment raises an error that says so, and step 3 prints a nonzero error. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 997 + +**Additional context** +Also on BETA 995. + +Only `onclick` was tried. The setter's test is on the name, so it applies to every name that starts with `on`. + +Severity: low. `AddEventListener` and inline handlers work, but an add-in that sets a handler property gets neither the handler nor an error. + + + +--- + +## LLVM-compiled code raises no error when a `Double` overflows, where it raises error 6 otherwise + +**Describe the bug** +In a procedure compiled with LLVM, a `Double` addition, subtraction, multiplication or exponentiation whose result is too large for a `Double` raises no error: `Err.Number` stays 0 and the target holds infinity. The same statement in a procedure compiled without LLVM raises error 6 (*Overflow*), as VB6 does. A program that relies on error 6 to detect an overflow gets a silent `Inf` instead. No project setting and no other attribute is involved: the one difference is `[CompilerOptions("+llvm")]` on the procedure, or `tbrun --llvm` on the whole project. Observed in a run of the reproducer project. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `llvm-double-overflow-no-error.twinproj` (attached as `llvm-double-overflow-no-error.zip`). It needs an LLVM licence (Ultimate): `LlvmCases` is compiled with LLVM by its attribute. `PlainCases` holds the same statements without it. Each runs under `On Error Resume Next`, for example: + ``` + [CompilerOptions("+llvm")] + Private Sub LlvmCases() + Dim big As Double = 1E+308 + Dim r As Double + On Error Resume Next + r = 5#: Err.Clear: r = big * 10# + Debug.Print "LLVM Double * Err=" & Err.Number & " r=" & r + End Sub + ``` +2. Run it (F5) and read the DEBUG CONSOLE: + ``` + plain Double * Err=6 r=1.#INF + plain Double + Err=6 r=1.#INF + plain Double ^ Err=6 + plain Double / Err=6 r=1.#INF + plain 1# / 0# Err=11 r=1.#INF + plain Long + Err=6 l=5 + LLVM Double * Err=0 r=1.#INF + LLVM Double + Err=0 r=1.#INF + LLVM Double ^ Err=0 + LLVM Double / Err=6 r=5 + LLVM 1# / 0# Err=11 r=5 + LLVM Long + Err=6 l=5 + ``` + +**Expected behavior** +Error 6 in LLVM-compiled code for every `Double` overflow, as in the same code without LLVM and in VB6 (attached as `llvm-double-overflow-no-error-vb6.zip`, which prints `Err=6` for the multiplication, the addition, the exponentiation and the division, and `Err=11` for `1# / 0#`). + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 997 + +**Additional context** +Also on BETA 995, identically, and on win32 and win64. Running the reproducer with `--llvm` compiles every procedure with LLVM and gives the `LLVM` results for both halves. With `[FloatingPointErrorChecks(True)]` on the LLVM procedure the multiplication still raises nothing. + +What does not reproduce it: a `Double` division that overflows (`1E+308 / 0.1`) raises 6, and so does `Exp(1000#)`, a `Long` overflow (`2147483647 + 1`) raises 6, and `1# / 0#` raises 11, all in LLVM-compiled code. So the overflow check is missing from the addition, subtraction, multiplication and `^` operators, not from the error path. + +A second difference shows in the same output. After a division that raises an error, LLVM-compiled code leaves the target unassigned: `r` is still 5 after `1# / 0#` (error 11) and after `1E+308 / 0.1` (error 6). Without LLVM the target is assigned infinity, `1.#INF`, and so it is in VB6 (`r=1.#INF` after each of its errors). Under `On Error Resume Next` the two builds then continue with different values. + +A `Single` addition, subtraction, multiplication or division that overflows raises no error without LLVM, where VB6 raises 6 (with LLVM the first three raise none either, and the division raises 6). That is a separate defect, with its own reproducer, in the entry "A `Single` that overflows raises no error, where VB6 raises error 6" (`bugs/single-overflow-no-error/`). + +Severity: a silent wrong result. An overflow that raises 6 in a debug run and no error in the LLVM-compiled build changes what a program computes, and nothing marks the difference. + + + +--- + +## A `Single` that overflows raises no error, where VB6 raises error 6 + +**Describe the bug** +When a `Single` addition, subtraction, multiplication or division produces a result too large for a `Single`, twinBASIC raises no error and the target holds infinity: `s = sbig + sbig` with `sbig` at 3E+38 leaves `Err.Number` at 0 and `s` at `1.#INF`. VB6 raises error 6 (*Overflow*) for the same statement. The same holds for `+=` and `*=`, for `Single * Long` and for some `Single` constant expressions. A `Double` overflow raises 6, so a `Single` and a `Double` are treated differently. Observed in a run of the reproducer project. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `single-overflow-no-error.twinproj` (attached as `single-overflow-no-error.zip`). Its `Sub Main` runs each case under `On Error Resume Next`, for example: + ``` + Dim sbig As Single = 3E+38! + Dim s As Single + On Error Resume Next + s = 5!: Err.Clear: s = sbig + sbig + Debug.Print "Single + Err=" & Err.Number & " s=" & s + ``` +2. Run it (F5) and read the DEBUG CONSOLE: + ``` + Single + Err=0 s=1.#INF + Single - Err=0 s=-1.#INF + Single * Err=0 s=1.#INF + Single / Err=0 s=1.#INF + Single * Long Err=0 s=1.#INF + s = s + x Err=0 s=1.#INF + s += x Err=0 s=1.#INF + s = s * x Err=0 s=1.#INF + s *= x Err=0 s=1.#INF + CSng(Double) Err=6 s=5 + Single ^ Err=6 s=5 + Variant * Err=0 Double + Variant + Err=0 Double + ``` + +**Expected behavior** +Error 6 for every `Single` arithmetic result that does not fit a `Single`, as in VB6 (attached as `single-overflow-no-error-vb6.zip`, which prints `Err=6 s=1.#INF` for `+`, `-`, `*`, `/`, `Single * Long`, `s = s + x` and `s = s * x`). The target is `1.#INF` after the error in both, so only the error is missing. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 997 + +**Additional context** +Also on BETA 995, identically, and on win64 (which prints `inf` where win32 prints `1.#INF`). Compiled with LLVM, `+`, `-`, `*` and the compound forms are the same, and `/` raises 6, as in VB6. + +What matches VB6: a `Variant` holding a `Single` that overflows becomes a `Double` variant, with no error, in both (`Variant *`, `Variant +` above). `CSng` of a `Double` that is too large raises 6 in both. The result of `Single * Long` is a `Double` in both (`TypeName`), though twinBASIC stores it into a `Single` without an error. + +Where twinBASIC differs from VB6 in the other direction: `s = sbig ^ 2!` raises 6 in twinBASIC and gives `1.#INF` with no error in VB6, and so does assigning a `Double` that is too large to a `Single` (`s = 1E+300`: error 6 in twinBASIC, `1.#INF` with no error in VB6). + +A `Single` constant expression that overflows is refused by VB6 at compile time with *Overflow*. twinBASIC compiles some forms (`Const A As Single = 3E+38! * 10!`, and the `+`, `-` and `/` forms, in a project of their own) and refuses others with TB5002 (`^`, `CSng(1E+300)`, and `1E+300` assigned to a `Single`); which constants it refuses also changed with the other constants in the project, so that part is not in the reproducer. + +Severity: a silent wrong result. A `Single` overflow that ends the program or reaches a handler in VB6 gives `Inf` and carries on in twinBASIC. + + + +--- + +## A module-level array `Const` with an element that does not fit its type crashes the compiler + +**Describe the bug** +An array `Const` at module level whose element is too large for the element type takes the compiler down: `Const Big() As Long = Array(2147483648)`. The IDE's compile restarts the compiler, which crashes again, and the project can neither be compiled nor run. The crash happens whether or not the constant is used. Observed with `tbbuild`, which reports "the compiler crashed 3x". + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `array-const-element-overflow-crash.twinproj` (attached as `array-const-element-overflow-crash.zip`). Its one module is: + ``` + Module Startup + Const Big() As Long = Array(2147483648) + Public Sub Main() + End Sub + End Module + ``` +2. Compile it. The compiler crashes. + +**Expected behavior** +A compile error, as for a scalar constant: `Const K As Long = 2147483648` is refused with TB5002, and `Const Small() As Integer = Array(32768)` with TB5001. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 997 + +**Additional context** +BETA 995 has no array constants: every array `Const` is refused there with TB5245, *array cannot be initialized here*. VB6 has no array constants either (`Const A() As Long = Array(1)` is a syntax error), so there is no VB6 project. + +What crashes, each in a module of its own, unused or used: `Array(2147483648)`, `Array(2147483648#)`, `Array(3000000000.5)` and `Array(1, 2147483648)` for `Long`; `Array(40000.5)` for `Integer`; `Array(300.5)` and `Array(-1.5)` for `Byte`; `Array(9223372036854775808#)` for `LongLong`. What does not: the same constant declared inside a procedure compiles clean; a `Single` or `Double` array with a huge element (`Array(1E+300)`) compiles; an `Integer` or `Byte` element that is an integer literal too large (`Array(32768)`) is the ordinary TB5001. + +A second crash seems to belong with this one, and it is not in the reproducer. A `Single` array `Const` whose element overflows, `Const A() As Single = Array(1E+39)`, compiles alone, but crashes the compiler when a later array `Const` of `Double` calls `Sqr`, `Timer` or `Rnd`, for example `Const B() As Double = Array(Sqr(4))` on the next line. The reverse order, `Abs` or `Len` in place of those, `Now` or `Date`, and a `Single` array of `Timer` do not crash. The cause is not established; it could be an overflow flag left by the first constant, which the second one's call then trips over. + +Severity: a compiler crash, and the project cannot be opened for compiling until the constant is removed. + + + +--- + +## A module-level array `Const` initialised from an element of another array `Const` crashes the compiler + +**Describe the bug** +At module level, an array `Const` whose `Array(...)` list names an element of another array `Const` crashes the compiler: `Const A() As Long = Array(1, 2, 3)` followed by `Const B() As Long = Array(A(0))`. The compiler crashes again each time the IDE restarts it, so the project cannot be compiled or run. Observed with `tbbuild`, which reports "the compiler crashed 3x". + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `array-const-in-array-const-crash.twinproj` (attached as `array-const-in-array-const-crash.zip`). Its one module is: + ``` + Module Startup + Const A() As Long = Array(1, 2, 3) + Const B() As Long = Array(A(0)) + Public Sub Main() + End Sub + End Module + ``` +2. Compile it. The compiler crashes. + +**Expected behavior** +Either `B` is `Array(1)`, as it is when the two constants are declared in a procedure, or the compiler refuses the element with an error, as it does for a scalar constant (`Const S As Long = A(0)` is TB5002). + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 997 + +**Additional context** +BETA 995 has no array constants: every array `Const` is refused there with TB5245. VB6 has none either, so there is no VB6 project. + +It crashes with `B` used or unused, with `Private` constants, with `Double` arrays, with the two in the opposite order, and when an array refers to its own element (`Const B() As Long = Array(1, B(0))`). + +What does not crash: the same two constants inside a procedure compile clean; an array whose element is a scalar `Const` compiles; `Array(A(0) + 1)` is an ordinary TB5001, *Type mismatch*. + +Severity: a compiler crash, and the project cannot be compiled until the constant is removed. + + + +--- + +## `ShiftRotateLeft`, `ShiftRotateRight`, `ShiftUnsignedRight` and `ByteSwap` ignore argument names and take their arguments by position + +**Describe the bug** +The four new intrinsics of the VBA package (BETA 997) take their arguments by position and discard the names a call gives. `ShiftRotateLeft(ShiftAmount:=4, Number:=n)` uses 4 as the number and `n` as the shift amount, with no error, so the result is wrong. A name that matches no parameter, as in `ShiftRotateLeft(Foo:=n, Bar:=k)`, is accepted too. An ordinary procedure honours the names. Observed in a run of the reproducer project. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `math-intrinsics-ignore-argument-names.twinproj` (attached as `math-intrinsics-ignore-argument-names.zip`). Its `Sub Main` calls each intrinsic with a `Long` `n` of `&H12345678` and `k` of 4, in several forms. +2. Run it (F5) and read the DEBUG CONSOLE: + ``` + positional 23456781 + named, in order 23456781 + named, swapped 4000000 + right, named, swapped 400 + unsigned, named, swapped 0 + unknown names 23456781 + ByteSwap, unknown name 78563412 + own function, swapped 23456781 + ``` + +**Expected behavior** +The names select the parameters, as they do for any procedure (the last line, a function of the project with the same parameter names): `ShiftRotateLeft(ShiftAmount:=4, Number:=n)` is `23456781`, `ShiftRotateRight` of the same is `81234567` and `ShiftUnsignedRight` is `01234567`. A name that matches no parameter is a compile error, as it is in VB6 (*Named argument not found*). + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 997 + +**Additional context** +The four intrinsics are new in BETA 997; BETA 995 does not know them (TB5079). VB6 has no equivalent functions, so there is no VB6 project; the VB6 behaviour quoted is for a named argument that matches no parameter. + +The declared parameters are `Number` and `ShiftAmount` (`ByteSwap` has `Number`). `Compilation.UnrollLoop Bogus:=4` is accepted in the same way. Calls that give the arguments in declaration order, with or without the names, work. + +Severity: a silent wrong result, but only for a call that names its arguments out of declaration order or misspells a name. + + + +--- + +## A module-level array `Const` declared after a procedure is not found by the procedures that follow it + +**Describe the bug** +In a module, an array `Const` that is declared after a procedure cannot be used by the procedures declared after it: the use is TB5079, *Unrecognized symbol*. A scalar `Const` in the same place works. A procedure declared before the constant does see the name, and reads the array as empty: `Arr(1)` raises error 9 at run time. Observed in the compiler's diagnostics. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `array-const-after-procedure-not-found.twinproj` (attached as `array-const-after-procedure-not-found.zip`). Its module is: + ``` + Module Startup + Public Sub Main() + End Sub + Const Scalar As Long = 5 + Const Arr() As Long = Array(1, 2, 3) + Public Sub UseIt() + Debug.Print Scalar + Debug.Print Arr(1) + End Sub + End Module + ``` +2. Compile it. The only error is `TB5079 Unrecognized symbol 'Arr'` on the use in `UseIt`; `Scalar` is found. + +**Expected behavior** +No error: the constant is visible to every procedure of the module wherever it is declared, as a scalar `Const` is. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 997 + +**Additional context** +BETA 995 has no array constants (TB5245). VB6 has none either, so there is no VB6 project. + +Any procedure kind before the constant has the effect: a `Sub`, a `Function` and a `Property Get` were each checked. A declaration before the first procedure is fine, as are one after a `Dim`, an `Enum` or a `Type`. A `Public` array `Const` after a procedure is also unreachable from another module: `Startup.Arr(1)` is TB5027. Qualifying the name inside the module does not help. Classes are not affected: the order does not matter there. + +A procedure declared before the constant does find it, and then fails at run time. With `[RunAfterBuild] Public Sub Run()` first and `Const Arr() As Long = Array(1, 2, 3)` after it, `v = Arr(1)` under `On Error Resume Next` leaves `Err=9 Subscript out of range` and `v` unchanged. + +Severity: low, since the use is refused with an error; the run-time error 9 in an earlier procedure is a less obvious form of the same fault. + + + +--- + +## A `Public` array `Const` in a class is accepted but cannot be reached from outside, where a `Public` scalar `Const` is refused + +**Describe the bug** +A class may not have a `Public` constant: `Public Const Num As Long = 5` in a class is refused with TB5250, *Constants in a class cannot be Public*. For an array constant the declaration `Public Const Arr() As Long = Array(1, 2, 3)` is accepted without a diagnostic, and then `Arr` cannot be reached from outside the class: `obj.Arr(1)` is TB5027, *Unrecognized member*. The declaration is accepted and the constant is private in practice. Observed in the compiler's diagnostics. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `class-public-array-const-unreachable.twinproj` (attached as `class-public-array-const-unreachable.zip`). Class `WithArray` declares `Public Const Arr() As Long = Array(1, 2, 3)` and class `WithScalar` declares `Public Const Num As Long = 5`. `Sub Main` reads `a.Arr(1)` and `s.Num` from instances. +2. Compile it. The diagnostics are: + ``` + Startup.twin [7,21]: TB5027 Unrecognized member 'Arr' on type '_WithArray' [non-extensible object] + Startup.twin [8,21]: TB5027 Unrecognized member 'Num' on type '_WithScalar' [non-extensible object] + WithScalar.twin [3,18]: TB5250 Constants in a class cannot be Public + ``` + The declaration in `WithArray` has no diagnostic of its own. + +**Expected behavior** +The same refusal for both: TB5250 on the declaration of `Arr`, so that the cause is named at the declaration and not as an unrecognized member at each use. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 997 + +**Additional context** +BETA 995 has no array constants (TB5245). VB6 has none either, so there is no VB6 project; a `Public Const` in a VB6 class is refused. + +Inside the class, the array constant reads correctly (`Arr(1)` in `WithArray.Show`). A `Private` array `Const` in a class works, and `Friend`, `Protected` and `Static` are accepted as well. + +Severity: low. The error appears at the use, with a message that does not mention the declaration. + + + +--- + +## Passing a module-level array `Const` to a `ParamArray` compiles clean and then fails with a codegen error + +**Describe the bug** +A module-level array `Const` passed as the argument of a `ParamArray` parameter compiles without a diagnostic, and the build then fails: the linker reports *compilation (codegen) error detected* at the call. The same constant passed to a parameter declared `a() As Long` is refused at compile time with TB5001, and to a `Variant` parameter with TB5077, so the `ParamArray` case slips through the check. Observed with `tbbuild --build`. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `array-const-paramarray-codegen-error.twinproj` (attached as `array-const-paramarray-codegen-error.zip`). Its module: + ``` + Module Startup + Const Values() As Long = Array(10, 20, 30) + Public Sub Main() + Count Values + End Sub + Private Sub Count(ParamArray Items() As Variant) + Debug.Print UBound(Items) + End Sub + End Module + ``` +2. Compile it: no errors. Build it (or press F5). +3. The build log reads `[LINKER] compilation (codegen) error detected in 'Startup.Mainrootmain' at line #7` and `[BUILD] failed`. + +**Expected behavior** +A compile error at the call, as for the other whole-array uses of a constant array (TB5001 or TB5077). Documentation says the array "cannot be assigned to an array variable or a Variant, or passed as an array argument". + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 997 + +**Additional context** +BETA 995 has no array constants (TB5245). VB6 has none either, so there is no VB6 project. + +With LLVM the same call gives *a feature used in your code is not yet supported with the LLVM compiler*. + +What does not reproduce it: the constant passed after another argument (`Count 1, Values`) fails the same way, but `Count Values(0)`, an element, builds clean, an ordinary array variable passed to a `ParamArray` builds clean, and a constant array declared inside the procedure builds clean. + +Severity: low. The build fails with a message that gives the line, but not the cause. + + + +--- + +## LLVM-compiled code rounds a `Single` or `Double` to an integer half away from zero, where VB6 and plain code round half to even + +**Describe the bug** +In a procedure compiled with LLVM, `CLng`, `CInt` and `CLngLng` of a `Single` or `Double` variable, an assignment of one to a `Long` or `Integer`, and the operand conversion of `\` round a value exactly halfway between two integers away from zero: `CLng(2.5)` of a `Double` variable is 3 and `CLng(-0.5)` is -1. The same code without LLVM rounds to the even integer, 2 and 0, as VB6 does. A value that is not a tie rounds the same everywhere. Observed in a run of the reproducer project. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `llvm-conversion-rounds-half-away.twinproj` (attached as `llvm-conversion-rounds-half-away.zip`). It needs an LLVM licence (Ultimate): `LlvmCases` is compiled with LLVM by its `[CompilerOptions("+llvm")]`, and `PlainCases` holds the same statements without it. Each procedure converts 0.5, 2.5, 3.5, -0.5 and -2.5. +2. Run it (F5). The DEBUG CONSOLE has one line per value and mode; for 2.5 and -0.5 they read: + ``` + plain 2.5: CLng(d)=2 CLng(s)=2 CInt(d)=2 CLngLng(d)=2 l=d:2 d\1=2 CLng(v)=2 Round=2 + plain -0.5: CLng(d)=0 CLng(s)=0 CInt(d)=0 CLngLng(d)=0 l=d:0 d\1=0 CLng(v)=0 Round=0 + LLVM 2.5: CLng(d)=3 CLng(s)=3 CInt(d)=3 CLngLng(d)=3 l=d:3 d\1=3 CLng(v)=2 Round=2 + LLVM -0.5: CLng(d)=-1 CLng(s)=-1 CInt(d)=-1 CLngLng(d)=-1 l=d:-1 d\1=-1 CLng(v)=0 Round=0 + ``` + +**Expected behavior** +The same result in both: round half to even, as in VB6 (attached as `llvm-conversion-rounds-half-away-vb6.zip`), which prints for the same values `CLng(d)=2` for 2.5 and `CLng(d)=0` for -0.5, `l=d:2`, `d\1=2`, `CLng(v)=2` and `Round=2`, `0` and `0`. VB6 has no `LongLong`, so that column is missing from its output. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 997 + +**Additional context** +Also on BETA 995, identically, and on win64. + +What does not differ in LLVM-compiled code: `CLng` of a `Variant` holding the `Double`, `Round`, `Int` and `Fix`, and conversions of a literal such as `CLng(2.5)` (folded to 2 by the compiler). + +A consequence: a value that rounds to zero in plain code can raise an error under LLVM. In a `Byte` conversion, `CByte(-0.5)` and `b = -0.5` (a `Double` variable) give 0 without LLVM and error 6, *Overflow*, with it, because the value becomes -1. + +Severity: a silent wrong result, at ties only, unless the rounding makes a conversion overflow. + + + +--- + +## In LLVM-compiled code a shift count is cut to the width of the shifted `Byte` or `Integer` + +**Describe the bug** +In a procedure compiled with LLVM, the count of `<<` or `>>` is reduced to the width of the shifted type before the shift, for a `Byte` and an `Integer`: `b << 256` with `b` a `Byte` of `&H81` leaves `&H81`, and `b >> 257` gives `&H40`, because the count becomes 0 and 1. Without LLVM both give 0, as the documentation says: a shift by as many bits as the type holds, or more, yields 0. An `Integer` shifted by 65536 or more behaves the same way. A `Long` is not affected, as the count is itself a `Long`. Observed in a run of the reproducer project. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `llvm-shift-count-truncated.twinproj` (attached as `llvm-shift-count-truncated.zip`). It needs an LLVM licence (Ultimate): `LlvmCases` is compiled with LLVM by its `[CompilerOptions("+llvm")]`, and `PlainCases` holds the same statements without it. +2. Run it (F5) and read the DEBUG CONSOLE: + ``` + plain Byte << 256 0 + plain Byte >> 257 0 + plain Integer << 65536 0 + plain Integer >> 65537 0 + plain Long << 65536 0 + plain Long << -1 80000000 + plain Long >> -1 FFFFFFFF + LLVM Byte << 256 81 + LLVM Byte >> 257 40 + LLVM Integer << 65536 8001 + LLVM Integer >> 65537 C000 + LLVM Long << 65536 0 + LLVM Long << -1 0 + LLVM Long >> -1 0 + ``` + +**Expected behavior** +0 for a `Byte` shifted by 8 or more, and for an `Integer` shifted by 16 or more, whatever the count, in LLVM-compiled code as without LLVM (docs/Reference/Core/LeftShift.md and RightShift.md: "A shift by as many bits as the type holds, or more, yields 0"). VB6 has no shift operators, so there is no VB6 project. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 997 + +**Additional context** +Also on BETA 995, identically, and on win64. + +Counts from 8 to 255 for a `Byte`, and 16 to 65535 for an `Integer`, give 0 in both modes. The difference starts where the count, taken modulo 256 or 65536, is below the width. + +A negative count also differs between the two modes, and the documentation calls the result of a negative count "no useful result". Without LLVM the count is reduced modulo 32 for every type (`1& << -1` is `&H80000000`, `&H80000001 >> -1` is `&HFFFFFFFF`); with LLVM a negative count gives 0 (a `Long`, `LongLong` or `Integer`; a `Byte` gives 0 too). Neither is documented, so that part is a difference, not a departure. + +Severity: a silent wrong result for a `Byte` shifted by 256 or more, or an `Integer` by 65536 or more. Counts that large are rare. + + + + + diff --git a/WIP.ExamplesBuild.md b/WIP.ExamplesBuild.md index 2b9ff4920..7cef1ff1d 100644 --- a/WIP.ExamplesBuild.md +++ b/WIP.ExamplesBuild.md @@ -451,9 +451,14 @@ All are forced by putting unrelated samples in one compilation unit: - **`Sub Main` comes from the template, and a fence may bring its own as well.** Two `Public Sub Main`s in different modules compile, so the WinServicesLib `Module Startup` samples build as written. (A *build* adds a rule; see [`--build` and `--llvm`](#--build-and---llvm-what-a-compile-does-not-ask).) -- **A generated module must not share a name with the project.** `project.name = "ProbeWS"` - beside `Module ProbeWS` makes `[RunAfterBuild]`'s `ProbeWS.ProbeWS.Probe` ambiguous, and - the IDE refuses it at *execution* time, so the build is green and nothing runs. +- **No generated module may be named like the project, in any letter case.** With + `project.name = "ProbeWS"`, a `Module ProbeWS` or a `Module probews` stops the + `[RunAfterBuild]` Sub from running, even when it is empty and the Sub is in another module. + Nothing reports it: the build is green, and the DEBUG CONSOLE ends at `[BUILD] Executing + '..'...` (BETA 995 and 997; the BUGS-TO-REPORT.md entry with the + reproducer `bugs/module-named-like-project/`). The tool names each project `DocSamples` + and every module it generates `tbx...`, so the two cannot meet; a sample that declared + `Module DocSamples3` would. **What collides is narrower than it looks, and guessing it wide is expensive.** Two generated modules may each declare `Public Function Foo`, `Public Type Rec` and `Public Const @@ -746,12 +751,10 @@ that fails only the second is one LLVM cannot generate code for. reads the DEBUG CONSOLE, which nothing removes an entry from, and exits 4 naming the file the compiler died parsing. **Treat any tooling that infers "finished" from "stopped changing" as suspect on this compiler.** -- **A two-line syntax skeleton crashes the compiler**, and it is in the corpus: - `Interface Extends ` / `End Interface`, in - `Reference/Attributes.md`. The placeholder name does it, given any `Extends` clause: - `Extends IBase` crashes it too. Recorded in [BUGS-TO-REPORT.md](BUGS-TO-REPORT.md). The - classifier refuses a `` as a declaration name, so it takes an explicit - `slot=` to reach the compiler with one. +- **The classifier refuses a `` as a declaration name**, so a syntax skeleton + such as `Interface Extends ` / `End Interface` (in + `Reference/Attributes.md`) takes an explicit `slot=` to reach the compiler. Through BETA + 995 that skeleton crashed the compiler (twinbasic/twinbasic#2453, fixed in 997). - **`project.buildPath` must be an explicit file.** The default `${SourcePath}\Build\...` template opens a native Save dialog, which on the private desktop is invisible and unreachable, so the build silently never happens --- and the WebView2 renderer stays diff --git a/WIP.Harness.md b/WIP.Harness.md index 4a73bfa1b..95beb79f4 100644 --- a/WIP.Harness.md +++ b/WIP.Harness.md @@ -216,9 +216,10 @@ other 193 are refused at every site. one form able to pass never compiled is inconclusive, not refused, and the same holds for a run cut short. -**It found a compiler crash the documentation never would:** a bare `[PopulateFrom]` on an -Enum kills the compiler, at all three Enum sites; the tool isolated it by halving to one -probe beside the canaries. It is in [BUGS-TO-REPORT.md](BUGS-TO-REPORT.md). +**It finds compiler crashes the documentation never would:** a crash in a batch is +isolated by halving down to one probe beside the canaries, which is how a bare +`[PopulateFrom]` on an Enum was found to kill the compiler (twinbasic/twinbasic#2450, fixed +in BETA 997). **Read the report's "accepted at most sites" section before believing an acceptance.** `[Description]` is taken at 53 of 61 sites and `[Hidden]` and `[Restricted]` at 42: either @@ -245,8 +246,23 @@ override that, and one of the two is needed for an install kept anywhere else. * path is hardcoded**, here or anywhere in the tooling: an install path contains a username. `--json` gives the same thing as one object and `--keep` leaves the IDE running. Exit codes -are 0 clean, 1 the project has errors, 2 the harness failed, 3 the compile never settled, 4 -the project crashes the compiler. +are 0 clean, 1 the project has errors, 2 the harness failed or the project file cannot be read, +3 the compile never settled, 4 the project crashes the compiler. + +**A project file the IDE cannot read is refused before an IDE starts.** `tbbuild` reads the +container with impexp's reader (`checkProject`, in +[scripts/lib/tb-project.mjs](scripts/lib/tb-project.mjs)) and exits 2 within a moment, +naming the file and the fault. Without that, BETA 995 gave exit 0, a clean compile with no +rows, after the whole `--timeout` (66 s at `--timeout 60`) for an empty file, a file cut off +halfway and a file with its first byte changed, and exit 3 for a text file. The IDE answers +such a file with a message box, which nobody can see or answer on the private desktop. The +status bar never settles, and a wait that ends at its deadline is read as a compile with no +diagnostics, because `waitForCompile` does not report that it ran out of time. Only the +container is checked: a file that reads as a project and that the IDE still cannot load is +not covered. `launchOnDesktop`'s `dialogs: "close"` can record a box, but only by pressing OK +on it, `launchIde` does not ask for it, and the job it matches boxes against is absent under +`--keep`. Making `waitForCompile` say that it ran out of time would cover every such wedge, +and is not done. **`--show` / `--hide`, and `TBBUILD_SHOW` for a whole session.** Hidden is the default. Its cost shows only when something goes wrong: a wedged IDE on a private desktop is invisible to @@ -434,8 +450,8 @@ other thirty their run. ### Why it drives the WebView rather than the compiler directly -The obvious improvement is to cut the browser out --- the compiler has websockets, so why go -through a UI at all? **It cannot be done, and the reason is structural.** +The obvious improvement is to cut the browser out: the compiler has websockets, so why go +through a UI at all? **It can be done, and the harness does not do it yet.** The IDE is three processes, and their command lines say how they relate: @@ -443,18 +459,20 @@ The IDE is three processes, and their command lines say how they relate: |---|---|---| | `twinBASIC.exe` | `` | shell; hosts the WebView2, and the only one given the project | | `twinBASIC_win32.exe` | `--ide=` | serves `ide/` over HTTP on an ephemeral port | -| `twinBASIC_win32_noDEP.exe` | `--compiler=` | the compiler; the page talks to it over websockets | +| `twinBASIC_win32_noDEP.exe` | `--compiler=` | the compiler; the page talks to it over websockets | The page's websockets carry everything the window shows about a compile, the diagnostics and their counts included. -**But what a connection needs, its address and its key, is minted inside the WebView**, by -host objects the shell gives its page --- reachable only from a page the shell has loaded. -Starting the compiler directly is no way round it either: `--compiler=` is not a port but -an opaque handle the shell hands it. So a proxy between the WebView and the HTTP server is -possible --- the page and its scripts come over plain HTTP, and a patched `main2.js` could -be served --- but it would not remove the WebView, it would only change what runs inside it. -The thing you would want to delete is the thing that mints the connection. +**The compiler chooses its own address and key, and tells the process that started it.** So +a program that starts a compiler itself can connect to it, and `--compiler=` need not name a +real window for a compile or a build (BETA 995). + +What stands in the way is the work the page does over those connections. It loads the +project, gives the compiler every package the project needs, answers what the compiler asks, +and works out when a compile has finished. A client without the WebView has to do all of +that itself, in a protocol that is the IDE's own and may change with any beta, while the IDE +always speaks its own build's. So the harness drives the IDE. Watching the page's traffic is another matter: CDP shows it to the harness with no key, and the wait for a compile ends on it (*How the wait for the compile ends*, above). The @@ -517,6 +535,19 @@ Rules it follows: - **`element.click()` on `#buildIcon` does nothing.** It is a plain DIV behind the IDE's own pointer handling and needs real `Input.dispatchMouseEvent` presses at its centre. +- **Build is pressed through `pressBuild` (`tb-ide.mjs`), which waits up to 60 s for the + page globals `licenceIsSet` to be true and `lockUICount` to be 0.** `tbBuild_Start` waits + only about five seconds for the compiler to answer the licence key's validation, which + `afterSocketsAreConnected` resets on every compiler connection, and then writes "[BUILD] + failed due to licence error" and builds nothing. `#appOverlay` is a transparent element + over the whole page, shown while `lockUICount` is above zero: by every modal dialog, the + "Compiling..." and project-loading progress dialogs among them, and by an open menu. A press + then fails with "its centre is covered by #appOverlay" (both were seen once each in a full + `bug_repro verify`). A licence still unset after the wait throws; an overlay still up is + left to `click`, which names it. +- **A click that fails saves a picture of the page** (`pagePicture` in `tb-click.mjs`, CDP + `Page.captureScreenshot`, which works on the private desktop) under `%TEMP%\tb-click\`, and + its error names the file. - **Read the console's backing array, not the pane.** The DEBUG CONSOLE is a `createListView()`, which keeps only the rows that fit in the DOM, so scraping its `innerText` returns the *tail* of a long probe and looks exactly like a complete capture @@ -763,6 +794,22 @@ variable left set in a shell would switch tidying off for good. Under `--keep` n tidied, because the kept IDE is still writing. `shutdownIde` waits for the IDE's process to be gone before anything is tidied, because `taskkill` only asks. +**The last of several runs puts back for all of them.** Two runs at once --- two `ide_test`, +a `tbrun` beside a lane run, another session's --- cannot each put back on their own. An IDE +holds the recent list in memory and writes all of it back when it opens a project, so a run +whose IDE is still open writes the other run's entries back after that run removed them (two +`ide_test` runs failed with exit 3 this way, each naming the other's lane folder). So the runs +share one record in `%TEMP%\tb-registry\`, read and written one run at a time under its +`busy` folder: the first run to start records the recent list, the association and the theme +(`base.json`); every run adds its folders and named projects (`-.json`); a run that +ends while another is running marks its record finished, puts nothing back and gets `{ +deferred: [pids] }` from `finishTidy`, which the lane runner reports instead of checking the +lists; and the last run to end puts back for all, a named project from its oldest record. A +record whose process died unfinished is swept by folder when the next run starts alone, and +its record of the theme and recent list is dropped, since it may be days old. +`check_tb_registry.mjs` plays out both. `tbbuild --keep` takes no part, so its IDE can still +write the lists after the last run has put them back. + **Why .NET through PowerShell and not `reg.exe`.** Node has no registry API. `reg.exe` prints value names in the console code page when its output is piped, so a path containing a character outside that code page (an accented user name in `%TEMP%` is enough) comes back @@ -1197,6 +1244,8 @@ leave the registry alone, and `finishTidy` once the last has ended. Then it chec than trusts: no project-state or recent-list entry may name a lane's folder, a second sweep of the remembered build targets must find none, and the add-ins' settings must be as recorded. Any failure is exit code 3, which wins over a lane's failure (1): the registry is what to repair. +While another harness run is still running, the lists and targets are that run's to put back +and are not checked; the report line says which pid. **An add-in's own settings are the runner's too.** `SaveSetting` writes under `HKCU\Software\VB and VBA Program Settings\`, the same key as any installed copy of the diff --git a/WIP.HelpAddin.md b/WIP.HelpAddin.md index 81fac93fe..96279ae78 100644 --- a/WIP.HelpAddin.md +++ b/WIP.HelpAddin.md @@ -359,7 +359,8 @@ with files put in a lane's copy of the install: own pages, then, and `sandbox` on the frame if that matters. So the offline site would work copied into `ide\`, with one cost the live site does not -have: writing into the install, which every new build replaces. Still deferred. +have: writing into the install, which every new build replaces. The add-in serves the +offline site itself instead (Stage 4, increment 5), so this route is not taken. ### What is under the cursor @@ -624,8 +625,7 @@ run, and the failure says what to update. The lanes and what rests on each: - [panes.test.mjs](test/addin/panes.test.mjs) (P3, P4, P12, and the shared id `""` found with P9): the NOTEs on the HtmlElement, HtmlElementProperties, HtmlElements and ToolWindow pages. -- [symbols.test.mjs](test/addin/symbols.test.mjs) (P5): Stage 4's context and an entry in - BUGS-TO-REPORT.md. It needs no add-in, only the project in `probes/symbols`. +- [symbols.test.mjs](test/addin/symbols.test.mjs) (P5, P15): Stage 4's context. It needs no add-in, only the project in `probes/symbols`. - [ideserver.test.mjs](test/addin/ideserver.test.mjs) (P13): the offline route. - [appdata.test.mjs](test/addin/appdata.test.mjs) (P6): the lanes' own `APPDATA` and the FAQ's answer on where add-ins go. @@ -647,7 +647,7 @@ The table gives each answer in brief; the sections above have the detail. | # | Question | What it decides | |---|---|---| | P1 | Do `{ctrl}` and `{alt}` add-in shortcuts ever fire? **Answered, BETA 983 and 995: no.** `d`, `{shift}d`, `f1` and `{shift}f1` fire; `{ctrl}{shift}d`, `{ctrl}d` and `{alt}f` fire only when the same key was pressed on its own less than 500 ms before. Queued in BUGS-TO-REPORT.md; the KeyboardShortcuts page has a NOTE. | the bug report; which key the add-in uses; the NOTE on the KeyboardShortcuts page | -| P2 | Does the add-in's `f1` fire with focus in the code editor, and what happens with signature help showing? **Answered, BETA 983 and 995: yes.** It fires with the focus in the code editor, in the DEBUG CONSOLE and on nothing, and types nothing. With signature help showing, the IDE expands or collapses it as well, and logs `command failed: "tbHelp_ToggleExpandSignatureHelp"`. | F1 or another key --- F1 | +| P2 | Does the add-in's `f1` fire with focus in the code editor, and what happens with signature help showing? **Answered, BETA 983 and 995: yes.** It fires with the focus in the code editor, in the DEBUG CONSOLE and on nothing, and types nothing. With signature help showing, the IDE expands or collapses it as well. | F1 or another key --- F1 | | P3 | Does an `iframe` of a documentation page load and navigate inside a tool window? **Answered, BETA 983 and 995: yes.** It loads, follows its own links, moves when the add-in sets `src`, scrolls, and fills the window under a wrapper's flex layout; the live site works too. Keys in the frame never reach the add-in, and the page's colour scheme is Windows', not the IDE's. | how pages are shown --- in the pane | | P4 | Does `innerHTML` render, and do inline handlers in it run page script? **Answered, BETA 983 and 995: yes, and yes.** Inline handlers run as the IDE page's own script, with its globals in reach. A property whose name starts with `on` is dropped, and the add-in hears no error. | how summaries are drawn; whether the page-internals route exists --- it does | | P5 | What does hover return for `MsgBox`, `Collection.Add`, `ToolWindows.Add` and a symbol declared in the project? What does definition return for a package symbol? **Answered, BETA 983 and 995:** hover gives the declaration, then a heading naming where it is declared: `in VBA.Interaction`, `in VBA._Collection`, `in tbIDE.IToolWindowsV1`, `in SymbolsProbe.Symbols` --- a class's members by its default interface, not by the class's name. Definition gives the declaration in the package's own source, which the IDE opens. Nothing for `Debug.Print` or a statement. Only page script can ask. | compiler-assisted context is possible; which route is [Stage 4](#stage-4-the-add-in-in-increments), increment 3 | @@ -660,7 +660,7 @@ The table gives each answer in brief; the sections above have the detail. | P12 | Does `raiseEvent` from plain tool-window HTML throw? **Answered, BETA 983 and 995: yes** --- `TypeError: Cannot read properties of null (reading 'rootEventHandler')`, and the listener is not called. An inline handler that calls the listener `AddEventListener` stored on its parent, `this.parentNode.(event)`, reaches the add-in. | how the pane's events are written | | P13 | Does the compiler's HTTP server serve any file placed under `ide\`? **Answered, BETA 983 and 995: yes**, and it is the page server, `twinBASIC_win32.exe --ide=`, not the compiler. A frame with a relative `src` shows the file on the IDE page's own origin. A query string makes a 404, and `.html` has no `Content-Type`. | an offline route --- it exists ([Offline](#ways-to-show-a-page)) | | P14 | What do `tbCreateCompilerAddin_v2` and `_v3` expect? **Answered, BETA 983 and 995: what `tbCreateCompilerAddin` does.** The names are version stamps: the linker exports a function named `tbCreateCompilerAddin` as `tbCreateCompilerAddin_v3` alone, and an IDE that knows none of a DLL's names refuses it as `compiled for a newer version of the twinBASIC IDE`, as a patched `_v4` was. | nothing in the design --- the add-in declares `tbCreateCompilerAddin` as the package says; the tbIDE page has a NOTE | -| P15 | Does the compiler say anything about a name's `[Description]`, or a COM type library's help string? **Answered, BETA 995: hover gives it**, after the line naming where the name is declared, for a procedure, a member, a constant, a module variable, an enumeration, a `Type`, a class and a module, and a type library's member (`Scripting.IDictionary.Add`: "Add a new key and item to the dictionary."). Classes and modules, the project's and a type library's, are `in library `. A procedure with none gets the IDE's tip, `*no further info available. Tip: use [Description("")] ...*`. On a procedure's name where it is declared, hover gives a block of code-generation details (`TB-DEBUG CODEGEN SIZE: [NOT-READY]`) instead. Signature help gives the same text. A member of an enumeration is the exception: its `[Description]` is listed as a member of the enumeration and not shown on the member (filed as twinbasic/twinbasic#2465, `bugs/filed/enum-member-description-hover/`). | Stage 4, increment 4: a name with no page shows its declaration and description from the hover; no parser of the project's source is needed | +| P15 | Does the compiler say anything about a name's `[Description]`, or a COM type library's help string? **Answered, BETA 995: hover gives it**, after the line naming where the name is declared, for a procedure, a member, a constant, a module variable, an enumeration, a `Type`, a class and a module, and a type library's member (`Scripting.IDictionary.Add`: "Add a new key and item to the dictionary."). Classes and modules, the project's and a type library's, are `in library `. A procedure with none gets the IDE's tip, `*no further info available. Tip: use [Description("")] ...*`. On a procedure's name where it is declared, hover gives a block of code-generation details (`TB-DEBUG CODEGEN SIZE: [NOT-READY]`) instead. Signature help gives the same text. A member of an enumeration has none: a `[Description(...)]` line inside an `Enum` is not an attribute but a member of its own, named by a bracketed identifier, as VB6 allows (`Debug.Print Shade.[Description("The light one.")]` prints its value), so hover lists it as a member of the enumeration (twinbasic/twinbasic#2465, closed as not a bug). | Stage 4, increment 4: a name with no page shows its declaration and description from the hover; no parser of the project's source is needed | ### Stage 3: the symbol index, generated by the docs build @@ -784,9 +784,9 @@ has the complete list): - **The rest of an embedded mode for pages.** The theme is done: the site reads a `theme=dark|light` query parameter (Stage 4, increment 2), since the add-in cannot reach into a cross-site frame. Hiding the header and navigation is a separate, optional - question, untested. The parameter serves the live site only: the IDE's own server answers - any URL with a query string with a 404 (P13), so the offline route would need the theme - some other way. + question, untested. The IDE's own server answers any URL with a query string with a 404 + (P13); the add-in's server (increment 5) ignores the query, so the parameter works + offline too. - **Keywords with no page of their own** --- `ElseIf`, `Until`, `Step`, `To`, `In`, `ByVal`, `ByRef`, `Optional`, `As` --- are in the index only where a page's title gives them. Adding one is a `symbols:` line on the page that explains it, which is a content @@ -795,7 +795,8 @@ has the complete list): `/Reference/Data-Types`, outside `/tB/`, so the index cannot carry them without breaking its own rule. Giving that page a `/tB/` permalink, with the old one in `redirect_from:`, would. Deferred. -- **The offline tree has no copy of the index**, since the offline route is deferred. +- **The offline tree has no copy of the index.** The add-in embeds its own, and the help + archive (increment 5) is the offline tree as built. ### Stage 4: the add-in, in increments @@ -950,9 +951,42 @@ harness about a second against opening a new IDE. right. - **The fault run** (`ShowPage` not putting the frame back) failed the four cases that show a page after a summary. -5. **Later:** hover help through `CodeEditor.AddMonacoWidget` after a pause (the cost of +5. **Offline help. Built**, tested by the lane `help-offline` on BETA 995, at the owner's + choice (2026-10-05): the add-in's own server, written in twinBASIC, serving one archive + embedded in the DLL. The owner's other choices: the archive is a resource; a separate + script makes it; the pane uses it when the DLL has it, and the live site otherwise; the + symbol index stays a resource of its own. + - **The archive** is `add-in/Resources/HELP/site.zip`, gitignored, written from + `docs/_site-offline` by `scripts/build_help_archive.mjs` (about 21 MB, 1,466 files; + the DLL builds with it for win32 and win64). An add-in built without it serves nothing + and uses the live site, as before. The offline tree is the one served: its links are + relative, so they work on any origin, and its pages hold only their part of the nav + tree. + - **No inflating in twinBASIC.** A deflated entry is sent as it is, between a 10-byte + gzip header and the CRC32 and size from the central directory, with + `Content-Encoding: gzip`; an image or font is stored and sent plain. So the format is + fixed by the script: no zip64, no data descriptors. + - **`HelpServer`** listens on 127.0.0.1 at a port Windows picks, on the add-in's own + thread: Winsock posts each socket's events to a message-only window + (`WSAAsyncSelect`), so there is no second thread. It reads the archive with + `LoadResDataInternal` once, at project load. A path is found as GitHub Pages finds it + (`/a/b` is `a/b.html`, or a 301 to `/a/b/` for a folder, keeping the query), the query + is ignored, and every reply closes its connection. It prints `serving site.zip, + files, at `, which the lane reads, since the port changes with every compiler + restart. + - **The origin is `http://localhost:`, not 127.0.0.1.** The IDE's page is on + localhost, so a frame on another localhost port is same-site and in its frame tree; a + frame on 127.0.0.1 is not there at all, and every case that reads the page failed. + - *Open in browser* still opens the live site. + - **The lane.** `help.test.mjs` runs twice: `help` builds a copy of `add-in/` without + `Resources/HELP`, whatever the working tree holds, and `help-offline` builds it with an + archive made by the script. A page's status is read from the page itself + (`PerformanceNavigationTiming.responseStatus`), so a 404 fails a case either way. An + offline-only case asks the server from Node: a page's gzip body and a stored image + byte for byte, a folder's 301, a 404 and `HEAD`. +6. **Later:** hover help through `CodeEditor.AddMonacoWidget` after a pause (the cost of adding and removing widgets is not measured); offering only the packages the project - references; offline use. + references; how a user gets an add-in with the archive. **Lookup:** diff --git a/WIP.md b/WIP.md index 4c258e90a..cc1f085c2 100644 --- a/WIP.md +++ b/WIP.md @@ -117,19 +117,19 @@ node scripts/tbrun.mjs # what does it print ``` - **Pack and unpack projects with `impexp`, never with the compiler executable's `import` or `export` verbs.** The tooling does both in process, through `packTree` and `unpackProject` in `scripts/lib/tb-project.mjs`; by hand it is `node scripts/impexp.mjs`. The compiler's `import` refuses a project that embeds a package (exit 999, twinbasic/twinbasic#841), and both verbs exit 0 after the failures they report. The reasons, and the path and stdin rules for anything that does run the executable (`bug_repro`'s `cli` mode), are in [WIP.Harness.md](WIP.Harness.md#getting-at-the-twin-sources). -- **`tbbuild` exit codes:** 0 clean, 1 the project has errors, 2 the harness failed, 3 the compile never settled, 4 the project crashes the compiler, 5 the build failed after a clean compile. `--json` returns one object, `--keep` leaves the IDE running. **`--build` and `--llvm` (which implies it) build the project after a clean compile**, because LLVM code generation runs in a build and a compile never asks for it. They first stage a copy with an explicit build path (`%TEMP%\tbbuild\\`), so the given `.twinproj` is untouched. A plain `--build` is the control for an `--llvm` run. `--llvm` refuses a Community or Personal licence. +- **`tbbuild` exit codes:** 0 clean, 1 the project has errors, 2 the harness failed or the project file cannot be read (empty, cut short, not a project: it is read before an IDE starts), 3 the compile never settled, 4 the project crashes the compiler, 5 the build failed after a clean compile. `--json` returns one object, `--keep` leaves the IDE running. **`--build` and `--llvm` (which implies it) build the project after a clean compile**, because LLVM code generation runs in a build and a compile never asks for it. They first stage a copy with an explicit build path (`%TEMP%\tbbuild\\`), so the given `.twinproj` is untouched. A plain `--build` is the control for an `--llvm` run. `--llvm` refuses a Community or Personal licence. - **It runs the IDE on a private Windows desktop**, so it cannot seize focus. Set `TBBUILD_SHOW=1` while working interactively and leave it unset for unattended runs: a wedged IDE nobody can see is expensive to diagnose. - **One project per IDE**, 6--8 seconds each and flat in project size. Reusing a live IDE for a second project wedges it, so the cold start is the unit of work, not overhead to optimise away. Run concurrently to go faster: distinct `--port` values give distinct DevTools ports, user-data folders, temp folders (`%TEMP%\tbbuild-tmp-`; IDEs building in one temp folder fail now and then to write the type library) and desktops. - **Keep a probe that might crash the compiler in a project of its own.** twinBASIC runs the compiler in the same process as user code, so one bad probe can take the run down and cost the other probes their answer. -- **`tbrun` takes an exported tree, not a `.twinproj`**, because it has to pin `project.buildPath` in its own staged copy. A project still on the default template opens a native Save dialog that is invisible on the private desktop: the build never happens, while every health check says the IDE is fine. The probe is a module with a `[RunAfterBuild]` Sub, and must start with `Debug.Cls`. Its exit codes: 0 the probe ran and its output was captured, 1 the project has compile errors, 2 the harness failed or the build did after a clean compile, 3 no output, 4 the compiler crashed, as `tbbuild` reports it, 5 the probe ended before it returned (`End`, or an error raised with no handler in LLVM-compiled code, which ends the run silently), 6 with `--exe`, the exe exited with a code other than 0 or was still running at `--timeout`. +- **`tbrun` takes an exported tree, not a `.twinproj`**, because it has to pin `project.buildPath` in its own staged copy. A project still on the default template opens a native Save dialog that is invisible on the private desktop: the build never happens, while every health check says the IDE is fine. The probe is a module with a `[RunAfterBuild]` Sub, and must start with `Debug.Cls`. Its exit codes: 0 the probe ran and its output was captured, 1 the project has compile errors, 2 the harness failed or the build did after a clean compile, or the probe's module holds a procedure named like the module, which stops the `[RunAfterBuild]` Sub from running (BETA 997; `--allow-name-clash` runs it anyway), 3 no output, 4 the compiler crashed, as `tbbuild` reports it, 5 the probe ended before it returned (`End`, or an error raised with no handler in LLVM-compiled code, which ends the run silently), 6 with `--exe`, the exe exited with a code other than 0 or was still running at `--timeout`. - **Measuring LLVM: `tbrun --llvm`** (or `--compiler-options ""`) compiles the whole probe with LLVM, by setting `compiler.debugOptions` --- what a `[RunAfterBuild]` run is compiled with; `compiler.buildOptions` alone does not reach it (BETA 995) --- and `compiler.buildOptions`, for the exe. It refuses a Community or Personal licence. `--exe` also runs the built exe on a private desktop; the exe runs `Sub Main` and prints with `TbRun.Out`, a module `tbrun` adds, because `Debug.Print` writes nothing in an exe. - **A census is evidence, not applicability.** The corpus not using an attribute somewhere does not mean the compiler refuses it there, and the reverse also holds. Only a probe settles that. - **The sweep is the probe for a whole attribute.** Before writing or changing an attribute's `Applicable to:` line, run `sweep_attributes.mjs --names ` (a minute or a few; the once-per-project ones, `RunAfterBuild` and `RunBeforeStartupObject`, take several); it tries every declaration site, not only the ones already claimed. **Always pass `--out` and `--dump-results`**: a run piped through `tail` keeps one line of a report that takes ten minutes. A clean build there means the compiler accepts the attribute, not that it does anything, and an Enum member cannot be tested at all (see [WIP.Harness.md](WIP.Harness.md#sweeping-every-attribute-at-every-site)). - **End an IDE by its pid, never by image name.** `taskkill /IM twinBASIC.exe` ends every other run's IDE, another session's included, and the user's own. `tbbuild --keep` prints the pid for this reason. -Why each of those is true, what the WebView/CDP route costs, why the compiler's -own websockets cannot be driven instead, and the seven ways a sweep of this -corpus returns a wrong answer: [WIP.Harness.md](WIP.Harness.md). +Why each of those is true, what the WebView/CDP route costs, why the harness +drives the IDE rather than the compiler's own websockets, and the seven ways a +sweep of this corpus returns a wrong answer: [WIP.Harness.md](WIP.Harness.md). **Testing an IDE add-in** is `addin-test.bat`, run by a person as `examples.bat` is. Each lane in `test/addin/lanes.mjs` builds the add-ins it tests into a private copy of the @@ -216,7 +216,9 @@ The vocabulary tables further down cover word choice. The rules in this subsecti One callout per concern; don't stack a NOTE and an IMPORTANT for the same point. - **A known twinBASIC defect that silently gives a wrong result is a `> [!WARNING]` until a fixed build is released** (owner's rule): a wrong value with no error raised is data corruption the reader cannot see. A crash is a WARNING too (unsaved data is lost), and so is an error that hides a silent part (a procedure run twice before the error). A defect the reader cannot miss and that loses nothing --- an error raised, a statement refused --- is a `> [!NOTE]`. Name the build in either; once fixed, it becomes a NOTE saying since which build. The defect's BUGS-TO-REPORT entry says in its HTML comment which page carries the callout, so the callout is changed when the entry is retired. + **Where twinBASIC and VB6 behave differently, say what VB6 does** (owner's rule, 2026-10-06), checked with `scripts/vb6run.mjs` or a reproducer's `vb6/` side, never from memory. Where twinBASIC's behaviour is the nominal one, the VB6 behaviour goes in a `> [!NOTE]`; inside a defect's WARNING it is one sentence, such as "VB6 raises error 6, *Overflow*." + + **A known twinBASIC defect that silently gives a wrong result is a `> [!WARNING]` until a fixed build is released** (owner's rule): a wrong value with no error raised is data corruption the reader cannot see. A crash is a WARNING too (unsaved data is lost), and so is an error that hides a silent part (a procedure run twice before the error). A defect the reader cannot miss and that loses nothing --- an error raised, a statement refused --- is a `> [!NOTE]`. Name the build in either. **Once a fixed build is released, the callout goes** (owner's rule, 2026-10-05): the documentation is not a record of past defects, so the page then states the correct behaviour with no mention of the defect. The defect's BUGS-TO-REPORT entry says in its HTML comment which page carries the callout, so the callout is removed when the entry is retired. 7. **See Also.** Last section on the page, after `Example`. Format: `- [Symbol](Symbol) ` where `` is the kind: statement, function, property, method, class, module, package. Pages with annotations use `- [Symbol](Symbol) -- short description` — the `--` source renders as a typographic dash via markdown-it's typographer (en-dash for `--`, em-dash for `---`). Don't write literal `—` in source; keep `--` for consistency across the docs. Order by conceptual proximity, not strict alphabetical. @@ -458,10 +460,11 @@ Why the report separates the wedged task from the merely blocked ones, and why - `book.bat` — renders the PDF from `docs\_site-pdf\book.html` via `node book\render-book.mjs` into `docs\_pdf\twinBASIC Book.pdf`. Run `build.bat` first to populate `_site-pdf/`; `book.bat` refuses a tree older than its sources rather than rendering the previous book (see [The book refuses a stale source tree](WIP.Build.md#the-book-refuses-a-stale-source-tree)). - `examples.bat` — compiles the documentation's own twinBASIC code samples, every `tb` fence marked `check_build`, and reports the ones the compiler refuses against the line in the page they came from; a statement sample also marked `check_run` is built and run, and what it prints is checked against the page. Needs a twinBASIC install and Windows, so it is outside every gate and outside CI; ~110 s over the 1,197 samples marked (33 of them `check_run`). `--build` also builds each project that compiles clean, and `--llvm` builds it with LLVM (see [WIP.ExamplesBuild.md](WIP.ExamplesBuild.md)). Two modes need no compiler at all: `--census` classifies every fence and says how many classifiable ones are still unmarked, and `--report ` groups a saved `--propose --json` survey by diagnostic, section and unresolved name. `--propose` itself does compile. See [Compiling the reference's own code samples](#compiling-the-references-own-code-samples) and [WIP.ExamplesBuild.md](WIP.ExamplesBuild.md). -- `addin-test.bat` — tests IDE add-ins by operating an IDE: every lane in `test/addin/lanes.mjs` builds the add-ins it tests into a private copy of the install, opens a project and checks what the add-in does. Outside every gate and outside CI for the same reasons as `examples.bat`; about two minutes for the twelve lanes today (BETA 995): Samples 10 and 15, the nine probe lanes behind Stage 2's answers in [WIP.HelpAddin.md](WIP.HelpAddin.md), and the help add-in in `add-in/`, built with its committed copy of the symbol index, `add-in/Resources/SYMBOLS/symbols.json`, which a local build rewrites whenever the index changes. The help lane serves the built `docs/_site` on localhost to the add-in's pane, so run `build.bat` first. Exit 0 every lane passed and the registry is as it was found, 1 a lane failed, 2 the harness failed, 3 the registry or a work folder was not put back. See [Driving the twinBASIC compiler](#driving-the-twinbasic-compiler) for its rules. +- `addin-test.bat` — tests IDE add-ins by operating an IDE: every lane in `test/addin/lanes.mjs` builds the add-ins it tests into a private copy of the install, opens a project and checks what the add-in does. Outside every gate and outside CI for the same reasons as `examples.bat`; about two minutes for the thirteen lanes today (BETA 995): Samples 10 and 15, the nine probe lanes behind Stage 2's answers in [WIP.HelpAddin.md](WIP.HelpAddin.md), and the help add-in in `add-in/` twice, built with its committed copy of the symbol index, `add-in/Resources/SYMBOLS/symbols.json`, which a local build rewrites whenever the index changes. The `help` lane serves the built `docs/_site` on localhost to the add-in's pane; `help-offline` builds the add-in with a help archive of `docs/_site-offline` and the pane's pages come from the add-in's own server. So run `build.bat` first. Exit 0 every lane passed and the registry is as it was found, 1 a lane failed, 2 the harness failed, 3 the registry or a work folder was not put back. See [Driving the twinBASIC compiler](#driving-the-twinbasic-compiler) for its rules. - `ide-test.bat` --- the same runner for scenarios that operate the IDE itself rather than an add-in: every lane in `test/ide/lanes.mjs`, base port 9660. Same exit codes, same standing outside every gate and outside CI, same rules. - `try-help-addin.bat` --- opens one IDE on the user's desktop with the help add-in built and loaded, to try it by hand, and puts the registry back once it is closed (see [Driving the twinBASIC compiler](#driving-the-twinbasic-compiler)). Needs `build.bat` first. Exit 0 closed and put back, 1 the add-in or the project did not compile, 2 the tool could not run, 3 not put back. -- `node scripts/bug_repro.mjs` --- the reproducer projects under `bugs//` for the entries of [BUGS-TO-REPORT.md](BUGS-TO-REPORT.md): `new`, `pack` (impexp, then the zip, written in Node), `compile`, `build` and `run` through `tbbuild` and `tbrun`, `vb6` (builds the optional `vb6/` VB6 project beside the twinBASIC one in a temp copy, runs its exe on a private desktop, and prints its `out.txt`; a project with a form or a user control is built without Unattended Execution, which VB6 refuses for it; `pack` zips its sources into `-vb6.zip`; VB6 is started only by `scripts/lib/vb6.mjs`, never from a shell; a graphical defect's `images` key makes `run` and `vb6` keep what the `PngDump` modules of `test/repro-templates/png/` draw as `images/-tb.png` and `-vb6.png`, with a `-compare.png` of both, tested by `test/png.test.mjs`), `file` (moves a filed entry and its reproducer to `bugs/filed//`; `file --marked` does every marked entry), and `verify`, which reads each `bugs/*/repro.json` and `bugs/filed/*/repro.json` and says whether the bug still reproduces on the newest beta. It has no wrapper. **`verify` is run by a person only**, never by a gate or CI: it needs a twinBASIC install, like `examples.bat`. Default port 9440, and `--jobs N` takes N ports from there. Its command-line cases are in `scripts/lib/cli-cases.mjs`, and need no IDE. +- `node scripts/build_help_archive.mjs` --- writes `add-in/Resources/HELP/site.zip` from `docs/_site-offline` (refusing a stale tree) and reads it back to verify it. The help add-in embeds it as a resource and serves the pages from it with its own server; a build of the add-in without it uses the live site. The file is gitignored (about 21 MB), so a build of the add-in from a clean clone is the online one; run `build.bat` and then this to get the offline one. The zip's format is fixed by the add-in's reader, which does no inflating ([WIP.HelpAddin.md](WIP.HelpAddin.md), Stage 4, increment 5). +- `node scripts/bug_repro.mjs` --- the reproducer projects under `bugs//` for the entries of [BUGS-TO-REPORT.md](BUGS-TO-REPORT.md): `new`, `pack` (impexp, then the zip, written in Node), `compile`, `build` and `run` through `tbbuild` and `tbrun`, `vb6` (builds the optional `vb6/` VB6 project beside the twinBASIC one in a temp copy, runs its exe on a private desktop, and prints its `out.txt`; a project with a form or a user control is built without Unattended Execution, which VB6 refuses for it; `pack` zips its sources into `-vb6.zip`; VB6 is started only by `scripts/lib/vb6.mjs`, never from a shell; a graphical defect's `images` key makes `run` and `vb6` keep what the `PngDump` modules of `test/repro-templates/png/` draw as `images/-tb.png` and `-vb6.png`, with a `-compare.png` of both, tested by `test/png.test.mjs`), `file` (moves a filed entry and its reproducer to `bugs/filed//`; `file --marked` does every marked entry), and `verify`, which reads each `bugs/*/repro.json` and `bugs/filed/*/repro.json` and says whether the bug still reproduces on the newest beta; a `lane` reproducer names the `ide-test` or `addin-test` lane and tests that assert its bug, which `verify` runs last through that runner, and a `probe` one a script under `scripts/`. **No reproducer should need a person at the IDE** (owner, 2026-10-05): a bug the IDE has to be operated for gets a lane test, and `manual` is left for what cannot be automated at all. It has no wrapper. **`verify` is run by a person only**, never by a gate or CI: it needs a twinBASIC install, like `examples.bat`. Default port 9440, and `--jobs N` takes N ports from there. Its command-line cases are in `scripts/lib/cli-cases.mjs`, and need no IDE. - `node scripts/vb6run.mjs ` and `node scripts/vb6run.mjs --docs [--only ]` --- build and run VB6 code, so that what a sample prints in twinBASIC can be compared with VB6: a stand-alone file of statements or a `.bas` module, or `--docs`, which builds the documentation's `check_run` fences in VB6 and says for each whether it prints what its page says (`same`, `differs`, `not VB6`, `error`, `refused`; a `projname=` group is built as a project of its own, its `slot=file` fences translated into VB6 `.cls` and `.bas` components). It has no wrapper and is outside every gate and outside CI: it needs VB6 (`--vb6`, `VB6_EXE`, or the standard install folder). **Never start `VB6.EXE` from a shell**: in Git Bash `/make` is rewritten as a path and VB6 answers with a modal box on the desktop; the tool spawns it from Node with an argument array, and a sample that calls `MsgBox` or `InputBox` or contains `End` is refused. Its `Debug.Print` rewrite and its file translation have probes in `test/example-batches.test.mjs`, and its command-line cases are in `scripts/lib/cli-cases.mjs`; both need no VB6. What it relies on is in [WIP.ExamplesBuild.md](WIP.ExamplesBuild.md). Three generators sit outside that loop and produce committed artifacts rather than build output — none runs during a build, and none is needed for one. `python scripts/build_fonts.py` rebuilds the subset webfaces under `docs/assets/fonts/` and needs a network connection; `node scripts/build_dot_metrics.mjs` regenerates `builder/inter-metrics.json` from those webfaces and needs only a browser. See [Typography](#typography). `node scripts/build_package_api.mjs` regenerates `builder/package-api.json`, the packages' declared API that the build's symbol index (`tB/symbols.json`, for the IDE help add-in) is annotated from; it needs a twinBASIC install, so **run it when the reference is re-indexed against a newer build** and commit it with the pages. See [WIP.HelpAddin.md, Stage 3](WIP.HelpAddin.md#stage-3-the-symbol-index-generated-by-the-docs-build). @@ -520,7 +523,7 @@ wrapper: | `test.bat` | `test/strftime.test.mjs` | `builder/strftime.mjs`'s `formatDate`, which no build calls because no page sets `last_modified_date`. Run by `node --test` | | `test.bat` | `test/example-batches.test.mjs` | `check_examples.mjs`'s probes, from `scripts/lib/example-batches.mjs`: batching, crash isolation through a fake lane, the canaries, the fence classifier and `check_run`'s refusals, expected output, dispatcher and marker reading, with no IDE. Run by `node --test` | | `test.bat` | `test/ports.test.mjs` | `scripts/lib/tb-ports.mjs` claims ports as the lane runners need: two claims at once are disjoint, a port in use and a live lock are passed over, a stale lock is taken over, a lock goes when its process exits. Run by `node --test` | -| `test.bat` | `check_pdf_shims_equiv` | the book's pdf-lib shims write what stock pdf-lib writes: one document written by the gate and one built with `PDFDocument.create` are each saved both ways in child processes, and each pair of files is compared object by object with streams inflated and their cross-reference entries checked; every shim must run, and the members of pdf-lib the shims patch must be those its `PATCHES` lists, each run in one document or the other unless marked there as not reached | +| `test.bat` | `check_pdf_shims_equiv` | the book's pdf-lib shims write what stock pdf-lib writes: one document written by the gate and one built with `PDFDocument.create` are each saved both ways in child processes, and each pair of files is compared object by object with streams inflated and their cross-reference entries checked; every shim must run, and the members of pdf-lib the shims patch must be those its `PATCHES` lists, each run in one document or the other unless marked there as not reached; each of them must also be in the target table its shim passes to `checkTargets`, which `book/lib/shim-targets.mjs` records for the side to ask | | `test.bat` | `check_impexp_parity` | `impexp.mjs` and `impexp.py` pass the same built-in tests, and one sequence of commands through each gives the exit code each command is there for, the same output and the same files. Without Python it reports itself skipped and passes, but fails when `CI=true` | | `test.bat` | `check_publish_policy`, `check_gate_lists`, `check_page_baseline`, `check_book_coverage`, `check_axe_patch_equiv` | the gates on the gates | diff --git a/add-in/Resources/SYMBOLS/symbols.json b/add-in/Resources/SYMBOLS/symbols.json index ffa3e528c..69e6f3bf1 100644 --- a/add-in/Resources/SYMBOLS/symbols.json +++ b/add-in/Resources/SYMBOLS/symbols.json @@ -1,6 +1,6 @@ { "format": 1, - "api": 995, + "api": 997, "packages": {"VBA":"VBA","VBRUN":"VBRUN","VB":"VB","AppGlobalClassProject":"AppGlobalClassObject","cefPackage":"CEF","CustomControls":"CustomControls","CustomControlsPackage":"CustomControls","Assert":"Assert","WebView2Package":"WebView2","WinEventLogLib":"WinEventLogLib","WinNamedPipesLib":"WinNamedPipesLib","WinNativeCommonCtls":"WinNativeCommonCtls","WinServicesLib":"WinServicesLib","tbIDE":"tbIDE"}, "interfaces": {"VBA._Collection":"VBA.Collection","VBA._ErrObject":"VBA.ErrObject","VBA.ITbExpressionService":"VBA.TbExpressionService","VBRUN._PropertyBag":"VBRUN.PropertyBag","VB._App":"VB.App","VB._Clipboard":"VB.Clipboard","VB._Screen":"VB.Screen","VB.VBGlobal":"VB.Global","AppGlobalClassProject._App":"AppGlobalClassProject.App","CustomControls._CustomControlTimer":"CustomControls.CustomControlTimer","CustomControls._CustomControlContext":"CustomControls.CustomControlContext","CustomControls._CustomFormContext":"CustomControls.CustomFormContext","CustomControls._CustomControlsCollection":"CustomControls.CustomControlsCollection","tbIDE.IAddInV1":"tbIDE.AddIn","tbIDE.IButtonV1":"tbIDE.Button","tbIDE.ICodeEditorV1":"tbIDE.CodeEditor","tbIDE.IDebugConsoleV1":"tbIDE.DebugConsole","tbIDE.IEditorV1":"tbIDE.Editor","tbIDE.IEditorsV1":"tbIDE.Editors","tbIDE.IFileV2":"tbIDE.File","tbIDE.IFileSystemV1":"tbIDE.FileSystem","tbIDE.IFileSystemItemV1":"tbIDE.FileSystemItem","tbIDE.IFolderV1":"tbIDE.Folder","tbIDE.IHostV1":"tbIDE.Host","tbIDE.IHtmlElementV1":"tbIDE.HtmlElement","tbIDE.IHtmlElementPropertiesV1":"tbIDE.HtmlElementProperties","tbIDE.IHtmlElementPropertyV1":"tbIDE.HtmlElementProperty","tbIDE.IHtmlElementsV1":"tbIDE.HtmlElements","tbIDE.IHtmlEventPropertiesV1":"tbIDE.HtmlEventProperties","tbIDE.IHtmlEventPropertyV1":"tbIDE.HtmlEventProperty","tbIDE.IKeyboardShortcutsV1":"tbIDE.KeyboardShortcuts","tbIDE.IProjectV1":"tbIDE.Project","tbIDE.IThemesV1":"tbIDE.Themes","tbIDE.IToolWindowV1":"tbIDE.ToolWindow","tbIDE.IToolWindowsV1":"tbIDE.ToolWindows","tbIDE.IToolbarV1":"tbIDE.Toolbar","tbIDE.IToolbarsV1":"tbIDE.Toolbars"}, "symbols": [ @@ -238,6 +238,7 @@ {"name":"CurrentProjectName","package":"VBA","container":"Compilation","kind":"function","url":"/tB/Modules/Compilation/CurrentProjectName"}, {"name":"CurrentSourceFile","package":"VBA","container":"Compilation","kind":"function","url":"/tB/Modules/Compilation/CurrentSourceFile"}, {"name":"ProcessorArchitecture","package":"VBA","container":"Compilation","kind":"function","url":"/tB/Modules/Compilation/ProcessorArchitecture"}, + {"name":"UnrollLoop","package":"VBA","container":"Compilation","kind":"sub","url":"/tB/Modules/Compilation/UnrollLoop"}, {"name":"Constants","package":"VBA","container":null,"kind":"module","url":"/tB/Modules/Constants/"}, {"name":"vbBack","package":"VBA","container":"Constants","kind":"constant","url":"/tB/Modules/Constants/"}, {"name":"vbCr","package":"VBA","container":"Constants","kind":"constant","url":"/tB/Modules/Constants/"}, @@ -609,6 +610,7 @@ {"name":"Math","package":"VBA","container":null,"kind":"module","url":"/tB/Modules/Math/"}, {"name":"Abs","package":"VBA","container":"Math","kind":"function","url":"/tB/Modules/Math/Abs"}, {"name":"Atn","package":"VBA","container":"Math","kind":"function","url":"/tB/Modules/Math/Atn"}, + {"name":"ByteSwap","package":"VBA","container":"Math","kind":"sub","url":"/tB/Modules/Math/ByteSwap"}, {"name":"Cos","package":"VBA","container":"Math","kind":"function","url":"/tB/Modules/Math/Cos"}, {"name":"Exp","package":"VBA","container":"Math","kind":"function","url":"/tB/Modules/Math/Exp"}, {"name":"Log","package":"VBA","container":"Math","kind":"function","url":"/tB/Modules/Math/Log"}, @@ -616,6 +618,9 @@ {"name":"Rnd","package":"VBA","container":"Math","kind":"function","url":"/tB/Modules/Math/Rnd"}, {"name":"Round","package":"VBA","container":"Math","kind":"function","url":"/tB/Modules/Math/Round"}, {"name":"Sgn","package":"VBA","container":"Math","kind":"function","url":"/tB/Modules/Math/Sgn"}, + {"name":"ShiftRotateLeft","package":"VBA","container":"Math","kind":"sub","url":"/tB/Modules/Math/ShiftRotateLeft"}, + {"name":"ShiftRotateRight","package":"VBA","container":"Math","kind":"sub","url":"/tB/Modules/Math/ShiftRotateRight"}, + {"name":"ShiftUnsignedRight","package":"VBA","container":"Math","kind":"sub","url":"/tB/Modules/Math/ShiftUnsignedRight"}, {"name":"Sin","package":"VBA","container":"Math","kind":"function","url":"/tB/Modules/Math/Sin"}, {"name":"Sqr","package":"VBA","container":"Math","kind":"function","url":"/tB/Modules/Math/Sqr"}, {"name":"Tan","package":"VBA","container":"Math","kind":"function","url":"/tB/Modules/Math/Tan"}, @@ -2446,8 +2451,11 @@ {"name":"Opacity","package":"VB","container":"MDIForm","kind":"property","url":"/tB/Packages/VB/MDIForm/#opacity"}, {"name":"Picture","package":"VB","container":"MDIForm","kind":"property","url":"/tB/Packages/VB/MDIForm/#picture"}, {"name":"PictureDpiScaling","package":"VB","container":"MDIForm","kind":"property","url":"/tB/Packages/VB/MDIForm/#picturedpiscaling"}, + {"name":"Point","package":"VB","container":"MDIForm","kind":"method","url":"/tB/Packages/VB/MDIForm/#point"}, {"name":"PopUpMenu","package":"VB","container":"MDIForm","kind":"method","url":"/tB/Packages/VB/MDIForm/#popupmenu"}, + {"name":"PrintForm","package":"VB","container":"MDIForm","kind":"method","url":"/tB/Packages/VB/MDIForm/#printform"}, {"name":"QueryUnload","package":"VB","container":"MDIForm","kind":"event","url":"/tB/Packages/VB/MDIForm/#queryunload"}, + {"name":"Refresh","package":"VB","container":"MDIForm","kind":"method","url":"/tB/Packages/VB/MDIForm/#refresh"}, {"name":"Resize","package":"VB","container":"MDIForm","kind":"event","url":"/tB/Packages/VB/MDIForm/#resize"}, {"name":"RightToLeft","package":"VB","container":"MDIForm","kind":"property","url":"/tB/Packages/VB/MDIForm/#righttoleft"}, {"name":"ScaleHeight","package":"VB","container":"MDIForm","kind":"property","url":"/tB/Packages/VB/MDIForm/#scaleheight"}, diff --git a/add-in/Settings b/add-in/Settings index 016d3ed78..6b835f144 100644 --- a/add-in/Settings +++ b/add-in/Settings @@ -3,7 +3,7 @@ "project.appTitle": "tbDocsHelp", "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", "project.buildType": "Standard DLL", - "project.description": "The twinBASIC documentation help add-in: F1 shows the page for the name under the cursor in a help pane, which can also search the index. WIP.HelpAddin.md is its plan. The symbol index is embedded as Resources/SYMBOLS/symbols.json, a committed copy of a docs build's tB/symbols.json.", + "project.description": "The twinBASIC documentation help add-in: F1 shows the page for the name under the cursor in a help pane, which can also search the index. WIP.HelpAddin.md is its plan. The symbol index is embedded as Resources/SYMBOLS/symbols.json, a committed copy of a docs build's tB/symbols.json. With Resources/HELP/site.zip, which scripts/build_help_archive.mjs writes and git ignores, the pane's pages come from the add-in's own server instead of the live site.", "project.exportPathIsV2": true, "project.id": "{4581D804-116B-436E-B7AB-1E4417F9703C}", "project.name": "tbDocsHelp", diff --git a/add-in/Sources/HelpAddIn.twin b/add-in/Sources/HelpAddIn.twin index 573a1f316..c9638491a 100644 --- a/add-in/Sources/HelpAddIn.twin +++ b/add-in/Sources/HelpAddIn.twin @@ -4,6 +4,8 @@ ' search. For the name under the cursor the compiler is asked where it is ' declared, through its hover (CompilerHover), so that c.Add finds ' Collection.Add when c is a Collection; a selection is looked up as it is. +' The pages come from the add-in's own server when it was built with the help +' archive (HelpServer), and from the live site otherwise. ' Every line it prints to the DEBUG CONSOLE starts "[tbDocsHelp] ", except ' "open " (see OpenUrl). Private Class HelpAddIn @@ -25,6 +27,7 @@ Private Class HelpAddIn Private HoverSink As HtmlElement ' where the compiler's answers come Private AskSeq As Long ' the number of the last question Private Asked As String ' its word, "" once answered + Private Server As HelpServer ' Nothing unless the add-in has the archive Private Const BASE_URL As String = "https://docs.twinbasic.com" Private Const TAG As String = "[tbDocsHelp] " @@ -48,6 +51,7 @@ Private Class HelpAddIn Set HelpButton = Host.Toolbars(0).AddButton("tbDocsHelp", "Help") Host.KeyboardShortcuts.Add "f1", AddressOf OnF1 Dim indexState As String = LoadIndex() + StartServer PageUrl = GetSetting(SETTINGS_APP, "Pane", "Page", "") BuildPane ' The scenarios wait for this line, and check the switch in it before @@ -67,8 +71,30 @@ Private Class HelpAddIn Return "no index (" & Err.Description & ")" End Function - ' The site the pages come from. Under the test harness TB_DOCS_HELP_SITE - ' can name another, a copy of the site the lane serves itself. + ' Serves the pages from the archive, when the add-in was built with one + ' (HelpServer). The line it prints names the origin, which the scenarios + ' read. + Private Sub StartServer() + Set Server = New HelpServer + Dim serving As String = Server.Start() + If Len(serving) > 0 Then Host.DebugConsole.PrintText TAG & serving + If Len(Server.Origin) = 0 Then Set Server = Nothing + End Sub + + Private Sub Class_Terminate() + If Not Server Is Nothing Then Server.CloseAll + End Sub + + ' Where the pane's pages come from: the add-in's own server when it has the + ' archive, otherwise the site. + Private Function PagesUrl() As String + If Not Server Is Nothing Then Return Server.Origin + Return SiteUrl() + End Function + + ' The site, which Open in browser always opens. Under the test harness + ' TB_DOCS_HELP_SITE can name another, a copy of the site the lane serves + ' itself. Private Function SiteUrl() As String If TestSwitchOn() Then Dim site As String = Environ$("TB_DOCS_HELP_SITE") @@ -90,7 +116,7 @@ Private Class HelpAddIn If group = "light" Or group = "dark" Then query = "?theme=" & group & "&pane=1" Dim hash As Long = InStr(url, "#") If hash = 0 Then hash = Len(url) + 1 - Return SiteUrl() & Left$(url, hash - 1) & query & Mid$(url, hash) + Return PagesUrl() & Left$(url, hash - 1) & query & Mid$(url, hash) End Function ' A theme change loads the page again with the new theme. The pane itself diff --git a/add-in/Sources/HelpServer.twin b/add-in/Sources/HelpServer.twin new file mode 100644 index 000000000..f99dbf0a5 --- /dev/null +++ b/add-in/Sources/HelpServer.twin @@ -0,0 +1,464 @@ +' Serves the documentation from the archive the add-in embeds as +' Resources/HELP/site.zip, on 127.0.0.1 at a port Windows picks, so the help +' pane needs no network. The archive is the site's offline tree, written by +' scripts/build_help_archive.mjs. A path is found as GitHub Pages finds it: +' /a/b is a/b.html, or a redirect to /a/b/ when a/b/index.html exists, and the +' query string is ignored. An entry the zip deflated is sent as it is, in a +' gzip wrapper (Content-Encoding: gzip), so nothing is inflated here. +' +' It listens on 127.0.0.1 and names itself http://localhost:: a frame +' on 127.0.0.1 is missing from the IDE page's frame tree, where the help lane +' looks for the pane's page, and one on localhost is not. +' +' The sockets belong to the thread that starts the server, the IDE's: Winsock +' posts their events to a message-only window (WSAAsyncSelect), so no request +' blocks the thread. Every reply closes its connection. +Private Class HelpServer + Private Declare PtrSafe Function WSAStartup Lib "ws2_32" (ByVal wVersionRequested As Integer, ByRef lpWSAData As Any) As Long + Private Declare PtrSafe Function WSAGetLastError Lib "ws2_32" () As Long + Private Declare PtrSafe Function WSAAsyncSelect Lib "ws2_32" (ByVal s As LongPtr, ByVal hWnd As LongPtr, _ + ByVal wMsg As Long, ByVal lEvent As Long) As Long + Private Declare PtrSafe Function OpenSocket Lib "ws2_32" Alias "socket" (ByVal af As Long, ByVal kind As Long, _ + ByVal protocol As Long) As LongPtr + Private Declare PtrSafe Function BindSocket Lib "ws2_32" Alias "bind" (ByVal s As LongPtr, ByRef name As Any, _ + ByVal nameLength As Long) As Long + Private Declare PtrSafe Function ListenOn Lib "ws2_32" Alias "listen" (ByVal s As LongPtr, ByVal backlog As Long) As Long + Private Declare PtrSafe Function GetSockName Lib "ws2_32" Alias "getsockname" (ByVal s As LongPtr, ByRef name As Any, _ + ByRef nameLength As Long) As Long + Private Declare PtrSafe Function AcceptOn Lib "ws2_32" Alias "accept" (ByVal s As LongPtr, ByVal addr As LongPtr, _ + ByVal addrLength As LongPtr) As LongPtr + Private Declare PtrSafe Function Recv Lib "ws2_32" Alias "recv" (ByVal s As LongPtr, ByRef buf As Any, _ + ByVal length As Long, ByVal flags As Long) As Long + Private Declare PtrSafe Function Send Lib "ws2_32" Alias "send" (ByVal s As LongPtr, ByRef buf As Any, _ + ByVal length As Long, ByVal flags As Long) As Long + Private Declare PtrSafe Function ShutdownSocket Lib "ws2_32" Alias "shutdown" (ByVal s As LongPtr, ByVal how As Long) As Long + Private Declare PtrSafe Function CloseSocket Lib "ws2_32" Alias "closesocket" (ByVal s As LongPtr) As Long + Private Declare PtrSafe Function CreateWindowExW Lib "user32" (ByVal dwExStyle As Long, ByVal lpClassName As LongPtr, _ + ByVal lpWindowName As LongPtr, ByVal dwStyle As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, _ + ByVal nHeight As Long, ByVal hWndParent As LongPtr, ByVal hMenu As LongPtr, ByVal hInstance As LongPtr, _ + ByVal lpParam As LongPtr) As LongPtr + Private Declare PtrSafe Function DestroyWindow Lib "user32" (ByVal hWnd As LongPtr) As Long + #If Win64 Then + Private Declare PtrSafe Function SetWindowProc Lib "user32" Alias "SetWindowLongPtrW" (ByVal hWnd As LongPtr, _ + ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr + #Else + Private Declare PtrSafe Function SetWindowProc Lib "user32" Alias "SetWindowLongW" (ByVal hWnd As LongPtr, _ + ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr + #End If + Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Any, _ + ByRef Source As Any, ByVal Length As LongPtr) + + Private Const FD_READ As Long = 1 + Private Const FD_WRITE As Long = 2 + Private Const FD_ACCEPT As Long = 8 + Private Const FD_CLOSE As Long = 32 + Private Const WSAEWOULDBLOCK As Long = 10035 + Private Const INVALID_SOCKET As LongPtr = -1 + Private Const SOCKET_ERROR As Long = -1 + Private Const SD_SEND As Long = 1 + Private Const HWND_MESSAGE As LongPtr = -3 + Private Const GWLP_WNDPROC As Long = -4 + Private Const MAX_REQUEST As Long = 16384 ' a request's head, at most + Private Const CHUNK As Long = 65536 ' sent at a time + + ' A connection's state: reading its request, sending its reply, or sent and + ' waiting for the browser to close it. + Private Const READING As Long = 0 + Private Const SENDING As Long = 1 + Private Const SENT As Long = 2 + + Private Type Connection + Sock As LongPtr ' 0 in a free slot + State As Long + Request As String ' as received, one character a byte + Reply() As Byte + Done As Long ' bytes of Reply sent + End Type + + Private Zip() As Byte + Private Entries As Collection ' an entry's number by its name + Private Names() As String + Private Method() As Long ' 0 stored, 8 deflated + Private DataAt() As Long ' where its data starts in Zip + Private Packed() As Long ' its size in Zip + Private CentralAt() As Long ' its central directory record, for the CRC and size + + Private Listener As LongPtr + Private Window As LongPtr + Private Conns() As Connection + Private ConnCount As Long + + ' "http://127.0.0.1:" while serving, else "". + Public Origin As String + Public FileCount As Long + + ' Starts serving and returns what it serves, for the DEBUG CONSOLE, or "" + ' when the add-in has no archive. Origin stays "" unless it is serving. + Public Function Start() As String + On Error GoTo NoArchive + Zip = LoadResDataInternal("site.zip", "HELP") + On Error GoTo Failed + If Not ReadDirectory() Then Err.Raise 5, , "site.zip is not an archive this add-in reads" + Dim wsa(1023) As Byte + If WSAStartup(&H202, wsa(0)) <> 0 Then Err.Raise 5, , "WSAStartup failed" + Listener = OpenSocket(2, 1, 6) ' AF_INET, SOCK_STREAM, IPPROTO_TCP + If Listener = INVALID_SOCKET Then + Listener = 0 + Fail "socket" + End If + Dim addr(15) As Byte ' sockaddr_in for 127.0.0.1, port 0 + addr(0) = 2 + addr(4) = 127 + addr(7) = 1 + If BindSocket(Listener, addr(0), 16) <> 0 Then Fail "bind" + If ListenOn(Listener, 64) <> 0 Then Fail "listen" + Dim size As Long = 16 + If GetSockName(Listener, addr(0), size) <> 0 Then Fail "getsockname" + Window = CreateWindowExW(0, StrPtr("STATIC"), 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, 0) + If Window = 0 Then Err.Raise 5, , "CreateWindowExW failed" + Set HelpServerWindow.Server = Me + HelpServerWindow.PreviousProc = SetWindowProc(Window, GWLP_WNDPROC, AddressOf HelpServerWindow.WndProc) + If WSAAsyncSelect(Listener, Window, HelpServerWindow.WM_SOCKET, FD_ACCEPT) <> 0 Then Fail "WSAAsyncSelect" + Origin = "http://localhost:" & (CLng(addr(2)) * 256 + addr(3)) + Return "serving site.zip, " & FileCount & " files, at " & Origin + NoArchive: + Return "" + Failed: + Dim message As String = Err.Description + CloseAll + Return "not serving site.zip (" & message & ")" + End Function + + Private Sub Fail(ByVal what As String) + Err.Raise 5, , what & " failed, Winsock error " & WSAGetLastError() + End Sub + + Public Sub CloseAll() + Dim i As Long + For i = 0 To ConnCount - 1 + If Conns(i).Sock <> 0 Then CloseSocket Conns(i).Sock + Next + ConnCount = 0 + Erase Conns + If Listener <> 0 Then CloseSocket Listener + Listener = 0 + If Window <> 0 Then DestroyWindow Window + Window = 0 + If HelpServerWindow.Server Is Me Then Set HelpServerWindow.Server = Nothing + Origin = "" + End Sub + + ' ---- the archive ---- + + ' Reads the central directory: no zip64, no data descriptors, the entries + ' as build_help_archive.mjs writes them. + Private Function ReadDirectory() As Boolean + Dim eocd As Long = UBound(Zip) + 1 - 22 + Do While eocd >= 0 + If U32(eocd) = &H06054B50 Then Exit Do + eocd = eocd - 1 + Loop + If eocd < 0 Then Return False + Dim count As Long = U16(eocd + 10) + If count = 0 Then Return False + Dim at As Long = U32(eocd + 16) + ReDim Names(count - 1) + ReDim Method(count - 1) + ReDim DataAt(count - 1) + ReDim Packed(count - 1) + ReDim CentralAt(count - 1) + Set Entries = New Collection + Dim i As Long + For i = 0 To count - 1 + If U32(at) <> &H02014B50 Then Return False + Dim nameLength As Long = U16(at + 28) + Dim header As Long = U32(at + 42) + If U32(header) <> &H04034B50 Then Return False + Names(i) = Utf8(at + 46, nameLength) + Method(i) = U16(at + 10) + If Method(i) <> 0 And Method(i) <> 8 Then Return False + Packed(i) = U32(at + 20) + CentralAt(i) = at + DataAt(i) = header + 30 + U16(header + 26) + U16(header + 28) + Entries.Add i, Names(i) + at = at + 46 + nameLength + U16(at + 30) + U16(at + 32) + Next + FileCount = count + Return True + End Function + + Private Function U16(ByVal at As Long) As Long + Return Zip(at) Or CLng(Zip(at + 1)) * &H100& + End Function + + ' Offsets and sizes only: an archive under 2 GB keeps them below &H80000000. + Private Function U32(ByVal at As Long) As Long + Return Zip(at) Or CLng(Zip(at + 1)) * &H100& Or CLng(Zip(at + 2)) * &H10000 Or CLng(Zip(at + 3)) * &H1000000 + End Function + + Private Function Utf8(ByVal at As Long, ByVal length As Long) As String + If length = 0 Then Return "" + Dim b() As Byte + ReDim b(length - 1) + CopyMemory b(0), Zip(at), length + Return StrConv(b, VbStrConv.vbFromUTF8) + End Function + + Private Function Lookup(ByVal name As String) As Long + On Error GoTo Missing + Return Entries(name) + Missing: + Return -1 + End Function + + ' ---- the sockets ---- + + ' Called by HelpServerWindow.WndProc for each event Winsock posts. + Public Sub OnSocket(ByVal sock As LongPtr, ByVal events As Long, ByVal failure As Long) + Dim i As Long = -1 + On Error GoTo Failed + If sock = Listener Then + If events = FD_ACCEPT And failure = 0 Then AcceptOne + Exit Sub + End If + i = Slot(sock) + If i < 0 Then Exit Sub + If failure <> 0 Then + Drop i + Exit Sub + End If + Select Case events + Case FD_READ + Receive i + Case FD_WRITE + Pump i + Case FD_CLOSE + Drop i + End Select + Exit Sub + Failed: + If i >= 0 Then Drop i + End Sub + + Private Sub AcceptOne() + Dim s As LongPtr = AcceptOn(Listener, 0, 0) + If s = INVALID_SOCKET Then Exit Sub + If WSAAsyncSelect(s, Window, HelpServerWindow.WM_SOCKET, FD_READ Or FD_WRITE Or FD_CLOSE) <> 0 Then + CloseSocket s + Exit Sub + End If + Dim i As Long = Slot(0) + If i < 0 Then + i = ConnCount + ConnCount = ConnCount + 1 + ReDim Preserve Conns(ConnCount - 1) + End If + Conns(i).Sock = s + Conns(i).State = READING + Conns(i).Request = "" + Conns(i).Done = 0 + End Sub + + Private Function Slot(ByVal sock As LongPtr) As Long + Dim i As Long + For i = 0 To ConnCount - 1 + If Conns(i).Sock = sock Then Return i + Next + Return -1 + End Function + + Private Sub Drop(ByVal i As Long) + If Conns(i).Sock <> 0 Then CloseSocket Conns(i).Sock + Conns(i).Sock = 0 + Conns(i).State = READING + Conns(i).Request = "" + Erase Conns(i).Reply + Conns(i).Done = 0 + End Sub + + Private Sub Receive(ByVal i As Long) + Dim buf(4095) As Byte + Dim n As Long = Recv(Conns(i).Sock, buf(0), 4096, 0) + If n = 0 Then + Drop i + Exit Sub + End If + If n < 0 Then + If WSAGetLastError() <> WSAEWOULDBLOCK Then Drop i + Exit Sub + End If + If Conns(i).State <> READING Then Exit Sub ' anything after the request's head is ignored + Dim k As Long + For k = 0 To n - 1 + Conns(i).Request = Conns(i).Request & ChrW$(buf(k)) + Next + If InStr(Conns(i).Request, vbCrLf & vbCrLf) = 0 Then + If Len(Conns(i).Request) > MAX_REQUEST Then Drop i + Exit Sub + End If + Conns(i).Reply = Answer(Left$(Conns(i).Request, InStr(Conns(i).Request, vbCrLf) - 1)) + Conns(i).Request = "" + Conns(i).State = SENDING + Pump i + End Sub + + ' Sends what the socket takes; FD_WRITE calls it again for the rest. Once + ' all is sent, the browser closes the connection (FD_CLOSE). + Private Sub Pump(ByVal i As Long) + If Conns(i).State <> SENDING Then Exit Sub + Dim size As Long = UBound(Conns(i).Reply) + 1 + Do While Conns(i).Done < size + Dim length As Long = size - Conns(i).Done + If length > CHUNK Then length = CHUNK + Dim n As Long = Send(Conns(i).Sock, Conns(i).Reply(Conns(i).Done), length, 0) + If n = SOCKET_ERROR Then + If WSAGetLastError() <> WSAEWOULDBLOCK Then Drop i + Exit Sub + End If + Conns(i).Done = Conns(i).Done + n + Loop + Erase Conns(i).Reply + Conns(i).State = SENT + ShutdownSocket Conns(i).Sock, SD_SEND + End Sub + + ' ---- HTTP ---- + + Private Function Answer(ByVal requestLine As String) As Byte() + On Error GoTo BadRequest + Dim parts() As String = Split(requestLine, " ") + If UBound(parts) <> 2 Then GoTo BadRequest + If parts(0) <> "GET" And parts(0) <> "HEAD" Then Return Reply("405 Method Not Allowed", -1, False) + Dim headOnly As Boolean = parts(0) = "HEAD" + Dim target As String = parts(1) + Dim query As String + Dim q As Long = InStr(target, "?") + If q > 0 Then + query = Mid$(target, q) + target = Left$(target, q - 1) + End If + Dim path As String = Unescape(target) + If Left$(path, 1) <> "/" Or InStr(path, "..") > 0 Or InStr(path, "\") > 0 Then + Return Reply("404 Not Found", Lookup("404.html"), headOnly) + End If + Dim name As String = Mid$(path, 2) + If Len(name) = 0 Or Right$(name, 1) = "/" Then name = name & "index.html" + Dim i As Long = Lookup(name) + If i < 0 And InStr(Mid$(name, InStrRev(name, "/") + 1), ".") = 0 Then + i = Lookup(name & ".html") + If i < 0 And Lookup(name & "/index.html") >= 0 Then + Return Reply("301 Moved Permanently", -1, headOnly, target & "/" & query) + End If + End If + If i < 0 Then Return Reply("404 Not Found", Lookup("404.html"), headOnly) + Return Reply("200 OK", i, headOnly) + BadRequest: + Return Reply("400 Bad Request", -1, False) + End Function + + ' The reply's bytes: the head and, unless headOnly, the entry's data, which + ' a deflated entry sends between a gzip header and its CRC32 and size. + Private Function Reply(ByVal status As String, ByVal entry As Long, ByVal headOnly As Boolean, _ + Optional ByVal location As String = "") As Byte() + Dim head As String = "HTTP/1.1 " & status & vbCrLf + Dim gzip As Boolean + Dim bodySize As Long + If entry >= 0 Then + gzip = Method(entry) = 8 + bodySize = Packed(entry) + IIf(gzip, 18, 0) + head = head & "Content-Type: " & ContentType(Names(entry)) & vbCrLf + If gzip Then head = head & "Content-Encoding: gzip" & vbCrLf + head = head & "Cache-Control: max-age=3600" & vbCrLf + End If + If Len(location) > 0 Then head = head & "Location: " & location & vbCrLf + head = head & "Content-Length: " & bodySize & vbCrLf & "Connection: close" & vbCrLf & vbCrLf + Dim headLength As Long = Len(head) + Dim out() As Byte + ReDim out(headLength + IIf(headOnly, 0, bodySize) - 1) + Dim k As Long + For k = 1 To headLength + out(k - 1) = AscW(Mid$(head, k, 1)) And &HFF + Next + If headOnly Or entry < 0 Then Return out + Dim p As Long = headLength + If gzip Then + out(p) = &H1F + out(p + 1) = &H8B + out(p + 2) = 8 + out(p + 9) = &HFF ' flags, time and extra flags 0, OS unknown + p = p + 10 + End If + If Packed(entry) > 0 Then CopyMemory out(p), Zip(DataAt(entry)), Packed(entry) + p = p + Packed(entry) + If gzip Then + CopyMemory out(p), Zip(CentralAt(entry) + 16), 4 ' CRC32 + CopyMemory out(p + 4), Zip(CentralAt(entry) + 24), 4 ' uncompressed size + End If + Return out + End Function + + ' A request path's %XX escapes decoded, as UTF-8. + Private Function Unescape(ByVal s As String) As String + If Len(s) = 0 Then Return "" + Dim b() As Byte + ReDim b(Len(s) - 1) + Dim n As Long, k As Long = 1 + Do While k <= Len(s) + Dim c As String = Mid$(s, k, 1) + If c = "%" Then + b(n) = CByte("&H" & Mid$(s, k + 1, 2)) + k = k + 3 + Else + b(n) = AscW(c) And &HFF + k = k + 1 + End If + n = n + 1 + Loop + ReDim Preserve b(n - 1) + Return StrConv(b, VbStrConv.vbFromUTF8) + End Function + + Private Function ContentType(ByVal name As String) As String + Select Case LCase$(Mid$(name, InStrRev(name, ".") + 1)) + Case "html": Return "text/html; charset=utf-8" + Case "css": Return "text/css; charset=utf-8" + Case "js": Return "text/javascript; charset=utf-8" + Case "json": Return "application/json" + Case "svg": Return "image/svg+xml" + Case "png": Return "image/png" + Case "jpg", "jpeg": Return "image/jpeg" + Case "gif": Return "image/gif" + Case "webp": Return "image/webp" + Case "ico": Return "image/x-icon" + Case "woff2": Return "font/woff2" + Case "woff": Return "font/woff" + Case "txt", "dot": Return "text/plain; charset=utf-8" + Case "xml": Return "application/xml" + Case "pdf": Return "application/pdf" + Case "mp4": Return "video/mp4" + Case Else: Return "application/octet-stream" + End Select + End Function +End Class + +' The procedure of HelpServer's message-only window: Winsock's events go to +' the server, every other message to the window's own procedure. +Module HelpServerWindow + Private Declare PtrSafe Function CallWindowProcW Lib "user32" (ByVal lpPrevWndFunc As LongPtr, ByVal hWnd As LongPtr, _ + ByVal uMsg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr + + Public Const WM_SOCKET As Long = &H8001& ' WM_APP + 1 + Public Server As HelpServer + Public PreviousProc As LongPtr + + Public Function WndProc(ByVal hWnd As LongPtr, ByVal uMsg As Long, ByVal wParam As LongPtr, _ + ByVal lParam As LongPtr) As LongPtr + If uMsg = WM_SOCKET Then + If Not Server Is Nothing Then + Server.OnSocket wParam, CLng(lParam And &HFFFF&), CLng((lParam \ &H10000) And &HFFFF&) + End If + Return 0 + End If + Return CallWindowProcW(PreviousProc, hWnd, uMsg, wParam, lParam) + End Function +End Module diff --git a/book/lib/fast-array-onebuf.mjs b/book/lib/fast-array-onebuf.mjs index 38cea8a06..12dccc7ec 100644 --- a/book/lib/fast-array-onebuf.mjs +++ b/book/lib/fast-array-onebuf.mjs @@ -5,13 +5,10 @@ // PDFObject references directly. No encoding, no decode on read; the // hot path is `arrayMain[start + i]`. // -// Phase 3 of fast-dict-encoded did the same range-view refactor on -// PDFArray but used a Float64Array + encoded slots (mirroring its -// dict shape). The encoded backing cost ~300 ms of decodeValue -// dispatch during save (PDFArray.copyBytesInto iterates ~500 k -// elements). This shim keeps the heap win (~19 MB on the book by -// removing each PDFArray's per-instance `this.array = []`) without -// paying the decode cost: slots are JS references, reads are direct. +// The heap win (~19 MB on the book) comes from removing each +// PDFArray's per-instance `this.array = []`. Slots are not encoded, +// because PDFArray.copyBytesInto reads ~500 k elements during save and +// a per-slot decode there costs more than the heap it saves. // // 40-bit packed Number layout (well within Number.MAX_SAFE_INTEGER): // bits 0-23: start (24 bits, max 16 M slots in arrayMain) @@ -36,8 +33,7 @@ // fast-dict-onebuf. The buffer and the context are this shim's own, so // it loads without that one. // -// Composes with --fast-dict-onebuf. Mutually exclusive with -// --fast-dict-encoded (which subsumes both via its own encoded shape). +// Composes with --fast-dict-onebuf. // // At load it checks that what it replaces is as in pdf-lib 1.17.1 (see // shim-targets.mjs), PDFArray's constructor included, since no instance is diff --git a/book/lib/fast-dict-onebuf.mjs b/book/lib/fast-dict-onebuf.mjs index 750abc7b8..97bb82dbe 100644 --- a/book/lib/fast-dict-onebuf.mjs +++ b/book/lib/fast-dict-onebuf.mjs @@ -18,7 +18,7 @@ // onebuf-range.mjs's packing handles. // // PDFPageLeaf collapses to the same single-`d` field as plain PDFDict; -// `normalized` and `autoNormalizeCTM` are gettters/setters that mask +// `normalized` and `autoNormalizeCTM` are getters/setters that mask // in/out of `d`'s bits 23 and 24. Heap floor matches `_FastDict` (no // separate boolean property slots). // @@ -46,9 +46,6 @@ // onebuf-range.mjs's, shared with fast-array-onebuf. The buffer and the // context are this shim's own, so it loads without that one. // -// Mutually exclusive with --fast-dict-double / --fast-dict-view / -// --fast-dict-array. -// // At load it checks that what it replaces is as in pdf-lib 1.17.1 (see // shim-targets.mjs), the constructors of PDFDict and its three subclasses // included, since no instance is built with them; and throws otherwise. It @@ -512,10 +509,4 @@ if (!PDFDict.prototype.__fastDictOnebufInstalled) { }; PDFDict.prototype.__fastDictOnebufInstalled = true; - // Mark subsumed shims as installed. - PDFDict.prototype.__fastDictDoubleInstalled = true; - PDFDict.prototype.__fastDictViewInstalled = true; - PDFDict.prototype.__fastDictArrayInstalled = true; - PDFDict.prototype.__fastDictIterInstalled = true; - PDFObjectParser.prototype.__fastParseDictInstalled = true; } diff --git a/book/lib/fast-indirect-objects.mjs b/book/lib/fast-indirect-objects.mjs index f1795d7cc..709834a4f 100644 --- a/book/lib/fast-indirect-objects.mjs +++ b/book/lib/fast-indirect-objects.mjs @@ -1,8 +1,8 @@ // Replace PDFContext.indirectObjects (Map) with a // dense array keyed by objectNumber for the gen=0 path. // -// Motivation. With fast-dict-array applied, the only remaining hot -// Map.set in the process-phase heap profile is +// Motivation. Once PDFDict is not Map-backed (fast-dict-onebuf), the +// only remaining hot Map.set in the process-phase heap profile is // PDFContext.assign's `this.indirectObjects.set(ref, object)`: // // $ node find-heap-callers.mjs .heapprofile set diff --git a/book/lib/fast-pdfnumber-pool.mjs b/book/lib/fast-pdfnumber-pool.mjs index 6ef70c139..4e3600171 100644 --- a/book/lib/fast-pdfnumber-pool.mjs +++ b/book/lib/fast-pdfnumber-pool.mjs @@ -1,7 +1,7 @@ // Pool PDFNumber instances by value. // -// With fast-refs / fast-indirect-objects / fast-dict-array applied, -// the residual heap profile attributes ~15 MB of self-size to +// With the other heap shims applied (fast-refs-class, +// fast-indirect-objects, fast-dict-onebuf), the residual heap profile attributes ~15 MB of self-size to // PDFObjectParser.parseNumberOrRef -- mostly inlined `new // PDFNumber(value)` calls (each of which also allocates a fresh // stringValue via `numberToString(value)`): diff --git a/book/lib/shim-targets.mjs b/book/lib/shim-targets.mjs index 6417e283c..4c17d5654 100644 --- a/book/lib/shim-targets.mjs +++ b/book/lib/shim-targets.mjs @@ -25,16 +25,42 @@ export function fingerprint(fn) { return createHash("sha256").update(Function.prototype.toString.call(fn)).digest("hex").slice(0, 12); } +// Each call's roots and table, by the calling shim's URL, so that +// scripts/check_pdf_shims_equiv.mjs can ask whether a member a shim patched is +// in its table (tableCovers). Nothing else reads it. +const registered = new Map(); + +// A path such as 'PDFDict.prototype.get', followed from the roots: the object +// that holds the member, which is undefined if the path leads nowhere, and the +// member's name. +function followPath(roots, path) { + const names = path.split("."); + const key = names.pop(); + let holder = roots; + for (const name of names) holder = holder?.[name]; + return { names, key, holder }; +} + +// Whether the table `shimUrl` gave checkTargets has an entry for `key` of +// `holder`, whether a fingerprint or ABSENT. It is false for a shim that has +// not called checkTargets. +export function tableCovers(shimUrl, holder, key) { + const entry = registered.get(shimUrl); + if (!entry) return false; + return Object.keys(entry.targets).some((path) => { + const at = followPath(entry.roots, path); + return at.holder === holder && at.key === key; + }); +} + // shimUrl is the shim's import.meta.url. roots holds the pdf-lib objects the // shim imports, by name; each key of targets is a path from one of them, such // as 'PDFDict.prototype.get', and each value is [arity, fingerprint] or ABSENT. export function checkTargets(shimUrl, roots, targets) { + registered.set(shimUrl, { roots, targets }); const faults = []; for (const [path, expected] of Object.entries(targets)) { - const names = path.split("."); - const key = names.pop(); - let holder = roots; - for (const name of names) holder = holder?.[name]; + const { names, key, holder } = followPath(roots, path); if (Object(holder) !== holder) { faults.push(`${path}: ${names.join(".")} is missing`); } else if (expected === ABSENT) { diff --git a/book/render-book.mjs b/book/render-book.mjs index 6870dbd03..377cf70dd 100644 --- a/book/render-book.mjs +++ b/book/render-book.mjs @@ -84,15 +84,10 @@ import { exitOnCrash, numberOption, parseCli, printHelpAndExit, withUsageError } // Mutations: in-place replace for existing keys, COW (copy // range to tail, push new pair) for new keys or delete. // PDFContext is a singleton -- one PDFDocument.load per -// process; a second distinct context throws. Subsumes -// fast-dict-array. Process-phase heap traffic drops from the -// Map-backed baseline of ~152 MB down to ~66 MB (-57%); -22% -// beyond fast-dict-array. See "One-buffer PDFDict" in +// process; a second distinct context throws. Process-phase heap +// traffic drops from the Map-backed baseline of ~152 MB to +// ~66 MB (-57%). See "One-buffer PDFDict" in // perf/notes/08-pdf-lib.md. -// -// The earlier dict-shape shims it replaced (fast-dict-array, -// fast-dict-iter, fast-parse-dict) were deleted; their measurements -// are in perf/notes/08-pdf-lib.md, and git history has the code. // fast-parse-object -- replace PDFObjectParser.prototype.parseObject // with a first-byte-dispatch version that gates the three // matchKeyword (true / false / null) scans behind a byte check. @@ -132,7 +127,7 @@ import { exitOnCrash, numberOption, parseCli, printHelpAndExit, withUsageError } // in step with this shim. // fast-indirect-objects -- replace PDFContext.indirectObjects // (Map) with a dense array indexed by -// objectNumber for the gen=0 path. With fast-dict-array applied, +// objectNumber for the gen=0 path. Once PDFDict is not Map-backed, // PDFContext.assign's `this.indirectObjects.set(ref, object)` is // the only hot Map.set left in the heap profile (~7 MB of set // traffic from the parser's once-per-indirect-object assign). diff --git a/bugs/array-const-after-procedure-not-found/array-const-after-procedure-not-found.twinproj b/bugs/array-const-after-procedure-not-found/array-const-after-procedure-not-found.twinproj new file mode 100644 index 000000000..f5923a56a Binary files /dev/null and b/bugs/array-const-after-procedure-not-found/array-const-after-procedure-not-found.twinproj differ diff --git a/bugs/array-const-after-procedure-not-found/repro.json b/bugs/array-const-after-procedure-not-found/repro.json new file mode 100644 index 000000000..92a3ac258 --- /dev/null +++ b/bugs/array-const-after-procedure-not-found/repro.json @@ -0,0 +1,7 @@ +{ + "mode": "compile", + "expect": { + "exit": 1, + "diagnostics": ["TB5079"] + } +} diff --git a/bugs/array-const-after-procedure-not-found/src/Settings b/bugs/array-const-after-procedure-not-found/src/Settings new file mode 100644 index 000000000..73daf6f22 --- /dev/null +++ b/bugs/array-const-after-procedure-not-found/src/Settings @@ -0,0 +1,61 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "ArrayConstAfterProcedureNotFound", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: A module-level array Const declared after a procedure is not found by the procedures that follow it", + "project.exportPathIsV2": true, + "project.id": "{2BEB2C3D-7435-4AB0-9355-BAFDA31BBC8A}", + "project.name": "ArrayConstAfterProcedureNotFound", + "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 +} diff --git a/bugs/array-const-after-procedure-not-found/src/Sources/Startup.twin b/bugs/array-const-after-procedure-not-found/src/Sources/Startup.twin new file mode 100644 index 000000000..cd3df4bf1 --- /dev/null +++ b/bugs/array-const-after-procedure-not-found/src/Sources/Startup.twin @@ -0,0 +1,15 @@ +' A module-level array Const declared after a procedure is not found by the procedures that follow it. +Module Startup + + Public Sub Main() + End Sub + + Const Scalar As Long = 5 + Const Arr() As Long = Array(1, 2, 3) + + Public Sub UseIt() + Debug.Print Scalar + Debug.Print Arr(1) + End Sub + +End Module diff --git a/bugs/filed/embed-package-without-packages-folder/embed-package-without-packages-folder.twinproj b/bugs/array-const-element-overflow-crash/array-const-element-overflow-crash.twinproj similarity index 68% rename from bugs/filed/embed-package-without-packages-folder/embed-package-without-packages-folder.twinproj rename to bugs/array-const-element-overflow-crash/array-const-element-overflow-crash.twinproj index 9dd8eb2e3..01c0c8fc2 100644 Binary files a/bugs/filed/embed-package-without-packages-folder/embed-package-without-packages-folder.twinproj and b/bugs/array-const-element-overflow-crash/array-const-element-overflow-crash.twinproj differ diff --git a/bugs/filed/populatefrom-no-args/repro.json b/bugs/array-const-element-overflow-crash/repro.json similarity index 71% rename from bugs/filed/populatefrom-no-args/repro.json rename to bugs/array-const-element-overflow-crash/repro.json index 92e884327..db90b39cf 100644 --- a/bugs/filed/populatefrom-no-args/repro.json +++ b/bugs/array-const-element-overflow-crash/repro.json @@ -2,6 +2,5 @@ "mode": "compile", "expect": { "exit": 4 - }, - "issue": 2450 + } } diff --git a/bugs/filed/embed-package-without-packages-folder/src/Settings b/bugs/array-const-element-overflow-crash/src/Settings similarity index 83% rename from bugs/filed/embed-package-without-packages-folder/src/Settings rename to bugs/array-const-element-overflow-crash/src/Settings index 815a8b85a..9f9b64492 100644 --- a/bugs/filed/embed-package-without-packages-folder/src/Settings +++ b/bugs/array-const-element-overflow-crash/src/Settings @@ -1,12 +1,12 @@ { "configuration.inherits": "Defaults", - "project.appTitle": "EmbedPackageWithoutPackagesFolder", + "project.appTitle": "ArrayConstElementOverflowCrash", "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", "project.buildType": "Standard EXE", - "project.description": "Reproduces: Embedding a package with no `Packages` folder puts the compiler in a crash loop", + "project.description": "Reproduces: A module-level array Const with an element that does not fit its type crashes the compiler", "project.exportPathIsV2": true, - "project.id": "{40E080EA-E880-41FF-A7F2-C2D9A63B5696}", - "project.name": "EmbedPackageWithoutPackagesFolder", + "project.id": "{4401A4EA-F2FC-4E51-B2B1-5E7F888EC3AF}", + "project.name": "ArrayConstElementOverflowCrash", "project.optionExplicit": true, "project.references": [ { diff --git a/bugs/array-const-element-overflow-crash/src/Sources/Startup.twin b/bugs/array-const-element-overflow-crash/src/Sources/Startup.twin new file mode 100644 index 000000000..f3ad96fac --- /dev/null +++ b/bugs/array-const-element-overflow-crash/src/Sources/Startup.twin @@ -0,0 +1,9 @@ +' Crashes the compiler: an array Const whose element does not fit the element type. +Module Startup + + Const Big() As Long = Array(2147483648) + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/filed/crash-placeholder-interface/crash-placeholder-interface.twinproj b/bugs/array-const-in-array-const-crash/array-const-in-array-const-crash.twinproj similarity index 65% rename from bugs/filed/crash-placeholder-interface/crash-placeholder-interface.twinproj rename to bugs/array-const-in-array-const-crash/array-const-in-array-const-crash.twinproj index 9ac459e8c..a474a5412 100644 Binary files a/bugs/filed/crash-placeholder-interface/crash-placeholder-interface.twinproj and b/bugs/array-const-in-array-const-crash/array-const-in-array-const-crash.twinproj differ diff --git a/bugs/filed/crash-placeholder-interface/repro.json b/bugs/array-const-in-array-const-crash/repro.json similarity index 71% rename from bugs/filed/crash-placeholder-interface/repro.json rename to bugs/array-const-in-array-const-crash/repro.json index facbed9b0..db90b39cf 100644 --- a/bugs/filed/crash-placeholder-interface/repro.json +++ b/bugs/array-const-in-array-const-crash/repro.json @@ -2,6 +2,5 @@ "mode": "compile", "expect": { "exit": 4 - }, - "issue": 2453 + } } diff --git a/bugs/filed/crash-placeholder-interface/src/Settings b/bugs/array-const-in-array-const-crash/src/Settings similarity index 83% rename from bugs/filed/crash-placeholder-interface/src/Settings rename to bugs/array-const-in-array-const-crash/src/Settings index 75407d390..7059415f1 100644 --- a/bugs/filed/crash-placeholder-interface/src/Settings +++ b/bugs/array-const-in-array-const-crash/src/Settings @@ -1,12 +1,12 @@ { "configuration.inherits": "Defaults", - "project.appTitle": "CrashPlaceholderInterface", + "project.appTitle": "ArrayConstInArrayConstCrash", "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", "project.buildType": "Standard EXE", - "project.description": "Reproduces: Compiler crashes on an `Interface` named by an angle-bracket placeholder that has an `Extends` clause", + "project.description": "Reproduces: A module-level array Const initialised from an element of another array Const crashes the compiler", "project.exportPathIsV2": true, - "project.id": "{51505D43-8F7F-448C-B5F9-370DF2631F41}", - "project.name": "CrashPlaceholderInterface", + "project.id": "{155E2A64-CA94-4EBE-B050-FAC1C69B8ED9}", + "project.name": "ArrayConstInArrayConstCrash", "project.optionExplicit": true, "project.references": [ { diff --git a/bugs/array-const-in-array-const-crash/src/Sources/Startup.twin b/bugs/array-const-in-array-const-crash/src/Sources/Startup.twin new file mode 100644 index 000000000..b046b14ee --- /dev/null +++ b/bugs/array-const-in-array-const-crash/src/Sources/Startup.twin @@ -0,0 +1,10 @@ +' Crashes the compiler: an array Const initialised from an element of another array Const. +Module Startup + + Const A() As Long = Array(1, 2, 3) + Const B() As Long = Array(A(0)) + + Public Sub Main() + End Sub + +End Module diff --git a/bugs/array-const-paramarray-codegen-error/array-const-paramarray-codegen-error.twinproj b/bugs/array-const-paramarray-codegen-error/array-const-paramarray-codegen-error.twinproj new file mode 100644 index 000000000..a0a6bcd9d Binary files /dev/null and b/bugs/array-const-paramarray-codegen-error/array-const-paramarray-codegen-error.twinproj differ diff --git a/bugs/lset-long-codegen-error/repro.json b/bugs/array-const-paramarray-codegen-error/repro.json similarity index 100% rename from bugs/lset-long-codegen-error/repro.json rename to bugs/array-const-paramarray-codegen-error/repro.json diff --git a/bugs/array-const-paramarray-codegen-error/src/Settings b/bugs/array-const-paramarray-codegen-error/src/Settings new file mode 100644 index 000000000..0c521c124 --- /dev/null +++ b/bugs/array-const-paramarray-codegen-error/src/Settings @@ -0,0 +1,61 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "ArrayConstParamarrayCodegenError", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: Passing a module-level array Const to a ParamArray compiles clean and then fails with a codegen error", + "project.exportPathIsV2": true, + "project.id": "{905FF907-B511-47BF-9641-9C0CE6049E83}", + "project.name": "ArrayConstParamarrayCodegenError", + "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 +} diff --git a/bugs/array-const-paramarray-codegen-error/src/Sources/Startup.twin b/bugs/array-const-paramarray-codegen-error/src/Sources/Startup.twin new file mode 100644 index 000000000..c491c6764 --- /dev/null +++ b/bugs/array-const-paramarray-codegen-error/src/Sources/Startup.twin @@ -0,0 +1,14 @@ +' A module-level array Const passed to a ParamArray compiles clean, and the build fails with a codegen error. +Module Startup + + Const Values() As Long = Array(10, 20, 30) + + Public Sub Main() + Count Values + End Sub + + Private Sub Count(ParamArray Items() As Variant) + Debug.Print UBound(Items) + End Sub + +End Module diff --git a/bugs/break-in-doevents-evaluate-crash/repro.json b/bugs/break-in-doevents-evaluate-crash/repro.json index 20c8579e2..5ff2dcf77 100644 --- a/bugs/break-in-doevents-evaluate-crash/repro.json +++ b/bugs/break-in-doevents-evaluate-crash/repro.json @@ -1,4 +1,9 @@ { - "mode": "manual", + "mode": "lane", + "lane": "ide:break-into", + "tests": [ + "? 1 in the DEBUG CONSOLE at the first stop ends the program with an access violation", + "so does a watch, which the IDE evaluates at the stop" + ], "steps": "1. Open break-in-doevents-evaluate-crash.twinproj in the twinBASIC IDE and press F5. Main loops on DoEvents.\n2. Press Ctrl+Break (Run > Break). The IDE stops inside the VB package: it opens IdleMessageLoopBreak.twin under Packages/VB/Sources/SUPPORT at the procedure IdleMessageLoopBreakpoint, and CALL STACK shows IdleMessageLoopBreakpoint above Main.\n3. Type ? 1 in the DEBUG CONSOLE and press Enter.\n4. Expected: 1. Actual: the program ends, and the DEBUG CONSOLE prints (runtime error -2147467259: NATIVE EXCEPTION: ACCESS_VIOLATION) and NATIVE EXCEPTION: ACCESS_VIOLATION {unknown} [$1EBD9288:twinBASIC_win32.dll+001D9288]. The IDE stays in break mode: the yellow arrow stays on the package's line, and Stop and the step commands stay enabled. Restarting the compiler puts it right.\nThe same happens with no console line at all when any watch exists, because the IDE evaluates the watches at the stop.\nControl: F5 at that first stop stops again at once, in Main, on the line after DoEvents; ? 1 there prints 1.\nAsserted on BETA 995 by `ide-test.bat --only break-into` (the project is test/ide/probes/break-into)." } diff --git a/bugs/build-and-exit-silent/repro.json b/bugs/build-and-exit-silent/repro.json index e2887e78a..3d6533551 100644 --- a/bugs/build-and-exit-silent/repro.json +++ b/bugs/build-and-exit-silent/repro.json @@ -1,4 +1,9 @@ { - "mode": "manual", - "steps": "Not automatable: --buildAndExit32 is a switch of the IDE executable twinBASIC.exe, which opens a window. 1. Pack the project (node scripts/bug_repro.mjs pack build-and-exit-silent) or unzip build-and-exit-silent.zip. 2. Open it in the IDE: 1 error, TB5079 at Startup.twin line 8. 3. Run: twinBASIC.exe --buildAndExit32 \\build-and-exit-silent.twinproj. 4. See it exit 0 after a few seconds with Build\\BuildAndExitSilent_win32.exe written, and nothing about the build on stdout or stderr. 5. Edit Main to call Unused, pack again and repeat: the command never exits and writes no .exe. End the hung IDE by its pid, never by image name." + "mode": "lane", + "lane": "ide:build-and-exit-silent", + "tests": [ + "an error in a procedure nothing calls: it exits 0, writes the exe, and says nothing", + "an error in code the build reaches: it never exits and writes no exe" + ], + "steps": "1. Pack the project (node scripts/bug_repro.mjs pack build-and-exit-silent) or unzip build-and-exit-silent.zip. 2. Open it in the IDE: 1 error, TB5079 at Startup.twin line 8. 3. Run: twinBASIC.exe --buildAndExit32 \\build-and-exit-silent.twinproj. 4. See it exit 0 after a few seconds with Build\\BuildAndExitSilent_win32.exe written, and nothing about the build on stdout or stderr. 5. Edit Main to call Unused, pack again and repeat: the command never exits and writes no .exe. End the hung IDE by its pid, never by image name." } diff --git a/bugs/filed/asnew-optional-ctor/asnew-optional-ctor.twinproj b/bugs/class-public-array-const-unreachable/class-public-array-const-unreachable.twinproj similarity index 58% rename from bugs/filed/asnew-optional-ctor/asnew-optional-ctor.twinproj rename to bugs/class-public-array-const-unreachable/class-public-array-const-unreachable.twinproj index 96098ce6d..eb95df1b5 100644 Binary files a/bugs/filed/asnew-optional-ctor/asnew-optional-ctor.twinproj and b/bugs/class-public-array-const-unreachable/class-public-array-const-unreachable.twinproj differ diff --git a/bugs/class-public-array-const-unreachable/repro.json b/bugs/class-public-array-const-unreachable/repro.json new file mode 100644 index 000000000..c665aa318 --- /dev/null +++ b/bugs/class-public-array-const-unreachable/repro.json @@ -0,0 +1,7 @@ +{ + "mode": "compile", + "expect": { + "exit": 1, + "diagnostics": ["TB5027", "TB5250"] + } +} diff --git a/bugs/class-public-array-const-unreachable/src/Settings b/bugs/class-public-array-const-unreachable/src/Settings new file mode 100644 index 000000000..145b727d8 --- /dev/null +++ b/bugs/class-public-array-const-unreachable/src/Settings @@ -0,0 +1,61 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "ClassPublicArrayConstUnreachable", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: A Public array Const in a class is accepted but cannot be reached from outside, where a Public scalar Const is refused", + "project.exportPathIsV2": true, + "project.id": "{5E544EA9-CBDF-4369-AE04-F88DCF5047A5}", + "project.name": "ClassPublicArrayConstUnreachable", + "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 +} diff --git a/bugs/class-public-array-const-unreachable/src/Sources/Startup.twin b/bugs/class-public-array-const-unreachable/src/Sources/Startup.twin new file mode 100644 index 000000000..2ec58f224 --- /dev/null +++ b/bugs/class-public-array-const-unreachable/src/Sources/Startup.twin @@ -0,0 +1,11 @@ +' A Public array Const in a class is accepted but cannot be reached; a Public scalar Const is refused. +Module Startup + + Public Sub Main() + Dim a As New WithArray + Dim s As New WithScalar + Debug.Print a.Arr(1) + Debug.Print s.Num + End Sub + +End Module diff --git a/bugs/class-public-array-const-unreachable/src/Sources/WithArray.twin b/bugs/class-public-array-const-unreachable/src/Sources/WithArray.twin new file mode 100644 index 000000000..174556bef --- /dev/null +++ b/bugs/class-public-array-const-unreachable/src/Sources/WithArray.twin @@ -0,0 +1,9 @@ +Class WithArray + + Public Const Arr() As Long = Array(1, 2, 3) + + Public Sub Show() + Debug.Print Arr(1) + End Sub + +End Class diff --git a/bugs/class-public-array-const-unreachable/src/Sources/WithScalar.twin b/bugs/class-public-array-const-unreachable/src/Sources/WithScalar.twin new file mode 100644 index 000000000..dcf91595c --- /dev/null +++ b/bugs/class-public-array-const-unreachable/src/Sources/WithScalar.twin @@ -0,0 +1,5 @@ +Class WithScalar + + Public Const Num As Long = 5 + +End Class diff --git a/bugs/clear-all-breakpoints-restart/repro.json b/bugs/clear-all-breakpoints-restart/repro.json index 509f834dd..b6803d427 100644 --- a/bugs/clear-all-breakpoints-restart/repro.json +++ b/bugs/clear-all-breakpoints-restart/repro.json @@ -1,4 +1,8 @@ { - "mode": "manual", + "mode": "lane", + "lane": "ide:breakpoints", + "tests": [ + "Clear All Breakpoints is undone by a restart: the breakpoint is back, and the run stops at it" + ], "steps": "1. Open clear-all-breakpoints-restart.twinproj in the twinBASIC IDE and open Startup.twin.\n2. Put the cursor on the line marked BREAK and press F9. A breakpoint appears in the margin.\n3. Choose Debug > Clear All Breakpoints (Ctrl+Shift+F9). The breakpoint goes.\n4. Click Restart the compiler on the toolbar, and wait for the compile to end.\n5. Expected: no breakpoint. Actual: the breakpoint is back in the margin.\n6. Press F5. Expected: the DEBUG CONSOLE shows main start, the breakpoint's line and main end. Actual: the run stops at the breakpoint.\nControls: a breakpoint removed with F9 instead stays removed after a restart, and saving the project (Ctrl+S) between Clear All Breakpoints and the restart keeps them cleared. A breakpoint saved with the project survives a restart, as it should.\nAsserted on BETA 995 by `ide-test.bat --only breakpoints` (the project is test/ide/probes/breakpoints)." } diff --git a/bugs/concurrent-builds-shared-temp/repro.json b/bugs/concurrent-builds-shared-temp/repro.json index 3d06ed65d..ead9afca6 100644 --- a/bugs/concurrent-builds-shared-temp/repro.json +++ b/bugs/concurrent-builds-shared-temp/repro.json @@ -1,4 +1,9 @@ { - "mode": "manual", - "steps": "Needs several IDEs building at once, which bug_repro cannot run.\n1. Unzip concurrent-builds-shared-temp.zip and copy the project into eight folders.\n2. Open each copy in its own IDE, all eight at once, with the default environment, so that all share one TEMP.\n3. Press Build in all eight at about the same time, and repeat. About one build in 24 fails with [TYPELIB] failed to finalize typelibrary. Disk error? then [LINKER] FAILED to create type library and [BUILD] failed.\n4. Control: give each IDE a TEMP and TMP of its own; no build fails.\nMeasured on BETA 995 through the documentation's harness with TEMP shared (8 of 192 failed) and per IDE (0 of 192). launchIde gives each IDE its own TEMP today, so pass TEMP and TMP in launchIde's env to measure it again." + "mode": "probe", + "probe": ["scripts/probe_shared_temp.mjs"], + "expect": { + "exit": 1, + "output": ["^[1-9]\\d* of \\d+ builds failed to write the type library \\(TEMP shared\\)$"] + }, + "steps": "1. Unzip concurrent-builds-shared-temp.zip and copy the project into eight folders.\n2. Open each copy in its own IDE, all eight at once, with the default environment, so that all share one TEMP.\n3. Press Build in all eight at about the same time, and repeat. About one build in 24 fails with [TYPELIB] failed to finalize typelibrary. Disk error? then [LINKER] FAILED to create type library and [BUILD] failed.\n4. Control: give each IDE a TEMP and TMP of its own; no build fails.\nscripts/probe_shared_temp.mjs does this with a TEMP shared by eight IDEs whose builds start together, and --control with a TEMP for each. On BETA 997, 3 of 8 builds failed in the first round shared, and 0 of 48 with a TEMP each." } diff --git a/bugs/console-completion-after-print/repro.json b/bugs/console-completion-after-print/repro.json index 8ce27d68e..1fa08c1a6 100644 --- a/bugs/console-completion-after-print/repro.json +++ b/bugs/console-completion-after-print/repro.json @@ -1,4 +1,10 @@ { - "mode": "manual", + "mode": "lane", + "lane": "ide:console-completion", + "tests": [ + "? tot, with a space, opens no list", + "Debug.Print tot opens no list", + "Call Tou and total = to open no list" + ], "steps": "1. Open console-completion-after-print.twinproj in the twinBASIC IDE, put a breakpoint on the line marked BREAK (F9) and press F5. The run stops there, with total and items, locals of Main, in scope.\n2. Click in the DEBUG CONSOLE's input and type ? tot.\n3. Expected: the IntelliSense list opens with total in it, as it does for ?tot. Actual: no list opens at any key, and Ctrl+Space at the end of the line opens none either.\n4. Replace the line with ?tot, with no space, or with tot alone: the list opens at the t, and offers total.\nThe same: Debug.Print tot opens no list; Touch tot, a Sub call's argument, opens it; ? items. opens it with the Collection's members.\nAsserted on BETA 995 and 987 by `ide-test.bat --only console-completion` (the project is test/ide/probes/console-completion)." } diff --git a/bugs/console-space-accepts-completion/repro.json b/bugs/console-space-accepts-completion/repro.json index 296027be6..4256174df 100644 --- a/bugs/console-space-accepts-completion/repro.json +++ b/bugs/console-space-accepts-completion/repro.json @@ -1,4 +1,11 @@ { - "mode": "manual", + "mode": "lane", + "lane": "ide:console-space", + "tests": [ + "Print total becomes Printertotal, which the console refuses", + "Call Touch(total) becomes CallByDispId(Touch(total)), which the console refuses", + "Set obj = items becomes SetAttr(obj = items), which the console refuses", + "with no program running, Print 1 + 1 becomes Printer1 + 1" + ], "steps": "1. Open console-space-accepts-completion.twinproj in the twinBASIC IDE, put a breakpoint on the line marked BREAK (F9) and press F5. The run stops there, with total, items and obj, locals of Main, in scope.\n2. Click in the DEBUG CONSOLE's input and type Print total at an ordinary speed.\n3. Expected: the line stays Print total, and Enter prints 5. Actual: the IntelliSense list opens as Print is typed, and the space after it writes the list's first entry, Printer, over Print and is lost; the line becomes Printertotal, and Enter prints (compile error: Unrecognized symbol 'Printertotal').\n4. The same: Call Touch(total) becomes CallByDispId(Touch(total)), and Set obj = items becomes SetAttr(obj = items). Escape before the space keeps the line as typed.\nAsserted on BETA 995 and 987 by `ide-test.bat --only console-space` (the project is test/ide/probes/console-space)." } diff --git a/bugs/export-stops-at-readonly-file/repro.json b/bugs/export-stops-at-readonly-file/repro.json index 9160f8487..b15815e0d 100644 --- a/bugs/export-stops-at-readonly-file/repro.json +++ b/bugs/export-stops-at-readonly-file/repro.json @@ -1,4 +1,8 @@ { - "mode": "manual", + "mode": "lane", + "lane": "ide:export", + "tests": [ + "a read-only file stops the export part-way, and the IDE reports nothing" + ], "steps": "Do it in a scratch folder made for this, below (for example C:\\Scratch\\readonly-test). File > Export Project empties \\export.\n1. Unzip export-stops-at-readonly-file.zip into , so that \\export-stops-at-readonly-file.twinproj exists.\n2. In a Command Prompt: mkdir \\export, then make three files in it, a-first.txt, m-readonly.txt and z-last.txt (echo x> \\export\\a-first.txt, and so on), and attrib +R \\export\\m-readonly.txt.\n3. Open the project in the twinBASIC IDE. Its Export Path is ${SourcePath}\\export and Export Verbose is on (Project Settings, Export).\n4. Choose File > Export Project.\n5. See, as the last three lines of the DEBUG CONSOLE: [EXPORT] DELETE FAILED: \\\\?\\\\export\\m-readonly.txt, [EXPORT] ERROR: unable to clean the output folder and [EXPORT] export failed. No dialog opens. \\export holds m-readonly.txt and z-last.txt: a-first.txt, which sorts before the read-only file, is deleted, and nothing was exported.\n6. Afterwards: attrib -R \\export\\m-readonly.txt.\nRe-measured on BETA 995 by `ide-test.bat --only export`." } diff --git a/bugs/filed/addin-shortcut-ctrl-alt/repro.json b/bugs/filed/addin-shortcut-ctrl-alt/repro.json index 5f7044793..65a8d5ee5 100644 --- a/bugs/filed/addin-shortcut-ctrl-alt/repro.json +++ b/bugs/filed/addin-shortcut-ctrl-alt/repro.json @@ -1,5 +1,10 @@ { - "mode": "manual", + "mode": "lane", + "lane": "addin:keys", + "tests": [ + "P1: Ctrl and Alt keys do not fire", + "P1: a Ctrl or Alt key fires when the same key was pressed alone just before" + ], "steps": "This is an add-in project: building it makes a DLL that the IDE's compiler loads on every start until the DLL is removed. Install it knowingly, in an IDE you can restart, and remove it afterwards.\n1. Open addin-shortcut-ctrl-alt.twinproj and build it for Win32. The DLL is written to Build\\AddinShortcutCtrlAlt_win32.dll beside the project file. Close the IDE, copy the DLL into the IDE's addins\\win32 folder (beside twinBASIC.exe), and start the IDE again. Open any project: the DEBUG CONSOLE shows [KeysProbe] registered.\n2. With no text box focused (click an empty part of the toolbar), press, one at a time and a second apart: D, Shift+D, F1, Shift+F1. Each prints [KeysProbe] fired , as d, {shift}d, f1 and {shift}f1.\n3. Press Ctrl+Shift+D, Ctrl+D and Alt+F, a second apart. Nothing is printed for any of them, although the add-in registered {CTRL}{SHIFT}d, {ctrl}d and {ALT}f.\n4. Press D, then within half a second Ctrl+D, Ctrl+Shift+D, then F and Alt+F: now all of them print.\n5. Remove the add-in: close the IDE and delete addins\\win32\\AddinShortcutCtrlAlt_win32.dll.\nRe-measured on BETA 995 by `addin-test.bat --only keys` (the project is test/addin/probes/keys; the lane builds it into a private copy of the install and presses the keys as CDP key events).", "issue": 2445 } diff --git a/bugs/filed/asnew-optional-ctor/REPORT.md b/bugs/filed/asnew-optional-ctor/REPORT.md deleted file mode 100644 index 639f9bd08..000000000 --- a/bugs/filed/asnew-optional-ctor/REPORT.md +++ /dev/null @@ -1,40 +0,0 @@ -Filed as [twinbasic/twinbasic#2459](https://github.com/twinbasic/twinbasic/issues/2459). - -## `As New` refuses a class whose only constructor has all-`Optional` arguments - -**Describe the bug** -`Dim x As New C` fails with TB5121 when the only constructor of `C` has nothing but `Optional` arguments, so it can be called with none. The same class passes TB5135, the check for COM exposure: it compiles as a public class without `[COMCreatable(False)]`, so that check counts the constructor as one that takes no arguments. The two checks for "can this class be created without arguments" disagree. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `asnew-optional-ctor.twinproj` (attached as `asnew-optional-ctor.zip`). Its one source file, `Probe.twin`, holds the whole bug: - ``` - Class COpt - Public V As Long - Public Sub New(Optional ByVal n As Long = 3) - V = n - End Sub - End Class - - Module Probe - Public Sub T() - Dim x As New COpt - Debug.Print x.V - End Sub - End Module - ``` -2. See the project fail to compile, on the `Dim`, with `TB5121 can't use this type with As-New syntax as it doesn't have a parameterless constructor`. - -What does not reproduce it: a class with a `Class_Initialize` beside a `Sub New` that takes a required argument, or with a second `Sub New` with no parameters, is accepted. A class whose only `Sub New` takes a required argument is refused, `Private` or `[COMCreatable(False)]` alike, which is the diagnostic working as intended. - -**Expected behavior** -The project compiles, and `x.V` prints `3`, as it does on BETA 983. A constructor whose arguments are all `Optional` can be called without arguments, which is what `As New` needs. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -BETA 983 accepts the reproducer and runs it. TB5121 is the diagnostic BETA 993's notes describe ("classes with [COMCreatable(False)] set on them cannot be used as an As-New datatype"), corrected in 995. Severity: code that compiled before BETA 993 stops compiling. - - diff --git a/bugs/filed/asnew-optional-ctor/repro.json b/bugs/filed/asnew-optional-ctor/repro.json deleted file mode 100644 index 8d3ed22b9..000000000 --- a/bugs/filed/asnew-optional-ctor/repro.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "mode": "compile", - "expect": { - "diagnostics": [ - "TB5121" - ] - }, - "issue": 2459 -} diff --git a/bugs/filed/asnew-optional-ctor/src/Sources/Probe.twin b/bugs/filed/asnew-optional-ctor/src/Sources/Probe.twin deleted file mode 100644 index 16a1482a4..000000000 --- a/bugs/filed/asnew-optional-ctor/src/Sources/Probe.twin +++ /dev/null @@ -1,14 +0,0 @@ -' As New refuses a class whose only constructor has all-Optional arguments (TB5121), though the same class passes the COM-exposure check. -Class COpt - Public V As Long - Public Sub New(Optional ByVal n As Long = 3) - V = n - End Sub -End Class - -Module Probe - Public Sub T() - Dim x As New COpt - Debug.Print x.V - End Sub -End Module diff --git a/bugs/filed/boolean-intdiv-string/REPORT.md b/bugs/filed/boolean-intdiv-string/REPORT.md deleted file mode 100644 index 3003404bf..000000000 --- a/bugs/filed/boolean-intdiv-string/REPORT.md +++ /dev/null @@ -1,34 +0,0 @@ -Filed as [twinbasic/twinbasic#2439](https://github.com/twinbasic/twinbasic/issues/2439). - -## `Boolean \ String` and `Boolean Mod String` convert the `String` to `Boolean` - -**Describe the bug** -With a `Boolean` on the left and a `String` on the right, `\` and `Mod` convert the `String` to `Boolean` instead of to a number, so the result has the wrong value and the wrong type. There is no diagnostic. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `boolean-intdiv-string.twinproj` (attached as `boolean-intdiv-string.zip`) and run it (F5). Its `Sub Main` holds the whole bug: - ``` - Dim b As Boolean = True - Debug.Print TypeName(b \ "2") - ``` -2. See `Boolean`, where `Long` is expected. The project prints these: - - | expression | result | expected | - |---|---|---| - | `b \ "2"` | `Boolean` True | `Long` 0, as `b \ 2.0` gives | - | `b Mod "2"` | `Boolean` False | `Long` -1, as `b Mod 2.0` gives | - -The results are consistent with converting `"2"` to `Boolean` (`True`, -1) first: -1 \ -1 is 1, stored as `True`, and -1 Mod -1 is 0, stored as `False`. A `String` literal and a `String` variable on the right both reproduce it. - -**Expected behavior** -The `String` should be converted to a number, as every other operator does: `b + "2"` is the `Double` 1 and `b / "2"` the `Double` -0.5. `b \ "2"` should be the `Long` 0, and `b Mod "2"` the `Long` -1, the same as with the number 2.0. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -What does not reproduce it: every other operator converts the `String` to a number, as `b + "2"` and `b / "2"` above show, and so do `\` and `Mod` with the operands the other way round: `"2" \ b` is the `Long` -2. Also on BETA 983, with the same results. Severity: a wrong value and a wrong type, with no diagnostic. - - diff --git a/bugs/filed/boolean-intdiv-string/boolean-intdiv-string.twinproj b/bugs/filed/boolean-intdiv-string/boolean-intdiv-string.twinproj deleted file mode 100644 index 37a58735d..000000000 Binary files a/bugs/filed/boolean-intdiv-string/boolean-intdiv-string.twinproj and /dev/null differ diff --git a/bugs/filed/boolean-intdiv-string/repro.json b/bugs/filed/boolean-intdiv-string/repro.json deleted file mode 100644 index 29a29855b..000000000 --- a/bugs/filed/boolean-intdiv-string/repro.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "mode": "run", - "expect": { - "exit": 0, - "output": [ - "^b \\\\ \"2\"\\s+= True Boolean$", - "^b Mod \"2\" = False Boolean$", - "^b \\\\ s\\s+= True Boolean$", - "^b Mod s\\s+= False Boolean$" - ] - }, - "issue": 2439 -} diff --git a/bugs/filed/boolean-intdiv-string/src/Sources/Startup.twin b/bugs/filed/boolean-intdiv-string/src/Sources/Startup.twin deleted file mode 100644 index 15665163c..000000000 --- a/bugs/filed/boolean-intdiv-string/src/Sources/Startup.twin +++ /dev/null @@ -1,20 +0,0 @@ -' Boolean \ String and Boolean Mod String convert the String to Boolean, not to a number. -Module Startup - - Public Sub Main() - Dim b As Boolean = True - Dim s As String = "2" - - Debug.Print "b \ ""2"" = "; b \ "2"; " "; TypeName(b \ "2") ' expected Long 0, got Boolean True - Debug.Print "b Mod ""2"" = "; b Mod "2"; " "; TypeName(b Mod "2") ' expected Long -1, got Boolean False - Debug.Print "b \ s = "; b \ s; " "; TypeName(b \ s) - Debug.Print "b Mod s = "; b Mod s; " "; TypeName(b Mod s) - - ' The same operators with the number 2.0, and with the operands the other way round. - Debug.Print "b \ 2.0 = "; b \ 2#; " "; TypeName(b \ 2#) - Debug.Print "b Mod 2.0 = "; b Mod 2#; " "; TypeName(b Mod 2#) - Debug.Print """2"" \ b = "; "2" \ b; " "; TypeName("2" \ b) - Debug.Print "b + ""2"" = "; b + "2"; " "; TypeName(b + "2") - End Sub - -End Module diff --git a/bugs/filed/circle-positive-angle-unchecked/REPORT.md b/bugs/filed/circle-positive-angle-unchecked/REPORT.md deleted file mode 100644 index c2a2ad4e3..000000000 --- a/bugs/filed/circle-positive-angle-unchecked/REPORT.md +++ /dev/null @@ -1,41 +0,0 @@ -Filed as [twinbasic/twinbasic#2463](https://github.com/twinbasic/twinbasic/issues/2463). - -## `Circle` with a positive angle above 2 pi raises no error and draws an arc, where VB6 raises error 5 - -**Describe the bug** -`Circle` does not check a positive *Start* or *End*. VB6 accepts angles from -2 pi to 2 pi and raises error 5 (*Invalid procedure call or argument*) for any other, drawing nothing. twinBASIC raises error 5 for a negative angle below -2 pi, as VB6 does, but a positive angle above 2 pi raises nothing and is drawn as if 2 pi had been subtracted from it: `Form1.Circle (195, 65), 50, vbRed, 7, 3` draws the arc from 0.72 to 3 radians. Observed by drawing on a form with `AutoRedraw` on, printing `Err.Number`, and saving what the form holds as a picture. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `circle-positive-angle-unchecked.twinproj` (attached as `circle-positive-angle-unchecked.zip`). Its `Sub Main` loads `Form1` without showing it, sets `AutoRedraw`, `ScaleMode = vbPixels`, a white background and a client area of 260 x 130 pixels, and then runs: - ``` - Form1.Circle (65, 65), 50, vbBlue, -0.5, -2 - On Error Resume Next - Form1.Circle (195, 65), 50, vbRed, 7, 3 - n = Err.Number - On Error GoTo 0 - Debug.Print "Circle Start 7: Err " & n - PngDump.Surface Form1, "main" - ``` - The blue pie is a valid call, there to give the picture a reference. `PngDump` is a module in the project that copies the form's pixels with `BitBlt` and saves them as `main.png` with GDI+, so the picture does not depend on twinBASIC's own picture code. -2. Run it. -3. The DEBUG CONSOLE shows `Circle Start 7: Err 0`, and `main.png` shows a red arc beside the pie. - -**Expected behavior** -Error 5, and nothing drawn by the second call, as VB6 does. The VB6 project (attached as `circle-positive-angle-unchecked-vb6.zip`, the same code with the same `PngDump` module) prints `Circle Start 7: Err 5`, and its picture holds the pie alone. - -**Screenshots** -What twinBASIC BETA 995 drew on the left, what VB6 drew in the middle, and on the right the 101 pixels that differ, in red over a grey copy of the VB6 picture: - -![Circle drawn by twinBASIC BETA 995 and by VB6, and the pixels that differ](images/main-compare.png) - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -Severity: low. A program whose angle grows past 2 pi by mistake gets a drawing instead of the error that would show the mistake. - -What was tried: BETA 983 behaves the same. A *Start* of 2 pi + 0.001 and an *End* of 7 are drawn the same way; -7 and -2 pi - 0.001, as *Start* or *End*, raise error 5 and draw nothing in both twinBASIC and VB6. VB6's limit is a little above 2 pi, the same for either sign: the **Single** 6.283186 is accepted and 6.283187 refused, and twinBASIC refuses the same negative values. The rest of `Circle`'s angle handling matches VB6: a negative angle draws the radius to that end, a pie (both negative) is filled with `FillColor` and `FillStyle`, and an arc with one radius or none is not. The package source shows the cause: `Circle` in the VB package's `Graphics.twin` compares the angle with `6.28318619728` only inside `If Start < 0 Then` (and `If _End < 0 Then`), after taking its absolute value, so a positive angle is never compared. - - diff --git a/bugs/filed/circle-positive-angle-unchecked/circle-positive-angle-unchecked.twinproj b/bugs/filed/circle-positive-angle-unchecked/circle-positive-angle-unchecked.twinproj deleted file mode 100644 index 4867f70d4..000000000 Binary files a/bugs/filed/circle-positive-angle-unchecked/circle-positive-angle-unchecked.twinproj and /dev/null differ diff --git a/bugs/filed/circle-positive-angle-unchecked/images/main-compare.png b/bugs/filed/circle-positive-angle-unchecked/images/main-compare.png deleted file mode 100644 index 1072310a1..000000000 Binary files a/bugs/filed/circle-positive-angle-unchecked/images/main-compare.png and /dev/null differ diff --git a/bugs/filed/circle-positive-angle-unchecked/images/main-tb.png b/bugs/filed/circle-positive-angle-unchecked/images/main-tb.png deleted file mode 100644 index 86777567a..000000000 Binary files a/bugs/filed/circle-positive-angle-unchecked/images/main-tb.png and /dev/null differ diff --git a/bugs/filed/circle-positive-angle-unchecked/images/main-vb6.png b/bugs/filed/circle-positive-angle-unchecked/images/main-vb6.png deleted file mode 100644 index 1c2dc4ceb..000000000 Binary files a/bugs/filed/circle-positive-angle-unchecked/images/main-vb6.png and /dev/null differ diff --git a/bugs/filed/circle-positive-angle-unchecked/images/tb-beta.txt b/bugs/filed/circle-positive-angle-unchecked/images/tb-beta.txt deleted file mode 100644 index 6e1fc58db..000000000 --- a/bugs/filed/circle-positive-angle-unchecked/images/tb-beta.txt +++ /dev/null @@ -1 +0,0 @@ -995 diff --git a/bugs/filed/circle-positive-angle-unchecked/repro.json b/bugs/filed/circle-positive-angle-unchecked/repro.json deleted file mode 100644 index 77c46636c..000000000 --- a/bugs/filed/circle-positive-angle-unchecked/repro.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "mode": "run", - "images": [ - "main" - ], - "expect": { - "exit": 0, - "output": [ - "^Circle Start 7: Err 0$" - ], - "imagesDiffer": true - }, - "issue": 2462 -} diff --git a/bugs/filed/circle-positive-angle-unchecked/src/Sources/Form1.tbform b/bugs/filed/circle-positive-angle-unchecked/src/Sources/Form1.tbform deleted file mode 100644 index 7fb728da5..000000000 --- a/bugs/filed/circle-positive-angle-unchecked/src/Sources/Form1.tbform +++ /dev/null @@ -1,95 +0,0 @@ -[ - { - "AlwaysShowKeyboardCues": false, - "Appearance": "vbAppear3d", - "AutoRedraw": false, - "BackColor": -2147483633, - "BorderStyle": "vbSizable", - "Caption": "Form1", - "ClipControls": true, - "ControlBox": true, - "DrawMode": "vbCopyPen", - "DrawStyle": "vbSolid", - "DrawWidth": 1, - "Enabled": true, - "FillColor": 0, - "FillStyle": "vbFSTransparent", - "FontBold": false, - "FontItalic": false, - "FontName": "Segoe UI", - "FontSize": 8, - "FontStrikethru": false, - "FontTransparent": true, - "FontUnderline": false, - "ForeColor": -2147483630, - "FormDesignerId": "{EAEAEAEA-EAEA-EAEA-EAEA-EAEAEAEAEA01}", - "HasDC": true, - "Height": 300, - "HelpContextID": 0, - "Icon": [], - "KeyPreview": false, - "Left": 0, - "LinkMode": "vbLinkNone", - "LinkTopic": null, - "MDIChild": false, - "MaxButton": true, - "MaxHeight": 0, - "MaxWidth": 0, - "MinButton": true, - "MinHeight": 0, - "MinWidth": 0, - "MouseIcon": [], - "MousePointer": "vbDefault", - "Moveable": true, - "Name": "Form1", - "NegotiateMenus": false, - "OLEDropMode": "vbOLEDropNone", - "Opacity": 100, - "Palette": [], - "PaletteMode": "vbPaletteModeHalftone", - "Picture": [], - "PictureDpiScaling": false, - "RightToLeft": false, - "ScaleHeight": 4500, - "ScaleLeft": 0, - "ScaleMode": "vbTwips", - "ScaleTop": 0, - "ScaleWidth": 4500, - "ShowInTaskbar": true, - "StartUpPosition": "vbStartUpScreen", - "TabFocusAutoSelect": false, - "Tag": null, - "Top": 0, - "TopMost": false, - "TransparencyKey": -1, - "Visible": true, - "WhatsThisButton": false, - "WhatsThisHelp": false, - "Width": 300, - "WindowState": "vbNormal", - "__IDEOptions": { - "alignToGrid": false, - "gridHeight": 10, - "gridWidth": 10, - "lockedControls": [], - "multiColoredGrabbers": false, - "showGrid": true, - "showOutlines": false - }, - "__lastUpdateMarker": 2061908507, - "_className": "Form", - "_clsid": "{33AD4F38-6699-11CF-B70C-00AA0060D393}", - "_children": [ - { - "Index": -1, - "Name": "Label1", - "Left": 20, - "Top": 60, - "Width": 100, - "Height": 30, - "_className": "Label", - "_clsid": "{33AD4ED8-6699-11CF-B70C-00AA0060D393}" - } - ] - } -] diff --git a/bugs/filed/circle-positive-angle-unchecked/src/Sources/Form1.twin b/bugs/filed/circle-positive-angle-unchecked/src/Sources/Form1.twin deleted file mode 100644 index fa6a1c3a9..000000000 --- a/bugs/filed/circle-positive-angle-unchecked/src/Sources/Form1.twin +++ /dev/null @@ -1,9 +0,0 @@ -[Description("")] -[FormDesignerId("EAEAEAEA-EAEA-EAEA-EAEA-EAEAEAEAEA01")] -[PredeclaredId] -Class Form1 - - Sub New() - End Sub - -End Class diff --git a/bugs/filed/circle-positive-angle-unchecked/src/Sources/PngDump.twin b/bugs/filed/circle-positive-angle-unchecked/src/Sources/PngDump.twin deleted file mode 100644 index 766d74baa..000000000 --- a/bugs/filed/circle-positive-angle-unchecked/src/Sources/PngDump.twin +++ /dev/null @@ -1,201 +0,0 @@ -Module PngDump - - ' Saves what a surface or a window shows as a PNG file, for a bug report that is - ' best shown by a picture. The same module, as PngDump.bas, runs in VB6, so that the - ' two pictures can be put side by side (node scripts/bug_repro.mjs run, then vb6). - ' - ' PngDump.Surface Form1, "main" ' an object with an hDC: a Form, PictureBox, UserControl - ' PngDump.Window Text1.hWnd, "text" ' a control with no hDC; its client area - ' - ' The file is \.png. The folder is the BUGREPRO_IMAGES environment - ' variable, which bug_repro.mjs sets, or else App.Path. Name uses letters, digits, - ' - and _. Any failure raises error 5, whose Description says which call failed. - ' - ' The pixels are copied with BitBlt (or PrintWindow) into a bitmap of this module's own - ' and saved with GDI+, so the picture is what was drawn, not what the object's Image - ' property or SavePicture would make of it. - ' - ' The Declares are aliased to Png... names, so that a reproducer's own BitBlt or - ' SelectObject cannot collide with them. - - Private Type PngGuid - Data1 As Long - Data2 As Integer - Data3 As Integer - Data4(0 To 7) As Byte - End Type - - Private Type PngGdipInput - GdiplusVersion As Long - DebugEventCallback As LongPtr - SuppressBackgroundThread As Long - SuppressExternalCodecs As Long - End Type - - Private Type PngRect - Left As Long - Top As Long - Right As Long - Bottom As Long - End Type - - Private Type PngBmpHeader - biSize As Long - biWidth As Long - biHeight As Long - biPlanes As Integer - biBitCount As Integer - biCompression As Long - biSizeImage As Long - biXPelsPerMeter As Long - biYPelsPerMeter As Long - biClrUsed As Long - biClrImportant As Long - End Type - - Private Declare PtrSafe Function PngGdiplusStartup Lib "gdiplus" Alias "GdiplusStartup" ( _ - ByRef token As LongPtr, ByRef startInput As PngGdipInput, ByVal startOutput As LongPtr) As Long - Private Declare PtrSafe Sub PngGdiplusShutdown Lib "gdiplus" Alias "GdiplusShutdown" ( _ - ByVal token As LongPtr) - Private Declare PtrSafe Function PngGdipCreateBitmapFromHBITMAP Lib "gdiplus" Alias "GdipCreateBitmapFromHBITMAP" ( _ - ByVal hbm As LongPtr, ByVal hpal As LongPtr, ByRef bitmap As LongPtr) As Long - Private Declare PtrSafe Function PngGdipSaveImageToFile Lib "gdiplus" Alias "GdipSaveImageToFile" ( _ - ByVal image As LongPtr, ByVal fileName As LongPtr, ByRef clsidEncoder As PngGuid, ByVal encoderParams As LongPtr) As Long - Private Declare PtrSafe Function PngGdipDisposeImage Lib "gdiplus" Alias "GdipDisposeImage" ( _ - ByVal image As LongPtr) As Long - Private Declare PtrSafe Function PngCLSIDFromString Lib "ole32" Alias "CLSIDFromString" ( _ - ByVal lpsz As LongPtr, ByRef pclsid As PngGuid) As Long - Private Declare PtrSafe Function PngCreateCompatibleDC Lib "gdi32" Alias "CreateCompatibleDC" ( _ - ByVal hdc As LongPtr) As LongPtr - Private Declare PtrSafe Function PngCreateDIBSection Lib "gdi32" Alias "CreateDIBSection" ( _ - ByVal hdc As LongPtr, ByRef pbmi As PngBmpHeader, ByVal usage As Long, ByRef ppvBits As LongPtr, _ - ByVal hSection As LongPtr, ByVal offset As Long) As LongPtr - Private Declare PtrSafe Function PngSelectObject Lib "gdi32" Alias "SelectObject" ( _ - ByVal hdc As LongPtr, ByVal hObject As LongPtr) As LongPtr - Private Declare PtrSafe Function PngDeleteObject Lib "gdi32" Alias "DeleteObject" ( _ - ByVal hObject As LongPtr) As Long - Private Declare PtrSafe Function PngDeleteDC Lib "gdi32" Alias "DeleteDC" ( _ - ByVal hdc As LongPtr) As Long - Private Declare PtrSafe Function PngBitBlt Lib "gdi32" Alias "BitBlt" ( _ - ByVal hdcDest As LongPtr, ByVal xDest As Long, ByVal yDest As Long, ByVal cx As Long, ByVal cy As Long, _ - ByVal hdcSrc As LongPtr, ByVal xSrc As Long, ByVal ySrc As Long, ByVal rop As Long) As Long - Private Declare PtrSafe Function PngGetClientRect Lib "user32" Alias "GetClientRect" ( _ - ByVal hwnd As LongPtr, ByRef rc As PngRect) As Long - Private Declare PtrSafe Function PngPrintWindow Lib "user32" Alias "PrintWindow" ( _ - ByVal hwnd As LongPtr, ByVal hdcBlt As LongPtr, ByVal flags As Long) As Long - - ' Saves the pixels an object with an hDC has drawn, .png. - Public Sub Surface(ByVal Obj As Object, ByVal Name As String) - Dim target As String - Dim w As Long - Dim h As Long - Dim src As LongPtr - target = TargetFile(Name) - w = CLng(Obj.ScaleX(Obj.ScaleWidth, Obj.ScaleMode, vbPixels)) - h = CLng(Obj.ScaleY(Obj.ScaleHeight, Obj.ScaleMode, vbPixels)) - src = CLngPtr(Obj.hDC) - Capture 0, src, w, h, target - End Sub - - ' Saves the client area of a window, .png: for a control with no hDC. - Public Sub Window(ByVal hWnd As LongPtr, ByVal Name As String) - Dim target As String - Dim rc As PngRect - target = TargetFile(Name) - If hWnd = 0 Then Err.Raise 5, "PngDump", "the window handle is 0" - If PngGetClientRect(hWnd, rc) = 0 Then - Err.Raise 5, "PngDump", "GetClientRect failed, error " & Err.LastDllError - End If - Capture hWnd, 0, rc.Right - rc.Left, rc.Bottom - rc.Top, target - End Sub - - ' The file's path, or error 5 for a name that is empty or uses a character outside - ' letters, digits, - and _. - Private Function TargetFile(ByVal Name As String) As String - Dim i As Long - Dim c As Long - Dim folder As String - If Len(Name) = 0 Then Err.Raise 5, "PngDump", "the image name is empty" - For i = 1 To Len(Name) - c = AscW(Mid$(Name, i, 1)) - If Not ((c >= 48 And c <= 57) Or (c >= 65 And c <= 90) Or (c >= 97 And c <= 122) Or c = 45 Or c = 95) Then - Err.Raise 5, "PngDump", "the image name """ & Name & """ must use only letters, digits, - and _" - End If - Next i - folder = Environ$("BUGREPRO_IMAGES") - If Len(folder) = 0 Then folder = App.Path - If Right$(folder, 1) = "\" Then folder = Left$(folder, Len(folder) - 1) - TargetFile = folder & "\" & Name & ".png" - End Function - - ' Copies w by h pixels, from the DC srcDC or, when hWnd is not 0, with PrintWindow, into a - ' 24-bit bitmap of our own and saves it as target. Every GDI object and the GDI+ token - ' are released on every path, and the error is raised after that. - Private Sub Capture(ByVal hWnd As LongPtr, ByVal srcDC As LongPtr, ByVal w As Long, ByVal h As Long, ByVal target As String) - Dim token As LongPtr - Dim memDC As LongPtr - Dim dib As LongPtr - Dim oldBmp As LongPtr - Dim image As LongPtr - Dim bits As LongPtr - Dim hdr As PngBmpHeader - Dim gdipIn As PngGdipInput - Dim encoder As PngGuid - Dim status As Long - Dim failure As String - - If w < 1 Or h < 1 Then failure = "the surface is " & w & " by " & h & " pixels: nothing to capture": GoTo Done - - gdipIn.GdiplusVersion = 1 - status = PngGdiplusStartup(token, gdipIn, 0) - If status <> 0 Then token = 0: failure = "GdiplusStartup failed, status " & status: GoTo Done - - status = PngCLSIDFromString(StrPtr("{557CF406-1A04-11D3-9A73-0000F81EF32E}"), encoder) - If status <> 0 Then failure = "CLSIDFromString failed for the PNG encoder, HRESULT " & Hex$(status): GoTo Done - - memDC = PngCreateCompatibleDC(0) - If memDC = 0 Then failure = "CreateCompatibleDC failed, error " & Err.LastDllError: GoTo Done - - hdr.biSize = 40 - hdr.biWidth = w - hdr.biHeight = h - hdr.biPlanes = 1 - hdr.biBitCount = 24 - dib = PngCreateDIBSection(memDC, hdr, 0, bits, 0, 0) - If dib = 0 Then failure = "CreateDIBSection failed, error " & Err.LastDllError: GoTo Done - - oldBmp = PngSelectObject(memDC, dib) - If oldBmp = 0 Then failure = "SelectObject failed, error " & Err.LastDllError: GoTo Done - - If hWnd = 0 Then - If PngBitBlt(memDC, 0, 0, w, h, srcDC, 0, 0, &HCC0020&) = 0 Then - failure = "BitBlt failed, error " & Err.LastDllError: GoTo Done - End If - Else - If PngPrintWindow(hWnd, memDC, 3) = 0 Then - failure = "PrintWindow failed, error " & Err.LastDllError: GoTo Done - End If - End If - - ' GDI+ must not be given a bitmap that is selected into a device context. - PngSelectObject memDC, oldBmp - oldBmp = 0 - - status = PngGdipCreateBitmapFromHBITMAP(dib, 0, image) - If status <> 0 Then image = 0: failure = "GdipCreateBitmapFromHBITMAP failed, status " & status: GoTo Done - - status = PngGdipSaveImageToFile(image, StrPtr(target), encoder, 0) - If status <> 0 Then failure = "GdipSaveImageToFile failed for " & target & ", status " & status - -Done: - If image <> 0 Then PngGdipDisposeImage image - If memDC <> 0 Then - If oldBmp <> 0 Then PngSelectObject memDC, oldBmp - If dib <> 0 Then PngDeleteObject dib - PngDeleteDC memDC - End If - If token <> 0 Then PngGdiplusShutdown token - If Len(failure) > 0 Then Err.Raise 5, "PngDump", failure - End Sub - -End Module diff --git a/bugs/filed/circle-positive-angle-unchecked/src/Sources/Startup.twin b/bugs/filed/circle-positive-angle-unchecked/src/Sources/Startup.twin deleted file mode 100644 index 6f0f3b872..000000000 --- a/bugs/filed/circle-positive-angle-unchecked/src/Sources/Startup.twin +++ /dev/null @@ -1,25 +0,0 @@ -Module Startup - - Public Sub Main() - Dim n As Long - Load Form1 - Form1.AutoRedraw = True - Form1.ScaleMode = vbPixels - Form1.BackColor = vbWhite - Form1.Width = Form1.Width + (260 - Form1.ScaleWidth) * Screen.TwipsPerPixelX - Form1.Height = Form1.Height + (130 - Form1.ScaleHeight) * Screen.TwipsPerPixelY - Form1.Cls - Debug.Print "Client area " & Form1.ScaleWidth & " x " & Form1.ScaleHeight & " pixels" - ' A valid pie, as the picture's context: a negative Start and End draw the radius lines. - Form1.Circle (65, 65), 50, vbBlue, -0.5, -2 - ' A Start above 2 * pi: VB6 raises error 5 and draws nothing. - On Error Resume Next - Form1.Circle (195, 65), 50, vbRed, 7, 3 - n = Err.Number - On Error GoTo 0 - Debug.Print "Circle Start 7: Err " & n - PngDump.Surface Form1, "main" - Unload Form1 - End Sub - -End Module diff --git a/bugs/filed/circle-positive-angle-unchecked/vb6/Form1.frm b/bugs/filed/circle-positive-angle-unchecked/vb6/Form1.frm deleted file mode 100644 index 69b0b2971..000000000 --- a/bugs/filed/circle-positive-angle-unchecked/vb6/Form1.frm +++ /dev/null @@ -1,19 +0,0 @@ -VERSION 5.00 -Begin VB.Form Form1 - Caption = "Form1" - ClientHeight = 1950 - ClientLeft = 60 - ClientTop = 345 - ClientWidth = 3900 - LinkTopic = "Form1" - ScaleHeight = 1950 - ScaleWidth = 3900 - StartUpPosition = 3 'Windows Default - Visible = 0 'False -End -Attribute VB_Name = "Form1" -Attribute VB_GlobalNameSpace = False -Attribute VB_Creatable = False -Attribute VB_PredeclaredId = True -Attribute VB_Exposed = False -Option Explicit diff --git a/bugs/filed/circle-positive-angle-unchecked/vb6/Module1.bas b/bugs/filed/circle-positive-angle-unchecked/vb6/Module1.bas deleted file mode 100644 index 2fb9744b7..000000000 --- a/bugs/filed/circle-positive-angle-unchecked/vb6/Module1.bas +++ /dev/null @@ -1,34 +0,0 @@ -Attribute VB_Name = "Module1" -Option Explicit - -' The VB6 side of the reproducer: node scripts/bug_repro.mjs vb6 circle-positive-angle-unchecked - -Sub Main() - Dim n As Long - On Error GoTo Fail - Open App.Path & "\out.txt" For Output As #9 - Load Form1 - Form1.AutoRedraw = True - Form1.ScaleMode = vbPixels - Form1.BackColor = vbWhite - Form1.Width = Form1.Width + (260 - Form1.ScaleWidth) * Screen.TwipsPerPixelX - Form1.Height = Form1.Height + (130 - Form1.ScaleHeight) * Screen.TwipsPerPixelY - Form1.Cls - Print #9, "Client area " & Form1.ScaleWidth & " x " & Form1.ScaleHeight & " pixels" - ' A valid pie, as the picture's context: a negative Start and End draw the radius lines. - Form1.Circle (65, 65), 50, vbBlue, -0.5, -2 - ' A Start above 2 * pi: VB6 raises error 5 and draws nothing. - On Error Resume Next - Form1.Circle (195, 65), 50, vbRed, 7, 3 - n = Err.Number - On Error GoTo Fail - Print #9, "Circle Start 7: Err " & n - PngDump.Surface Form1, "main" - Unload Form1 - Close #9 - Exit Sub -Fail: - On Error Resume Next - Print #9, "unhandled error " & Err.Number & ": " & Err.Description - Close #9 -End Sub diff --git a/bugs/filed/circle-positive-angle-unchecked/vb6/PngDump.bas b/bugs/filed/circle-positive-angle-unchecked/vb6/PngDump.bas deleted file mode 100644 index 181d5e68c..000000000 --- a/bugs/filed/circle-positive-angle-unchecked/vb6/PngDump.bas +++ /dev/null @@ -1,200 +0,0 @@ -Attribute VB_Name = "PngDump" -Option Explicit - -' Saves what a surface or a window shows as a PNG file, for a bug report that is -' best shown by a picture. The same module, as PngDump.twin, runs in twinBASIC, so that -' the two pictures can be put side by side (node scripts/bug_repro.mjs run, then vb6). -' -' PngDump.Surface Form1, "main" ' an object with an hDC: a Form, PictureBox, UserControl -' PngDump.Window Text1.hWnd, "text" ' a control with no hDC; its client area -' -' The file is \.png. The folder is the BUGREPRO_IMAGES environment -' variable, which bug_repro.mjs sets, or else App.Path. Name uses letters, digits, -' - and _. Any failure raises error 5, whose Description says which call failed. -' -' The pixels are copied with BitBlt (or PrintWindow) into a bitmap of this module's own -' and saved with GDI+, so the picture is what was drawn, not what the object's Image -' property or SavePicture would make of it. -' -' The Declares are aliased to Png... names, so that a reproducer's own BitBlt or -' SelectObject cannot collide with them. - -Private Type PngGuid - Data1 As Long - Data2 As Integer - Data3 As Integer - Data4(0 To 7) As Byte -End Type - -Private Type PngGdipInput - GdiplusVersion As Long - DebugEventCallback As Long - SuppressBackgroundThread As Long - SuppressExternalCodecs As Long -End Type - -Private Type PngRect - Left As Long - Top As Long - Right As Long - Bottom As Long -End Type - -Private Type PngBmpHeader - biSize As Long - biWidth As Long - biHeight As Long - biPlanes As Integer - biBitCount As Integer - biCompression As Long - biSizeImage As Long - biXPelsPerMeter As Long - biYPelsPerMeter As Long - biClrUsed As Long - biClrImportant As Long -End Type - -Private Declare Function PngGdiplusStartup Lib "gdiplus" Alias "GdiplusStartup" ( _ - ByRef token As Long, ByRef startInput As PngGdipInput, ByVal startOutput As Long) As Long -Private Declare Sub PngGdiplusShutdown Lib "gdiplus" Alias "GdiplusShutdown" ( _ - ByVal token As Long) -Private Declare Function PngGdipCreateBitmapFromHBITMAP Lib "gdiplus" Alias "GdipCreateBitmapFromHBITMAP" ( _ - ByVal hbm As Long, ByVal hpal As Long, ByRef bitmap As Long) As Long -Private Declare Function PngGdipSaveImageToFile Lib "gdiplus" Alias "GdipSaveImageToFile" ( _ - ByVal image As Long, ByVal fileName As Long, ByRef clsidEncoder As PngGuid, ByVal encoderParams As Long) As Long -Private Declare Function PngGdipDisposeImage Lib "gdiplus" Alias "GdipDisposeImage" ( _ - ByVal image As Long) As Long -Private Declare Function PngCLSIDFromString Lib "ole32" Alias "CLSIDFromString" ( _ - ByVal lpsz As Long, ByRef pclsid As PngGuid) As Long -Private Declare Function PngCreateCompatibleDC Lib "gdi32" Alias "CreateCompatibleDC" ( _ - ByVal hdc As Long) As Long -Private Declare Function PngCreateDIBSection Lib "gdi32" Alias "CreateDIBSection" ( _ - ByVal hdc As Long, ByRef pbmi As PngBmpHeader, ByVal usage As Long, ByRef ppvBits As Long, _ - ByVal hSection As Long, ByVal offset As Long) As Long -Private Declare Function PngSelectObject Lib "gdi32" Alias "SelectObject" ( _ - ByVal hdc As Long, ByVal hObject As Long) As Long -Private Declare Function PngDeleteObject Lib "gdi32" Alias "DeleteObject" ( _ - ByVal hObject As Long) As Long -Private Declare Function PngDeleteDC Lib "gdi32" Alias "DeleteDC" ( _ - ByVal hdc As Long) As Long -Private Declare Function PngBitBlt Lib "gdi32" Alias "BitBlt" ( _ - ByVal hdcDest As Long, ByVal xDest As Long, ByVal yDest As Long, ByVal cx As Long, ByVal cy As Long, _ - ByVal hdcSrc As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal rop As Long) As Long -Private Declare Function PngGetClientRect Lib "user32" Alias "GetClientRect" ( _ - ByVal hwnd As Long, ByRef rc As PngRect) As Long -Private Declare Function PngPrintWindow Lib "user32" Alias "PrintWindow" ( _ - ByVal hwnd As Long, ByVal hdcBlt As Long, ByVal flags As Long) As Long - -' Saves the pixels an object with an hDC has drawn, .png. -Public Sub Surface(ByVal Obj As Object, ByVal Name As String) - Dim target As String - Dim w As Long - Dim h As Long - Dim src As Long - target = TargetFile(Name) - w = CLng(Obj.ScaleX(Obj.ScaleWidth, Obj.ScaleMode, vbPixels)) - h = CLng(Obj.ScaleY(Obj.ScaleHeight, Obj.ScaleMode, vbPixels)) - src = CLng(Obj.hDC) - Capture 0, src, w, h, target -End Sub - -' Saves the client area of a window, .png: for a control with no hDC. -Public Sub Window(ByVal hWnd As Long, ByVal Name As String) - Dim target As String - Dim rc As PngRect - target = TargetFile(Name) - If hWnd = 0 Then Err.Raise 5, "PngDump", "the window handle is 0" - If PngGetClientRect(hWnd, rc) = 0 Then - Err.Raise 5, "PngDump", "GetClientRect failed, error " & Err.LastDllError - End If - Capture hWnd, 0, rc.Right - rc.Left, rc.Bottom - rc.Top, target -End Sub - -' The file's path, or error 5 for a name that is empty or uses a character outside -' letters, digits, - and _. -Private Function TargetFile(ByVal Name As String) As String - Dim i As Long - Dim c As Long - Dim folder As String - If Len(Name) = 0 Then Err.Raise 5, "PngDump", "the image name is empty" - For i = 1 To Len(Name) - c = AscW(Mid$(Name, i, 1)) - If Not ((c >= 48 And c <= 57) Or (c >= 65 And c <= 90) Or (c >= 97 And c <= 122) Or c = 45 Or c = 95) Then - Err.Raise 5, "PngDump", "the image name """ & Name & """ must use only letters, digits, - and _" - End If - Next i - folder = Environ$("BUGREPRO_IMAGES") - If Len(folder) = 0 Then folder = App.Path - If Right$(folder, 1) = "\" Then folder = Left$(folder, Len(folder) - 1) - TargetFile = folder & "\" & Name & ".png" -End Function - -' Copies w by h pixels, from the DC srcDC or, when hWnd is not 0, with PrintWindow, into a -' 24-bit bitmap of our own and saves it as target. Every GDI object and the GDI+ token -' are released on every path, and the error is raised after that. -Private Sub Capture(ByVal hWnd As Long, ByVal srcDC As Long, ByVal w As Long, ByVal h As Long, ByVal target As String) - Dim token As Long - Dim memDC As Long - Dim dib As Long - Dim oldBmp As Long - Dim image As Long - Dim bits As Long - Dim hdr As PngBmpHeader - Dim gdipIn As PngGdipInput - Dim encoder As PngGuid - Dim status As Long - Dim failure As String - - If w < 1 Or h < 1 Then failure = "the surface is " & w & " by " & h & " pixels: nothing to capture": GoTo Done - - gdipIn.GdiplusVersion = 1 - status = PngGdiplusStartup(token, gdipIn, 0) - If status <> 0 Then token = 0: failure = "GdiplusStartup failed, status " & status: GoTo Done - - status = PngCLSIDFromString(StrPtr("{557CF406-1A04-11D3-9A73-0000F81EF32E}"), encoder) - If status <> 0 Then failure = "CLSIDFromString failed for the PNG encoder, HRESULT " & Hex$(status): GoTo Done - - memDC = PngCreateCompatibleDC(0) - If memDC = 0 Then failure = "CreateCompatibleDC failed, error " & Err.LastDllError: GoTo Done - - hdr.biSize = 40 - hdr.biWidth = w - hdr.biHeight = h - hdr.biPlanes = 1 - hdr.biBitCount = 24 - dib = PngCreateDIBSection(memDC, hdr, 0, bits, 0, 0) - If dib = 0 Then failure = "CreateDIBSection failed, error " & Err.LastDllError: GoTo Done - - oldBmp = PngSelectObject(memDC, dib) - If oldBmp = 0 Then failure = "SelectObject failed, error " & Err.LastDllError: GoTo Done - - If hWnd = 0 Then - If PngBitBlt(memDC, 0, 0, w, h, srcDC, 0, 0, &HCC0020) = 0 Then - failure = "BitBlt failed, error " & Err.LastDllError: GoTo Done - End If - Else - If PngPrintWindow(hWnd, memDC, 3) = 0 Then - failure = "PrintWindow failed, error " & Err.LastDllError: GoTo Done - End If - End If - - ' GDI+ must not be given a bitmap that is selected into a device context. - PngSelectObject memDC, oldBmp - oldBmp = 0 - - status = PngGdipCreateBitmapFromHBITMAP(dib, 0, image) - If status <> 0 Then image = 0: failure = "GdipCreateBitmapFromHBITMAP failed, status " & status: GoTo Done - - status = PngGdipSaveImageToFile(image, StrPtr(target), encoder, 0) - If status <> 0 Then failure = "GdipSaveImageToFile failed for " & target & ", status " & status - -Done: - If image <> 0 Then PngGdipDisposeImage image - If memDC <> 0 Then - If oldBmp <> 0 Then PngSelectObject memDC, oldBmp - If dib <> 0 Then PngDeleteObject dib - PngDeleteDC memDC - End If - If token <> 0 Then PngGdiplusShutdown token - If Len(failure) > 0 Then Err.Raise 5, "PngDump", failure -End Sub diff --git a/bugs/filed/crash-placeholder-interface/REPORT.md b/bugs/filed/crash-placeholder-interface/REPORT.md deleted file mode 100644 index aa7620cec..000000000 --- a/bugs/filed/crash-placeholder-interface/REPORT.md +++ /dev/null @@ -1,37 +0,0 @@ -Filed as [twinbasic/twinbasic#2453](https://github.com/twinbasic/twinbasic/issues/2453). - -## Compiler crashes on an `Interface` named by an angle-bracket placeholder that has an `Extends` clause - -**Describe the bug** -A source file holding an `Interface` whose name is an angle-bracket placeholder and that has an `Extends` clause crashes the compiler while it parses the file. The DEBUG CONSOLE shows `NATIVE EXCEPTION: ACCESS_VIOLATION {no-basic-code}` with `>>> thread 0004: ParsingFileStart, `, then `restarting from MEMORY`, three times over, and then the IDE gives up. The input is not real code (it is a syntax skeleton, the shape the documentation uses to show where an attribute goes), but a parser given nonsense should report a diagnostic, and this one dereferences something instead. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `crash-placeholder-interface.twinproj` (attached as `crash-placeholder-interface.zip`). Its one source file, `Placeholder.twin`, is the whole reproduction: - ``` - Interface Extends - End Interface - ``` -2. See the compiler crash with `NATIVE EXCEPTION: ACCESS_VIOLATION {no-basic-code}` in the DEBUG CONSOLE, restart from memory three times, and then stop restarting. - -It takes a placeholder name and an `Extends` clause, and what the clause names does not matter: - -| source | result | -|---|---| -| `Interface ` + `End Interface` | TB5182 Syntax error, no crash | -| `Interface IFoo Extends ` + `End Interface` | TB5182 + TB5079 + TB5127, no crash | -| `Interface Extends ` + `End Interface` | **crash** | -| `Interface Extends IBase` + `End Interface`, no `IBase` anywhere | **crash** | -| the same, with `Interface IBase` or `Class IBase` declared in another file | **crash** | - -**Expected behavior** -A syntax error (TB5182, as for the same line without the `Extends` clause), not a crash. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -Also on BETA 983, at `twinBASIC_win32.dll+00141F7A`. Severity: crash. It takes the compiler down, three restarts, then the IDE gives up. A crash in a batch of projects costs the whole batch its result, so a tool that compiles many files at once has to isolate the file that does it. An earlier version of this report said the crash needs a placeholder in both positions, the name and the base; the last two rows of the table, each measured with a project of its own, show the base can be a real name or missing entirely. - - diff --git a/bugs/filed/crash-placeholder-interface/src/Sources/Placeholder.twin b/bugs/filed/crash-placeholder-interface/src/Sources/Placeholder.twin deleted file mode 100644 index f224906cf..000000000 --- a/bugs/filed/crash-placeholder-interface/src/Sources/Placeholder.twin +++ /dev/null @@ -1,3 +0,0 @@ -' An Interface named by an angle-bracket placeholder, with an Extends clause: the compiler crashes while parsing this file. -Interface Extends -End Interface diff --git a/bugs/filed/crash-placeholder-interface/src/Sources/Startup.twin b/bugs/filed/crash-placeholder-interface/src/Sources/Startup.twin deleted file mode 100644 index 20233b3a9..000000000 --- a/bugs/filed/crash-placeholder-interface/src/Sources/Startup.twin +++ /dev/null @@ -1,6 +0,0 @@ -Module Startup - - Public Sub Main() - End Sub - -End Module diff --git a/bugs/filed/embed-package-without-packages-folder/DocProbePkg-control.twinpack b/bugs/filed/embed-package-without-packages-folder/DocProbePkg-control.twinpack deleted file mode 100644 index cf3aa1cbe..000000000 Binary files a/bugs/filed/embed-package-without-packages-folder/DocProbePkg-control.twinpack and /dev/null differ diff --git a/bugs/filed/embed-package-without-packages-folder/DocProbePkg-nopackages.twinpack b/bugs/filed/embed-package-without-packages-folder/DocProbePkg-nopackages.twinpack deleted file mode 100644 index ba77179b2..000000000 Binary files a/bugs/filed/embed-package-without-packages-folder/DocProbePkg-nopackages.twinpack and /dev/null differ diff --git a/bugs/filed/embed-package-without-packages-folder/REPORT.md b/bugs/filed/embed-package-without-packages-folder/REPORT.md deleted file mode 100644 index 228d0216a..000000000 --- a/bugs/filed/embed-package-without-packages-folder/REPORT.md +++ /dev/null @@ -1,30 +0,0 @@ -Filed as [twinbasic/twinbasic#2442](https://github.com/twinbasic/twinbasic/issues/2442). - -## Embedding a package with no `Packages` folder puts the compiler in a crash loop - -**Describe the bug** -A `.twinpack` whose tree has no `Packages` folder, imported through Settings → References → Available Packages → **Import from file...**, ticked and applied, makes the compiler crash on each restart: `restarting from FILE` four times about two seconds apart, and then the IDE reports "Compiler crash loop detected. Restarting in SAFE mode." - -**To Reproduce** -The package is invalid input, and it crashes the compiler four times: use an IDE you can restart. - -Steps to reproduce the behavior: -1. Open `embed-package-without-packages-folder.twinproj` (attached as `embed-package-without-packages-folder.zip`). The package is in the second attachment, `DocProbePkg-nopackages.zip`: `DocProbePkg-nopackages.twinpack`, a package of one function, packed by `twinBASIC_win32.exe import` from a tree with a `Settings` file and `Sources\DocProbe.twin` and no other folder (the tree is `package\DocProbePkg` in the reproducer). The `import` verb writes `.twinproj`; the file was renamed to `.twinpack`. The tree may lack `ImportedTypeLibraries` and `Miscellaneous` as well; neither matters. -2. In the project, Settings → References → Available Packages → **Import from file...** `DocProbePkg-nopackages.twinpack`, tick it, and **Apply**. -3. The Debug Console shows `[PROJECT] twinBASIC project saving to disk [DONE]`, then `restarting from FILE` four times about two seconds apart, and the IDE reports "Compiler crash loop detected. Restarting in SAFE mode." - -**Expected behavior** -The compiler embeds the package, or refuses it with a diagnostic, and does not crash. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -Severity: low. The input is invalid, and nothing in a normal workflow makes it: every package the IDE writes has the folder, and `scripts/impexp.mjs` and `impexp.py` add it when a tree lacks it. A crash is still a poor answer to it. - -An empty `Packages` folder in the package tree is enough to prevent it: the same steps with that folder alone, or with all three (`DocProbePkg-control.twinpack` in the same attachment, which `impexp.mjs` made), restart the compiler once and run the package. - -What does not reproduce it: a project with the same package already embedded under `Packages\DocProbePkg`, without the folder, and opened cold compiles clean (also on BETA 983). So the loop needs the package to be embedded by the IDE. BETA 983 has not been tried through the IDE: in a lane its References page never finishes loading. - - diff --git a/bugs/filed/embed-package-without-packages-folder/package/DocProbePkg/Settings b/bugs/filed/embed-package-without-packages-folder/package/DocProbePkg/Settings deleted file mode 100644 index 5811b67c9..000000000 --- a/bugs/filed/embed-package-without-packages-folder/package/DocProbePkg/Settings +++ /dev/null @@ -1,21 +0,0 @@ -{ - "configuration.inherits": "Defaults", - "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", - "project.buildType": "Package TWINPACK", - "project.description": "A package of one function, DocProbeVersion, for a bug report.", - "project.id": "{7B247500-0000-4000-9000-7B2475000004}", - "project.licence": "MIT", - "project.name": "DocProbePkg", - "project.optionExplicit": true, - "project.packageVisibility": "PUBLIC", - "project.references": [], - "project.versionBuild": 0, - "project.versionMajor": 1, - "project.versionMinor": 0, - "project.versionRevision": 0, - "project.warnings": { - "errors": [], - "ignored": [] - }, - "runtime.useUnicodeStandardLibrary": true -} diff --git a/bugs/filed/embed-package-without-packages-folder/package/DocProbePkg/Sources/DocProbe.twin b/bugs/filed/embed-package-without-packages-folder/package/DocProbePkg/Sources/DocProbe.twin deleted file mode 100644 index 2f81c56cb..000000000 --- a/bugs/filed/embed-package-without-packages-folder/package/DocProbePkg/Sources/DocProbe.twin +++ /dev/null @@ -1,6 +0,0 @@ -' The package of the report: one function, DocProbeVersion, which returns 1. -Module DocProbe - Public Function DocProbeVersion() As Long - Return 1 - End Function -End Module diff --git a/bugs/filed/embed-package-without-packages-folder/repro.json b/bugs/filed/embed-package-without-packages-folder/repro.json deleted file mode 100644 index 67a162598..000000000 --- a/bugs/filed/embed-package-without-packages-folder/repro.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "mode": "manual", - "attach": ["DocProbePkg-nopackages.twinpack", "DocProbePkg-control.twinpack"], - "steps": "Invalid input, and it crashes the compiler four times: use an IDE you can restart.\n1. Unzip embed-package-without-packages-folder.zip (pack writes it; the issue has the project and the packages as two attachments), which holds embed-package-without-packages-folder.twinproj, DocProbePkg-nopackages.twinpack, made by \"\\bin\\twinBASIC_win32.exe\" import from package/DocProbePkg, a tree that has no Packages folder, and DocProbePkg-control.twinpack. Open the project.\n2. Project Settings > References > Available Packages > Import from file... DocProbePkg-nopackages.twinpack; tick the package; Apply.\n3. See, in the DEBUG CONSOLE, [PROJECT] twinBASIC project saving to disk [DONE], then restarting from FILE four times about two seconds apart, and the IDE reports \"Compiler crash loop detected. Restarting in SAFE mode.\"\nControl: the same steps with DocProbePkg-control.twinpack (made by scripts/impexp.mjs, which adds the empty Packages, ImportedTypeLibraries and Miscellaneous folders): the compiler restarts once and runs the package.\nMeasured on BETA 995 earlier this session with the packages lane's host and package, with the package's three empty folders varied one at a time; `ide-test.bat` now packs the package with impexp.mjs, which adds the folders, so no lane reproduces the crash today.", - "issue": 2442 -} diff --git a/bugs/filed/embed-package-without-packages-folder/src/Sources/Startup.twin b/bugs/filed/embed-package-without-packages-folder/src/Sources/Startup.twin deleted file mode 100644 index 20eabc893..000000000 --- a/bugs/filed/embed-package-without-packages-folder/src/Sources/Startup.twin +++ /dev/null @@ -1,5 +0,0 @@ -' The project the report imports DocProbePkg into. Main stays empty until the package is referenced. -Module Startup - Public Sub Main() - End Sub -End Module diff --git a/bugs/filed/enum-connection-points-contract/REPORT.md b/bugs/filed/enum-connection-points-contract/REPORT.md deleted file mode 100644 index 06d0ba8fb..000000000 --- a/bugs/filed/enum-connection-points-contract/REPORT.md +++ /dev/null @@ -1,44 +0,0 @@ -Filed as [twinbasic/twinbasic#2462](https://github.com/twinbasic/twinbasic/issues/2462). - -## The `IEnumConnectionPoints` of a twinBASIC class raises `E_FAIL` at the end of the list, and `Skip` and `Clone` raise `E_NOTIMPL` - -**Describe the bug** -The enumerator that `IConnectionPointContainer.EnumConnectionPoints` returns for a class with an `Event` does not follow the `IEnum` contract. `Next` raises `E_FAIL` when no item is left, and when more items are asked for than remain, where `S_FALSE` is expected. `Skip` and `Clone` both raise `E_NOTIMPL`. `Reset`, and `Next` with exactly one item left, work. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `enum-connection-points-contract.twinproj` (attached as `enum-connection-points-contract.zip`). Its one source file, `Startup.twin`, declares the project's own copies of `IConnectionPointContainer` and `IEnumConnectionPoints` (`stdole` has neither) and a class with one event. `Sub Main` gets the enumerator and calls it with `On Error Resume Next`: - ``` - points.Next 1, item, fetched ' the one point - points.Next 1, item, fetched ' at the end - points.Reset - points.Next 2, item, fetched ' two asked for, one left - points.Reset - points.Skip 1 - Dim copy As IEnumConnectionPoints = points.Clone() - ``` -2. Run the project in the IDE (F5). -3. See in the DEBUG CONSOLE: - ``` - Next 1, one point: error=0 LastHresult=0 fetched=1 - Next 1, at the end: error=80004005 LastHresult=0 fetched=0 - Next 2, one point: error=80004005 LastHresult=0 fetched=0 - Skip 1: error=80004001 LastHresult=0 fetched=0 - Clone: error=80004001 LastHresult=0 fetched=0 - ``` - -**Expected behavior** -The enumerator follows the contract, as the Windows SDK pages state it. [IEnumConnectionPoints::Next](https://learn.microsoft.com/en-us/windows/win32/api/ocidl/nf-ocidl-ienumconnectionpoints-next): "If there are fewer than the requested number of items left in the sequence, this method retrieves the remaining elements", *pcFetched* is the number retrieved, and "If the method retrieves the number of items requested, the return value is S_OK. Otherwise, it is S_FALSE." So the second call returns `S_FALSE` with 0 items and the third returns `S_FALSE` with 1. [Skip](https://learn.microsoft.com/en-us/windows/win32/api/ocidl/nf-ocidl-ienumconnectionpoints-skip) returns `S_OK`, or `S_FALSE` when it could not skip as many as asked. [Clone](https://learn.microsoft.com/en-us/windows/win32/api/ocidl/nf-ocidl-ienumconnectionpoints-clone) returns a new enumerator with the same state, and lists `E_INVALIDARG`, `E_OUTOFMEMORY`, `E_UNEXPECTED` and `S_OK`, not `E_NOTIMPL`. The sibling `IEnumConnections` of the same connection points does return `S_FALSE` at its end and implements `Skip` and `Clone`. VB6 follows the contract. For a VB6 class with one `Event`, the same calls through the vtable return: `Next 1` with one point: `S_OK`, fetched=1; `Next 1` at the end: `S_FALSE` (`00000001`), fetched=0; after `Reset`, `Next 2` with one point: `S_FALSE`, fetched=1; `Skip 1`: `S_OK`; `Clone`: `S_OK` and a new enumerator. No call fails. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -Severity: low for twinBASIC code, which knows there is one connection point, but a client written for the contract, such as a C++ loop that runs while `Next` returns `S_OK` and treats any failure as fatal, gets an error where it expects the end of the list. - -What was tried: a class with one event has exactly one connection point, so `Next 1` returning it and `Reset` followed by another `Next` both work. Reading `LastHresult` after the failing call gives 0, because the call raised its error instead of returning a success code. The same calls from a probe that passed raw pointers instead of typed variables gave the same codes. - -VB6 comparison: the VB6 project is attached as `enum-connection-points-contract-vb6.zip`. VB6 cannot declare these interfaces, so it calls them through the vtable with `DispCallFunc` on raw pointers. It was built with VB6 SP6 and run, and writes `out.txt` beside the exe. Its output for this entry's calls is `Next 1, one point: hr=00000000 fetched=1`, `Next 1, at the end: hr=00000001 fetched=0`, `Next 2, one point: hr=00000001 fetched=1`, `Skip 1: hr=00000000`, `Clone: hr=00000000`, against twinBASIC's `80004005`, `80004005`, `80004001` and `80004001` for the last four. - - diff --git a/bugs/filed/enum-connection-points-contract/enum-connection-points-contract.twinproj b/bugs/filed/enum-connection-points-contract/enum-connection-points-contract.twinproj deleted file mode 100644 index 073f7bb19..000000000 Binary files a/bugs/filed/enum-connection-points-contract/enum-connection-points-contract.twinproj and /dev/null differ diff --git a/bugs/filed/enum-connection-points-contract/repro.json b/bugs/filed/enum-connection-points-contract/repro.json deleted file mode 100644 index 7a974f1d6..000000000 --- a/bugs/filed/enum-connection-points-contract/repro.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "mode": "run", - "expect": { - "exit": 0, - "output": [ - "^Next 1, at the end: error=80004005 ", - "^Next 2, one point: error=80004005 ", - "^Skip 1: error=80004001 ", - "^Clone: error=80004001 " - ] - }, - "issue": 2462 -} diff --git a/bugs/filed/enum-connection-points-contract/src/Sources/Startup.twin b/bugs/filed/enum-connection-points-contract/src/Sources/Startup.twin deleted file mode 100644 index 494a12845..000000000 --- a/bugs/filed/enum-connection-points-contract/src/Sources/Startup.twin +++ /dev/null @@ -1,48 +0,0 @@ -' The IEnumConnectionPoints that a class with an Event returns: -' Next raises E_FAIL at the end of the list (S_FALSE expected), and Skip and Clone raise E_NOTIMPL. -[InterfaceId("B196B284-BAB4-101A-B69C-00AA00341D07")] -Private Interface IConnectionPointContainer Extends stdole.IUnknown - Function EnumConnectionPoints() As IEnumConnectionPoints -End Interface - -[InterfaceId("B196B285-BAB4-101A-B69C-00AA00341D07")] -Private Interface IEnumConnectionPoints Extends stdole.IUnknown - Sub Next(ByVal cConnections As Long, ByRef ppCP As stdole.IUnknown, ByRef pcFetched As Long) - Sub Skip(ByVal cConnections As Long) - Sub Reset() - Function Clone() As IEnumConnectionPoints -End Interface - -Private Class Source - Public Event Ping() -End Class - -Module Startup - - Private Sub Report(ByVal What As String, ByVal Fetched As Long) - Debug.Print What & ": error=" & Hex$(Err.Number) & " LastHresult=" & Hex$(Err.LastHresult) & " fetched=" & Fetched - Err.Clear - End Sub - - Public Sub Main() - Dim src As New Source - Dim container As IConnectionPointContainer = src - Dim points As IEnumConnectionPoints = container.EnumConnectionPoints() - Dim item As stdole.IUnknown, fetched As Long - On Error Resume Next - - points.Next 1, item, fetched - Report "Next 1, one point", fetched - points.Next 1, item, fetched - Report "Next 1, at the end", fetched - points.Reset - points.Next 2, item, fetched - Report "Next 2, one point", fetched - points.Reset - points.Skip 1 - Report "Skip 1", fetched - Dim copy As IEnumConnectionPoints = points.Clone() - Report "Clone", fetched - End Sub - -End Module diff --git a/bugs/filed/enum-connection-points-contract/vb6/Holder.cls b/bugs/filed/enum-connection-points-contract/vb6/Holder.cls deleted file mode 100644 index e2db21fa3..000000000 --- a/bugs/filed/enum-connection-points-contract/vb6/Holder.cls +++ /dev/null @@ -1,20 +0,0 @@ -VERSION 1.0 CLASS -BEGIN - MultiUse = -1 'True -END -Attribute VB_Name = "Holder" -Attribute VB_GlobalNameSpace = False -Attribute VB_Creatable = False -Attribute VB_PredeclaredId = False -Attribute VB_Exposed = False -Option Explicit - -' Each Holder with a WithEvents variable set to the source is one connection. -Private WithEvents Src As Source - -Public Sub Attach(ByVal Target As Source) - Set Src = Target -End Sub - -Private Sub Src_Ping() -End Sub diff --git a/bugs/filed/enum-connection-points-contract/vb6/ProbeMain.bas b/bugs/filed/enum-connection-points-contract/vb6/ProbeMain.bas deleted file mode 100644 index 6e7c357bc..000000000 --- a/bugs/filed/enum-connection-points-contract/vb6/ProbeMain.bas +++ /dev/null @@ -1,218 +0,0 @@ -Attribute VB_Name = "ProbeMain" -Option Explicit - -' The VB6 counterpart of bugs/enum-connections-one-item and bugs/enum-connection-points-contract. -' VB6 cannot declare IConnectionPointContainer, IEnumConnectionPoints, IConnectionPoint or -' IEnumConnections, so each call goes through the vtable with DispCallFunc on raw pointers. -' Vtable slots: IUnknown 0..2; IConnectionPointContainer.EnumConnectionPoints 3; -' IEnumConnectionPoints / IEnumConnections Next 3, Skip 4, Reset 5, Clone 6; -' IConnectionPoint.EnumConnections 7. Output goes to out.txt beside the exe. - -Private Declare Function DispCallFunc Lib "oleaut32" (ByVal pvInstance As Long, ByVal oVft As Long, ByVal cc As Long, ByVal vtReturn As Integer, ByVal cActuals As Long, prgvt As Integer, prgpvarg As Long, pvargResult As Variant) As Long -Private Declare Function IIDFromString Lib "ole32" (ByVal lpsz As Long, lpiid As GUID) As Long - -Private Type GUID - Data1 As Long - Data2 As Integer - Data3 As Integer - Data4(0 To 7) As Byte -End Type - -Private Type CONNECTDATA - pUnk As Long - dwCookie As Long -End Type - -Private Const CC_STDCALL As Long = 4 -Private Const VT_I4 As Integer = 3 - -Private Items(0 To 3) As CONNECTDATA -Private Points(0 To 3) As Long - -Sub Main() - On Error GoTo Fail - Open App.Path & "\out.txt" For Output As #9 - Run - Close #9 - Exit Sub -Fail: - On Error Resume Next - Print #9, "unhandled error " & Err.Number & ": " & Err.Description - Close #9 -End Sub - -Private Sub Emit(ByVal s As String) - Print #9, s -End Sub - -Private Function H(ByVal v As Long) As String - H = Right$("00000000" & Hex$(v), 8) -End Function - -' Calls vtable slot Slot of the object at pObj with up to four Long arguments; returns the HRESULT. -Private Function VCall(ByVal pObj As Long, ByVal Slot As Long, ParamArray A() As Variant) As Long - Dim n As Long, i As Long - Dim vars(0 To 3) As Variant - Dim vts(0 To 3) As Integer - Dim ptrs(0 To 3) As Long - Dim res As Variant - Dim rc As Long - n = UBound(A) + 1 - For i = 0 To n - 1 - vars(i) = CLng(A(i)) - vts(i) = VT_I4 - ptrs(i) = VarPtr(vars(i)) - Next i - res = Empty - rc = DispCallFunc(pObj, Slot * 4, CC_STDCALL, VT_I4, n, vts(0), ptrs(0), res) - If rc <> 0 Then - VCall = rc - Else - VCall = CLng(res) - End If -End Function - -Private Sub ReleasePtr(ByVal p As Long) - If p <> 0 Then VCall p, 2 -End Sub - -Private Sub Run() - Dim src As Source - Dim a As Holder, b As Holder - Dim iid As GUID - Dim pCPC As Long, pEnumPts As Long, pPoint As Long - Dim hr As Long - - Set src = New Source - Set a = New Holder - Set b = New Holder - a.Attach src - b.Attach src - - hr = IIDFromString(StrPtr("{B196B284-BAB4-101A-B69C-00AA00341D07}"), iid) - Emit "IIDFromString: hr=" & H(hr) - hr = VCall(ObjPtr(src), 0, VarPtr(iid), VarPtr(pCPC)) - Emit "QueryInterface(IConnectionPointContainer): hr=" & H(hr) & " ptr<>0: " & (pCPC <> 0) - If pCPC = 0 Then Exit Sub - - ' --- IEnumConnectionPoints: the sequence of enum-connection-points-contract --- - hr = VCall(pCPC, 3, VarPtr(pEnumPts)) - Emit "EnumConnectionPoints: hr=" & H(hr) - If pEnumPts = 0 Then Exit Sub - PointsNext pEnumPts, 1, "Next 1, one point" - PointsNext pEnumPts, 1, "Next 1, at the end" - Emit "Reset: hr=" & H(VCall(pEnumPts, 5)) - PointsNext pEnumPts, 2, "Next 2, one point" - Emit "Reset: hr=" & H(VCall(pEnumPts, 5)) - Emit "Skip 1: hr=" & H(VCall(pEnumPts, 4, 1)) - Dim pCopy As Long - hr = VCall(pEnumPts, 6, VarPtr(pCopy)) - Emit "Clone: hr=" & H(hr) & " ptr<>0: " & (pCopy <> 0) - ReleasePtr pCopy - ReleasePtr pEnumPts - - ' --- IEnumConnections: the sequence of enum-connections-one-item --- - hr = VCall(pCPC, 3, VarPtr(pEnumPts)) - Emit "EnumConnectionPoints (again): hr=" & H(hr) - PointsNext pEnumPts, 1, "Next 1, one point (to get the point)" - pPoint = Points(0) - Points(0) = 0 - ReleasePtr pEnumPts - If pPoint = 0 Then Exit Sub - - Dim pEnum As Long, pClone As Long - - pEnum = NewConnEnum(pPoint) - ConnNext pEnum, 2, "Next 2, two connections" - ConnNext pEnum, 1, "Next 1, after Next 2 took both" - ReleasePtr pEnum - - pEnum = NewConnEnum(pPoint) - ConnNext pEnum, 3, "Next 3, two connections" - ReleasePtr pEnum - - pEnum = NewConnEnum(pPoint) - ConnNext pEnum, 1, "Next 1, first" - ConnNext pEnum, 1, "Next 1, second" - ConnNext pEnum, 1, "Next 1, at the end" - Emit "Reset: hr=" & H(VCall(pEnum, 5)) - ConnNext pEnum, 2, "Next 2, after Reset" - ReleasePtr pEnum - - pEnum = NewConnEnum(pPoint) - Emit "Skip 1: hr=" & H(VCall(pEnum, 4, 1)) - ConnNext pEnum, 1, "Next 1, after Skip 1" - ReleasePtr pEnum - - pEnum = NewConnEnum(pPoint) - ConnNext pEnum, 1, "Next 1, before Clone" - pClone = 0 - hr = VCall(pEnum, 6, VarPtr(pClone)) - Emit "Clone: hr=" & H(hr) & " ptr<>0: " & (pClone <> 0) - If pClone <> 0 Then ConnNext pClone, 2, "Next 2 on the clone, one left" - ReleasePtr pClone - ReleasePtr pEnum - - pEnum = NewConnEnum(pPoint) - ClearItems - hr = VCall(pEnum, 3, 1, VarPtr(Items(0)), 0) - Emit "Next 1 with null pcFetched: hr=" & H(hr) & " cookie=" & Items(0).dwCookie - ReleaseItems - ReleasePtr pEnum - - ReleasePtr pPoint - ReleasePtr pCPC -End Sub - -Private Sub PointsNext(ByVal pEnumPts As Long, ByVal Count As Long, ByVal Label As String) - Dim i As Long, hr As Long, fetched As Long - For i = 0 To 3 - Points(i) = 0 - Next i - fetched = -1 - hr = VCall(pEnumPts, 3, Count, VarPtr(Points(0)), VarPtr(fetched)) - Emit Label & ": hr=" & H(hr) & " fetched=" & fetched - ' Points(0) stays set for the caller that wants the point; release every other pointer. - Dim keep As Long - If InStr(Label, "to get the point") > 0 Then keep = 1 Else keep = 0 - For i = keep To 3 - ReleasePtr Points(i) - Points(i) = 0 - Next i -End Sub - -Private Function NewConnEnum(ByVal pPoint As Long) As Long - Dim hr As Long, p As Long - hr = VCall(pPoint, 7, VarPtr(p)) - If hr <> 0 Then Emit "EnumConnections: hr=" & H(hr) - NewConnEnum = p -End Function - -Private Sub ClearItems() - Dim i As Long - For i = 0 To 3 - Items(i).pUnk = 0 - Items(i).dwCookie = 0 - Next i -End Sub - -Private Sub ReleaseItems() - Dim i As Long - For i = 0 To 3 - ReleasePtr Items(i).pUnk - Items(i).pUnk = 0 - Next i -End Sub - -Private Sub ConnNext(ByVal pEnum As Long, ByVal Count As Long, ByVal Label As String) - Dim hr As Long, fetched As Long, i As Long, s As String - ClearItems - fetched = -1 - hr = VCall(pEnum, 3, Count, VarPtr(Items(0)), VarPtr(fetched)) - s = "" - For i = 0 To Count - 1 - s = s & " " & Items(i).dwCookie - Next i - Emit Label & ": hr=" & H(hr) & " fetched=" & fetched & ", cookies" & s - ReleaseItems -End Sub diff --git a/bugs/filed/enum-connection-points-contract/vb6/Source.cls b/bugs/filed/enum-connection-points-contract/vb6/Source.cls deleted file mode 100644 index 59f1b368c..000000000 --- a/bugs/filed/enum-connection-points-contract/vb6/Source.cls +++ /dev/null @@ -1,13 +0,0 @@ -VERSION 1.0 CLASS -BEGIN - MultiUse = -1 'True -END -Attribute VB_Name = "Source" -Attribute VB_GlobalNameSpace = False -Attribute VB_Creatable = False -Attribute VB_PredeclaredId = False -Attribute VB_Exposed = False -Option Explicit - -' A class with one event, so one connection point. -Public Event Ping() diff --git a/bugs/filed/enum-connections-one-item/REPORT.md b/bugs/filed/enum-connections-one-item/REPORT.md deleted file mode 100644 index 3e2846d9b..000000000 --- a/bugs/filed/enum-connections-one-item/REPORT.md +++ /dev/null @@ -1,34 +0,0 @@ -Filed as [twinbasic/twinbasic#2461](https://github.com/twinbasic/twinbasic/issues/2461). - -## `IEnumConnections.Next` returns one item when asked for two with two connections present - -**Describe the bug** -The enumerator that `IConnectionPoint.EnumConnections` returns for a twinBASIC class's connection point returns at most one item per `Next` call, whatever the count asked for. With two sinks connected, `Next 2` fills one `CONNECTDATA`, reports `pcFetched` as 1, and leaves the second element of the array untouched. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `enum-connections-one-item.twinproj` (attached as `enum-connections-one-item.zip`). Its one source file, `Startup.twin`, declares the project's own copies of the connection-point interfaces (`stdole` has none of them), a class `Source` with one event, and a class `Listener` with a `WithEvents` variable of type `Source`. `Sub Main` attaches two listeners to one source, takes the connection point, and asks for both connections at once: - ``` - Dim items(0 To 1) As CONNECTDATA - Dim connections As IEnumConnections = point.EnumConnections() - connections.Next 2, items(0), fetched - Debug.Print "asked for 2, two connections present: fetched=" & fetched & ", cookies " & items(0).dwCookie & " and " & items(1).dwCookie - ``` -2. Run the project in the IDE (F5). -3. See `asked for 2, two connections present: fetched=1, cookies 1 and 0`. - -**Expected behavior** -`fetched` is 2 and the cookies are 1 and 2. The Windows SDK page for [IEnumConnections::Next](https://learn.microsoft.com/en-us/windows/win32/api/ocidl/nf-ocidl-ienumconnections-next) says *cConnections* is "The number of items to be retrieved. If there are fewer than the requested number of items left in the sequence, this method retrieves the remaining elements", *pcFetched* is "The number of items that were retrieved", and the return value is `S_OK` "If the method retrieves the number of items requested". A caller that passes an array and a count, as the page says it may, loses every connection after the first, and does not know it, because the call returns `S_OK`. VB6 follows the contract. With two `WithEvents` holders on one source object, `Next 2` returns `S_OK` with fetched=2 and both `CONNECTDATA` elements filled (the cookies are two different values, `4685276` and `4686156` in that run); `Next 3` returns `S_FALSE` with fetched=2; `Next 1` called three times returns the two items in turn, then `S_FALSE` with fetched=0, and `Next 2` after `Reset` returns both again. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -Severity: low; the interface is mostly read one item at a time, which works, and the call at the end of the list is correct. It matters to a client that reads in blocks. - -What was tried: `Next 3` with two connections returns 1 item as well. `Next 1` called repeatedly returns the items in turn, and returns `S_FALSE` with 0 items at the end. `Skip` and `Clone` work, and a null *pcFetched* is accepted for a request of 1. `IEnumConnectionPoints`, the sibling enumerator, has a different fault (see the entry about its `E_FAIL`), so the two are separate. - -VB6 comparison: the VB6 project is attached as `enum-connections-one-item-vb6.zip`. VB6 cannot declare these interfaces, so it calls them through the vtable with `DispCallFunc` on raw pointers: a `Source` class with one `Event`, two `Holder` classes each with a `WithEvents` variable set to the one source, then the same calls. It was built with VB6 SP6 and run, and writes `out.txt` beside the exe. Its output for the call of this entry is `Next 2, two connections: hr=00000000 fetched=2, cookies 4685276 4686156`, against twinBASIC's `fetched=1, cookies 1 and 0`. The other calls match twinBASIC: `Skip 1` and `Clone` return `S_OK`, a null *pcFetched* is accepted for `Next 1`, and `Next 1` at the end returns `S_FALSE` with fetched=0. VB6's cookies are large values, not 1 and 2, so only the count and the two distinct non-zero cookies are the expectation, not the values 1 and 2. - - diff --git a/bugs/filed/enum-connections-one-item/enum-connections-one-item.twinproj b/bugs/filed/enum-connections-one-item/enum-connections-one-item.twinproj deleted file mode 100644 index 92e546c04..000000000 Binary files a/bugs/filed/enum-connections-one-item/enum-connections-one-item.twinproj and /dev/null differ diff --git a/bugs/filed/enum-connections-one-item/repro.json b/bugs/filed/enum-connections-one-item/repro.json deleted file mode 100644 index 2ddfc55fc..000000000 --- a/bugs/filed/enum-connections-one-item/repro.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "mode": "run", - "expect": { - "exit": 0, - "output": [ - "^asked for 2, two connections present: fetched=1, cookies 1 and 0$" - ] - }, - "issue": 2461 -} diff --git a/bugs/filed/enum-connections-one-item/src/Sources/Startup.twin b/bugs/filed/enum-connections-one-item/src/Sources/Startup.twin deleted file mode 100644 index f4f098a23..000000000 --- a/bugs/filed/enum-connections-one-item/src/Sources/Startup.twin +++ /dev/null @@ -1,69 +0,0 @@ -' The IEnumConnections that a connection point returns hands back one item per Next call, -' even when asked for two with two connections present. -Public Module Types - Public Type CONNECTDATA - pUnk As stdole.IUnknown - dwCookie As Long - End Type -End Module - -[InterfaceId("B196B284-BAB4-101A-B69C-00AA00341D07")] -Private Interface IConnectionPointContainer Extends stdole.IUnknown - Function EnumConnectionPoints() As IEnumConnectionPoints -End Interface - -[InterfaceId("B196B285-BAB4-101A-B69C-00AA00341D07")] -Private Interface IEnumConnectionPoints Extends stdole.IUnknown - Sub Next(ByVal cConnections As Long, ByRef ppCP As IConnectionPoint, ByRef pcFetched As Long) -End Interface - -[InterfaceId("B196B286-BAB4-101A-B69C-00AA00341D07")] -Private Interface IConnectionPoint Extends stdole.IUnknown - Sub GetConnectionInterface(ByVal piid As LongPtr) - Function GetConnectionPointContainer() As IConnectionPointContainer - Function Advise(ByVal pUnkSink As stdole.IUnknown) As Long - Sub Unadvise(ByVal dwCookie As Long) - Function EnumConnections() As IEnumConnections -End Interface - -[InterfaceId("B196B287-BAB4-101A-B69C-00AA00341D07")] -Private Interface IEnumConnections Extends stdole.IUnknown - Sub Next(ByVal cConnections As Long, ByRef rgcd As CONNECTDATA, ByRef pcFetched As Long) - Sub Skip(ByVal cConnections As Long) - Sub Reset() - Function Clone() As IEnumConnections -End Interface - -Private Class Source - Public Event Ping() -End Class - -Private Class Listener - Private WithEvents Src As Source - Public Sub Attach(ByVal Target As Source) - Set Src = Target - End Sub - Private Sub Src_Ping() - End Sub -End Class - -Module Startup - - Public Sub Main() - Dim src As New Source - Dim a As New Listener, b As New Listener - a.Attach src - b.Attach src - - Dim container As IConnectionPointContainer = src - Dim points As IEnumConnectionPoints = container.EnumConnectionPoints() - Dim point As IConnectionPoint, fetched As Long - points.Next 1, point, fetched - - Dim items(0 To 1) As CONNECTDATA - Dim connections As IEnumConnections = point.EnumConnections() - connections.Next 2, items(0), fetched - Debug.Print "asked for 2, two connections present: fetched=" & fetched & ", cookies " & items(0).dwCookie & " and " & items(1).dwCookie - End Sub - -End Module diff --git a/bugs/filed/enum-connections-one-item/vb6/Holder.cls b/bugs/filed/enum-connections-one-item/vb6/Holder.cls deleted file mode 100644 index e2db21fa3..000000000 --- a/bugs/filed/enum-connections-one-item/vb6/Holder.cls +++ /dev/null @@ -1,20 +0,0 @@ -VERSION 1.0 CLASS -BEGIN - MultiUse = -1 'True -END -Attribute VB_Name = "Holder" -Attribute VB_GlobalNameSpace = False -Attribute VB_Creatable = False -Attribute VB_PredeclaredId = False -Attribute VB_Exposed = False -Option Explicit - -' Each Holder with a WithEvents variable set to the source is one connection. -Private WithEvents Src As Source - -Public Sub Attach(ByVal Target As Source) - Set Src = Target -End Sub - -Private Sub Src_Ping() -End Sub diff --git a/bugs/filed/enum-connections-one-item/vb6/ProbeMain.bas b/bugs/filed/enum-connections-one-item/vb6/ProbeMain.bas deleted file mode 100644 index 6e7c357bc..000000000 --- a/bugs/filed/enum-connections-one-item/vb6/ProbeMain.bas +++ /dev/null @@ -1,218 +0,0 @@ -Attribute VB_Name = "ProbeMain" -Option Explicit - -' The VB6 counterpart of bugs/enum-connections-one-item and bugs/enum-connection-points-contract. -' VB6 cannot declare IConnectionPointContainer, IEnumConnectionPoints, IConnectionPoint or -' IEnumConnections, so each call goes through the vtable with DispCallFunc on raw pointers. -' Vtable slots: IUnknown 0..2; IConnectionPointContainer.EnumConnectionPoints 3; -' IEnumConnectionPoints / IEnumConnections Next 3, Skip 4, Reset 5, Clone 6; -' IConnectionPoint.EnumConnections 7. Output goes to out.txt beside the exe. - -Private Declare Function DispCallFunc Lib "oleaut32" (ByVal pvInstance As Long, ByVal oVft As Long, ByVal cc As Long, ByVal vtReturn As Integer, ByVal cActuals As Long, prgvt As Integer, prgpvarg As Long, pvargResult As Variant) As Long -Private Declare Function IIDFromString Lib "ole32" (ByVal lpsz As Long, lpiid As GUID) As Long - -Private Type GUID - Data1 As Long - Data2 As Integer - Data3 As Integer - Data4(0 To 7) As Byte -End Type - -Private Type CONNECTDATA - pUnk As Long - dwCookie As Long -End Type - -Private Const CC_STDCALL As Long = 4 -Private Const VT_I4 As Integer = 3 - -Private Items(0 To 3) As CONNECTDATA -Private Points(0 To 3) As Long - -Sub Main() - On Error GoTo Fail - Open App.Path & "\out.txt" For Output As #9 - Run - Close #9 - Exit Sub -Fail: - On Error Resume Next - Print #9, "unhandled error " & Err.Number & ": " & Err.Description - Close #9 -End Sub - -Private Sub Emit(ByVal s As String) - Print #9, s -End Sub - -Private Function H(ByVal v As Long) As String - H = Right$("00000000" & Hex$(v), 8) -End Function - -' Calls vtable slot Slot of the object at pObj with up to four Long arguments; returns the HRESULT. -Private Function VCall(ByVal pObj As Long, ByVal Slot As Long, ParamArray A() As Variant) As Long - Dim n As Long, i As Long - Dim vars(0 To 3) As Variant - Dim vts(0 To 3) As Integer - Dim ptrs(0 To 3) As Long - Dim res As Variant - Dim rc As Long - n = UBound(A) + 1 - For i = 0 To n - 1 - vars(i) = CLng(A(i)) - vts(i) = VT_I4 - ptrs(i) = VarPtr(vars(i)) - Next i - res = Empty - rc = DispCallFunc(pObj, Slot * 4, CC_STDCALL, VT_I4, n, vts(0), ptrs(0), res) - If rc <> 0 Then - VCall = rc - Else - VCall = CLng(res) - End If -End Function - -Private Sub ReleasePtr(ByVal p As Long) - If p <> 0 Then VCall p, 2 -End Sub - -Private Sub Run() - Dim src As Source - Dim a As Holder, b As Holder - Dim iid As GUID - Dim pCPC As Long, pEnumPts As Long, pPoint As Long - Dim hr As Long - - Set src = New Source - Set a = New Holder - Set b = New Holder - a.Attach src - b.Attach src - - hr = IIDFromString(StrPtr("{B196B284-BAB4-101A-B69C-00AA00341D07}"), iid) - Emit "IIDFromString: hr=" & H(hr) - hr = VCall(ObjPtr(src), 0, VarPtr(iid), VarPtr(pCPC)) - Emit "QueryInterface(IConnectionPointContainer): hr=" & H(hr) & " ptr<>0: " & (pCPC <> 0) - If pCPC = 0 Then Exit Sub - - ' --- IEnumConnectionPoints: the sequence of enum-connection-points-contract --- - hr = VCall(pCPC, 3, VarPtr(pEnumPts)) - Emit "EnumConnectionPoints: hr=" & H(hr) - If pEnumPts = 0 Then Exit Sub - PointsNext pEnumPts, 1, "Next 1, one point" - PointsNext pEnumPts, 1, "Next 1, at the end" - Emit "Reset: hr=" & H(VCall(pEnumPts, 5)) - PointsNext pEnumPts, 2, "Next 2, one point" - Emit "Reset: hr=" & H(VCall(pEnumPts, 5)) - Emit "Skip 1: hr=" & H(VCall(pEnumPts, 4, 1)) - Dim pCopy As Long - hr = VCall(pEnumPts, 6, VarPtr(pCopy)) - Emit "Clone: hr=" & H(hr) & " ptr<>0: " & (pCopy <> 0) - ReleasePtr pCopy - ReleasePtr pEnumPts - - ' --- IEnumConnections: the sequence of enum-connections-one-item --- - hr = VCall(pCPC, 3, VarPtr(pEnumPts)) - Emit "EnumConnectionPoints (again): hr=" & H(hr) - PointsNext pEnumPts, 1, "Next 1, one point (to get the point)" - pPoint = Points(0) - Points(0) = 0 - ReleasePtr pEnumPts - If pPoint = 0 Then Exit Sub - - Dim pEnum As Long, pClone As Long - - pEnum = NewConnEnum(pPoint) - ConnNext pEnum, 2, "Next 2, two connections" - ConnNext pEnum, 1, "Next 1, after Next 2 took both" - ReleasePtr pEnum - - pEnum = NewConnEnum(pPoint) - ConnNext pEnum, 3, "Next 3, two connections" - ReleasePtr pEnum - - pEnum = NewConnEnum(pPoint) - ConnNext pEnum, 1, "Next 1, first" - ConnNext pEnum, 1, "Next 1, second" - ConnNext pEnum, 1, "Next 1, at the end" - Emit "Reset: hr=" & H(VCall(pEnum, 5)) - ConnNext pEnum, 2, "Next 2, after Reset" - ReleasePtr pEnum - - pEnum = NewConnEnum(pPoint) - Emit "Skip 1: hr=" & H(VCall(pEnum, 4, 1)) - ConnNext pEnum, 1, "Next 1, after Skip 1" - ReleasePtr pEnum - - pEnum = NewConnEnum(pPoint) - ConnNext pEnum, 1, "Next 1, before Clone" - pClone = 0 - hr = VCall(pEnum, 6, VarPtr(pClone)) - Emit "Clone: hr=" & H(hr) & " ptr<>0: " & (pClone <> 0) - If pClone <> 0 Then ConnNext pClone, 2, "Next 2 on the clone, one left" - ReleasePtr pClone - ReleasePtr pEnum - - pEnum = NewConnEnum(pPoint) - ClearItems - hr = VCall(pEnum, 3, 1, VarPtr(Items(0)), 0) - Emit "Next 1 with null pcFetched: hr=" & H(hr) & " cookie=" & Items(0).dwCookie - ReleaseItems - ReleasePtr pEnum - - ReleasePtr pPoint - ReleasePtr pCPC -End Sub - -Private Sub PointsNext(ByVal pEnumPts As Long, ByVal Count As Long, ByVal Label As String) - Dim i As Long, hr As Long, fetched As Long - For i = 0 To 3 - Points(i) = 0 - Next i - fetched = -1 - hr = VCall(pEnumPts, 3, Count, VarPtr(Points(0)), VarPtr(fetched)) - Emit Label & ": hr=" & H(hr) & " fetched=" & fetched - ' Points(0) stays set for the caller that wants the point; release every other pointer. - Dim keep As Long - If InStr(Label, "to get the point") > 0 Then keep = 1 Else keep = 0 - For i = keep To 3 - ReleasePtr Points(i) - Points(i) = 0 - Next i -End Sub - -Private Function NewConnEnum(ByVal pPoint As Long) As Long - Dim hr As Long, p As Long - hr = VCall(pPoint, 7, VarPtr(p)) - If hr <> 0 Then Emit "EnumConnections: hr=" & H(hr) - NewConnEnum = p -End Function - -Private Sub ClearItems() - Dim i As Long - For i = 0 To 3 - Items(i).pUnk = 0 - Items(i).dwCookie = 0 - Next i -End Sub - -Private Sub ReleaseItems() - Dim i As Long - For i = 0 To 3 - ReleasePtr Items(i).pUnk - Items(i).pUnk = 0 - Next i -End Sub - -Private Sub ConnNext(ByVal pEnum As Long, ByVal Count As Long, ByVal Label As String) - Dim hr As Long, fetched As Long, i As Long, s As String - ClearItems - fetched = -1 - hr = VCall(pEnum, 3, Count, VarPtr(Items(0)), VarPtr(fetched)) - s = "" - For i = 0 To Count - 1 - s = s & " " & Items(i).dwCookie - Next i - Emit Label & ": hr=" & H(hr) & " fetched=" & fetched & ", cookies" & s - ReleaseItems -End Sub diff --git a/bugs/filed/enum-connections-one-item/vb6/Source.cls b/bugs/filed/enum-connections-one-item/vb6/Source.cls deleted file mode 100644 index 59f1b368c..000000000 --- a/bugs/filed/enum-connections-one-item/vb6/Source.cls +++ /dev/null @@ -1,13 +0,0 @@ -VERSION 1.0 CLASS -BEGIN - MultiUse = -1 'True -END -Attribute VB_Name = "Source" -Attribute VB_GlobalNameSpace = False -Attribute VB_Creatable = False -Attribute VB_PredeclaredId = False -Attribute VB_Exposed = False -Option Explicit - -' A class with one event, so one connection point. -Public Event Ping() diff --git a/bugs/filed/enum-member-description-hover/REPORT.md b/bugs/filed/enum-member-description-hover/REPORT.md deleted file mode 100644 index eafc22f5e..000000000 --- a/bugs/filed/enum-member-description-hover/REPORT.md +++ /dev/null @@ -1,51 +0,0 @@ -Filed as [twinbasic/twinbasic#2465](https://github.com/twinbasic/twinbasic/issues/2465). - -## A member's `[Description]` is listed as a member of its enum in the hover, and is not shown on the member - -**Describe the bug** -Hovering over an enum whose member carries a `[Description]` attribute lists the attribute in the hover's *contains* list, as if it were a third member of the enum. Hovering over the member itself shows no description. With the enum below, hovering over `Shade` shows `Dark`, `Light` and a third line, `Description("The light one.")`, and hovering over `Light` shows its value and where it is declared, and nothing of "The light one.". The project compiles clean. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `enum-member-description-hover.twinproj` (attached as `enum-member-description-hover.zip`) and open `Startup.twin`. It holds: - ``` - Module DescProbe - [Description("A colour.")] - Public Enum Shade - [Description("The light one.")] - Light = 1 - Dark = 2 - End Enum - - Public Sub UseThem() - Debug.Print Shade.Light, Dark - End Sub - End Module - ``` -2. Hover over `Shade` where `UseThem` writes `Shade.Light`. -3. The hover shows: - - > *enum* **Shade** ... in component EnumMemberDescriptionHover.DescProbe - > - > A colour. - > - > **contains** - > - Dark - > - Light - > - Description("The light one.") - -4. Hover over `Light`. The hover shows `*enum-value* **Light** = 1 (&H00000001)` and `in EnumMemberDescriptionHover.DescProbe.Shade`, and no description. - -**Expected behavior** -The *contains* list holds `Dark` and `Light` only, and the hover over `Light` shows "The light one.", as the hover over a procedure, a constant or a variable with a `[Description]` shows its text: the hover over a constant ends with its description. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -The enum's own `[Description]`, "A colour.", is shown, so the attribute is read on the enum, and the same attribute on a member is taken for a member. This was checked by sending `textDocument/hover` over the compiler's language socket, with the parameters the IDE's own hover provider sends; the text above is the markdown that request returns, which is what the IDE's hover shows. The tooltip itself was not looked at in this run. - -Severity: cosmetic, but the hover is where a `[Description]` on an enum member is meant to be read, and the list names a member that does not exist. - - diff --git a/bugs/filed/enum-member-description-hover/enum-member-description-hover.twinproj b/bugs/filed/enum-member-description-hover/enum-member-description-hover.twinproj deleted file mode 100644 index 8920e0d2e..000000000 Binary files a/bugs/filed/enum-member-description-hover/enum-member-description-hover.twinproj and /dev/null differ diff --git a/bugs/filed/enum-member-description-hover/repro.json b/bugs/filed/enum-member-description-hover/repro.json deleted file mode 100644 index 953813a2e..000000000 --- a/bugs/filed/enum-member-description-hover/repro.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "mode": "manual", - "steps": "1. Open enum-member-description-hover.twinproj in the twinBASIC IDE and open Startup.twin.\n2. Hover the mouse over Shade, where UseThem writes Shade.Light.\n3. The hover shows: *enum* **Shade** ... in component EnumMemberDescriptionHover.DescProbe, then \"A colour.\", then a contains list of three lines: - Dark, - Light and - Description(\"The light one.\"). The attribute on Light is listed as if it were a third member of the enum.\n4. Hover over Light. The hover shows *enum-value* **Light** = 1 (&H00000001) in EnumMemberDescriptionHover.DescProbe.Shade, with no description: \"The light one.\" is shown nowhere.\nExpected: the contains list holds Dark and Light only, and the hover over Light shows \"The light one.\", as the hover over a procedure, a constant or a variable with a [Description] shows its text.\nChecked on BETA 995 by sending textDocument/hover over the compiler's language socket, as the IDE's own hover provider does; the text in steps 3 and 4 is what that request returns, and the hover tooltip itself was not looked at.", - "issue": 2465 -} diff --git a/bugs/filed/enum-member-description-hover/src/Settings b/bugs/filed/enum-member-description-hover/src/Settings deleted file mode 100644 index 3e77d70cd..000000000 --- a/bugs/filed/enum-member-description-hover/src/Settings +++ /dev/null @@ -1,61 +0,0 @@ -{ - "configuration.inherits": "Defaults", - "project.appTitle": "EnumMemberDescriptionHover", - "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", - "project.buildType": "Standard EXE", - "project.description": "Reproduces: A member's `[Description]` is listed as a member of its enum in the hover, and is not shown on the member", - "project.exportPathIsV2": true, - "project.id": "{A7CEE9B8-7276-4B81-9EE0-D69C43188CAE}", - "project.name": "EnumMemberDescriptionHover", - "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 -} diff --git a/bugs/filed/enum-member-description-hover/src/Sources/Startup.twin b/bugs/filed/enum-member-description-hover/src/Sources/Startup.twin deleted file mode 100644 index 3ba43c20b..000000000 --- a/bugs/filed/enum-member-description-hover/src/Sources/Startup.twin +++ /dev/null @@ -1,17 +0,0 @@ -' Hover over Shade and over Light where UseThem uses them. project.optionExplicit is true. -Module DescProbe - [Description("A colour.")] - Public Enum Shade - [Description("The light one.")] - Light = 1 - Dark = 2 - End Enum - - Public Sub UseThem() - Debug.Print Shade.Light, Dark - End Sub - - Public Sub Main() - UseThem - End Sub -End Module diff --git a/bugs/filed/err-raise-helpcontext/REPORT.md b/bugs/filed/err-raise-helpcontext/REPORT.md index 8da9ef9d2..7af5a6371 100644 --- a/bugs/filed/err-raise-helpcontext/REPORT.md +++ b/bugs/filed/err-raise-helpcontext/REPORT.md @@ -21,6 +21,15 @@ What does work, each verified on its own: the same call with `Source:=`, `Descri **Expected behavior** The call compiles. `HelpContext` is what VBA itself names that parameter. Read out of the VBA type library on the machine this was found on (`VBE7.DLL` 7.01.1158, VBA7.1, via `LoadTypeLibEx` and `ITypeInfo::GetNames` on `_ErrObject`), the method is `Raise(Number, Source, Description, HelpFile, HelpContext)`. All five names are exactly the ones the failing call uses, and the call is Microsoft's own `Err.Source` example, named arguments and all, so the code twinBASIC rejects is the code a VBA developer is most likely to have copied. Four of the five names are accepted here; only the fifth is not. +VB6 compiles the same call, named arguments and all, and its handler reads back every value passed (the project is attached as `err-raise-helpcontext-vb6.zip`; `myHelpFile` is `"my.hlp"` and `myHelpContext` is 42 there): +``` +Number: 894 + vbObjectError +Source: MyApp.MyClass +Description: Was not able to complete your task +HelpFile: my.hlp +HelpContext: 42 +``` + **Desktop:** - OS: Windows 10 Pro 22H2 (build 19045) - twinBASIC compiler version: BETA 995 diff --git a/bugs/filed/err-raise-helpcontext/vb6/Module1.bas b/bugs/filed/err-raise-helpcontext/vb6/Module1.bas new file mode 100644 index 000000000..0e60b8f36 --- /dev/null +++ b/bugs/filed/err-raise-helpcontext/vb6/Module1.bas @@ -0,0 +1,34 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' The VB6 side of err-raise-helpcontext: the twinBASIC project's call, with all five +' arguments named, compiles; the handler writes what Err holds to out.txt. + +Sub Main() + On Error GoTo Fail + Open App.Path & "\out.txt" For Output As #9 + T + Close #9 + Exit Sub +Fail: + On Error Resume Next + Print #9, "unhandled error " & Err.Number & ": " & Err.Description + Close #9 +End Sub + +Sub T() + Dim myHelpFile As String, myHelpContext As Long + myHelpFile = "my.hlp" + myHelpContext = 42 + On Error GoTo Handler + Err.Raise vbObjectError + 894, Source:="MyApp.MyClass", _ + Description:="Was not able to complete your task", _ + HelpFile:=myHelpFile, HelpContext:=myHelpContext + Exit Sub +Handler: + Print #9, "Number: "; Err.Number - vbObjectError; "+ vbObjectError" + Print #9, "Source: "; Err.Source + Print #9, "Description: "; Err.Description + Print #9, "HelpFile: "; Err.HelpFile + Print #9, "HelpContext: "; Err.HelpContext +End Sub diff --git a/bugs/filed/proper-case-even-separators/vb6/Probe.vbp b/bugs/filed/err-raise-helpcontext/vb6/Probe.vbp similarity index 100% rename from bugs/filed/proper-case-even-separators/vb6/Probe.vbp rename to bugs/filed/err-raise-helpcontext/vb6/Probe.vbp diff --git a/bugs/filed/export-follows-junction/REPORT.md b/bugs/filed/export-follows-junction/REPORT.md deleted file mode 100644 index 823ae6e56..000000000 --- a/bugs/filed/export-follows-junction/REPORT.md +++ /dev/null @@ -1,38 +0,0 @@ -Filed as [twinbasic/twinbasic#2456](https://github.com/twinbasic/twinbasic/issues/2456). - -## Export Project follows a directory junction in its folder and deletes what it points to - -**Describe the bug** -**File → Export Project** empties its export folder before it writes, as the *Export Path* setting warns. It does not stop at a directory junction in that folder: it goes through the junction, deletes the files in the folder the junction points to, and then deletes the junction. Data outside the export folder is lost, and the only record is the Debug Console, with *Export Verbose* on. - -**To Reproduce** -**This deletes files.** Do it only in a scratch folder made for this, `` below, for example `C:\Scratch\junction-test`. Everything in `\outside` is deleted. - -Steps to reproduce the behavior: -1. Open `export-follows-junction.twinproj` (attached as `export-follows-junction.zip`), after unzipping it into ``. Its *Export Path* is `${SourcePath}\export` and *Export Verbose* is on (Project Settings, Export). -2. In a Command Prompt, make the export folder, a folder outside it that holds a file, and a junction in the export folder to that folder: - ``` - mkdir \export - mkdir \outside - echo keep me> \outside\precious.txt - mklink /J \export\linked \outside - ``` -3. Choose **File → Export Project**. -4. The Debug Console shows: - ``` - [EXPORT] DELETED: \\?\\export\linked\precious.txt - [EXPORT] DELETED: \\?\\export\linked - ``` - and `\outside` is empty afterwards. - -**Expected behavior** -The export deletes the junction itself, or leaves it alone, and never what it points to. A junction (or symbolic link) is not part of the export folder's contents. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -Severity: data loss outside the folder the user chose. The command-line `export` verb does not reproduce it: it deletes nothing. - - diff --git a/bugs/filed/export-follows-junction/repro.json b/bugs/filed/export-follows-junction/repro.json deleted file mode 100644 index dced202b5..000000000 --- a/bugs/filed/export-follows-junction/repro.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "mode": "manual", - "steps": "WARNING: File > Export Project empties its export folder before it writes, and this bug makes it empty the folder a junction in that folder points to as well. Do it only in a scratch folder made for this, below (for example C:\\Scratch\\junction-test). Anything in \\outside is deleted.\n1. Unzip export-follows-junction.zip into , so that \\export-follows-junction.twinproj exists.\n2. In a Command Prompt: mkdir \\export, mkdir \\outside, echo keep me> \\outside\\precious.txt, then mklink /J \\export\\linked \\outside.\n3. Open the project in the twinBASIC IDE. Its Export Path is ${SourcePath}\\export and Export Verbose is on (Project Settings, Export).\n4. Choose File > Export Project.\n5. See, in the DEBUG CONSOLE, [EXPORT] DELETED: \\\\?\\\\export\\linked\\precious.txt and then [EXPORT] DELETED: \\\\?\\\\export\\linked. \\outside is empty: precious.txt is gone.\nRe-measured on BETA 995 by `ide-test.bat --only export` (it calls exportProjectTo() on a scratch folder under its work folder).", - "issue": 2456 -} diff --git a/bugs/filed/export-follows-junction/src/Settings b/bugs/filed/export-follows-junction/src/Settings deleted file mode 100644 index 609cc3e8c..000000000 --- a/bugs/filed/export-follows-junction/src/Settings +++ /dev/null @@ -1,63 +0,0 @@ -{ - "configuration.inherits": "Defaults", - "project.appTitle": "ExportFollowsJunction", - "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", - "project.buildType": "Standard EXE", - "project.description": "Reproduces: Export Project follows a directory junction in its folder and deletes what it points to", - "project.exportPath": "${SourcePath}\\export", - "project.exportPathIsV2": true, - "project.exportVerbose": true, - "project.id": "{CB4E9670-139D-4170-99CD-7A828375DA9D}", - "project.name": "ExportFollowsJunction", - "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 -} diff --git a/bugs/filed/export-follows-junction/src/Sources/Startup.twin b/bugs/filed/export-follows-junction/src/Sources/Startup.twin deleted file mode 100644 index 1cd8339bc..000000000 --- a/bugs/filed/export-follows-junction/src/Sources/Startup.twin +++ /dev/null @@ -1,5 +0,0 @@ -' Nothing to run. The bug is in File > Export Project, which the steps in the report set up. -Module Startup - Public Sub Main() - End Sub -End Module diff --git a/bugs/filed/export-path-sourcepath-as-path/repro.json b/bugs/filed/export-path-sourcepath-as-path/repro.json index bd9dc59c5..06e4306ed 100644 --- a/bugs/filed/export-path-sourcepath-as-path/repro.json +++ b/bugs/filed/export-path-sourcepath-as-path/repro.json @@ -1,5 +1,9 @@ { - "mode": "manual", + "mode": "lane", + "lane": "ide:export", + "tests": [ + "an export into the project's own folder deletes the project file" + ], "steps": "WARNING: this deletes the project file. Do it in a scratch folder made for this, below (for example C:\\Scratch\\sourcepath-test), holding nothing but the unzipped project.\n1. Unzip export-path-sourcepath-as-path.zip into , so that \\export-path-sourcepath-as-path.twinproj is the only file there. Open it in the twinBASIC IDE. Export Verbose is on.\n2. Project Settings > Export > Export Path: type ${SourcePath}. The editor refuses it: \"${SourcePath} on it's own is not allowed. This would DELETE the project file ...\".\n3. Type in full instead (C:\\Scratch\\sourcepath-test). The editor accepts it. Apply.\n4. Choose File > Export Project.\n5. See, in the DEBUG CONSOLE, [EXPORT] DELETED: \\\\?\\\\export-path-sourcepath-as-path.twinproj, and then [EXPORT] COMPLETED. The project file is no longer on disk; the project is still open, and Save Project would write it back.\nRe-measured on BETA 995 by `ide-test.bat --only export`, which calls exportProjectTo() on the project's own folder (the compiler's side: step 4 and 5). Step 3, that the Settings editor accepts the full path, is read from the validate() function of project.exportPath in ide/main.js, which compares the text with ${sourcepath} and ${sourcepath}\\ and does not resolve the path; it has not been tried by hand.", "issue": 2457 } diff --git a/bugs/filed/export-writes-compiler-packages/repro.json b/bugs/filed/export-writes-compiler-packages/repro.json index 651a6d989..dff3672a3 100644 --- a/bugs/filed/export-writes-compiler-packages/repro.json +++ b/bugs/filed/export-writes-compiler-packages/repro.json @@ -1,5 +1,10 @@ { - "mode": "manual", + "mode": "lane", + "lane": "ide:export", + "tests": [ + "an export into an empty folder writes the compiler packages", + "the command line's import refuses the IDE's export, for its compiler packages" + ], "steps": "Do it in a scratch folder made for this, below (for example C:\\Scratch\\packages-test). File > Export Project empties \\export.\n1. Unzip export-writes-compiler-packages.zip into , so that \\export-writes-compiler-packages.twinproj exists. In a Command Prompt: mkdir \\export.\n2. Open the project in the twinBASIC IDE. Its Export Path is ${SourcePath}\\export. Choose File > Export Project.\n3. See \\export\\Packages: it holds the source of the compiler packages the project uses, VB, VBA, VBRUN and AppGlobalClassProject (475 of the 477 files an export of a two-file project wrote). The project file holds none of them.\n4. In a Command Prompt, with the folder that holds twinBASIC.exe: \"\\bin\\twinBASIC_win32.exe\" import \\repacked.twinproj \\export\\ --overwrite. It stops with exit code 999 and writes nothing.\n5. Control: \"\\bin\\twinBASIC_win32.exe\" export \\export-writes-compiler-packages.twinproj \\fromfile\\ --overwrite writes 3 files, Settings and the two sources, and an empty Packages folder, because the project file holds no package.\nRe-measured on BETA 995 by `ide-test.bat --only export` (steps 2 to 4).", "issue": 841, "existing": true diff --git a/bugs/filed/f1-fold-signature-help/REPORT.md b/bugs/filed/f1-fold-signature-help/REPORT.md deleted file mode 100644 index c874f733f..000000000 --- a/bugs/filed/f1-fold-signature-help/REPORT.md +++ /dev/null @@ -1,28 +0,0 @@ -Filed as [twinbasic/twinbasic#2446](https://github.com/twinbasic/twinbasic/issues/2446). - -## F1 and the fold icon toggle the signature help, then fail - -**Describe the bug** -In the code editor's signature help, F1 and a click on the fold icon both toggle the help between expanded and collapsed, and then fail. Every F1 adds `command failed: "tbHelp_ToggleExpandSignatureHelp"` to the Debug Console, and every click on the icon throws an error in the page. The toggle works, so the error is the only symptom. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `f1-fold-signature-help.twinproj` (attached as `f1-fold-signature-help.zip`) and open `Main.twin`. Put the cursor inside the parentheses of the call `FindTheNeedle(3)` and press Ctrl+Space. The signature help shows, with a fold icon whose tooltip reads *Fold/Collapse (F1)*. -2. Press F1. The signature help expands, and the Debug Console shows `command failed: "tbHelp_ToggleExpandSignatureHelp"`. F1 again collapses it, with a second such line. -3. Click the fold icon instead. The signature help toggles, and the page throws `TypeError: Cannot read properties of undefined (reading 'stopPropagation') at toggleSigHelp`. - -**Expected behavior** -The toggle, and no error. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -Severity: cosmetic. The toggle works, but every F1 adds the failure line to the Debug Console, and every click on the icon throws in the page. - -`toggleSigHelp(e)` in `ide/main.js` ends with `e.stopPropagation();e.preventDefault()`, and neither caller passes an event: the command is `internalAction:()=>{toggleSigHelp()}`, and the icon is `onclick='toggleSigHelp()'`. The toggle comes first, so the error is the only symptom. `executeKeyboardShortcuts` catches the command's error and writes the Debug Console line. - -F1 was measured on BETA 995, in a run where an add-in that registers F1 was loaded (it only shows that the shortcut fires; the IDE's own command is the one that fails). The click on the icon was measured on BETA 983, in a harness IDE with the page's exceptions recorded over CDP; `toggleSigHelp` and both callers are unchanged in BETA 995's `ide/main.js`. - - diff --git a/bugs/filed/f1-fold-signature-help/f1-fold-signature-help.twinproj b/bugs/filed/f1-fold-signature-help/f1-fold-signature-help.twinproj deleted file mode 100644 index acdf3db8d..000000000 Binary files a/bugs/filed/f1-fold-signature-help/f1-fold-signature-help.twinproj and /dev/null differ diff --git a/bugs/filed/f1-fold-signature-help/repro.json b/bugs/filed/f1-fold-signature-help/repro.json deleted file mode 100644 index aa196caad..000000000 --- a/bugs/filed/f1-fold-signature-help/repro.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "mode": "manual", - "steps": "1. Open f1-fold-signature-help.twinproj in the twinBASIC IDE and open Main.twin. Put the cursor inside the parentheses of FindTheNeedle( in the call, and press Ctrl+Space. The signature help shows, with a fold icon whose tooltip reads Fold/Collapse (F1).\n2. Press F1. The signature help expands, and the DEBUG CONSOLE shows command failed: \"tbHelp_ToggleExpandSignatureHelp\". F1 again collapses it, with a second such line.\n3. Click the fold icon instead. The signature help toggles, and the IDE page throws TypeError: Cannot read properties of undefined (reading 'stopPropagation') at toggleSigHelp.\nExpected: the toggle, and no error. Measured on BETA 995 by `addin-test.bat --only keys` (the last test; an add-in that registers F1 was loaded in that run, to see the shortcut fire, and the IDE's own command fails the same way). The click was measured on BETA 983 only, in a harness IDE with the page's exceptions recorded over CDP; toggleSigHelp and both callers are unchanged in BETA 995's ide/main.js.", - "issue": 2446 -} diff --git a/bugs/filed/f1-fold-signature-help/src/Settings b/bugs/filed/f1-fold-signature-help/src/Settings deleted file mode 100644 index 50517dba9..000000000 --- a/bugs/filed/f1-fold-signature-help/src/Settings +++ /dev/null @@ -1,61 +0,0 @@ -{ - "configuration.inherits": "Defaults", - "project.appTitle": "F1FoldSignatureHelp", - "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", - "project.buildType": "Standard EXE", - "project.description": "Reproduces: F1 and the fold icon toggle the signature help, then fail", - "project.exportPathIsV2": true, - "project.id": "{E749543A-96A4-414C-83F4-CAE38BBA3E60}", - "project.name": "F1FoldSignatureHelp", - "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 -} diff --git a/bugs/filed/filecopy-open-file/REPORT.md b/bugs/filed/filecopy-open-file/REPORT.md deleted file mode 100644 index ae9c539fd..000000000 --- a/bugs/filed/filecopy-open-file/REPORT.md +++ /dev/null @@ -1,30 +0,0 @@ -Filed as [twinbasic/twinbasic#2451](https://github.com/twinbasic/twinbasic/issues/2451). - -## `FileCopy` of an open file raises `&H80004005`, where VB6 raises 55 or copies it - -**Describe the bug** -`FileCopy` of a file that is open raises -2147467259 (`&H80004005`, *Unspecified error*), whatever mode the file is open in. VB6 raises error 55, *File already open*, for a file open `For Append`, and copies a file open `For Input` without an error. Code that handles VB6's error 55 does not recognise the error, and a copy that VB6 makes is refused. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `filecopy-open-file.twinproj` (attached as `filecopy-open-file.zip`) and run it (F5). Its `Sub Main` makes a file in `%TEMP%` and then does this: - ``` - On Error Resume Next - Open f For Append As #2 - FileCopy f, f & ".copy" - Debug.Print Err.Number, Err.Description - ``` -2. See `-2147467259 Unspecified error`. VB6 prints `55 File already open`. -3. See the second line the project prints: with the file open `For Input` instead, twinBASIC raises the same `-2147467259`, and VB6 copies the file without an error. - -**Expected behavior** -Error 55 for the file open `For Append`, and a successful copy for the file open `For Input`, as in VB6. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -BETA 983 copied an open file with no error, in both modes (the project prints 0 for all three lines); BETA 984's notes list the change ("FileSystem.FileCopy function would previously allow copying of an already open file without error"), and only the error number and the `Input` case differ from VB6. What does not reproduce it: the file closed (the third line the project prints is 0). Severity: code that handles VB6's error 55 does not recognise the error, and a copy that VB6 makes is refused. - - diff --git a/bugs/filed/filecopy-open-file/filecopy-open-file.twinproj b/bugs/filed/filecopy-open-file/filecopy-open-file.twinproj deleted file mode 100644 index 74d9aaa40..000000000 Binary files a/bugs/filed/filecopy-open-file/filecopy-open-file.twinproj and /dev/null differ diff --git a/bugs/filed/filecopy-open-file/repro.json b/bugs/filed/filecopy-open-file/repro.json deleted file mode 100644 index 4c2b6be14..000000000 --- a/bugs/filed/filecopy-open-file/repro.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "mode": "run", - "expect": { - "exit": 0, - "output": [ - "^open For Append: -2147467259 ", - "^open For Input: -2147467259 ", - "^closed:\\s+0\\s*$" - ] - }, - "issue": 2451 -} diff --git a/bugs/filed/filecopy-open-file/src/Settings b/bugs/filed/filecopy-open-file/src/Settings deleted file mode 100644 index 4f72a85ee..000000000 --- a/bugs/filed/filecopy-open-file/src/Settings +++ /dev/null @@ -1,61 +0,0 @@ -{ - "configuration.inherits": "Defaults", - "project.appTitle": "FilecopyOpenFile", - "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", - "project.buildType": "Standard EXE", - "project.description": "Reproduces: FileCopy of an open file raises `&H80004005`, where VB6 raises 55 or copies it", - "project.exportPathIsV2": true, - "project.id": "{C8DBD6E9-219B-4F1A-A73E-EA267F50EA49}", - "project.name": "FilecopyOpenFile", - "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 -} diff --git a/bugs/filed/filecopy-open-file/src/Sources/Startup.twin b/bugs/filed/filecopy-open-file/src/Sources/Startup.twin deleted file mode 100644 index 6774cb54a..000000000 --- a/bugs/filed/filecopy-open-file/src/Sources/Startup.twin +++ /dev/null @@ -1,30 +0,0 @@ -' FileCopy of an open file raises &H80004005, where VB6 raises error 55 or copies it. -Module Startup - - Public Sub Main() - Dim f As String = Environ$("TEMP") & "\filecopy-probe.txt" - Open f For Output As #1: Print #1, "one": Close #1 - - On Error Resume Next - - Open f For Append As #2 - FileCopy f, f & ".copy" - Debug.Print "open For Append: "; Err.Number; Err.Description ' VB6: 55 File already open - Err.Clear - Close #2 - - Open f For Input As #2 - FileCopy f, f & ".copy" - Debug.Print "open For Input: "; Err.Number; Err.Description ' VB6: copies, no error - Err.Clear - Close #2 - - FileCopy f, f & ".copy" - Debug.Print "closed: "; Err.Number; Err.Description ' no error - Err.Clear - - Kill f - Kill f & ".copy" - End Sub - -End Module diff --git a/bugs/filed/forms-by-index-crash/REPORT.md b/bugs/filed/forms-by-index-crash/REPORT.md deleted file mode 100644 index 875fa1e83..000000000 --- a/bugs/filed/forms-by-index-crash/REPORT.md +++ /dev/null @@ -1,37 +0,0 @@ -Filed as [twinbasic/twinbasic#2458](https://github.com/twinbasic/twinbasic/issues/2458). - -## Reading `Forms` by index returns a broken reference, and the process then crashes - -**Describe the bug** -In a loop over `Forms.Count`, `Set f = Forms(k)` with `f` declared `As Form`, followed by a read of `f.Name`, ends in an access violation (`0xC0000005`). As a compiled EXE the process exits with that code. Run in the IDE, the DEBUG CONSOLE reports `NATIVE EXCEPTION: ACCESS_VIOLATION` at the line that reads `f.Name`, and the run ends without returning. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `forms-by-index-crash.twinproj` (attached as `forms-by-index-crash.zip`). It is a Standard EXE project with one empty form, `Form1`, and a `Sub Main` that loads it: - ``` - Dim f As Form - Dim k As Long - Load Form1 - For k = 0 To Forms.Count - 1 - Set f = Forms(k) - Debug.Print "k = "; k; " name = "; f.Name - Next k - ``` -2. Build the project and run the EXE, or run it in the IDE (F5). -3. See the process end with `0xC0000005` (exit code -1073741819), or the access violation in the DEBUG CONSOLE. - -Declaring `f` As `Form1` or As `Object` instead, with everything else the same, returns the form and the program exits 0. With three forms loaded, the loop crashes the same way, and an earlier probe saw the loop variable corrupted: `k` read 0, 0, 0, then 8195702. - -**Expected behavior** -`Forms(k)` returns the loaded form, `f.Name` is `Form1`, and the program exits 0, as it does with `f` declared `As Object`. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -Also on BETA 983, as a compiled EXE (reproduced again with this project). Severity: crash (`0xC0000005`), from a form of access the documentation shows. What does not reproduce it, measured on BETA 995 as a compiled EXE: `Set f = Forms(0)` with the literal index 0 and no loop, then `f.Name`, returns `Form1` and the program exits 0; so does `s = Forms(0).Name`, and `Debug.Print Forms(0).Name`. An earlier entry said `s = Forms(0).Name` returns an empty string and the process later dies; this was not seen again here, and the loop is the smallest form found. From earlier probes: `n = 0: Set f = Forms(n)` outside a loop returns the form and the program exits 0; `For Each f In Forms` and `Unload Forms(i)` work; `Printers(0)` with a literal index works. A variant of the loop that wrote the name with a helper `Sub` instead of `Debug.Print` did not crash in one run; the crash was seen with `Debug.Print`, and with `Dim t As String = f.Name` and no printing. - - - - diff --git a/bugs/filed/forms-by-index-crash/forms-by-index-crash.twinproj b/bugs/filed/forms-by-index-crash/forms-by-index-crash.twinproj deleted file mode 100644 index bcadc3111..000000000 Binary files a/bugs/filed/forms-by-index-crash/forms-by-index-crash.twinproj and /dev/null differ diff --git a/bugs/filed/forms-by-index-crash/repro.json b/bugs/filed/forms-by-index-crash/repro.json deleted file mode 100644 index cbf52636c..000000000 --- a/bugs/filed/forms-by-index-crash/repro.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "mode": "run", - "exe": true, - "expect": { - "exit": 6 - }, - "issue": 2458 -} diff --git a/bugs/filed/forms-by-index-crash/src/Settings b/bugs/filed/forms-by-index-crash/src/Settings deleted file mode 100644 index f99f10a51..000000000 --- a/bugs/filed/forms-by-index-crash/src/Settings +++ /dev/null @@ -1,61 +0,0 @@ -{ - "configuration.inherits": "Defaults", - "project.appTitle": "FormsByIndexCrash", - "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", - "project.buildType": "Standard EXE", - "project.description": "Reproduces: Reading `Forms` by index returns a broken reference, and the process then crashes", - "project.exportPathIsV2": true, - "project.id": "{E4316C37-27D2-4045-A50A-369871A26E4A}", - "project.name": "FormsByIndexCrash", - "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 -} diff --git a/bugs/filed/forms-by-index-crash/src/Sources/Form1.tbform b/bugs/filed/forms-by-index-crash/src/Sources/Form1.tbform deleted file mode 100644 index a7f451a7d..000000000 --- a/bugs/filed/forms-by-index-crash/src/Sources/Form1.tbform +++ /dev/null @@ -1,83 +0,0 @@ -[ - { - "AlwaysShowKeyboardCues": false, - "Appearance": "vbAppear3d", - "AutoRedraw": false, - "BackColor": -2147483633, - "BorderStyle": "vbSizable", - "Caption": "Form1", - "ClipControls": true, - "ControlBox": true, - "DrawMode": "vbCopyPen", - "DrawStyle": "vbSolid", - "DrawWidth": 1, - "Enabled": true, - "FillColor": 0, - "FillStyle": "vbFSTransparent", - "FontBold": false, - "FontItalic": false, - "FontName": "Segoe UI", - "FontSize": 8, - "FontStrikethru": false, - "FontTransparent": true, - "FontUnderline": false, - "ForeColor": -2147483630, - "FormDesignerId": "{EAEAEAEA-EAEA-EAEA-EAEA-EAEAEAEAEA01}", - "HasDC": true, - "Height": 300, - "HelpContextID": 0, - "Icon": [], - "KeyPreview": false, - "Left": 0, - "LinkMode": "vbLinkNone", - "LinkTopic": null, - "MDIChild": false, - "MaxButton": true, - "MaxHeight": 0, - "MaxWidth": 0, - "MinButton": true, - "MinHeight": 0, - "MinWidth": 0, - "MouseIcon": [], - "MousePointer": "vbDefault", - "Moveable": true, - "Name": "Form1", - "NegotiateMenus": false, - "OLEDropMode": "vbOLEDropNone", - "Opacity": 100, - "Palette": [], - "PaletteMode": "vbPaletteModeHalftone", - "Picture": [], - "PictureDpiScaling": false, - "RightToLeft": false, - "ScaleHeight": 4500, - "ScaleLeft": 0, - "ScaleMode": "vbTwips", - "ScaleTop": 0, - "ScaleWidth": 4500, - "ShowInTaskbar": true, - "StartUpPosition": "vbStartUpScreen", - "TabFocusAutoSelect": false, - "Tag": null, - "Top": 0, - "TopMost": false, - "TransparencyKey": -1, - "Visible": true, - "WhatsThisButton": false, - "WhatsThisHelp": false, - "Width": 300, - "WindowState": "vbNormal", - "__IDEOptions": { - "alignToGrid": false, - "gridHeight": 10, - "gridWidth": 10, - "lockedControls": [], - "multiColoredGrabbers": false, - "showGrid": true, - "showOutlines": false - }, - "__lastUpdateMarker": 2061908507, - "_className": "Form", - "_clsid": "{33AD4F38-6699-11CF-B70C-00AA0060D393}" - } -] \ No newline at end of file diff --git a/bugs/filed/forms-by-index-crash/src/Sources/Form1.twin b/bugs/filed/forms-by-index-crash/src/Sources/Form1.twin deleted file mode 100644 index cc6a437d0..000000000 --- a/bugs/filed/forms-by-index-crash/src/Sources/Form1.twin +++ /dev/null @@ -1,10 +0,0 @@ -' An empty form, loaded by Startup.Main. Its design is in Form1.tbform. -[Description("")] -[FormDesignerId("EAEAEAEA-EAEA-EAEA-EAEA-EAEAEAEAEA01")] -[PredeclaredId] -Class Form1 - - Sub New() - End Sub - -End Class diff --git a/bugs/filed/forms-by-index-crash/src/Sources/Startup.twin b/bugs/filed/forms-by-index-crash/src/Sources/Startup.twin deleted file mode 100644 index 5dfd00b0e..000000000 --- a/bugs/filed/forms-by-index-crash/src/Sources/Startup.twin +++ /dev/null @@ -1,17 +0,0 @@ -' Reading Forms(k) through a variable declared As Form returns a broken reference, and the EXE crashes. -' Build the project and run the EXE (Debug.Print writes nothing there): it ends with 0xC0000005. -Module Startup - - Public Sub Main() - Dim f As Form ' the crash needs As Form; As Form1 or As Object works - Dim k As Long - - Load Form1 - For k = 0 To Forms.Count - 1 - Set f = Forms(k) - Debug.Print "k = "; k; " name = "; f.Name - Next k - End - End Sub - -End Module diff --git a/bugs/filed/hover-byval-option-explicit/REPORT.md b/bugs/filed/hover-byval-option-explicit/REPORT.md deleted file mode 100644 index ce03019f7..000000000 --- a/bugs/filed/hover-byval-option-explicit/REPORT.md +++ /dev/null @@ -1,48 +0,0 @@ -Filed as [twinbasic/twinbasic#2448](https://github.com/twinbasic/twinbasic/issues/2448). - -## Hover says a `ByVal` parameter was auto-generated because `Option Explicit` is off - -**Describe the bug** -In a project with `Option Explicit` on, hovering over a `ByVal` parameter of type `String`, `Variant`, `Object`, a class or tbIDE's `Host` shows a note that the variable was auto-generated because `Option Explicit` is off, and recommends turning it on. The option is on, and the parameter was declared by the user. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `hover-byval-option-explicit.twinproj` (attached as `hover-byval-option-explicit.zip`) and open `Symbols.twin`. `project.optionExplicit` is true in its `Settings`. The file holds: - ``` - Public Sub Probe2(ByVal h As Host, ByVal count As Long, ByVal col As Collection, _ - ByVal o As Object, ByVal v As Variant, ByRef r As Host, ByVal s As String) - Dim d As Host - Debug.Print h Is Nothing, count, col Is Nothing, o Is Nothing, IsEmpty(v), r Is Nothing, s, d Is Nothing - End Sub - ``` -2. Hover over `s` where `Debug.Print` uses it. -3. The hover shows: - - > *parameter* ByVal s As String - > - > ***note:*** *this variable was auto-generated due to* ***Option Explicit*** *being Off* - > - > ***recommendation:*** *use Option Explicit and declare variables explicitly* - -| hovered | note | -|---|---| -| `ByVal` of `String`, `Variant`, `Object`, `Collection` or tbIDE's `Host` | **yes** | -| `ByVal` of `Long` | no | -| `ByRef r As Host` | no | -| a local, `Dim d As Host` or `Dim c As New Collection` | no | - -**Expected behavior** -No note on any of them: `Option Explicit` is on and every one of these names is declared. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -Severity: cosmetic, but it tells the user to turn on an option that is already on, over a parameter they declared. - -So it takes `ByVal` and a type that is not a plain number. That looks like a hidden local copy that the compiler makes for such a parameter, which the hover then describes as a variable it generated for an undeclared name. - -This was measured by sending `textDocument/hover` over the compiler's language socket, with the parameters the IDE's own hover provider sends; the text above is the markdown that request returns, which is what the IDE's hover shows. The tooltip itself was not looked at in this run. - - diff --git a/bugs/filed/hover-byval-option-explicit/hover-byval-option-explicit.twinproj b/bugs/filed/hover-byval-option-explicit/hover-byval-option-explicit.twinproj deleted file mode 100644 index 0e1c2b7d6..000000000 Binary files a/bugs/filed/hover-byval-option-explicit/hover-byval-option-explicit.twinproj and /dev/null differ diff --git a/bugs/filed/hover-byval-option-explicit/repro.json b/bugs/filed/hover-byval-option-explicit/repro.json deleted file mode 100644 index bec73993f..000000000 --- a/bugs/filed/hover-byval-option-explicit/repro.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "mode": "manual", - "steps": "1. Open hover-byval-option-explicit.twinproj in the twinBASIC IDE (project.optionExplicit is true in its Settings) and open Symbols.twin.\n2. Hover the mouse over the name s, where Debug.Print uses it.\n3. The hover shows: *parameter* ByVal s As String, then \"note: this variable was auto-generated due to Option Explicit being Off\" and \"recommendation: use Option Explicit and declare variables explicitly\".\n4. Hover over count (ByVal Long), r (ByRef r As Host) and d (a Dim): no such note. Over h (Host), col (Collection), o (Object) and v (Variant): the note.\nExpected: no note on any of them, since Option Explicit is on and the parameters are declared.\nRe-measured on BETA 995 by `addin-test.bat --only symbols` (a script sends textDocument/hover over the compiler's language socket, as the IDE's own hover provider does; the project is test/addin/probes/symbols). The text in step 3 is what that request returns; the hover tooltip itself was not looked at.", - "issue": 2448 -} diff --git a/bugs/filed/hover-byval-option-explicit/src/Sources/Symbols.twin b/bugs/filed/hover-byval-option-explicit/src/Sources/Symbols.twin deleted file mode 100644 index b1beae21d..000000000 --- a/bugs/filed/hover-byval-option-explicit/src/Sources/Symbols.twin +++ /dev/null @@ -1,8 +0,0 @@ -' Hover over the parameters where Probe2 uses them. project.optionExplicit is true. -Module Symbols - Public Sub Probe2(ByVal h As Host, ByVal count As Long, ByVal col As Collection, _ - ByVal o As Object, ByVal v As Variant, ByRef r As Host, ByVal s As String) - Dim d As Host - Debug.Print h Is Nothing, count, col Is Nothing, o Is Nothing, IsEmpty(v), r Is Nothing, s, d Is Nothing - End Sub -End Module diff --git a/bugs/filed/index-error-numbers/REPORT.md b/bugs/filed/index-error-numbers/REPORT.md deleted file mode 100644 index 74ef83ae1..000000000 --- a/bugs/filed/index-error-numbers/REPORT.md +++ /dev/null @@ -1,36 +0,0 @@ -Filed as [twinbasic/twinbasic#2440](https://github.com/twinbasic/twinbasic/issues/2440). - -## An out-of-range index raises `&H8002000B` or `&H80004005`, not VBA's error 9 - -**Describe the bug** -An array or `Collection` index that is out of range raises -2147352565 (`&H8002000B`, *Invalid index.*) or -2147467259 (`&H80004005`, *Unspecified error*). VBA raises error 9, *Subscript out of range*, so code that handles `Err.Number = 9` does not recognise the error. There is no diagnostic. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `index-error-numbers.twinproj` (attached as `index-error-numbers.zip`) and run it (F5). Its `Sub Main` holds the whole bug: - ``` - Dim a(5) As Long - On Error Resume Next - a(7) = 1 - Debug.Print Err.Number, Hex$(Err.Number), Err.Description - ``` -2. See `-2147352565 8002000B Invalid index.` for this case. The project goes on to try the other cases, one line each: - - | access | twinBASIC | VBA, per VBA-Docs' *Subscript out of range (Error 9)* | - |---|---|---| - | past a fixed or dynamic array's bound, a `Variant` array's, or `Split("x y")(5)` | -2147352565 (`8002000B`) *Invalid index.* | 9 | - | an element of an array never dimensioned: `Dim u() As Integer: u(8) = 234`, VBA-Docs' own example | -2147467259 (`80004005`) *Unspecified error* | 9 | - | a `Collection` member by a missing index or key | -2147467259 *Unspecified error* | 9 for a missing member | - | `Forms(99)`, `Forms.Item(-1)` | -2147467259 *Unspecified error* | --- | - -**Expected behavior** -Error 9, as in VBA, for each of these. `Err.Raise 9` already gives 9 with the description *Subscript out of range*. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -Also on BETA 983, in the IDE and in a compiled EXE alike. Severity: VBA code that handles `Err.Number = 9` does not recognise the error, with no diagnostic. What does not reproduce it: `UBound` of an erased array, `Printers(99)` and `Err.Raise 9` all give 9, and division by zero gives 11. The IDE's run-time error panel shows the same number `Err.Number` holds, for the array case. An erased array behaves as one never dimensioned: `-2147467259` for an element, 9 from `LBound` and `UBound`. `Printers` raises 9 past its end but `-2147467259` for a negative index and for an unknown name. The description of `-2147467259` varies between runs: *Unspecified error* in one, *Automation error* in another. The project's output on BETA 995 matches the table's first three rows; the `Forms` row and the other facts in this paragraph are from earlier runs of the same kind, not re-run for this report. - - diff --git a/bugs/filed/index-error-numbers/index-error-numbers.twinproj b/bugs/filed/index-error-numbers/index-error-numbers.twinproj deleted file mode 100644 index cd76c7e21..000000000 Binary files a/bugs/filed/index-error-numbers/index-error-numbers.twinproj and /dev/null differ diff --git a/bugs/filed/index-error-numbers/repro.json b/bugs/filed/index-error-numbers/repro.json deleted file mode 100644 index d06f67c3a..000000000 --- a/bugs/filed/index-error-numbers/repro.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "mode": "run", - "expect": { - "exit": 0, - "output": [ - "^a\\(7\\) = 1, past a fixed array: -2147352565 +8002000B ", - "^u\\(8\\) = 234, an array never dimensioned: -2147467259 +80004005 ", - "^c\\(5\\), a missing Collection index: -2147467259 +80004005 " - ] - }, - "issue": 2440 -} diff --git a/bugs/filed/index-error-numbers/src/Settings b/bugs/filed/index-error-numbers/src/Settings deleted file mode 100644 index f41aba416..000000000 --- a/bugs/filed/index-error-numbers/src/Settings +++ /dev/null @@ -1,61 +0,0 @@ -{ - "configuration.inherits": "Defaults", - "project.appTitle": "IndexErrorNumbers", - "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", - "project.buildType": "Standard EXE", - "project.description": "Reproduces: An out-of-range index raises `&H8002000B` or `&H80004005`, not VBA's error 9", - "project.exportPathIsV2": true, - "project.id": "{D19A71F3-2CAA-4DC8-89B9-102978B7BB28}", - "project.name": "IndexErrorNumbers", - "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 -} diff --git a/bugs/filed/index-error-numbers/src/Sources/Startup.twin b/bugs/filed/index-error-numbers/src/Sources/Startup.twin deleted file mode 100644 index 633132048..000000000 --- a/bugs/filed/index-error-numbers/src/Sources/Startup.twin +++ /dev/null @@ -1,38 +0,0 @@ -' An out-of-range index raises &H8002000B or &H80004005, where VBA raises error 9. -Module Startup - - Private Sub Show(ByVal what As String) - Debug.Print what; ": "; Err.Number; " "; Hex$(Err.Number); " "; Err.Description - Err.Clear - End Sub - - Public Sub Main() - On Error Resume Next - - Dim a(5) As Long - a(7) = 1 - Show "a(7) = 1, past a fixed array" ' VBA: 9 - - Dim v As Variant = Array(1, 2, 3) - v(9) = 1 - Show "v(9) = 1, past a Variant array" ' VBA: 9 - - Dim s As String = Split("x y")(5) - Show "Split(""x y"")(5)" ' VBA: 9 - - Dim u() As Integer - u(8) = 234 - Show "u(8) = 234, an array never dimensioned" ' VBA: 9 - - Dim c As New Collection - c.Add 1, "one" - v = c(5) - Show "c(5), a missing Collection index" ' VBA: 9 - v = c("nokey") - Show "c(""nokey""), a missing Collection key" - - Err.Raise 9 - Show "Err.Raise 9" ' 9 - End Sub - -End Module diff --git a/bugs/filed/llvm-err-after-raise/REPORT.md b/bugs/filed/llvm-err-after-raise/REPORT.md index 18eb2ba48..8d6b5fd37 100644 --- a/bugs/filed/llvm-err-after-raise/REPORT.md +++ b/bugs/filed/llvm-err-after-raise/REPORT.md @@ -19,7 +19,11 @@ Steps to reproduce the behavior: 2. See `-353703423 / / Application-defined or object-defined error`. The project also calls the same procedure without the attribute, and that one prints `5 / MySrc / my text`. **Expected behavior** -`5 / MySrc / my text`, as without the attribute. +`5 / MySrc / my text`, as without the attribute. VB6 gives the same after `On Error Resume Next` and in an `On Error GoTo` handler (the project is attached as `llvm-err-after-raise-vb6.zip`): +``` +plain: 5 / MySrc / my text +GoTo: 5 / MySrc / my text +``` **Desktop:** - OS: Windows 10 Pro 22H2 (build 19045) diff --git a/bugs/filed/llvm-err-after-raise/vb6/Module1.bas b/bugs/filed/llvm-err-after-raise/vb6/Module1.bas new file mode 100644 index 000000000..ee198fd09 --- /dev/null +++ b/bugs/filed/llvm-err-after-raise/vb6/Module1.bas @@ -0,0 +1,32 @@ +Attribute VB_Name = "Module1" +Option Explicit + +' The VB6 side of llvm-err-after-raise: what Err holds after a handled Err.Raise, +' written to out.txt as the twinBASIC project's lines are. + +Sub Main() + On Error GoTo Fail + Open App.Path & "\out.txt" For Output As #9 + ResumeNextRaise + GoToRaise + Close #9 + Exit Sub +Fail: + On Error Resume Next + Print #9, "unhandled error " & Err.Number & ": " & Err.Description + Close #9 +End Sub + +Private Sub ResumeNextRaise() + On Error Resume Next + Err.Raise 5, "MySrc", "my text" + Print #9, "plain: "; Err.Number; " / "; Err.Source; " / "; Err.Description +End Sub + +Private Sub GoToRaise() + On Error GoTo Handler + Err.Raise 5, "MySrc", "my text" + Exit Sub +Handler: + Print #9, "GoTo: "; Err.Number; " / "; Err.Source; " / "; Err.Description +End Sub diff --git a/bugs/lset-long-codegen-error/vb6/Probe.vbp b/bugs/filed/llvm-err-after-raise/vb6/Probe.vbp similarity index 100% rename from bugs/lset-long-codegen-error/vb6/Probe.vbp rename to bugs/filed/llvm-err-after-raise/vb6/Probe.vbp diff --git a/bugs/filed/lset-long-codegen-error/REPORT.md b/bugs/filed/lset-long-codegen-error/REPORT.md new file mode 100644 index 000000000..2de286edf --- /dev/null +++ b/bugs/filed/lset-long-codegen-error/REPORT.md @@ -0,0 +1,32 @@ +Filed as [twinbasic/twinbasic#2466](https://github.com/twinbasic/twinbasic/issues/2466). + +## `LSet` on a `Long` variable compiles, and the build fails with a codegen error + +**Describe the bug** +`LSet` is defined for a string or a user-defined type. A statement `LSet n = "ab"` with `n` declared `As Long` is accepted by the compiler without a diagnostic, and building the project then fails: the linker reports a code-generation error at the statement, and no exe is produced. Run from the IDE (F5), the project stops the same way. Observed in a build of the reproducer project. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `lset-long-codegen-error.twinproj` (attached as `lset-long-codegen-error.zip`). Its one source file, `Startup.twin`, is a `Sub Main` with `Dim n As Long` and `LSet n = "ab"`. +2. Compile it: no errors, warnings or hints. +3. Build it (Build, or F5) and read the DEBUG CONSOLE: + ``` + [BUILD] Starting... + [LINKER] compilation (codegen) error detected in 'Startup.Mainrootmain' at line #5 + [LINKER] FAILED due to compilation errors 'LsetLongCodegenError_win32.exe' + [BUILD] failed + ``` + +**Expected behavior** +A compile error on the `LSet` line, as for the neighbouring wrong operands: `LSet s = a` with `a` a user-defined type and `RSet a = b` with user-defined types are refused with TB5001 (*unable to convert type ... to String*), and `LSet a = s` with a string source and a user-defined type destination is refused with TB5249. VB6 refuses this statement at compile time with *LSet allowed only on strings and user-defined types* (attached as `lset-long-codegen-error-vb6.zip`, which does not build). + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 995 + +**Additional context** +Severity: low. The error is reported, so nothing runs wrongly, but it points at no cause, and the compile step that the IDE shows while typing says the project is fine. + +Tried with a `Long` destination only. The same on BETA 983. + + diff --git a/bugs/lset-long-codegen-error/lset-long-codegen-error.twinproj b/bugs/filed/lset-long-codegen-error/lset-long-codegen-error.twinproj similarity index 100% rename from bugs/lset-long-codegen-error/lset-long-codegen-error.twinproj rename to bugs/filed/lset-long-codegen-error/lset-long-codegen-error.twinproj diff --git a/bugs/filed/lset-long-codegen-error/repro.json b/bugs/filed/lset-long-codegen-error/repro.json new file mode 100644 index 000000000..8d13680da --- /dev/null +++ b/bugs/filed/lset-long-codegen-error/repro.json @@ -0,0 +1,8 @@ +{ + "mode": "build", + "expect": { + "exit": 5, + "message": "codegen" + }, + "issue": 2466 +} diff --git a/bugs/lset-long-codegen-error/src/Settings b/bugs/filed/lset-long-codegen-error/src/Settings similarity index 100% rename from bugs/lset-long-codegen-error/src/Settings rename to bugs/filed/lset-long-codegen-error/src/Settings diff --git a/bugs/lset-long-codegen-error/src/Sources/Startup.twin b/bugs/filed/lset-long-codegen-error/src/Sources/Startup.twin similarity index 100% rename from bugs/lset-long-codegen-error/src/Sources/Startup.twin rename to bugs/filed/lset-long-codegen-error/src/Sources/Startup.twin diff --git a/bugs/lset-long-codegen-error/vb6/Module1.bas b/bugs/filed/lset-long-codegen-error/vb6/Module1.bas similarity index 100% rename from bugs/lset-long-codegen-error/vb6/Module1.bas rename to bugs/filed/lset-long-codegen-error/vb6/Module1.bas diff --git a/bugs/lset-variant-array-element-noop/vb6/Probe.vbp b/bugs/filed/lset-long-codegen-error/vb6/Probe.vbp similarity index 100% rename from bugs/lset-variant-array-element-noop/vb6/Probe.vbp rename to bugs/filed/lset-long-codegen-error/vb6/Probe.vbp diff --git a/bugs/filed/lset-variant-array-element-noop/REPORT.md b/bugs/filed/lset-variant-array-element-noop/REPORT.md new file mode 100644 index 000000000..515611a1c --- /dev/null +++ b/bugs/filed/lset-variant-array-element-noop/REPORT.md @@ -0,0 +1,31 @@ +Filed as [twinbasic/twinbasic#2467](https://github.com/twinbasic/twinbasic/issues/2467). + +## `LSet` and `RSet` on an element of a `Variant` that holds an array change nothing + +**Describe the bug** +`LSet v(0) = "zz"` and `RSet v(1) = "zz"` do nothing when `v` is a `Variant` that holds an array, whether the array came from `Array(...)` or from `ReDim v(0 To 1)` on the `Variant`. The statements raise no error, and the element keeps its value. The same statements on an element of an array declared `Dim w(0 To 1) As Variant` work. Observed in a run of the reproducer project. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `lset-variant-array-element-noop.twinproj` (attached as `lset-variant-array-element-noop.zip`). Its one source file, `Startup.twin`, has a `Sub Main` that assigns `v = Array("0123456789", "abcde")`, runs `LSet v(0) = "zz"` and `RSet v(1) = "zz"`, and does the same to the elements of `Dim w(0 To 1) As Variant`. +2. Run it (F5) and read the DEBUG CONSOLE: + ``` + LSet v(0), v = Array(...): [0123456789] + RSet v(1), v = Array(...): [abcde] + LSet w(0), w(0 To 1) As Variant: [zz ] + RSet w(1), w(0 To 1) As Variant: [ zz] + ``` + +**Expected behavior** +The elements of `v` change as those of `w` do: `[zz ]` and `[ zz]`. VB6 does so (attached as `lset-variant-array-element-noop-vb6.zip`). + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 995 + +**Additional context** +Severity: low to medium. A statement that has no effect and no error is easy to miss. + +Tried: `ReDim v2(0 To 1)` on a `Variant`, then `LSet v2(0) = "zz"`, does the same. The `w` elements and `Dim a(0 To 2) As String` elements work. On BETA 983 the `v` lines read the same, and the `w` line for `LSet` reads `[zz23456789]`, because `LSet` did not yet fill with spaces there (fixed in BETA 984, which is a separate matter). + + diff --git a/bugs/lset-variant-array-element-noop/lset-variant-array-element-noop.twinproj b/bugs/filed/lset-variant-array-element-noop/lset-variant-array-element-noop.twinproj similarity index 100% rename from bugs/lset-variant-array-element-noop/lset-variant-array-element-noop.twinproj rename to bugs/filed/lset-variant-array-element-noop/lset-variant-array-element-noop.twinproj diff --git a/bugs/lset-variant-array-element-noop/repro.json b/bugs/filed/lset-variant-array-element-noop/repro.json similarity index 94% rename from bugs/lset-variant-array-element-noop/repro.json rename to bugs/filed/lset-variant-array-element-noop/repro.json index 96b80ba1f..3f63cb0af 100644 --- a/bugs/lset-variant-array-element-noop/repro.json +++ b/bugs/filed/lset-variant-array-element-noop/repro.json @@ -8,5 +8,6 @@ "^LSet w\\(0\\), w\\(0 To 1\\) As Variant: \\[zz \\]$", "^RSet w\\(1\\), w\\(0 To 1\\) As Variant: \\[ zz\\]$" ] - } + }, + "issue": 2467 } diff --git a/bugs/lset-variant-array-element-noop/src/Settings b/bugs/filed/lset-variant-array-element-noop/src/Settings similarity index 100% rename from bugs/lset-variant-array-element-noop/src/Settings rename to bugs/filed/lset-variant-array-element-noop/src/Settings diff --git a/bugs/lset-variant-array-element-noop/src/Sources/Startup.twin b/bugs/filed/lset-variant-array-element-noop/src/Sources/Startup.twin similarity index 100% rename from bugs/lset-variant-array-element-noop/src/Sources/Startup.twin rename to bugs/filed/lset-variant-array-element-noop/src/Sources/Startup.twin diff --git a/bugs/lset-variant-array-element-noop/vb6/Module1.bas b/bugs/filed/lset-variant-array-element-noop/vb6/Module1.bas similarity index 100% rename from bugs/lset-variant-array-element-noop/vb6/Module1.bas rename to bugs/filed/lset-variant-array-element-noop/vb6/Module1.bas diff --git a/bugs/filed/circle-positive-angle-unchecked/vb6/Probe.vbp b/bugs/filed/lset-variant-array-element-noop/vb6/Probe.vbp similarity index 71% rename from bugs/filed/circle-positive-angle-unchecked/vb6/Probe.vbp rename to bugs/filed/lset-variant-array-element-noop/vb6/Probe.vbp index 58915ee0b..4d210b0e8 100644 --- a/bugs/filed/circle-positive-angle-unchecked/vb6/Probe.vbp +++ b/bugs/filed/lset-variant-array-element-noop/vb6/Probe.vbp @@ -1,7 +1,5 @@ Type=Exe -Form=Form1.frm Module=Module1; Module1.bas -Module=PngDump; PngDump.bas Startup="Sub Main" ExeName32="Probe.exe" Name="Probe" diff --git a/bugs/filed/populatefrom-no-args/REPORT.md b/bugs/filed/populatefrom-no-args/REPORT.md deleted file mode 100644 index dd02a849f..000000000 --- a/bugs/filed/populatefrom-no-args/REPORT.md +++ /dev/null @@ -1,39 +0,0 @@ -Filed as [twinbasic/twinbasic#2450](https://github.com/twinbasic/twinbasic/issues/2450). - -## `[PopulateFrom]` with no arguments crashes the compiler - -**Describe the bug** -An `Enum` marked `[PopulateFrom]` with no argument list crashes the compiler while the project is parsed. `tbbuild` reports it as a crash, `the compiler crashed 2x -- this project takes it down`, `last parsing: CrashProbe.twin`. A person who forgets the arguments is not told what is missing. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `populatefrom-no-args.twinproj` (attached as `populatefrom-no-args.zip`). Its one source file, `Probe.twin`, holds the whole bug: - ``` - Public Module CrashProbe - [PopulateFrom] - Public Enum E - End Enum - End Module - ``` -2. See the compiler crash and restart while the project is parsed. - -The body of the Enum does not matter: the same crash comes with a member in it, and with the Enum inside a Class instead of a Module. The documented shape is five strings, `("json", "/Resources/PROBE/Strings.json", "events", "name", "id")`, and the other wrong shapes tried are handled: - -| argument list | result | -|---|---| -| none, `[PopulateFrom]` | **the compiler crashes** | -| `(True)`, `(False)`, `(1)` | TB5155 `This attribute is not supported in this context` | -| `("probe")`, on the reproduction above | TB5083 `unsupported data source` | -| the documented five strings, with a resource that exists | compiles | - -**Expected behavior** -A diagnostic naming the missing arguments, as the other wrong argument lists get (TB5155 or TB5083), not a crash. A missing argument list is the one wrong shape that is not checked. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -First seen on BETA 987; the reproducer crashes BETA 983 as well. Severity: the compiler process dies while the project is being parsed. The same project with `[PopulateFrom("probe")]` builds and reports the one TB5083 row. The rows for `(True)`, `(False)` and `(1)` come from a batch of probes, not from the reproducer. - - diff --git a/bugs/filed/populatefrom-no-args/src/Sources/Probe.twin b/bugs/filed/populatefrom-no-args/src/Sources/Probe.twin deleted file mode 100644 index 3674040aa..000000000 --- a/bugs/filed/populatefrom-no-args/src/Sources/Probe.twin +++ /dev/null @@ -1,6 +0,0 @@ -' [PopulateFrom] with no argument list on an Enum: the compiler crashes while parsing this file. -Public Module CrashProbe - [PopulateFrom] - Public Enum E - End Enum -End Module diff --git a/bugs/filed/populatefrom-no-args/src/Sources/Startup.twin b/bugs/filed/populatefrom-no-args/src/Sources/Startup.twin deleted file mode 100644 index 20233b3a9..000000000 --- a/bugs/filed/populatefrom-no-args/src/Sources/Startup.twin +++ /dev/null @@ -1,6 +0,0 @@ -Module Startup - - Public Sub Main() - End Sub - -End Module diff --git a/bugs/filed/proper-case-even-separators/REPORT.md b/bugs/filed/proper-case-even-separators/REPORT.md deleted file mode 100644 index da3f7dd82..000000000 --- a/bugs/filed/proper-case-even-separators/REPORT.md +++ /dev/null @@ -1,31 +0,0 @@ -Filed as [twinbasic/twinbasic#2464](https://github.com/twinbasic/twinbasic/issues/2464). - -## `StrConv` with `vbProperCase` leaves a word in lowercase when it follows an even number of separators - -**Describe the bug** -`StrConv(s, vbProperCase)` capitalises the first letter of a word that follows one, three or five separator characters (space, tab, `vbCr`, `vbLf`, vertical tab, form feed, NUL), and a word at the start of the string. After two, four or six separators the word is left in lowercase, so every word after a `vbCrLf` in multi-line text stays lowercase, and so does the second word of `"a b"` (two spaces). It is a regression: BETA 983 capitalised every word. Observed in a run of the reproducer project. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `proper-case-even-separators.twinproj` (attached as `proper-case-even-separators.zip`). Its one source file, `Startup.twin`, has a `Sub Main` that calls `StrConv` with `vbProperCase` on `"a b"`, `"a b"`, `"a b"` and `"a" & vbCrLf & "b"`. -2. Run it (F5) and read the DEBUG CONSOLE: - ``` - one space: [A B] - two spaces: [A b] - three spaces: [A B] - vbCrLf: [Ab] - ``` - -**Expected behavior** -Every word is capitalised, whatever the number of separators before it: `[A B]` and `[AB]`. VB6 does so (attached as `proper-case-even-separators-vb6.zip`, which prints `[A B]` and `[AB]`), and so does BETA 983. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -Severity: medium. Text with a line break or with aligned columns comes out half capitalised, and nothing reports it. - -With `"a" & Space$(n) & "b" & Space$(n) & "c"`, n = 0 gives `Abc` and n = 1, 3 and 5 give every word capitalised; n = 2, 4 and 6 give `A`, then `b` and `c` in lowercase. A space followed by a tab counts as two separators, and `" a b c "` (one leading space) comes out as `" a b c "`, all lowercase. The result is the same with an explicit `LCID` of 1033. It looks as if each separator toggles a "capitalise the next letter" flag instead of setting it. BETA 983 and VB6 give the same result as each other for all of these. - - diff --git a/bugs/filed/proper-case-even-separators/proper-case-even-separators.twinproj b/bugs/filed/proper-case-even-separators/proper-case-even-separators.twinproj deleted file mode 100644 index 1bfa56779..000000000 Binary files a/bugs/filed/proper-case-even-separators/proper-case-even-separators.twinproj and /dev/null differ diff --git a/bugs/filed/proper-case-even-separators/repro.json b/bugs/filed/proper-case-even-separators/repro.json deleted file mode 100644 index 0693c751d..000000000 --- a/bugs/filed/proper-case-even-separators/repro.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "mode": "run", - "expect": { - "exit": 0, - "output": [ - "^one space: \\[A B\\]$", - "^two spaces: \\[A b\\]$", - "^three spaces: \\[A B\\]$", - "^vbCrLf: \\[Ab\\]$" - ] - }, - "issue": 2464 -} diff --git a/bugs/filed/proper-case-even-separators/src/Settings b/bugs/filed/proper-case-even-separators/src/Settings deleted file mode 100644 index 17cad0c21..000000000 --- a/bugs/filed/proper-case-even-separators/src/Settings +++ /dev/null @@ -1,61 +0,0 @@ -{ - "configuration.inherits": "Defaults", - "project.appTitle": "ProperCaseEvenSeparators", - "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", - "project.buildType": "Standard EXE", - "project.description": "Reproduces: StrConv vbProperCase leaves a word in lowercase after an even number of separators", - "project.exportPathIsV2": true, - "project.id": "{003E64D6-B17F-4683-B01E-20DD325B7651}", - "project.name": "ProperCaseEvenSeparators", - "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 -} diff --git a/bugs/filed/proper-case-even-separators/src/Sources/Startup.twin b/bugs/filed/proper-case-even-separators/src/Sources/Startup.twin deleted file mode 100644 index ce1283cc8..000000000 --- a/bugs/filed/proper-case-even-separators/src/Sources/Startup.twin +++ /dev/null @@ -1,10 +0,0 @@ -Module Startup - - Public Sub Main() - Debug.Print "one space: [" & StrConv("a b", vbProperCase) & "]" - Debug.Print "two spaces: [" & StrConv("a b", vbProperCase) & "]" - Debug.Print "three spaces: [" & StrConv("a b", vbProperCase) & "]" - Debug.Print "vbCrLf: [" & Replace(StrConv("a" & vbCrLf & "b", vbProperCase), vbCrLf, "") & "]" - End Sub - -End Module diff --git a/bugs/filed/proper-case-even-separators/vb6/Module1.bas b/bugs/filed/proper-case-even-separators/vb6/Module1.bas deleted file mode 100644 index d6befbbbe..000000000 --- a/bugs/filed/proper-case-even-separators/vb6/Module1.bas +++ /dev/null @@ -1,17 +0,0 @@ -Attribute VB_Name = "Module1" -Option Explicit - -Sub Main() - On Error GoTo Fail - Open App.Path & "\out.txt" For Output As #9 - Print #9, "one space: [" & StrConv("a b", vbProperCase) & "]" - Print #9, "two spaces: [" & StrConv("a b", vbProperCase) & "]" - Print #9, "three spaces: [" & StrConv("a b", vbProperCase) & "]" - Print #9, "vbCrLf: [" & Replace(StrConv("a" & vbCrLf & "b", vbProperCase), vbCrLf, "") & "]" - Close #9 - Exit Sub -Fail: - On Error Resume Next - Print #9, "unhandled error " & Err.Number & ": " & Err.Description - Close #9 -End Sub diff --git a/bugs/filed/stop-at-error-ends-procedure/repro.json b/bugs/filed/stop-at-error-ends-procedure/repro.json index c74db8c9b..5db08cd39 100644 --- a/bugs/filed/stop-at-error-ends-procedure/repro.json +++ b/bugs/filed/stop-at-error-ends-procedure/repro.json @@ -1,5 +1,10 @@ { - "mode": "manual", + "mode": "lane", + "lane": "ide:debugger", + "tests": [ + "the panel's Stop ends only the failing procedure: Main goes on", + "the Stop command, as the toolbar and Run > End give it, does the same" + ], "steps": "1. Open stop-at-error-ends-procedure.twinproj in the twinBASIC IDE and press F5. FillTable raises an error at the line marked FAILS and the error panel opens. The DEBUG CONSOLE shows main start and loop 0.\n2. In the error panel choose Stop (or the toolbar's Stop, or Run > End).\n3. Expected: the run ends, and nothing more is printed. Actual: FillTable ends, and Main goes on: the DEBUG CONSOLE shows main after call.\n4. A failed assertion: in Main, change the call FillTable to TestPadLeft, press F5, and at the error panel (Assert.Exact.AreEqual raised it) choose Stop, or Run > End. TestPadLeft carries on past the failed assertion (test after assert) and Main prints main after call.\nControl: Stop at an ordinary break, such as a breakpoint or a step, ends the whole run and prints aborted.\nRe-measured on BETA 995 by `ide-test.bat --only debugger` and `--only assert` (the projects are test/ide/probes/debugger and assert); both pass on BETA 983 too.", "issue": 2441 } diff --git a/bugs/filed/toolwindow-no-id/repro.json b/bugs/filed/toolwindow-no-id/repro.json index 0d7cbc4b9..91e37aa1c 100644 --- a/bugs/filed/toolwindow-no-id/repro.json +++ b/bugs/filed/toolwindow-no-id/repro.json @@ -1,5 +1,9 @@ { - "mode": "manual", + "mode": "lane", + "lane": "addin:panes", + "tests": [ + "ToolWindows.Add: two windows given no id are one window, emptied by the second Add" + ], "steps": "This is an add-in project: building it makes a DLL that the IDE's compiler loads on every start until the DLL is removed. Install it knowingly, in an IDE you can restart, and remove it afterwards.\n1. Open toolwindow-no-id.twinproj and build it for Win32. The DLL is written to Build\\ToolwindowNoId_win32.dll beside the project file. Close the IDE, copy the DLL into the IDE's addins\\win32 folder (beside twinBASIC.exe), and start the IDE again. Open any project: the toolbar has a button, No id windows.\n2. Click the button. The add-in adds two tool windows, \"Window one\" and \"Window two\", with no id, and then adds a third element to the first.\n3. See one tool window, titled NO ID 2, holding the elements noid2 (\"second\") and noid1again (\"first again\"). The first window is gone, and what was added through its ToolWindow object went into the second window.\n4. Remove the add-in: close the IDE and delete addins\\win32\\ToolwindowNoId_win32.dll.\nRe-measured on BETA 995 by `addin-test.bat --only panes` (the last test; its add-in is test/addin/probes/panes, whose third button runs the same code).", "issue": 2449 } diff --git a/bugs/filed/typing-after-open-reversed/REPORT.md b/bugs/filed/typing-after-open-reversed/REPORT.md index 1fd08407b..6291771b7 100644 --- a/bugs/filed/typing-after-open-reversed/REPORT.md +++ b/bugs/filed/typing-after-open-reversed/REPORT.md @@ -32,4 +32,4 @@ Found by the add-in test harness: `MsgBox(`, typed into the code editor just aft BETA 995 was not measured: no lane exercises it. `parseDocumentDecorations` and `revealLineInEditor` are unchanged in BETA 995's `ide/main.js` (the `revealedLineTime<700` test was read again), and the test harness, which runs on BETA 995, still has to wait out the 700 ms before it places a cursor. - + diff --git a/bugs/filed/typing-after-open-reversed/repro.json b/bugs/filed/typing-after-open-reversed/repro.json index 6d52d4b2f..948097d1e 100644 --- a/bugs/filed/typing-after-open-reversed/repro.json +++ b/bugs/filed/typing-after-open-reversed/repro.json @@ -1,5 +1,10 @@ { - "mode": "manual", - "steps": "Measured by a script, not by hand: a person has to type without a pause of 0.7 s, which is hard to time.\n1. Open typing-after-open-reversed.twinproj in the twinBASIC IDE, with Haystack.twin not open in an editor tab.\n2. Open Haystack.twin at line 4, column 9 the way Find in Files does: in the page, openEditors.openFile(node, false, false, false, 4, 9), with node the file's entry in the project tree. By hand: Find in Files for \"Dim needleCount\" (one match, at line 4, column 9), and double-click the result.\n3. 0.3 s later move the cursor to line 3, column 1, and type xyz, one key every 150 ms.\n4. Expected: line 3 starts with xyz. Actual: line 3 starts with x, and line 4 reads \" zyDim needleCount As Long\".\nControl: the same typing started more than 0.7 s after the file opened puts xyz at 3:1 in order.\nBETA 983 only: no lane measures it on BETA 995. In BETA 995's ide/main.js, parseDocumentDecorations still ends with if(performance.now()-revealedLineTime<700){revealLineInEditor(...)}, and the test harness (afterReveal in scripts/lib/tb-operate.mjs, used by the keys and symbols lanes on 995) still has to wait out that time before it places a cursor.", + "mode": "lane", + "lane": "ide:typing-after-open", + "tests": [ + "typing begun 0.3 s after a file opens at a line is reordered by the re-reveal", + "the same typing begun 1 s after the file opens puts xyz at the start of the line in order" + ], + "steps": "Measured by a script, not by hand: a person has to type without a pause of 0.7 s, which is hard to time.\n1. Open typing-after-open-reversed.twinproj in the twinBASIC IDE, with Haystack.twin not open in an editor tab.\n2. Open Haystack.twin at line 4, column 9 the way Find in Files does: in the page, openEditors.openFile(node, false, false, false, 4, 9), with node the file's entry in the project tree. By hand: Find in Files for \"Dim needleCount\" (one match, at line 4, column 9), and double-click the result.\n3. 0.3 s later move the cursor to line 3, column 1, and type xyz, one key every 150 ms.\n4. Expected: line 3 starts with xyz. Actual: line 3 starts with x, and line 4 reads \" zyDim needleCount As Long\".\nControl: the same typing started more than 0.7 s after the file opened puts xyz at 3:1 in order.\nAsserted on BETA 997 by ide-test.bat --only typing-after-open, with the control.", "issue": 2447 } diff --git a/bugs/filed/wv2-headers-foreach-crash/REPORT.md b/bugs/filed/wv2-headers-foreach-crash/REPORT.md deleted file mode 100644 index e4fcd77a3..000000000 --- a/bugs/filed/wv2-headers-foreach-crash/REPORT.md +++ /dev/null @@ -1,36 +0,0 @@ -Filed as [twinbasic/twinbasic#2460](https://github.com/twinbasic/twinbasic/issues/2460). - -## For Each over WebView2 request or response headers crashes in WebView2HeadersCollection.Next - -**Describe the bug** -`For Each` over the `WebView2RequestHeaders` that `NavigationStarting` receives crashes with an access violation in `WebView2HeadersCollection.Next`. `WebView2ResponseHeaders` returns the same enumerator from its `_NewEnum`, so `For Each` over response headers reaches the same code (not run). `For Each` calls `IEnumVARIANT::Next` with `pCeltFetched` set to a null pointer, which the interface allows, and the package's `Next` assigns to `pCeltFetched` without testing it. The DEBUG CONSOLE shows `NATIVE EXCEPTION: ACCESS_VIOLATION /WebView2HeadersCollection.twin; WebView2HeadersCollection.Next`. - -**To Reproduce** -Steps to reproduce the behavior: -1. Open `wv2-headers-foreach-crash.twinproj` (attached as `wv2-headers-foreach-crash.zip`). It references the WebView2 package and has one form, `Form1`, with one WebView2 control, `WebView21`. `Sub Main` shows the form modally. When the control is ready it navigates to `about:blank`, and its `NavigationStarting` handler goes through the request headers: - ``` - Private Sub WebView21_NavigationStarting(ByVal Uri As String, ByVal IsUserInitiated As Boolean, _ - ByVal IsRedirected As Boolean, ByVal RequestHeaders As WebView2RequestHeaders, _ - Cancel As Boolean) Handles WebView21.NavigationStarting - Debug.Print "NavigationStarting " & Uri - Dim h As WebView2Header - For Each h In RequestHeaders - Debug.Print h.Name & ": " & h.Value - Next - Debug.Print "after For Each" - End Sub - ``` -2. Run the project (F5). -3. See `NavigationStarting about:blank` in the DEBUG CONSOLE, and then `NATIVE EXCEPTION: ACCESS_VIOLATION /WebView2HeadersCollection.twin; WebView2HeadersCollection.Next`. `after For Each` is never printed. - -**Expected behavior** -`For Each` yields each header, and the loop ends. The package's documentation shows this loop in a `NavigationStarting` handler. `Next` should assign to `pCeltFetched` only when its address is not zero, for example `If VarPtr(pCeltFetched) <> 0 Then pCeltFetched = 1`, in both places it assigns it. - -**Desktop:** - - OS: Windows 10 Pro 22H2 (build 19045) - - twinBASIC compiler version: BETA 995 - -**Additional context** -Severity: medium; `For Each` is the documented way to read the headers, and it ends the program. Without the `For Each`, the same project navigates, closes the form and returns. The same project crashes the same way on BETA 983. Calling `Next` directly, through a copy of `IEnumVARIANT` with a variable for `pCeltFetched`, returns the headers and then the end; the crash needs the null pointer that `For Each` passes. The same null `pCeltFetched` from `For Each` was measured with an enumerator written in a project: an unguarded assignment fails with an access violation there too. `Reset`, which `For Each` calls first, returns `E_NOTIMPL` here, and `For Each` goes on to call `Next` regardless. - - diff --git a/bugs/filed/wv2-headers-foreach-crash/repro.json b/bugs/filed/wv2-headers-foreach-crash/repro.json deleted file mode 100644 index dad8ae658..000000000 --- a/bugs/filed/wv2-headers-foreach-crash/repro.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "mode": "run", - "expect": { - "exit": 5, - "output": [ - "^NavigationStarting about:blank$", - "ACCESS_VIOLATION .*WebView2HeadersCollection\\.Next" - ], - "absent": [ - "^after For Each$" - ] - }, - "issue": 2460 -} diff --git a/bugs/filed/wv2-headers-foreach-crash/src/Settings b/bugs/filed/wv2-headers-foreach-crash/src/Settings deleted file mode 100644 index 852228161..000000000 --- a/bugs/filed/wv2-headers-foreach-crash/src/Settings +++ /dev/null @@ -1,75 +0,0 @@ -{ - "configuration.inherits": "Defaults", - "project.appTitle": "Wv2HeadersForeachCrash", - "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", - "project.buildType": "Standard EXE", - "project.description": "Reproduces: For Each over WebView2 request or response headers crashes in WebView2HeadersCollection.Next", - "project.exportPathIsV2": true, - "project.id": "{6BD10F28-52EF-443F-AAE0-C288FDBC31CB}", - "project.name": "Wv2HeadersForeachCrash", - "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 - }, - { - "id": "{5D19653F-F49E-41F2-ABF8-16E473A89A7C}", - "isCompilerPackage": true, - "licence": "MIT", - "name": "[COMPILER PACKAGE] WebView2 Package", - "path32": "", - "path64": "", - "publisher": "TWINBASIC-COMPILER", - "symbolId": "WebView2Package", - "versionBuild": 0, - "versionMajor": 0, - "versionMinor": 0, - "versionRevision": 3 - } - ], - "project.settingsVersion": 1, - "project.startupObject": "Sub Main", - "project.warnings": { - "errors": [], - "hints": [], - "ignored": [], - "info": [], - "warnings": [] - }, - "runtime.useUnicodeStandardLibrary": true -} diff --git a/bugs/filed/wv2-headers-foreach-crash/src/Sources/Form1.tbform b/bugs/filed/wv2-headers-foreach-crash/src/Sources/Form1.tbform deleted file mode 100644 index 5610cc552..000000000 --- a/bugs/filed/wv2-headers-foreach-crash/src/Sources/Form1.tbform +++ /dev/null @@ -1,146 +0,0 @@ -[ - { - "AlwaysShowKeyboardCues": false, - "Appearance": "vbAppear3d", - "AutoRedraw": false, - "BackColor": 13223074, - "BorderStyle": "vbSizable", - "Caption": "WebView2 form", - "ClipControls": true, - "ControlBox": true, - "DrawMode": "vbCopyPen", - "DrawStyle": "vbSolid", - "DrawWidth": 1, - "Enabled": true, - "FillColor": 0, - "FillStyle": "vbFSTransparent", - "FontBold": false, - "FontItalic": false, - "FontName": "Segoe UI", - "FontSize": 8, - "FontStrikethru": false, - "FontTransparent": true, - "FontUnderline": false, - "ForeColor": -2147483630, - "FormDesignerId": "{7B247400-0000-4000-9000-7B2474000F01}", - "HasDC": true, - "Height": 480, - "HelpContextID": 0, - "Icon": "", - "KeyPreview": false, - "Left": 0, - "LinkMode": "vbLinkNone", - "LinkTopic": null, - "MDIChild": false, - "MaxButton": true, - "MaxHeight": 0, - "MaxWidth": 0, - "MinButton": true, - "MinHeight": 0, - "MinWidth": 0, - "MouseIcon": "", - "MousePointer": "vbDefault", - "Moveable": true, - "Name": "Form1", - "NegotiateMenus": false, - "OLEDropMode": "vbOLEDropNone", - "Opacity": 100, - "Palette": "", - "PaletteMode": "vbPaletteModeHalftone", - "Picture": "", - "PictureDpiScaling": false, - "RightToLeft": false, - "ScaleHeight": 8560, - "ScaleLeft": 0, - "ScaleMode": "vbTwips", - "ScaleTop": 0, - "ScaleWidth": 11760, - "ShowInTaskbar": true, - "StartUpPosition": "vbStartUpScreen", - "TabFocusAutoSelect": false, - "Tag": null, - "Top": 0, - "TopMost": false, - "TransparencyKey": -1, - "Visible": true, - "WhatsThisButton": false, - "WhatsThisHelp": false, - "Width": 640, - "WindowState": "vbNormal", - "__IDEOptions": { - "alignToGrid": false, - "gridHeight": 10, - "gridWidth": 10, - "lockedControls": [], - "multiColoredGrabbers": true, - "showGrid": true, - "showOutlines": false - }, - "__lastUpdateMarker": 3573907440, - "_children": [ - { - "AdditionalAllowedFrameAncestors": null, - "Anchors": { - "Bottom": true, - "Left": true, - "Right": true, - "Top": true, - "_clsid": "{8524D4B5-72A9-40A9-A189-29E7905C40CA}" - }, - "AreBrowserAcceleratorKeysEnabled": true, - "AreDefaultContextMenusEnabled": true, - "AreDefaultScriptDialogsEnabled": true, - "AreDevToolsEnabled": true, - "AreHostObjectsAllowed": true, - "BackColor": 10534293, - "CausesValidation": true, - "Dock": "vbDockNone", - "DocumentURL": "about:blank", - "DragIcon": "", - "DragMode": "vbManual", - "Enabled": true, - "EnvironmentOptions": { - "AdditionalBrowserArguments": null, - "AllowSingleSignOnUsingOSPrimaryAccount": false, - "BrowserExecutableFolder": null, - "EnableTrackingPrevention": true, - "ExclusiveUserDataFolderAccess": false, - "Language": null, - "TargetCompatibleBrowserVersion": "86.0.616.0", - "UserDataFolder": "", - "_className": "WebView2EnvironmentOptions", - "_clsid": "{185E63C0-E641-47ED-B6A9-8565168A7FCF}" - }, - "Height": 440, - "Index": -1, - "IsBuiltInErrorPageEnabled": true, - "IsGeneralAutoFillEnabled": true, - "IsMuted": false, - "IsPasswordAutoSaveEnabled": false, - "IsPinchZoomEnabled": true, - "IsScriptEnabled": true, - "IsStatusBarEnabled": false, - "IsSwipeNavigationEnabled": true, - "IsWebMessageEnabled": true, - "IsZoomControlEnabled": true, - "Left": 0, - "MouseIcon": "", - "MousePointer": "vbDefault", - "Name": "WebView21", - "TabIndex": 7, - "TabStop": true, - "Tag": null, - "Top": 0, - "UseDeferredEvents": true, - "UserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0", - "Visible": true, - "Width": 620, - "ZoomFactor": 1, - "_className": "WebView2", - "_clsid": "{93B7E07E-6C7F-4758-8E51-4B1B17EC0A62}" - } - ], - "_className": "Form", - "_clsid": "{33AD4F38-6699-11CF-B70C-00AA0060D393}" - } -] diff --git a/bugs/filed/wv2-headers-foreach-crash/src/Sources/Form1.twin b/bugs/filed/wv2-headers-foreach-crash/src/Sources/Form1.twin deleted file mode 100644 index b8dbc7895..000000000 --- a/bugs/filed/wv2-headers-foreach-crash/src/Sources/Form1.twin +++ /dev/null @@ -1,32 +0,0 @@ -' For Each over the request headers that NavigationStarting receives crashes -' with an access violation in WebView2HeadersCollection.Next: For Each passes -' Next a null pCeltFetched, and Next assigns to it without testing it. -' Without the For Each, the form opens about:blank and closes, and Main returns. - -[FormDesignerId("7B247400-0000-4000-9000-7B2474000F01")] -[PredeclaredId] -Class Form1 - - Private Sub WebView21_Ready() Handles WebView21.Ready - Debug.Print "WebView21 ready" - WebView21.Navigate "about:blank" - End Sub - - Private Sub WebView21_NavigationStarting(ByVal Uri As String, ByVal IsUserInitiated As Boolean, _ - ByVal IsRedirected As Boolean, ByVal RequestHeaders As WebView2RequestHeaders, _ - Cancel As Boolean) Handles WebView21.NavigationStarting - Debug.Print "NavigationStarting " & Uri - Dim h As WebView2Header - For Each h In RequestHeaders - Debug.Print h.Name & ": " & h.Value - Next - Debug.Print "after For Each" - End Sub - - Private Sub WebView21_NavigationComplete(ByVal IsSuccess As Boolean, ByVal WebErrorStatus As Long) _ - Handles WebView21.NavigationComplete - Debug.Print "NavigationComplete " & IsSuccess - Me.Close - End Sub - -End Class diff --git a/bugs/filed/wv2-headers-foreach-crash/src/Sources/Startup.twin b/bugs/filed/wv2-headers-foreach-crash/src/Sources/Startup.twin deleted file mode 100644 index 9d70f1799..000000000 --- a/bugs/filed/wv2-headers-foreach-crash/src/Sources/Startup.twin +++ /dev/null @@ -1,8 +0,0 @@ -Module Startup - - Public Sub Main() - Form1.Show vbModal - Debug.Print "Form1 closed" - End Sub - -End Module diff --git a/bugs/filed/wv2-headers-foreach-crash/wv2-headers-foreach-crash.twinproj b/bugs/filed/wv2-headers-foreach-crash/wv2-headers-foreach-crash.twinproj deleted file mode 100644 index c0506abbd..000000000 Binary files a/bugs/filed/wv2-headers-foreach-crash/wv2-headers-foreach-crash.twinproj and /dev/null differ diff --git a/bugs/forecolor-invalid-value-stored/forecolor-invalid-value-stored.twinproj b/bugs/forecolor-invalid-value-stored/forecolor-invalid-value-stored.twinproj index b1d28aab9..e0fa6e340 100644 Binary files a/bugs/forecolor-invalid-value-stored/forecolor-invalid-value-stored.twinproj and b/bugs/forecolor-invalid-value-stored/forecolor-invalid-value-stored.twinproj differ diff --git a/bugs/forecolor-invalid-value-stored/repro.json b/bugs/forecolor-invalid-value-stored/repro.json index 5af787d28..d8ab2da25 100644 --- a/bugs/forecolor-invalid-value-stored/repro.json +++ b/bugs/forecolor-invalid-value-stored/repro.json @@ -19,7 +19,11 @@ "^Printer\\.FillColor = -1: Err 0, reads FFFFFFFF$", "^UserControl\\.ForeColor = -1: Err 5, reads FFFFFFFF$", "^UserControl\\.BackColor = -1: Err 0, reads FFFFFFFF$", - "^UserControl\\.FillColor = -1: Err 0, reads FFFFFFFF$" + "^UserControl\\.FillColor = -1: Err 0, reads FFFFFFFF$", + "^MDIForm\\.BackColor = -1: Err 0, reads FFFFFFFF$", + "^MDIForm\\.BackColor = &H8000001F: Err 0, reads 8000001F$", + "^MDIForm \\(shown\\)\\.BackColor = -1: Err 5, reads FFFFFFFF$", + "^MDIForm \\(shown\\)\\.BackColor = &H8000001F: Err 5, reads 8000001F$" ] } } diff --git a/bugs/forecolor-invalid-value-stored/src/Sources/MyMDIForm.tbform b/bugs/forecolor-invalid-value-stored/src/Sources/MyMDIForm.tbform new file mode 100644 index 000000000..904a66f2f --- /dev/null +++ b/bugs/forecolor-invalid-value-stored/src/Sources/MyMDIForm.tbform @@ -0,0 +1,59 @@ +[ + { + "AlwaysShowKeyboardCues": false, + "Appearance": "vbAppear3d", + "AutoShowChildren": true, + "BackColor": -2147483636, + "Caption": "Window", + "Enabled": true, + "FormDesignerId": "{EAEAEAEA-EAEA-EAEA-EAEA-EAEAEAEAEA02}", + "Form_AlwaysShowKeyboardCues": false, + "Form_Opacity": 100, + "Form_TopMost": false, + "Height": 571, + "HelpContextID": 0, + "Icon": "", + "Index": -1, + "Left": 0, + "LinkMode": "vbLinkNone", + "LinkTopic": null, + "MaxHeight": 0, + "MaxWidth": 0, + "MinHeight": 0, + "MinWidth": 0, + "MouseIcon": "", + "MousePointer": "vbDefault", + "Moveable": true, + "Name": "MyMDIForm", + "NegotiateToolbars": true, + "OLEDropMode": "vbOLEDropNone", + "Opacity": 100, + "Picture": "", + "PictureDpiScaling": true, + "RightToLeft": false, + "ScrollBars": true, + "StartUpPosition": "vbStartUpWindowsDefault", + "TabFocusAutoSelect": false, + "Tag": null, + "Top": 0, + "TopMost": false, + "TransparencyKey": -1, + "Visible": true, + "WhatsThisHelp": false, + "Width": 912, + "WindowState": "vbNormal", + "__IDEOptions": { + "alignToGrid": false, + "gridHeight": 10, + "gridWidth": 10, + "lockedControls": [], + "multiColoredGrabbers": true, + "showGrid": true, + "showOutlines": false + }, + "__lastUpdateMarker": 3416063361, + "_children": [], + "_className": "MDIForm", + "_clsid": "{33AD4F70-6699-11CF-B70C-00AA0060D393}" + } +] diff --git a/bugs/forecolor-invalid-value-stored/src/Sources/MyMDIForm.twin b/bugs/forecolor-invalid-value-stored/src/Sources/MyMDIForm.twin new file mode 100644 index 000000000..ee52cbbad --- /dev/null +++ b/bugs/forecolor-invalid-value-stored/src/Sources/MyMDIForm.twin @@ -0,0 +1,9 @@ +[Description("")] +[FormDesignerId("EAEAEAEA-EAEA-EAEA-EAEA-EAEAEAEAEA02")] +[PredeclaredId] +Class MyMDIForm + + Sub New() + End Sub + +End Class diff --git a/bugs/forecolor-invalid-value-stored/src/Sources/Startup.twin b/bugs/forecolor-invalid-value-stored/src/Sources/Startup.twin index 006430f0b..4ae73f0f1 100644 --- a/bugs/forecolor-invalid-value-stored/src/Sources/Startup.twin +++ b/bugs/forecolor-invalid-value-stored/src/Sources/Startup.twin @@ -47,6 +47,21 @@ Module Startup Debug.Print "Printer.FillColor = -1: Err " & Err.Number & ", reads " & Hex$(Printer.FillColor): Err.Clear Form1.UC1.RunCases Unload Form1 + Load MyMDIForm + MyMDIForm.BackColor = vbGreen + MyMDIForm.BackColor = -1 + Debug.Print "MDIForm.BackColor = -1: Err " & Err.Number & ", reads " & Hex$(MyMDIForm.BackColor): Err.Clear + MyMDIForm.BackColor = vbGreen + MyMDIForm.BackColor = &H8000001F + Debug.Print "MDIForm.BackColor = &H8000001F: Err " & Err.Number & ", reads " & Hex$(MyMDIForm.BackColor): Err.Clear + MyMDIForm.Show + MyMDIForm.BackColor = vbGreen + MyMDIForm.BackColor = -1 + Debug.Print "MDIForm (shown).BackColor = -1: Err " & Err.Number & ", reads " & Hex$(MyMDIForm.BackColor): Err.Clear + MyMDIForm.BackColor = vbGreen + MyMDIForm.BackColor = &H8000001F + Debug.Print "MDIForm (shown).BackColor = &H8000001F: Err " & Err.Number & ", reads " & Hex$(MyMDIForm.BackColor): Err.Clear + Unload MyMDIForm End Sub End Module diff --git a/bugs/forecolor-invalid-value-stored/vb6/MDIForm1.frm b/bugs/forecolor-invalid-value-stored/vb6/MDIForm1.frm new file mode 100644 index 000000000..b922d4917 --- /dev/null +++ b/bugs/forecolor-invalid-value-stored/vb6/MDIForm1.frm @@ -0,0 +1,16 @@ +VERSION 5.00 +Begin VB.MDIForm MDIForm1 + Caption = "MDIForm1" + ClientHeight = 3000 + ClientLeft = 60 + ClientTop = 345 + ClientWidth = 4000 + LinkTopic = "MDIForm1" + StartUpPosition = 3 'Windows Default +End +Attribute VB_Name = "MDIForm1" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False +Option Explicit diff --git a/bugs/forecolor-invalid-value-stored/vb6/Module1.bas b/bugs/forecolor-invalid-value-stored/vb6/Module1.bas index 05057c104..8f1e9a3f9 100644 --- a/bugs/forecolor-invalid-value-stored/vb6/Module1.bas +++ b/bugs/forecolor-invalid-value-stored/vb6/Module1.bas @@ -50,6 +50,21 @@ Private Sub Cases() Print #9, "Printer.FillColor = -1: Err " & Err.Number & ", reads " & Hex$(Printer.FillColor): Err.Clear Form1.UC1.RunCases Unload Form1 + Load MDIForm1 + MDIForm1.BackColor = vbGreen + MDIForm1.BackColor = -1 + Print #9, "MDIForm.BackColor = -1: Err " & Err.Number & ", reads " & Hex$(MDIForm1.BackColor): Err.Clear + MDIForm1.BackColor = vbGreen + MDIForm1.BackColor = &H8000001F + Print #9, "MDIForm.BackColor = &H8000001F: Err " & Err.Number & ", reads " & Hex$(MDIForm1.BackColor): Err.Clear + MDIForm1.Show + MDIForm1.BackColor = vbGreen + MDIForm1.BackColor = -1 + Print #9, "MDIForm (shown).BackColor = -1: Err " & Err.Number & ", reads " & Hex$(MDIForm1.BackColor): Err.Clear + MDIForm1.BackColor = vbGreen + MDIForm1.BackColor = &H8000001F + Print #9, "MDIForm (shown).BackColor = &H8000001F: Err " & Err.Number & ", reads " & Hex$(MDIForm1.BackColor): Err.Clear + Unload MDIForm1 End Sub Sub Main() diff --git a/bugs/forecolor-invalid-value-stored/vb6/Probe.vbp b/bugs/forecolor-invalid-value-stored/vb6/Probe.vbp index f2be8429f..950884b68 100644 --- a/bugs/forecolor-invalid-value-stored/vb6/Probe.vbp +++ b/bugs/forecolor-invalid-value-stored/vb6/Probe.vbp @@ -1,5 +1,6 @@ Type=Exe Form=Form1.frm +Form=MDIForm1.frm UserControl=UC1.ctl Module=Module1; Module1.bas Startup="Sub Main" diff --git a/bugs/import-package-unticked/repro.json b/bugs/import-package-unticked/repro.json index 2d924c7b7..aaf1df29e 100644 --- a/bugs/import-package-unticked/repro.json +++ b/bugs/import-package-unticked/repro.json @@ -1,5 +1,11 @@ { - "mode": "manual", - "attach": ["DocProbePkg.twinpack"], + "mode": "lane", + "lane": "ide:packages", + "tests": [ + "the imported package is listed, but not ticked" + ], + "attach": [ + "DocProbePkg.twinpack" + ], "steps": "1. Unzip import-package-unticked.zip (attached to the report; pack writes it), which holds import-package-unticked.twinproj and DocProbePkg.twinpack. Open the project in the twinBASIC IDE.\n2. Open Project Settings > References > Available Packages and press Import from file...; choose DocProbePkg.twinpack.\n3. DocProbePkg appears in the Available Packages list, unticked. The compiler has answered the importPackage request with success: true and body: { packageSymbol: \"DocProbePkg\" }, so the import worked. Expected: the package is ticked, as after the online import. Actual: nothing in the project can use it until it is ticked by hand.\nRe-measured on BETA 995 by `ide-test.bat --only packages` (first test; its probe is test/ide/probes/packages); BETA 983 by hand. The lane has no native file picker, so it answers the dialog's OpenPackage call itself; the click on the button is real." } diff --git a/bugs/llvm-conversion-rounds-half-away/llvm-conversion-rounds-half-away.twinproj b/bugs/llvm-conversion-rounds-half-away/llvm-conversion-rounds-half-away.twinproj new file mode 100644 index 000000000..bd96cc769 Binary files /dev/null and b/bugs/llvm-conversion-rounds-half-away/llvm-conversion-rounds-half-away.twinproj differ diff --git a/bugs/llvm-conversion-rounds-half-away/repro.json b/bugs/llvm-conversion-rounds-half-away/repro.json new file mode 100644 index 000000000..6dc062226 --- /dev/null +++ b/bugs/llvm-conversion-rounds-half-away/repro.json @@ -0,0 +1,12 @@ +{ + "mode": "run", + "expect": { + "exit": 0, + "output": [ + "^plain 2\\.5:\\s+CLng\\(d\\)=2\\s", + "^plain -0\\.5:\\s+CLng\\(d\\)=0\\s", + "^LLVM\\s+2\\.5:\\s+CLng\\(d\\)=3\\s+CLng\\(s\\)=3\\s+CInt\\(d\\)=3\\s+CLngLng\\(d\\)=3\\s+l=d:3\\s+d\\\\1=3\\s+CLng\\(v\\)=2\\s+Round=2$", + "^LLVM\\s+-0\\.5:\\s+CLng\\(d\\)=-1\\s" + ] + } +} diff --git a/bugs/llvm-conversion-rounds-half-away/src/Settings b/bugs/llvm-conversion-rounds-half-away/src/Settings new file mode 100644 index 000000000..a9ff307f5 --- /dev/null +++ b/bugs/llvm-conversion-rounds-half-away/src/Settings @@ -0,0 +1,61 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "LlvmConversionRoundsHalfAway", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: LLVM-compiled code rounds a Single or Double to an integer half away from zero, where VB6 and plain code round half to even", + "project.exportPathIsV2": true, + "project.id": "{8ABB5233-02CD-48C9-A0C8-C67DD5442C43}", + "project.name": "LlvmConversionRoundsHalfAway", + "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 +} diff --git a/bugs/llvm-conversion-rounds-half-away/src/Sources/Startup.twin b/bugs/llvm-conversion-rounds-half-away/src/Sources/Startup.twin new file mode 100644 index 000000000..51dc658c7 --- /dev/null +++ b/bugs/llvm-conversion-rounds-half-away/src/Sources/Startup.twin @@ -0,0 +1,31 @@ +' Rounding a Single or Double to an integer: plain code rounds half to even, LLVM-compiled code half away from zero. +' Needs an LLVM licence (Ultimate): LlvmCases is compiled with LLVM by its [CompilerOptions("+llvm")]. +Module Startup + + Private Sub PlainCases() + Dim d As Double, s As Single, v As Variant, l As Long + Dim x As Variant + For Each x In Array(0.5, 2.5, 3.5, -0.5, -2.5) + d = x: s = x: v = d + l = d + Debug.Print "plain " & d & ": CLng(d)=" & CLng(d) & " CLng(s)=" & CLng(s) & " CInt(d)=" & CInt(d) & " CLngLng(d)=" & CLngLng(d) & " l=d:" & l & " d\1=" & (d \ 1) & " CLng(v)=" & CLng(v) & " Round=" & Round(d) + Next + End Sub + + [CompilerOptions("+llvm")] + Private Sub LlvmCases() + Dim d As Double, s As Single, v As Variant, l As Long + Dim x As Variant + For Each x In Array(0.5, 2.5, 3.5, -0.5, -2.5) + d = x: s = x: v = d + l = d + Debug.Print "LLVM " & d & ": CLng(d)=" & CLng(d) & " CLng(s)=" & CLng(s) & " CInt(d)=" & CInt(d) & " CLngLng(d)=" & CLngLng(d) & " l=d:" & l & " d\1=" & (d \ 1) & " CLng(v)=" & CLng(v) & " Round=" & Round(d) + Next + End Sub + + Public Sub Main() + PlainCases + LlvmCases + End Sub + +End Module diff --git a/bugs/llvm-conversion-rounds-half-away/vb6/Module1.bas b/bugs/llvm-conversion-rounds-half-away/vb6/Module1.bas new file mode 100644 index 000000000..fd2a36fae --- /dev/null +++ b/bugs/llvm-conversion-rounds-half-away/vb6/Module1.bas @@ -0,0 +1,23 @@ +Attribute VB_Name = "Module1" +Option Explicit + +Sub Main() + Dim d As Double + Dim s As Single + Dim v As Variant + Dim l As Long + Dim x As Variant + On Error GoTo Fail + Open App.Path & "\out.txt" For Output As #9 + For Each x In Array(0.5, 2.5, 3.5, -0.5, -2.5) + d = x: s = x: v = d + l = d + Print #9, "VB6 " & d & ": CLng(d)=" & CLng(d) & " CLng(s)=" & CLng(s) & " CInt(d)=" & CInt(d) & " l=d:" & l & " d\1=" & (d \ 1) & " CLng(v)=" & CLng(v) & " Round=" & Round(d) + Next + Close #9 + Exit Sub +Fail: + On Error Resume Next + Print #9, "unhandled error " & Err.Number & ": " & Err.Description + Close #9 +End Sub diff --git a/bugs/filed/enum-connection-points-contract/vb6/Probe.vbp b/bugs/llvm-conversion-rounds-half-away/vb6/Probe.vbp similarity index 52% rename from bugs/filed/enum-connection-points-contract/vb6/Probe.vbp rename to bugs/llvm-conversion-rounds-half-away/vb6/Probe.vbp index f4a01c6eb..4d210b0e8 100644 --- a/bugs/filed/enum-connection-points-contract/vb6/Probe.vbp +++ b/bugs/llvm-conversion-rounds-half-away/vb6/Probe.vbp @@ -1,7 +1,5 @@ Type=Exe -Module=ProbeMain; ProbeMain.bas -Class=Source; Source.cls -Class=Holder; Holder.cls +Module=Module1; Module1.bas Startup="Sub Main" ExeName32="Probe.exe" Name="Probe" diff --git a/bugs/llvm-double-overflow-no-error/llvm-double-overflow-no-error.twinproj b/bugs/llvm-double-overflow-no-error/llvm-double-overflow-no-error.twinproj new file mode 100644 index 000000000..9be1ab50b Binary files /dev/null and b/bugs/llvm-double-overflow-no-error/llvm-double-overflow-no-error.twinproj differ diff --git a/bugs/llvm-double-overflow-no-error/repro.json b/bugs/llvm-double-overflow-no-error/repro.json new file mode 100644 index 000000000..608117a1b --- /dev/null +++ b/bugs/llvm-double-overflow-no-error/repro.json @@ -0,0 +1,15 @@ +{ + "mode": "run", + "expect": { + "exit": 0, + "output": [ + "^plain\\s+Double \\*\\s+Err=6 r=1\\.#INF$", + "^plain\\s+Double \\+\\s+Err=6 r=1\\.#INF$", + "^LLVM\\s+Double \\*\\s+Err=0 r=1\\.#INF$", + "^LLVM\\s+Double \\+\\s+Err=0 r=1\\.#INF$", + "^LLVM\\s+Double \\^\\s+Err=0$", + "^LLVM\\s+Double /\\s+Err=6 r=5$", + "^LLVM\\s+1# / 0#\\s+Err=11 r=5$" + ] + } +} diff --git a/bugs/llvm-double-overflow-no-error/src/Settings b/bugs/llvm-double-overflow-no-error/src/Settings new file mode 100644 index 000000000..8682f7dee --- /dev/null +++ b/bugs/llvm-double-overflow-no-error/src/Settings @@ -0,0 +1,61 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "LlvmDoubleOverflowNoError", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: Double overflow raises no error in LLVM-compiled code, where it raises error 6 otherwise", + "project.exportPathIsV2": true, + "project.id": "{435AF0D4-D4C0-427F-ACDC-4B52412E8B55}", + "project.name": "LlvmDoubleOverflowNoError", + "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 +} diff --git a/bugs/llvm-double-overflow-no-error/src/Sources/Startup.twin b/bugs/llvm-double-overflow-no-error/src/Sources/Startup.twin new file mode 100644 index 000000000..5bd7dd56f --- /dev/null +++ b/bugs/llvm-double-overflow-no-error/src/Sources/Startup.twin @@ -0,0 +1,44 @@ +' Double overflow raises no error in an LLVM-compiled procedure. +' Needs an LLVM licence (Ultimate): LlvmCases is compiled with LLVM by its [CompilerOptions("+llvm")]. +' No project setting and no other attribute is involved. +Module Startup + + Private Sub PlainCases() + Dim big As Double = 1E+308 + Dim z As Double = 0# + Dim lmax As Long = 2147483647 + Dim r As Double + Dim l As Long + Dim n As Long + On Error Resume Next + r = 5#: Err.Clear: r = big * 10#: n = Err.Number: Debug.Print "plain Double * Err=" & n & " r=" & r + r = 5#: Err.Clear: r = big + big: n = Err.Number: Debug.Print "plain Double + Err=" & n & " r=" & r + r = 5#: Err.Clear: r = 10# ^ 400#: n = Err.Number: Debug.Print "plain Double ^ Err=" & n + r = 5#: Err.Clear: r = big / 0.1: n = Err.Number: Debug.Print "plain Double / Err=" & n & " r=" & r + r = 5#: Err.Clear: r = 1# / z: n = Err.Number: Debug.Print "plain 1# / 0# Err=" & n & " r=" & r + l = 5: Err.Clear: l = lmax + 1: n = Err.Number: Debug.Print "plain Long + Err=" & n & " l=" & l + End Sub + + [CompilerOptions("+llvm")] + Private Sub LlvmCases() + Dim big As Double = 1E+308 + Dim z As Double = 0# + Dim lmax As Long = 2147483647 + Dim r As Double + Dim l As Long + Dim n As Long + On Error Resume Next + r = 5#: Err.Clear: r = big * 10#: n = Err.Number: Debug.Print "LLVM Double * Err=" & n & " r=" & r + r = 5#: Err.Clear: r = big + big: n = Err.Number: Debug.Print "LLVM Double + Err=" & n & " r=" & r + r = 5#: Err.Clear: r = 10# ^ 400#: n = Err.Number: Debug.Print "LLVM Double ^ Err=" & n + r = 5#: Err.Clear: r = big / 0.1: n = Err.Number: Debug.Print "LLVM Double / Err=" & n & " r=" & r + r = 5#: Err.Clear: r = 1# / z: n = Err.Number: Debug.Print "LLVM 1# / 0# Err=" & n & " r=" & r + l = 5: Err.Clear: l = lmax + 1: n = Err.Number: Debug.Print "LLVM Long + Err=" & n & " l=" & l + End Sub + + Public Sub Main() + PlainCases + LlvmCases + End Sub + +End Module diff --git a/bugs/llvm-double-overflow-no-error/vb6/Module1.bas b/bugs/llvm-double-overflow-no-error/vb6/Module1.bas new file mode 100644 index 000000000..ff006f2ed --- /dev/null +++ b/bugs/llvm-double-overflow-no-error/vb6/Module1.bas @@ -0,0 +1,29 @@ +Attribute VB_Name = "Module1" +Option Explicit + +Sub Main() + Dim big As Double + Dim z As Double + Dim lmax As Long + Dim r As Double + Dim l As Long + Dim n As Long + On Error GoTo Fail + Open App.Path & "\out.txt" For Output As #9 + big = 1E+308 + z = 0# + lmax = 2147483647 + On Error Resume Next + r = 5#: Err.Clear: r = big * 10#: n = Err.Number: Print #9, "VB6 Double * Err=" & n & " r=" & r + r = 5#: Err.Clear: r = big + big: n = Err.Number: Print #9, "VB6 Double + Err=" & n & " r=" & r + r = 5#: Err.Clear: r = 10# ^ 400#: n = Err.Number: Print #9, "VB6 Double ^ Err=" & n + r = 5#: Err.Clear: r = big / 0.1: n = Err.Number: Print #9, "VB6 Double / Err=" & n & " r=" & r + r = 5#: Err.Clear: r = 1# / z: n = Err.Number: Print #9, "VB6 1# / 0# Err=" & n & " r=" & r + l = 5: Err.Clear: l = lmax + 1: n = Err.Number: Print #9, "VB6 Long + Err=" & n & " l=" & l + Close #9 + Exit Sub +Fail: + On Error Resume Next + Print #9, "unhandled error " & Err.Number & ": " & Err.Description + Close #9 +End Sub diff --git a/bugs/filed/enum-connections-one-item/vb6/Probe.vbp b/bugs/llvm-double-overflow-no-error/vb6/Probe.vbp similarity index 52% rename from bugs/filed/enum-connections-one-item/vb6/Probe.vbp rename to bugs/llvm-double-overflow-no-error/vb6/Probe.vbp index f4a01c6eb..4d210b0e8 100644 --- a/bugs/filed/enum-connections-one-item/vb6/Probe.vbp +++ b/bugs/llvm-double-overflow-no-error/vb6/Probe.vbp @@ -1,7 +1,5 @@ Type=Exe -Module=ProbeMain; ProbeMain.bas -Class=Source; Source.cls -Class=Holder; Holder.cls +Module=Module1; Module1.bas Startup="Sub Main" ExeName32="Probe.exe" Name="Probe" diff --git a/bugs/llvm-shift-count-truncated/llvm-shift-count-truncated.twinproj b/bugs/llvm-shift-count-truncated/llvm-shift-count-truncated.twinproj new file mode 100644 index 000000000..2850a8e38 Binary files /dev/null and b/bugs/llvm-shift-count-truncated/llvm-shift-count-truncated.twinproj differ diff --git a/bugs/llvm-shift-count-truncated/repro.json b/bugs/llvm-shift-count-truncated/repro.json new file mode 100644 index 000000000..ba7f8b8bc --- /dev/null +++ b/bugs/llvm-shift-count-truncated/repro.json @@ -0,0 +1,14 @@ +{ + "mode": "run", + "expect": { + "exit": 0, + "output": [ + "^plain\\s+Byte\\s+<< 256\\s+0$", + "^plain\\s+Integer << 65536\\s+0$", + "^LLVM\\s+Byte\\s+<< 256\\s+81$", + "^LLVM\\s+Byte\\s+>> 257\\s+40$", + "^LLVM\\s+Integer << 65536\\s+8001$", + "^LLVM\\s+Integer >> 65537\\s+C000$" + ] + } +} diff --git a/bugs/filed/boolean-intdiv-string/src/Settings b/bugs/llvm-shift-count-truncated/src/Settings similarity index 84% rename from bugs/filed/boolean-intdiv-string/src/Settings rename to bugs/llvm-shift-count-truncated/src/Settings index fa1b61944..dbca49875 100644 --- a/bugs/filed/boolean-intdiv-string/src/Settings +++ b/bugs/llvm-shift-count-truncated/src/Settings @@ -1,12 +1,12 @@ { "configuration.inherits": "Defaults", - "project.appTitle": "BooleanIntdivString", + "project.appTitle": "LlvmShiftCountTruncated", "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", "project.buildType": "Standard EXE", - "project.description": "Reproduces: `Boolean \\ String` and `Boolean Mod String` convert the `String` to `Boolean`", + "project.description": "Reproduces: In LLVM-compiled code a shift count is cut to the width of the shifted Byte or Integer", "project.exportPathIsV2": true, - "project.id": "{084C6717-68EE-47C6-A59C-477702219315}", - "project.name": "BooleanIntdivString", + "project.id": "{866FFD96-BEB9-450F-9C8E-9A6579A6F69A}", + "project.name": "LlvmShiftCountTruncated", "project.optionExplicit": true, "project.references": [ { diff --git a/bugs/llvm-shift-count-truncated/src/Sources/Startup.twin b/bugs/llvm-shift-count-truncated/src/Sources/Startup.twin new file mode 100644 index 000000000..88e1e722b --- /dev/null +++ b/bugs/llvm-shift-count-truncated/src/Sources/Startup.twin @@ -0,0 +1,39 @@ +' A shift count is cut to the width of the shifted Byte or Integer in LLVM-compiled code. +' Needs an LLVM licence (Ultimate): LlvmCases is compiled with LLVM by its [CompilerOptions("+llvm")]. +Module Startup + + Private Sub PlainCases() + Dim b As Byte = &H81 + Dim i As Integer = &H8001 + Dim l As Long = &H80000001 + Dim n As Long + n = 256: Debug.Print "plain Byte << 256 " & Hex$(b << n) + n = 257: Debug.Print "plain Byte >> 257 " & Hex$(b >> n) + n = 65536: Debug.Print "plain Integer << 65536 " & Hex$(i << n) + n = 65537: Debug.Print "plain Integer >> 65537 " & Hex$(i >> n) + n = 65536: Debug.Print "plain Long << 65536 " & Hex$(l << n) + n = -1: Debug.Print "plain Long << -1 " & Hex$(l << n) + n = -1: Debug.Print "plain Long >> -1 " & Hex$(l >> n) + End Sub + + [CompilerOptions("+llvm")] + Private Sub LlvmCases() + Dim b As Byte = &H81 + Dim i As Integer = &H8001 + Dim l As Long = &H80000001 + Dim n As Long + n = 256: Debug.Print "LLVM Byte << 256 " & Hex$(b << n) + n = 257: Debug.Print "LLVM Byte >> 257 " & Hex$(b >> n) + n = 65536: Debug.Print "LLVM Integer << 65536 " & Hex$(i << n) + n = 65537: Debug.Print "LLVM Integer >> 65537 " & Hex$(i >> n) + n = 65536: Debug.Print "LLVM Long << 65536 " & Hex$(l << n) + n = -1: Debug.Print "LLVM Long << -1 " & Hex$(l << n) + n = -1: Debug.Print "LLVM Long >> -1 " & Hex$(l >> n) + End Sub + + Public Sub Main() + PlainCases + LlvmCases + End Sub + +End Module diff --git a/bugs/math-intrinsics-ignore-argument-names/math-intrinsics-ignore-argument-names.twinproj b/bugs/math-intrinsics-ignore-argument-names/math-intrinsics-ignore-argument-names.twinproj new file mode 100644 index 000000000..a8fa450d1 Binary files /dev/null and b/bugs/math-intrinsics-ignore-argument-names/math-intrinsics-ignore-argument-names.twinproj differ diff --git a/bugs/math-intrinsics-ignore-argument-names/repro.json b/bugs/math-intrinsics-ignore-argument-names/repro.json new file mode 100644 index 000000000..cc4748aad --- /dev/null +++ b/bugs/math-intrinsics-ignore-argument-names/repro.json @@ -0,0 +1,16 @@ +{ + "mode": "run", + "expect": { + "exit": 0, + "output": [ + "^positional\\s+23456781$", + "^named, in order\\s+23456781$", + "^named, swapped\\s+4000000$", + "^right, named, swapped\\s+400$", + "^unsigned, named, swapped\\s+0$", + "^unknown names\\s+23456781$", + "^ByteSwap, unknown name\\s+78563412$", + "^own function, swapped\\s+23456781$" + ] + } +} diff --git a/bugs/math-intrinsics-ignore-argument-names/src/Settings b/bugs/math-intrinsics-ignore-argument-names/src/Settings new file mode 100644 index 000000000..362c75e5e --- /dev/null +++ b/bugs/math-intrinsics-ignore-argument-names/src/Settings @@ -0,0 +1,61 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "MathIntrinsicsIgnoreArgumentNames", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: ShiftRotateLeft, ShiftRotateRight, ShiftUnsignedRight and ByteSwap ignore argument names and take their arguments by position", + "project.exportPathIsV2": true, + "project.id": "{22F9CD19-BE88-4EEF-BAA3-504F2AD9A16C}", + "project.name": "MathIntrinsicsIgnoreArgumentNames", + "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 +} diff --git a/bugs/math-intrinsics-ignore-argument-names/src/Sources/Startup.twin b/bugs/math-intrinsics-ignore-argument-names/src/Sources/Startup.twin new file mode 100644 index 000000000..122767302 --- /dev/null +++ b/bugs/math-intrinsics-ignore-argument-names/src/Sources/Startup.twin @@ -0,0 +1,21 @@ +' The shift, rotate and byte-swap intrinsics take their arguments by position and ignore the names given. +Module Startup + + Private Function Rotl(ByVal Number As Long, ByVal ShiftAmount As Long) As Long + Rotl = (Number << (ShiftAmount And 31)) Or ((Number >> (32 - (ShiftAmount And 31))) And ((1 << (ShiftAmount And 31)) - 1)) + End Function + + Public Sub Main() + Dim n As Long = &H12345678 + Dim k As Long = 4 + Debug.Print "positional " & Hex$(ShiftRotateLeft(n, k)) + Debug.Print "named, in order " & Hex$(ShiftRotateLeft(Number:=n, ShiftAmount:=k)) + Debug.Print "named, swapped " & Hex$(ShiftRotateLeft(ShiftAmount:=k, Number:=n)) + Debug.Print "right, named, swapped " & Hex$(ShiftRotateRight(ShiftAmount:=k, Number:=n)) + Debug.Print "unsigned, named, swapped " & Hex$(ShiftUnsignedRight(ShiftAmount:=k, Number:=n)) + Debug.Print "unknown names " & Hex$(ShiftRotateLeft(Foo:=n, Bar:=k)) + Debug.Print "ByteSwap, unknown name " & Hex$(ByteSwap(Foo:=n)) + Debug.Print "own function, swapped " & Hex$(Rotl(ShiftAmount:=k, Number:=n)) + End Sub + +End Module diff --git a/bugs/filed/export-follows-junction/export-follows-junction.twinproj b/bugs/module-named-like-project/module-named-like-project.twinproj similarity index 67% rename from bugs/filed/export-follows-junction/export-follows-junction.twinproj rename to bugs/module-named-like-project/module-named-like-project.twinproj index 102b4c7a7..0936cc3ac 100644 Binary files a/bugs/filed/export-follows-junction/export-follows-junction.twinproj and b/bugs/module-named-like-project/module-named-like-project.twinproj differ diff --git a/bugs/module-named-like-project/repro.json b/bugs/module-named-like-project/repro.json new file mode 100644 index 000000000..41fd2997b --- /dev/null +++ b/bugs/module-named-like-project/repro.json @@ -0,0 +1,13 @@ +{ + "mode": "run", + "exe": true, + "expect": { + "exit": 5, + "output": [ + "^\\[BUILD\\] Executing 'ModuleNamedLikeProject\\.ModuleNamedLikeProject\\.\\w+'\\.\\.\\.$" + ], + "absent": [ + "^RunAfterBuild ran$" + ] + } +} diff --git a/bugs/module-named-like-project/src/Settings b/bugs/module-named-like-project/src/Settings new file mode 100644 index 000000000..aabccfd71 --- /dev/null +++ b/bugs/module-named-like-project/src/Settings @@ -0,0 +1,61 @@ +{ + "configuration.inherits": "Defaults", + "project.appTitle": "ModuleNamedLikeProject", + "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", + "project.buildType": "Standard EXE", + "project.description": "Reproduces: A [RunAfterBuild] Sub does not run, and nothing says so, when a module is named like the project", + "project.exportPathIsV2": true, + "project.id": "{36F80D98-2C72-4F38-80C0-45BBD3314CCC}", + "project.name": "ModuleNamedLikeProject", + "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 +} diff --git a/bugs/module-named-like-project/src/Sources/ModuleNamedLikeProject.twin b/bugs/module-named-like-project/src/Sources/ModuleNamedLikeProject.twin new file mode 100644 index 000000000..2611c83ae --- /dev/null +++ b/bugs/module-named-like-project/src/Sources/ModuleNamedLikeProject.twin @@ -0,0 +1,12 @@ +Module ModuleNamedLikeProject + + Public Sub Main() + End Sub + + [RunAfterBuild] + Public Sub Hello() + Debug.Cls + Debug.Print "RunAfterBuild ran" + End Sub + +End Module diff --git a/bugs/module-named-like-project/vb6/Module1.bas b/bugs/module-named-like-project/vb6/Module1.bas new file mode 100644 index 000000000..0d5e5dd16 --- /dev/null +++ b/bugs/module-named-like-project/vb6/Module1.bas @@ -0,0 +1,19 @@ +Attribute VB_Name = "ModuleNamedLikeProject" +Option Explicit + +' The VB6 side of the reproducer: a project named ModuleNamedLikeProject with a +' standard module of the same name. VB6 has no [RunAfterBuild], so what is checked +' is whether it accepts the name at all. +' Build and run it with: node scripts/bug_repro.mjs vb6 module-named-like-project + +Sub Main() + On Error GoTo Fail + Open App.Path & "\out.txt" For Output As #9 + Print #9, "Sub Main ran, in a module named like the project" + Close #9 + Exit Sub +Fail: + On Error Resume Next + Print #9, "unhandled error " & Err.Number & ": " & Err.Description + Close #9 +End Sub diff --git a/bugs/module-named-like-project/vb6/Probe.vbp b/bugs/module-named-like-project/vb6/Probe.vbp new file mode 100644 index 000000000..1b4e6c124 --- /dev/null +++ b/bugs/module-named-like-project/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=ModuleNamedLikeProject; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="ModuleNamedLikeProject" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/on-property-ignored/on-property-ignored.twinproj b/bugs/on-property-ignored/on-property-ignored.twinproj new file mode 100644 index 000000000..5d4133009 Binary files /dev/null and b/bugs/on-property-ignored/on-property-ignored.twinproj differ diff --git a/bugs/on-property-ignored/repro.json b/bugs/on-property-ignored/repro.json new file mode 100644 index 000000000..312335325 --- /dev/null +++ b/bugs/on-property-ignored/repro.json @@ -0,0 +1,8 @@ +{ + "mode": "lane", + "lane": "addin:panes", + "tests": [ + "P4: a property whose name starts with \"on\" is dropped, and the add-in is told nothing" + ], + "steps": "1. Open on-property-ignored.twinproj in the twinBASIC IDE and build it (win32). Copy Build\\OnPropertyIgnored_win32.dll into %APPDATA%\\twinBASIC\\addins\\win32\\, restart the IDE and open any project.\n2. Click the toolbar button on property repro. A tool window opens with [property] and [inline], and the DEBUG CONSOLE prints onclick set, error 0.\n3. Click [property]. Expected: its text changes to clicked, or step 2 raised an error. Actual: nothing happens; the element has no onclick handler and no onclick attribute.\n4. Control: click [inline]. Its text changes to clicked.\nAsserted on BETA 995 and 997 by `addin-test.bat --only panes` (P4, the PanesProbe add-in in test/addin/probes/panes, of which this project is a cut-down copy)." +} diff --git a/bugs/filed/hover-byval-option-explicit/src/Settings b/bugs/on-property-ignored/src/Settings similarity index 72% rename from bugs/filed/hover-byval-option-explicit/src/Settings rename to bugs/on-property-ignored/src/Settings index d8efc19c5..b0b0addd6 100644 --- a/bugs/filed/hover-byval-option-explicit/src/Settings +++ b/bugs/on-property-ignored/src/Settings @@ -1,12 +1,12 @@ { "configuration.inherits": "Defaults", - "project.appTitle": "HoverByvalOptionExplicit", + "project.appTitle": "OnPropertyIgnored", "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", "project.buildType": "Standard DLL", - "project.description": "Reproduces: Hover says a `ByVal` parameter was auto-generated because `Option Explicit` is off", + "project.description": "Reproduces: Setting a property whose name starts with `on` through `HtmlElementProperties` does nothing, and raises no error", "project.exportPathIsV2": true, - "project.id": "{0A327BDD-05D5-4672-AE72-41AF6C684CD8}", - "project.name": "HoverByvalOptionExplicit", + "project.id": "{26B62393-7D96-4393-A9BC-FEAE2891D137}", + "project.name": "OnPropertyIgnored", "project.optionExplicit": true, "project.references": [ { @@ -15,6 +15,7 @@ "name": "OLE Automation", "path32": "C:\\Windows\\SysWOW64\\stdole2.tlb", "path64": "C:\\Windows\\System32\\stdole2.tlb", + "symbolId": "stdole", "versionMajor": 2, "versionMinor": 0 }, @@ -36,7 +37,10 @@ "project.settingsVersion": 1, "project.warnings": { "errors": [], - "ignored": [] + "hints": [], + "ignored": [], + "info": [], + "warnings": [] }, "runtime.useUnicodeStandardLibrary": true } diff --git a/bugs/on-property-ignored/src/Sources/Startup.twin b/bugs/on-property-ignored/src/Sources/Startup.twin new file mode 100644 index 000000000..e862793e0 --- /dev/null +++ b/bugs/on-property-ignored/src/Sources/Startup.twin @@ -0,0 +1,47 @@ +' An add-in. Once a project is open, its toolbar button "on property repro" +' opens a tool window with two divs. Clicking either one is meant to change its +' text to "clicked": "property" has its onclick set through Properties, and +' "inline" has the same handler written in its HTML. +Module Startup + [DllExport] + Public Function tbCreateCompilerAddin(ByVal Host As Host) As AddIn + Return New ReproAddin(Host) + End Function +End Module + +Private Class ReproAddin + Implements AddIn + Private WithEvents Host As Host + Private WithEvents ShowButton As Button + Private Pane As ToolWindow + + Public Sub New(ByVal Host As Host) + Set Me.Host = Host + End Sub + + Private Property Get AddIn_Name() As String + Return "on property repro" + End Property + + Private Sub Host_OnProjectLoaded() + Set ShowButton = Host.Toolbars(0).AddButton("OnPropertyReproShow", "on property repro") + End Sub + + Private Sub ShowButton_OnClick() + Set Pane = Host.ToolWindows.Add("on property repro", "OnPropertyReproPane") + Pane.Title = "ON PROPERTY REPRO" + With Pane.RootDomElement.ChildDomElements + With .Add("property", "div") + .Properties.innerText = "[property]" + On Error Resume Next + .Properties.onclick = "this.innerText = 'clicked'" + Host.DebugConsole.PrintText "onclick set, error " & Err.Number + On Error GoTo 0 + End With + With .Add("inline", "div") + .Properties.innerHTML = "[inline]" + End With + End With + Pane.Visible = True + End Sub +End Class diff --git a/bugs/raiseevent-plain-html-typeerror/raiseevent-plain-html-typeerror.twinproj b/bugs/raiseevent-plain-html-typeerror/raiseevent-plain-html-typeerror.twinproj new file mode 100644 index 000000000..c0af99d98 Binary files /dev/null and b/bugs/raiseevent-plain-html-typeerror/raiseevent-plain-html-typeerror.twinproj differ diff --git a/bugs/raiseevent-plain-html-typeerror/repro.json b/bugs/raiseevent-plain-html-typeerror/repro.json new file mode 100644 index 000000000..e5b9efb2e --- /dev/null +++ b/bugs/raiseevent-plain-html-typeerror/repro.json @@ -0,0 +1,8 @@ +{ + "mode": "lane", + "lane": "addin:panes", + "tests": [ + "P12: raiseEvent from plain tool-window HTML throws, and the add-in hears nothing" + ], + "steps": "1. Open raiseevent-plain-html-typeerror.twinproj in the twinBASIC IDE and build it (win32). Copy Build\\RaiseeventPlainHtmlTypeerror_win32.dll into %APPDATA%\\twinBASIC\\addins\\win32\\, restart the IDE and open any project.\n2. Click the toolbar button raiseEvent repro. A tool window opens with [raise] and [direct].\n3. Click [raise]. Expected: the DEBUG CONSOLE prints myEvent from raise. Actual: nothing is printed, and the IDE's page has an uncaught TypeError: Cannot read properties of null (reading 'rootEventHandler').\n4. Control: click [direct]. The DEBUG CONSOLE prints myEvent from direct.\nAsserted on BETA 995 and 997 by `addin-test.bat --only panes` (P12, the PanesProbe add-in in test/addin/probes/panes, of which this project is a cut-down copy)." +} diff --git a/bugs/filed/populatefrom-no-args/src/Settings b/bugs/raiseevent-plain-html-typeerror/src/Settings similarity index 50% rename from bugs/filed/populatefrom-no-args/src/Settings rename to bugs/raiseevent-plain-html-typeerror/src/Settings index 610bb3183..436d96f66 100644 --- a/bugs/filed/populatefrom-no-args/src/Settings +++ b/bugs/raiseevent-plain-html-typeerror/src/Settings @@ -1,12 +1,12 @@ { "configuration.inherits": "Defaults", - "project.appTitle": "PopulatefromNoArgs", + "project.appTitle": "RaiseeventPlainHtmlTypeerror", "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", - "project.buildType": "Standard EXE", - "project.description": "Reproduces: `[PopulateFrom]` with no arguments crashes the compiler", + "project.buildType": "Standard DLL", + "project.description": "Reproduces: `raiseEvent` in a tool window's own HTML throws a TypeError, and the add-in's listener is not called", "project.exportPathIsV2": true, - "project.id": "{283FE68E-EE87-4A38-97FD-E228E8D2CB5F}", - "project.name": "PopulatefromNoArgs", + "project.id": "{2AE1CC14-228C-47AB-82CE-5BAA17DC4510}", + "project.name": "RaiseeventPlainHtmlTypeerror", "project.optionExplicit": true, "project.references": [ { @@ -20,36 +20,21 @@ "versionMinor": 0 }, { - "id": "{F50B82D0-DCAB-43FE-9631-11959D4A4728}", + "id": "{99DEC38C-75F6-4488-8EE7-2D52D83881D2}", "isCompilerPackage": true, "licence": "MIT", - "name": "[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)", + "name": "[COMPILER PACKAGE] twinBASIC IDE Extensibility Package", "path32": "", "path64": "", "publisher": "TWINBASIC-COMPILER", - "symbolId": "VB", + "symbolId": "tbIDE", "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 + "versionRevision": 1 } ], "project.settingsVersion": 1, - "project.startupObject": "Sub Main", "project.warnings": { "errors": [], "hints": [], diff --git a/bugs/raiseevent-plain-html-typeerror/src/Sources/Startup.twin b/bugs/raiseevent-plain-html-typeerror/src/Sources/Startup.twin new file mode 100644 index 000000000..51917c936 --- /dev/null +++ b/bugs/raiseevent-plain-html-typeerror/src/Sources/Startup.twin @@ -0,0 +1,46 @@ +' An add-in. Once a project is open, its toolbar button "raiseEvent repro" +' opens a tool window with two spans. Both are meant to call the listener +' that AddEventListener registers as "myEvent" on their parent div: +' "raise" through raiseEvent, as the list view items of Sample 15 do, and +' "direct" by calling the function AddEventListener stored on the div. +Module Startup + [DllExport] + Public Function tbCreateCompilerAddin(ByVal Host As Host) As AddIn + Return New ReproAddin(Host) + End Function +End Module + +Private Class ReproAddin + Implements AddIn + Private WithEvents Host As Host + Private WithEvents ShowButton As Button + Private Pane As ToolWindow + + Public Sub New(ByVal Host As Host) + Set Me.Host = Host + End Sub + + Private Property Get AddIn_Name() As String + Return "raiseEvent repro" + End Property + + Private Sub Host_OnProjectLoaded() + Set ShowButton = Host.Toolbars(0).AddButton("RaiseEventReproShow", "raiseEvent repro") + End Sub + + Private Sub ShowButton_OnClick() + Set Pane = Host.ToolWindows.Add("raiseEvent repro", "RaiseEventReproPane") + Pane.Title = "RAISEEVENT REPRO" + With Pane.RootDomElement.ChildDomElements.Add("box", "div") + .AddEventListener("myEvent", AddressOf MyEvent) + .Properties.innerHTML = _ + "[raise] " & _ + "[direct]" + End With + Pane.Visible = True + End Sub + + Private Sub MyEvent(ByVal eventInfo As HtmlEventProperties) + Host.DebugConsole.PrintText "myEvent from " & eventInfo.target.id + End Sub +End Class diff --git a/bugs/recent-projects-copies/repro.json b/bugs/recent-projects-copies/repro.json index 8859c0424..15ebbced9 100644 --- a/bugs/recent-projects-copies/repro.json +++ b/bugs/recent-projects-copies/repro.json @@ -1,4 +1,8 @@ { - "mode": "manual", - "steps": "Not automatable: it changes HKCU\\Software\\VB and VBA Program Settings\\twinBASIC_IDE\\RecentlyOpened, the person's own list. Export that key first with reg export, and import it back afterwards. 1. Copy recent-projects-copies.twinproj to A.twinproj, B.twinproj and C.twinproj. 2. In RecentlyOpened leave \"0\" = A.twinproj and \"1\" = B.twinproj, and delete \"2\" to \"20\" or set them to empty strings. 3. Open C.twinproj (twinBASIC.exe C.twinproj is enough). 4. Read the values: \"0\" is C, \"1\" is A and \"2\" to \"20\" are all B.twinproj, nineteen copies." + "mode": "lane", + "lane": "ide:recent-projects-copies", + "tests": [ + "opening a project fills the slots that held nothing with copies of the last entry" + ], + "steps": "It changes HKCU\\Software\\VB and VBA Program Settings\\twinBASIC_IDE\\RecentlyOpened, the person's own list. Export that key first with reg export, and import it back afterwards. 1. Copy recent-projects-copies.twinproj to A.twinproj, B.twinproj and C.twinproj. 2. In RecentlyOpened leave \"0\" = A.twinproj and \"1\" = B.twinproj, and delete \"2\" to \"20\" or set them to empty strings. 3. Open C.twinproj (twinBASIC.exe C.twinproj is enough). 4. Read the values: \"0\" is C, \"1\" is A and \"2\" to \"20\" are all B.twinproj, nineteen copies." } diff --git a/bugs/replace-embedded-package-one-apply/repro.json b/bugs/replace-embedded-package-one-apply/repro.json index 41bc11334..320e8e97d 100644 --- a/bugs/replace-embedded-package-one-apply/repro.json +++ b/bugs/replace-embedded-package-one-apply/repro.json @@ -1,5 +1,12 @@ { - "mode": "manual", - "attach": ["DocProbePkg-v1.twinpack", "DocProbePkg-v2.twinpack"], + "mode": "lane", + "lane": "ide:packages", + "tests": [ + "one Apply after the untick, the import and the tick: no restart, no save, and v1 still runs" + ], + "attach": [ + "DocProbePkg-v1.twinpack", + "DocProbePkg-v2.twinpack" + ], "steps": "Use a machine with no copy of DocProbePkg in %APPDATA%\\twinBASIC\\packages (a linked copy makes the bug go away).\n1. Unzip replace-embedded-package-one-apply.zip (attached; pack writes it), which holds replace-embedded-package-one-apply.twinproj, DocProbePkg-v1.twinpack and DocProbePkg-v2.twinpack, whose function DocProbeVersion returns 1 and 2. Open the project.\n2. Embed v1: Project Settings > References > Available Packages > Import from file... DocProbePkg-v1.twinpack, tick it, Apply. Replace the body of Main with Debug.Print \"DocProbeVersion=\" & DocProbeVersion(), and press F5: the DEBUG CONSOLE shows DocProbeVersion=1.\n3. Replace it with v2 under ONE Apply: in References untick DocProbePkg, then Available Packages > Import from file... DocProbePkg-v2.twinpack, tick it, and press Apply once.\n4. See, in the DEBUG CONSOLE, only [COMPILER] Project settings updated: no compiler restart and no save. Press F5: it prints DocProbeVersion=1. Expected: 2.\nControl: Apply after the untick, and again after the import and tick: each Apply restarts the compiler and saves, and F5 prints DocProbeVersion=2.\nRe-measured on BETA 995 by `ide-test.bat --only packages` (the second and third tests, with the control; probes in test/ide/probes/packages)." } diff --git a/bugs/single-overflow-no-error/repro.json b/bugs/single-overflow-no-error/repro.json new file mode 100644 index 000000000..ae91f9abe --- /dev/null +++ b/bugs/single-overflow-no-error/repro.json @@ -0,0 +1,14 @@ +{ + "mode": "run", + "expect": { + "exit": 0, + "output": [ + "^Single \\+\\s+Err=0 s=1\\.#INF$", + "^Single -\\s+Err=0 s=-1\\.#INF$", + "^Single \\*\\s+Err=0 s=1\\.#INF$", + "^Single /\\s+Err=0 s=1\\.#INF$", + "^s \\+= x\\s+Err=0 s=1\\.#INF$", + "^CSng\\(Double\\)\\s+Err=6 s=5$" + ] + } +} diff --git a/bugs/single-overflow-no-error/single-overflow-no-error.twinproj b/bugs/single-overflow-no-error/single-overflow-no-error.twinproj new file mode 100644 index 000000000..080d6c8ff Binary files /dev/null and b/bugs/single-overflow-no-error/single-overflow-no-error.twinproj differ diff --git a/bugs/filed/enum-connections-one-item/src/Settings b/bugs/single-overflow-no-error/src/Settings similarity index 86% rename from bugs/filed/enum-connections-one-item/src/Settings rename to bugs/single-overflow-no-error/src/Settings index e67c99a84..cd2dc96f7 100644 --- a/bugs/filed/enum-connections-one-item/src/Settings +++ b/bugs/single-overflow-no-error/src/Settings @@ -1,12 +1,12 @@ { "configuration.inherits": "Defaults", - "project.appTitle": "EnumConnectionsOneItem", + "project.appTitle": "SingleOverflowNoError", "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", "project.buildType": "Standard EXE", - "project.description": "Reproduces: t", + "project.description": "Reproduces: Single overflow raises no error, where VB6 raises error 6", "project.exportPathIsV2": true, - "project.id": "{56E42A1F-E0AB-43A8-AEC4-446D3A1833A9}", - "project.name": "EnumConnectionsOneItem", + "project.id": "{1CB427D1-0FF2-49BF-8020-6506C006BB8F}", + "project.name": "SingleOverflowNoError", "project.optionExplicit": true, "project.references": [ { diff --git a/bugs/single-overflow-no-error/src/Sources/Startup.twin b/bugs/single-overflow-no-error/src/Sources/Startup.twin new file mode 100644 index 000000000..0b47e6cb8 --- /dev/null +++ b/bugs/single-overflow-no-error/src/Sources/Startup.twin @@ -0,0 +1,30 @@ +' A Single that overflows raises no error: each way of overflowing one. +Module Startup + + Public Sub Main() + Dim sbig As Single = 3E+38! + Dim nbig As Single = -3E+38! + Dim tiny As Single = 1E-10! + Dim dbig As Double = 1E+300 + Dim s As Single + Dim n As Long + Dim v As Variant + Dim vr As Variant + Dim lten As Long = 10 + On Error Resume Next + s = 5!: Err.Clear: s = sbig + sbig: n = Err.Number: Debug.Print "Single + Err=" & n & " s=" & s + s = 5!: Err.Clear: s = nbig - sbig: n = Err.Number: Debug.Print "Single - Err=" & n & " s=" & s + s = 5!: Err.Clear: s = sbig * 10!: n = Err.Number: Debug.Print "Single * Err=" & n & " s=" & s + s = 5!: Err.Clear: s = sbig / tiny: n = Err.Number: Debug.Print "Single / Err=" & n & " s=" & s + s = 5!: Err.Clear: s = sbig * lten: n = Err.Number: Debug.Print "Single * Long Err=" & n & " s=" & s + s = sbig: Err.Clear: s = s + sbig: n = Err.Number: Debug.Print "s = s + x Err=" & n & " s=" & s + s = sbig: Err.Clear: s += sbig: n = Err.Number: Debug.Print "s += x Err=" & n & " s=" & s + s = sbig: Err.Clear: s = s * 10!: n = Err.Number: Debug.Print "s = s * x Err=" & n & " s=" & s + s = sbig: Err.Clear: s *= 10!: n = Err.Number: Debug.Print "s *= x Err=" & n & " s=" & s + s = 5!: Err.Clear: s = CSng(dbig): n = Err.Number: Debug.Print "CSng(Double) Err=" & n & " s=" & s + s = 5!: Err.Clear: s = sbig ^ 2!: n = Err.Number: Debug.Print "Single ^ Err=" & n & " s=" & s + v = sbig: vr = 5!: Err.Clear: vr = v * 10!: n = Err.Number: Debug.Print "Variant * Err=" & n & " " & TypeName(vr) + v = sbig: vr = 5!: Err.Clear: vr = v + v: n = Err.Number: Debug.Print "Variant + Err=" & n & " " & TypeName(vr) + End Sub + +End Module diff --git a/bugs/single-overflow-no-error/vb6/Module1.bas b/bugs/single-overflow-no-error/vb6/Module1.bas new file mode 100644 index 000000000..b7cdf104b --- /dev/null +++ b/bugs/single-overflow-no-error/vb6/Module1.bas @@ -0,0 +1,39 @@ +Attribute VB_Name = "Module1" +Option Explicit + +Sub Main() + Dim sbig As Single + Dim nbig As Single + Dim tiny As Single + Dim dbig As Double + Dim s As Single + Dim n As Long + Dim v As Variant + Dim vr As Variant + Dim lten As Long + On Error GoTo Fail + Open App.Path & "\out.txt" For Output As #9 + sbig = 3E+38 + nbig = -3E+38 + tiny = 1E-10 + dbig = 1E+300 + lten = 10 + On Error Resume Next + s = 5!: Err.Clear: s = sbig + sbig: n = Err.Number: Print #9, "Single + Err=" & n & " s=" & s + s = 5!: Err.Clear: s = nbig - sbig: n = Err.Number: Print #9, "Single - Err=" & n & " s=" & s + s = 5!: Err.Clear: s = sbig * 10!: n = Err.Number: Print #9, "Single * Err=" & n & " s=" & s + s = 5!: Err.Clear: s = sbig / tiny: n = Err.Number: Print #9, "Single / Err=" & n & " s=" & s + s = 5!: Err.Clear: s = sbig * lten: n = Err.Number: Print #9, "Single * Long Err=" & n & " s=" & s + s = sbig: Err.Clear: s = s + sbig: n = Err.Number: Print #9, "s = s + x Err=" & n & " s=" & s + s = sbig: Err.Clear: s = s * 10!: n = Err.Number: Print #9, "s = s * x Err=" & n & " s=" & s + s = 5!: Err.Clear: s = CSng(dbig): n = Err.Number: Print #9, "CSng(Double) Err=" & n & " s=" & s + s = 5!: Err.Clear: s = sbig ^ 2!: n = Err.Number: Print #9, "Single ^ Err=" & n & " s=" & s + v = sbig: vr = 5!: Err.Clear: vr = v * 10!: n = Err.Number: Print #9, "Variant * Err=" & n & " " & TypeName(vr) + v = sbig: vr = 5!: Err.Clear: vr = v + v: n = Err.Number: Print #9, "Variant + Err=" & n & " " & TypeName(vr) + Close #9 + Exit Sub +Fail: + On Error Resume Next + Print #9, "unhandled error " & Err.Number & ": " & Err.Description + Close #9 +End Sub diff --git a/bugs/single-overflow-no-error/vb6/Probe.vbp b/bugs/single-overflow-no-error/vb6/Probe.vbp new file mode 100644 index 000000000..4d210b0e8 --- /dev/null +++ b/bugs/single-overflow-no-error/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Module1; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="Probe" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/step-key-leaves-step-pending/repro.json b/bugs/step-key-leaves-step-pending/repro.json index dcd9c128a..76964abee 100644 --- a/bugs/step-key-leaves-step-pending/repro.json +++ b/bugs/step-key-leaves-step-pending/repro.json @@ -1,4 +1,8 @@ { - "mode": "manual", + "mode": "lane", + "lane": "ide:debugger", + "tests": [ + "after F8 there, Ignore stops on the next line instead of running on" + ], "steps": "1. Open step-key-leaves-step-pending.twinproj in the twinBASIC IDE and press F5. FillTable raises an error at the line marked FAILS (subscript out of range) and the error panel opens. The DEBUG CONSOLE shows main start and loop 0.\n2. Press F8 on that line. The line runs again, the error recurs and the mark does not move: as expected.\n3. In the error panel choose Ignore (Resume Next).\n4. Expected: the program runs on from the next line. Actual: it stops at the next line, Debug.Print \"after fail \" & i, and has not printed after fail 0.\n5. Press F5. It does not run on from there: it runs to the loop's next error, so the DEBUG CONSOLE now ends with after fail 0 and loop 1, and the error panel is open again. The step F8 asked for was used up by that one stop.\nControl: restart, and choose Ignore (Resume Next) without pressing F8 first: it runs on from the next line, to the next pass's error.\nRe-measured on BETA 995 by `ide-test.bat --only debugger` (the project is test/ide/probes/debugger). The same on BETA 983." } diff --git a/bugs/sub-named-like-module/repro.json b/bugs/sub-named-like-module/repro.json new file mode 100644 index 000000000..4f1682ddb --- /dev/null +++ b/bugs/sub-named-like-module/repro.json @@ -0,0 +1,13 @@ +{ + "mode": "run", + "exe": true, + "expect": { + "exit": 5, + "output": [ + "^\\[BUILD\\] Executing 'SubNamedLikeModule\\.Probe\\.\\w+'\\.\\.\\.$" + ], + "absent": [ + "^RunAfterBuild ran$" + ] + } +} diff --git a/bugs/filed/circle-positive-angle-unchecked/src/Settings b/bugs/sub-named-like-module/src/Settings similarity index 83% rename from bugs/filed/circle-positive-angle-unchecked/src/Settings rename to bugs/sub-named-like-module/src/Settings index 98af685ba..fb3a4cbc7 100644 --- a/bugs/filed/circle-positive-angle-unchecked/src/Settings +++ b/bugs/sub-named-like-module/src/Settings @@ -1,12 +1,12 @@ { "configuration.inherits": "Defaults", - "project.appTitle": "CirclePositiveAngleUnchecked", + "project.appTitle": "SubNamedLikeModule", "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", "project.buildType": "Standard EXE", - "project.description": "Reproduces: Circle accepts a Start or End above 2 pi, and draws a short arc where VB6 raises error 5", + "project.description": "Reproduces: A [RunAfterBuild] Sub does not run, and nothing says so, when its module holds a procedure named like the module", "project.exportPathIsV2": true, - "project.id": "{6E04B665-A7E0-4612-AA05-759573420612}", - "project.name": "CirclePositiveAngleUnchecked", + "project.id": "{FA93C97C-3E20-473A-A1C5-06C4F673AB54}", + "project.name": "SubNamedLikeModule", "project.optionExplicit": true, "project.references": [ { diff --git a/bugs/sub-named-like-module/src/Sources/Probe.twin b/bugs/sub-named-like-module/src/Sources/Probe.twin new file mode 100644 index 000000000..0b8b86b66 --- /dev/null +++ b/bugs/sub-named-like-module/src/Sources/Probe.twin @@ -0,0 +1,13 @@ +Module Probe + + [RunAfterBuild] + Public Sub Hello() + Debug.Cls + Debug.Print "RunAfterBuild ran" + End Sub + + ' Never called. Its name is the module's name. + Public Sub Probe() + End Sub + +End Module diff --git a/bugs/filed/asnew-optional-ctor/src/Sources/Startup.twin b/bugs/sub-named-like-module/src/Sources/Startup.twin similarity index 100% rename from bugs/filed/asnew-optional-ctor/src/Sources/Startup.twin rename to bugs/sub-named-like-module/src/Sources/Startup.twin diff --git a/bugs/filed/populatefrom-no-args/populatefrom-no-args.twinproj b/bugs/sub-named-like-module/sub-named-like-module.twinproj similarity index 73% rename from bugs/filed/populatefrom-no-args/populatefrom-no-args.twinproj rename to bugs/sub-named-like-module/sub-named-like-module.twinproj index 9d301c332..a44afff91 100644 Binary files a/bugs/filed/populatefrom-no-args/populatefrom-no-args.twinproj and b/bugs/sub-named-like-module/sub-named-like-module.twinproj differ diff --git a/bugs/sub-named-like-module/vb6/Module1.bas b/bugs/sub-named-like-module/vb6/Module1.bas new file mode 100644 index 000000000..c51ae261d --- /dev/null +++ b/bugs/sub-named-like-module/vb6/Module1.bas @@ -0,0 +1,24 @@ +Attribute VB_Name = "Probe" +Option Explicit + +' The VB6 side of the reproducer: a standard module named Probe that holds a +' Sub named Probe. VB6 has no [RunAfterBuild], so what is checked is whether it +' accepts the name, and whether the Sub can be called. +' Build and run it with: node scripts/bug_repro.mjs vb6 sub-named-like-module + +Public Sub Probe() + Print #9, "Sub Probe ran, in a module named Probe" +End Sub + +Sub Main() + On Error GoTo Fail + Open App.Path & "\out.txt" For Output As #9 + Probe + Print #9, "Sub Main ran" + Close #9 + Exit Sub +Fail: + On Error Resume Next + Print #9, "unhandled error " & Err.Number & ": " & Err.Description + Close #9 +End Sub diff --git a/bugs/sub-named-like-module/vb6/Probe.vbp b/bugs/sub-named-like-module/vb6/Probe.vbp new file mode 100644 index 000000000..33a0347fc --- /dev/null +++ b/bugs/sub-named-like-module/vb6/Probe.vbp @@ -0,0 +1,7 @@ +Type=Exe +Module=Probe; Module1.bas +Startup="Sub Main" +ExeName32="Probe.exe" +Name="SubNamedLikeModule" +CompilationType=0 +OptimizationType=0 diff --git a/bugs/two-calling-conventions-accepted/repro.json b/bugs/two-calling-conventions-accepted/repro.json new file mode 100644 index 000000000..4f4c37de7 --- /dev/null +++ b/bugs/two-calling-conventions-accepted/repro.json @@ -0,0 +1,12 @@ +{ + "mode": "run", + "expect": { + "exit": 0, + "output": [ + "^FastCall CDecl: +44332211$", + "^CDecl FastCall: +44332211$", + "^ThisCall CDecl: +44332211$", + "^CDecl ThisCall: +44332211$" + ] + } +} diff --git a/bugs/filed/asnew-optional-ctor/src/Settings b/bugs/two-calling-conventions-accepted/src/Settings similarity index 84% rename from bugs/filed/asnew-optional-ctor/src/Settings rename to bugs/two-calling-conventions-accepted/src/Settings index d1f654b73..5c385d9ca 100644 --- a/bugs/filed/asnew-optional-ctor/src/Settings +++ b/bugs/two-calling-conventions-accepted/src/Settings @@ -1,12 +1,12 @@ { "configuration.inherits": "Defaults", - "project.appTitle": "AsnewOptionalCtor", + "project.appTitle": "TwoCallingConventionsAccepted", "project.buildPath": "${SourcePath}\\Build\\${ProjectName}_${Architecture}.${FileExtension}", "project.buildType": "Standard EXE", - "project.description": "Reproduces: `As New` refuses a class whose only constructor has all-`Optional` arguments", + "project.description": "Reproduces: A declaration that names two calling conventions compiles without a diagnostic", "project.exportPathIsV2": true, - "project.id": "{3E2698F7-6F1E-4F69-B66D-7135C879453E}", - "project.name": "AsnewOptionalCtor", + "project.id": "{01FC4406-FC9C-4EBB-A5F5-0E15C3B6F1D2}", + "project.name": "TwoCallingConventionsAccepted", "project.optionExplicit": true, "project.references": [ { diff --git a/bugs/two-calling-conventions-accepted/src/Sources/Startup.twin b/bugs/two-calling-conventions-accepted/src/Sources/Startup.twin new file mode 100644 index 000000000..654341065 --- /dev/null +++ b/bugs/two-calling-conventions-accepted/src/Sources/Startup.twin @@ -0,0 +1,35 @@ +' Every declaration below names two calling conventions, and the project +' compiles with no diagnostic. On a 32-bit build, ntdll exports +' RtlUlongByteSwap as fastcall: it takes its argument in ECX. + +[InterfaceId("6B0C2F61-3D1E-4C8A-9F0B-2A7E5D4C3B21")] +[OleAutomation(False)] +Interface ITwo Extends IUnknown + Function Add ThisCall CDecl(ByVal Amount As Long) As Long +End Interface + +Module Startup + + Private Declare PtrSafe Function SwapFast FastCall Lib "ntdll" Alias "RtlUlongByteSwap" (ByVal Source As Long) As Long + Private Declare PtrSafe Function SwapCdecl CDecl Lib "ntdll" Alias "RtlUlongByteSwap" (ByVal Source As Long) As Long + Private Declare PtrSafe Function SwapFastCdecl FastCall CDecl Lib "ntdll" Alias "RtlUlongByteSwap" (ByVal Source As Long) As Long + Private Declare PtrSafe Function SwapCdeclFast CDecl FastCall Lib "ntdll" Alias "RtlUlongByteSwap" (ByVal Source As Long) As Long + Private Declare PtrSafe Function SwapThisCdecl ThisCall CDecl Lib "ntdll" Alias "RtlUlongByteSwap" (ByVal Source As Long) As Long + Private Declare PtrSafe Function SwapCdeclThis CDecl ThisCall Lib "ntdll" Alias "RtlUlongByteSwap" (ByVal Source As Long) As Long + + Private Delegate Function TwoDelegate CDecl FastCall (ByVal x As Long) As Long + + Public Function TwoProcedure CDecl ThisCall(ByVal x As Long) As Long + Return x + End Function + + Public Sub Main() + Debug.Print "FastCall: " & Hex$(SwapFast(&H11223344)) + Debug.Print "FastCall CDecl: " & Hex$(SwapFastCdecl(&H11223344)) + Debug.Print "CDecl FastCall: " & Hex$(SwapCdeclFast(&H11223344)) + Debug.Print "ThisCall CDecl: " & Hex$(SwapThisCdecl(&H11223344)) + Debug.Print "CDecl ThisCall: " & Hex$(SwapCdeclThis(&H11223344)) + Debug.Print "CDecl: " & Hex$(SwapCdecl(&H11223344)) + End Sub + +End Module diff --git a/bugs/two-calling-conventions-accepted/two-calling-conventions-accepted.twinproj b/bugs/two-calling-conventions-accepted/two-calling-conventions-accepted.twinproj new file mode 100644 index 000000000..78b72ee6c Binary files /dev/null and b/bugs/two-calling-conventions-accepted/two-calling-conventions-accepted.twinproj differ diff --git a/bugs/udt-watch-codegen-error/repro.json b/bugs/udt-watch-codegen-error/repro.json index 5effeb9a4..8e38054d3 100644 --- a/bugs/udt-watch-codegen-error/repro.json +++ b/bugs/udt-watch-codegen-error/repro.json @@ -1,4 +1,10 @@ { - "mode": "manual", + "mode": "lane", + "lane": "ide:watches", + "tests": [ + "a watch on p fails with a codegen error, and the console reports a linker error", + "the next stop reports the linker error again", + "? p in the DEBUG CONSOLE fails the same way" + ], "steps": "1. Open udt-watch-codegen-error.twinproj in the twinBASIC IDE, put a breakpoint on the line marked BREAK (F9) and press F5. The run stops there; VARIABLES shows p as {user defined type, 8 bytes}, with X and Y.\n2. Add a watch on p (Debug > Add Watch..., or the plus sign in WATCHES).\n3. Expected: the watch shows p as VARIABLES does. Actual: its value is (compile error: codegen error; check for compilation errors) and its type ERROR, and the DEBUG CONSOLE prints [LINKER] compilation (codegen) error detected in 'Startup.{temp_procedure}' at line #1.\n4. Press F5. The run stops at the breakpoint again, on the loop's second pass, and the DEBUG CONSOLE prints the linker error again: it does so at every stop while the watch is there.\n5. Type ? p in the DEBUG CONSOLE and press Enter: the same error, and the same linker line.\nControl: a watch on p.X shows 7, of type Variant [Long].\nAsserted on BETA 995 by `ide-test.bat --only watches` (the project is test/ide/probes/watches)." } diff --git a/builder/check-tree.mjs b/builder/check-tree.mjs index 39d1e3edd..3bbf757ba 100644 --- a/builder/check-tree.mjs +++ b/builder/check-tree.mjs @@ -18,8 +18,9 @@ // site, and the offline tree has no reader for it yet. const ONLINE_AUX = ["sitemap.xml", "robots.txt", "assets/js/search-data.json", "tB/symbols.json"]; // The offline tree carries neither sitemap nor search index, but does -// get the JS wrapper writeOffline generates around the search data. -const OFFLINE_AUX = ["assets/js/search-data.js"]; +// get the JS wrapper writeOffline generates around the search data, and +// the nav.js that puts the whole nav tree in place of a page's part of it. +const OFFLINE_AUX = ["assets/js/search-data.js", "assets/js/nav.js"]; // Every relative path a tree receives. `which` is "online" or "offline"; // the PDF tree's contents come from writePdf, which knows them exactly. diff --git a/builder/cpu-worker.mjs b/builder/cpu-worker.mjs index 14eb842b4..00a1f1a73 100644 --- a/builder/cpu-worker.mjs +++ b/builder/cpu-worker.mjs @@ -11,11 +11,11 @@ import { regenerateDot } from "./dot.mjs"; import { captureBuildInfo } from "./build-info.mjs"; import { createMarkdownIt, renderPhase } from "./render.mjs"; -import { templatePhase } from "./template.mjs"; +import { renderFullNav, templatePhase, withPartialNav } from "./template.mjs"; import { unpackShared } from "./sab-broadcast.mjs"; import { deriveSearchEntries } from "./search.mjs"; import { computeChunkSeo } from "./seo.mjs"; -import { deriveOfflinePage, deriveOfflinePageCached, sliceNavBlock, posixDirname } from "./offline-rewrite.mjs"; +import { deriveOfflinePage } from "./offline-rewrite.mjs"; import { normalizeBaseurl } from "./url.mjs"; import { @@ -206,48 +206,12 @@ const handlers = { caches: { rawResolution: new Map(), seg: new Map(), result: new Map() }, }; - // PLAN-9 §5.3 (B7) nav-block cache: the just-the-docs sidebar in - // `` is byte-identical across every page - // site-wide before rewrite (template.mjs's renderSidebar takes only - // `site`, not `page`; the per-page active highlight lives in a - // separate `