GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||
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
|
| 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:
IReferenceArray<T>;Int32[] when they're all 32-bit integers, Double[] otherwise), all booleans, or all WinRT objects (or null).Object arguments no longer leakEvery 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.
String[] and object arrays passed in or filledPropertyValue.CreateStringArray, IVector<T>.ReplaceAll, …): a JS array becomes HSTRINGs, or objects queried to the element's interface. They're released after the call.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.[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.PropertyValue that frees the returned HSTRINGsout byte[], including an empty oneString[]/Object[] passed in, and round trips through PropertySetIVector<String>.ReplaceAll/GetMany, and GetMany returning the filled countIVector<IJsonValue>.ReplaceAll/GetManyCertificateExtension.Value set and getListBoxAutomationPeer.GetSelection() in a live XamlWindownpm test: 442/442cargo test -p runtime --lib: the same failures as mainNot 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.PropertyValue.CreatePointArray).
Sorry, something went wrong.
There was an error while loading. Please reload this page.
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.
There was an error while loading. Please reload this page.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
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.
- 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.
47a3e7c to
6038bfb
Compare
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.