Skip to content

Access and Assign methods, _SCREEN and _VFP, released forms, FOR EACH over Forms - #7

Merged
RandomChirp merged 2 commits into
FoxDevCommunity:mainfrom
jefflroberts:screen-and-objects
Oct 5, 2026
Merged

RandomChirp merged 2 commits into
FoxDevCommunity:mainfrom
jefflroberts:screen-and-objects

Conversation

@jefflroberts

Copy link
Copy Markdown
Collaborator

Found running a CodeMine application (Shutter Ace); each behaviour measured in
Visual FoxPro 9 first, and in tests/runtime/formsAndAccessors.test.ts.

  • Prop_Access and Prop_Assign stand in for reading and writing a property;
    inside either one, THIS.Prop is the property itself. The parser no longer
    warns that they are not honoured.
  • _SCREEN holds what a program writes to it and what AddProperty gives it,
    and _SCREEN.AddObject makes its object as CREATEOBJECT would - a program's
    class included (CodeMine keeps its global object manager there).
    _SCREEN.HWnd answers 0.
  • FOR EACH walks _SCREEN.Forms (and a formset's Forms) through a new optional
    host read, enumerate(); _SCREEN.Forms(n) counts from the topmost form and is
    1924 past either end; FormCount leaves Custom objects out.
  • A released form reads as .NULL. from every variable, array element and
    property still holding it; Release() answers .T.
  • A form made from a class has no DataEnvironment; only a form file's does.
  • An event called as a method with no code behind it answers .T.
  • Name set by a program is what Name reads back; a page answers PageOrder
    with its place in the pageframe.
  • _VFP takes property writes, and an unknown name is 1426.

Verification. New tests/runtime/formsAndAccessors.test.ts, each case mirroring a probe run in vfp9.exe, plus a _SCREEN case in tests/runtime/objectModel.test.ts. The full VM suite and the runtime, shared and vfp TypeScript tests pass on this branch.

One of several pull requests split out of work on a CodeMine application (Shutter Ace); each stands on main on its own except where it says otherwise.

🤖 Generated with Claude Code

@RandomChirp RandomChirp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every measured claim in the new tests holds in vfp9.exe: _SCREEN.Forms is topmost first with a Custom left out, FOR EACH walks it, Forms(n) past the end and a read of Forms as a value are both 1924, a released form is .NULL. (VARTYPE X) to a variable and to a property holding it, Release() answers .T., PEMSTATUS(o, "DataEnvironment", 5) is .F. for CREATEOBJECT("form") and for a Form subclass, an event called as a method with no code answers .T., PageOrder is the page's place, _VFP.Height takes a write and _VFP.Foo is 1426, _SCREEN.cNothing is 1734, _SCREEN.AddObject makes the object and names it in capitals, and the Access/Assign program prints read 10, after write 60, inner 60, flag .F., flag2 .T., calls 3 5/1. cargo test -p foxvm is green on a merge with main.

The ubuntu check failed only on the stale instructions.json guard, which main no longer has; a rebase clears it.

One note, not a blocker: after this change every property read makes a host call (has_code_method, a Reflect::get plus a JS call) and every load of an object value another (released). FoxPro code reads THIS.x constantly, so it is worth timing a property-heavy loop before and after. If it shows, the answer is probably a per-object flag the host sets once (has any _ACCESS or _ASSIGN method at all) rather than a call per read.

Review generated with Claude Code (Claude Fable 5.1), run against vfp9.exe and the test suites on a merge with main.

@RandomChirp RandomChirp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Withdrawing the approval: on a merge with main this branch stops one of the forms Visual FoxPro ships with from opening. tests/vfp/samplesRun.test.ts (which needs the product installed, so CI does not run it) passes on main and fails with this branch:

Solution/Tahoe/aa_fun.scx: Error 31 in FRMTRACK.INIT line 4: Invalid subscript reference.

That form is the Access/Assign sample. Its Init reads THIS.myButtons[1] where myButtons is an array property with a myButtons_Access method that takes the subscript. The branch calls the Access method with no argument on every read of the property, and then subscripts what it returned.

Measured in vfp9.exe with an array property myButtons[3] and this Access method:

PROCEDURE myButtons_Access
  LPARAMETERS nIndex
  ? "access got", VARTYPE(nIndex), nIndex
  IF VARTYPE(nIndex) = "N"
    RETURN THIS.myButtons[nIndex]
  ENDIF
  RETURN "whole"
ENDPROC
  • o.myButtons[1] calls the Access method with N 1, and o.myButtons[2] with N 2; the method's own THIS.myButtons[nIndex] reads the element without calling itself again.
  • o.myButtons("x") calls it with C x.
  • VARTYPE(o.myButtons), the whole array named with no subscript, calls it with N 1.
  • o.myButtons[1] = 5 does not call it (an Assign method would be the one called); a read after it calls Access with 1 and gets 5.
  • ALEN(o.myButtons) does not call it and answers 3; DIMENSION o.myButtons[4] works on the array itself.
  • A read of THIS.myButtons[1] from another method of the object calls it, as a read from outside does.

So for an array property the subscript is the Access method's argument, and a write, ALEN and DIMENSION bypass it. With this branch every one of those reads calls Access with nothing and then fails with error 31 on the result, which is what the sample's Init hits.

The second failure the run reported, foxmedia.scx, was my environment (no COM addon in the worktree) and is not this branch's.

Review generated with Claude Code (Claude Fable 5.1), run against vfp9.exe and the test suites on a merge with main.

jefflroberts and others added 2 commits September 24, 2026 20:10
… over Forms

Found running a CodeMine application (Shutter Ace); each behaviour measured in
Visual FoxPro 9 first, and in tests/runtime/formsAndAccessors.test.ts.

- Prop_Access and Prop_Assign stand in for reading and writing a property;
  inside either one, THIS.Prop is the property itself. The parser no longer
  warns that they are not honoured.
- _SCREEN holds what a program writes to it and what AddProperty gives it,
  and _SCREEN.AddObject makes its object as CREATEOBJECT would - a program's
  class included (CodeMine keeps its global object manager there).
  _SCREEN.HWnd answers 0.
- FOR EACH walks _SCREEN.Forms (and a formset's Forms) through a new optional
  host read, enumerate(); _SCREEN.Forms(n) counts from the topmost form and is
  1924 past either end; FormCount leaves Custom objects out.
- A released form reads as .NULL. from every variable, array element and
  property still holding it; Release() answers .T.
- A form made from a class has no DataEnvironment; only a form file's does.
- An event called as a method with no code behind it answers .T.
- Name set by a program is what Name reads back; a page answers PageOrder
  with its place in the pageframe.
- _VFP takes property writes, and an unknown name is 1426.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review on FoxDevCommunity#7: Solution/Tahoe/aa_fun.scx stopped opening with error 31, because
THIS.myButtons[1] called myButtons_Access with nothing and then subscripted what it
returned. Measured in vfp9.exe with an array property myButtons[3]:

- obj.aProp[n] and obj.aProp("x") call the Access method with the subscript; the
  method's own THIS.aProp[n] reads the element without calling itself again
- the array named whole, VARTYPE(obj.aProp), calls it with 1
- a write to an element, ALEN(obj.aProp) and DIMENSION obj.aProp[4] do not call it

obj.aProp[n] now compiles to one instruction, GetMemberIndex, instead of a member
read and then a subscript; ALEN's array argument is read with GetProp, which passes
over the Access method; a method call on a property with an Access method goes to it.
The member read that GetMember and the a.b path of LoadField each spelt out is one
helper, read_member.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jefflroberts

Copy link
Copy Markdown
Collaborator Author

Thanks for catching this, and for measuring the array cases. I've rebased on main and pushed a fix. obj.aProp[n] is now one read that hands the subscript to the Access method, not a read followed by a subscript. obj.aProp("x") hands it "x", the whole array read with no subscript hands it 1, and a write to an element, ALEN() and DIMENSION pass the Access method by. The Access method's own THIS.aProp[n] reads the element without calling itself again. There's a new test that runs your cases, and samplesRun.test.ts opens aa_fun.scx again here. foxmedia.scx still fails for me for the same COM-addon reason as yours. cargo test -p foxvm and the vitest suite are green.

On the performance note: I haven't changed the per-read has_code_method call yet. I'd like to time it as you suggested, and I can do that in a follow-up if it shows.

@RandomChirp
RandomChirp merged commit 4219dd9 into FoxDevCommunity:main Oct 5, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants