Skip to content

Interop: an index on a host collection is one property, however script spelled it - #3416

Merged
lahma merged 1 commit into
sebastienros:mainfrom
lahma:issue-3384-growable-index-write
Aug 27, 2026
Merged

lahma merged 1 commit into
sebastienros:mainfrom
lahma:issue-3384-growable-index-write

Conversation

@lahma

@lahma lahma commented Aug 26, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #3384.

A wrapped growable List<int> answers list.length = 5 by growing and answered list[3] = 9 with the
CLR's own ArgumentOutOfRangeException out of Engine.Evaluate. Building the matrix the issue asks for
showed the reported cell is one of forty-four, and that the cause is not "growth is missing" but something
narrower and worse: an array-like view owned only the number spelling of an index.

x[3] and x["3"] are one property key — ToPropertyKey makes both the String "3" — and they were
answered from two different places. A number key went to the view, which knows its own length. A string key
went to the reflected indexer, which parsed an index out of the key and handed it to the collection. So the
same request, written the two ways a script writes it, gave two different answers on almost every row, and
one of the two was a CLR exception no catch on either side of the boundary can see.

Reproduction, before the change

Measured on main, net10.0, Release, JIT, from a program that does nothing but engine.SetValue("x", …)
and Execute, with Interop.AllowWrite = true and ArrayConversion = LiveView. Every collection holds
1, 2, 3. A cell is outcome + collection afterwards; where sloppy and strict differ it is written
sloppy → strict. AOORE is System.ArgumentOutOfRangeException, IOORE
System.IndexOutOfRangeException, AE System.ArgumentException, all escaping Execute.

The six growable shapes — List<int>, List<string>, Collection<int>, a host IList<int>, ArrayList
and a host non-generic IList — are byte-identical on every row apart from the default element a grown
slot gets (0 for int, null for string and for the untyped lists), so they are one column here. The
six span all three wrappers: the first four reach GenericListWrapper<T>, the last two ListWrapper.

growable (6 shapes) int[] LiveView ArraySegment<int> ReadOnlyCollection<int>
wrapper GenericListWrapper / ListWrapper ArrayWrapper GenericListWrapper GenericListWrapper
x[2] = 9 writes writes writes no-op → TypeError
x["2"] = 9 writes AE writes no-op → TypeError
x[3] = 9 AOORE TypeError TypeError no-op → TypeError
x["3"] = 9 AOORE IOORE AOORE no-op → TypeError
x[5] = 9 AOORE TypeError TypeError no-op → TypeError
x[100000] = 9 AOORE TypeError TypeError no-op → TypeError
x[-1] = 9 AOORE TypeError TypeError no-op → TypeError
x["-1"] = 9 AOORE IOORE AOORE no-op → TypeError
x["08"] = 9 AOORE IOORE AOORE no-op → TypeError
x[3.5] = 9 no-op no-op no-op no-op
x[2**31] = 9 no-op no-op no-op no-op
delete x[0] resets the slot no-op → TypeError no-op → TypeError no-op → TypeError
delete x["0"] no-op → TypeError no-op → TypeError no-op → TypeError no-op → TypeError
delete x[3] AOORE true true true
delete x["3"] no-op → TypeError no-op → TypeError no-op → TypeError true
delete x[-1] AOORE true true true
x.length = 5 grows TypeError TypeError no-op → TypeError
x.push(9) grows TypeError TypeError TypeError
x.foo = 1 expando expando expando expando
x[3] = 9, AllowWrite = false no-op → TypeError no-op → TypeError no-op → TypeError no-op → TypeError
preventExtensions(x); x[3] = 9 no-op → TypeError no-op → TypeError no-op → TypeError no-op → TypeError

And the read side, which the issue did not mention at all and which is the same split:

growable (6 shapes) int[] LiveView ArraySegment<int> ReadOnlyCollection<int>
x[2] / x["2"] 3 / 3 3 / 3 3 / 3 3 / 3
x[3] undefined undefined undefined undefined
x["3"] AOORE IOORE AOORE AOORE
x["-1"], x["08"] AOORE IOORE AOORE AOORE
Object.assign(x, {3: 9}) AOORE IOORE AOORE TypeError
3 in x, "3" in x, Object.keys(x), for..in, JSON.stringify(x) correct correct correct correct

