named property handler - getter being called for both gets and sets

274 views
Skip to first unread message

Zac Hansen

unread,
Oct 29, 2016, 6:33:14 AM10/29/16
to v8-users
I run the following javascript:

var p = new Point(); p.foo = 5;

where Point creates an object from an ObjectTemplate that has had SetHandler called on it.   However, this calls my getter callback.  Everything I've tried from javascript calls my getter callback.

object_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
// Getter
                    [](v8::Local<v8::Name> property_name,
  v8::PropertyCallbackInfo<v8::Value> const & info){
printf("IN GETTER CALLBACK111 %s\n", *v8::String::Utf8Value(property_name));
   },
                    // setter
                    [](v8::Local<v8::Name> property_name,
                       v8::Local<v8::Value> new_property_value,
                       v8::PropertyCallbackInfo<v8::Value> const & info){
printf("IN SETTER CALLBACK222 %s\n", *v8::String::Utf8Value(property_name));
                    },
nullptr, // query
nullptr, // deleter
nullptr, // enumerator
    v8::External::New(this->isolate, (void *)data),
                    v8::PropertyHandlerFlags::kNonMasking)); // <== Tried with and without this
};


and prints out: IN GETTER CALLBACK111 foo

I originally tried with the older API for string-only property names and had the same results.

#define V8_MAJOR_VERSION 5
#define V8_MINOR_VERSION 6
#define V8_BUILD_NUMBER 0
#define V8_PATCH_LEVEL 0

on os x 10.11 clang 3.9

Zac Hansen

unread,
Oct 29, 2016, 5:15:39 PM10/29/16
to v8-users
I added a delete callback and when I call delete p.foo, I get no callbacks.

Zac Hansen

unread,
Oct 29, 2016, 6:17:44 PM10/29/16
to v8-users
I don't really understand what this means, but it seems to be choosing to call the getter because this function returns false:

bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
  DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
  // Optimization that only works if configuration_ is not mutable.
  if (!check_prototype_chain()) return true;
  DisallowHeapAllocation no_gc;
  if (*receiver_ == *holder_) return true;
  if (!receiver_->IsJSReceiver()) return false;
  JSReceiver* current = JSReceiver::cast(*receiver_);
  JSReceiver* object = *holder_;
  if (!current->map()->has_hidden_prototype()) return false; <=== THIS IS WHERE IT RETURNS

when called from Object::SetPropertyInternal

On Saturday, October 29, 2016 at 3:33:14 AM UTC-7, Zac Hansen wrote:

Zac Hansen

unread,
Oct 29, 2016, 7:15:03 PM10/29/16
to v8-users
I've since added a query callback and tried returning None and ReadOnly.   No change.


On Saturday, October 29, 2016 at 3:33:14 AM UTC-7, Zac Hansen wrote:

Zac Hansen

unread,
Oct 29, 2016, 7:27:52 PM10/29/16
to v8-users
I've added an enumerator callback.  it is not called.


On Saturday, October 29, 2016 at 3:33:14 AM UTC-7, Zac Hansen wrote:

Zac Hansen

unread,
Oct 30, 2016, 2:49:11 PM10/30/16
to v8-users
I've written a very simple program exercising SetHandlers and get the expected results, with setter being called, so it's clearly something in my real program's particular code, but I've run the debugger as far down into the v8 code as makes any amount of sense to me and don't understand what's going on when it decides not to call the setter callback.

On Saturday, October 29, 2016 at 3:33:14 AM UTC-7, Zac Hansen wrote:

Zac Hansen

unread,
Oct 30, 2016, 6:18:51 PM10/30/16
to v8-users
I've boiled it down to the

LookupIterator::configuration & kPrototypeChain == true 


being the problem.   So, what can cause that to be true?   It seems to be the default unless name->IsPrivate() is true here:


https://github.com/v8/v8/blob/c2a5dc81c7a26579a599ddaf31a7cf84706135b9/src/lookup.h#L329


but I don't know what that means.


On Saturday, October 29, 2016 at 3:33:14 AM UTC-7, Zac Hansen wrote:

Zac Hansen

unread,
Oct 30, 2016, 6:37:41 PM10/30/16
to v8-users
I've reproduced all the complexities I can think of from my real program in a plain v8 api program and cannot reproduce it:



On Saturday, October 29, 2016 at 3:33:14 AM UTC-7, Zac Hansen wrote:

Caitlin Potter

unread,
Oct 30, 2016, 6:43:58 PM10/30/16
to v8-u...@googlegroups.com



