The most efficient way to store private values

97 views
Skip to first unread message

Danny Dorfman

unread,
May 5, 2016, 8:46:33 AM5/5/16
to v8-users
Hello there,

I am looking for a time-efficient way to store private (hidden) values in an object. I get key-value pairs
(where key is of type v8::String and value of type v8::Value), and I need to attach them to existing objects.

What I came up with so far - is using v8::Object's SetPrivate() function. The only problem with it, is that this method
takes a v8::Private as its first argument. There is a costly conversion between v8::String to v8::Private (using the ForApi method).
Performance goes down, and after profiling with callgrind, I see that ForApi is taking up too many cycles.

Is there a quicker way to accomplish this? (I am running 4.9)

Regards,
Danny

Jochen Eisinger

unread,
May 6, 2016, 2:58:38 AM5/6/16
to v8-users
You don't have to use ForApi() to create privates, you can create the once and then keep them around as keys.

Another alternative is to maintain a weak map from the objects to your properties.

--
--
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.

Toon Verwaest

unread,
May 6, 2016, 7:45:19 AM5/6/16
to v8-users
You could also just

var symbol = new Private()
o[symbol] = {__proto__:null}
o[symbol]["your key"] = ...
Message has been deleted

Jochen Eisinger

unread,
May 12, 2016, 5:37:13 AM5/12/16
to v8-users
You can e.g. create a v8::NativeWeakMap and map from the object to whatever private value you want to store on it.

The map won't keep the object alive, but if it's GC'd, the entry is deleted automatically.

On Wed, May 11, 2016 at 9:45 AM Danny Dorfman <wilder...@gmail.com> wrote:
Hello Jochen,

I tried caching the keys after creating them using ForApi(), and this is somewhat faster.
Still, I see that the GetPrivate/SetPrivate API's are considerably slower than their Get/Set counterparts.
What did you mean by maintaining a weak map? Can you please be more specific?

Thank you in advance.

Danny Dorfman

unread,
May 16, 2016, 9:54:52 AM5/16/16
to v8-users
Hello again,

I'm trying to use v8::NativeWeakMap on the global object, and I'm getting inconsistent results. It looks like "this" (on the outer scope)
and just using plain variables (on the outer scope) get me to two different places. How is that possible? Here is a sample program:

#include <iostream>
#include <string.h>
#include "libplatform/libplatform.h"
#include "v8/v8.h"

void runit(v8::Isolate *isolate, const char *script_text)
{
  std::cout << "------------------------------------------" << std::endl;
  std::cout << "/" << script_text << "/ <==" << std::endl;
  v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate,script_text);
  v8::Local<v8::Script> script = v8::Script::Compile(source);
  v8::Local<v8::Value> result = script->Run();
  v8::String::Utf8Value ascii(result);
  std::cout << "/" << script_text << "/ ==> " << *ascii << std::endl;
}

v8::Persistent<v8::NativeWeakMap, v8::CopyablePersistentTraits<v8::NativeWeakMap> > gWeakMap;

void GetNamedAccessor(v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info)
{
  std::cout << "[" << std::hex << info.This()->GetIdentityHash() << "] GetNamedAccessor, name=" << *v8::String::Utf8Value(name) << std::endl;
}

void SetNamedAccessor(v8::Local<v8::Name> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
{
  std::cout << "[" << std::hex << info.This()->GetIdentityHash() << "] SetNamedAccessor, name=" << *v8::String::Utf8Value(name)
            << ", value=" << *v8::String::Utf8Value(value) << std::endl;
  auto myWeakMap = v8::Local<v8::NativeWeakMap>::New(info.GetIsolate(),gWeakMap);
  v8::Local<v8::Value> val = myWeakMap->Get(info.This());
  std::cout << "[ weak map holds " << *v8::String::Utf8Value(val) << " ]" << std::endl;
}

class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
 public:
  virtual void* Allocate(size_t length) {
    std::cout << "!!! Allocate runs !!! length = " << length << std::endl;
    void* data = AllocateUninitialized(length);
    return data == NULL ? data : memset(data, 0, length);
  }
  virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
  virtual void Free(void* data, size_t) { free(data); }
};

int main(int argc, char* argv[])
{
  // initialize v8
  v8::V8::InitializeICU();
  v8::V8::InitializeExternalStartupData(argv[0]);
  v8::Platform* platform = v8::platform::CreateDefaultPlatform();
  v8::V8::InitializePlatform(platform);
  v8::V8::Initialize();
  ArrayBufferAllocator allocator;
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = &allocator;
  auto isolate = v8::Isolate::New(create_params);
  v8::Isolate::Scope isolate_scope(isolate);
  v8::HandleScope handle_scope(isolate);
  auto context = v8::Context::New(isolate);
  auto glob = context->Global();
  v8::Context::Scope context_scope(context);

  // set up the map
  auto myWeakMap = v8::NativeWeakMap::New(isolate);
  auto vvvStr = v8::String::NewFromUtf8(isolate,"vvv");
  myWeakMap->Set(glob, vvvStr);
  gWeakMap.Reset(isolate,myWeakMap);

  // create accessor
  auto onStr = v8::String::NewFromUtf8(isolate,"on");
  glob->SetAccessor(onStr, GetNamedAccessor, SetNamedAccessor);

  // do some testing
  runit(isolate,"this.on = 1");
  runit(isolate,"on = 2");
  return 0;
}

When I run it, I get this output:

------------------------------------------
/this.on = 1/ <==
[37cec19f] SetNamedAccessor, name=on, value=1
[ weak map holds vvv ]
/this.on = 1/ ==> 1
------------------------------------------
/on = 2/ <==
[3c587822] SetNamedAccessor, name=on, value=2
[ weak map holds undefined ]
/on = 2/ ==> 2

As you can see, the hash value for the second case is different, and theNativeWeakMap doesn't work at all.
Please note, that the named accessor works properly in both cases.

Any ideas why this is happening?

Danny Dorfman

unread,
May 16, 2016, 11:54:57 PM5/16/16
to v8-users
Okay, I think I have figured it out. The 2nd call goes to global object's prototype, and not the global object per se.
Reply all
Reply to author
Forward
0 new messages