Object.assign is the row that says this is not a curiosity: it is the ordinary way a script copies into a
host list, it is specified over string keys, and it was a CLR exception on every array-like wrapper,
read-only ones included.

What each row should do, from the specification

The wrapper is an ordinary object carrying Array.prototype, so the answer is per operation and per
position, never per spelling.

Reproduction, after the change

Only the cells that moved; every other cell above is unchanged.

growable (6 shapes) int[] LiveView ArraySegment<int>
x["2"] = 9 — writes —
x[3] = 9 grows: 1,2,3,9 — —
x["3"] = 9 grows: 1,2,3,9 TypeError TypeError
x[5] = 9 grows: 1,2,3,0,0,9 — —
x[100000] = 9 grows to 100001 — —
x[-1] = 9 no-op → TypeError — —
x["-1"] = 9, x["08"] = 9 no-op → TypeError TypeError TypeError
x[2**31] = 9 no-op → TypeError TypeError TypeError
delete x["0"] resets the slot — —
delete x[3], delete x[-1] true, untouched — —
delete x["3"] true, untouched true true
x["3"] (read) undefined undefined undefined
Object.assign(x, {3: 9}) grows: 1,2,3,9 TypeError TypeError

Two rows are worth calling out because they are not about growth at all. A T[] live view refused
x["2"] = 9 — a perfectly ordinary in-range element write — with ArgumentException, because the reflected
indexer for an array is the object-typed IList one and the write bypassed item-type coercion; the source
comment on the fixed-size lane predicted exactly that and the lane only covered number keys. And
delete x["0"] was refused where delete x[0] reset the slot.

What changed

ArrayLikeWrapper classifies a property key once — ClassifyElementKey — and Get, Set,
HasProperty and Delete all read that answer instead of each re-deriving a different one. Three outcomes:
a Position the target could address, an OutOfBand key that is index-shaped but can never be a position
(negative, non-canonical, past int), and None for everything else, which falls through to base exactly
as before. The "08"/"-1" guard and the dictionary-shaped carve-out are not new — HasProperty already
had both, and this makes them the rule for the other three.

Set then grows for a Position at or past the end of a target that is not fixed-size, which is SetAt,
which is EnsureCapacity + write — the same two calls x.length = n and x.push(v) already make.

One further fact the matrix forced, and the only part of this that is not the classifier. ListWrapper took
its writability from the target, so an int[] handed to script as IReadOnlyList<T> produced a writable
view; the refusal came from that interface's get-only indexer, and therefore only for a string-spelled
index. It now also treats an exposed contract with no writable indexer as read-only — which is what the
typed ReadOnlyListWrapper<T> says from its type argument, and what
InteropTests.LiveViewHonorsNonArrayDeclaredType has always asserted. ListWrapper is what such an
exposure degrades to because ResolveArrayLikeWrapperFactoryType scans the exposed type's GetInterfaces()
and an interface is not among its own; that gap is filed separately rather than closed here, because closing
it changes which wrapper several exposures get and deserves its own matrix.

Tests

Jint.Tests.PublicInterface/HostCollectionIndexWriteTests.cs — the only suite without
InternalsVisibleTo, which is what makes "an embedder cannot catch this" a statement a test can make. Six
growable shapes, both spellings of every index-shaped key, the growth-equals-length equivalence, the
fixed-size and exposed-read-only contrasts, and a blanket row asserting that what leaves Execute is a
JavaScript error or nothing.

Failing first, on unmodified upstream/main with only the new test file added:

Failed!  - Failed:    55, Passed:     8, Skipped:     0, Total:    63 - Jint.Tests.PublicInterface.dll (net10.0)

  Failed AnIndexWriteAtTheEndOfAGrowableCollectionAppends("List","3")
   System.ArgumentOutOfRangeException : Index was out of range. …
  Failed CopyingIntoAGrowableCollectionGrowsIt("ArrayList")
   System.ArgumentOutOfRangeException : Index was out of range. …
  Failed ReadingAnAbsentIndexIsUndefinedInEitherSpelling("List")
   System.ArgumentOutOfRangeException : Index was out of range. …
  Failed AFixedSizeViewAnswersBothSpellingsOfAnIndex
   System.ArgumentException : Cannot widen from source type to target type …
  Failed AnExposedReadOnlyContractRefusesElementWritesInEitherSpelling("x.length = 5")
   Expected … to be "no-throw" because a failed [[Set]] is silent outside strict mode, but …

