1 // 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 "src/api-natives.h" 6 7 #include "src/api.h" 8 #include "src/isolate-inl.h" 9 #include "src/lookup.h" 10 #include "src/messages.h" 11 12 namespace v8 { 13 namespace internal { 14 15 16 namespace { 17 18 MaybeHandle<JSObject> InstantiateObject(Isolate* isolate, 19 Handle<ObjectTemplateInfo> data); 20 21 22 MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate, 23 Handle<FunctionTemplateInfo> data, 24 Handle<Name> name = Handle<Name>()); 25 26 27 MaybeHandle<Object> Instantiate(Isolate* isolate, Handle<Object> data, 28 Handle<Name> name = Handle<Name>()) { 29 if (data->IsFunctionTemplateInfo()) { 30 return InstantiateFunction(isolate, 31 Handle<FunctionTemplateInfo>::cast(data), name); 32 } else if (data->IsObjectTemplateInfo()) { 33 return InstantiateObject(isolate, Handle<ObjectTemplateInfo>::cast(data)); 34 } else { 35 return data; 36 } 37 } 38 39 40 MaybeHandle<Object> DefineAccessorProperty(Isolate* isolate, 41 Handle<JSObject> object, 42 Handle<Name> name, 43 Handle<Object> getter, 44 Handle<Object> setter, 45 PropertyAttributes attributes) { 46 if (!getter->IsUndefined()) { 47 ASSIGN_RETURN_ON_EXCEPTION( 48 isolate, getter, 49 InstantiateFunction(isolate, 50 Handle<FunctionTemplateInfo>::cast(getter)), 51 Object); 52 } 53 if (!setter->IsUndefined()) { 54 ASSIGN_RETURN_ON_EXCEPTION( 55 isolate, setter, 56 InstantiateFunction(isolate, 57 Handle<FunctionTemplateInfo>::cast(setter)), 58 Object); 59 } 60 RETURN_ON_EXCEPTION(isolate, JSObject::DefineAccessor(object, name, getter, 61 setter, attributes), 62 Object); 63 return object; 64 } 65 66 67 MaybeHandle<Object> DefineDataProperty(Isolate* isolate, 68 Handle<JSObject> object, 69 Handle<Name> name, 70 Handle<Object> prop_data, 71 PropertyAttributes attributes) { 72 Handle<Object> value; 73 ASSIGN_RETURN_ON_EXCEPTION(isolate, value, 74 Instantiate(isolate, prop_data, name), Object); 75 76 LookupIterator it = LookupIterator::PropertyOrElement( 77 isolate, object, name, LookupIterator::OWN_SKIP_INTERCEPTOR); 78 79 #ifdef DEBUG 80 Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it); 81 DCHECK(maybe.IsJust()); 82 if (it.IsFound()) { 83 THROW_NEW_ERROR( 84 isolate, 85 NewTypeError(MessageTemplate::kDuplicateTemplateProperty, name), 86 Object); 87 } 88 #endif 89 90 MAYBE_RETURN_NULL( 91 Object::AddDataProperty(&it, value, attributes, Object::THROW_ON_ERROR, 92 Object::CERTAINLY_NOT_STORE_FROM_KEYED)); 93 return value; 94 } 95 96 97 void DisableAccessChecks(Isolate* isolate, Handle<JSObject> object) { 98 Handle<Map> old_map(object->map()); 99 // Copy map so it won't interfere constructor's initial map. 100 Handle<Map> new_map = Map::Copy(old_map, "DisableAccessChecks"); 101 new_map->set_is_access_check_needed(false); 102 JSObject::MigrateToMap(Handle<JSObject>::cast(object), new_map); 103 } 104 105 106 void EnableAccessChecks(Isolate* isolate, Handle<JSObject> object) { 107 Handle<Map> old_map(object->map()); 108 // Copy map so it won't interfere constructor's initial map. 109 Handle<Map> new_map = Map::Copy(old_map, "EnableAccessChecks"); 110 new_map->set_is_access_check_needed(true); 111 JSObject::MigrateToMap(object, new_map); 112 } 113 114 115 class AccessCheckDisableScope { 116 public: 117 AccessCheckDisableScope(Isolate* isolate, Handle<JSObject> obj) 118 : isolate_(isolate), 119 disabled_(obj->map()->is_access_check_needed()), 120 obj_(obj) { 121 if (disabled_) { 122 DisableAccessChecks(isolate_, obj_); 123 } 124 } 125 ~AccessCheckDisableScope() { 126 if (disabled_) { 127 EnableAccessChecks(isolate_, obj_); 128 } 129 } 130 131 private: 132 Isolate* isolate_; 133 const bool disabled_; 134 Handle<JSObject> obj_; 135 }; 136 137 138 Object* GetIntrinsic(Isolate* isolate, v8::Intrinsic intrinsic) { 139 Handle<Context> native_context = isolate->native_context(); 140 DCHECK(!native_context.is_null()); 141 switch (intrinsic) { 142 #define GET_INTRINSIC_VALUE(name, iname) \ 143 case v8::k##name: \ 144 return native_context->iname(); 145 V8_INTRINSICS_LIST(GET_INTRINSIC_VALUE) 146 #undef GET_INTRINSIC_VALUE 147 } 148 return nullptr; 149 } 150 151 152 MaybeHandle<JSObject> ConfigureInstance(Isolate* isolate, Handle<JSObject> obj, 153 Handle<TemplateInfo> data) { 154 auto property_list = handle(data->property_list(), isolate); 155 if (property_list->IsUndefined()) return obj; 156 // TODO(dcarney): just use a FixedArray here. 157 NeanderArray properties(property_list); 158 if (properties.length() == 0) return obj; 159 HandleScope scope(isolate); 160 // Disable access checks while instantiating the object. 161 AccessCheckDisableScope access_check_scope(isolate, obj); 162 163 int i = 0; 164 for (int c = 0; c < data->number_of_properties(); c++) { 165 auto name = handle(Name::cast(properties.get(i++)), isolate); 166 auto bit = handle(properties.get(i++), isolate); 167 if (bit->IsSmi()) { 168 PropertyDetails details(Smi::cast(*bit)); 169 PropertyAttributes attributes = details.attributes(); 170 PropertyKind kind = details.kind(); 171 172 if (kind == kData) { 173 auto prop_data = handle(properties.get(i++), isolate); 174 175 RETURN_ON_EXCEPTION(isolate, DefineDataProperty(isolate, obj, name, 176 prop_data, attributes), 177 JSObject); 178 } else { 179 auto getter = handle(properties.get(i++), isolate); 180 auto setter = handle(properties.get(i++), isolate); 181 RETURN_ON_EXCEPTION(isolate, 182 DefineAccessorProperty(isolate, obj, name, getter, 183 setter, attributes), 184 JSObject); 185 } 186 } else { 187 // Intrinsic data property --- Get appropriate value from the current 188 // context. 189 PropertyDetails details(Smi::cast(properties.get(i++))); 190 PropertyAttributes attributes = details.attributes(); 191 DCHECK_EQ(kData, details.kind()); 192 193 v8::Intrinsic intrinsic = 194 static_cast<v8::Intrinsic>(Smi::cast(properties.get(i++))->value()); 195 auto prop_data = handle(GetIntrinsic(isolate, intrinsic), isolate); 196 197 RETURN_ON_EXCEPTION(isolate, DefineDataProperty(isolate, obj, name, 198 prop_data, attributes), 199 JSObject); 200 } 201 } 202 return obj; 203 } 204 205 206 MaybeHandle<JSObject> InstantiateObject(Isolate* isolate, 207 Handle<ObjectTemplateInfo> data) { 208 // Enter a new scope. Recursion could otherwise create a lot of handles. 209 HandleScope scope(isolate); 210 // Fast path. 211 Handle<JSObject> result; 212 auto info = Handle<ObjectTemplateInfo>::cast(data); 213 auto constructor = handle(info->constructor(), isolate); 214 Handle<JSFunction> cons; 215 if (constructor->IsUndefined()) { 216 cons = isolate->object_function(); 217 } else { 218 auto cons_templ = Handle<FunctionTemplateInfo>::cast(constructor); 219 ASSIGN_RETURN_ON_EXCEPTION( 220 isolate, cons, InstantiateFunction(isolate, cons_templ), JSFunction); 221 } 222 auto object = isolate->factory()->NewJSObject(cons); 223 ASSIGN_RETURN_ON_EXCEPTION( 224 isolate, result, ConfigureInstance(isolate, object, info), JSFunction); 225 // TODO(dcarney): is this necessary? 226 JSObject::MigrateSlowToFast(result, 0, "ApiNatives::InstantiateObject"); 227 return scope.CloseAndEscape(result); 228 } 229 230 231 void CacheFunction(Isolate* isolate, Handle<Smi> serial_number, 232 Handle<JSFunction> function) { 233 auto cache = isolate->function_cache(); 234 auto new_cache = ObjectHashTable::Put(cache, serial_number, function); 235 isolate->native_context()->set_function_cache(*new_cache); 236 } 237 238 239 void UncacheFunction(Isolate* isolate, Handle<Smi> serial_number) { 240 auto cache = isolate->function_cache(); 241 bool was_present = false; 242 auto new_cache = ObjectHashTable::Remove(cache, serial_number, &was_present); 243 DCHECK(was_present); 244 isolate->native_context()->set_function_cache(*new_cache); 245 } 246 247 248 MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate, 249 Handle<FunctionTemplateInfo> data, 250 Handle<Name> name) { 251 auto serial_number = handle(Smi::cast(data->serial_number()), isolate); 252 // Probe cache. 253 if (!data->do_not_cache()) { 254 auto cache = isolate->function_cache(); 255 Object* element = cache->Lookup(serial_number); 256 if (element->IsJSFunction()) { 257 return handle(JSFunction::cast(element), isolate); 258 } 259 } 260 // Enter a new scope. Recursion could otherwise create a lot of handles. 261 HandleScope scope(isolate); 262 Handle<JSObject> prototype; 263 if (!data->remove_prototype()) { 264 auto prototype_templ = handle(data->prototype_template(), isolate); 265 if (prototype_templ->IsUndefined()) { 266 prototype = isolate->factory()->NewJSObject(isolate->object_function()); 267 } else { 268 ASSIGN_RETURN_ON_EXCEPTION( 269 isolate, prototype, 270 InstantiateObject(isolate, 271 Handle<ObjectTemplateInfo>::cast(prototype_templ)), 272 JSFunction); 273 } 274 auto parent = handle(data->parent_template(), isolate); 275 if (!parent->IsUndefined()) { 276 Handle<JSFunction> parent_instance; 277 ASSIGN_RETURN_ON_EXCEPTION( 278 isolate, parent_instance, 279 InstantiateFunction(isolate, 280 Handle<FunctionTemplateInfo>::cast(parent)), 281 JSFunction); 282 // TODO(dcarney): decide what to do here. 283 Handle<Object> parent_prototype; 284 ASSIGN_RETURN_ON_EXCEPTION( 285 isolate, parent_prototype, 286 JSObject::GetProperty(parent_instance, 287 isolate->factory()->prototype_string()), 288 JSFunction); 289 MAYBE_RETURN(JSObject::SetPrototype(prototype, parent_prototype, false, 290 Object::THROW_ON_ERROR), 291 MaybeHandle<JSFunction>()); 292 } 293 } 294 auto function = ApiNatives::CreateApiFunction( 295 isolate, data, prototype, ApiNatives::JavaScriptObjectType); 296 if (!name.is_null() && name->IsString()) { 297 function->shared()->set_name(*name); 298 } 299 if (!data->do_not_cache()) { 300 // Cache the function. 301 CacheFunction(isolate, serial_number, function); 302 } 303 auto result = ConfigureInstance(isolate, function, data); 304 if (result.is_null()) { 305 // Uncache on error. 306 if (!data->do_not_cache()) { 307 UncacheFunction(isolate, serial_number); 308 } 309 return MaybeHandle<JSFunction>(); 310 } 311 return scope.CloseAndEscape(function); 312 } 313 314 315 class InvokeScope { 316 public: 317 explicit InvokeScope(Isolate* isolate) 318 : isolate_(isolate), save_context_(isolate) {} 319 ~InvokeScope() { 320 bool has_exception = isolate_->has_pending_exception(); 321 if (has_exception) { 322 isolate_->ReportPendingMessages(); 323 } else { 324 isolate_->clear_pending_message(); 325 } 326 } 327 328 private: 329 Isolate* isolate_; 330 SaveContext save_context_; 331 }; 332 333 334 void AddPropertyToPropertyList(Isolate* isolate, Handle<TemplateInfo> templ, 335 int length, Handle<Object>* data) { 336 auto list = handle(templ->property_list(), isolate); 337 if (list->IsUndefined()) { 338 list = NeanderArray(isolate).value(); 339 templ->set_property_list(*list); 340 } 341 templ->set_number_of_properties(templ->number_of_properties() + 1); 342 NeanderArray array(list); 343 for (int i = 0; i < length; i++) { 344 Handle<Object> value = 345 data[i].is_null() 346 ? Handle<Object>::cast(isolate->factory()->undefined_value()) 347 : data[i]; 348 array.add(isolate, value); 349 } 350 } 351 352 } // namespace 353 354 355 MaybeHandle<JSFunction> ApiNatives::InstantiateFunction( 356 Handle<FunctionTemplateInfo> data) { 357 Isolate* isolate = data->GetIsolate(); 358 InvokeScope invoke_scope(isolate); 359 return ::v8::internal::InstantiateFunction(isolate, data); 360 } 361 362 363 MaybeHandle<JSObject> ApiNatives::InstantiateObject( 364 Handle<ObjectTemplateInfo> data) { 365 Isolate* isolate = data->GetIsolate(); 366 InvokeScope invoke_scope(isolate); 367 return ::v8::internal::InstantiateObject(isolate, data); 368 } 369 370 371 MaybeHandle<FunctionTemplateInfo> ApiNatives::ConfigureInstance( 372 Isolate* isolate, Handle<FunctionTemplateInfo> desc, 373 Handle<JSObject> instance) { 374 // Configure the instance by adding the properties specified by the 375 // instance template. 376 if (desc->instance_template()->IsUndefined()) return desc; 377 InvokeScope invoke_scope(isolate); 378 Handle<ObjectTemplateInfo> instance_template( 379 ObjectTemplateInfo::cast(desc->instance_template()), isolate); 380 RETURN_ON_EXCEPTION(isolate, ::v8::internal::ConfigureInstance( 381 isolate, instance, instance_template), 382 FunctionTemplateInfo); 383 return desc; 384 } 385 386 387 void ApiNatives::AddDataProperty(Isolate* isolate, Handle<TemplateInfo> info, 388 Handle<Name> name, Handle<Object> value, 389 PropertyAttributes attributes) { 390 const int kSize = 3; 391 PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell); 392 auto details_handle = handle(details.AsSmi(), isolate); 393 Handle<Object> data[kSize] = {name, details_handle, value}; 394 AddPropertyToPropertyList(isolate, info, kSize, data); 395 } 396 397 398 void ApiNatives::AddDataProperty(Isolate* isolate, Handle<TemplateInfo> info, 399 Handle<Name> name, v8::Intrinsic intrinsic, 400 PropertyAttributes attributes) { 401 const int kSize = 4; 402 auto value = handle(Smi::FromInt(intrinsic), isolate); 403 auto intrinsic_marker = isolate->factory()->true_value(); 404 PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell); 405 auto details_handle = handle(details.AsSmi(), isolate); 406 Handle<Object> data[kSize] = {name, intrinsic_marker, details_handle, value}; 407 AddPropertyToPropertyList(isolate, info, kSize, data); 408 } 409 410 411 void ApiNatives::AddAccessorProperty(Isolate* isolate, 412 Handle<TemplateInfo> info, 413 Handle<Name> name, 414 Handle<FunctionTemplateInfo> getter, 415 Handle<FunctionTemplateInfo> setter, 416 PropertyAttributes attributes) { 417 const int kSize = 4; 418 PropertyDetails details(attributes, ACCESSOR, 0, PropertyCellType::kNoCell); 419 auto details_handle = handle(details.AsSmi(), isolate); 420 Handle<Object> data[kSize] = {name, details_handle, getter, setter}; 421 AddPropertyToPropertyList(isolate, info, kSize, data); 422 } 423 424 425 void ApiNatives::AddNativeDataProperty(Isolate* isolate, 426 Handle<TemplateInfo> info, 427 Handle<AccessorInfo> property) { 428 auto list = handle(info->property_accessors(), isolate); 429 if (list->IsUndefined()) { 430 list = NeanderArray(isolate).value(); 431 info->set_property_accessors(*list); 432 } 433 NeanderArray array(list); 434 array.add(isolate, property); 435 } 436 437 438 Handle<JSFunction> ApiNatives::CreateApiFunction( 439 Isolate* isolate, Handle<FunctionTemplateInfo> obj, 440 Handle<Object> prototype, ApiInstanceType instance_type) { 441 Handle<Code> code; 442 if (obj->call_code()->IsCallHandlerInfo() && 443 CallHandlerInfo::cast(obj->call_code())->fast_handler()->IsCode()) { 444 code = isolate->builtins()->HandleFastApiCall(); 445 } else { 446 code = isolate->builtins()->HandleApiCall(); 447 } 448 Handle<Code> construct_stub = 449 prototype.is_null() ? isolate->builtins()->ConstructedNonConstructable() 450 : isolate->builtins()->JSConstructStubApi(); 451 452 obj->set_instantiated(true); 453 Handle<JSFunction> result; 454 if (obj->remove_prototype()) { 455 result = isolate->factory()->NewFunctionWithoutPrototype( 456 isolate->factory()->empty_string(), code); 457 } else { 458 int internal_field_count = 0; 459 if (!obj->instance_template()->IsUndefined()) { 460 Handle<ObjectTemplateInfo> instance_template = Handle<ObjectTemplateInfo>( 461 ObjectTemplateInfo::cast(obj->instance_template())); 462 internal_field_count = 463 Smi::cast(instance_template->internal_field_count())->value(); 464 } 465 466 // TODO(svenpanne) Kill ApiInstanceType and refactor things by generalizing 467 // JSObject::GetHeaderSize. 468 int instance_size = kPointerSize * internal_field_count; 469 InstanceType type; 470 switch (instance_type) { 471 case JavaScriptObjectType: 472 type = JS_OBJECT_TYPE; 473 instance_size += JSObject::kHeaderSize; 474 break; 475 case GlobalObjectType: 476 type = JS_GLOBAL_OBJECT_TYPE; 477 instance_size += JSGlobalObject::kSize; 478 break; 479 case GlobalProxyType: 480 type = JS_GLOBAL_PROXY_TYPE; 481 instance_size += JSGlobalProxy::kSize; 482 break; 483 default: 484 UNREACHABLE(); 485 type = JS_OBJECT_TYPE; // Keep the compiler happy. 486 break; 487 } 488 489 result = isolate->factory()->NewFunction( 490 isolate->factory()->empty_string(), code, prototype, type, 491 instance_size, obj->read_only_prototype(), true); 492 } 493 494 result->shared()->set_length(obj->length()); 495 Handle<Object> class_name(obj->class_name(), isolate); 496 if (class_name->IsString()) { 497 result->shared()->set_instance_class_name(*class_name); 498 result->shared()->set_name(*class_name); 499 } 500 result->shared()->set_function_data(*obj); 501 result->shared()->set_construct_stub(*construct_stub); 502 result->shared()->DontAdaptArguments(); 503 504 if (obj->remove_prototype()) { 505 DCHECK(result->shared()->IsApiFunction()); 506 DCHECK(!result->has_initial_map()); 507 DCHECK(!result->has_prototype()); 508 return result; 509 } 510 511 #ifdef DEBUG 512 LookupIterator it(handle(JSObject::cast(result->prototype())), 513 isolate->factory()->constructor_string(), 514 LookupIterator::OWN_SKIP_INTERCEPTOR); 515 MaybeHandle<Object> maybe_prop = Object::GetProperty(&it); 516 DCHECK(it.IsFound()); 517 DCHECK(maybe_prop.ToHandleChecked().is_identical_to(result)); 518 #endif 519 520 // Down from here is only valid for API functions that can be used as a 521 // constructor (don't set the "remove prototype" flag). 522 523 Handle<Map> map(result->initial_map()); 524 525 // Mark as undetectable if needed. 526 if (obj->undetectable()) { 527 map->set_is_undetectable(); 528 } 529 530 // Mark as hidden for the __proto__ accessor if needed. 531 if (obj->hidden_prototype()) { 532 map->set_is_hidden_prototype(); 533 } 534 535 // Mark as needs_access_check if needed. 536 if (obj->needs_access_check()) { 537 map->set_is_access_check_needed(true); 538 } 539 540 // Set interceptor information in the map. 541 if (!obj->named_property_handler()->IsUndefined()) { 542 map->set_has_named_interceptor(); 543 } 544 if (!obj->indexed_property_handler()->IsUndefined()) { 545 map->set_has_indexed_interceptor(); 546 } 547 548 // Mark instance as callable in the map. 549 if (!obj->instance_call_handler()->IsUndefined()) { 550 map->set_is_callable(); 551 map->set_is_constructor(); 552 } 553 554 // Recursively copy parent instance templates' accessors, 555 // 'data' may be modified. 556 int max_number_of_additional_properties = 0; 557 int max_number_of_static_properties = 0; 558 FunctionTemplateInfo* info = *obj; 559 while (true) { 560 if (!info->instance_template()->IsUndefined()) { 561 Object* props = ObjectTemplateInfo::cast(info->instance_template()) 562 ->property_accessors(); 563 if (!props->IsUndefined()) { 564 Handle<Object> props_handle(props, isolate); 565 NeanderArray props_array(props_handle); 566 max_number_of_additional_properties += props_array.length(); 567 } 568 } 569 if (!info->property_accessors()->IsUndefined()) { 570 Object* props = info->property_accessors(); 571 if (!props->IsUndefined()) { 572 Handle<Object> props_handle(props, isolate); 573 NeanderArray props_array(props_handle); 574 max_number_of_static_properties += props_array.length(); 575 } 576 } 577 Object* parent = info->parent_template(); 578 if (parent->IsUndefined()) break; 579 info = FunctionTemplateInfo::cast(parent); 580 } 581 582 Map::EnsureDescriptorSlack(map, max_number_of_additional_properties); 583 584 // Use a temporary FixedArray to acculumate static accessors 585 int valid_descriptors = 0; 586 Handle<FixedArray> array; 587 if (max_number_of_static_properties > 0) { 588 array = isolate->factory()->NewFixedArray(max_number_of_static_properties); 589 } 590 591 while (true) { 592 // Install instance descriptors 593 if (!obj->instance_template()->IsUndefined()) { 594 Handle<ObjectTemplateInfo> instance = Handle<ObjectTemplateInfo>( 595 ObjectTemplateInfo::cast(obj->instance_template()), isolate); 596 Handle<Object> props = 597 Handle<Object>(instance->property_accessors(), isolate); 598 if (!props->IsUndefined()) { 599 Map::AppendCallbackDescriptors(map, props); 600 } 601 } 602 // Accumulate static accessors 603 if (!obj->property_accessors()->IsUndefined()) { 604 Handle<Object> props = Handle<Object>(obj->property_accessors(), isolate); 605 valid_descriptors = 606 AccessorInfo::AppendUnique(props, array, valid_descriptors); 607 } 608 // Climb parent chain 609 Handle<Object> parent = Handle<Object>(obj->parent_template(), isolate); 610 if (parent->IsUndefined()) break; 611 obj = Handle<FunctionTemplateInfo>::cast(parent); 612 } 613 614 // Install accumulated static accessors 615 for (int i = 0; i < valid_descriptors; i++) { 616 Handle<AccessorInfo> accessor(AccessorInfo::cast(array->get(i))); 617 JSObject::SetAccessor(result, accessor).Assert(); 618 } 619 620 DCHECK(result->shared()->IsApiFunction()); 621 return result; 622 } 623 624 } // namespace internal 625 } // namespace v8 626