| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting. Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughArgConverter now recognizes Objective-C collections that respond to count and objectAtIndex: for indexed reads, and rejects indexed writes to unsupported non-NSArray collections with TypeError. Tests cover NSOrderedSet reads, NSOrderedSet write rejection, and NSMutableArray indexed assignment. ChangesDuck-typed collection indexing
Estimated code review effort: 3 (Moderate) | ~20 minutes Poem 🚥 Pre-merge checks | ✅ 5 ✅ Passed checks (5 passed)
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. ❤️ ShareComment @coderabbitai help to get the list of available commands. |
Sorry, something went wrong.
There was a problem hiding this comment.
NativeScript/runtime/ArgConverter.mm (1)🤖 Prompt for all review comments with AI agents886-897: ⚡ Quick win
Add regression tests for new duck-typed indexable collections.
Line 886 introduces a broader capability check (count + objectAtIndex:), but this behavior change is not protected by tests in this PR. Please add a TestRunner case that verifies obj[i] on at least NSOrderedSet (and ideally another non-NSArray type) including out-of-range behavior.
🤖 Prompt for AI AgentsVerify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@NativeScript/runtime/ArgConverter.mm` around lines 886 - 897, Add regression tests exercising the new duck-typed indexable behavior introduced in ArgConverter.mm (the count + objectAtIndex: check) by adding a TestRunner case that (1) creates an NSOrderedSet and at least one other non-NSArray indexable (e.g., PHFetchResult / custom object that implements count and objectAtIndex:), (2) verifies that Objective-C indexing via obj[i] returns the same elements as NSArray-style access for valid indices, and (3) verifies out-of-range behavior (confirm it returns nil or the same error/behavior as NSArray in the runtime) for indices >= count; place assertions in the TestRunner so this new behavior is covered by the test suite.
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. Nitpick comments: In `@NativeScript/runtime/ArgConverter.mm`: - Around line 886-897: Add regression tests exercising the new duck-typed indexable behavior introduced in ArgConverter.mm (the count + objectAtIndex: check) by adding a TestRunner case that (1) creates an NSOrderedSet and at least one other non-NSArray indexable (e.g., PHFetchResult / custom object that implements count and objectAtIndex:), (2) verifies that Objective-C indexing via obj[i] returns the same elements as NSArray-style access for valid indices, and (3) verifies out-of-range behavior (confirm it returns nil or the same error/behavior as NSArray in the runtime) for indices >= count; place assertions in the TestRunner so this new behavior is covered by the test suite.
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: a76db5ad-c7e4-4afe-8a22-8c1226e52930
📥 CommitsReviewing files that changed from the base of the PR and between 05224d3 and c210aa5.
📒 Files selected for processing (1)
Sorry, something went wrong.
|
@coderabbitai Good call. Added a TestRunner regression case in ApiTests.js ("indexed access works for non-NSArray indexable collections") that builds an NSOrderedSet (not an NSArray, but responds to count + objectAtIndex:), verifies set[i] resolves the same elements as objectAtIndex(i) for valid indices, and asserts out-of-range access returns undefined. NSOrderedSet is the cleanest non-NSArray indexable to cover here since it needs no extra native fixture. |
Sorry, something went wrong.
|
Tip For best results, initiate chat on the files or code changes. @adrian-niculescu That's a clean and self-contained test — using NSOrderedSet is exactly the right pick here since it satisfies the count + objectAtIndex: capability check, requires zero native scaffolding, and is clearly distinct from NSArray. Covering both the in-bounds path (parity with objectAtIndex:) and the out-of-bounds path (undefined) gives solid regression coverage for the new behaviour. Thanks for adding it! 🐇 ✨ |
Sorry, something went wrong.
|
I'm curious about assignment. Does assigning work at all with these collections? (ordered set[5] = "someValue") |
Sorry, something went wrong.
|
No, assignment doesn't do anything on these collections, which is what the "I left the setter alone" note in the description was getting at. The setter (IndexedPropertySetterCallback) is untouched by this PR and still bails unless the target is an NSMutableArray: if (![target isKindOfClass:[NSMutableArray class]]) {
return;
}So for an NSOrderedSet or PHFetchResult, obj[i] = x never reaches the native object. When the interceptor returns without calling info.GetReturnValue().Set(...), V8 treats the write as not intercepted and stores a plain JS property on the wrapper instead. So orderedSet[5] = "someValue" sets a JS expando on the wrapper and the native ordered set is unchanged. You can run this yourself, drop it into TestRunner/app/tests/ApiTests.js: it("indexed assignment does not mutate non-NSMutableArray collections", function () {
const set = NSOrderedSet.orderedSetWithArray(['a', 'b', 'c']);
set[5] = "ghost";
expect(set.count).toBe(3); // native object untouched
expect(set[5]).toBe("ghost"); // out-of-range read returns the JS expando
set[0] = "ghost";
expect(set.objectAtIndex(0)).toBe('a'); // native object still untouched
expect(set[0]).toBe('a'); // in-range read: getter shadows the JS expando
});Both reads behave the way the comments say: out of range, the getter declines past count so V8 falls through to the JS property; in range, the getter interceptor runs first and shadows it. It's not new behavior from this PR either. The same already happens on a real NSMutableArray for an out-of-range index, since the setter returns early when index >= count: arr[5] = "ghost" leaves count at 3 and stashes a JS-only "ghost". Keeping the setter restricted to NSMutableArray is deliberate. The collections this PR helps (PHFetchResult, NSOrderedSet) are read-only, so relaxing writes would either silently no-op or risk mutating something that isn't meant to be mutated. |
Sorry, something went wrong.
|
My main concern is exactly your test case. You're reading from one source, but assigning to another. This can also create kind of "memory leak": expect(set[0]).toBe('a')
set[0] = "ghost";
expect(set.objectAtIndex(0)).toBe('a'); // native object still untouched
expect(set[0]).toBe('a'); // in-range read: getter shadows the JS expandoset[0] never changed, but you just created a JS property 0 in the object that can never be accessed (unless the set count goes back to 0). I guess the NSArray also has this issue right now as if it grows past that count the JS property is now shadowed, but I feel it's mostly being used for assignments. I think I'd expect an error being thrown on your test case: expect(set[0]).toBe('a')
expect(() => set[0]).toThrow(...);as that'd make it clear for the user that the operation failed. What are your thoughts on this? |
Sorry, something went wrong.
|
Indeed, the orphaned property is the pre-existing fall-through in IndexedPropertySetterCallback: when the target isn't an NSMutableArray it returns without calling info.GetReturnValue().Set(...), so V8 stores a plain JS property at the integer key. The getter shadows it while index < count and it resurfaces if count shrinks past it. I implemented and pushed the throw. The setter now rejects obj[i] = x with a TypeError when the target is indexable (count + objectAtIndex:) but is neither NSMutableArray nor NSArray: if (![target isKindOfClass:[NSMutableArray class]]) {
if ([target respondsToSelector:@selector(count)] &&
[target respondsToSelector:@selector(objectAtIndex:)] &&
![target isKindOfClass:[NSArray class]]) {
isolate->ThrowException(Exception::TypeError(
tns::ToV8String(isolate, "Indexed assignment is only supported for NSMutableArray")));
}
return;
}So NSOrderedSet and PHFetchResult reject indexed writes instead of leaking an expando, while NSMutableArray is unchanged. I left immutable NSArray on its existing silent no-op rather than changing the most common type's write behavior in this PR. If you'd rather it throw for NSArray as well, that's a one-line change to drop the exclusion. Worth noting: NSMutableOrderedSet is index-writable (it responds to replaceObjectAtIndex:withObject:), so it also hits this throw even though it's mutable. It's not a regression, since the old setter only ever wrote to NSMutableArray, but it's why the message says assignment is only supported for NSMutableArray rather than calling the target read-only. Added TestRunner specs: NSOrderedSet[i] = x throws in and out of range and leaves no stray property, and NSMutableArray[i] = x still works in range. Full suite passes on the simulator. |
Sorry, something went wrong.
|
I noticed the Test job started failing after the merge commit, so I took a look. It's the pre-existing flaky worker hang, not something this PR touches. Both attempts timed out the same way: the suite runs into the last block (the shared Worker specs), the app stays alive, and the results POST never happens. From the failing run's xcresult diagnostics, the last JS activity is the expected WorkerInvalidSyntax.js error, then objc: Class TimerTargetImpl2 is implemented in both ..., then silence until the 600s timeout. The duplicate-class warning comes from the teardown spec spawning 10 workers that each require Infrastructure/timers: ClassBuilder::GetExtendedClass resolves the class name collision with an allocate/register retry loop that can race across worker threads (it even carries a "maybe a lock is needed" TODO). Every hanging run I checked has that warning; no passing run does. For comparison, run 29869540491 (feat/update-libffi, unrelated change) failed with the identical signature down to a duplicate TimerTargetImpl5 warning, and went green right after. The full suite passes locally on this branch at the merge commit: SUCCESS: 852 specs, 0 failures. |
Sorry, something went wrong.
Indexed access from JS (obj[i]) only worked when the native object was an NSArray, because the getter checked isKindOfClass:[NSArray class]. Switched that to a capability check so anything responding to both count and objectAtIndex: is indexable too, like PHFetchResult and NSOrderedSet. The setter still only accepts NSMutableArray so we don't try to mutate read-only collections.
|
Rebased on main to clear the conflict. The V8 upgrade changed the indexed interceptors to return Intercepted, so the getter early exits are now kNo, and the setter returns kYes after throwing so V8 skips the ordinary store and the rejected write leaves no stray property. Behavior is otherwise unchanged. |
Sorry, something went wrong.
Indexed assignment (obj[i] = x) silently no-op'd for any non-NSMutableArray target, letting V8 store an unreachable JS property on the wrapper. Now that non-NSArray collections are readable by index, throw a TypeError for indexable read-only targets (responds to count + objectAtIndex:, not NSMutableArray) so a dropped write surfaces instead of leaking an orphaned expando. NSArray keeps its silent no-op for backward compatibility.
| Back | FazBrowse Home | New Git URL |
Right now obj[i] from JS only works when the native object is an NSArray, because the indexed getter checks isKindOfClass:[NSArray class]. Plenty of other ordered collections are just as indexable but get left out. The one that bit me was PHFetchResult from the Photos framework.
This swaps that NSArray check in the getter for a capability check: if the target responds to both count and objectAtIndex:, treat it as indexable. NSArray keeps behaving exactly like before, and things like PHFetchResult and NSOrderedSet start working. The bounds check is unchanged, so out of range still returns nothing.
The setter uses the same capability check. It still writes only to NSMutableArray, but instead of silently dropping obj[i] = x for other indexable collections (which lets V8 stash an unreachable JS property on the wrapper), it now throws a TypeError when the target is indexable and not an NSArray. Immutable NSArray keeps its existing silent no-op.
A few notes:
TestRunner covers bracket reads on NSOrderedSet (in range matches objectAtIndex:, out of range returns undefined) and indexed assignment throwing without leaving a stray property, with NSMutableArray writes still working.
Summary by CodeRabbit