The 8 that pass are the in-range writes, and the exposed-IReadOnlyList<T> element writes the get-only
indexer already refused by accident.

Two rows of ClrWriteConfigurationTests.ArrayGenericsCannotMutateFrozenClrListsWhenWritesAreEnabled were
asserting the string spelling's answer and move: Object.freeze(list); list["0"] = 9 outside strict mode is
now silent, as list[0] = 9 always was and as a frozen JavaScript array is, and
Reflect.set(list, "0", 9) returns false rather than throwing, which is what Reflect.set is specified
to do. Both are now pinned in both spellings, in two tests named for what they assert.

Verification

  • dotnet build -c Release — 0 warnings, 0 errors.
  • Jint.Tests — 10,752 passed on net10.0 and net8.0, 7,376 on net472, 0 failed.
  • Jint.Tests.PublicInterface — 3,145 passed on net10.0, 3,135 on net8.0, 2,516 on net472, 0 failed.
  • Both again with JINT_HOST_CONTRACT_VERIFICATION=1 — 0 failed.
  • Jint.Tests.Test262 — 102,495 passed, 0 failed, 189 skipped (102,684 total).
  • No public API baseline moved and UndocumentedPublicApi.txt is unchanged: everything touched is a member
    of an internal type.
  • docs/v5-migration.md §4.33 carries the embedder-facing form. No benchmark: the read path gains one
    branch for a key that is not a JsNumber and loses the reflected-indexer resolution it used to take, and
    the write path replaces one type test with one classification.

@lahma
lahma force-pushed the issue-3384-growable-index-write branch 11 times, most recently from 5589df3 to 4f01d9c Compare August 27, 2026 02:36
…t spelled it

Fixes sebastienros#3384.

A wrapped growable `List<int>` answered `list.length = 5` by growing and `list[3] = 9` by raising the
CLR's own `ArgumentOutOfRangeException` out of `Engine.Evaluate`. Building the matrix showed the cause is
wider than the reported cell: an array-like view owned only the *number* spelling of an index. A string
key — `list["3"]`, and therefore `Object.assign`, a `for..in` copy and every `Set(O, k, v, true)` over a
string key — went to the reflected indexer, which parsed an index out of the key and handed it to the
collection.

`x[3]` and `x["3"]` are one property key, so they now take one lane:

* a canonical index in range writes the element, whichever way it was spelled;
* a canonical index at or past the end of a **growable** target makes room and writes, which is exactly
  what a `length` write of `index + 1` already did — the view is an extensible ordinary object, so
  `CreateDataProperty` succeeds;
* a **fixed-size** target keeps sebastienros#3381's `TypeError` and a **read-only** one keeps sebastienros#3382's refusal, now for
  both spellings;
* an index the view can never hold — negative, non-canonical (`"08"`, `"+3"`), or past what the target can
  address — is the ordinary `[[Set]]` refusal rather than something the collection is left to reject;
* `delete` of an absent position returns `true`, and of a present one resets the slot, in both spellings.

One further fact the matrix forced. `ListWrapper` took its writability from the target, so an array handed
to script as `IReadOnlyList<T>` produced a writable view; the refusal came from that interface's get-only
indexer, and therefore only for a string-spelled index. It now also treats an exposed contract with no
writable indexer as read-only, which is what the typed `ReadOnlyListWrapper<T>` says and what
`InteropTests.LiveViewHonorsNonArrayDeclaredType` has always asserted.

`docs/v5-migration.md` §4.33 carries the embedder-facing form, including the two rows of
`ClrWriteConfigurationTests` that were asserting the string spelling's answer: a frozen list's
`list["0"] = 9` is now silent outside strict mode, as `list[0] = 9` always was, and `Reflect.set` reports
the refusal as `false` rather than throwing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014W5mbjGhyvgAS4pivXoc4S
@lahma
lahma force-pushed the issue-3384-growable-index-write branch from 4f01d9c to eb34db9 Compare August 27, 2026 02:42
@lahma
lahma merged commit 23e4546 into sebastienros:main Aug 27, 2026
7 checks passed
lahma added a commit that referenced this pull request Sep 1, 2026
…tion's elements (#3561)

