Commit 7303633b authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[api] Introduce Object::New() pendant of Object.create().

This adds a new C++ API method

```cpp
Local<Object> Object::New(
  Isolate* isolate,
  Local<Value> prototype_or_null,
  Local<Name>* keys,
  Local<Value>* values,
  size_t size);
```

which is similar to the `Object.create()` builtin exposed by JavaScript.
This new API is supposed to be used by the `http2` (in Node.js) to speed
up the creation of the HTTP header object.

Bug: v8:8422
Change-Id: I9910e88de0af2cbd8ce8a1d6cb6caa9451fb8cb4
Design-Document: http://bit.ly/v8-fast-object-create-cpp
Reviewed-on: https://chromium-review.googlesource.com/c/1337569
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57570}
parent f2ea65d9
......@@ -3650,6 +3650,18 @@ class V8_EXPORT Object : public Value {
static Local<Object> New(Isolate* isolate);
/**
* Creates a JavaScript object with the given properties, and
* a the given prototype_or_null (which can be any JavaScript
* value, and if it's null, the newly created object won't have
* a prototype at all). This is similar to Object.create().
* All properties will be created as enumerable, configurable
* and writable properties.
*/
static Local<Object> New(Isolate* isolate, Local<Value> prototype_or_null,
Local<Name>* names, Local<Value>* values,
size_t length);
V8_INLINE static Object* Cast(Value* obj);
private:
......
......@@ -51,6 +51,7 @@
#include "src/messages.h"
#include "src/objects-inl.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/heap-object.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-collection-inl.h"
......@@ -6691,6 +6692,64 @@ Local<v8::Object> v8::Object::New(Isolate* isolate) {
return Utils::ToLocal(obj);
}
Local<v8::Object> v8::Object::New(Isolate* isolate,
Local<Value> prototype_or_null,
Local<Name>* names, Local<Value>* values,
size_t length) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Handle<i::Object> proto = Utils::OpenHandle(*prototype_or_null);
if (!Utils::ApiCheck(proto->IsNull() || proto->IsJSReceiver(),
"v8::Object::New", "prototype must be null or object")) {
return Local<v8::Object>();
}
LOG_API(i_isolate, Object, New);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
// We assume that this API is mostly used to create objects with named
// properties, and so we default to creating a properties backing store
// large enough to hold all of them, while we start with no elements
// (see http://bit.ly/v8-fast-object-create-cpp for the motivation).
i::Handle<i::NameDictionary> properties =
i::NameDictionary::New(i_isolate, static_cast<int>(length));
i::Handle<i::FixedArrayBase> elements =
i_isolate->factory()->empty_fixed_array();
for (size_t i = 0; i < length; ++i) {
i::Handle<i::Name> name = Utils::OpenHandle(*names[i]);
i::Handle<i::Object> value = Utils::OpenHandle(*values[i]);
// See if the {name} is a valid array index, in which case we need to
// add the {name}/{value} pair to the {elements}, otherwise they end
// up in the {properties} backing store.
uint32_t index;
if (name->AsArrayIndex(&index)) {
// If this is the first element, allocate a proper
// dictionary elements backing store for {elements}.
if (!elements->IsNumberDictionary()) {
elements =
i::NumberDictionary::New(i_isolate, static_cast<int>(length));
}
elements = i::NumberDictionary::Set(
i_isolate, i::Handle<i::NumberDictionary>::cast(elements), index,
value);
} else {
// Internalize the {name} first.
name = i_isolate->factory()->InternalizeName(name);
int const entry = properties->FindEntry(i_isolate, name);
if (entry == i::NameDictionary::kNotFound) {
// Add the {name}/{value} pair as a new entry.
properties = i::NameDictionary::Add(i_isolate, properties, name, value,
i::PropertyDetails::Empty());
} else {
// Overwrite the {entry} with the {value}.
properties->ValueAtPut(entry, *value);
}
}
}
i::Handle<i::JSObject> obj =
i_isolate->factory()->NewSlowJSObjectWithPropertiesAndElements(
proto, properties, elements);
return Utils::ToLocal(obj);
}
Local<v8::Value> v8::NumberObject::New(Isolate* isolate, double value) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
......
......@@ -2942,6 +2942,26 @@ Handle<JSObject> Factory::NewSlowJSObjectFromMap(Handle<Map> map, int capacity,
return js_object;
}
Handle<JSObject> Factory::NewSlowJSObjectWithPropertiesAndElements(
Handle<Object> prototype, Handle<NameDictionary> properties,
Handle<FixedArrayBase> elements, PretenureFlag pretenure) {
Handle<Map> object_map = isolate()->slow_object_with_object_prototype_map();
if (object_map->prototype() != *prototype) {
object_map = Map::TransitionToPrototype(isolate(), object_map, prototype);
}
DCHECK(object_map->is_dictionary_map());
Handle<JSObject> object = NewJSObjectFromMap(object_map, pretenure);
object->set_raw_properties_or_hash(*properties);
if (*elements != ReadOnlyRoots(isolate()).empty_fixed_array()) {
DCHECK(elements->IsNumberDictionary());
object_map =
JSObject::GetElementsTransitionMap(object, DICTIONARY_ELEMENTS);
JSObject::MigrateToMap(object, object_map);
object->set_elements(*elements);
}
return object;
}
Handle<JSArray> Factory::NewJSArray(ElementsKind elements_kind,
PretenureFlag pretenure) {
NativeContext* native_context = isolate()->raw_native_context();
......
......@@ -600,6 +600,15 @@ class V8_EXPORT_PRIVATE Factory {
Handle<Map> map,
int number_of_slow_properties = NameDictionary::kInitialCapacity,
PretenureFlag pretenure = NOT_TENURED);
// Allocates and initializes a new JavaScript object with the given
// {prototype} and {properties}. The newly created object will be
// in dictionary properties mode. The {elements} can either be the
// empty fixed array, in which case the resulting object will have
// fast elements, or a NumberDictionary, in which case the resulting
// object will have dictionary elements.
Handle<JSObject> NewSlowJSObjectWithPropertiesAndElements(
Handle<Object> prototype, Handle<NameDictionary> properties,
Handle<FixedArrayBase> elements, PretenureFlag pretenure = NOT_TENURED);
// JS arrays are pretenured when allocated by the parser.
......
......@@ -24525,6 +24525,143 @@ THREADED_TEST(FunctionNew) {
CHECK(v8::Integer::New(isolate, 17)->Equals(env.local(), result2).FromJust());
}
namespace {
void Verify(v8::Isolate* isolate, Local<v8::Object> obj) {
#if VERIFY_HEAP
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Handle<i::JSReceiver> i_obj = v8::Utils::OpenHandle(*obj);
i_obj->ObjectVerify(i_isolate);
#endif
}
} // namespace
THREADED_TEST(ObjectNew) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
{
// Verify that Object::New(null) produces an object with a null
// [[Prototype]].
Local<v8::Object> obj =
v8::Object::New(isolate, v8::Null(isolate), nullptr, nullptr, 0);
CHECK(obj->GetPrototype()->IsNull());
Verify(isolate, obj);
Local<Array> keys = obj->GetOwnPropertyNames(env.local()).ToLocalChecked();
CHECK_EQ(0, keys->Length());
}
{
// Verify that Object::New(proto) produces an object with
// proto as it's [[Prototype]].
Local<v8::Object> proto = v8::Object::New(isolate);
Local<v8::Object> obj =
v8::Object::New(isolate, proto, nullptr, nullptr, 0);
Verify(isolate, obj);
CHECK(obj->GetPrototype()->SameValue(proto));
}
{
// Verify that the properties are installed correctly.
Local<v8::Name> names[3] = {v8_str("a"), v8_str("b"), v8_str("c")};
Local<v8::Value> values[3] = {v8_num(1), v8_num(2), v8_num(3)};
Local<v8::Object> obj = v8::Object::New(isolate, v8::Null(isolate), names,
values, arraysize(values));
Verify(isolate, obj);
Local<Array> keys = obj->GetOwnPropertyNames(env.local()).ToLocalChecked();
CHECK_EQ(arraysize(names), keys->Length());
for (uint32_t i = 0; i < arraysize(names); ++i) {
CHECK(names[i]->SameValue(keys->Get(env.local(), i).ToLocalChecked()));
CHECK(values[i]->SameValue(
obj->Get(env.local(), names[i]).ToLocalChecked()));
}
}
{
// Same as above, but with non-null prototype.
Local<v8::Object> proto = v8::Object::New(isolate);
Local<v8::Name> names[3] = {v8_str("x"), v8_str("y"), v8_str("z")};
Local<v8::Value> values[3] = {v8_num(1), v8_num(2), v8_num(3)};
Local<v8::Object> obj =
v8::Object::New(isolate, proto, names, values, arraysize(values));
CHECK(obj->GetPrototype()->SameValue(proto));
Verify(isolate, obj);
Local<Array> keys = obj->GetOwnPropertyNames(env.local()).ToLocalChecked();
CHECK_EQ(arraysize(names), keys->Length());
for (uint32_t i = 0; i < arraysize(names); ++i) {
CHECK(names[i]->SameValue(keys->Get(env.local(), i).ToLocalChecked()));
CHECK(values[i]->SameValue(
obj->Get(env.local(), names[i]).ToLocalChecked()));
}
}
{
// This has to work with duplicate names too.
Local<v8::Name> names[3] = {v8_str("a"), v8_str("a"), v8_str("a")};
Local<v8::Value> values[3] = {v8_num(1), v8_num(2), v8_num(3)};
Local<v8::Object> obj = v8::Object::New(isolate, v8::Null(isolate), names,
values, arraysize(values));
Verify(isolate, obj);
Local<Array> keys = obj->GetOwnPropertyNames(env.local()).ToLocalChecked();
CHECK_EQ(1, keys->Length());
CHECK(v8_str("a")->SameValue(keys->Get(env.local(), 0).ToLocalChecked()));
CHECK(v8_num(3)->SameValue(
obj->Get(env.local(), v8_str("a")).ToLocalChecked()));
}
{
// This has to work with array indices too.
Local<v8::Name> names[2] = {v8_str("0"), v8_str("1")};
Local<v8::Value> values[2] = {v8_num(0), v8_num(1)};
Local<v8::Object> obj = v8::Object::New(isolate, v8::Null(isolate), names,
values, arraysize(values));
Verify(isolate, obj);
Local<Array> keys = obj->GetOwnPropertyNames(env.local()).ToLocalChecked();
CHECK_EQ(arraysize(names), keys->Length());
for (uint32_t i = 0; i < arraysize(names); ++i) {
CHECK(v8::Number::New(isolate, i)
->SameValue(keys->Get(env.local(), i).ToLocalChecked()));
CHECK(values[i]->SameValue(obj->Get(env.local(), i).ToLocalChecked()));
}
}
{
// This has to work with mixed array indices / property names too.
Local<v8::Name> names[2] = {v8_str("0"), v8_str("x")};
Local<v8::Value> values[2] = {v8_num(42), v8_num(24)};
Local<v8::Object> obj = v8::Object::New(isolate, v8::Null(isolate), names,
values, arraysize(values));
Verify(isolate, obj);
Local<Array> keys = obj->GetOwnPropertyNames(env.local()).ToLocalChecked();
CHECK_EQ(arraysize(names), keys->Length());
// 0 -> 42
CHECK(v8_num(0)->SameValue(keys->Get(env.local(), 0).ToLocalChecked()));
CHECK(
values[0]->SameValue(obj->Get(env.local(), names[0]).ToLocalChecked()));
// "x" -> 24
CHECK(v8_str("x")->SameValue(keys->Get(env.local(), 1).ToLocalChecked()));
CHECK(
values[1]->SameValue(obj->Get(env.local(), names[1]).ToLocalChecked()));
}
{
// Verify that this also works for a couple thousand properties.
size_t const kLength = 10 * 1024;
Local<v8::Name> names[kLength];
Local<v8::Value> values[kLength];
for (size_t i = 0; i < arraysize(names); ++i) {
std::ostringstream ost;
ost << "a" << i;
names[i] = v8_str(ost.str().c_str());
values[i] = v8_num(static_cast<double>(i));
}
Local<v8::Object> obj = v8::Object::New(isolate, v8::Null(isolate), names,
values, arraysize(names));
Verify(isolate, obj);
Local<Array> keys = obj->GetOwnPropertyNames(env.local()).ToLocalChecked();
CHECK_EQ(arraysize(names), keys->Length());
for (uint32_t i = 0; i < arraysize(names); ++i) {
CHECK(names[i]->SameValue(keys->Get(env.local(), i).ToLocalChecked()));
CHECK(values[i]->SameValue(
obj->Get(env.local(), names[i]).ToLocalChecked()));
}
}
}
TEST(EscapableHandleScope) {
HandleScope outer_scope(CcTest::isolate());
LocalContext context;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment