blob: 97de25bd4c15a8bf36843ef5e0311a7f0607f4e4 [file] [log] [blame]
Ben Murdoch014dc512016-03-22 12:00:34 +00001// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <ostream>
6
7#include "src/accessors.h"
8#include "src/compilation-dependencies.h"
9#include "src/compiler/access-info.h"
10#include "src/field-index-inl.h"
Ben Murdoch109988c2016-05-18 11:27:45 +010011#include "src/field-type.h"
Ben Murdoch13e2dad2016-09-16 13:49:30 +010012#include "src/objects-inl.h"
Ben Murdoch014dc512016-03-22 12:00:34 +000013#include "src/type-cache.h"
Ben Murdoch014dc512016-03-22 12:00:34 +000014
15namespace v8 {
16namespace internal {
17namespace compiler {
18
19namespace {
20
21bool CanInlineElementAccess(Handle<Map> map) {
22 if (!map->IsJSObjectMap()) return false;
23 if (map->is_access_check_needed()) return false;
24 if (map->has_indexed_interceptor()) return false;
25 ElementsKind const elements_kind = map->elements_kind();
26 if (IsFastElementsKind(elements_kind)) return true;
27 // TODO(bmeurer): Add support for other elements kind.
Ben Murdochf91f0612016-11-29 16:50:11 +000028 if (elements_kind == UINT8_CLAMPED_ELEMENTS) return false;
29 if (IsFixedTypedArrayElementsKind(elements_kind)) return true;
Ben Murdoch014dc512016-03-22 12:00:34 +000030 return false;
31}
32
33
34bool CanInlinePropertyAccess(Handle<Map> map) {
35 // We can inline property access to prototypes of all primitives, except
36 // the special Oddball ones that have no wrapper counterparts (i.e. Null,
37 // Undefined and TheHole).
38 STATIC_ASSERT(ODDBALL_TYPE == LAST_PRIMITIVE_TYPE);
39 if (map->IsBooleanMap()) return true;
40 if (map->instance_type() < LAST_PRIMITIVE_TYPE) return true;
41 return map->IsJSObjectMap() && !map->is_dictionary_map() &&
42 !map->has_named_interceptor() &&
43 // TODO(verwaest): Whitelist contexts to which we have access.
44 !map->is_access_check_needed();
45}
46
47} // namespace
48
49
50std::ostream& operator<<(std::ostream& os, AccessMode access_mode) {
51 switch (access_mode) {
52 case AccessMode::kLoad:
53 return os << "Load";
54 case AccessMode::kStore:
55 return os << "Store";
56 }
57 UNREACHABLE();
58 return os;
59}
60
Ben Murdochf91f0612016-11-29 16:50:11 +000061ElementAccessInfo::ElementAccessInfo() {}
62
63ElementAccessInfo::ElementAccessInfo(MapList const& receiver_maps,
64 ElementsKind elements_kind)
65 : elements_kind_(elements_kind), receiver_maps_(receiver_maps) {}
Ben Murdoch014dc512016-03-22 12:00:34 +000066
67// static
Ben Murdochf91f0612016-11-29 16:50:11 +000068PropertyAccessInfo PropertyAccessInfo::NotFound(MapList const& receiver_maps,
Ben Murdoch014dc512016-03-22 12:00:34 +000069 MaybeHandle<JSObject> holder) {
Ben Murdochf91f0612016-11-29 16:50:11 +000070 return PropertyAccessInfo(holder, receiver_maps);
Ben Murdoch014dc512016-03-22 12:00:34 +000071}
72
Ben Murdoch014dc512016-03-22 12:00:34 +000073// static
74PropertyAccessInfo PropertyAccessInfo::DataConstant(
Ben Murdochf91f0612016-11-29 16:50:11 +000075 MapList const& receiver_maps, Handle<Object> constant,
Ben Murdoch014dc512016-03-22 12:00:34 +000076 MaybeHandle<JSObject> holder) {
Ben Murdochf91f0612016-11-29 16:50:11 +000077 return PropertyAccessInfo(kDataConstant, holder, constant, receiver_maps);
Ben Murdoch014dc512016-03-22 12:00:34 +000078}
79
Ben Murdoch014dc512016-03-22 12:00:34 +000080// static
81PropertyAccessInfo PropertyAccessInfo::DataField(
Ben Murdochf91f0612016-11-29 16:50:11 +000082 MapList const& receiver_maps, FieldIndex field_index, Type* field_type,
Ben Murdoch13e2dad2016-09-16 13:49:30 +010083 MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map) {
84 return PropertyAccessInfo(holder, transition_map, field_index, field_type,
Ben Murdochf91f0612016-11-29 16:50:11 +000085 receiver_maps);
Ben Murdoch014dc512016-03-22 12:00:34 +000086}
87
Ben Murdochf91f0612016-11-29 16:50:11 +000088// static
89PropertyAccessInfo PropertyAccessInfo::AccessorConstant(
90 MapList const& receiver_maps, Handle<Object> constant,
91 MaybeHandle<JSObject> holder) {
92 return PropertyAccessInfo(kAccessorConstant, holder, constant, receiver_maps);
93}
Ben Murdoch014dc512016-03-22 12:00:34 +000094
95PropertyAccessInfo::PropertyAccessInfo()
Ben Murdochf91f0612016-11-29 16:50:11 +000096 : kind_(kInvalid), field_type_(Type::None()) {}
Ben Murdoch014dc512016-03-22 12:00:34 +000097
98PropertyAccessInfo::PropertyAccessInfo(MaybeHandle<JSObject> holder,
Ben Murdochf91f0612016-11-29 16:50:11 +000099 MapList const& receiver_maps)
Ben Murdoch014dc512016-03-22 12:00:34 +0000100 : kind_(kNotFound),
Ben Murdochf91f0612016-11-29 16:50:11 +0000101 receiver_maps_(receiver_maps),
Ben Murdoch014dc512016-03-22 12:00:34 +0000102 holder_(holder),
Ben Murdochf91f0612016-11-29 16:50:11 +0000103 field_type_(Type::None()) {}
Ben Murdoch014dc512016-03-22 12:00:34 +0000104
Ben Murdochf91f0612016-11-29 16:50:11 +0000105PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
Ben Murdoch014dc512016-03-22 12:00:34 +0000106 Handle<Object> constant,
Ben Murdochf91f0612016-11-29 16:50:11 +0000107 MapList const& receiver_maps)
108 : kind_(kind),
109 receiver_maps_(receiver_maps),
Ben Murdoch014dc512016-03-22 12:00:34 +0000110 constant_(constant),
111 holder_(holder),
112 field_type_(Type::Any()) {}
113
Ben Murdoch014dc512016-03-22 12:00:34 +0000114PropertyAccessInfo::PropertyAccessInfo(MaybeHandle<JSObject> holder,
115 MaybeHandle<Map> transition_map,
Ben Murdoch13e2dad2016-09-16 13:49:30 +0100116 FieldIndex field_index, Type* field_type,
Ben Murdochf91f0612016-11-29 16:50:11 +0000117 MapList const& receiver_maps)
Ben Murdoch014dc512016-03-22 12:00:34 +0000118 : kind_(kDataField),
Ben Murdochf91f0612016-11-29 16:50:11 +0000119 receiver_maps_(receiver_maps),
Ben Murdoch014dc512016-03-22 12:00:34 +0000120 transition_map_(transition_map),
121 holder_(holder),
122 field_index_(field_index),
Ben Murdoch014dc512016-03-22 12:00:34 +0000123 field_type_(field_type) {}
124
Ben Murdochf91f0612016-11-29 16:50:11 +0000125bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that) {
126 if (this->kind_ != that->kind_) return false;
127 if (this->holder_.address() != that->holder_.address()) return false;
128
129 switch (this->kind_) {
130 case kInvalid:
131 break;
132
133 case kNotFound:
134 return true;
135
136 case kDataField: {
137 // Check if we actually access the same field.
138 if (this->transition_map_.address() == that->transition_map_.address() &&
139 this->field_index_ == that->field_index_ &&
140 this->field_type_->Is(that->field_type_) &&
141 that->field_type_->Is(this->field_type_)) {
142 this->receiver_maps_.insert(this->receiver_maps_.end(),
143 that->receiver_maps_.begin(),
144 that->receiver_maps_.end());
145 return true;
146 }
147 return false;
148 }
149
150 case kDataConstant:
151 case kAccessorConstant: {
152 // Check if we actually access the same constant.
153 if (this->constant_.address() == that->constant_.address()) {
154 this->receiver_maps_.insert(this->receiver_maps_.end(),
155 that->receiver_maps_.begin(),
156 that->receiver_maps_.end());
157 return true;
158 }
159 return false;
160 }
161 }
162
163 UNREACHABLE();
164 return false;
165}
166
Ben Murdoch014dc512016-03-22 12:00:34 +0000167AccessInfoFactory::AccessInfoFactory(CompilationDependencies* dependencies,
168 Handle<Context> native_context, Zone* zone)
169 : dependencies_(dependencies),
170 native_context_(native_context),
171 isolate_(native_context->GetIsolate()),
172 type_cache_(TypeCache::Get()),
173 zone_(zone) {
174 DCHECK(native_context->IsNativeContext());
175}
176
177
178bool AccessInfoFactory::ComputeElementAccessInfo(
179 Handle<Map> map, AccessMode access_mode, ElementAccessInfo* access_info) {
180 // Check if it is safe to inline element access for the {map}.
181 if (!CanInlineElementAccess(map)) return false;
Ben Murdoch014dc512016-03-22 12:00:34 +0000182 ElementsKind const elements_kind = map->elements_kind();
Ben Murdochf91f0612016-11-29 16:50:11 +0000183 *access_info = ElementAccessInfo(MapList{map}, elements_kind);
Ben Murdoch014dc512016-03-22 12:00:34 +0000184 return true;
185}
186
187
188bool AccessInfoFactory::ComputeElementAccessInfos(
189 MapHandleList const& maps, AccessMode access_mode,
190 ZoneVector<ElementAccessInfo>* access_infos) {
191 // Collect possible transition targets.
192 MapHandleList possible_transition_targets(maps.length());
193 for (Handle<Map> map : maps) {
194 if (Map::TryUpdate(map).ToHandle(&map)) {
195 if (CanInlineElementAccess(map) &&
196 IsFastElementsKind(map->elements_kind()) &&
197 GetInitialFastElementsKind() != map->elements_kind()) {
198 possible_transition_targets.Add(map);
199 }
200 }
201 }
202
203 // Separate the actual receiver maps and the possible transition sources.
204 MapHandleList receiver_maps(maps.length());
205 MapTransitionList transitions(maps.length());
206 for (Handle<Map> map : maps) {
207 if (Map::TryUpdate(map).ToHandle(&map)) {
Ben Murdochbcf72ee2016-08-08 18:44:38 +0100208 Map* transition_target =
209 map->FindElementsKindTransitionedMap(&possible_transition_targets);
210 if (transition_target == nullptr) {
Ben Murdoch014dc512016-03-22 12:00:34 +0000211 receiver_maps.Add(map);
212 } else {
Ben Murdochbcf72ee2016-08-08 18:44:38 +0100213 transitions.push_back(std::make_pair(map, handle(transition_target)));
Ben Murdoch014dc512016-03-22 12:00:34 +0000214 }
215 }
216 }
217
218 for (Handle<Map> receiver_map : receiver_maps) {
219 // Compute the element access information.
220 ElementAccessInfo access_info;
221 if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
222 return false;
223 }
224
225 // Collect the possible transitions for the {receiver_map}.
226 for (auto transition : transitions) {
227 if (transition.second.is_identical_to(receiver_map)) {
228 access_info.transitions().push_back(transition);
229 }
230 }
231
232 // Schedule the access information.
233 access_infos->push_back(access_info);
234 }
235 return true;
236}
237
238
239bool AccessInfoFactory::ComputePropertyAccessInfo(
240 Handle<Map> map, Handle<Name> name, AccessMode access_mode,
241 PropertyAccessInfo* access_info) {
242 // Check if it is safe to inline property access for the {map}.
243 if (!CanInlinePropertyAccess(map)) return false;
244
245 // Compute the receiver type.
246 Handle<Map> receiver_map = map;
247
Ben Murdoch109988c2016-05-18 11:27:45 +0100248 // Property lookups require the name to be internalized.
249 name = isolate()->factory()->InternalizeName(name);
250
Ben Murdoch014dc512016-03-22 12:00:34 +0000251 // We support fast inline cases for certain JSObject getters.
252 if (access_mode == AccessMode::kLoad &&
253 LookupSpecialFieldAccessor(map, name, access_info)) {
254 return true;
255 }
256
257 MaybeHandle<JSObject> holder;
258 do {
259 // Lookup the named property on the {map}.
260 Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
Ben Murdoch109988c2016-05-18 11:27:45 +0100261 int const number = descriptors->SearchWithCache(isolate(), *name, *map);
Ben Murdoch014dc512016-03-22 12:00:34 +0000262 if (number != DescriptorArray::kNotFound) {
263 PropertyDetails const details = descriptors->GetDetails(number);
264 if (access_mode == AccessMode::kStore) {
265 // Don't bother optimizing stores to read-only properties.
266 if (details.IsReadOnly()) {
267 return false;
268 }
269 // Check for store to data property on a prototype.
270 if (details.kind() == kData && !holder.is_null()) {
271 // Store to property not found on the receiver but on a prototype, we
272 // need to transition to a new data property.
273 // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
274 return LookupTransition(receiver_map, name, holder, access_info);
275 }
276 }
Ben Murdochf91f0612016-11-29 16:50:11 +0000277 switch (details.type()) {
278 case DATA_CONSTANT: {
279 *access_info = PropertyAccessInfo::DataConstant(
280 MapList{receiver_map},
281 handle(descriptors->GetValue(number), isolate()), holder);
282 return true;
Ben Murdoch014dc512016-03-22 12:00:34 +0000283 }
Ben Murdochf91f0612016-11-29 16:50:11 +0000284 case DATA: {
285 int index = descriptors->GetFieldIndex(number);
286 Representation field_representation = details.representation();
287 FieldIndex field_index = FieldIndex::ForPropertyIndex(
288 *map, index, field_representation.IsDouble());
289 Type* field_type = Type::Tagged();
290 if (field_representation.IsSmi()) {
291 field_type = type_cache_.kSmi;
292 } else if (field_representation.IsDouble()) {
293 field_type = type_cache_.kFloat64;
294 } else if (field_representation.IsHeapObject()) {
295 // Extract the field type from the property details (make sure its
296 // representation is TaggedPointer to reflect the heap object case).
297 field_type = Type::Intersect(
298 descriptors->GetFieldType(number)->Convert(zone()),
299 Type::TaggedPointer(), zone());
300 if (field_type->Is(Type::None())) {
301 // Store is not safe if the field type was cleared.
302 if (access_mode == AccessMode::kStore) return false;
303
304 // The field type was cleared by the GC, so we don't know anything
305 // about the contents now.
306 // TODO(bmeurer): It would be awesome to make this saner in the
307 // runtime/GC interaction.
308 field_type = Type::TaggedPointer();
309 } else if (!Type::Any()->Is(field_type)) {
310 // Add proper code dependencies in case of stable field map(s).
311 Handle<Map> field_owner_map(map->FindFieldOwner(number),
312 isolate());
313 dependencies()->AssumeFieldType(field_owner_map);
314 }
315 if (access_mode == AccessMode::kLoad) {
316 field_type = Type::Any();
317 }
318 }
319 *access_info = PropertyAccessInfo::DataField(
320 MapList{receiver_map}, field_index, field_type, holder);
321 return true;
322 }
323 case ACCESSOR_CONSTANT: {
324 Handle<Object> accessors(descriptors->GetValue(number), isolate());
325 if (!accessors->IsAccessorPair()) return false;
326 Handle<Object> accessor(
327 access_mode == AccessMode::kLoad
328 ? Handle<AccessorPair>::cast(accessors)->getter()
329 : Handle<AccessorPair>::cast(accessors)->setter(),
330 isolate());
331 if (!accessor->IsJSFunction()) {
332 // TODO(turbofan): Add support for API accessors.
333 return false;
334 }
335 *access_info = PropertyAccessInfo::AccessorConstant(
336 MapList{receiver_map}, accessor, holder);
337 return true;
338 }
339 case ACCESSOR: {
340 // TODO(turbofan): Add support for general accessors?
341 return false;
342 }
Ben Murdoch014dc512016-03-22 12:00:34 +0000343 }
Ben Murdochf91f0612016-11-29 16:50:11 +0000344 UNREACHABLE();
345 return false;
Ben Murdoch014dc512016-03-22 12:00:34 +0000346 }
347
348 // Don't search on the prototype chain for special indices in case of
349 // integer indexed exotic objects (see ES6 section 9.4.5).
350 if (map->IsJSTypedArrayMap() && name->IsString() &&
351 IsSpecialIndex(isolate()->unicode_cache(), String::cast(*name))) {
352 return false;
353 }
354
355 // Don't lookup private symbols on the prototype chain.
356 if (name->IsPrivate()) return false;
357
358 // Walk up the prototype chain.
359 if (!map->prototype()->IsJSObject()) {
360 // Perform the implicit ToObject for primitives here.
361 // Implemented according to ES6 section 7.3.2 GetV (V, P).
362 Handle<JSFunction> constructor;
363 if (Map::GetConstructorFunction(map, native_context())
364 .ToHandle(&constructor)) {
365 map = handle(constructor->initial_map(), isolate());
366 DCHECK(map->prototype()->IsJSObject());
Ben Murdoch13e2dad2016-09-16 13:49:30 +0100367 } else if (map->prototype()->IsNull(isolate())) {
Ben Murdoch014dc512016-03-22 12:00:34 +0000368 // Store to property not found on the receiver or any prototype, we need
369 // to transition to a new data property.
370 // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
371 if (access_mode == AccessMode::kStore) {
372 return LookupTransition(receiver_map, name, holder, access_info);
373 }
374 // The property was not found, return undefined or throw depending
375 // on the language mode of the load operation.
376 // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver)
Ben Murdochf91f0612016-11-29 16:50:11 +0000377 *access_info =
378 PropertyAccessInfo::NotFound(MapList{receiver_map}, holder);
Ben Murdoch014dc512016-03-22 12:00:34 +0000379 return true;
380 } else {
381 return false;
382 }
383 }
384 Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
385 if (map_prototype->map()->is_deprecated()) {
386 // Try to migrate the prototype object so we don't embed the deprecated
387 // map into the optimized code.
388 JSObject::TryMigrateInstance(map_prototype);
389 }
390 map = handle(map_prototype->map(), isolate());
391 holder = map_prototype;
392 } while (CanInlinePropertyAccess(map));
393 return false;
394}
395
Ben Murdoch014dc512016-03-22 12:00:34 +0000396bool AccessInfoFactory::ComputePropertyAccessInfos(
397 MapHandleList const& maps, Handle<Name> name, AccessMode access_mode,
398 ZoneVector<PropertyAccessInfo>* access_infos) {
399 for (Handle<Map> map : maps) {
400 if (Map::TryUpdate(map).ToHandle(&map)) {
401 PropertyAccessInfo access_info;
402 if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) {
403 return false;
404 }
Ben Murdochf91f0612016-11-29 16:50:11 +0000405 // Try to merge the {access_info} with an existing one.
406 bool merged = false;
407 for (PropertyAccessInfo& other_info : *access_infos) {
408 if (other_info.Merge(&access_info)) {
409 merged = true;
410 break;
411 }
412 }
413 if (!merged) access_infos->push_back(access_info);
Ben Murdoch014dc512016-03-22 12:00:34 +0000414 }
415 }
416 return true;
417}
418
419
420bool AccessInfoFactory::LookupSpecialFieldAccessor(
421 Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) {
422 // Check for special JSObject field accessors.
423 int offset;
424 if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) {
425 FieldIndex field_index = FieldIndex::ForInObjectOffset(offset);
426 Type* field_type = Type::Tagged();
427 if (map->IsStringMap()) {
428 DCHECK(Name::Equals(factory()->length_string(), name));
429 // The String::length property is always a smi in the range
430 // [0, String::kMaxLength].
431 field_type = type_cache_.kStringLengthType;
432 } else if (map->IsJSArrayMap()) {
433 DCHECK(Name::Equals(factory()->length_string(), name));
434 // The JSArray::length property is a smi in the range
435 // [0, FixedDoubleArray::kMaxLength] in case of fast double
436 // elements, a smi in the range [0, FixedArray::kMaxLength]
437 // in case of other fast elements, and [0, kMaxUInt32] in
438 // case of other arrays.
439 if (IsFastDoubleElementsKind(map->elements_kind())) {
440 field_type = type_cache_.kFixedDoubleArrayLengthType;
441 } else if (IsFastElementsKind(map->elements_kind())) {
442 field_type = type_cache_.kFixedArrayLengthType;
443 } else {
444 field_type = type_cache_.kJSArrayLengthType;
445 }
446 }
Ben Murdochf91f0612016-11-29 16:50:11 +0000447 *access_info =
448 PropertyAccessInfo::DataField(MapList{map}, field_index, field_type);
Ben Murdoch014dc512016-03-22 12:00:34 +0000449 return true;
450 }
Ben Murdoch014dc512016-03-22 12:00:34 +0000451 return false;
452}
453
454
455bool AccessInfoFactory::LookupTransition(Handle<Map> map, Handle<Name> name,
456 MaybeHandle<JSObject> holder,
457 PropertyAccessInfo* access_info) {
458 // Check if the {map} has a data transition with the given {name}.
459 if (map->unused_property_fields() == 0) return false;
460 Handle<Map> transition_map;
461 if (TransitionArray::SearchTransition(map, kData, name, NONE)
462 .ToHandle(&transition_map)) {
463 int const number = transition_map->LastAdded();
464 PropertyDetails const details =
465 transition_map->instance_descriptors()->GetDetails(number);
466 // Don't bother optimizing stores to read-only properties.
467 if (details.IsReadOnly()) return false;
468 // TODO(bmeurer): Handle transition to data constant?
469 if (details.type() != DATA) return false;
470 int const index = details.field_index();
471 Representation field_representation = details.representation();
472 FieldIndex field_index = FieldIndex::ForPropertyIndex(
473 *transition_map, index, field_representation.IsDouble());
474 Type* field_type = Type::Tagged();
475 if (field_representation.IsSmi()) {
476 field_type = type_cache_.kSmi;
477 } else if (field_representation.IsDouble()) {
478 field_type = type_cache_.kFloat64;
479 } else if (field_representation.IsHeapObject()) {
480 // Extract the field type from the property details (make sure its
481 // representation is TaggedPointer to reflect the heap object case).
482 field_type = Type::Intersect(
Ben Murdoch109988c2016-05-18 11:27:45 +0100483 transition_map->instance_descriptors()->GetFieldType(number)->Convert(
Ben Murdoch014dc512016-03-22 12:00:34 +0000484 zone()),
485 Type::TaggedPointer(), zone());
486 if (field_type->Is(Type::None())) {
487 // Store is not safe if the field type was cleared.
488 return false;
489 } else if (!Type::Any()->Is(field_type)) {
490 // Add proper code dependencies in case of stable field map(s).
491 Handle<Map> field_owner_map(transition_map->FindFieldOwner(number),
492 isolate());
493 dependencies()->AssumeFieldType(field_owner_map);
494 }
495 DCHECK(field_type->Is(Type::TaggedPointer()));
496 }
497 dependencies()->AssumeMapNotDeprecated(transition_map);
Ben Murdochf91f0612016-11-29 16:50:11 +0000498 *access_info = PropertyAccessInfo::DataField(
499 MapList{map}, field_index, field_type, holder, transition_map);
Ben Murdoch014dc512016-03-22 12:00:34 +0000500 return true;
501 }
502 return false;
503}
504
505
506Factory* AccessInfoFactory::factory() const { return isolate()->factory(); }
507
508} // namespace compiler
509} // namespace internal
510} // namespace v8