Fixes #3558.

`Options.Interop.TypeResolver.MemberFilter` is CLR-containment configuration — it is how a host says
which members script may reach — and a member it rejects reads as `undefined` and cannot be written.
That held for the indexer of an ordinary wrapped object, which resolves a member per access. It did
not hold for a wrapped **collection**. `ArrayLikeWrapper` answers every index-shaped key itself, which
is the whole point of #3416 and #3384: without it an out-of-range `list[3] = 9` was the collection's
own `ArgumentOutOfRangeException` out of `Evaluate`. But that view was never told what the filter had
decided, so the same filter, asked the same question, gave two answers depending on whether Jint
happened to build a view.

Three refusals had become writes since #3416 (`list[0] = 42`, `list['0'] = 42`, and growth
`list[3] = 42`), and reads, `delete`, `push` and `sort` had bypassed the filter for longer than that.
The existing `HostIndexerFilterTests.AMemberFilterExcludingTheIndexerBlocksIndexedWrites` asserted the
contract and passed on `main` only because its engine left `Options.Interop.AllowWrite` at the `false`
#3054 made it default to; with writes on, it fails.

The whole element contract is closed rather than only the write half. Under a filter that hides the
indexer, an array-like view now has no element properties at all: `Get` reads `undefined`, `in` is
`false` (agreeing with `hasOwnProperty` and `Object.keys`, which already said so — `OrdinaryHasProperty`
is defined in terms of `[[GetOwnProperty]]`, so they may not disagree), `Set` and `DefineOwnProperty`
refuse, `delete` returns `true` without touching the slot, a `length` write neither grows nor truncates,
and `ArrayOperations.For` routes every `Array.prototype` generic to `ObjectOperations` exactly as a
countable-but-not-indexable `Queue<T>` is routed. Containment is asked **before** the read-only and
fixed-size refusals of #3382/#3385 so the two compose rather than mask each other: a fixed-size array
whose indexer is hidden reports "no such property" rather than the `TypeError` naming its bounds, which
would answer a question the host never granted.

Three lanes are deliberately left out of the contract, and say so. `length` is produced from `Count`,
a member the filter decides about separately. Iteration is `GetEnumerator`'s business, so `[...list]`
still yields elements — the shape a `Queue<T>` has always had. And `ArrayConversionMode.Copy`, the
default, turns a `T[]` into a JavaScript array before any member is accessed; that is a conversion of
a value, not an access to a member.

The decision is the one `IndexerAccessor.TryFindIndexer` would have made — the first integer-keyed
indexer the exposed type declares, falling back to the descriptor's `IList.Item` for a `T[]`, which
declares none of its own — memoized per resolver and per type behind `TypeResolver.ExposesIndexedElements`
and dropped with the rest of the resolved state when the filter is reassigned. A resolver with the
default filter returns from one bool field read and caches nothing; a filtered one pays one dictionary
lookup per array-like wrapper construction, beside the `TypeDescriptor.Get` the base constructor
already does, and every element access afterwards reads a `readonly bool` field. No per-operation
allocation and no per-operation delegate invocation anywhere.

`docs/v5-migration.md` §4.97 carries the embedder-facing form, including what an allow-list filter has
to add to keep the elements it was reaching by accident.


Claude-Session: https://claude.ai/code/session_014W5mbjGhyvgAS4pivXoc4S

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
lahma added a commit to lahma/jint that referenced this pull request Sep 1, 2026
… filter that hides the indexer hides it

Backport of eight main pull requests that together decide one question — what an index-shaped key means on a
wrapped CLR collection — plus the fix for the containment hole the seventh of them opened. They are one unit:
each of the first seven moves the answer, and taking any of them alone leaves the lanes disagreeing with each
other.

  sebastienros#3356  a host collection with a count is not a host collection with an index
  sebastienros#3381  a degraded array view still refuses a resize
  sebastienros#3425  the IndexWrappedOperations lane is not AOT-only, and a generic that must grow refuses
  sebastienros#3416  an index on a host collection is one property, however script spelled it
  sebastienros#3464  hasOwnProperty and "in" give one answer about an index on a wrapped collection
  sebastienros#3472  an index outside a wrapped collection is refused, not handed to the collection
  sebastienros#3480  a collection exposed as IList<T> or IReadOnlyList<T> gets the wrapper that contract names
  sebastienros#3561  a member filter that hides an indexer hides a wrapped collection's elements (fixes sebastienros#3558)

