GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — feat(runtime): arrays WinRT hands back, String[]/object arrays, boxed-argument leak by triniwiz · Pull Request #44 · NativeScript/windows

Conversation

@triniwiz

@triniwiz triniwiz commented Nov 6, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Follows #41. Arrays now convert in both directions for every element type, on both engines, including arrays WinRT allocates and hands back. Before, those were refused when the call was built ("Unsupported array return type"). Three commits:

1. Arrays WinRT hands back

A received array takes two ABI slots, UINT32* length and T** data. MethodCall and PropertyCall lay these out for:

  • array return values and array-typed property getters, which land in the call's return buffer;
  • ByRef T[] parameters, as two out slots.

After the call, (length, data) is converted and the CoTaskMem array freed. The element type comes from classify_return, through the new ReturnKind::Array { element, element_type, size }:

element JS value
primitives (UInt8 … Double, Boolean, Char16, enums) typed array of the matching kind, the same kinds interop.Array.create returns
String array of strings
classes, interfaces, Object array of wrappers (null for null)
structs, Guid array of struct objects

String and object elements are taken over by the conversion, so nothing is left to release. Out parameters behave as they do for other types: classic accepts interop.out() or returns [result, ...outs]; napi needs interop.out().

const out = interop.out();
CryptographicBuffer.CopyToByteArray(buffer, out); // out.value: Uint8Array
Windows.Graphics.Display.DisplayServices.FindAll(); // [{ Value: 65537 }]
new ListBoxAutomationPeer(list).GetSelection(); // [wrapper, wrapper]
certificateExtension.Value; // Uint8Array