Sent from my iPhone
On Oct 30, 2016, at 6:18 PM, Zac Hansen <xax...@gmail.com> wrote:

I've boiled it down to the

LookupIterator::configuration & kPrototypeChain == true 


being the problem.   So, what can cause that to be true?   It seems to be the default unless name->IsPrivate() is true here:


https://github.com/v8/v8/blob/c2a5dc81c7a26579a599ddaf31a7cf84706135b9/src/lookup.h#L329


but I don't know what that means.

IsPrivate() is true if the name is a Symbol with its private field set, equivalent to ECMA262 "internal fields" (like, [[ViewedArrayBuffer]], for example). It looks like it's possible to create such a symbol via the API using v8::Private::New, or the SymbolFor api. These are never looked up in the prototype chain, AFAIK are never exposed to interceptors, and are never exposed to JSProxy hooks.

Hope that helps in some way.

--
--
v8-users mailing list
v8-u...@googlegroups.com
http://groups.google.com/group/v8-users
---
You received this message because you are subscribed to the Google Groups "v8-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to v8-users+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Zac Hansen

unread,
Oct 30, 2016, 6:55:35 PM10/30/16
to v8-users
Well, I'm not doing any Private::New, so it must be something else causing the the configuration to be different :(

Zac Hansen

unread,
Oct 30, 2016, 7:02:04 PM10/30/16
to v8-users
I'm also noticing a difference between the working/non-working code:

Runtime_KeyedStoreIC_Miss


and

Runtime_StoreIC_Miss

But I don' tknow what a Keyed store is vs a non-keyed store.

On Saturday, October 29, 2016 at 3:33:14 AM UTC-7, Zac Hansen wrote:

Zac Hansen

unread,
Oct 30, 2016, 9:10:13 PM10/30/16
to v8-users
Stack traces for the working and non-working versions:



my v8 source is at commit:

a05f85a3db33860f7f9651d904fd4193ee757848


On Saturday, October 29, 2016 at 3:33:14 AM UTC-7, Zac Hansen wrote:

Toon Verwaest

unread,
Oct 31, 2016, 4:39:29 AM10/31/16
to v8-users
It just follows JS property access semantics. And that's complicated.

If you're trying to set a property, but it doesn't exist on the receiver, we need check whether the property exists on the prototype chain. If it does and has a setter, we need to call the setter. If it does and is read-only, we need to throw. If it exists and isn't either of those, that would mask a property up the prototype chain. Hence if we see an interceptor that's not on the receiver, we need to check whether it has the property. If it does, that would mask a non-writable/settable property further up the prototype chain.

So we basically get the attributes to see whether it exists and whether it's writable. That can either be returned through the query callback, or through the getter.

Now for your specific problem; it seems like your simple example must mismatch the actual code you're having. If you don't see your setter being called at all, that just means that you put the setter on Point.prototype, not on instances of Point. Setters for "native data properties"; which includes interceptors; are only invoked on the receiver directly. Only real JS accessors are called through the prototype chain. This again follows the spec.

--

Zac Hansen

unread,
Oct 31, 2016, 6:16:50 AM10/31/16
to v8-users
Thank you for your help.   I mostly understand what you're saying, but I'm not quite clear on what I'm doing that's making holder != this.   Are there any common/semi-common things you can do while wrapping a c++ object in a JS Object's internalfield that causes this to happen?   

When I say new MyType(), that calls into a FunctionTemplate that takes info.This() and puts a C++ object in its internal field.   The "new MyType()" FunctionTemplate can have a bunch of stuff on it's Instance/Prototype templates including accessors, data members, etc, though I thought I filtered most of that out in my test cases.   Is anything I said or maybe something I'm probably doing and didn't say going to cause the holder != this?

Thank you so much.

--Zac

Zac Hansen

unread,
Oct 31, 2016, 6:29:08 AM10/31/16
to v8-users
Never mind.   You were right, I had it on my prototype template.   I had moved it at one point but at some point I must have undid the change without realizing it, because I knew where it belonged.   

Thank you again.

Toon Verwaest

unread,
Oct 31, 2016, 6:29:50 AM10/31/16
to v8-users
I suspect you put the interceptor either on the prototype template or instance template of the prototype. It should be on the instance template of the function template.

Toon Verwaest

unread,
Oct 31, 2016, 6:30:19 AM10/31/16
to v8-u...@googlegroups.com
Ok great :)
Reply all
Reply to author
Forward
0 new messages