sebastienros#3385 - a read-only host collection refuses script with a JavaScript error - is the ninth member of the
cluster and is already on this branch as sebastienros#3556, so its hunks are not here. Its suite,
HostReadOnlyCollectionTests, is 68 of 68 green both before and after this change, which is what says so.

What script sees. An index-shaped key is now the view's own property, whichever way it is spelled and whether
or not the position exists. A read outside the range is undefined rather than the collection's own
ArgumentOutOfRangeException out of Evaluate; a write at the end grows a growable target exactly as a "length"
write of the same size does; "in", hasOwnProperty, propertyIsEnumerable and getOwnPropertyDescriptor give one
answer, because OrdinaryHasProperty is defined in terms of [[GetOwnProperty]] and may not disagree with it; a
delete of an absent position succeeds without reaching the collection; and a countable-but-not-indexable
target - Queue<T>, Stack<T>, LinkedList<T>, SortedSet<T> - is array-like with no element at index 0 rather
than an InvalidCastException from a lane that cast it to IList.

The containment half is why sebastienros#3561 is in the same change. Options.Interop.TypeResolver.MemberFilter is how a
host says which members script may reach, and an ArrayLikeWrapper answers every index-shaped key itself, so
the filter's decision about the indexer never reached the element lanes. On this branch that matters more than
it does on main: Interop.AllowWrite ships on here, so a filter that hid the indexer stopped nothing. Measured
on this branch, with the cluster applied and sebastienros#3561 held back, three refusals had become writes
(list[0] = 42, list['0'] = 42 and growth list[3] = 42) and reads, "in", delete, push and sort had never been
covered at all - and the pre-existing
HostIndexerFilterTests.AMemberFilterExcludingTheIndexerBlocksIndexedWrites, which passes on stock 4.x, fails.
The whole element contract is closed rather than only the write half, and containment is asked before the
read-only and fixed-size refusals of sebastienros#3382/sebastienros#3385 so the two compose: a fixed-size array whose indexer is
hidden reports "no such property" rather than the TypeError naming its bounds, which would answer a question
the host never granted.

Evidence, on net10.0 and net472 alike (identical counts on both). Against stock 4.x with the suites in place:
HostNonIndexedCollectionTests 33 of 45 failed, HostExposedCollectionTypeTests 21 of 33,
HostCollectionIndexWriteTests 56 of 63, HostCollectionIndexAgreementTests 11 of 18,
HostCollectionIndexBoundsTests 28 of 35, HostIndexerFilterTests 13 of 18, and 6 of the 7 new
InteropTests.ClrArrayLiveView cases. All of them pass now. The containment tests run in both write
configurations, because on this branch the default is the interesting one: the elements leak under
AllowWrite = true and the reads leak under AllowWrite = false, and both are pinned.