Boxed arrays: IReferenceArray<T> values (from PropertyValue.Create*Array, a PropertySet, settings, device properties) unbox the way boxed scalars already do, through IPropertyValue.Get*Array and the same conversion. In the other direction, an Object parameter boxes these with PropertyValue, so a returned array can be passed back:

  • typed arrays, which box as the matching IReferenceArray<T>;
  • plain arrays whose elements are all strings, all numbers (Int32[] when they're all 32-bit integers, Double[] otherwise), all booleans, or all WinRT objects (or null).

2. Boxed Object arguments no longer leak

Every JS value boxed for an Object parameter (strings, numbers, booleans, struct shapes, and now arrays) used to forget its reference, so each argument leaked its box. Each call now opens a BoxedArgs scope, and the boxes made while parsing its arguments are released when the call returns. A call made during argument parsing (e.g. from a getter) opens its own scope and releases only its own boxes.

Inserting the same values into a PropertySet 200k times (napi, memory after GC):

value before after
2 KB string +1087 MB +262 MB
Float64Array(256) +693 MB +262 MB
number +285 MB +262 MB
existing WinRT object (no boxing) +262 MB +261 MB

The ~262 MB the no-boxing row still shows isn't a leak. It's napi finalizers waiting for the event loop, which this synchronous loop never yields to. With the loop yielding between 50k-call batches, memory levels off after a few batches.

3. String[] and object arrays passed in or filled

  • Passed in (PropertyValue.CreateStringArray, IVector<T>.ReplaceAll, …): a JS array becomes HSTRINGs, or objects queried to the element's interface. They're released after the call.
  • Filled by the callee (IVector<T>.GetMany): the caller passes a JS array as long as the slots it wants, and the results are written back into that array. interop.Array.create('String', n), ('Object', n) or ('Windows.Data.Json.IJsonValue', n) returns an array of nulls for this.
  • Fill parameters are recognized by the [out] param attribute (ParameterDeclaration::is_fill_array).
  • GenericMethodCall, which is only used for open generic interfaces, refuses these arrays rather than passing them as bytes.

Testing

  • Rust unit tests:
    • array classification and layout
    • string/object element resolution
    • a box/unbox round trip through PropertyValue that frees the returned HSTRINGs
  • End-to-end, 24/24 on Node with windows-napi, 23/23 on classic V8 (the XAML check needs a XAML host):
    • out byte[], including an empty one
    • struct-array return
    • boxed arrays of every primitive kind
    • String[]/Object[] passed in, and round trips through PropertySet
    • IVector<String>.ReplaceAll/GetMany, and GetMany returning the filled count
    • IVector<IJsonValue>.ReplaceAll/GetMany
    • CertificateExtension.Value set and get
    • ListBoxAutomationPeer.GetSelection() in a live XamlWindow
  • Existing suites:
    • windows-napi npm test: 442/442
    • the feat(runtime): primitive arrays, interop.Array.create, delegates boxed for Object params #41 end-to-end checks: 27/27 napi, 24/24 classic
    • the events checks: 26/26
    • cargo test -p runtime --lib: the same failures as main

Not covered:

  • IReferenceArray<T>.Value called directly on an interface instance. It goes through the same property path, but boxed arrays now unbox before JS can reach that member, so I couldn't test it.
  • Struct arrays passed in (e.g. PropertyValue.CreatePointArray).

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@triniwiz
feat(runtime): arrays WinRT hands back
c8f2046
WinRT-allocated arrays now convert on both engines, where they were
refused when the call was built:

- T[] return values and received (out T[]) parameters. The callee's
  (length, data) pair is converted and the CoTaskMem array freed:
  primitive elements become a typed array of the matching kind (as
  interop.Array.create allocates), strings and objects a JS array, and
  structs an array of struct objects.
- Boxed arrays (IReferenceArray<T>, e.g. from PropertyValue.Create*Array
  or a PropertySet value) unbox the same way boxed scalars already do.
  In the other direction, a typed array, or a plain array of strings,
  numbers, booleans or WinRT objects, passed for an Object parameter is
  boxed with PropertyValue, so a returned array can be passed back.

Examples: CryptographicBuffer.CopyToByteArray(buffer, out) gives a
Uint8Array; DisplayServices.FindAll() gives an array of DisplayId;
ListBoxAutomationPeer.GetSelection() gives an array of wrappers.

Arrays of strings or objects passed in (String[] parameters) are still
unsupported.
@coderabbitai

coderabbitai Bot commented Nov 6, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 1dd688ea-e360-46c5-99e5-14505ff67193

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

  • X
  • Mastodon
  • Reddit
  • LinkedIn
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

triniwiz added 2 commits November 6, 2026 04:20
@triniwiz
fix(runtime): release values boxed for Object arguments
c8b4c22
A JS string, number, boolean, struct shape or (since this branch) array
passed for an Object parameter is boxed with PropertyValue, and the
box's reference was forgotten so the pointer stayed valid through the
call: every such argument leaked its box. Passing a 2 KB string 200k
times grew the process by ~0.8 GB more than passing an existing WinRT
object the same number of times.

Each call now opens a BoxedArgs scope; boxes made while its arguments
are parsed are released when the call returns. A call made during
argument parsing opens its own scope and releases only its own boxes.
Boxing outside any call keeps the reference as before.
@triniwiz
feat(runtime): String[] and object arrays, array properties
6038bfb
- String[] and object-array parameters (Object[], IJsonValue[],
  Uri[], ...) the caller passes or the callee fills, on both engines.
  A passed JS array becomes HSTRINGs or objects queried to the element
  interface, released after the call; a fill array (IVector<T>.GetMany)
  takes a JS array as long as the slots wanted and gets the results
  written back into it. interop.Array.create('String', n), ('Object',
  n) or (TypeName, n) returns such an array.
- Array-typed property getters (e.g. CertificateExtension.Value)
  convert like array returns.

GenericMethodCall, used only for open generic interfaces, refuses
string and object arrays rather than passing them as bytes.
@triniwiz
triniwiz force-pushed the feat/winrt-received-arrays branch from 47a3e7c to 6038bfb Compare November 6, 2026 08:24
@triniwiz triniwiz changed the title feat(runtime): arrays WinRT hands back (out T[], T[] returns, boxed arrays) feat(runtime): arrays WinRT hands back, String[]/object arrays, boxed-argument leak Nov 6, 2026
@triniwiz
triniwiz merged commit f9f28cc into main Nov 6, 2026
3 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@triniwiz
triniwiz deleted the feat/winrt-received-arrays branch November 6, 2026 10:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@triniwiz
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.