You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit ec59fe1
Browse filesBrowse the repository at this point in the historyBrowse files
fix(runtime): unwrap Proxy receivers and arguments before native dispatch (#485)
* fix(runtime): unwrap Proxy receivers and arguments before native dispatch
Native wrappers wrapped in a JS Proxy (e.g. by Vue's reactive()) expose no
internal fields, so method calls were dispatched as class methods, property
accessors returned undefined, toString returned an object, and passing a
proxy as an argument aborted the process in GetCreationContext.
- tns::UnwrapProxy walks Proxy::GetTarget chains; tns::GetValue resolves
through it, so every wrapper lookup sees the target.
- MethodCallback, property getter/setter and toString resolve a proxy
receiver to its native target (or class constructor for methods) and throw
a TypeError for revoked proxies or non-native targets.
- WriteValue/WriteTypeValue/ToArray unwrap before marshalling and throw a
TypeError for revoked proxies; ToObject and callback return values treat
a revoked proxy as nil.
- GetCreationContext call sites fall back to the current context instead of
asserting when the object has none.
* fix(runtime): key wrapper state by the Proxy target in SetValue/DeleteValue
GetValue resolves through proxies while SetValue and DeleteValue addressed
the object they were handed, so __releaseNativeCounterpart(proxy) deleted
the target's wrapper and then cleared the slot on the Proxy, leaving the
target's internal field dangling. All three now resolve to the same target.
ResolveProxyReceiver accepts a function target only when it carries an
ObjCClass wrapper; a proxied plain function is a TypeError instead of a
static call on the metadata class.
* fix(runtime): zero the whole return slot for empty callback results
ArgConverter::SetValue wrote a single ffi_arg when a JS callback returned
null, undefined or a revoked Proxy, so struct-valued returns handed native
code stale bytes past the first word. Callers now pass cif->rtype->size
and the empty branch clears the full slot.
* fix(runtime): read JavaScript-backed Proxies through their traps
JS-to-native conversion unwrapped every Proxy and read its target, which
bypasses the get/ownKeys/has traps for ordinary objects and arrays, so
native code saw different data than JS. Vue's reactive() get trap
auto-unwraps refs, so dictionaryWithDictionary(reactive({ a: ref(1) }))
produced { a: <RefImpl> } instead of { a: 1 }.
Rule, applied at every conversion site via tns::ProxyReadSource: the
unwrapped target is still used for classification and native-wrapper
lookups (a revoked Proxy still throws a TypeError). When the target is an
ordinary object or Array, the original Proxy is what gets read. Map, Set,
WeakMap/WeakSet, ArrayBuffer/views, Date, RegExp, boxed primitives,
Promises, functions and objects with internal fields stay on the target:
their prototype methods reject a Proxy receiver, and reactive(new Map())
only works through the raw Map.
Sites:
- Interop::WriteValue / WriteTypeValue: array-like -> ArrayAdapter,
DictionaryAdapter, constant arrays and all struct-from-object
initializers read the Proxy.
- Interop::ToObject: plain objects hand the Proxy to DictionaryAdapter;
proxied arrays are snapshotted through the traps before ArrayAdapter
(which needs a real v8::Array). A snapshot failure is reported like the
adapters' other read failures and yields nil.
- Interop::ToArray runs Array.prototype.slice on the Proxy and throws a
NativeScriptException carrying the trap's error instead of asserting.
- Interop::InitializeStruct and ArgConverter::GetInitializerArgs throw a
catchable error when a field read throws instead of asserting.
- ArgConverter::FindInitializer passes the Proxy to GetInitializerArgs.
- ArgConverter::SetValue (callback return) hands the Proxy to
DictionaryAdapter; struct returns stay on the target because a throw
cannot leave the ffi closure.
- DictionaryAdapter remembers the Proxy target that holds its claim, so
dealloc still finds and frees the claim after the Proxy is revoked.
0 commit comments