Deliberate divergences from main. sebastienros#3054 - which is what makes Interop.AllowWrite default to false there - is
a v5 default change and stays out, so this branch keeps its Delete and ArrayOperations.Set guards and the two
suites spell the write switch out where main could leave it to the default. Jint.AotExample's probes from
sebastienros#3381/sebastienros#3425/sebastienros#3480 are not ported: this branch's AotExample is a 22-line stub with none of the AOT probe
harness those hunks extend. docs/v5-migration.md and Jint/Runtime/Interop/AGENTS.md do not exist here, so
their hunks are carried into the XML docs and comments beside the code instead. The suites are xUnit v3 here
rather than the NUnit main moved to in sebastienros#3409.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014W5mbjGhyvgAS4pivXoc4S
lahma added a commit that referenced this pull request Sep 1, 2026
… filter that hides the indexer hides it (#3562)

Backport of eight main pull requests that together decide one question — what an index-shaped key means on a
wrapped CLR collection — plus the fix for the containment hole the seventh of them opened. They are one unit:
each of the first seven moves the answer, and taking any of them alone leaves the lanes disagreeing with each
other.

  #3356  a host collection with a count is not a host collection with an index
  #3381  a degraded array view still refuses a resize
  #3425  the IndexWrappedOperations lane is not AOT-only, and a generic that must grow refuses
  #3416  an index on a host collection is one property, however script spelled it
  #3464  hasOwnProperty and "in" give one answer about an index on a wrapped collection
  #3472  an index outside a wrapped collection is refused, not handed to the collection
  #3480  a collection exposed as IList<T> or IReadOnlyList<T> gets the wrapper that contract names
  #3561  a member filter that hides an indexer hides a wrapped collection's elements (fixes #3558)

#3385 - a read-only host collection refuses script with a JavaScript error - is the ninth member of the
cluster and is already on this branch as #3556, so its hunks are not here. Its suite,
HostReadOnlyCollectionTests, is 68 of 68 green both before and after this change, which is what says so.

What script sees. An index-shaped key is now the view's own property, whichever way it is spelled and whether
or not the position exists. A read outside the range is undefined rather than the collection's own
ArgumentOutOfRangeException out of Evaluate; a write at the end grows a growable target exactly as a "length"
write of the same size does; "in", hasOwnProperty, propertyIsEnumerable and getOwnPropertyDescriptor give one
answer, because OrdinaryHasProperty is defined in terms of [[GetOwnProperty]] and may not disagree with it; a
delete of an absent position succeeds without reaching the collection; and a countable-but-not-indexable
target - Queue<T>, Stack<T>, LinkedList<T>, SortedSet<T> - is array-like with no element at index 0 rather
than an InvalidCastException from a lane that cast it to IList.

The containment half is why #3561 is in the same change. Options.Interop.TypeResolver.MemberFilter is how a
host says which members script may reach, and an ArrayLikeWrapper answers every index-shaped key itself, so
the filter's decision about the indexer never reached the element lanes. On this branch that matters more than
it does on main: Interop.AllowWrite ships on here, so a filter that hid the indexer stopped nothing. Measured
on this branch, with the cluster applied and #3561 held back, three refusals had become writes
(list[0] = 42, list['0'] = 42 and growth list[3] = 42) and reads, "in", delete, push and sort had never been
covered at all - and the pre-existing
HostIndexerFilterTests.AMemberFilterExcludingTheIndexerBlocksIndexedWrites, which passes on stock 4.x, fails.
The whole element contract is closed rather than only the write half, and containment is asked before the
read-only and fixed-size refusals of #3382/#3385 so the two compose: a fixed-size array whose indexer is
hidden reports "no such property" rather than the TypeError naming its bounds, which would answer a question
the host never granted.

Evidence, on net10.0 and net472 alike (identical counts on both). Against stock 4.x with the suites in place:
HostNonIndexedCollectionTests 33 of 45 failed, HostExposedCollectionTypeTests 21 of 33,
HostCollectionIndexWriteTests 56 of 63, HostCollectionIndexAgreementTests 11 of 18,
HostCollectionIndexBoundsTests 28 of 35, HostIndexerFilterTests 13 of 18, and 6 of the 7 new
InteropTests.ClrArrayLiveView cases. All of them pass now. The containment tests run in both write
configurations, because on this branch the default is the interesting one: the elements leak under
AllowWrite = true and the reads leak under AllowWrite = false, and both are pinned.

Deliberate divergences from main. #3054 - which is what makes Interop.AllowWrite default to false there - is
a v5 default change and stays out, so this branch keeps its Delete and ArrayOperations.Set guards and the two
suites spell the write switch out where main could leave it to the default. Jint.AotExample's probes from
#3381/#3425/#3480 are not ported: this branch's AotExample is a 22-line stub with none of the AOT probe
harness those hunks extend. docs/v5-migration.md and Jint/Runtime/Interop/AGENTS.md do not exist here, so
their hunks are carried into the XML docs and comments beside the code instead. The suites are xUnit v3 here
rather than the NUnit main moved to in #3409.


Claude-Session: https://claude.ai/code/session_014W5mbjGhyvgAS4pivXoc4S

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

Interop: a growable host list refuses an index write past the end with a CLR exception, where the same write through length grows it

1 participant