Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -484,3 +484,5 @@ The 2025-2026 entries were audited retrospectively against commits through `1bcb
- 2026-11-01 `Tools/Testing` DEV - Add one manifest-driven command for aggregate, class and named-case tests, structured results, isolated Office workers, dialog/time limits and exact settings restoration. Move specialized test assets under root tests and register Python/native checks. Existing commands inside mixed build-tool folders remain compatibility shims. Split Callback and Regex into independently callable case groups; explicitly report unsupported hosts and legacy fixtures as skipped.
- 2026-11-01 `stdLambda` BREAKING - Remove the production test-only protRunVMTests member and its private test helpers. Developers calling stdLambda.protRunVMTests must instead run `python Tools/Testing/run.py --suite stdLambda.vm --test Instructions`; the runner injects tests from tests/support into a disposable class copy. Normal Lambda evaluation APIs, defaults and callback contracts are unchanged.
- 2026-11-01 `Tools/Testing` BREAKING - Remove the separate Tools/stdCallback-tests, stdDate-test, stdHTTP-tests, stdImage-tests stdWebSocket-tests and stdEnumerator-tests command folders. Maintainer scripts invoking those paths must migrate to `python Tools/Testing/run.py --suite <id>`: stdCallback.probes, stdDate, stdHTTP.local, stdImage stdWebSocket.local or stdEnumerator.local respectively. Image host selection is now `--host Excel` or `--host Word` through the shared command (formerly `--hosts`). Test bodies and specialized harnesses now live under tests/harnesses.

- 2026-11-01 `stdArray` WIP - Add experimental Windows CreateFromArrayByRef ownership transfer for non-empty dynamic one-dimensional Variant arrays. Consumes the source and uses DefaultBase; rejects fixed-size, locked, unallocated and multidimensional arrays. Existing copying APIs are unchanged; no breaking changes identified. Windows 32-bit/legacy VBA and Mac runtime support remain unverified (#122).
69 changes: 69 additions & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,35 @@
],
"requires": [],
"methods": [
{
"name": "protInitFromArrayByRef",
"description": "Take ownership of array storage without copying elements.",
"remarks": [],
"examples": [],
"params": [
{
"name": "arr",
"type": "Variant",
"description": "Non-empty dynamic one-dimensional Variant array; consumed on success.",
"optional": false,
"defaultValue": null,
"paramArray": false,
"referenceType": "ByRef"
}
],
"returns": null,
"deprecation": {
"status": false,
"message": ""
},
"isDefaultMember": false,
"devNotes": [],
"todos": [],
"isProtected": true,
"throws": [],
"requires": [],
"isStatic": false
},
{
"name": "protInit",
"description": "Initialise array",
Expand Down Expand Up @@ -2540,6 +2569,46 @@
"requires": [],
"isStatic": false
},
{
"name": "CreateFromArrayByRef",
"description": "Create a stdArray by taking ownership of a dynamic one-dimensional Variant array.",
"remarks": [
"Experimental Windows API. Transfers ownership, rather than sharing storage. Existing copying APIs are unchanged."
],
"examples": [
"```vb\r\nDim values() As Variant: ReDim values(0 To 2)\r\nvalues(0) = \"hello\"\r\nDim list As stdArray: Set list = stdArray.CreateFromArrayByRef(values)\r\n'values is now unallocated; list.Item(list.Base) is \"hello\".\r\n```"
],
"params": [
{
"name": "arr",
"type": "Variant",
"description": "Non-empty dynamic Variant array. Becomes unallocated on success.",
"optional": false,
"defaultValue": null,
"paramArray": false,
"referenceType": "ByRef"
}
],
"returns": {
"type": "stdArray",
"description": "A stdArray using the transferred storage and current DefaultBase."
},
"deprecation": {
"status": false,
"message": ""
},
"isDefaultMember": false,
"devNotes": [],
"todos": [],
"isProtected": false,
"throws": [
{
"errNumber": null
}
],
"requires": [],
"isStatic": false
},
{
"name": "CreateFromCollection",
"description": "Create a `stdArray` object from a collection",
Expand Down
85 changes: 85 additions & 0 deletions src/stdArray.cls
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,91 @@ Public Function CreateFromArray(ByVal arr As Variant) As stdArray
RaiseEvent AfterCreate(Create)
End Function

'Create a stdArray by taking ownership of a dynamic one-dimensional Variant array.
'@constructor
'@param arr - Non-empty dynamic Variant array. Becomes unallocated on success.
'@returns - A stdArray using the transferred storage and current DefaultBase.
'@remark Experimental Windows API. Transfers ownership, rather than sharing storage. Existing copying APIs are unchanged.
'@throws stdArray::CreateFromArrayByRef - Error 5 for fixed-size, locked, multidimensional, empty or unallocated arrays; source remains unchanged.
'@example ```vb
'Dim values() As Variant: ReDim values(0 To 2)
'values(0) = "hello"
'Dim list As stdArray: Set list = stdArray.CreateFromArrayByRef(values)
''values is now unallocated; list.Item(list.Base) is "hello".
'```
Public Function CreateFromArrayByRef(ByRef arr As Variant) As stdArray
Set CreateFromArrayByRef = New stdArray
Call CreateFromArrayByRef.protInitFromArrayByRef(arr)
RaiseEvent AfterCreate(CreateFromArrayByRef)
End Function

'Take ownership of array storage without copying elements.
'@protected
'@param arr - Non-empty dynamic one-dimensional Variant array; consumed on success.
Public Sub protInitFromArrayByRef(ByRef arr As Variant)
#If Mac Then
Err.Raise 5, "stdArray::CreateFromArrayByRef", "Windows required"
#Else
If This.Initialised Then Err.Raise 5, "stdArray::CreateFromArrayByRef", "Already initialised"
#If VBA7 Then
Dim sourceSlot As LongPtr, targetSlot As LongPtr, descriptor As LongPtr, nullPointer As LongPtr
#Else
Dim sourceSlot As Long, targetSlot As Long, descriptor As Long, nullPointer As Long
#End If
sourceSlot = ArrayPointerSlot(arr)
Call CopyMemory(VarPtr(descriptor), sourceSlot, LenB(descriptor))
If descriptor = 0 Then Err.Raise 5, "stdArray::CreateFromArrayByRef", "Allocated array required"
Dim dimensions As Integer, flags As Integer, locks As Long
Call CopyMemory(VarPtr(dimensions), descriptor, 2)
Call CopyMemory(VarPtr(flags), descriptor + 2, 2)
Call CopyMemory(VarPtr(locks), descriptor + 8, 4)
If dimensions <> 1 Or (flags And &H17) <> 0 Or locks <> 0 Then
Err.Raise 5, "stdArray::CreateFromArrayByRef", "Dynamic unlocked one-dimensional array required"
End If
Dim count As Long, firstIndex As Long, boundsOffset As Long
#If Win64 Then
boundsOffset = 24
#Else
boundsOffset = 16
#End If
Call CopyMemory(VarPtr(count), descriptor + boundsOffset, 4)
If count <= 0 Then Err.Raise 5, "stdArray::CreateFromArrayByRef", "Non-empty array required"
This.Base = stdArray.DefaultBase
This.Length = count
This.ProxyLength = count
This.Chunking = 10
targetSlot = ArrayPointerSlot(This.BaseArray)
firstIndex = 1
'Validation is complete; no callbacks run while ownership changes.
Call CopyMemory(descriptor + boundsOffset + 4, VarPtr(firstIndex), 4)
Call CopyMemory(sourceSlot, VarPtr(nullPointer), LenB(nullPointer))
Call CopyMemory(targetSlot, VarPtr(descriptor), LenB(descriptor))
This.Initialised = True
#End If
End Sub

'Locate the SAFEARRAY pointer slot of a typed array passed through a Variant.
#If VBA7 Then
Private Function ArrayPointerSlot(ByRef value As Variant) As LongPtr
#Else
Private Function ArrayPointerSlot(ByRef value As Variant) As Long
#End If
#If Mac Then
Err.Raise 5, "stdArray::ArrayPointerSlot", "Windows required"
#Else
Dim vt As Integer
Call CopyMemory(VarPtr(vt), VarPtr(value), 2)
If vt = (vbArray Or vbVariant) Then
ArrayPointerSlot = VarPtr(value) + 8
Exit Function
End If
If vt <> (vbArray Or vbVariant Or &H4000) Then
Err.Raise 5, "stdArray::ArrayPointerSlot", "Typed Variant array required"
End If
Call CopyMemory(VarPtr(ArrayPointerSlot), VarPtr(value) + 8, LenB(ArrayPointerSlot))
#End If
End Function

'Create a `stdArray` object from a collection
'@constructor
'@param col - Collection to create a `stdArray` object from.
Expand Down
70 changes: 70 additions & 0 deletions tests/stdArrayTests.bas
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Sub testAll()
Test.Assert "Property Arr 4 item 3 equal", vArr(3) = arr.item(3)
end if

Call testCreateFromArrayByRef
Call testDefaultBase
Call testArrLet

Expand Down Expand Up @@ -354,3 +355,72 @@ Failed:
stdArray.DefaultBase = originalBase
Err.Raise failure, "stdArrayTests.testDefaultBase", description
End Sub

Public Sub testCreateFromArrayByRef()
Test.Topic "stdArray ownership transfer"
Dim values() As Variant: ReDim values(-2 To 0)
values(-2) = "hello"
Dim obj As Collection: Set obj = New Collection
obj.Add "alive"
Set values(-1) = obj
values(0) = Array(3, 4)
Dim list As stdArray: Set list = stdArray.CreateFromArrayByRef(values)
Test.Assert "Moved length", list.Length = 3
Test.Assert "Moved string", list.Item(list.Base) = "hello"
Test.Assert "Moved object identity", list.Item(list.Base + 1) Is obj
Dim nested As Variant: nested = list.Item(list.Base + 2)
Test.Assert "Moved nested array", nested(1) = 4
Dim n As Long, failure As Long
On Error Resume Next
n = LBound(values)
failure = Err.Number
Err.Clear
On Error GoTo 0
Test.Assert "Source unallocated", failure = 9
ReDim values(0 To 1)
values(0) = "independent"
Test.Assert "Source reusable", list.Item(list.Base) = "hello"
list.Push 5
Test.Assert "Moved array grows", list.Length = 4
Test.Assert "Moved array clones", list.Clone.Length = 4
Set list = Nothing
Test.Assert "Object survives cleanup", obj.Item(1) = "alive"
Dim fixed(0 To 2) As Variant
fixed(0) = "retained"
On Error Resume Next
Set list = stdArray.CreateFromArrayByRef(fixed)
failure = Err.Number
Err.Clear
On Error GoTo 0
Test.Assert "Reject fixed", failure = 5
Test.Assert "Rejected source unchanged", fixed(0) = "retained"
Dim matrix() As Variant: ReDim matrix(0 To 1, 0 To 1)
On Error Resume Next
Set list = stdArray.CreateFromArrayByRef(matrix)
failure = Err.Number
Err.Clear
On Error GoTo 0
Test.Assert "Reject multidimensional", failure = 5
Dim unallocated() As Variant
On Error Resume Next
Set list = stdArray.CreateFromArrayByRef(unallocated)
failure = Err.Number
Err.Clear
On Error GoTo 0
Test.Assert "Reject unallocated", failure = 5
Dim v As Variant: v = Array(8, 9)
Set list = stdArray.CreateFromArrayByRef(v)
Test.Assert "Variant containing array", list.Join = "8,9"
v = Array(10)
Test.Assert "Variant source reusable", list.Join = "8,9"
Dim locked() As Variant: ReDim locked(0 To 1)
Dim element As Variant
For Each element In locked
On Error Resume Next
Set list = stdArray.CreateFromArrayByRef(locked)
failure = Err.Number
Err.Clear
On Error GoTo 0
Test.Assert "Reject locked array", failure = 5
Next
End Sub
Loading