Skip to content

Commit 294ea31

Browse files
committed
perf(ffi/v8): kNonMasking on native instances - 773ms benchmark
Use kNonMasking interceptor on native object instances only (not class or bridge host objects). This allows V8 to find prototype properties without calling the interceptor, giving a 40% speedup. Benchmark: 773ms (was 1250ms) Tests: 713 total, 7 failures remaining (superclass/instanceof edge cases) Also fix readonly property test expectations to accept V8's native error message with kNonMasking.
1 parent 7083baa commit 294ea31

4 files changed

Lines changed: 31 additions & 3 deletions

File tree

NativeScript/ffi/shared/bridge/HostObjects.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1762,7 +1762,7 @@ Value makeNativeObjectValue(Runtime& runtime,
17621762
bridge->forgetRoundTripValue(runtime, object);
17631763
}
17641764

1765-
Object result = Object::createFromHostObject(
1765+
Object result = Object::createNativeInstanceHostObject(
17661766
runtime,
17671767
std::make_shared<NativeApiObjectHostObject>(bridge, object, ownsObject));
17681768
Value prototypeValue = Value::undefined();

NativeScript/ffi/v8/NativeApiV8HostObjects.mm

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,34 @@ void emplace(size_t index, Value&& value) {
5252
if (state->hostObjectTemplate.IsEmpty()) {
5353
v8::Local<v8::ObjectTemplate> objectTemplate = v8::ObjectTemplate::New(runtime.isolate());
5454
objectTemplate->SetInternalFieldCount(1);
55+
// toString must be own property to override Object.prototype.toString
56+
// when using kNonMasking interceptor.
57+
objectTemplate->Set(
58+
makeV8String(runtime.isolate(), "toString"),
59+
v8::FunctionTemplate::New(runtime.isolate(),
60+
[](const v8::FunctionCallbackInfo<v8::Value>& info) {
61+
v8::Local<v8::Object> self = info.This();
62+
if (self.IsEmpty() || self->InternalFieldCount() < 1) return;
63+
auto* holder = static_cast<HostObjectHolder*>(
64+
self->GetAlignedPointerFromInternalField(0));
65+
if (holder == nullptr || holder->hostObject == nullptr) return;
66+
Runtime rt(holder->state);
67+
try {
68+
Value toStr = holder->hostObject->get(rt, PropNameID("toString"));
69+
if (!toStr.isUndefined()) {
70+
v8::Local<v8::Value> v8Val = toStr.local(rt);
71+
if (v8Val->IsFunction()) {
72+
v8::Local<v8::Value> result;
73+
if (v8Val.As<v8::Function>()->Call(rt.context(), self, 0, nullptr)
74+
.ToLocal(&result)) {
75+
info.GetReturnValue().Set(result);
76+
return;
77+
}
78+
}
79+
}
80+
} catch (...) {}
81+
}),
82+
v8::DontEnum);
5583
objectTemplate->SetHandler(v8::NamedPropertyHandlerConfiguration(
5684
[](v8::Local<v8::Name> property,
5785
const v8::PropertyCallbackInfo<v8::Value>& info) -> v8::Intercepted {

test/runtime/runner/app/tests/Inheritance/InheritanceTests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ describe(module.id, function () {
307307
UNUSED(object.baseProtocolProperty2Optional);
308308
object.baseProperty = 0;
309309
UNUSED(object.baseProperty);
310-
expect(() => object.baseReadOnlyProperty = 0).toThrowError("Attempted to assign to readonly property.");
310+
expect(() => object.baseReadOnlyProperty = 0).toThrowError(/Attempted to assign to readonly property|Cannot set property.*which has only a getter/);
311311
UNUSED(object.baseReadOnlyProperty);
312312
object.baseCategoryProtocolProperty1 = 0;
313313
UNUSED(object.baseCategoryProtocolProperty1);

test/runtime/runner/app/tests/MethodCallsTests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,7 @@ describe(module.id, function () {
10001000
it('Derived_DerivedPropertyReadOnly', function () {
10011001
"use strict";
10021002
var instance = TNSDerivedInterface.alloc().init();
1003-
expect(() => instance.derivedPropertyReadOnly = 1).toThrowError(/Attempted to assign to readonly property/);
1003+
expect(() => instance.derivedPropertyReadOnly = 1).toThrowError(/Attempted to assign to readonly property|Cannot set property.*which has only a getter/);
10041004
UNUSED(instance.derivedPropertyReadOnly);
10051005

10061006
var actual = TNSGetOutput();

0 commit comments

Comments
 (0)