diff options
Diffstat (limited to 'deps/v8/src/objects.cc')
-rw-r--r-- | deps/v8/src/objects.cc | 5231 |
1 files changed, 2299 insertions, 2932 deletions
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 51993f3f32..00721c2d1b 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -6,15 +6,16 @@ #include <cmath> #include <iomanip> +#include <memory> #include <sstream> #include "src/objects-inl.h" #include "src/accessors.h" #include "src/allocation-site-scopes.h" -#include "src/api.h" -#include "src/api-arguments.h" +#include "src/api-arguments-inl.h" #include "src/api-natives.h" +#include "src/api.h" #include "src/base/bits.h" #include "src/base/utils/random-number-generator.h" #include "src/bootstrapper.h" @@ -22,6 +23,8 @@ #include "src/codegen.h" #include "src/compilation-dependencies.h" #include "src/compiler.h" +#include "src/counters-inl.h" +#include "src/counters.h" #include "src/date.h" #include "src/debug/debug.h" #include "src/deoptimizer.h" @@ -30,12 +33,14 @@ #include "src/field-index-inl.h" #include "src/field-index.h" #include "src/field-type.h" +#include "src/frames-inl.h" #include "src/full-codegen/full-codegen.h" +#include "src/globals.h" #include "src/ic/ic.h" #include "src/identity-map.h" #include "src/interpreter/bytecode-array-iterator.h" +#include "src/interpreter/bytecode-decoder.h" #include "src/interpreter/interpreter.h" -#include "src/interpreter/source-position-table.h" #include "src/isolate-inl.h" #include "src/keys.h" #include "src/list.h" @@ -44,20 +49,23 @@ #include "src/macro-assembler.h" #include "src/messages.h" #include "src/objects-body-descriptors-inl.h" -#include "src/profiler/cpu-profiler.h" #include "src/property-descriptor.h" #include "src/prototype.h" #include "src/regexp/jsregexp.h" #include "src/safepoint-table.h" +#include "src/snapshot/code-serializer.h" +#include "src/source-position-table.h" #include "src/string-builder.h" #include "src/string-search.h" #include "src/string-stream.h" #include "src/utils.h" +#include "src/wasm/wasm-module.h" #include "src/zone.h" #ifdef ENABLE_DISASSEMBLER #include "src/disasm.h" #include "src/disassembler.h" +#include "src/eh-frame.h" #endif namespace v8 { @@ -121,8 +129,8 @@ MaybeHandle<JSReceiver> Object::ConvertReceiver(Isolate* isolate, Handle<Object> object) { if (object->IsJSReceiver()) return Handle<JSReceiver>::cast(object); if (*object == isolate->heap()->null_value() || - *object == isolate->heap()->undefined_value()) { - return handle(isolate->global_proxy(), isolate); + object->IsUndefined(isolate)) { + return isolate->global_proxy(); } return Object::ToObject(isolate, object); } @@ -213,6 +221,155 @@ MaybeHandle<String> Object::ToString(Isolate* isolate, Handle<Object> input) { } } +namespace { + +bool IsErrorObject(Isolate* isolate, Handle<Object> object) { + if (!object->IsJSReceiver()) return false; + Handle<Symbol> symbol = isolate->factory()->stack_trace_symbol(); + return JSReceiver::HasOwnProperty(Handle<JSReceiver>::cast(object), symbol) + .FromMaybe(false); +} + +Handle<String> AsStringOrEmpty(Isolate* isolate, Handle<Object> object) { + return object->IsString() ? Handle<String>::cast(object) + : isolate->factory()->empty_string(); +} + +Handle<String> NoSideEffectsErrorToString(Isolate* isolate, + Handle<Object> input) { + Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(input); + + Handle<Name> name_key = isolate->factory()->name_string(); + Handle<Object> name = JSReceiver::GetDataProperty(receiver, name_key); + Handle<String> name_str = AsStringOrEmpty(isolate, name); + + Handle<Name> msg_key = isolate->factory()->message_string(); + Handle<Object> msg = JSReceiver::GetDataProperty(receiver, msg_key); + Handle<String> msg_str = AsStringOrEmpty(isolate, msg); + + if (name_str->length() == 0) return msg_str; + if (msg_str->length() == 0) return name_str; + + IncrementalStringBuilder builder(isolate); + builder.AppendString(name_str); + builder.AppendCString(": "); + builder.AppendString(msg_str); + + return builder.Finish().ToHandleChecked(); +} + +} // namespace + +// static +Handle<String> Object::NoSideEffectsToString(Isolate* isolate, + Handle<Object> input) { + DisallowJavascriptExecution no_js(isolate); + + if (input->IsString() || input->IsNumber() || input->IsOddball() || + input->IsSimd128Value()) { + return Object::ToString(isolate, input).ToHandleChecked(); + } else if (input->IsFunction()) { + // -- F u n c t i o n + Handle<String> fun_str; + if (input->IsJSBoundFunction()) { + fun_str = JSBoundFunction::ToString(Handle<JSBoundFunction>::cast(input)); + } else { + DCHECK(input->IsJSFunction()); + fun_str = JSFunction::ToString(Handle<JSFunction>::cast(input)); + } + + if (fun_str->length() > 128) { + IncrementalStringBuilder builder(isolate); + builder.AppendString(isolate->factory()->NewSubString(fun_str, 0, 111)); + builder.AppendCString("...<omitted>..."); + builder.AppendString(isolate->factory()->NewSubString( + fun_str, fun_str->length() - 2, fun_str->length())); + + return builder.Finish().ToHandleChecked(); + } + return fun_str; + } else if (input->IsSymbol()) { + // -- S y m b o l + Handle<Symbol> symbol = Handle<Symbol>::cast(input); + + IncrementalStringBuilder builder(isolate); + builder.AppendCString("Symbol("); + if (symbol->name()->IsString()) { + builder.AppendString(handle(String::cast(symbol->name()), isolate)); + } + builder.AppendCharacter(')'); + + return builder.Finish().ToHandleChecked(); + } else if (input->IsJSReceiver()) { + // -- J S R e c e i v e r + Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(input); + Handle<Object> to_string = JSReceiver::GetDataProperty( + receiver, isolate->factory()->toString_string()); + + if (IsErrorObject(isolate, input) || + *to_string == *isolate->error_to_string()) { + // When internally formatting error objects, use a side-effects-free + // version of Error.prototype.toString independent of the actually + // installed toString method. + return NoSideEffectsErrorToString(isolate, input); + } else if (*to_string == *isolate->object_to_string()) { + Handle<Object> ctor = JSReceiver::GetDataProperty( + receiver, isolate->factory()->constructor_string()); + if (ctor->IsFunction()) { + Handle<String> ctor_name; + if (ctor->IsJSBoundFunction()) { + ctor_name = JSBoundFunction::GetName( + isolate, Handle<JSBoundFunction>::cast(ctor)) + .ToHandleChecked(); + } else if (ctor->IsJSFunction()) { + Handle<Object> ctor_name_obj = + JSFunction::GetName(isolate, Handle<JSFunction>::cast(ctor)); + ctor_name = AsStringOrEmpty(isolate, ctor_name_obj); + } + + if (ctor_name->length() != 0) { + IncrementalStringBuilder builder(isolate); + builder.AppendCString("#<"); + builder.AppendString(ctor_name); + builder.AppendCString(">"); + + return builder.Finish().ToHandleChecked(); + } + } + } + } + + // At this point, input is either none of the above or a JSReceiver. + + Handle<JSReceiver> receiver; + if (input->IsJSReceiver()) { + receiver = Handle<JSReceiver>::cast(input); + } else { + // This is the only case where Object::ToObject throws. + DCHECK(!input->IsSmi()); + int constructor_function_index = + Handle<HeapObject>::cast(input)->map()->GetConstructorFunctionIndex(); + if (constructor_function_index == Map::kNoConstructorFunctionIndex) { + return isolate->factory()->NewStringFromAsciiChecked("[object Unknown]"); + } + + receiver = Object::ToObject(isolate, input, isolate->native_context()) + .ToHandleChecked(); + } + + Handle<String> builtin_tag = handle(receiver->class_name(), isolate); + Handle<Object> tag_obj = JSReceiver::GetDataProperty( + receiver, isolate->factory()->to_string_tag_symbol()); + Handle<String> tag = + tag_obj->IsString() ? Handle<String>::cast(tag_obj) : builtin_tag; + + IncrementalStringBuilder builder(isolate); + builder.AppendCString("[object "); + builder.AppendString(tag); + builder.AppendCString("]"); + + return builder.Finish().ToHandleChecked(); +} // static MaybeHandle<Object> Object::ToLength(Isolate* isolate, Handle<Object> input) { @@ -226,11 +383,25 @@ MaybeHandle<Object> Object::ToLength(Isolate* isolate, Handle<Object> input) { return isolate->factory()->NewNumber(len); } +// static +MaybeHandle<Object> Object::ToIndex(Isolate* isolate, Handle<Object> input, + MessageTemplate::Template error_index) { + if (input->IsUndefined(isolate)) return isolate->factory()->NewNumber(0.0); + ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(input), Object); + double len = DoubleToInteger(input->Number()) + 0.0; + auto js_len = isolate->factory()->NewNumber(len); + if (len < 0.0 || len > kMaxSafeInteger) { + THROW_NEW_ERROR(isolate, NewRangeError(error_index, js_len), Object); + } + return js_len; +} bool Object::BooleanValue() { - if (IsBoolean()) return IsTrue(); if (IsSmi()) return Smi::cast(this)->value() != 0; - if (IsUndefined() || IsNull()) return false; + DCHECK(IsHeapObject()); + Isolate* isolate = HeapObject::cast(this)->GetIsolate(); + if (IsBoolean()) return IsTrue(isolate); + if (IsUndefined(isolate) || IsNull(isolate)) return false; if (IsUndetectable()) return false; // Undetectable object is false. if (IsString()) return String::cast(this)->length() != 0; if (IsHeapNumber()) return HeapNumber::cast(this)->HeapNumberBooleanValue(); @@ -568,6 +739,86 @@ MaybeHandle<Object> Object::BitwiseXor(Isolate* isolate, Handle<Object> lhs, NumberToInt32(*rhs)); } +// static +MaybeHandle<Object> Object::OrdinaryHasInstance(Isolate* isolate, + Handle<Object> callable, + Handle<Object> object) { + // The {callable} must have a [[Call]] internal method. + if (!callable->IsCallable()) return isolate->factory()->false_value(); + + // Check if {callable} is a bound function, and if so retrieve its + // [[BoundTargetFunction]] and use that instead of {callable}. + if (callable->IsJSBoundFunction()) { + Handle<Object> bound_callable( + Handle<JSBoundFunction>::cast(callable)->bound_target_function(), + isolate); + return Object::InstanceOf(isolate, object, bound_callable); + } + + // If {object} is not a receiver, return false. + if (!object->IsJSReceiver()) return isolate->factory()->false_value(); + + // Get the "prototype" of {callable}; raise an error if it's not a receiver. + Handle<Object> prototype; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, prototype, + Object::GetProperty(callable, isolate->factory()->prototype_string()), + Object); + if (!prototype->IsJSReceiver()) { + THROW_NEW_ERROR( + isolate, + NewTypeError(MessageTemplate::kInstanceofNonobjectProto, prototype), + Object); + } + + // Return whether or not {prototype} is in the prototype chain of {object}. + Maybe<bool> result = JSReceiver::HasInPrototypeChain( + isolate, Handle<JSReceiver>::cast(object), prototype); + if (result.IsNothing()) return MaybeHandle<Object>(); + return isolate->factory()->ToBoolean(result.FromJust()); +} + +// static +MaybeHandle<Object> Object::InstanceOf(Isolate* isolate, Handle<Object> object, + Handle<Object> callable) { + // The {callable} must be a receiver. + if (!callable->IsJSReceiver()) { + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kNonObjectInInstanceOfCheck), + Object); + } + + // Lookup the @@hasInstance method on {callable}. + Handle<Object> inst_of_handler; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, inst_of_handler, + JSReceiver::GetMethod(Handle<JSReceiver>::cast(callable), + isolate->factory()->has_instance_symbol()), + Object); + if (!inst_of_handler->IsUndefined(isolate)) { + // Call the {inst_of_handler} on the {callable}. + Handle<Object> result; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, result, + Execution::Call(isolate, inst_of_handler, callable, 1, &object), + Object); + return isolate->factory()->ToBoolean(result->BooleanValue()); + } + + // The {callable} must have a [[Call]] internal method. + if (!callable->IsCallable()) { + THROW_NEW_ERROR( + isolate, NewTypeError(MessageTemplate::kNonCallableInInstanceOfCheck), + Object); + } + + // Fall back to OrdinaryHasInstance with {callable} and {object}. + Handle<Object> result; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, result, + JSReceiver::OrdinaryHasInstance(isolate, callable, object), Object); + return result; +} Maybe<bool> Object::IsArray(Handle<Object> object) { if (object->IsJSArray()) return Just(true); @@ -586,20 +837,6 @@ Maybe<bool> Object::IsArray(Handle<Object> object) { } -bool Object::IsPromise(Handle<Object> object) { - if (!object->IsJSObject()) return false; - auto js_object = Handle<JSObject>::cast(object); - // Promises can't have access checks. - if (js_object->map()->is_access_check_needed()) return false; - auto isolate = js_object->GetIsolate(); - // TODO(dcarney): this should just be read from the symbol registry so as not - // to be context dependent. - auto key = isolate->factory()->promise_status_symbol(); - // Shouldn't be possible to throw here. - return JSObject::HasRealNamedProperty(js_object, key).FromJust(); -} - - // static MaybeHandle<Object> Object::GetMethod(Handle<JSReceiver> receiver, Handle<Name> name) { @@ -607,7 +844,7 @@ MaybeHandle<Object> Object::GetMethod(Handle<JSReceiver> receiver, Isolate* isolate = receiver->GetIsolate(); ASSIGN_RETURN_ON_EXCEPTION(isolate, func, JSReceiver::GetProperty(receiver, name), Object); - if (func->IsNull() || func->IsUndefined()) { + if (func->IsNull(isolate) || func->IsUndefined(isolate)) { return isolate->factory()->undefined_value(); } if (!func->IsCallable()) { @@ -634,14 +871,9 @@ MaybeHandle<FixedArray> Object::CreateListFromArrayLike( } // 4. Let len be ? ToLength(? Get(obj, "length")). Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object); - Handle<Object> raw_length_obj; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, raw_length_obj, - JSReceiver::GetProperty(receiver, isolate->factory()->length_string()), - FixedArray); Handle<Object> raw_length_number; ASSIGN_RETURN_ON_EXCEPTION(isolate, raw_length_number, - Object::ToLength(isolate, raw_length_obj), + Object::GetLengthFromArrayLike(isolate, receiver), FixedArray); uint32_t len; if (!raw_length_number->ToUint32(&len) || @@ -688,6 +920,16 @@ MaybeHandle<FixedArray> Object::CreateListFromArrayLike( // static +MaybeHandle<Object> Object::GetLengthFromArrayLike(Isolate* isolate, + Handle<Object> object) { + Handle<Object> val; + Handle<Object> key = isolate->factory()->length_string(); + ASSIGN_RETURN_ON_EXCEPTION( + isolate, val, Runtime::GetObjectProperty(isolate, object, key), Object); + return Object::ToLength(isolate, val); +} + +// static Maybe<bool> JSReceiver::HasProperty(LookupIterator* it) { for (; it->IsFound(); it->Next()) { switch (it->state()) { @@ -762,17 +1004,6 @@ MaybeHandle<Object> Object::GetProperty(LookupIterator* it) { } -#define STACK_CHECK(result_value) \ - do { \ - StackLimitCheck stack_check(isolate); \ - if (stack_check.HasOverflowed()) { \ - isolate->Throw(*isolate->factory()->NewRangeError( \ - MessageTemplate::kStackOverflow)); \ - return result_value; \ - } \ - } while (false) - - // static MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate, Handle<JSProxy> proxy, @@ -788,7 +1019,7 @@ MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate, } DCHECK(!name->IsPrivate()); - STACK_CHECK(MaybeHandle<Object>()); + STACK_CHECK(isolate, MaybeHandle<Object>()); Handle<Name> trap_name = isolate->factory()->get_string(); // 1. Assert: IsPropertyKey(P) is true. // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O. @@ -808,7 +1039,7 @@ MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate, isolate, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name), Object); // 7. If trap is undefined, then - if (trap->IsUndefined()) { + if (trap->IsUndefined(isolate)) { // 7.a Return target.[[Get]](P, Receiver). LookupIterator it = LookupIterator::PropertyOrElement(isolate, receiver, name, target); @@ -848,8 +1079,8 @@ MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate, // 10.b.i. If trapResult is not undefined, throw a TypeError exception. inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) && !target_desc.configurable() && - target_desc.get()->IsUndefined() && - !trap_result->IsUndefined(); + target_desc.get()->IsUndefined(isolate) && + !trap_result->IsUndefined(isolate); if (inconsistent) { THROW_NEW_ERROR( isolate, @@ -908,12 +1139,40 @@ bool Object::ToInt32(int32_t* value) { return false; } +Handle<SharedFunctionInfo> FunctionTemplateInfo::GetOrCreateSharedFunctionInfo( + Isolate* isolate, Handle<FunctionTemplateInfo> info) { + Object* current_info = info->shared_function_info(); + if (current_info->IsSharedFunctionInfo()) { + return handle(SharedFunctionInfo::cast(current_info), isolate); + } -bool FunctionTemplateInfo::IsTemplateFor(Object* object) { - if (!object->IsHeapObject()) return false; - return IsTemplateFor(HeapObject::cast(object)->map()); -} + Handle<Object> class_name(info->class_name(), isolate); + Handle<String> name = class_name->IsString() + ? Handle<String>::cast(class_name) + : isolate->factory()->empty_string(); + Handle<Code> code; + if (info->call_code()->IsCallHandlerInfo() && + CallHandlerInfo::cast(info->call_code())->fast_handler()->IsCode()) { + code = isolate->builtins()->HandleFastApiCall(); + } else { + code = isolate->builtins()->HandleApiCall(); + } + bool is_constructor = !info->remove_prototype(); + Handle<SharedFunctionInfo> result = + isolate->factory()->NewSharedFunctionInfo(name, code, is_constructor); + if (is_constructor) { + result->SetConstructStub(*isolate->builtins()->JSConstructStubApi()); + } + + result->set_length(info->length()); + if (class_name->IsString()) result->set_instance_class_name(*class_name); + result->set_api_func_data(*info); + result->DontAdaptArguments(); + DCHECK(result->IsApiFunction()); + info->set_shared_function_info(*result); + return result; +} bool FunctionTemplateInfo::IsTemplateFor(Map* map) { // There is a constraint on the object; check. @@ -934,25 +1193,25 @@ bool FunctionTemplateInfo::IsTemplateFor(Map* map) { } -// TODO(dcarney): CallOptimization duplicates this logic, merge. -Object* FunctionTemplateInfo::GetCompatibleReceiver(Isolate* isolate, - Object* receiver) { - // API calls are only supported with JSObject receivers. - if (!receiver->IsJSObject()) return isolate->heap()->null_value(); - Object* recv_type = this->signature(); - // No signature, return holder. - if (recv_type->IsUndefined()) return receiver; - FunctionTemplateInfo* signature = FunctionTemplateInfo::cast(recv_type); - // Check the receiver. - for (PrototypeIterator iter(isolate, JSObject::cast(receiver), - PrototypeIterator::START_AT_RECEIVER, - PrototypeIterator::END_AT_NON_HIDDEN); - !iter.IsAtEnd(); iter.Advance()) { - if (signature->IsTemplateFor(iter.GetCurrent())) return iter.GetCurrent(); - } - return isolate->heap()->null_value(); +// static +Handle<TemplateList> TemplateList::New(Isolate* isolate, int size) { + Handle<FixedArray> list = + isolate->factory()->NewFixedArray(kLengthIndex + size); + list->set(kLengthIndex, Smi::FromInt(0)); + return Handle<TemplateList>::cast(list); } +// static +Handle<TemplateList> TemplateList::Add(Isolate* isolate, + Handle<TemplateList> list, + Handle<i::Object> value) { + STATIC_ASSERT(kFirstElementIndex == 1); + int index = list->length() + 1; + Handle<i::FixedArray> fixed_array = Handle<FixedArray>::cast(list); + fixed_array = FixedArray::SetAndGrow(fixed_array, index, value); + fixed_array->set(kLengthIndex, Smi::FromInt(index)); + return Handle<TemplateList>::cast(fixed_array); +} // static MaybeHandle<JSObject> JSObject::New(Handle<JSFunction> constructor, @@ -1001,7 +1260,7 @@ MaybeHandle<Object> JSProxy::GetPrototype(Handle<JSProxy> proxy) { Isolate* isolate = proxy->GetIsolate(); Handle<String> trap_name = isolate->factory()->getPrototypeOf_string(); - STACK_CHECK(MaybeHandle<Object>()); + STACK_CHECK(isolate, MaybeHandle<Object>()); // 1. Let handler be the value of the [[ProxyHandler]] internal slot. // 2. If handler is null, throw a TypeError exception. @@ -1020,7 +1279,7 @@ MaybeHandle<Object> JSProxy::GetPrototype(Handle<JSProxy> proxy) { ASSIGN_RETURN_ON_EXCEPTION(isolate, trap, GetMethod(handler, trap_name), Object); // 6. If trap is undefined, then return target.[[GetPrototypeOf]](). - if (trap->IsUndefined()) { + if (trap->IsUndefined(isolate)) { return JSReceiver::GetPrototype(isolate, target); } // 7. Let handlerProto be ? Call(trap, handler, «target»). @@ -1030,7 +1289,7 @@ MaybeHandle<Object> JSProxy::GetPrototype(Handle<JSProxy> proxy) { isolate, handler_proto, Execution::Call(isolate, trap, handler, arraysize(argv), argv), Object); // 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError. - if (!(handler_proto->IsJSReceiver() || handler_proto->IsNull())) { + if (!(handler_proto->IsJSReceiver() || handler_proto->IsNull(isolate))) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyGetPrototypeOfInvalid), Object); @@ -1098,16 +1357,9 @@ MaybeHandle<Object> Object::GetPropertyWithAccessor(LookupIterator* it) { // Regular accessor. Handle<Object> getter(AccessorPair::cast(*structure)->getter(), isolate); if (getter->IsFunctionTemplateInfo()) { - auto result = Builtins::InvokeApiFunction( - Handle<FunctionTemplateInfo>::cast(getter), receiver, 0, nullptr); - if (isolate->has_pending_exception()) { - return MaybeHandle<Object>(); - } - Handle<Object> return_value; - if (result.ToHandle(&return_value)) { - return_value->VerifyApiCallResultType(); - return handle(*return_value, isolate); - } + return Builtins::InvokeApiFunction( + isolate, false, Handle<FunctionTemplateInfo>::cast(getter), receiver, 0, + nullptr, isolate->factory()->undefined_value()); } else if (getter->IsCallable()) { // TODO(rossberg): nicer would be to cast to some JSCallable here... return Object::GetPropertyWithDefinedGetter( @@ -1117,6 +1369,20 @@ MaybeHandle<Object> Object::GetPropertyWithAccessor(LookupIterator* it) { return ReadAbsentProperty(isolate, receiver, it->GetName()); } +// static +Address AccessorInfo::redirect(Isolate* isolate, Address address, + AccessorComponent component) { + ApiFunction fun(address); + DCHECK_EQ(ACCESSOR_GETTER, component); + ExternalReference::Type type = ExternalReference::DIRECT_GETTER_CALL; + return ExternalReference(&fun, type, isolate).address(); +} + +Address AccessorInfo::redirected_getter() const { + Address accessor = v8::ToCData<Address>(getter()); + if (accessor == nullptr) return nullptr; + return redirect(GetIsolate(), accessor, ACCESSOR_GETTER); +} bool AccessorInfo::IsCompatibleReceiverMap(Isolate* isolate, Handle<AccessorInfo> info, @@ -1173,12 +1439,12 @@ Maybe<bool> Object::SetPropertyWithAccessor(LookupIterator* it, Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate); if (setter->IsFunctionTemplateInfo()) { Handle<Object> argv[] = {value}; - auto result = - Builtins::InvokeApiFunction(Handle<FunctionTemplateInfo>::cast(setter), - receiver, arraysize(argv), argv); - if (isolate->has_pending_exception()) { - return Nothing<bool>(); - } + RETURN_ON_EXCEPTION_VALUE( + isolate, Builtins::InvokeApiFunction( + isolate, false, Handle<FunctionTemplateInfo>::cast(setter), + receiver, arraysize(argv), argv, + isolate->factory()->undefined_value()), + Nothing<bool>()); return Just(true); } else if (setter->IsCallable()) { // TODO(rossberg): nicer would be to cast to some JSCallable here... @@ -1230,18 +1496,6 @@ Maybe<bool> Object::SetPropertyWithDefinedSetter(Handle<Object> receiver, // static -bool Object::IsErrorObject(Isolate* isolate, Handle<Object> object) { - if (!object->IsJSObject()) return false; - // Use stack_trace_symbol as proxy for [[ErrorData]]. - Handle<Name> symbol = isolate->factory()->stack_trace_symbol(); - Maybe<bool> has_stack_trace = - JSReceiver::HasOwnProperty(Handle<JSReceiver>::cast(object), symbol); - DCHECK(!has_stack_trace.IsNothing()); - return has_stack_trace.FromJust(); -} - - -// static bool JSObject::AllCanRead(LookupIterator* it) { // Skip current iteration, it's in state ACCESS_CHECK or INTERCEPTOR, both of // which have already been checked. @@ -1263,19 +1517,191 @@ bool JSObject::AllCanRead(LookupIterator* it) { return false; } +namespace { + +MaybeHandle<Object> GetPropertyWithInterceptorInternal( + LookupIterator* it, Handle<InterceptorInfo> interceptor, bool* done) { + *done = false; + Isolate* isolate = it->isolate(); + // Make sure that the top context does not change when doing callbacks or + // interceptor calls. + AssertNoContextChange ncc(isolate); + + if (interceptor->getter()->IsUndefined(isolate)) { + return isolate->factory()->undefined_value(); + } + + Handle<JSObject> holder = it->GetHolder<JSObject>(); + Handle<Object> result; + Handle<Object> receiver = it->GetReceiver(); + if (!receiver->IsJSReceiver()) { + ASSIGN_RETURN_ON_EXCEPTION( + isolate, receiver, Object::ConvertReceiver(isolate, receiver), Object); + } + PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, + *holder, Object::DONT_THROW); + + if (it->IsElement()) { + uint32_t index = it->index(); + v8::IndexedPropertyGetterCallback getter = + v8::ToCData<v8::IndexedPropertyGetterCallback>(interceptor->getter()); + result = args.Call(getter, index); + } else { + Handle<Name> name = it->name(); + DCHECK(!name->IsPrivate()); + + if (name->IsSymbol() && !interceptor->can_intercept_symbols()) { + return isolate->factory()->undefined_value(); + } + + v8::GenericNamedPropertyGetterCallback getter = + v8::ToCData<v8::GenericNamedPropertyGetterCallback>( + interceptor->getter()); + result = args.Call(getter, name); + } + + RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); + if (result.is_null()) return isolate->factory()->undefined_value(); + *done = true; + // Rebox handle before return + return handle(*result, isolate); +} + +Maybe<PropertyAttributes> GetPropertyAttributesWithInterceptorInternal( + LookupIterator* it, Handle<InterceptorInfo> interceptor) { + Isolate* isolate = it->isolate(); + // Make sure that the top context does not change when doing + // callbacks or interceptor calls. + AssertNoContextChange ncc(isolate); + HandleScope scope(isolate); + + Handle<JSObject> holder = it->GetHolder<JSObject>(); + if (!it->IsElement() && it->name()->IsSymbol() && + !interceptor->can_intercept_symbols()) { + return Just(ABSENT); + } + Handle<Object> receiver = it->GetReceiver(); + if (!receiver->IsJSReceiver()) { + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver, + Object::ConvertReceiver(isolate, receiver), + Nothing<PropertyAttributes>()); + } + PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, + *holder, Object::DONT_THROW); + if (!interceptor->query()->IsUndefined(isolate)) { + Handle<Object> result; + if (it->IsElement()) { + uint32_t index = it->index(); + v8::IndexedPropertyQueryCallback query = + v8::ToCData<v8::IndexedPropertyQueryCallback>(interceptor->query()); + result = args.Call(query, index); + } else { + Handle<Name> name = it->name(); + DCHECK(!name->IsPrivate()); + v8::GenericNamedPropertyQueryCallback query = + v8::ToCData<v8::GenericNamedPropertyQueryCallback>( + interceptor->query()); + result = args.Call(query, name); + } + if (!result.is_null()) { + int32_t value; + CHECK(result->ToInt32(&value)); + return Just(static_cast<PropertyAttributes>(value)); + } + } else if (!interceptor->getter()->IsUndefined(isolate)) { + // TODO(verwaest): Use GetPropertyWithInterceptor? + Handle<Object> result; + if (it->IsElement()) { + uint32_t index = it->index(); + v8::IndexedPropertyGetterCallback getter = + v8::ToCData<v8::IndexedPropertyGetterCallback>(interceptor->getter()); + result = args.Call(getter, index); + } else { + Handle<Name> name = it->name(); + DCHECK(!name->IsPrivate()); + v8::GenericNamedPropertyGetterCallback getter = + v8::ToCData<v8::GenericNamedPropertyGetterCallback>( + interceptor->getter()); + result = args.Call(getter, name); + } + if (!result.is_null()) return Just(DONT_ENUM); + } + + RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<PropertyAttributes>()); + return Just(ABSENT); +} + +Maybe<bool> SetPropertyWithInterceptorInternal( + LookupIterator* it, Handle<InterceptorInfo> interceptor, + Object::ShouldThrow should_throw, Handle<Object> value) { + Isolate* isolate = it->isolate(); + // Make sure that the top context does not change when doing callbacks or + // interceptor calls. + AssertNoContextChange ncc(isolate); + + if (interceptor->setter()->IsUndefined(isolate)) return Just(false); + + Handle<JSObject> holder = it->GetHolder<JSObject>(); + bool result; + Handle<Object> receiver = it->GetReceiver(); + if (!receiver->IsJSReceiver()) { + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver, + Object::ConvertReceiver(isolate, receiver), + Nothing<bool>()); + } + PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, + *holder, should_throw); + + if (it->IsElement()) { + uint32_t index = it->index(); + v8::IndexedPropertySetterCallback setter = + v8::ToCData<v8::IndexedPropertySetterCallback>(interceptor->setter()); + // TODO(neis): In the future, we may want to actually return the + // interceptor's result, which then should be a boolean. + result = !args.Call(setter, index, value).is_null(); + } else { + Handle<Name> name = it->name(); + DCHECK(!name->IsPrivate()); + + if (name->IsSymbol() && !interceptor->can_intercept_symbols()) { + return Just(false); + } + + v8::GenericNamedPropertySetterCallback setter = + v8::ToCData<v8::GenericNamedPropertySetterCallback>( + interceptor->setter()); + result = !args.Call(setter, name, value).is_null(); + } + + RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>()); + return Just(result); +} + +} // namespace MaybeHandle<Object> JSObject::GetPropertyWithFailedAccessCheck( LookupIterator* it) { + Isolate* isolate = it->isolate(); Handle<JSObject> checked = it->GetHolder<JSObject>(); - while (AllCanRead(it)) { - if (it->state() == LookupIterator::ACCESSOR) { - return GetPropertyWithAccessor(it); + Handle<InterceptorInfo> interceptor = + it->GetInterceptorForFailedAccessCheck(); + if (interceptor.is_null()) { + while (AllCanRead(it)) { + if (it->state() == LookupIterator::ACCESSOR) { + return GetPropertyWithAccessor(it); + } + DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); + bool done; + Handle<Object> result; + ASSIGN_RETURN_ON_EXCEPTION(isolate, result, + GetPropertyWithInterceptor(it, &done), Object); + if (done) return result; } - DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); + } else { + MaybeHandle<Object> result; bool done; - Handle<Object> result; - ASSIGN_RETURN_ON_EXCEPTION(it->isolate(), result, - GetPropertyWithInterceptor(it, &done), Object); + result = GetPropertyWithInterceptorInternal(it, interceptor, &done); + RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); if (done) return result; } @@ -1286,27 +1712,36 @@ MaybeHandle<Object> JSObject::GetPropertyWithFailedAccessCheck( return it->factory()->undefined_value(); } - it->isolate()->ReportFailedAccessCheck(checked); - RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object); + isolate->ReportFailedAccessCheck(checked); + RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); return it->factory()->undefined_value(); } Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithFailedAccessCheck( LookupIterator* it) { + Isolate* isolate = it->isolate(); Handle<JSObject> checked = it->GetHolder<JSObject>(); - while (AllCanRead(it)) { - if (it->state() == LookupIterator::ACCESSOR) { - return Just(it->property_attributes()); + Handle<InterceptorInfo> interceptor = + it->GetInterceptorForFailedAccessCheck(); + if (interceptor.is_null()) { + while (AllCanRead(it)) { + if (it->state() == LookupIterator::ACCESSOR) { + return Just(it->property_attributes()); + } + DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); + auto result = GetPropertyAttributesWithInterceptor(it); + if (isolate->has_scheduled_exception()) break; + if (result.IsJust() && result.FromJust() != ABSENT) return result; } - DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); - auto result = GetPropertyAttributesWithInterceptor(it); - if (it->isolate()->has_scheduled_exception()) break; - if (result.IsJust() && result.FromJust() != ABSENT) return result; + } else { + Maybe<PropertyAttributes> result = + GetPropertyAttributesWithInterceptorInternal(it, interceptor); + RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<PropertyAttributes>()); + if (result.FromMaybe(ABSENT) != ABSENT) return result; } - it->isolate()->ReportFailedAccessCheck(checked); - RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), - Nothing<PropertyAttributes>()); + isolate->ReportFailedAccessCheck(checked); + RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<PropertyAttributes>()); return Just(ABSENT); } @@ -1327,13 +1762,23 @@ bool JSObject::AllCanWrite(LookupIterator* it) { Maybe<bool> JSObject::SetPropertyWithFailedAccessCheck( LookupIterator* it, Handle<Object> value, ShouldThrow should_throw) { + Isolate* isolate = it->isolate(); Handle<JSObject> checked = it->GetHolder<JSObject>(); - if (AllCanWrite(it)) { - return SetPropertyWithAccessor(it, value, should_throw); + Handle<InterceptorInfo> interceptor = + it->GetInterceptorForFailedAccessCheck(); + if (interceptor.is_null()) { + if (AllCanWrite(it)) { + return SetPropertyWithAccessor(it, value, should_throw); + } + } else { + Maybe<bool> result = SetPropertyWithInterceptorInternal( + it, interceptor, should_throw, value); + RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); + if (result.IsJust()) return result; } - it->isolate()->ReportFailedAccessCheck(checked); - RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>()); + isolate->ReportFailedAccessCheck(checked); + RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); return Just(true); } @@ -1349,44 +1794,47 @@ void JSObject::SetNormalizedProperty(Handle<JSObject> object, } if (object->IsJSGlobalObject()) { - Handle<GlobalDictionary> property_dictionary(object->global_dictionary()); + Handle<GlobalDictionary> dictionary(object->global_dictionary()); - int entry = property_dictionary->FindEntry(name); + int entry = dictionary->FindEntry(name); if (entry == GlobalDictionary::kNotFound) { - auto cell = object->GetIsolate()->factory()->NewPropertyCell(); + Isolate* isolate = object->GetIsolate(); + auto cell = isolate->factory()->NewPropertyCell(); cell->set_value(*value); - auto cell_type = value->IsUndefined() ? PropertyCellType::kUndefined - : PropertyCellType::kConstant; + auto cell_type = value->IsUndefined(isolate) + ? PropertyCellType::kUndefined + : PropertyCellType::kConstant; details = details.set_cell_type(cell_type); value = cell; - property_dictionary = - GlobalDictionary::Add(property_dictionary, name, value, details); - object->set_properties(*property_dictionary); + dictionary = GlobalDictionary::Add(dictionary, name, value, details); + object->set_properties(*dictionary); } else { - PropertyCell::UpdateCell(property_dictionary, entry, value, details); + Handle<PropertyCell> cell = + PropertyCell::PrepareForValue(dictionary, entry, value, details); + cell->set_value(*value); } } else { - Handle<NameDictionary> property_dictionary(object->property_dictionary()); + Handle<NameDictionary> dictionary(object->property_dictionary()); - int entry = property_dictionary->FindEntry(name); + int entry = dictionary->FindEntry(name); if (entry == NameDictionary::kNotFound) { - property_dictionary = - NameDictionary::Add(property_dictionary, name, value, details); - object->set_properties(*property_dictionary); + dictionary = NameDictionary::Add(dictionary, name, value, details); + object->set_properties(*dictionary); } else { - PropertyDetails original_details = property_dictionary->DetailsAt(entry); + PropertyDetails original_details = dictionary->DetailsAt(entry); int enumeration_index = original_details.dictionary_index(); DCHECK(enumeration_index > 0); details = details.set_index(enumeration_index); - property_dictionary->SetEntry(entry, name, value, details); + dictionary->SetEntry(entry, name, value, details); } } } +// static Maybe<bool> JSReceiver::HasInPrototypeChain(Isolate* isolate, Handle<JSReceiver> object, Handle<Object> proto) { - PrototypeIterator iter(isolate, object, PrototypeIterator::START_AT_RECEIVER); + PrototypeIterator iter(isolate, object, kStartAtReceiver); while (true) { if (!iter.AdvanceFollowingProxies()) return Nothing<bool>(); if (iter.IsAtEnd()) return Just(false); @@ -1396,7 +1844,6 @@ Maybe<bool> JSReceiver::HasInPrototypeChain(Isolate* isolate, } } - Map* Object::GetRootMap(Isolate* isolate) { DisallowHeapAllocation no_alloc; if (IsSmi()) { @@ -1421,28 +1868,21 @@ Map* Object::GetRootMap(Isolate* isolate) { return isolate->heap()->null_value()->map(); } +namespace { -Object* Object::GetHash() { - Object* hash = GetSimpleHash(); - if (hash->IsSmi()) return hash; - - DisallowHeapAllocation no_gc; - DCHECK(IsJSReceiver()); - JSReceiver* receiver = JSReceiver::cast(this); - Isolate* isolate = receiver->GetIsolate(); - return *JSReceiver::GetIdentityHash(isolate, handle(receiver, isolate)); -} - - -Object* Object::GetSimpleHash() { +// Returns a non-SMI for JSObjects, but returns the hash code for simple +// objects. This avoids a double lookup in the cases where we know we will +// add the hash to the JSObject if it does not already exist. +Object* GetSimpleHash(Object* object) { // The object is either a Smi, a HeapNumber, a name, an odd-ball, // a SIMD value type, a real JS object, or a Harmony proxy. - if (IsSmi()) { - uint32_t hash = ComputeIntegerHash(Smi::cast(this)->value(), kZeroHashSeed); + if (object->IsSmi()) { + uint32_t hash = + ComputeIntegerHash(Smi::cast(object)->value(), kZeroHashSeed); return Smi::FromInt(hash & Smi::kMaxValue); } - if (IsHeapNumber()) { - double num = HeapNumber::cast(this)->value(); + if (object->IsHeapNumber()) { + double num = HeapNumber::cast(object)->value(); if (std::isnan(num)) return Smi::FromInt(Smi::kMaxValue); if (i::IsMinusZero(num)) num = 0; if (IsSmiDouble(num)) { @@ -1451,30 +1891,43 @@ Object* Object::GetSimpleHash() { uint32_t hash = ComputeLongHash(double_to_uint64(num)); return Smi::FromInt(hash & Smi::kMaxValue); } - if (IsName()) { - uint32_t hash = Name::cast(this)->Hash(); + if (object->IsName()) { + uint32_t hash = Name::cast(object)->Hash(); return Smi::FromInt(hash); } - if (IsOddball()) { - uint32_t hash = Oddball::cast(this)->to_string()->Hash(); + if (object->IsOddball()) { + uint32_t hash = Oddball::cast(object)->to_string()->Hash(); return Smi::FromInt(hash); } - if (IsSimd128Value()) { - uint32_t hash = Simd128Value::cast(this)->Hash(); + if (object->IsSimd128Value()) { + uint32_t hash = Simd128Value::cast(object)->Hash(); return Smi::FromInt(hash & Smi::kMaxValue); } + DCHECK(object->IsJSReceiver()); + // Simply return the receiver as it is guaranteed to not be a SMI. + return object; +} + +} // namespace + +Object* Object::GetHash() { + Object* hash = GetSimpleHash(this); + if (hash->IsSmi()) return hash; + + DisallowHeapAllocation no_gc; DCHECK(IsJSReceiver()); JSReceiver* receiver = JSReceiver::cast(this); - return receiver->GetHeap()->undefined_value(); + Isolate* isolate = receiver->GetIsolate(); + return JSReceiver::GetIdentityHash(isolate, handle(receiver, isolate)); } - -Handle<Smi> Object::GetOrCreateHash(Isolate* isolate, Handle<Object> object) { - Handle<Object> hash(object->GetSimpleHash(), isolate); - if (hash->IsSmi()) return Handle<Smi>::cast(hash); +Smi* Object::GetOrCreateHash(Isolate* isolate, Handle<Object> object) { + Object* hash = GetSimpleHash(*object); + if (hash->IsSmi()) return Smi::cast(hash); DCHECK(object->IsJSReceiver()); - return JSReceiver::GetOrCreateIdentityHash(Handle<JSReceiver>::cast(object)); + return JSReceiver::GetOrCreateIdentityHash(isolate, + Handle<JSReceiver>::cast(object)); } @@ -1555,11 +2008,7 @@ bool Object::SameValueZero(Object* other) { MaybeHandle<Object> Object::ArraySpeciesConstructor( Isolate* isolate, Handle<Object> original_array) { - Handle<Context> native_context = isolate->native_context(); Handle<Object> default_species = isolate->array_function(); - if (!FLAG_harmony_species) { - return default_species; - } if (original_array->IsJSArray() && Handle<JSArray>::cast(original_array)->HasArrayPrototype(isolate) && isolate->IsArraySpeciesLookupChainIntact()) { @@ -1580,7 +2029,7 @@ MaybeHandle<Object> Object::ArraySpeciesConstructor( isolate, constructor_context, JSReceiver::GetFunctionRealm(Handle<JSReceiver>::cast(constructor)), Object); - if (*constructor_context != *native_context && + if (*constructor_context != *isolate->native_context() && *constructor == constructor_context->array_function()) { constructor = isolate->factory()->undefined_value(); } @@ -1591,12 +2040,12 @@ MaybeHandle<Object> Object::ArraySpeciesConstructor( JSReceiver::GetProperty(Handle<JSReceiver>::cast(constructor), isolate->factory()->species_symbol()), Object); - if (constructor->IsNull()) { + if (constructor->IsNull(isolate)) { constructor = isolate->factory()->undefined_value(); } } } - if (constructor->IsUndefined()) { + if (constructor->IsUndefined(isolate)) { return default_species; } else { if (!constructor->IsConstructor()) { @@ -1718,6 +2167,7 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) { Heap* heap = GetHeap(); bool is_one_byte = this->IsOneByteRepresentation(); bool is_internalized = this->IsInternalizedString(); + bool has_pointers = this->IsConsString() || this->IsSlicedString(); // Morph the string to an external string by replacing the map and // reinitializing the fields. This won't work if the space the existing @@ -1746,6 +2196,9 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) { int new_size = this->SizeFromMap(new_map); heap->CreateFillerObjectAt(this->address() + new_size, size - new_size, ClearRecordedSlots::kNo); + if (has_pointers) { + heap->ClearRecordedSlotRange(this->address(), this->address() + new_size); + } // We are storing the new map using release store after creating a filler for // the left-over space to avoid races with the sweeper thread. @@ -1786,6 +2239,7 @@ bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) { if (size < ExternalString::kShortSize) return false; Heap* heap = GetHeap(); bool is_internalized = this->IsInternalizedString(); + bool has_pointers = this->IsConsString() || this->IsSlicedString(); // Morph the string to an external string by replacing the map and // reinitializing the fields. This won't work if the space the existing @@ -1808,6 +2262,9 @@ bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) { int new_size = this->SizeFromMap(new_map); heap->CreateFillerObjectAt(this->address() + new_size, size - new_size, ClearRecordedSlots::kNo); + if (has_pointers) { + heap->ClearRecordedSlotRange(this->address(), this->address() + new_size); + } // We are storing the new map using release store after creating a filler for // the left-over space to avoid races with the sweeper thread. @@ -1821,8 +2278,7 @@ bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) { return true; } - -void String::StringShortPrint(StringStream* accumulator) { +void String::StringShortPrint(StringStream* accumulator, bool show_details) { int len = length(); if (len > kMaxShortPrintLength) { accumulator->Add("<Very long string[%u]>", len); @@ -1851,15 +2307,15 @@ void String::StringShortPrint(StringStream* accumulator) { } stream.Reset(this); if (one_byte) { - accumulator->Add("<String[%u]: ", length()); + if (show_details) accumulator->Add("<String[%u]: ", length()); for (int i = 0; i < len; i++) { accumulator->Put(static_cast<char>(stream.GetNext())); } - accumulator->Put('>'); + if (show_details) accumulator->Put('>'); } else { // Backslash indicates that the string contains control // characters and that backslashes are therefore escaped. - accumulator->Add("<String[%u]\\: ", length()); + if (show_details) accumulator->Add("<String[%u]\\: ", length()); for (int i = 0; i < len; i++) { uint16_t c = stream.GetNext(); if (c == '\n') { @@ -1879,7 +2335,7 @@ void String::StringShortPrint(StringStream* accumulator) { accumulator->Put('.'); accumulator->Put('.'); } - accumulator->Put('>'); + if (show_details) accumulator->Put('>'); } return; } @@ -1897,23 +2353,15 @@ void String::PrintUC16(std::ostream& os, int start, int end) { // NOLINT void JSObject::JSObjectShortPrint(StringStream* accumulator) { switch (map()->instance_type()) { case JS_ARRAY_TYPE: { - double length = JSArray::cast(this)->length()->IsUndefined() - ? 0 - : JSArray::cast(this)->length()->Number(); + double length = JSArray::cast(this)->length()->IsUndefined(GetIsolate()) + ? 0 + : JSArray::cast(this)->length()->Number(); accumulator->Add("<JS Array[%u]>", static_cast<uint32_t>(length)); break; } case JS_BOUND_FUNCTION_TYPE: { JSBoundFunction* bound_function = JSBoundFunction::cast(this); - Object* name = bound_function->name(); accumulator->Add("<JS BoundFunction"); - if (name->IsString()) { - String* str = String::cast(name); - if (str->length() > 0) { - accumulator->Add(" "); - accumulator->Put(str); - } - } accumulator->Add( " (BoundTargetFunction %p)>", reinterpret_cast<void*>(bound_function->bound_target_function())); @@ -1946,6 +2394,18 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) { if (!printed) { accumulator->Add("<JS Function"); } + if (FLAG_trace_file_names) { + Object* source_name = + Script::cast(function->shared()->script())->name(); + if (source_name->IsString()) { + String* str = String::cast(source_name); + if (str->length() > 0) { + accumulator->Add(" <"); + accumulator->Put(str); + accumulator->Add(">"); + } + } + } accumulator->Add(" (SharedFunctionInfo %p)", reinterpret_cast<void*>(function->shared())); accumulator->Put('>'); @@ -2131,6 +2591,7 @@ void JSObject::PrintInstanceMigration(FILE* file, void HeapObject::HeapObjectShortPrint(std::ostream& os) { // NOLINT Heap* heap = GetHeap(); + Isolate* isolate = heap->isolate(); if (!heap->Contains(this)) { os << "!!!INVALID POINTER!!!"; return; @@ -2192,8 +2653,7 @@ void HeapObject::HeapObjectShortPrint(std::ostream& os) { // NOLINT case SHARED_FUNCTION_INFO_TYPE: { SharedFunctionInfo* shared = SharedFunctionInfo::cast(this); - base::SmartArrayPointer<char> debug_name = - shared->DebugName()->ToCString(); + std::unique_ptr<char[]> debug_name = shared->DebugName()->ToCString(); if (debug_name[0] != 0) { os << "<SharedFunctionInfo " << debug_name.get() << ">"; } else { @@ -2216,18 +2676,20 @@ void HeapObject::HeapObjectShortPrint(std::ostream& os) { // NOLINT break; } case ODDBALL_TYPE: { - if (IsUndefined()) { + if (IsUndefined(isolate)) { os << "<undefined>"; - } else if (IsTheHole()) { + } else if (IsTheHole(isolate)) { os << "<the hole>"; - } else if (IsNull()) { + } else if (IsNull(isolate)) { os << "<null>"; - } else if (IsTrue()) { + } else if (IsTrue(isolate)) { os << "<true>"; - } else if (IsFalse()) { + } else if (IsFalse(isolate)) { os << "<false>"; } else { - os << "<Odd Oddball>"; + os << "<Odd Oddball: "; + os << Oddball::cast(this)->to_string()->ToCString().get(); + os << ">"; } break; } @@ -2458,25 +2920,6 @@ String* JSReceiver::class_name() { } -MaybeHandle<String> JSReceiver::BuiltinStringTag(Handle<JSReceiver> object) { - Maybe<bool> is_array = Object::IsArray(object); - MAYBE_RETURN(is_array, MaybeHandle<String>()); - Isolate* const isolate = object->GetIsolate(); - if (is_array.FromJust()) { - return isolate->factory()->Array_string(); - } - // TODO(adamk): According to ES2015, we should return "Function" when - // object has a [[Call]] internal method (corresponds to IsCallable). - // But this is well cemented in layout tests and might cause webbreakage. - // if (object->IsCallable()) { - // return isolate->factory()->Function_string(); - // } - // TODO(adamk): class_name() is expensive, replace with instance type - // checks where possible. - return handle(object->class_name(), isolate); -} - - // static Handle<String> JSReceiver::GetConstructorName(Handle<JSReceiver> receiver) { Isolate* isolate = receiver->GetIsolate(); @@ -2600,68 +3043,6 @@ MaybeHandle<Map> Map::CopyWithConstant(Handle<Map> map, return Map::CopyAddDescriptor(map, &new_constant_desc, flag); } - -void JSObject::AddSlowProperty(Handle<JSObject> object, - Handle<Name> name, - Handle<Object> value, - PropertyAttributes attributes) { - DCHECK(!object->HasFastProperties()); - Isolate* isolate = object->GetIsolate(); - if (object->IsJSGlobalObject()) { - Handle<GlobalDictionary> dict(object->global_dictionary()); - PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell); - int entry = dict->FindEntry(name); - // If there's a cell there, just invalidate and set the property. - if (entry != GlobalDictionary::kNotFound) { - PropertyCell::UpdateCell(dict, entry, value, details); - // TODO(ishell): move this to UpdateCell. - // Need to adjust the details. - int index = dict->NextEnumerationIndex(); - dict->SetNextEnumerationIndex(index + 1); - PropertyCell* cell = PropertyCell::cast(dict->ValueAt(entry)); - details = cell->property_details().set_index(index); - cell->set_property_details(details); - - } else { - auto cell = isolate->factory()->NewPropertyCell(); - cell->set_value(*value); - auto cell_type = value->IsUndefined() ? PropertyCellType::kUndefined - : PropertyCellType::kConstant; - details = details.set_cell_type(cell_type); - value = cell; - - Handle<GlobalDictionary> result = - GlobalDictionary::Add(dict, name, value, details); - if (*dict != *result) object->set_properties(*result); - } - } else { - Handle<NameDictionary> dict(object->property_dictionary()); - PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell); - Handle<NameDictionary> result = - NameDictionary::Add(dict, name, value, details); - if (*dict != *result) object->set_properties(*result); - } -} - - -MaybeHandle<Object> JSObject::EnqueueChangeRecord(Handle<JSObject> object, - const char* type_str, - Handle<Name> name, - Handle<Object> old_value) { - DCHECK(!object->IsJSGlobalProxy()); - DCHECK(!object->IsJSGlobalObject()); - Isolate* isolate = object->GetIsolate(); - HandleScope scope(isolate); - Handle<String> type = isolate->factory()->InternalizeUtf8String(type_str); - Handle<Object> args[] = { type, object, name, old_value }; - int argc = name.is_null() ? 2 : old_value->IsTheHole() ? 3 : 4; - - return Execution::Call(isolate, - Handle<JSFunction>(isolate->observers_notify_change()), - isolate->factory()->undefined_value(), argc, args); -} - - const char* Representation::Mnemonic() const { switch (kind_) { case kNone: return "v"; @@ -2727,7 +3108,6 @@ bool Map::InstancesNeedRewriting(Map* target, int target_number_of_fields, void JSObject::UpdatePrototypeUserRegistration(Handle<Map> old_map, Handle<Map> new_map, Isolate* isolate) { - if (!FLAG_track_prototype_users) return; if (!old_map->is_prototype_map()) return; DCHECK(new_map->is_prototype_map()); bool was_registered = JSObject::UnregisterPrototypeUser(old_map, isolate); @@ -3124,7 +3504,7 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map, // Ensure that no transition was inserted for prototype migrations. DCHECK_EQ( 0, TransitionArray::NumberOfTransitions(old_map->raw_transitions())); - DCHECK(new_map->GetBackPointer()->IsUndefined()); + DCHECK(new_map->GetBackPointer()->IsUndefined(new_map->GetIsolate())); } } else { MigrateFastToSlow(object, new_map, expected_additional_properties); @@ -3138,6 +3518,15 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map, // When adding code here, add a DisallowHeapAllocation too. } +void JSObject::ForceSetPrototype(Handle<JSObject> object, + Handle<Object> proto) { + // object.__proto__ = proto; + Handle<Map> old_map = Handle<Map>(object->map()); + Handle<Map> new_map = Map::Copy(old_map, "ForceSetPrototype"); + Map::SetPrototype(new_map, proto, FAST_PROTOTYPE); + JSObject::MigrateToMap(object, new_map); +} + int Map::NumberOfFields() { DescriptorArray* descriptors = instance_descriptors(); int result = 0; @@ -3236,17 +3625,18 @@ static inline bool EqualImmutableValues(Object* obj1, Object* obj2) { // proper sharing of descriptor arrays. void Map::ReplaceDescriptors(DescriptorArray* new_descriptors, LayoutDescriptor* new_layout_descriptor) { + Isolate* isolate = GetIsolate(); // Don't overwrite the empty descriptor array or initial map's descriptors. - if (NumberOfOwnDescriptors() == 0 || GetBackPointer()->IsUndefined()) { + if (NumberOfOwnDescriptors() == 0 || GetBackPointer()->IsUndefined(isolate)) { return; } DescriptorArray* to_replace = instance_descriptors(); - GetHeap()->incremental_marking()->IterateBlackObject(to_replace); + isolate->heap()->incremental_marking()->IterateBlackObject(to_replace); Map* current = this; while (current->instance_descriptors() == to_replace) { Object* next = current->GetBackPointer(); - if (next->IsUndefined()) break; // Stop overwriting at initial map. + if (next->IsUndefined(isolate)) break; // Stop overwriting at initial map. current->SetEnumLength(kInvalidEnumCacheSentinel); current->UpdateDescriptors(new_descriptors, new_layout_descriptor); current = Map::cast(next); @@ -3257,9 +3647,10 @@ void Map::ReplaceDescriptors(DescriptorArray* new_descriptors, Map* Map::FindRootMap() { Map* result = this; + Isolate* isolate = GetIsolate(); while (true) { Object* back = result->GetBackPointer(); - if (back->IsUndefined()) { + if (back->IsUndefined(isolate)) { // Initial map always owns descriptors and doesn't have unused entries // in the descriptor array. DCHECK(result->owns_descriptors()); @@ -3317,9 +3708,10 @@ Map* Map::FindFieldOwner(int descriptor) { DisallowHeapAllocation no_allocation; DCHECK_EQ(DATA, instance_descriptors()->GetDetails(descriptor).type()); Map* result = this; + Isolate* isolate = GetIsolate(); while (true) { Object* back = result->GetBackPointer(); - if (back->IsUndefined()) break; + if (back->IsUndefined(isolate)) break; Map* parent = Map::cast(back); if (parent->NumberOfOwnDescriptors() <= descriptor) break; result = parent; @@ -4125,56 +4517,14 @@ Handle<Map> Map::Update(Handle<Map> map) { ALLOW_IN_DESCRIPTOR); } - Maybe<bool> JSObject::SetPropertyWithInterceptor(LookupIterator* it, ShouldThrow should_throw, Handle<Object> value) { - Isolate* isolate = it->isolate(); - // Make sure that the top context does not change when doing callbacks or - // interceptor calls. - AssertNoContextChange ncc(isolate); - DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); - Handle<InterceptorInfo> interceptor(it->GetInterceptor()); - if (interceptor->setter()->IsUndefined()) return Just(false); - - Handle<JSObject> holder = it->GetHolder<JSObject>(); - bool result; - Handle<Object> receiver = it->GetReceiver(); - if (!receiver->IsJSReceiver()) { - ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver, - Object::ConvertReceiver(isolate, receiver), - Nothing<bool>()); - } - PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, - *holder, should_throw); - - if (it->IsElement()) { - uint32_t index = it->index(); - v8::IndexedPropertySetterCallback setter = - v8::ToCData<v8::IndexedPropertySetterCallback>(interceptor->setter()); - // TODO(neis): In the future, we may want to actually return the - // interceptor's result, which then should be a boolean. - result = !args.Call(setter, index, value).is_null(); - } else { - Handle<Name> name = it->name(); - DCHECK(!name->IsPrivate()); - - if (name->IsSymbol() && !interceptor->can_intercept_symbols()) { - return Just(false); - } - - v8::GenericNamedPropertySetterCallback setter = - v8::ToCData<v8::GenericNamedPropertySetterCallback>( - interceptor->setter()); - result = !args.Call(setter, name, value).is_null(); - } - - RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>()); - return Just(result); + return SetPropertyWithInterceptorInternal(it, it->GetInterceptor(), + should_throw, value); } - MaybeHandle<Object> Object::SetProperty(Handle<Object> object, Handle<Name> name, Handle<Object> value, LanguageMode language_mode, @@ -4190,6 +4540,7 @@ Maybe<bool> Object::SetPropertyInternal(LookupIterator* it, LanguageMode language_mode, StoreFromKeyed store_mode, bool* found) { + it->UpdateProtector(); DCHECK(it->IsFound()); ShouldThrow should_throw = is_sloppy(language_mode) ? DONT_THROW : THROW_ON_ERROR; @@ -4214,23 +4565,49 @@ Maybe<bool> Object::SetPropertyInternal(LookupIterator* it, return JSProxy::SetProperty(it->GetHolder<JSProxy>(), it->GetName(), value, it->GetReceiver(), language_mode); - case LookupIterator::INTERCEPTOR: + case LookupIterator::INTERCEPTOR: { + Handle<Map> store_target_map; + if (it->GetReceiver()->IsJSObject()) { + store_target_map = handle(it->GetStoreTarget()->map(), it->isolate()); + } if (it->HolderIsReceiverOrHiddenPrototype()) { Maybe<bool> result = JSObject::SetPropertyWithInterceptor(it, should_throw, value); if (result.IsNothing() || result.FromJust()) return result; + // Interceptor modified the store target but failed to set the + // property. + // TODO(jochen): Remove after we've identified the faulty interceptor. + if (!store_target_map.is_null() && + *store_target_map != it->GetStoreTarget()->map()) { + it->isolate()->PushStackTraceAndDie( + 0xabababaa, v8::ToCData<void*>(it->GetInterceptor()->setter()), + nullptr, 0xabababab); + } + Utils::ApiCheck(store_target_map.is_null() || + *store_target_map == it->GetStoreTarget()->map(), + it->IsElement() ? "v8::IndexedPropertySetterCallback" + : "v8::NamedPropertySetterCallback", + "Interceptor silently changed store target."); } else { Maybe<PropertyAttributes> maybe_attributes = JSObject::GetPropertyAttributesWithInterceptor(it); if (!maybe_attributes.IsJust()) return Nothing<bool>(); - if (maybe_attributes.FromJust() == ABSENT) break; if ((maybe_attributes.FromJust() & READ_ONLY) != 0) { return WriteToReadOnlyProperty(it, value, should_throw); } + // Interceptor modified the store target but failed to set the + // property. + Utils::ApiCheck(store_target_map.is_null() || + *store_target_map == it->GetStoreTarget()->map(), + it->IsElement() ? "v8::IndexedPropertySetterCallback" + : "v8::NamedPropertySetterCallback", + "Interceptor silently changed store target."); + if (maybe_attributes.FromJust() == ABSENT) break; *found = false; return Nothing<bool>(); } break; + } case LookupIterator::ACCESSOR: { if (it->IsReadOnly()) { @@ -4272,7 +4649,6 @@ Maybe<bool> Object::SetPropertyInternal(LookupIterator* it, Maybe<bool> Object::SetProperty(LookupIterator* it, Handle<Object> value, LanguageMode language_mode, StoreFromKeyed store_mode) { - it->UpdateProtector(); if (it->IsFound()) { bool found = true; Maybe<bool> result = @@ -4300,7 +4676,6 @@ Maybe<bool> Object::SetSuperProperty(LookupIterator* it, Handle<Object> value, StoreFromKeyed store_mode) { Isolate* isolate = it->isolate(); - it->UpdateProtector(); if (it->IsFound()) { bool found = true; Maybe<bool> result = @@ -4308,6 +4683,8 @@ Maybe<bool> Object::SetSuperProperty(LookupIterator* it, Handle<Object> value, if (found) return result; } + it->UpdateProtector(); + // The property either doesn't exist on the holder or exists there as a data // property. @@ -4319,7 +4696,7 @@ Maybe<bool> Object::SetSuperProperty(LookupIterator* it, Handle<Object> value, } Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver()); - LookupIterator::Configuration c = LookupIterator::HIDDEN; + LookupIterator::Configuration c = LookupIterator::OWN; LookupIterator own_lookup = it->IsElement() ? LookupIterator(isolate, receiver, it->index(), c) : LookupIterator(receiver, it->name(), c); @@ -4443,18 +4820,10 @@ Maybe<bool> Object::SetDataProperty(LookupIterator* it, Handle<Object> value) { // Store on the holder which may be hidden behind the receiver. DCHECK(it->HolderIsReceiverOrHiddenPrototype()); - // Old value for the observation change record. - // Fetch before transforming the object since the encoding may become - // incompatible with what's cached in |it|. - bool is_observed = receiver->map()->is_observed() && - (it->IsElement() || !it->name()->IsPrivate()); - MaybeHandle<Object> maybe_old; - if (is_observed) maybe_old = it->GetDataValue(); - Handle<Object> to_assign = value; // Convert the incoming value to a number for storing into typed arrays. if (it->IsElement() && receiver->HasFixedTypedArrayElements()) { - if (!value->IsNumber() && !value->IsUndefined()) { + if (!value->IsNumber() && !value->IsUndefined(it->isolate())) { ASSIGN_RETURN_ON_EXCEPTION_VALUE( it->isolate(), to_assign, Object::ToNumber(value), Nothing<bool>()); // We have to recheck the length. However, it can only change if the @@ -4473,15 +4842,6 @@ Maybe<bool> Object::SetDataProperty(LookupIterator* it, Handle<Object> value) { // Write the property value. it->WriteDataValue(to_assign); - // Send the change record if there are observers. - if (is_observed && !value->SameValue(*maybe_old.ToHandleChecked())) { - RETURN_ON_EXCEPTION_VALUE( - it->isolate(), - JSObject::EnqueueChangeRecord(receiver, "update", it->GetName(), - maybe_old.ToHandleChecked()), - Nothing<bool>()); - } - #if VERIFY_HEAP if (FLAG_verify_heap) { receiver->JSObjectVerify(); @@ -4491,47 +4851,6 @@ Maybe<bool> Object::SetDataProperty(LookupIterator* it, Handle<Object> value) { } -MUST_USE_RESULT static MaybeHandle<Object> BeginPerformSplice( - Handle<JSArray> object) { - Isolate* isolate = object->GetIsolate(); - HandleScope scope(isolate); - Handle<Object> args[] = {object}; - - return Execution::Call( - isolate, Handle<JSFunction>(isolate->observers_begin_perform_splice()), - isolate->factory()->undefined_value(), arraysize(args), args); -} - - -MUST_USE_RESULT static MaybeHandle<Object> EndPerformSplice( - Handle<JSArray> object) { - Isolate* isolate = object->GetIsolate(); - HandleScope scope(isolate); - Handle<Object> args[] = {object}; - - return Execution::Call( - isolate, Handle<JSFunction>(isolate->observers_end_perform_splice()), - isolate->factory()->undefined_value(), arraysize(args), args); -} - - -MUST_USE_RESULT static MaybeHandle<Object> EnqueueSpliceRecord( - Handle<JSArray> object, uint32_t index, Handle<JSArray> deleted, - uint32_t add_count) { - Isolate* isolate = object->GetIsolate(); - HandleScope scope(isolate); - Handle<Object> index_object = isolate->factory()->NewNumberFromUint(index); - Handle<Object> add_count_object = - isolate->factory()->NewNumberFromUint(add_count); - - Handle<Object> args[] = {object, index_object, deleted, add_count_object}; - - return Execution::Call( - isolate, Handle<JSFunction>(isolate->observers_enqueue_splice()), - isolate->factory()->undefined_value(), arraysize(args), args); -} - - Maybe<bool> Object::AddDataProperty(LookupIterator* it, Handle<Object> value, PropertyAttributes attributes, ShouldThrow should_throw, @@ -4586,6 +4905,7 @@ Maybe<bool> Object::AddDataProperty(LookupIterator* it, Handle<Object> value, JSObject::ValidateElements(receiver); return result; } else { + it->UpdateProtector(); // Migrate to the most up-to-date map that will be able to store |value| // under it->name() with |attributes|. it->PrepareTransitionToDataProperty(receiver, value, attributes, @@ -4593,22 +4913,9 @@ Maybe<bool> Object::AddDataProperty(LookupIterator* it, Handle<Object> value, DCHECK_EQ(LookupIterator::TRANSITION, it->state()); it->ApplyTransitionToDataProperty(receiver); - // TODO(verwaest): Encapsulate dictionary handling better. - if (receiver->map()->is_dictionary_map()) { - // TODO(dcarney): just populate TransitionPropertyCell here? - JSObject::AddSlowProperty(receiver, it->name(), value, attributes); - } else { - // Write the property value. - it->WriteDataValue(value); - } + // Write the property value. + it->WriteDataValue(value); - // Send the change record if there are observers. - if (receiver->map()->is_observed() && !it->name()->IsPrivate()) { - RETURN_ON_EXCEPTION_VALUE(isolate, JSObject::EnqueueChangeRecord( - receiver, "add", it->name(), - it->factory()->the_hole_value()), - Nothing<bool>()); - } #if VERIFY_HEAP if (FLAG_verify_heap) { receiver->JSObjectVerify(); @@ -4649,22 +4956,22 @@ void Map::EnsureDescriptorSlack(Handle<Map> map, int slack) { new_descriptors->CopyEnumCacheFrom(*descriptors); } + Isolate* isolate = map->GetIsolate(); // Replace descriptors by new_descriptors in all maps that share it. - map->GetHeap()->incremental_marking()->IterateBlackObject(*descriptors); + isolate->heap()->incremental_marking()->IterateBlackObject(*descriptors); Map* current = *map; while (current->instance_descriptors() == *descriptors) { Object* next = current->GetBackPointer(); - if (next->IsUndefined()) break; // Stop overwriting at initial map. + if (next->IsUndefined(isolate)) break; // Stop overwriting at initial map. current->UpdateDescriptors(*new_descriptors, layout_descriptor); current = Map::cast(next); } map->UpdateDescriptors(*new_descriptors, layout_descriptor); } - -template<class T> -static int AppendUniqueCallbacks(NeanderArray* callbacks, +template <class T> +static int AppendUniqueCallbacks(Handle<TemplateList> callbacks, Handle<typename T::Array> array, int valid_descriptors) { int nof_callbacks = callbacks->length(); @@ -4743,9 +5050,9 @@ void Map::AppendCallbackDescriptors(Handle<Map> map, Handle<Object> descriptors) { int nof = map->NumberOfOwnDescriptors(); Handle<DescriptorArray> array(map->instance_descriptors()); - NeanderArray callbacks(descriptors); - DCHECK(array->NumberOfSlackDescriptors() >= callbacks.length()); - nof = AppendUniqueCallbacks<DescriptorArrayAppender>(&callbacks, array, nof); + Handle<TemplateList> callbacks = Handle<TemplateList>::cast(descriptors); + DCHECK_GE(array->NumberOfSlackDescriptors(), callbacks->length()); + nof = AppendUniqueCallbacks<DescriptorArrayAppender>(callbacks, array, nof); map->SetNumberOfOwnDescriptors(nof); } @@ -4753,10 +5060,9 @@ void Map::AppendCallbackDescriptors(Handle<Map> map, int AccessorInfo::AppendUnique(Handle<Object> descriptors, Handle<FixedArray> array, int valid_descriptors) { - NeanderArray callbacks(descriptors); - DCHECK(array->length() >= callbacks.length() + valid_descriptors); - return AppendUniqueCallbacks<FixedArrayAppender>(&callbacks, - array, + Handle<TemplateList> callbacks = Handle<TemplateList>::cast(descriptors); + DCHECK_GE(array->length(), callbacks->length() + valid_descriptors); + return AppendUniqueCallbacks<FixedArrayAppender>(callbacks, array, valid_descriptors); } @@ -4915,7 +5221,7 @@ Handle<Map> Map::TransitionElementsTo(Handle<Map> map, } } - DCHECK(!map->IsUndefined()); + DCHECK(!map->IsUndefined(isolate)); // Check if we can go back in the elements kind transition chain. if (IsHoleyElementsKind(from_kind) && to_kind == GetPackedElementsKind(from_kind) && @@ -4969,7 +5275,7 @@ void JSProxy::Revoke(Handle<JSProxy> proxy) { Maybe<bool> JSProxy::HasProperty(Isolate* isolate, Handle<JSProxy> proxy, Handle<Name> name) { DCHECK(!name->IsPrivate()); - STACK_CHECK(Nothing<bool>()); + STACK_CHECK(isolate, Nothing<bool>()); // 1. (Assert) // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O. Handle<Object> handler(proxy->handler(), isolate); @@ -4989,7 +5295,7 @@ Maybe<bool> JSProxy::HasProperty(Isolate* isolate, Handle<JSProxy> proxy, isolate->factory()->has_string()), Nothing<bool>()); // 7. If trap is undefined, then - if (trap->IsUndefined()) { + if (trap->IsUndefined(isolate)) { // 7a. Return target.[[HasProperty]](P). return JSReceiver::HasProperty(target, name); } @@ -5038,7 +5344,7 @@ Maybe<bool> JSProxy::SetProperty(Handle<JSProxy> proxy, Handle<Name> name, LanguageMode language_mode) { DCHECK(!name->IsPrivate()); Isolate* isolate = proxy->GetIsolate(); - STACK_CHECK(Nothing<bool>()); + STACK_CHECK(isolate, Nothing<bool>()); Factory* factory = isolate->factory(); Handle<String> trap_name = factory->set_string(); ShouldThrow should_throw = @@ -5055,7 +5361,7 @@ Maybe<bool> JSProxy::SetProperty(Handle<JSProxy> proxy, Handle<Name> name, Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>()); - if (trap->IsUndefined()) { + if (trap->IsUndefined(isolate)) { LookupIterator it = LookupIterator::PropertyOrElement(isolate, receiver, name, target); return Object::SetSuperProperty(&it, value, language_mode, @@ -5091,7 +5397,7 @@ Maybe<bool> JSProxy::SetProperty(Handle<JSProxy> proxy, Handle<Name> name, } inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) && !target_desc.configurable() && - target_desc.set()->IsUndefined(); + target_desc.set()->IsUndefined(isolate); if (inconsistent) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxySetFrozenAccessor, name)); @@ -5109,7 +5415,7 @@ Maybe<bool> JSProxy::DeletePropertyOrElement(Handle<JSProxy> proxy, ShouldThrow should_throw = is_sloppy(language_mode) ? DONT_THROW : THROW_ON_ERROR; Isolate* isolate = proxy->GetIsolate(); - STACK_CHECK(Nothing<bool>()); + STACK_CHECK(isolate, Nothing<bool>()); Factory* factory = isolate->factory(); Handle<String> trap_name = factory->deleteProperty_string(); @@ -5124,7 +5430,7 @@ Maybe<bool> JSProxy::DeletePropertyOrElement(Handle<JSProxy> proxy, Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>()); - if (trap->IsUndefined()) { + if (trap->IsUndefined(isolate)) { return JSReceiver::DeletePropertyOrElement(target, name, language_mode); } @@ -5200,6 +5506,44 @@ MaybeHandle<Context> JSBoundFunction::GetFunctionRealm( handle(function->bound_target_function())); } +// static +MaybeHandle<String> JSBoundFunction::GetName(Isolate* isolate, + Handle<JSBoundFunction> function) { + Handle<String> prefix = isolate->factory()->bound__string(); + if (!function->bound_target_function()->IsJSFunction()) return prefix; + Handle<JSFunction> target(JSFunction::cast(function->bound_target_function()), + isolate); + Handle<Object> target_name = JSFunction::GetName(isolate, target); + if (!target_name->IsString()) return prefix; + Factory* factory = isolate->factory(); + return factory->NewConsString(prefix, Handle<String>::cast(target_name)); +} + +// static +Handle<Object> JSFunction::GetName(Isolate* isolate, + Handle<JSFunction> function) { + if (function->shared()->name_should_print_as_anonymous()) { + return isolate->factory()->anonymous_string(); + } + return handle(function->shared()->name(), isolate); +} + +// static +MaybeHandle<Smi> JSFunction::GetLength(Isolate* isolate, + Handle<JSFunction> function) { + int length = 0; + if (function->shared()->is_compiled()) { + length = function->shared()->length(); + } else { + // If the function isn't compiled yet, the length is not computed + // correctly yet. Compile it now and return the right length. + if (Compiler::Compile(function, Compiler::KEEP_EXCEPTION)) { + length = function->shared()->length(); + } + if (isolate->has_pending_exception()) return MaybeHandle<Smi>(); + } + return handle(Smi::FromInt(length), isolate); +} // static Handle<Context> JSFunction::GetFunctionRealm(Handle<JSFunction> function) { @@ -5342,8 +5686,6 @@ Maybe<bool> JSObject::DefineOwnPropertyIgnoreAttributes( ShouldThrow should_throw, AccessorInfoHandling handling) { it->UpdateProtector(); Handle<JSObject> object = Handle<JSObject>::cast(it->GetReceiver()); - bool is_observed = object->map()->is_observed() && - (it->IsElement() || !it->name()->IsPrivate()); for (; it->IsFound(); it->Next()) { switch (it->state()) { @@ -5403,14 +5745,6 @@ Maybe<bool> JSObject::DefineOwnPropertyIgnoreAttributes( it->ReconfigureDataProperty(value, attributes); } - if (is_observed) { - RETURN_ON_EXCEPTION_VALUE( - it->isolate(), - EnqueueChangeRecord(object, "reconfigure", it->GetName(), - it->factory()->the_hole_value()), - Nothing<bool>()); - } - return Just(true); } case LookupIterator::INTEGER_INDEXED_EXOTIC: @@ -5431,20 +5765,8 @@ Maybe<bool> JSObject::DefineOwnPropertyIgnoreAttributes( } // Reconfigure the data property if the attributes mismatch. - Handle<Object> old_value = it->factory()->the_hole_value(); - if (is_observed) old_value = it->GetDataValue(); - it->ReconfigureDataProperty(value, attributes); - if (is_observed) { - if (old_value->SameValue(*value)) { - old_value = it->factory()->the_hole_value(); - } - RETURN_ON_EXCEPTION_VALUE( - it->isolate(), EnqueueChangeRecord(object, "reconfigure", - it->GetName(), old_value), - Nothing<bool>()); - } return Just(true); } } @@ -5457,7 +5779,7 @@ Maybe<bool> JSObject::DefineOwnPropertyIgnoreAttributes( MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes( Handle<JSObject> object, Handle<Name> name, Handle<Object> value, PropertyAttributes attributes) { - DCHECK(!value->IsTheHole()); + DCHECK(!value->IsTheHole(object->GetIsolate())); LookupIterator it(object, name, object, LookupIterator::OWN); return DefineOwnPropertyIgnoreAttributes(&it, value, attributes); } @@ -5479,73 +5801,11 @@ MaybeHandle<Object> JSObject::DefinePropertyOrElementIgnoreAttributes( return DefineOwnPropertyIgnoreAttributes(&it, value, attributes); } - Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithInterceptor( LookupIterator* it) { - Isolate* isolate = it->isolate(); - // Make sure that the top context does not change when doing - // callbacks or interceptor calls. - AssertNoContextChange ncc(isolate); - HandleScope scope(isolate); - - Handle<JSObject> holder = it->GetHolder<JSObject>(); - Handle<InterceptorInfo> interceptor(it->GetInterceptor()); - if (!it->IsElement() && it->name()->IsSymbol() && - !interceptor->can_intercept_symbols()) { - return Just(ABSENT); - } - Handle<Object> receiver = it->GetReceiver(); - if (!receiver->IsJSReceiver()) { - ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver, - Object::ConvertReceiver(isolate, receiver), - Nothing<PropertyAttributes>()); - } - PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, - *holder, Object::DONT_THROW); - if (!interceptor->query()->IsUndefined()) { - Handle<Object> result; - if (it->IsElement()) { - uint32_t index = it->index(); - v8::IndexedPropertyQueryCallback query = - v8::ToCData<v8::IndexedPropertyQueryCallback>(interceptor->query()); - result = args.Call(query, index); - } else { - Handle<Name> name = it->name(); - DCHECK(!name->IsPrivate()); - v8::GenericNamedPropertyQueryCallback query = - v8::ToCData<v8::GenericNamedPropertyQueryCallback>( - interceptor->query()); - result = args.Call(query, name); - } - if (!result.is_null()) { - int32_t value; - CHECK(result->ToInt32(&value)); - return Just(static_cast<PropertyAttributes>(value)); - } - } else if (!interceptor->getter()->IsUndefined()) { - // TODO(verwaest): Use GetPropertyWithInterceptor? - Handle<Object> result; - if (it->IsElement()) { - uint32_t index = it->index(); - v8::IndexedPropertyGetterCallback getter = - v8::ToCData<v8::IndexedPropertyGetterCallback>(interceptor->getter()); - result = args.Call(getter, index); - } else { - Handle<Name> name = it->name(); - DCHECK(!name->IsPrivate()); - v8::GenericNamedPropertyGetterCallback getter = - v8::ToCData<v8::GenericNamedPropertyGetterCallback>( - interceptor->getter()); - result = args.Call(getter, name); - } - if (!result.is_null()) return Just(DONT_ENUM); - } - - RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<PropertyAttributes>()); - return Just(ABSENT); + return GetPropertyAttributesWithInterceptorInternal(it, it->GetInterceptor()); } - Maybe<PropertyAttributes> JSReceiver::GetPropertyAttributes( LookupIterator* it) { for (; it->IsFound(); it->Next()) { @@ -5660,7 +5920,7 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object, // Compute the length of the instance descriptor. for (int i = 0; i < instance_descriptor_length; i++) { int index = Smi::cast(iteration_order->get(i))->value(); - DCHECK(dictionary->IsKey(dictionary->KeyAt(index))); + DCHECK(dictionary->IsKey(isolate, dictionary->KeyAt(index))); Object* value = dictionary->ValueAt(index); PropertyType type = dictionary->DetailsAt(index).type(); @@ -5876,216 +6136,56 @@ static Smi* GenerateIdentityHash(Isolate* isolate) { return Smi::FromInt(hash_value); } +template <typename ProxyType> +static Smi* GetOrCreateIdentityHashHelper(Isolate* isolate, + Handle<ProxyType> proxy) { + Object* maybe_hash = proxy->hash(); + if (maybe_hash->IsSmi()) return Smi::cast(maybe_hash); -template<typename ProxyType> -static Handle<Smi> GetOrCreateIdentityHashHelper(Handle<ProxyType> proxy) { - Isolate* isolate = proxy->GetIsolate(); - - Handle<Object> maybe_hash(proxy->hash(), isolate); - if (maybe_hash->IsSmi()) return Handle<Smi>::cast(maybe_hash); - - Handle<Smi> hash(GenerateIdentityHash(isolate), isolate); - proxy->set_hash(*hash); + Smi* hash = GenerateIdentityHash(isolate); + proxy->set_hash(hash); return hash; } // static -Handle<Object> JSObject::GetIdentityHash(Isolate* isolate, - Handle<JSObject> object) { +Object* JSObject::GetIdentityHash(Isolate* isolate, Handle<JSObject> object) { if (object->IsJSGlobalProxy()) { - return handle(JSGlobalProxy::cast(*object)->hash(), isolate); + return JSGlobalProxy::cast(*object)->hash(); } Handle<Name> hash_code_symbol = isolate->factory()->hash_code_symbol(); - return JSReceiver::GetDataProperty(object, hash_code_symbol); + return *JSReceiver::GetDataProperty(object, hash_code_symbol); } // static -Handle<Smi> JSObject::GetOrCreateIdentityHash(Handle<JSObject> object) { +Smi* JSObject::GetOrCreateIdentityHash(Isolate* isolate, + Handle<JSObject> object) { if (object->IsJSGlobalProxy()) { - return GetOrCreateIdentityHashHelper(Handle<JSGlobalProxy>::cast(object)); + return GetOrCreateIdentityHashHelper(isolate, + Handle<JSGlobalProxy>::cast(object)); } - Isolate* isolate = object->GetIsolate(); Handle<Name> hash_code_symbol = isolate->factory()->hash_code_symbol(); LookupIterator it(object, hash_code_symbol, object, LookupIterator::OWN); if (it.IsFound()) { DCHECK_EQ(LookupIterator::DATA, it.state()); - Handle<Object> maybe_hash = it.GetDataValue(); - if (maybe_hash->IsSmi()) return Handle<Smi>::cast(maybe_hash); + Object* maybe_hash = *it.GetDataValue(); + if (maybe_hash->IsSmi()) return Smi::cast(maybe_hash); } - Handle<Smi> hash(GenerateIdentityHash(isolate), isolate); - CHECK(AddDataProperty(&it, hash, NONE, THROW_ON_ERROR, + Smi* hash = GenerateIdentityHash(isolate); + CHECK(AddDataProperty(&it, handle(hash, isolate), NONE, THROW_ON_ERROR, CERTAINLY_NOT_STORE_FROM_KEYED) .IsJust()); return hash; } // static -Handle<Object> JSProxy::GetIdentityHash(Isolate* isolate, - Handle<JSProxy> proxy) { - return handle(proxy->hash(), isolate); +Object* JSProxy::GetIdentityHash(Handle<JSProxy> proxy) { + return proxy->hash(); } - -Handle<Smi> JSProxy::GetOrCreateIdentityHash(Handle<JSProxy> proxy) { - return GetOrCreateIdentityHashHelper(proxy); -} - - -Object* JSObject::GetHiddenProperty(Handle<Name> key) { - DisallowHeapAllocation no_gc; - DCHECK(key->IsUniqueName()); - if (IsJSGlobalProxy()) { - // For a proxy, use the prototype as target object. - PrototypeIterator iter(GetIsolate(), this); - // If the proxy is detached, return undefined. - if (iter.IsAtEnd()) return GetHeap()->the_hole_value(); - DCHECK(iter.GetCurrent()->IsJSGlobalObject()); - return iter.GetCurrent<JSObject>()->GetHiddenProperty(key); - } - DCHECK(!IsJSGlobalProxy()); - Object* inline_value = GetHiddenPropertiesHashTable(); - - if (inline_value->IsUndefined()) return GetHeap()->the_hole_value(); - - ObjectHashTable* hashtable = ObjectHashTable::cast(inline_value); - Object* entry = hashtable->Lookup(key); - return entry; -} - - -Handle<Object> JSObject::SetHiddenProperty(Handle<JSObject> object, - Handle<Name> key, - Handle<Object> value) { - Isolate* isolate = object->GetIsolate(); - - DCHECK(key->IsUniqueName()); - if (object->IsJSGlobalProxy()) { - // For a proxy, use the prototype as target object. - PrototypeIterator iter(isolate, object); - // If the proxy is detached, return undefined. - if (iter.IsAtEnd()) return isolate->factory()->undefined_value(); - DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); - return SetHiddenProperty(PrototypeIterator::GetCurrent<JSObject>(iter), key, - value); - } - DCHECK(!object->IsJSGlobalProxy()); - - Handle<Object> inline_value(object->GetHiddenPropertiesHashTable(), isolate); - - Handle<ObjectHashTable> hashtable = - GetOrCreateHiddenPropertiesHashtable(object); - - // If it was found, check if the key is already in the dictionary. - Handle<ObjectHashTable> new_table = ObjectHashTable::Put(hashtable, key, - value); - if (*new_table != *hashtable) { - // If adding the key expanded the dictionary (i.e., Add returned a new - // dictionary), store it back to the object. - SetHiddenPropertiesHashTable(object, new_table); - } - - // Return this to mark success. - return object; -} - - -void JSObject::DeleteHiddenProperty(Handle<JSObject> object, Handle<Name> key) { - Isolate* isolate = object->GetIsolate(); - DCHECK(key->IsUniqueName()); - - if (object->IsJSGlobalProxy()) { - PrototypeIterator iter(isolate, object); - if (iter.IsAtEnd()) return; - DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); - return DeleteHiddenProperty(PrototypeIterator::GetCurrent<JSObject>(iter), - key); - } - - Object* inline_value = object->GetHiddenPropertiesHashTable(); - - if (inline_value->IsUndefined()) return; - - Handle<ObjectHashTable> hashtable(ObjectHashTable::cast(inline_value)); - bool was_present = false; - ObjectHashTable::Remove(hashtable, key, &was_present); -} - - -bool JSObject::HasHiddenProperties(Handle<JSObject> object) { - Isolate* isolate = object->GetIsolate(); - Handle<Symbol> hidden = isolate->factory()->hidden_properties_symbol(); - LookupIterator it(object, hidden, object); - Maybe<PropertyAttributes> maybe = GetPropertyAttributes(&it); - // Cannot get an exception since the hidden_properties_symbol isn't exposed to - // JS. - DCHECK(maybe.IsJust()); - return maybe.FromJust() != ABSENT; -} - - -Object* JSObject::GetHiddenPropertiesHashTable() { - DCHECK(!IsJSGlobalProxy()); - if (HasFastProperties()) { - // If the object has fast properties, check whether the first slot - // in the descriptor array matches the hidden string. Since the - // hidden strings hash code is zero (and no other name has hash - // code zero) it will always occupy the first entry if present. - DescriptorArray* descriptors = this->map()->instance_descriptors(); - if (descriptors->number_of_descriptors() > 0) { - int sorted_index = descriptors->GetSortedKeyIndex(0); - if (descriptors->GetKey(sorted_index) == - GetHeap()->hidden_properties_symbol() && - sorted_index < map()->NumberOfOwnDescriptors()) { - DCHECK(descriptors->GetType(sorted_index) == DATA); - DCHECK(descriptors->GetDetails(sorted_index).representation(). - IsCompatibleForLoad(Representation::Tagged())); - FieldIndex index = FieldIndex::ForDescriptor(this->map(), - sorted_index); - return this->RawFastPropertyAt(index); - } else { - return GetHeap()->undefined_value(); - } - } else { - return GetHeap()->undefined_value(); - } - } else { - Isolate* isolate = GetIsolate(); - Handle<Symbol> hidden = isolate->factory()->hidden_properties_symbol(); - Handle<JSObject> receiver(this, isolate); - LookupIterator it(receiver, hidden, receiver); - // Access check is always skipped for the hidden string anyways. - return *GetDataProperty(&it); - } -} - -Handle<ObjectHashTable> JSObject::GetOrCreateHiddenPropertiesHashtable( - Handle<JSObject> object) { - Isolate* isolate = object->GetIsolate(); - - static const int kInitialCapacity = 4; - Handle<Object> inline_value(object->GetHiddenPropertiesHashTable(), isolate); - if (inline_value->IsHashTable()) { - return Handle<ObjectHashTable>::cast(inline_value); - } - - Handle<ObjectHashTable> hashtable = ObjectHashTable::New( - isolate, kInitialCapacity, USE_CUSTOM_MINIMUM_CAPACITY); - - DCHECK(inline_value->IsUndefined()); - SetHiddenPropertiesHashTable(object, hashtable); - return hashtable; -} - - -Handle<Object> JSObject::SetHiddenPropertiesHashTable(Handle<JSObject> object, - Handle<Object> value) { - DCHECK(!object->IsJSGlobalProxy()); - Isolate* isolate = object->GetIsolate(); - Handle<Symbol> name = isolate->factory()->hidden_properties_symbol(); - SetOwnPropertyIgnoreAttributes(object, name, value, DONT_ENUM).Assert(); - return object; +Smi* JSProxy::GetOrCreateIdentityHash(Isolate* isolate, Handle<JSProxy> proxy) { + return GetOrCreateIdentityHashHelper(isolate, proxy); } @@ -6098,7 +6198,7 @@ Maybe<bool> JSObject::DeletePropertyWithInterceptor(LookupIterator* it, DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); Handle<InterceptorInfo> interceptor(it->GetInterceptor()); - if (interceptor->deleter()->IsUndefined()) return Nothing<bool>(); + if (interceptor->deleter()->IsUndefined(isolate)) return Nothing<bool>(); Handle<JSObject> holder = it->GetHolder<JSObject>(); Handle<Object> receiver = it->GetReceiver(); @@ -6132,7 +6232,7 @@ Maybe<bool> JSObject::DeletePropertyWithInterceptor(LookupIterator* it, DCHECK(result->IsBoolean()); // Rebox CustomArguments::kReturnValueOffset before returning. - return Just(result->IsTrue()); + return Just(result->IsTrue(isolate)); } @@ -6149,9 +6249,8 @@ void JSReceiver::DeleteNormalizedProperty(Handle<JSReceiver> object, auto cell = PropertyCell::InvalidateEntry(dictionary, entry); cell->set_value(isolate->heap()->the_hole_value()); - // TODO(ishell): InvalidateForDelete cell->set_property_details( - cell->property_details().set_cell_type(PropertyCellType::kInvalidated)); + PropertyDetails::Empty(PropertyCellType::kUninitialized)); } else { Handle<NameDictionary> dictionary(object->property_dictionary()); DCHECK_NE(NameDictionary::kNotFound, entry); @@ -6185,11 +6284,6 @@ Maybe<bool> JSReceiver::DeleteProperty(LookupIterator* it, } Handle<JSObject> receiver = Handle<JSObject>::cast(it->GetReceiver()); - bool is_observed = receiver->map()->is_observed() && - (it->IsElement() || !it->name()->IsPrivate()); - - Handle<Object> old_value = it->factory()->the_hole_value(); - for (; it->IsFound(); it->Next()) { switch (it->state()) { case LookupIterator::JSPROXY: @@ -6217,10 +6311,6 @@ Maybe<bool> JSReceiver::DeleteProperty(LookupIterator* it, case LookupIterator::INTEGER_INDEXED_EXOTIC: return Just(true); case LookupIterator::DATA: - if (is_observed) { - old_value = it->GetDataValue(); - } - // Fall through. case LookupIterator::ACCESSOR: { if (!it->IsConfigurable()) { // Fail if the property is not configurable. @@ -6235,13 +6325,6 @@ Maybe<bool> JSReceiver::DeleteProperty(LookupIterator* it, it->Delete(); - if (is_observed) { - RETURN_ON_EXCEPTION_VALUE( - isolate, JSObject::EnqueueChangeRecord(receiver, "delete", - it->GetName(), old_value), - Nothing<bool>()); - } - return Just(true); } } @@ -6254,7 +6337,7 @@ Maybe<bool> JSReceiver::DeleteProperty(LookupIterator* it, Maybe<bool> JSReceiver::DeleteElement(Handle<JSReceiver> object, uint32_t index, LanguageMode language_mode) { LookupIterator it(object->GetIsolate(), object, index, object, - LookupIterator::HIDDEN); + LookupIterator::OWN); return DeleteProperty(&it, language_mode); } @@ -6262,7 +6345,7 @@ Maybe<bool> JSReceiver::DeleteElement(Handle<JSReceiver> object, uint32_t index, Maybe<bool> JSReceiver::DeleteProperty(Handle<JSReceiver> object, Handle<Name> name, LanguageMode language_mode) { - LookupIterator it(object, name, object, LookupIterator::HIDDEN); + LookupIterator it(object, name, object, LookupIterator::OWN); return DeleteProperty(&it, language_mode); } @@ -6271,13 +6354,15 @@ Maybe<bool> JSReceiver::DeletePropertyOrElement(Handle<JSReceiver> object, Handle<Name> name, LanguageMode language_mode) { LookupIterator it = LookupIterator::PropertyOrElement( - name->GetIsolate(), object, name, object, LookupIterator::HIDDEN); + name->GetIsolate(), object, name, object, LookupIterator::OWN); return DeleteProperty(&it, language_mode); } // ES6 7.1.14 -MaybeHandle<Object> ToPropertyKey(Isolate* isolate, Handle<Object> value) { +// static +MaybeHandle<Object> Object::ToPropertyKey(Isolate* isolate, + Handle<Object> value) { // 1. Let key be ToPrimitive(argument, hint String). MaybeHandle<Object> maybe_key = Object::ToPrimitive(value, ToPrimitiveHint::kString); @@ -6348,16 +6433,15 @@ MaybeHandle<Object> JSReceiver::DefineProperties(Isolate* isolate, // 2. Let props be ToObject(Properties). // 3. ReturnIfAbrupt(props). Handle<JSReceiver> props; - if (!Object::ToObject(isolate, properties).ToHandle(&props)) { - THROW_NEW_ERROR(isolate, - NewTypeError(MessageTemplate::kUndefinedOrNullToObject), - Object); - } + ASSIGN_RETURN_ON_EXCEPTION(isolate, props, + Object::ToObject(isolate, properties), Object); + // 4. Let keys be props.[[OwnPropertyKeys]](). // 5. ReturnIfAbrupt(keys). Handle<FixedArray> keys; ASSIGN_RETURN_ON_EXCEPTION( - isolate, keys, JSReceiver::GetKeys(props, OWN_ONLY, ALL_PROPERTIES), + isolate, keys, KeyAccumulator::GetKeys(props, KeyCollectionMode::kOwnOnly, + ALL_PROPERTIES), Object); // 6. Let descriptors be an empty List. int capacity = keys->length(); @@ -6370,7 +6454,7 @@ MaybeHandle<Object> JSReceiver::DefineProperties(Isolate* isolate, // 7b. ReturnIfAbrupt(propDesc). bool success = false; LookupIterator it = LookupIterator::PropertyOrElement( - isolate, props, next_key, &success, LookupIterator::HIDDEN); + isolate, props, next_key, &success, LookupIterator::OWN); DCHECK(success); Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it); if (!maybe.IsJust()) return MaybeHandle<Object>(); @@ -6445,7 +6529,7 @@ Maybe<bool> JSReceiver::OrdinaryDefineOwnProperty(Isolate* isolate, bool success = false; DCHECK(key->IsName() || key->IsNumber()); // |key| is a PropertyKey... LookupIterator it = LookupIterator::PropertyOrElement( - isolate, object, key, &success, LookupIterator::HIDDEN); + isolate, object, key, &success, LookupIterator::OWN); DCHECK(success); // ...so creating a LookupIterator can't fail. // Deal with access checks first. @@ -6990,10 +7074,7 @@ Maybe<bool> JSArray::ArraySetLength(Isolate* isolate, Handle<JSArray> a, // (Not needed.) } // Most of steps 16 through 19 is implemented by JSArray::SetLength. - if (JSArray::ObservableSetLength(a, new_len).is_null()) { - DCHECK(isolate->has_pending_exception()); - return Nothing<bool>(); - } + JSArray::SetLength(a, new_len); // Steps 19d-ii, 20. if (!new_writable) { PropertyDescriptor readonly; @@ -7025,7 +7106,7 @@ Maybe<bool> JSProxy::DefineOwnProperty(Isolate* isolate, Handle<JSProxy> proxy, Handle<Object> key, PropertyDescriptor* desc, ShouldThrow should_throw) { - STACK_CHECK(Nothing<bool>()); + STACK_CHECK(isolate, Nothing<bool>()); if (key->IsSymbol() && Handle<Symbol>::cast(key)->IsPrivate()) { return SetPrivateProperty(isolate, proxy, Handle<Symbol>::cast(key), desc, should_throw); @@ -7051,7 +7132,7 @@ Maybe<bool> JSProxy::DefineOwnProperty(Isolate* isolate, Handle<JSProxy> proxy, Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name), Nothing<bool>()); // 7. If trap is undefined, then: - if (trap->IsUndefined()) { + if (trap->IsUndefined(isolate)) { // 7a. Return target.[[DefineOwnProperty]](P, Desc). return JSReceiver::DefineOwnProperty(isolate, target, key, desc, should_throw); @@ -7175,7 +7256,7 @@ Maybe<bool> JSReceiver::GetOwnPropertyDescriptor(Isolate* isolate, bool success = false; DCHECK(key->IsName() || key->IsNumber()); // |key| is a PropertyKey... LookupIterator it = LookupIterator::PropertyOrElement( - isolate, object, key, &success, LookupIterator::HIDDEN); + isolate, object, key, &success, LookupIterator::OWN); DCHECK(success); // ...so creating a LookupIterator can't fail. return GetOwnPropertyDescriptor(&it, desc); } @@ -7246,7 +7327,7 @@ Maybe<bool> JSProxy::GetOwnPropertyDescriptor(Isolate* isolate, Handle<Name> name, PropertyDescriptor* desc) { DCHECK(!name->IsPrivate()); - STACK_CHECK(Nothing<bool>()); + STACK_CHECK(isolate, Nothing<bool>()); Handle<String> trap_name = isolate->factory()->getOwnPropertyDescriptor_string(); @@ -7269,7 +7350,7 @@ Maybe<bool> JSProxy::GetOwnPropertyDescriptor(Isolate* isolate, Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name), Nothing<bool>()); // 7. If trap is undefined, then - if (trap->IsUndefined()) { + if (trap->IsUndefined(isolate)) { // 7a. Return target.[[GetOwnProperty]](P). return JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, desc); } @@ -7282,7 +7363,8 @@ Maybe<bool> JSProxy::GetOwnPropertyDescriptor(Isolate* isolate, Nothing<bool>()); // 9. If Type(trapResultObj) is neither Object nor Undefined, throw a // TypeError exception. - if (!trap_result_obj->IsJSReceiver() && !trap_result_obj->IsUndefined()) { + if (!trap_result_obj->IsJSReceiver() && + !trap_result_obj->IsUndefined(isolate)) { isolate->Throw(*isolate->factory()->NewTypeError( MessageTemplate::kProxyGetOwnPropertyDescriptorInvalid, name)); return Nothing<bool>(); @@ -7293,7 +7375,7 @@ Maybe<bool> JSProxy::GetOwnPropertyDescriptor(Isolate* isolate, JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc); MAYBE_RETURN(found, Nothing<bool>()); // 11. If trapResultObj is undefined, then - if (trap_result_obj->IsUndefined()) { + if (trap_result_obj->IsUndefined(isolate)) { // 11a. If targetDesc is undefined, return undefined. if (!found.FromJust()) return Just(false); // 11b. If targetDesc.[[Configurable]] is false, throw a TypeError @@ -7358,19 +7440,20 @@ Maybe<bool> JSProxy::GetOwnPropertyDescriptor(Isolate* isolate, bool JSObject::ReferencesObjectFromElements(FixedArray* elements, ElementsKind kind, Object* object) { + Isolate* isolate = elements->GetIsolate(); if (IsFastObjectElementsKind(kind) || kind == FAST_STRING_WRAPPER_ELEMENTS) { int length = IsJSArray() ? Smi::cast(JSArray::cast(this)->length())->value() : elements->length(); for (int i = 0; i < length; ++i) { Object* element = elements->get(i); - if (!element->IsTheHole() && element == object) return true; + if (!element->IsTheHole(isolate) && element == object) return true; } } else { DCHECK(kind == DICTIONARY_ELEMENTS || kind == SLOW_STRING_WRAPPER_ELEMENTS); Object* key = SeededNumberDictionary::cast(elements)->SlowReverseLookup(object); - if (!key->IsUndefined()) return true; + if (!key->IsUndefined(isolate)) return true; } return false; } @@ -7394,7 +7477,7 @@ bool JSObject::ReferencesObject(Object* obj) { // Check if the object is among the named properties. Object* key = SlowReverseLookup(obj); - if (!key->IsUndefined()) { + if (!key->IsUndefined(heap->isolate())) { return true; } @@ -7432,7 +7515,7 @@ bool JSObject::ReferencesObject(Object* obj) { int length = parameter_map->length(); for (int i = 2; i < length; ++i) { Object* value = parameter_map->get(i); - if (!value->IsTheHole() && value == obj) return true; + if (!value->IsTheHole(heap->isolate()) && value == obj) return true; } // Check the arguments. FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); @@ -7500,8 +7583,7 @@ Maybe<bool> JSReceiver::SetIntegrityLevel(Handle<JSReceiver> receiver, if (receiver->IsJSObject()) { Handle<JSObject> object = Handle<JSObject>::cast(receiver); - if (!object->HasSloppyArgumentsElements() && - !object->map()->is_observed()) { // Fast path. + if (!object->HasSloppyArgumentsElements()) { // Fast path. if (level == SEALED) { return JSObject::PreventExtensionsWithTransition<SEALED>(object, should_throw); @@ -7605,7 +7687,7 @@ Maybe<bool> JSReceiver::PreventExtensions(Handle<JSReceiver> object, Maybe<bool> JSProxy::PreventExtensions(Handle<JSProxy> proxy, ShouldThrow should_throw) { Isolate* isolate = proxy->GetIsolate(); - STACK_CHECK(Nothing<bool>()); + STACK_CHECK(isolate, Nothing<bool>()); Factory* factory = isolate->factory(); Handle<String> trap_name = factory->preventExtensions_string(); @@ -7620,7 +7702,7 @@ Maybe<bool> JSProxy::PreventExtensions(Handle<JSProxy> proxy, Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>()); - if (trap->IsUndefined()) { + if (trap->IsUndefined(isolate)) { return JSReceiver::PreventExtensions(target, should_throw); } @@ -7652,7 +7734,7 @@ Maybe<bool> JSObject::PreventExtensions(Handle<JSObject> object, ShouldThrow should_throw) { Isolate* isolate = object->GetIsolate(); - if (!object->HasSloppyArgumentsElements() && !object->map()->is_observed()) { + if (!object->HasSloppyArgumentsElements()) { return PreventExtensionsWithTransition<NONE>(object, should_throw); } @@ -7693,13 +7775,6 @@ Maybe<bool> JSObject::PreventExtensions(Handle<JSObject> object, JSObject::MigrateToMap(object, new_map); DCHECK(!object->map()->is_extensible()); - if (object->map()->is_observed()) { - RETURN_ON_EXCEPTION_VALUE( - isolate, - EnqueueChangeRecord(object, "preventExtensions", Handle<Name>(), - isolate->factory()->the_hole_value()), - Nothing<bool>()); - } return Just(true); } @@ -7714,7 +7789,7 @@ Maybe<bool> JSReceiver::IsExtensible(Handle<JSReceiver> object) { Maybe<bool> JSProxy::IsExtensible(Handle<JSProxy> proxy) { Isolate* isolate = proxy->GetIsolate(); - STACK_CHECK(Nothing<bool>()); + STACK_CHECK(isolate, Nothing<bool>()); Factory* factory = isolate->factory(); Handle<String> trap_name = factory->isExtensible_string(); @@ -7729,7 +7804,7 @@ Maybe<bool> JSProxy::IsExtensible(Handle<JSProxy> proxy) { Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>()); - if (trap->IsUndefined()) { + if (trap->IsUndefined(isolate)) { return JSReceiver::IsExtensible(target); } @@ -7773,9 +7848,10 @@ template <typename Dictionary> static void ApplyAttributesToDictionary(Dictionary* dictionary, const PropertyAttributes attributes) { int capacity = dictionary->Capacity(); + Isolate* isolate = dictionary->GetIsolate(); for (int i = 0; i < capacity; i++) { Object* k = dictionary->KeyAt(i); - if (dictionary->IsKey(k) && + if (dictionary->IsKey(isolate, k) && !(k->IsSymbol() && Symbol::cast(k)->is_private())) { PropertyDetails details = dictionary->DetailsAt(i); int attrs = attributes; @@ -7800,7 +7876,6 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition( // Sealing/freezing sloppy arguments should be handled elsewhere. DCHECK(!object->HasSloppyArgumentsElements()); - DCHECK(!object->map()->is_observed()); Isolate* isolate = object->GetIsolate(); if (object->IsAccessCheckNeeded() && @@ -7919,28 +7994,6 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition( } -void JSObject::SetObserved(Handle<JSObject> object) { - DCHECK(!object->IsJSGlobalProxy()); - DCHECK(!object->IsJSGlobalObject()); - Isolate* isolate = object->GetIsolate(); - Handle<Map> new_map; - Handle<Map> old_map(object->map(), isolate); - DCHECK(!old_map->is_observed()); - Map* transition = TransitionArray::SearchSpecial( - *old_map, isolate->heap()->observed_symbol()); - if (transition != NULL) { - new_map = handle(transition, isolate); - DCHECK(new_map->is_observed()); - } else if (TransitionArray::CanHaveMoreTransitions(old_map)) { - new_map = Map::CopyForObserved(old_map); - } else { - new_map = Map::Copy(old_map, "SlowObserved"); - new_map->set_is_observed(); - } - JSObject::MigrateToMap(object, new_map); -} - - Handle<Object> JSObject::FastPropertyAt(Handle<JSObject> object, Representation representation, FieldIndex index) { @@ -8069,9 +8122,8 @@ MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk( // an array. PropertyFilter filter = static_cast<PropertyFilter>( ONLY_WRITABLE | ONLY_ENUMERABLE | ONLY_CONFIGURABLE); - KeyAccumulator accumulator(isolate, OWN_ONLY, filter); - accumulator.NextPrototype(); - copy->CollectOwnPropertyNames(&accumulator, filter); + KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly, filter); + accumulator.CollectOwnPropertyNames(copy, copy); Handle<FixedArray> names = accumulator.GetKeys(); for (int i = 0; i < names->length(); i++) { DCHECK(names->get(i)->IsName()); @@ -8126,7 +8178,7 @@ MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk( int capacity = element_dictionary->Capacity(); for (int i = 0; i < capacity; i++) { Object* k = element_dictionary->KeyAt(i); - if (element_dictionary->IsKey(k)) { + if (element_dictionary->IsKey(isolate, k)) { Handle<Object> value(element_dictionary->ValueAt(i), isolate); if (value->IsJSObject()) { Handle<JSObject> result; @@ -8205,19 +8257,9 @@ MaybeHandle<Object> JSReceiver::ToPrimitive(Handle<JSReceiver> receiver, ASSIGN_RETURN_ON_EXCEPTION( isolate, exotic_to_prim, GetMethod(receiver, isolate->factory()->to_primitive_symbol()), Object); - if (!exotic_to_prim->IsUndefined()) { - Handle<Object> hint_string; - switch (hint) { - case ToPrimitiveHint::kDefault: - hint_string = isolate->factory()->default_string(); - break; - case ToPrimitiveHint::kNumber: - hint_string = isolate->factory()->number_string(); - break; - case ToPrimitiveHint::kString: - hint_string = isolate->factory()->string_string(); - break; - } + if (!exotic_to_prim->IsUndefined(isolate)) { + Handle<Object> hint_string = + isolate->factory()->ToPrimitiveHintString(hint); Handle<Object> result; ASSIGN_RETURN_ON_EXCEPTION( isolate, result, @@ -8335,28 +8377,6 @@ bool JSObject::HasEnumerableElements() { return true; } -// Tests for the fast common case for property enumeration: -// - This object and all prototypes has an enum cache (which means that -// it is no proxy, has no interceptors and needs no access checks). -// - This object has no elements. -// - No prototype has enumerable properties/elements. -bool JSReceiver::IsSimpleEnum() { - for (PrototypeIterator iter(GetIsolate(), this, - PrototypeIterator::START_AT_RECEIVER); - !iter.IsAtEnd(); iter.Advance()) { - if (!iter.GetCurrent()->IsJSObject()) return false; - JSObject* current = iter.GetCurrent<JSObject>(); - int enum_length = current->map()->EnumLength(); - if (enum_length == kInvalidEnumCacheSentinel) return false; - if (current->IsAccessCheckNeeded()) return false; - DCHECK(!current->HasNamedInterceptor()); - DCHECK(!current->HasIndexedInterceptor()); - if (current->HasEnumerableElements()) return false; - if (current != this && enum_length != 0) return false; - } - return true; -} - int Map::NumberOfDescribedProperties(DescriptorFlag which, PropertyFilter filter) { @@ -8390,447 +8410,14 @@ int Map::NextFreePropertyIndex() { } -static bool ContainsOnlyValidKeys(Handle<FixedArray> array) { - int len = array->length(); - for (int i = 0; i < len; i++) { - Object* e = array->get(i); - if (!(e->IsName() || e->IsNumber())) return false; - } - return true; -} - - -static Handle<FixedArray> ReduceFixedArrayTo( - Handle<FixedArray> array, int length) { - DCHECK_LE(length, array->length()); - if (array->length() == length) return array; - return array->GetIsolate()->factory()->CopyFixedArrayUpTo(array, length); -} - bool Map::OnlyHasSimpleProperties() { // Wrapped string elements aren't explicitly stored in the elements backing // store, but are loaded indirectly from the underlying string. return !IsStringWrapperElementsKind(elements_kind()) && - (instance_type() > LAST_SPECIAL_RECEIVER_TYPE && - instance_type() != JS_GLOBAL_PROXY_TYPE) && + instance_type() > LAST_SPECIAL_RECEIVER_TYPE && !has_hidden_prototype() && !is_dictionary_map(); } -// static -Handle<FixedArray> JSObject::GetFastEnumPropertyKeys(Isolate* isolate, - Handle<JSObject> object) { - Handle<Map> map(object->map()); - bool cache_enum_length = map->OnlyHasSimpleProperties(); - - Handle<DescriptorArray> descs = - Handle<DescriptorArray>(map->instance_descriptors(), isolate); - int own_property_count = map->EnumLength(); - // If the enum length of the given map is set to kInvalidEnumCache, this - // means that the map itself has never used the present enum cache. The - // first step to using the cache is to set the enum length of the map by - // counting the number of own descriptors that are ENUMERABLE_STRINGS. - if (own_property_count == kInvalidEnumCacheSentinel) { - own_property_count = - map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS); - } else { - DCHECK( - own_property_count == - map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS)); - } - - if (descs->HasEnumCache()) { - Handle<FixedArray> keys(descs->GetEnumCache(), isolate); - // In case the number of properties required in the enum are actually - // present, we can reuse the enum cache. Otherwise, this means that the - // enum cache was generated for a previous (smaller) version of the - // Descriptor Array. In that case we regenerate the enum cache. - if (own_property_count <= keys->length()) { - isolate->counters()->enum_cache_hits()->Increment(); - if (cache_enum_length) map->SetEnumLength(own_property_count); - return ReduceFixedArrayTo(keys, own_property_count); - } - } - - if (descs->IsEmpty()) { - isolate->counters()->enum_cache_hits()->Increment(); - if (cache_enum_length) map->SetEnumLength(0); - return isolate->factory()->empty_fixed_array(); - } - - isolate->counters()->enum_cache_misses()->Increment(); - - Handle<FixedArray> storage = - isolate->factory()->NewFixedArray(own_property_count); - Handle<FixedArray> indices = - isolate->factory()->NewFixedArray(own_property_count); - - int size = map->NumberOfOwnDescriptors(); - int index = 0; - - for (int i = 0; i < size; i++) { - PropertyDetails details = descs->GetDetails(i); - if (details.IsDontEnum()) continue; - Object* key = descs->GetKey(i); - if (key->IsSymbol()) continue; - storage->set(index, key); - if (!indices.is_null()) { - if (details.type() != DATA) { - indices = Handle<FixedArray>(); - } else { - FieldIndex field_index = FieldIndex::ForDescriptor(*map, i); - int load_by_field_index = field_index.GetLoadByFieldIndex(); - indices->set(index, Smi::FromInt(load_by_field_index)); - } - } - index++; - } - DCHECK(index == storage->length()); - - DescriptorArray::SetEnumCache(descs, isolate, storage, indices); - if (cache_enum_length) { - map->SetEnumLength(own_property_count); - } - return storage; -} - - -Handle<FixedArray> JSObject::GetEnumPropertyKeys(Handle<JSObject> object) { - Isolate* isolate = object->GetIsolate(); - if (object->HasFastProperties()) { - return GetFastEnumPropertyKeys(isolate, object); - } else if (object->IsJSGlobalObject()) { - Handle<GlobalDictionary> dictionary(object->global_dictionary()); - int length = dictionary->NumberOfEnumElements(); - if (length == 0) { - return isolate->factory()->empty_fixed_array(); - } - Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length); - dictionary->CopyEnumKeysTo(*storage); - return storage; - } else { - Handle<NameDictionary> dictionary(object->property_dictionary()); - int length = dictionary->NumberOfEnumElements(); - if (length == 0) { - return isolate->factory()->empty_fixed_array(); - } - Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length); - dictionary->CopyEnumKeysTo(*storage); - return storage; - } -} - - -enum IndexedOrNamed { kIndexed, kNamed }; - - -// Returns |true| on success, |nothing| on exception. -template <class Callback, IndexedOrNamed type> -static Maybe<bool> GetKeysFromInterceptor(Isolate* isolate, - Handle<JSReceiver> receiver, - Handle<JSObject> object, - PropertyFilter filter, - KeyAccumulator* accumulator) { - if (type == kIndexed) { - if (!object->HasIndexedInterceptor()) return Just(true); - } else { - if (!object->HasNamedInterceptor()) return Just(true); - } - Handle<InterceptorInfo> interceptor(type == kIndexed - ? object->GetIndexedInterceptor() - : object->GetNamedInterceptor(), - isolate); - if ((filter & ONLY_ALL_CAN_READ) && !interceptor->all_can_read()) { - return Just(true); - } - PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, - *object, Object::DONT_THROW); - Handle<JSObject> result; - if (!interceptor->enumerator()->IsUndefined()) { - Callback enum_fun = v8::ToCData<Callback>(interceptor->enumerator()); - const char* log_tag = type == kIndexed ? "interceptor-indexed-enum" - : "interceptor-named-enum"; - LOG(isolate, ApiObjectAccess(log_tag, *object)); - result = args.Call(enum_fun); - } - RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); - if (result.is_null()) return Just(true); - DCHECK(result->IsJSArray() || result->HasSloppyArgumentsElements()); - // The accumulator takes care of string/symbol filtering. - if (type == kIndexed) { - accumulator->AddElementKeysFromInterceptor(result); - } else { - accumulator->AddKeys(result, DO_NOT_CONVERT); - } - return Just(true); -} - - -// Returns |true| on success, |false| if prototype walking should be stopped, -// |nothing| if an exception was thrown. -static Maybe<bool> GetKeysFromJSObject(Isolate* isolate, - Handle<JSReceiver> receiver, - Handle<JSObject> object, - PropertyFilter* filter, - KeyCollectionType type, - KeyAccumulator* accumulator) { - accumulator->NextPrototype(); - // Check access rights if required. - if (object->IsAccessCheckNeeded() && - !isolate->MayAccess(handle(isolate->context()), object)) { - // The cross-origin spec says that [[Enumerate]] shall return an empty - // iterator when it doesn't have access... - if (type == INCLUDE_PROTOS) { - return Just(false); - } - // ...whereas [[OwnPropertyKeys]] shall return whitelisted properties. - DCHECK_EQ(OWN_ONLY, type); - *filter = static_cast<PropertyFilter>(*filter | ONLY_ALL_CAN_READ); - } - - JSObject::CollectOwnElementKeys(object, accumulator, *filter); - - // Add the element keys from the interceptor. - Maybe<bool> success = - GetKeysFromInterceptor<v8::IndexedPropertyEnumeratorCallback, kIndexed>( - isolate, receiver, object, *filter, accumulator); - MAYBE_RETURN(success, Nothing<bool>()); - - if (*filter == ENUMERABLE_STRINGS) { - Handle<FixedArray> enum_keys = JSObject::GetEnumPropertyKeys(object); - accumulator->AddKeys(enum_keys, DO_NOT_CONVERT); - } else { - object->CollectOwnPropertyNames(accumulator, *filter); - } - - // Add the property keys from the interceptor. - success = GetKeysFromInterceptor<v8::GenericNamedPropertyEnumeratorCallback, - kNamed>(isolate, receiver, object, *filter, - accumulator); - MAYBE_RETURN(success, Nothing<bool>()); - return Just(true); -} - - -// Helper function for JSReceiver::GetKeys() below. Can be called recursively. -// Returns |true| or |nothing|. -static Maybe<bool> GetKeys_Internal(Isolate* isolate, - Handle<JSReceiver> receiver, - Handle<JSReceiver> object, - KeyCollectionType type, - PropertyFilter filter, - KeyAccumulator* accumulator) { - // Proxies have no hidden prototype and we should not trigger the - // [[GetPrototypeOf]] trap on the last iteration when using - // AdvanceFollowingProxies. - if (type == OWN_ONLY && object->IsJSProxy()) { - MAYBE_RETURN(JSProxy::OwnPropertyKeys(isolate, receiver, - Handle<JSProxy>::cast(object), filter, - accumulator), - Nothing<bool>()); - return Just(true); - } - - PrototypeIterator::WhereToEnd end = type == OWN_ONLY - ? PrototypeIterator::END_AT_NON_HIDDEN - : PrototypeIterator::END_AT_NULL; - for (PrototypeIterator iter(isolate, object, - PrototypeIterator::START_AT_RECEIVER, end); - !iter.IsAtEnd();) { - Handle<JSReceiver> current = - PrototypeIterator::GetCurrent<JSReceiver>(iter); - Maybe<bool> result = Just(false); // Dummy initialization. - if (current->IsJSProxy()) { - result = JSProxy::OwnPropertyKeys(isolate, receiver, - Handle<JSProxy>::cast(current), filter, - accumulator); - } else { - DCHECK(current->IsJSObject()); - result = GetKeysFromJSObject(isolate, receiver, - Handle<JSObject>::cast(current), &filter, - type, accumulator); - } - MAYBE_RETURN(result, Nothing<bool>()); - if (!result.FromJust()) break; // |false| means "stop iterating". - // Iterate through proxies but ignore access checks for the ALL_CAN_READ - // case on API objects for OWN_ONLY keys handlede in GgetKeysFromJSObject. - if (!iter.AdvanceFollowingProxiesIgnoringAccessChecks()) { - return Nothing<bool>(); - } - } - return Just(true); -} - - -// ES6 9.5.12 -// Returns |true| on success, |nothing| in case of exception. -// static -Maybe<bool> JSProxy::OwnPropertyKeys(Isolate* isolate, - Handle<JSReceiver> receiver, - Handle<JSProxy> proxy, - PropertyFilter filter, - KeyAccumulator* accumulator) { - STACK_CHECK(Nothing<bool>()); - // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O. - Handle<Object> handler(proxy->handler(), isolate); - // 2. If handler is null, throw a TypeError exception. - // 3. Assert: Type(handler) is Object. - if (proxy->IsRevoked()) { - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kProxyRevoked, isolate->factory()->ownKeys_string())); - return Nothing<bool>(); - } - // 4. Let target be the value of the [[ProxyTarget]] internal slot of O. - Handle<JSReceiver> target(proxy->target(), isolate); - // 5. Let trap be ? GetMethod(handler, "ownKeys"). - Handle<Object> trap; - ASSIGN_RETURN_ON_EXCEPTION_VALUE( - isolate, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler), - isolate->factory()->ownKeys_string()), - Nothing<bool>()); - // 6. If trap is undefined, then - if (trap->IsUndefined()) { - // 6a. Return target.[[OwnPropertyKeys]](). - return GetKeys_Internal(isolate, receiver, target, OWN_ONLY, filter, - accumulator); - } - // 7. Let trapResultArray be Call(trap, handler, «target»). - Handle<Object> trap_result_array; - Handle<Object> args[] = {target}; - ASSIGN_RETURN_ON_EXCEPTION_VALUE( - isolate, trap_result_array, - Execution::Call(isolate, trap, handler, arraysize(args), args), - Nothing<bool>()); - // 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray, - // «String, Symbol»). - Handle<FixedArray> trap_result; - ASSIGN_RETURN_ON_EXCEPTION_VALUE( - isolate, trap_result, - Object::CreateListFromArrayLike(isolate, trap_result_array, - ElementTypes::kStringAndSymbol), - Nothing<bool>()); - // 9. Let extensibleTarget be ? IsExtensible(target). - Maybe<bool> maybe_extensible = JSReceiver::IsExtensible(target); - MAYBE_RETURN(maybe_extensible, Nothing<bool>()); - bool extensible_target = maybe_extensible.FromJust(); - // 10. Let targetKeys be ? target.[[OwnPropertyKeys]](). - Handle<FixedArray> target_keys; - ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, target_keys, - JSReceiver::OwnPropertyKeys(target), - Nothing<bool>()); - // 11. (Assert) - // 12. Let targetConfigurableKeys be an empty List. - // To save memory, we're re-using target_keys and will modify it in-place. - Handle<FixedArray> target_configurable_keys = target_keys; - // 13. Let targetNonconfigurableKeys be an empty List. - Handle<FixedArray> target_nonconfigurable_keys = - isolate->factory()->NewFixedArray(target_keys->length()); - int nonconfigurable_keys_length = 0; - // 14. Repeat, for each element key of targetKeys: - for (int i = 0; i < target_keys->length(); ++i) { - // 14a. Let desc be ? target.[[GetOwnProperty]](key). - PropertyDescriptor desc; - Maybe<bool> found = JSReceiver::GetOwnPropertyDescriptor( - isolate, target, handle(target_keys->get(i), isolate), &desc); - MAYBE_RETURN(found, Nothing<bool>()); - // 14b. If desc is not undefined and desc.[[Configurable]] is false, then - if (found.FromJust() && !desc.configurable()) { - // 14b i. Append key as an element of targetNonconfigurableKeys. - target_nonconfigurable_keys->set(nonconfigurable_keys_length, - target_keys->get(i)); - nonconfigurable_keys_length++; - // The key was moved, null it out in the original list. - target_keys->set(i, Smi::FromInt(0)); - } else { - // 14c. Else, - // 14c i. Append key as an element of targetConfigurableKeys. - // (No-op, just keep it in |target_keys|.) - } - } - accumulator->NextPrototype(); // Prepare for accumulating keys. - // 15. If extensibleTarget is true and targetNonconfigurableKeys is empty, - // then: - if (extensible_target && nonconfigurable_keys_length == 0) { - // 15a. Return trapResult. - return accumulator->AddKeysFromProxy(proxy, trap_result); - } - // 16. Let uncheckedResultKeys be a new List which is a copy of trapResult. - Zone set_zone(isolate->allocator()); - const int kPresent = 1; - const int kGone = 0; - IdentityMap<int> unchecked_result_keys(isolate->heap(), &set_zone); - int unchecked_result_keys_size = 0; - for (int i = 0; i < trap_result->length(); ++i) { - DCHECK(trap_result->get(i)->IsUniqueName()); - Object* key = trap_result->get(i); - int* entry = unchecked_result_keys.Get(key); - if (*entry != kPresent) { - *entry = kPresent; - unchecked_result_keys_size++; - } - } - // 17. Repeat, for each key that is an element of targetNonconfigurableKeys: - for (int i = 0; i < nonconfigurable_keys_length; ++i) { - Object* key = target_nonconfigurable_keys->get(i); - // 17a. If key is not an element of uncheckedResultKeys, throw a - // TypeError exception. - int* found = unchecked_result_keys.Find(key); - if (found == nullptr || *found == kGone) { - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kProxyOwnKeysMissing, handle(key, isolate))); - return Nothing<bool>(); - } - // 17b. Remove key from uncheckedResultKeys. - *found = kGone; - unchecked_result_keys_size--; - } - // 18. If extensibleTarget is true, return trapResult. - if (extensible_target) { - return accumulator->AddKeysFromProxy(proxy, trap_result); - } - // 19. Repeat, for each key that is an element of targetConfigurableKeys: - for (int i = 0; i < target_configurable_keys->length(); ++i) { - Object* key = target_configurable_keys->get(i); - if (key->IsSmi()) continue; // Zapped entry, was nonconfigurable. - // 19a. If key is not an element of uncheckedResultKeys, throw a - // TypeError exception. - int* found = unchecked_result_keys.Find(key); - if (found == nullptr || *found == kGone) { - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kProxyOwnKeysMissing, handle(key, isolate))); - return Nothing<bool>(); - } - // 19b. Remove key from uncheckedResultKeys. - *found = kGone; - unchecked_result_keys_size--; - } - // 20. If uncheckedResultKeys is not empty, throw a TypeError exception. - if (unchecked_result_keys_size != 0) { - DCHECK_GT(unchecked_result_keys_size, 0); - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kProxyOwnKeysNonExtensible)); - return Nothing<bool>(); - } - // 21. Return trapResult. - return accumulator->AddKeysFromProxy(proxy, trap_result); -} - -MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, - KeyCollectionType type, - PropertyFilter filter, - GetKeysConversion keys_conversion, - bool filter_proxy_keys) { - USE(ContainsOnlyValidKeys); - Isolate* isolate = object->GetIsolate(); - KeyAccumulator accumulator(isolate, type, filter); - accumulator.set_filter_proxy_keys(filter_proxy_keys); - MAYBE_RETURN( - GetKeys_Internal(isolate, object, object, type, filter, &accumulator), - MaybeHandle<FixedArray>()); - Handle<FixedArray> keys = accumulator.GetKeys(keys_conversion); - DCHECK(ContainsOnlyValidKeys(keys)); - return keys; -} - MUST_USE_RESULT Maybe<bool> FastGetOwnValuesOrEntries( Isolate* isolate, Handle<JSReceiver> receiver, bool get_entries, Handle<FixedArray>* result) { @@ -8921,12 +8508,13 @@ MaybeHandle<FixedArray> GetOwnValuesOrEntries(Isolate* isolate, PropertyFilter key_filter = static_cast<PropertyFilter>(filter & ~ONLY_ENUMERABLE); - KeyAccumulator accumulator(isolate, OWN_ONLY, key_filter); - MAYBE_RETURN(GetKeys_Internal(isolate, object, object, OWN_ONLY, key_filter, - &accumulator), - MaybeHandle<FixedArray>()); - Handle<FixedArray> keys = accumulator.GetKeys(CONVERT_TO_STRING); - DCHECK(ContainsOnlyValidKeys(keys)); + + Handle<FixedArray> keys; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, keys, + KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, key_filter, + GetKeysConversion::kConvertToString), + MaybeHandle<FixedArray>()); values_or_entries = isolate->factory()->NewFixedArray(keys->length()); int length = 0; @@ -9011,7 +8599,7 @@ MaybeHandle<Object> JSObject::DefineAccessor(Handle<JSObject> object, Isolate* isolate = object->GetIsolate(); LookupIterator it = LookupIterator::PropertyOrElement( - isolate, object, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR); + isolate, object, name, LookupIterator::OWN_SKIP_INTERCEPTOR); return DefineAccessor(&it, getter, setter, attributes); } @@ -9039,40 +8627,11 @@ MaybeHandle<Object> JSObject::DefineAccessor(LookupIterator* it, return it->factory()->undefined_value(); } - Handle<Object> old_value = isolate->factory()->the_hole_value(); - bool is_observed = object->map()->is_observed() && - (it->IsElement() || !it->name()->IsPrivate()); - bool preexists = false; - if (is_observed) { - CHECK(GetPropertyAttributes(it).IsJust()); - preexists = it->IsFound(); - if (preexists && (it->state() == LookupIterator::DATA || - it->GetAccessors()->IsAccessorInfo())) { - old_value = Object::GetProperty(it).ToHandleChecked(); - } - } - - DCHECK(getter->IsCallable() || getter->IsUndefined() || getter->IsNull() || - getter->IsFunctionTemplateInfo()); - DCHECK(setter->IsCallable() || setter->IsUndefined() || setter->IsNull() || - getter->IsFunctionTemplateInfo()); - // At least one of the accessors needs to be a new value. - DCHECK(!getter->IsNull() || !setter->IsNull()); - if (!getter->IsNull()) { - it->TransitionToAccessorProperty(ACCESSOR_GETTER, getter, attributes); - } - if (!setter->IsNull()) { - it->TransitionToAccessorProperty(ACCESSOR_SETTER, setter, attributes); - } - - if (is_observed) { - // Make sure the top context isn't changed. - AssertNoContextChange ncc(isolate); - const char* type = preexists ? "reconfigure" : "add"; - RETURN_ON_EXCEPTION( - isolate, EnqueueChangeRecord(object, type, it->GetName(), old_value), - Object); - } + DCHECK(getter->IsCallable() || getter->IsUndefined(isolate) || + getter->IsNull(isolate) || getter->IsFunctionTemplateInfo()); + DCHECK(setter->IsCallable() || setter->IsUndefined(isolate) || + setter->IsNull(isolate) || setter->IsFunctionTemplateInfo()); + it->TransitionToAccessorProperty(getter, setter, attributes); return isolate->factory()->undefined_value(); } @@ -9084,7 +8643,7 @@ MaybeHandle<Object> JSObject::SetAccessor(Handle<JSObject> object, Handle<Name> name(Name::cast(info->name()), isolate); LookupIterator it = LookupIterator::PropertyOrElement( - isolate, object, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR); + isolate, object, name, LookupIterator::OWN_SKIP_INTERCEPTOR); // Duplicate ACCESS_CHECK outside of GetPropertyAttributes for the case that // the FailedAccessCheckCallbackFunction doesn't throw an exception. @@ -9118,53 +8677,6 @@ MaybeHandle<Object> JSObject::SetAccessor(Handle<JSObject> object, return object; } - -MaybeHandle<Object> JSObject::GetAccessor(Handle<JSObject> object, - Handle<Name> name, - AccessorComponent component) { - Isolate* isolate = object->GetIsolate(); - - // Make sure that the top context does not change when doing callbacks or - // interceptor calls. - AssertNoContextChange ncc(isolate); - - LookupIterator it = LookupIterator::PropertyOrElement( - isolate, object, name, LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR); - - for (; it.IsFound(); it.Next()) { - switch (it.state()) { - case LookupIterator::INTERCEPTOR: - case LookupIterator::NOT_FOUND: - case LookupIterator::TRANSITION: - UNREACHABLE(); - - case LookupIterator::ACCESS_CHECK: - if (it.HasAccess()) continue; - isolate->ReportFailedAccessCheck(it.GetHolder<JSObject>()); - RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); - return isolate->factory()->undefined_value(); - - case LookupIterator::JSPROXY: - return isolate->factory()->undefined_value(); - - case LookupIterator::INTEGER_INDEXED_EXOTIC: - return isolate->factory()->undefined_value(); - case LookupIterator::DATA: - continue; - case LookupIterator::ACCESSOR: { - Handle<Object> maybe_pair = it.GetAccessors(); - if (maybe_pair->IsAccessorPair()) { - return AccessorPair::GetComponent( - Handle<AccessorPair>::cast(maybe_pair), component); - } - } - } - } - - return isolate->factory()->undefined_value(); -} - - Object* JSObject::SlowReverseLookup(Object* value) { if (HasFastProperties()) { int number_of_own_descriptors = map()->NumberOfOwnDescriptors(); @@ -9236,7 +8748,8 @@ Handle<Map> Map::Normalize(Handle<Map> fast_map, PropertyNormalizationMode mode, Isolate* isolate = fast_map->GetIsolate(); Handle<Object> maybe_cache(isolate->native_context()->normalized_map_cache(), isolate); - bool use_cache = !fast_map->is_prototype_map() && !maybe_cache->IsUndefined(); + bool use_cache = + !fast_map->is_prototype_map() && !maybe_cache->IsUndefined(isolate); Handle<NormalizedMapCache> cache; if (use_cache) cache = Handle<NormalizedMapCache>::cast(maybe_cache); @@ -9319,6 +8832,17 @@ Handle<Map> Map::CopyNormalized(Handle<Map> map, return result; } +// Return an immutable prototype exotic object version of the input map. +// Never even try to cache it in the transition tree, as it is intended +// for the global object and its prototype chain, and excluding it saves +// memory on the map transition tree. + +// static +Handle<Map> Map::TransitionToImmutableProto(Handle<Map> map) { + Handle<Map> new_map = Map::Copy(map, "ImmutablePrototype"); + new_map->set_immutable_proto(true); + return new_map; +} Handle<Map> Map::CopyInitialMap(Handle<Map> map, int instance_size, int in_object_properties, @@ -9447,7 +8971,7 @@ void Map::TraceAllTransitions(Map* map) { void Map::ConnectTransition(Handle<Map> parent, Handle<Map> child, Handle<Name> name, SimpleTransitionFlag flag) { - if (!parent->GetBackPointer()->IsUndefined()) { + if (!parent->GetBackPointer()->IsUndefined(parent->GetIsolate())) { parent->set_owns_descriptors(false); } else { // |parent| is initial map and it must keep the ownership, there must be no @@ -9646,22 +9170,15 @@ Handle<Map> Map::AsLanguageMode(Handle<Map> initial_map, // using |strict_function_transition_symbol| as a key. if (language_mode == SLOPPY) return initial_map; Isolate* isolate = initial_map->GetIsolate(); - Factory* factory = isolate->factory(); - Handle<Symbol> transition_symbol; int map_index = Context::FunctionMapIndex(language_mode, kind); Handle<Map> function_map( Map::cast(isolate->native_context()->get(map_index))); - STATIC_ASSERT(LANGUAGE_END == 3); - switch (language_mode) { - case STRICT: - transition_symbol = factory->strict_function_transition_symbol(); - break; - default: - UNREACHABLE(); - break; - } + STATIC_ASSERT(LANGUAGE_END == 2); + DCHECK_EQ(STRICT, language_mode); + Handle<Symbol> transition_symbol = + isolate->factory()->strict_function_transition_symbol(); Map* maybe_transition = TransitionArray::SearchSpecial(*initial_map, *transition_symbol); if (maybe_transition != NULL) { @@ -9686,30 +9203,6 @@ Handle<Map> Map::AsLanguageMode(Handle<Map> initial_map, } -Handle<Map> Map::CopyForObserved(Handle<Map> map) { - DCHECK(!map->is_observed()); - - Isolate* isolate = map->GetIsolate(); - - bool insert_transition = - TransitionArray::CanHaveMoreTransitions(map) && !map->is_prototype_map(); - - if (insert_transition) { - Handle<Map> new_map = CopyForTransition(map, "CopyForObserved"); - new_map->set_is_observed(); - - Handle<Name> name = isolate->factory()->observed_symbol(); - ConnectTransition(map, new_map, name, SPECIAL_TRANSITION); - return new_map; - } - - // Create a new free-floating map only if we are not allowed to store it. - Handle<Map> new_map = Map::Copy(map, "CopyForObserved"); - new_map->set_is_observed(); - return new_map; -} - - Handle<Map> Map::CopyForTransition(Handle<Map> map, const char* reason) { DCHECK(!map->is_prototype_map()); Handle<Map> new_map = CopyDropDescriptors(map); @@ -9870,6 +9363,17 @@ Handle<Map> Map::TransitionToDataProperty(Handle<Map> map, Handle<Name> name, Handle<Object> value, PropertyAttributes attributes, StoreFromKeyed store_mode) { + RuntimeCallTimerScope stats_scope( + *map, map->is_prototype_map() + ? &RuntimeCallStats::PrototypeMap_TransitionToDataProperty + : &RuntimeCallStats::Map_TransitionToDataProperty); + TRACE_EVENT_RUNTIME_CALL_STATS_TRACING_SCOPED( + map->GetIsolate(), + (map->is_prototype_map() + ? &tracing::TraceEventStatsTable:: + PrototypeMap_TransitionToDataProperty + : &tracing::TraceEventStatsTable::Map_TransitionToDataProperty)) + DCHECK(name->IsUniqueName()); DCHECK(!map->is_dictionary_map()); @@ -9945,13 +9449,26 @@ Handle<Map> Map::ReconfigureExistingProperty(Handle<Map> map, int descriptor, return new_map; } -Handle<Map> Map::TransitionToAccessorProperty(Handle<Map> map, +Handle<Map> Map::TransitionToAccessorProperty(Isolate* isolate, Handle<Map> map, Handle<Name> name, int descriptor, - AccessorComponent component, - Handle<Object> accessor, + Handle<Object> getter, + Handle<Object> setter, PropertyAttributes attributes) { + RuntimeCallTimerScope stats_scope( + isolate, + map->is_prototype_map() + ? &RuntimeCallStats::PrototypeMap_TransitionToAccessorProperty + : &RuntimeCallStats::Map_TransitionToAccessorProperty); + TRACE_EVENT_RUNTIME_CALL_STATS_TRACING_SCOPED( + isolate, + (map->is_prototype_map() + ? &tracing::TraceEventStatsTable:: + PrototypeMap_TransitionToAccessorProperty + : &tracing::TraceEventStatsTable::Map_TransitionToAccessorProperty)); + + // At least one of the accessors needs to be a new value. + DCHECK(!getter->IsNull(isolate) || !setter->IsNull(isolate)); DCHECK(name->IsUniqueName()); - Isolate* isolate = name->GetIsolate(); // Dictionary maps can always have additional data properties. if (map->is_dictionary_map()) return map; @@ -9980,7 +9497,7 @@ Handle<Map> Map::TransitionToAccessorProperty(Handle<Map> map, } Handle<AccessorPair> pair = Handle<AccessorPair>::cast(maybe_pair); - if (pair->get(component) != *accessor) { + if (!pair->Equals(*getter, *setter)) { return Map::Normalize(map, mode, "TransitionToDifferentAccessor"); } @@ -10007,10 +9524,21 @@ Handle<Map> Map::TransitionToAccessorProperty(Handle<Map> map, return Map::Normalize(map, mode, "AccessorsOverwritingNonPair"); } - Object* current = Handle<AccessorPair>::cast(maybe_pair)->get(component); - if (current == *accessor) return map; + Handle<AccessorPair> current_pair = Handle<AccessorPair>::cast(maybe_pair); + if (current_pair->Equals(*getter, *setter)) return map; - if (!current->IsTheHole()) { + bool overwriting_accessor = false; + if (!getter->IsNull(isolate) && + !current_pair->get(ACCESSOR_GETTER)->IsNull(isolate) && + current_pair->get(ACCESSOR_GETTER) != *getter) { + overwriting_accessor = true; + } + if (!setter->IsNull(isolate) && + !current_pair->get(ACCESSOR_SETTER)->IsNull(isolate) && + current_pair->get(ACCESSOR_SETTER) != *setter) { + overwriting_accessor = true; + } + if (overwriting_accessor) { return Map::Normalize(map, mode, "AccessorsOverwritingAccessors"); } @@ -10022,7 +9550,8 @@ Handle<Map> Map::TransitionToAccessorProperty(Handle<Map> map, pair = isolate->factory()->NewAccessorPair(); } - pair->set(component, *accessor); + pair->SetComponents(*getter, *setter); + TransitionFlag flag = INSERT_TRANSITION; AccessorConstantDescriptor new_desc(name, pair, attributes); return Map::CopyInsertDescriptor(map, &new_desc, flag); @@ -10036,7 +9565,7 @@ Handle<Map> Map::CopyAddDescriptor(Handle<Map> map, // Share descriptors only if map owns descriptors and it not an initial map. if (flag == INSERT_TRANSITION && map->owns_descriptors() && - !map->GetBackPointer()->IsUndefined() && + !map->GetBackPointer()->IsUndefined(map->GetIsolate()) && TransitionArray::CanHaveMoreTransitions(map)) { return ShareDescriptor(map, descriptors, descriptor); } @@ -10166,222 +9695,186 @@ Handle<Map> Map::CopyReplaceDescriptor(Handle<Map> map, simple_flag); } - -void Map::UpdateCodeCache(Handle<Map> map, - Handle<Name> name, - Handle<Code> code) { - Isolate* isolate = map->GetIsolate(); - HandleScope scope(isolate); - // Allocate the code cache if not present. - if (map->code_cache()->IsFixedArray()) { - Handle<Object> result = isolate->factory()->NewCodeCache(); - map->set_code_cache(*result); +// Helper class to manage a Map's code cache. The layout depends on the number +// of entries; this is worthwhile because most code caches are very small, +// but some are huge (thousands of entries). +// For zero entries, the EmptyFixedArray is used. +// For one entry, we use a 2-element FixedArray containing [name, code]. +// For 2..100 entries, we use a FixedArray with linear lookups, the layout is: +// [0] - number of slots that are currently in use +// [1] - first name +// [2] - first code +// [3] - second name +// [4] - second code +// etc. +// For more than 128 entries, we use a CodeCacheHashTable. +class CodeCache : public AllStatic { + public: + // Returns the new cache, to be stored on the map. + static Handle<FixedArray> Put(Isolate* isolate, Handle<FixedArray> cache, + Handle<Name> name, Handle<Code> code) { + int length = cache->length(); + if (length == 0) return PutFirstElement(isolate, name, code); + if (length == kEntrySize) { + return PutSecondElement(isolate, cache, name, code); + } + if (length <= kLinearMaxSize) { + Handle<FixedArray> result = PutLinearElement(isolate, cache, name, code); + if (!result.is_null()) return result; + // Fall through if linear storage is getting too large. + } + return PutHashTableElement(isolate, cache, name, code); + } + + static Code* Lookup(FixedArray* cache, Name* name, Code::Flags flags) { + int length = cache->length(); + if (length == 0) return nullptr; + if (length == kEntrySize) return OneElementLookup(cache, name, flags); + if (!cache->IsCodeCacheHashTable()) { + return LinearLookup(cache, name, flags); + } else { + return CodeCacheHashTable::cast(cache)->Lookup(name, flags); + } } - // Update the code cache. - Handle<CodeCache> code_cache(CodeCache::cast(map->code_cache()), isolate); - CodeCache::Update(code_cache, name, code); -} - + private: + static const int kNameIndex = 0; + static const int kCodeIndex = 1; + static const int kEntrySize = 2; -Object* Map::FindInCodeCache(Name* name, Code::Flags flags) { - // Do a lookup if a code cache exists. - if (!code_cache()->IsFixedArray()) { - return CodeCache::cast(code_cache())->Lookup(name, flags); - } else { - return GetHeap()->undefined_value(); - } -} + static const int kLinearUsageIndex = 0; + static const int kLinearReservedSlots = 1; + static const int kLinearInitialCapacity = 2; + static const int kLinearMaxSize = 257; // == LinearSizeFor(128); + static const int kHashTableInitialCapacity = 200; // Number of entries. -int Map::IndexInCodeCache(Object* name, Code* code) { - // Get the internal index if a code cache exists. - if (!code_cache()->IsFixedArray()) { - return CodeCache::cast(code_cache())->GetIndex(name, code); + static int LinearSizeFor(int entries) { + return kLinearReservedSlots + kEntrySize * entries; } - return -1; -} - - -void Map::RemoveFromCodeCache(Name* name, Code* code, int index) { - // No GC is supposed to happen between a call to IndexInCodeCache and - // RemoveFromCodeCache so the code cache must be there. - DCHECK(!code_cache()->IsFixedArray()); - CodeCache::cast(code_cache())->RemoveByIndex(name, code, index); -} - -void CodeCache::Update( - Handle<CodeCache> code_cache, Handle<Name> name, Handle<Code> code) { - // The number of monomorphic stubs for normal load/store/call IC's can grow to - // a large number and therefore they need to go into a hash table. They are - // used to load global properties from cells. - if (code->type() == Code::NORMAL) { - // Make sure that a hash table is allocated for the normal load code cache. - if (code_cache->normal_type_cache()->IsUndefined()) { - Handle<Object> result = - CodeCacheHashTable::New(code_cache->GetIsolate(), - CodeCacheHashTable::kInitialSize); - code_cache->set_normal_type_cache(*result); - } - UpdateNormalTypeCache(code_cache, name, code); - } else { - DCHECK(code_cache->default_cache()->IsFixedArray()); - UpdateDefaultCache(code_cache, name, code); + static int LinearNewSize(int old_size) { + int old_entries = (old_size - kLinearReservedSlots) / kEntrySize; + return LinearSizeFor(old_entries * 2); } -} - -void CodeCache::UpdateDefaultCache( - Handle<CodeCache> code_cache, Handle<Name> name, Handle<Code> code) { - Isolate* isolate = code_cache->GetIsolate(); - // When updating the default code cache we disregard the type encoded in the - // flags. This allows call constant stubs to overwrite call field - // stubs, etc. - Code::Flags flags = Code::RemoveTypeFromFlags(code->flags()); - - // First check whether we can update existing code cache without - // extending it. - Handle<FixedArray> cache = handle(code_cache->default_cache()); - int length = cache->length(); - { - DisallowHeapAllocation no_alloc; - int deleted_index = -1; - Object* null = isolate->heap()->null_value(); - Object* undefined = isolate->heap()->undefined_value(); - DCHECK(name->IsUniqueName()); - for (int i = 0; i < length; i += kCodeCacheEntrySize) { - Object* key = cache->get(i); - if (key == null) { - if (deleted_index < 0) deleted_index = i; - continue; - } - if (key == undefined) { - if (deleted_index >= 0) i = deleted_index; - cache->set(i + kCodeCacheEntryNameOffset, *name); - cache->set(i + kCodeCacheEntryCodeOffset, *code); - return; - } - DCHECK(key->IsUniqueName()); - if (*name == key) { - Code::Flags found = - Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags(); - if (Code::RemoveTypeFromFlags(found) == flags) { - cache->set(i + kCodeCacheEntryCodeOffset, *code); - return; - } - } - } + static Code* OneElementLookup(FixedArray* cache, Name* name, + Code::Flags flags) { + DCHECK_EQ(cache->length(), kEntrySize); + if (cache->get(kNameIndex) != name) return nullptr; + Code* maybe_code = Code::cast(cache->get(kCodeIndex)); + if (maybe_code->flags() != flags) return nullptr; + return maybe_code; + } - // Reached the end of the code cache. If there were deleted - // elements, reuse the space for the first of them. - if (deleted_index >= 0) { - cache->set(deleted_index + kCodeCacheEntryNameOffset, *name); - cache->set(deleted_index + kCodeCacheEntryCodeOffset, *code); - return; + static Code* LinearLookup(FixedArray* cache, Name* name, Code::Flags flags) { + DCHECK_GE(cache->length(), kEntrySize); + DCHECK(!cache->IsCodeCacheHashTable()); + int usage = GetLinearUsage(cache); + for (int i = kLinearReservedSlots; i < usage; i += kEntrySize) { + if (cache->get(i + kNameIndex) != name) continue; + Code* code = Code::cast(cache->get(i + kCodeIndex)); + if (code->flags() == flags) return code; } + return nullptr; } - // Extend the code cache with some new entries (at least one). Must be a - // multiple of the entry size. - int new_length = length + (length >> 1) + kCodeCacheEntrySize; - new_length = new_length - new_length % kCodeCacheEntrySize; - DCHECK((new_length % kCodeCacheEntrySize) == 0); - cache = isolate->factory()->CopyFixedArrayAndGrow(cache, new_length - length); - - // Add the (name, code) pair to the new cache. - cache->set(length + kCodeCacheEntryNameOffset, *name); - cache->set(length + kCodeCacheEntryCodeOffset, *code); - code_cache->set_default_cache(*cache); -} - - -void CodeCache::UpdateNormalTypeCache( - Handle<CodeCache> code_cache, Handle<Name> name, Handle<Code> code) { - // Adding a new entry can cause a new cache to be allocated. - Handle<CodeCacheHashTable> cache( - CodeCacheHashTable::cast(code_cache->normal_type_cache())); - Handle<Object> new_cache = CodeCacheHashTable::Put(cache, name, code); - code_cache->set_normal_type_cache(*new_cache); -} - - -Object* CodeCache::Lookup(Name* name, Code::Flags flags) { - Object* result = LookupDefaultCache(name, Code::RemoveTypeFromFlags(flags)); - if (result->IsCode()) { - if (Code::cast(result)->flags() == flags) return result; - return GetHeap()->undefined_value(); + static Handle<FixedArray> PutFirstElement(Isolate* isolate, Handle<Name> name, + Handle<Code> code) { + Handle<FixedArray> cache = isolate->factory()->NewFixedArray(kEntrySize); + cache->set(kNameIndex, *name); + cache->set(kCodeIndex, *code); + return cache; } - return LookupNormalTypeCache(name, flags); -} - -Object* CodeCache::LookupDefaultCache(Name* name, Code::Flags flags) { - FixedArray* cache = default_cache(); - Heap* heap = GetHeap(); - Object* null = heap->null_value(); - Object* undefined = heap->undefined_value(); - int length = cache->length(); - DCHECK(name->IsUniqueName()); - for (int i = 0; i < length; i += kCodeCacheEntrySize) { - Object* key = cache->get(i + kCodeCacheEntryNameOffset); - // Skip deleted elements. - if (key == null) continue; - if (key == undefined) return key; - DCHECK(key->IsUniqueName()); - if (name == key) { - Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset)); - if (Code::RemoveTypeFromFlags(code->flags()) == flags) { - return code; + static Handle<FixedArray> PutSecondElement(Isolate* isolate, + Handle<FixedArray> cache, + Handle<Name> name, + Handle<Code> code) { + DCHECK_EQ(cache->length(), kEntrySize); + Handle<FixedArray> new_cache = isolate->factory()->NewFixedArray( + LinearSizeFor(kLinearInitialCapacity)); + new_cache->set(kLinearReservedSlots + kNameIndex, cache->get(kNameIndex)); + new_cache->set(kLinearReservedSlots + kCodeIndex, cache->get(kCodeIndex)); + new_cache->set(LinearSizeFor(1) + kNameIndex, *name); + new_cache->set(LinearSizeFor(1) + kCodeIndex, *code); + new_cache->set(kLinearUsageIndex, Smi::FromInt(LinearSizeFor(2))); + return new_cache; + } + + static Handle<FixedArray> PutLinearElement(Isolate* isolate, + Handle<FixedArray> cache, + Handle<Name> name, + Handle<Code> code) { + int length = cache->length(); + int usage = GetLinearUsage(*cache); + DCHECK_LE(usage, length); + // Check if we need to grow. + if (usage == length) { + int new_length = LinearNewSize(length); + if (new_length > kLinearMaxSize) return Handle<FixedArray>::null(); + Handle<FixedArray> new_cache = + isolate->factory()->NewFixedArray(new_length); + for (int i = kLinearReservedSlots; i < length; i++) { + new_cache->set(i, cache->get(i)); } + cache = new_cache; } + // Store new entry. + DCHECK_GE(cache->length(), usage + kEntrySize); + cache->set(usage + kNameIndex, *name); + cache->set(usage + kCodeIndex, *code); + cache->set(kLinearUsageIndex, Smi::FromInt(usage + kEntrySize)); + return cache; } - return GetHeap()->undefined_value(); -} - -Object* CodeCache::LookupNormalTypeCache(Name* name, Code::Flags flags) { - if (!normal_type_cache()->IsUndefined()) { - CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); - return cache->Lookup(name, flags); - } else { - return GetHeap()->undefined_value(); + static Handle<FixedArray> PutHashTableElement(Isolate* isolate, + Handle<FixedArray> cache, + Handle<Name> name, + Handle<Code> code) { + // Check if we need to transition from linear to hash table storage. + if (!cache->IsCodeCacheHashTable()) { + // Check that the initial hash table capacity is large enough. + DCHECK_EQ(kLinearMaxSize, LinearSizeFor(128)); + STATIC_ASSERT(kHashTableInitialCapacity > 128); + + int length = cache->length(); + // Only migrate from linear storage when it's full. + DCHECK_EQ(length, GetLinearUsage(*cache)); + DCHECK_EQ(length, kLinearMaxSize); + Handle<CodeCacheHashTable> table = + CodeCacheHashTable::New(isolate, kHashTableInitialCapacity); + HandleScope scope(isolate); + for (int i = kLinearReservedSlots; i < length; i += kEntrySize) { + Handle<Name> old_name(Name::cast(cache->get(i + kNameIndex)), isolate); + Handle<Code> old_code(Code::cast(cache->get(i + kCodeIndex)), isolate); + CodeCacheHashTable::Put(table, old_name, old_code); + } + cache = table; + } + // Store new entry. + DCHECK(cache->IsCodeCacheHashTable()); + return CodeCacheHashTable::Put(Handle<CodeCacheHashTable>::cast(cache), + name, code); } -} - -int CodeCache::GetIndex(Object* name, Code* code) { - if (code->type() == Code::NORMAL) { - if (normal_type_cache()->IsUndefined()) return -1; - CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); - return cache->GetIndex(Name::cast(name), code->flags()); + static inline int GetLinearUsage(FixedArray* linear_cache) { + DCHECK_GT(linear_cache->length(), kEntrySize); + return Smi::cast(linear_cache->get(kLinearUsageIndex))->value(); } +}; - FixedArray* array = default_cache(); - int len = array->length(); - for (int i = 0; i < len; i += kCodeCacheEntrySize) { - if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1; - } - return -1; +void Map::UpdateCodeCache(Handle<Map> map, + Handle<Name> name, + Handle<Code> code) { + Isolate* isolate = map->GetIsolate(); + Handle<FixedArray> cache(map->code_cache(), isolate); + Handle<FixedArray> new_cache = CodeCache::Put(isolate, cache, name, code); + map->set_code_cache(*new_cache); } - -void CodeCache::RemoveByIndex(Object* name, Code* code, int index) { - if (code->type() == Code::NORMAL) { - DCHECK(!normal_type_cache()->IsUndefined()); - CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); - DCHECK(cache->GetIndex(Name::cast(name), code->flags()) == index); - cache->RemoveByIndex(index); - } else { - FixedArray* array = default_cache(); - DCHECK(array->length() >= index && array->get(index)->IsCode()); - // Use null instead of undefined for deleted elements to distinguish - // deleted elements from unused elements. This distinction is used - // when looking up in the cache and when updating the cache. - DCHECK_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset); - array->set_null(index - 1); // Name. - array->set_null(index); // Code. - } +Code* Map::LookupInCodeCache(Name* name, Code::Flags flags) { + return CodeCache::Lookup(code_cache(), name, flags); } @@ -10392,20 +9885,23 @@ void CodeCache::RemoveByIndex(Object* name, Code* code, int index) { class CodeCacheHashTableKey : public HashTableKey { public: CodeCacheHashTableKey(Handle<Name> name, Code::Flags flags) - : name_(name), flags_(flags), code_() { } + : name_(name), flags_(flags), code_() { + DCHECK(name_->IsUniqueName()); + } CodeCacheHashTableKey(Handle<Name> name, Handle<Code> code) - : name_(name), flags_(code->flags()), code_(code) { } + : name_(name), flags_(code->flags()), code_(code) { + DCHECK(name_->IsUniqueName()); + } bool IsMatch(Object* other) override { - if (!other->IsFixedArray()) return false; + DCHECK(other->IsFixedArray()); FixedArray* pair = FixedArray::cast(other); Name* name = Name::cast(pair->get(0)); Code::Flags flags = Code::cast(pair->get(1))->flags(); - if (flags != flags_) { - return false; - } - return name_->Equals(name); + if (flags != flags_) return false; + DCHECK(name->IsUniqueName()); + return *name_ == name; } static uint32_t NameFlagsHashHelper(Name* name, Code::Flags flags) { @@ -10437,15 +9933,6 @@ class CodeCacheHashTableKey : public HashTableKey { }; -Object* CodeCacheHashTable::Lookup(Name* name, Code::Flags flags) { - DisallowHeapAllocation no_alloc; - CodeCacheHashTableKey key(handle(name), flags); - int entry = FindEntry(&key); - if (entry == kNotFound) return GetHeap()->undefined_value(); - return get(EntryToIndex(entry) + 1); -} - - Handle<CodeCacheHashTable> CodeCacheHashTable::Put( Handle<CodeCacheHashTable> cache, Handle<Name> name, Handle<Code> code) { CodeCacheHashTableKey key(name, code); @@ -10456,179 +9943,36 @@ Handle<CodeCacheHashTable> CodeCacheHashTable::Put( Handle<Object> k = key.AsHandle(cache->GetIsolate()); new_cache->set(EntryToIndex(entry), *k); - new_cache->set(EntryToIndex(entry) + 1, *code); new_cache->ElementAdded(); return new_cache; } - -int CodeCacheHashTable::GetIndex(Name* name, Code::Flags flags) { +Code* CodeCacheHashTable::Lookup(Name* name, Code::Flags flags) { DisallowHeapAllocation no_alloc; CodeCacheHashTableKey key(handle(name), flags); int entry = FindEntry(&key); - return (entry == kNotFound) ? -1 : entry; + if (entry == kNotFound) return nullptr; + return Code::cast(FixedArray::cast(get(EntryToIndex(entry)))->get(1)); } - -void CodeCacheHashTable::RemoveByIndex(int index) { - DCHECK(index >= 0); - Heap* heap = GetHeap(); - set(EntryToIndex(index), heap->the_hole_value()); - set(EntryToIndex(index) + 1, heap->the_hole_value()); - ElementRemoved(); -} - - -void PolymorphicCodeCache::Update(Handle<PolymorphicCodeCache> code_cache, - MapHandleList* maps, - Code::Flags flags, - Handle<Code> code) { - Isolate* isolate = code_cache->GetIsolate(); - if (code_cache->cache()->IsUndefined()) { - Handle<PolymorphicCodeCacheHashTable> result = - PolymorphicCodeCacheHashTable::New( - isolate, - PolymorphicCodeCacheHashTable::kInitialSize); - code_cache->set_cache(*result); - } else { - // This entry shouldn't be contained in the cache yet. - DCHECK(PolymorphicCodeCacheHashTable::cast(code_cache->cache()) - ->Lookup(maps, flags)->IsUndefined()); - } - Handle<PolymorphicCodeCacheHashTable> hash_table = - handle(PolymorphicCodeCacheHashTable::cast(code_cache->cache())); - Handle<PolymorphicCodeCacheHashTable> new_cache = - PolymorphicCodeCacheHashTable::Put(hash_table, maps, flags, code); - code_cache->set_cache(*new_cache); -} - - -Handle<Object> PolymorphicCodeCache::Lookup(MapHandleList* maps, - Code::Flags flags) { - if (!cache()->IsUndefined()) { - PolymorphicCodeCacheHashTable* hash_table = - PolymorphicCodeCacheHashTable::cast(cache()); - return Handle<Object>(hash_table->Lookup(maps, flags), GetIsolate()); - } else { - return GetIsolate()->factory()->undefined_value(); - } -} - - -// Despite their name, object of this class are not stored in the actual -// hash table; instead they're temporarily used for lookups. It is therefore -// safe to have a weak (non-owning) pointer to a MapList as a member field. -class PolymorphicCodeCacheHashTableKey : public HashTableKey { - public: - // Callers must ensure that |maps| outlives the newly constructed object. - PolymorphicCodeCacheHashTableKey(MapHandleList* maps, int code_flags) - : maps_(maps), - code_flags_(code_flags) {} - - bool IsMatch(Object* other) override { - MapHandleList other_maps(kDefaultListAllocationSize); - int other_flags; - FromObject(other, &other_flags, &other_maps); - if (code_flags_ != other_flags) return false; - if (maps_->length() != other_maps.length()) return false; - // Compare just the hashes first because it's faster. - int this_hash = MapsHashHelper(maps_, code_flags_); - int other_hash = MapsHashHelper(&other_maps, other_flags); - if (this_hash != other_hash) return false; - - // Full comparison: for each map in maps_, look for an equivalent map in - // other_maps. This implementation is slow, but probably good enough for - // now because the lists are short (<= 4 elements currently). - for (int i = 0; i < maps_->length(); ++i) { - bool match_found = false; - for (int j = 0; j < other_maps.length(); ++j) { - if (*(maps_->at(i)) == *(other_maps.at(j))) { - match_found = true; - break; - } - } - if (!match_found) return false; - } - return true; - } - - static uint32_t MapsHashHelper(MapHandleList* maps, int code_flags) { - uint32_t hash = code_flags; - for (int i = 0; i < maps->length(); ++i) { - hash ^= maps->at(i)->Hash(); - } - return hash; - } - - uint32_t Hash() override { return MapsHashHelper(maps_, code_flags_); } - - uint32_t HashForObject(Object* obj) override { - MapHandleList other_maps(kDefaultListAllocationSize); - int other_flags; - FromObject(obj, &other_flags, &other_maps); - return MapsHashHelper(&other_maps, other_flags); - } - - MUST_USE_RESULT Handle<Object> AsHandle(Isolate* isolate) override { - // The maps in |maps_| must be copied to a newly allocated FixedArray, - // both because the referenced MapList is short-lived, and because C++ - // objects can't be stored in the heap anyway. - Handle<FixedArray> list = - isolate->factory()->NewUninitializedFixedArray(maps_->length() + 1); - list->set(0, Smi::FromInt(code_flags_)); - for (int i = 0; i < maps_->length(); ++i) { - list->set(i + 1, *maps_->at(i)); - } - return list; - } - - private: - static MapHandleList* FromObject(Object* obj, - int* code_flags, - MapHandleList* maps) { - FixedArray* list = FixedArray::cast(obj); - maps->Rewind(0); - *code_flags = Smi::cast(list->get(0))->value(); - for (int i = 1; i < list->length(); ++i) { - maps->Add(Handle<Map>(Map::cast(list->get(i)))); - } - return maps; +Handle<FixedArray> FixedArray::SetAndGrow(Handle<FixedArray> array, int index, + Handle<Object> value) { + if (index < array->length()) { + array->set(index, *value); + return array; } - - MapHandleList* maps_; // weak. - int code_flags_; - static const int kDefaultListAllocationSize = kMaxKeyedPolymorphism + 1; -}; - - -Object* PolymorphicCodeCacheHashTable::Lookup(MapHandleList* maps, - int code_kind) { - DisallowHeapAllocation no_alloc; - PolymorphicCodeCacheHashTableKey key(maps, code_kind); - int entry = FindEntry(&key); - if (entry == kNotFound) return GetHeap()->undefined_value(); - return get(EntryToIndex(entry) + 1); -} - - -Handle<PolymorphicCodeCacheHashTable> PolymorphicCodeCacheHashTable::Put( - Handle<PolymorphicCodeCacheHashTable> hash_table, - MapHandleList* maps, - int code_kind, - Handle<Code> code) { - PolymorphicCodeCacheHashTableKey key(maps, code_kind); - Handle<PolymorphicCodeCacheHashTable> cache = - EnsureCapacity(hash_table, 1, &key); - int entry = cache->FindInsertionEntry(key.Hash()); - - Handle<Object> obj = key.AsHandle(hash_table->GetIsolate()); - cache->set(EntryToIndex(entry), *obj); - cache->set(EntryToIndex(entry) + 1, *code); - cache->ElementAdded(); - return cache; + int capacity = array->length(); + do { + capacity = JSObject::NewElementsCapacity(capacity); + } while (capacity <= index); + Handle<FixedArray> new_array = + array->GetIsolate()->factory()->NewUninitializedFixedArray(capacity); + array->CopyTo(0, *new_array, 0, array->length()); + new_array->FillWithHoles(array->length(), new_array->length()); + new_array->set(index, *value); + return new_array; } - void FixedArray::Shrink(int new_length) { DCHECK(0 <= new_length && new_length <= length()); if (new_length < length()) { @@ -10984,7 +10328,7 @@ Handle<Object> AccessorPair::GetComponent(Handle<AccessorPair> accessor_pair, .ToHandleChecked(); } Isolate* isolate = accessor_pair->GetIsolate(); - if (accessor->IsTheHole()) { + if (accessor->IsNull(isolate)) { return isolate->factory()->undefined_value(); } return handle(accessor, isolate); @@ -11012,12 +10356,21 @@ Handle<DeoptimizationOutputData> DeoptimizationOutputData::New( return Handle<DeoptimizationOutputData>::cast(result); } +const int LiteralsArray::kFeedbackVectorOffset = + LiteralsArray::OffsetOfElementAt(LiteralsArray::kVectorIndex); + +const int LiteralsArray::kOffsetToFirstLiteral = + LiteralsArray::OffsetOfElementAt(LiteralsArray::kFirstLiteralIndex); // static Handle<LiteralsArray> LiteralsArray::New(Isolate* isolate, Handle<TypeFeedbackVector> vector, int number_of_literals, PretenureFlag pretenure) { + if (vector->is_empty() && number_of_literals == 0) { + return Handle<LiteralsArray>::cast( + isolate->factory()->empty_literals_array()); + } Handle<FixedArray> literals = isolate->factory()->NewFixedArray( number_of_literals + kFirstLiteralIndex, pretenure); Handle<LiteralsArray> casted_literals = Handle<LiteralsArray>::cast(literals); @@ -11058,14 +10411,11 @@ int HandlerTable::LookupRange(int pc_offset, int* data_out, // TODO(turbofan): Make sure table is sorted and use binary search. -int HandlerTable::LookupReturn(int pc_offset, CatchPrediction* prediction_out) { +int HandlerTable::LookupReturn(int pc_offset) { for (int i = 0; i < length(); i += kReturnEntrySize) { int return_offset = Smi::cast(get(i + kReturnOffsetIndex))->value(); int handler_field = Smi::cast(get(i + kReturnHandlerIndex))->value(); if (pc_offset == return_offset) { - if (prediction_out) { - *prediction_out = HandlerPredictionField::decode(handler_field); - } return HandlerOffsetField::decode(handler_field); } } @@ -11085,6 +10435,34 @@ bool DescriptorArray::IsEqualTo(DescriptorArray* other) { } #endif +// static +Handle<String> String::Trim(Handle<String> string, TrimMode mode) { + Isolate* const isolate = string->GetIsolate(); + string = String::Flatten(string); + int const length = string->length(); + + // Perform left trimming if requested. + int left = 0; + UnicodeCache* unicode_cache = isolate->unicode_cache(); + if (mode == kTrim || mode == kTrimLeft) { + while (left < length && + unicode_cache->IsWhiteSpaceOrLineTerminator(string->Get(left))) { + left++; + } + } + + // Perform right trimming if requested. + int right = length; + if (mode == kTrim || mode == kTrimRight) { + while ( + right > left && + unicode_cache->IsWhiteSpaceOrLineTerminator(string->Get(right - 1))) { + right--; + } + } + + return isolate->factory()->NewSubString(string, left, right); +} bool String::LooksValid() { if (!GetIsolate()->heap()->Contains(this)) return false; @@ -11098,7 +10476,9 @@ MaybeHandle<String> Name::ToFunctionName(Handle<Name> name) { // ES6 section 9.2.11 SetFunctionName, step 4. Isolate* const isolate = name->GetIsolate(); Handle<Object> description(Handle<Symbol>::cast(name)->name(), isolate); - if (description->IsUndefined()) return isolate->factory()->empty_string(); + if (description->IsUndefined(isolate)) { + return isolate->factory()->empty_string(); + } IncrementalStringBuilder builder(isolate); builder.AppendCharacter('['); builder.AppendString(Handle<String>::cast(description)); @@ -11106,6 +10486,19 @@ MaybeHandle<String> Name::ToFunctionName(Handle<Name> name) { return builder.Finish(); } +// static +MaybeHandle<String> Name::ToFunctionName(Handle<Name> name, + Handle<String> prefix) { + Handle<String> name_string; + Isolate* const isolate = name->GetIsolate(); + ASSIGN_RETURN_ON_EXCEPTION(isolate, name_string, ToFunctionName(name), + String); + IncrementalStringBuilder builder(isolate); + builder.AppendString(prefix); + builder.AppendCharacter(' '); + builder.AppendString(name_string); + return builder.Finish(); +} namespace { @@ -11238,13 +10631,12 @@ String::FlatContent String::GetFlatContent() { } } - -base::SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls, - RobustnessFlag robust_flag, - int offset, int length, - int* length_return) { +std::unique_ptr<char[]> String::ToCString(AllowNullsFlag allow_nulls, + RobustnessFlag robust_flag, + int offset, int length, + int* length_return) { if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) { - return base::SmartArrayPointer<char>(NULL); + return std::unique_ptr<char[]>(); } // Negative length means the to the end of the string. if (length < 0) length = kMaxInt - offset; @@ -11281,13 +10673,12 @@ base::SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls, last = character; } result[utf8_byte_position] = 0; - return base::SmartArrayPointer<char>(result); + return std::unique_ptr<char[]>(result); } - -base::SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls, - RobustnessFlag robust_flag, - int* length_return) { +std::unique_ptr<char[]> String::ToCString(AllowNullsFlag allow_nulls, + RobustnessFlag robust_flag, + int* length_return) { return ToCString(allow_nulls, robust_flag, 0, -1, length_return); } @@ -11313,25 +10704,6 @@ const uc16* String::GetTwoByteData(unsigned start) { } -base::SmartArrayPointer<uc16> String::ToWideCString( - RobustnessFlag robust_flag) { - if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) { - return base::SmartArrayPointer<uc16>(); - } - StringCharacterStream stream(this); - - uc16* result = NewArray<uc16>(length() + 1); - - int i = 0; - while (stream.HasMore()) { - uint16_t character = stream.GetNext(); - result[i++] = character; - } - result[i] = 0; - return base::SmartArrayPointer<uc16>(result); -} - - const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) { return reinterpret_cast<uc16*>( reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start; @@ -12052,6 +11424,42 @@ ComparisonResult String::Compare(Handle<String> x, Handle<String> y) { return result; } +int String::IndexOf(Isolate* isolate, Handle<String> sub, Handle<String> pat, + int start_index) { + DCHECK(0 <= start_index); + DCHECK(start_index <= sub->length()); + + int pattern_length = pat->length(); + if (pattern_length == 0) return start_index; + + int subject_length = sub->length(); + if (start_index + pattern_length > subject_length) return -1; + + sub = String::Flatten(sub); + pat = String::Flatten(pat); + + DisallowHeapAllocation no_gc; // ensure vectors stay valid + // Extract flattened substrings of cons strings before getting encoding. + String::FlatContent seq_sub = sub->GetFlatContent(); + String::FlatContent seq_pat = pat->GetFlatContent(); + + // dispatch on type of strings + if (seq_pat.IsOneByte()) { + Vector<const uint8_t> pat_vector = seq_pat.ToOneByteVector(); + if (seq_sub.IsOneByte()) { + return SearchString(isolate, seq_sub.ToOneByteVector(), pat_vector, + start_index); + } + return SearchString(isolate, seq_sub.ToUC16Vector(), pat_vector, + start_index); + } + Vector<const uc16> pat_vector = seq_pat.ToUC16Vector(); + if (seq_sub.IsOneByte()) { + return SearchString(isolate, seq_sub.ToOneByteVector(), pat_vector, + start_index); + } + return SearchString(isolate, seq_sub.ToUC16Vector(), pat_vector, start_index); +} bool String::IsUtf8EqualTo(Vector<const char> str, bool allow_prefix_match) { int slen = length(); @@ -12200,8 +11608,8 @@ uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) { value |= length << String::ArrayIndexLengthBits::kShift; DCHECK((value & String::kIsNotArrayIndexMask) == 0); - DCHECK((length > String::kMaxCachedArrayIndexLength) || - (value & String::kContainsCachedArrayIndexMask) == 0); + DCHECK_EQ(length <= String::kMaxCachedArrayIndexLength, + (value & String::kContainsCachedArrayIndexMask) == 0); return value; } @@ -12377,15 +11785,18 @@ bool JSFunction::Inlines(SharedFunctionInfo* candidate) { return false; } +void JSFunction::MarkForBaseline() { + Isolate* isolate = GetIsolate(); + set_code_no_write_barrier( + isolate->builtins()->builtin(Builtins::kCompileBaseline)); + // No write barrier required, since the builtin is part of the root set. +} void JSFunction::MarkForOptimization() { Isolate* isolate = GetIsolate(); - // Do not optimize if function contains break points. - if (shared()->HasDebugInfo()) return; DCHECK(!IsOptimized()); DCHECK(shared()->allows_lazy_compilation() || !shared()->optimization_disabled()); - DCHECK(!shared()->HasDebugInfo()); set_code_no_write_barrier( isolate->builtins()->builtin(Builtins::kCompileOptimized)); // No write barrier required, since the builtin is part of the root set. @@ -12414,6 +11825,30 @@ void JSFunction::AttemptConcurrentOptimization() { // No write barrier required, since the builtin is part of the root set. } +// static +Handle<LiteralsArray> SharedFunctionInfo::FindOrCreateLiterals( + Handle<SharedFunctionInfo> shared, Handle<Context> native_context) { + Isolate* isolate = shared->GetIsolate(); + CodeAndLiterals result = + shared->SearchOptimizedCodeMap(*native_context, BailoutId::None()); + if (result.literals != nullptr) { + DCHECK(shared->feedback_metadata()->is_empty() || + !result.literals->feedback_vector()->is_empty()); + return handle(result.literals, isolate); + } + + Handle<TypeFeedbackVector> feedback_vector = + TypeFeedbackVector::New(isolate, handle(shared->feedback_metadata())); + Handle<LiteralsArray> literals = + LiteralsArray::New(isolate, feedback_vector, shared->num_literals()); + Handle<Code> code; + if (result.code != nullptr) { + code = Handle<Code>(result.code, isolate); + } + AddToOptimizedCodeMap(shared, native_context, code, literals, + BailoutId::None()); + return literals; +} void SharedFunctionInfo::AddSharedCodeToOptimizedCodeMap( Handle<SharedFunctionInfo> shared, Handle<Code> code) { @@ -12610,6 +12045,17 @@ void SharedFunctionInfo::TrimOptimizedCodeMap(int shrink_by) { } } +// static +void JSFunction::EnsureLiterals(Handle<JSFunction> function) { + Handle<SharedFunctionInfo> shared(function->shared()); + Handle<Context> native_context(function->context()->native_context()); + if (function->literals() == + function->GetIsolate()->heap()->empty_literals_array()) { + Handle<LiteralsArray> literals = + SharedFunctionInfo::FindOrCreateLiterals(shared, native_context); + function->set_literals(*literals); + } +} static void GetMinInobjectSlack(Map* map, void* data) { int slack = map->unused_property_fields(); @@ -12636,7 +12082,7 @@ static void StopSlackTracking(Map* map, void* data) { void Map::CompleteInobjectSlackTracking() { // Has to be an initial map. - DCHECK(GetBackPointer()->IsUndefined()); + DCHECK(GetBackPointer()->IsUndefined(GetIsolate())); int slack = unused_property_fields(); TransitionArray::TraverseTransitionTree(this, &GetMinInobjectSlack, &slack); @@ -12667,6 +12113,26 @@ static bool PrototypeBenefitsFromNormalization(Handle<JSObject> object) { return false; } +// static +void JSObject::MakePrototypesFast(Handle<Object> receiver, + WhereToStart where_to_start, + Isolate* isolate) { + if (!receiver->IsJSReceiver()) return; + for (PrototypeIterator iter(isolate, Handle<JSReceiver>::cast(receiver), + where_to_start); + !iter.IsAtEnd(); iter.Advance()) { + Handle<Object> current = PrototypeIterator::GetCurrent(iter); + if (!current->IsJSObject()) return; + Handle<JSObject> current_obj = Handle<JSObject>::cast(current); + Map* current_map = current_obj->map(); + if (current_map->is_prototype_map() && + !current_map->should_be_fast_prototype_map()) { + Handle<Map> map(current_map); + Map::SetShouldBeFastPrototypeMap(map, true, isolate); + JSObject::OptimizeAsPrototype(current_obj, FAST_PROTOTYPE); + } + } +} // static void JSObject::OptimizeAsPrototype(Handle<JSObject> object, @@ -12678,10 +12144,12 @@ void JSObject::OptimizeAsPrototype(Handle<JSObject> object, "NormalizeAsPrototype"); } Handle<Map> previous_map(object->map()); - if (!object->HasFastProperties()) { - JSObject::MigrateSlowToFast(object, 0, "OptimizeAsPrototype"); - } - if (!object->map()->is_prototype_map()) { + if (object->map()->is_prototype_map()) { + if (object->map()->should_be_fast_prototype_map() && + !object->HasFastProperties()) { + JSObject::MigrateSlowToFast(object, 0, "OptimizeAsPrototype"); + } + } else { if (object->map() == *previous_map) { Handle<Map> new_map = Map::Copy(handle(object->map()), "CopyAsPrototype"); JSObject::MigrateToMap(object, new_map); @@ -12709,13 +12177,13 @@ void JSObject::OptimizeAsPrototype(Handle<JSObject> object, // static void JSObject::ReoptimizeIfPrototype(Handle<JSObject> object) { if (!object->map()->is_prototype_map()) return; + if (!object->map()->should_be_fast_prototype_map()) return; OptimizeAsPrototype(object, FAST_PROTOTYPE); } // static void JSObject::LazyRegisterPrototypeUser(Handle<Map> user, Isolate* isolate) { - DCHECK(FLAG_track_prototype_users); // Contract: In line with InvalidatePrototypeChains()'s requirements, // leaf maps don't need to register as users, only prototypes do. DCHECK(user->is_prototype_map()); @@ -12821,7 +12289,6 @@ static void InvalidatePrototypeChainsInternal(Map* map) { // static void JSObject::InvalidatePrototypeChains(Map* map) { - if (!FLAG_eliminate_prototype_chain_checks) return; DisallowHeapAllocation no_gc; InvalidatePrototypeChainsInternal(map); } @@ -12852,6 +12319,15 @@ Handle<PrototypeInfo> Map::GetOrCreatePrototypeInfo(Handle<Map> prototype_map, return proto_info; } +// static +void Map::SetShouldBeFastPrototypeMap(Handle<Map> map, bool value, + Isolate* isolate) { + if (value == false && !map->prototype_info()->IsPrototypeInfo()) { + // "False" is the implicit default value, so there's nothing to do. + return; + } + GetOrCreatePrototypeInfo(map, isolate)->set_should_be_fast_map(value); +} // static Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map, @@ -12884,6 +12360,10 @@ Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map, // static void Map::SetPrototype(Handle<Map> map, Handle<Object> prototype, PrototypeOptimizationMode proto_mode) { + RuntimeCallTimerScope stats_scope(*map, &RuntimeCallStats::Map_SetPrototype); + TRACE_EVENT_RUNTIME_CALL_STATS_TRACING_SCOPED( + map->GetIsolate(), &tracing::TraceEventStatsTable::Map_SetPrototype); + bool is_hidden = false; if (prototype->IsJSObject()) { Handle<JSObject> prototype_jsobj = Handle<JSObject>::cast(prototype); @@ -12900,8 +12380,9 @@ void Map::SetPrototype(Handle<Map> map, Handle<Object> prototype, } map->set_has_hidden_prototype(is_hidden); - WriteBarrierMode wb_mode = - prototype->IsNull() ? SKIP_WRITE_BARRIER : UPDATE_WRITE_BARRIER; + WriteBarrierMode wb_mode = prototype->IsNull(map->GetIsolate()) + ? SKIP_WRITE_BARRIER + : UPDATE_WRITE_BARRIER; map->set_prototype(*prototype, wb_mode); } @@ -13009,8 +12490,13 @@ void JSFunction::SetPrototype(Handle<JSFunction> function, new_map->SetConstructor(*value); new_map->set_non_instance_prototype(true); Isolate* isolate = new_map->GetIsolate(); + construct_prototype = handle( - function->context()->native_context()->initial_object_prototype(), + IsGeneratorFunction(function->shared()->kind()) + ? function->context() + ->native_context() + ->initial_generator_prototype() + : function->context()->native_context()->initial_object_prototype(), isolate); } else { function->map()->set_non_instance_prototype(false); @@ -13065,50 +12551,54 @@ namespace { bool CanSubclassHaveInobjectProperties(InstanceType instance_type) { switch (instance_type) { - case JS_OBJECT_TYPE: - case JS_CONTEXT_EXTENSION_OBJECT_TYPE: - case JS_GENERATOR_OBJECT_TYPE: - case JS_MODULE_TYPE: - case JS_VALUE_TYPE: - case JS_DATE_TYPE: - case JS_ARRAY_TYPE: - case JS_MESSAGE_OBJECT_TYPE: + case JS_API_OBJECT_TYPE: case JS_ARRAY_BUFFER_TYPE: - case JS_TYPED_ARRAY_TYPE: + case JS_ARRAY_TYPE: + case JS_CONTEXT_EXTENSION_OBJECT_TYPE: case JS_DATA_VIEW_TYPE: - case JS_SET_TYPE: + case JS_DATE_TYPE: + case JS_FUNCTION_TYPE: + case JS_GENERATOR_OBJECT_TYPE: + case JS_MAP_ITERATOR_TYPE: case JS_MAP_TYPE: + case JS_MESSAGE_OBJECT_TYPE: + case JS_MODULE_TYPE: + case JS_OBJECT_TYPE: + case JS_ERROR_TYPE: + case JS_ARGUMENTS_TYPE: + case JS_PROMISE_TYPE: + case JS_REGEXP_TYPE: case JS_SET_ITERATOR_TYPE: - case JS_MAP_ITERATOR_TYPE: + case JS_SET_TYPE: + case JS_SPECIAL_API_OBJECT_TYPE: + case JS_TYPED_ARRAY_TYPE: + case JS_VALUE_TYPE: case JS_WEAK_MAP_TYPE: case JS_WEAK_SET_TYPE: - case JS_PROMISE_TYPE: - case JS_REGEXP_TYPE: - case JS_FUNCTION_TYPE: return true; - case JS_BOUND_FUNCTION_TYPE: - case JS_PROXY_TYPE: - case JS_GLOBAL_PROXY_TYPE: - case JS_GLOBAL_OBJECT_TYPE: + case BYTECODE_ARRAY_TYPE: + case BYTE_ARRAY_TYPE: + case CELL_TYPE: + case CODE_TYPE: + case FILLER_TYPE: case FIXED_ARRAY_TYPE: case FIXED_DOUBLE_ARRAY_TYPE: - case ODDBALL_TYPE: case FOREIGN_TYPE: - case MAP_TYPE: - case CODE_TYPE: - case CELL_TYPE: - case PROPERTY_CELL_TYPE: - case WEAK_CELL_TYPE: - case SYMBOL_TYPE: - case BYTECODE_ARRAY_TYPE: + case FREE_SPACE_TYPE: case HEAP_NUMBER_TYPE: + case JS_BOUND_FUNCTION_TYPE: + case JS_GLOBAL_OBJECT_TYPE: + case JS_GLOBAL_PROXY_TYPE: + case JS_PROXY_TYPE: + case MAP_TYPE: case MUTABLE_HEAP_NUMBER_TYPE: - case SIMD128_VALUE_TYPE: - case FILLER_TYPE: - case BYTE_ARRAY_TYPE: - case FREE_SPACE_TYPE: + case ODDBALL_TYPE: + case PROPERTY_CELL_TYPE: case SHARED_FUNCTION_INFO_TYPE: + case SIMD128_VALUE_TYPE: + case SYMBOL_TYPE: + case WEAK_CELL_TYPE: #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ case FIXED_##TYPE##_ARRAY_TYPE: @@ -13130,7 +12620,7 @@ bool CanSubclassHaveInobjectProperties(InstanceType instance_type) { void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) { - DCHECK(function->IsConstructor() || function->shared()->is_generator()); + DCHECK(function->IsConstructor() || function->shared()->is_resumable()); if (function->has_initial_map()) return; Isolate* isolate = function->GetIsolate(); @@ -13141,7 +12631,7 @@ void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) { // First create a new map with the size and number of in-object properties // suggested by the function. InstanceType instance_type; - if (function->shared()->is_generator()) { + if (function->shared()->is_resumable()) { instance_type = JS_GENERATOR_OBJECT_TYPE; } else { instance_type = JS_OBJECT_TYPE; @@ -13275,7 +12765,7 @@ MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate, void JSFunction::PrintName(FILE* out) { - base::SmartArrayPointer<char> name = shared()->DebugName()->ToCString(); + std::unique_ptr<char[]> name = shared()->DebugName()->ToCString(); PrintF(out, "%s", name.get()); } @@ -13341,18 +12831,6 @@ Handle<String> JSBoundFunction::ToString(Handle<JSBoundFunction> function) { return isolate->factory()->NewStringFromAsciiChecked(kNativeCodeSource); } -// static -MaybeHandle<String> JSBoundFunction::GetName(Isolate* isolate, - Handle<JSBoundFunction> function) { - Handle<String> prefix = isolate->factory()->bound__string(); - if (!function->bound_target_function()->IsJSFunction()) return prefix; - Handle<JSFunction> target(JSFunction::cast(function->bound_target_function()), - isolate); - Handle<Object> target_name = JSFunction::GetName(target); - if (!target_name->IsString()) return prefix; - Factory* factory = isolate->factory(); - return factory->NewConsString(prefix, Handle<String>::cast(target_name)); -} // static Handle<String> JSFunction::ToString(Handle<JSFunction> function) { @@ -13386,10 +12864,16 @@ Handle<String> JSFunction::ToString(Handle<JSFunction> function) { IncrementalStringBuilder builder(isolate); if (!shared_info->is_arrow()) { if (shared_info->is_concise_method()) { - if (shared_info->is_generator()) builder.AppendCharacter('*'); + if (shared_info->is_generator()) { + builder.AppendCharacter('*'); + } else if (shared_info->is_async()) { + builder.AppendCString("async "); + } } else { if (shared_info->is_generator()) { builder.AppendCString("function* "); + } else if (shared_info->is_async()) { + builder.AppendCString("async function "); } else { builder.AppendCString("function "); } @@ -13406,26 +12890,64 @@ Handle<String> JSFunction::ToString(Handle<JSFunction> function) { void Oddball::Initialize(Isolate* isolate, Handle<Oddball> oddball, const char* to_string, Handle<Object> to_number, - bool to_boolean, const char* type_of, byte kind) { + const char* type_of, byte kind) { Handle<String> internalized_to_string = isolate->factory()->InternalizeUtf8String(to_string); Handle<String> internalized_type_of = isolate->factory()->InternalizeUtf8String(type_of); - oddball->set_to_boolean(isolate->heap()->ToBoolean(to_boolean)); + oddball->set_to_number_raw(to_number->Number()); oddball->set_to_number(*to_number); oddball->set_to_string(*internalized_to_string); oddball->set_type_of(*internalized_type_of); oddball->set_kind(kind); } +void Script::SetEvalOrigin(Handle<Script> script, + Handle<SharedFunctionInfo> outer_info, + int eval_position) { + if (eval_position == kNoSourcePosition) { + // If the position is missing, attempt to get the code offset from the + // current activation. Do not translate the code offset into source + // position, but store it as negative value for lazy translation. + StackTraceFrameIterator it(script->GetIsolate()); + if (!it.done() && it.is_javascript()) { + FrameSummary summary = FrameSummary::GetFirst(it.javascript_frame()); + script->set_eval_from_shared(summary.function()->shared()); + script->set_eval_from_position(-summary.code_offset()); + return; + } + eval_position = 0; + } + script->set_eval_from_shared(*outer_info); + script->set_eval_from_position(eval_position); +} -void Script::InitLineEnds(Handle<Script> script) { - if (!script->line_ends()->IsUndefined()) return; +int Script::GetEvalPosition() { + DisallowHeapAllocation no_gc; + DCHECK(compilation_type() == Script::COMPILATION_TYPE_EVAL); + int position = eval_from_position(); + if (position < 0) { + // Due to laziness, the position may not have been translated from code + // offset yet, which would be encoded as negative integer. In that case, + // translate and set the position. + if (eval_from_shared()->IsUndefined(GetIsolate())) { + position = 0; + } else { + SharedFunctionInfo* shared = SharedFunctionInfo::cast(eval_from_shared()); + position = shared->abstract_code()->SourcePosition(-position); + } + DCHECK(position >= 0); + set_eval_from_position(position); + } + return position; +} +void Script::InitLineEnds(Handle<Script> script) { Isolate* isolate = script->GetIsolate(); + if (!script->line_ends()->IsUndefined(isolate)) return; if (!script->source()->IsString()) { - DCHECK(script->source()->IsUndefined()); + DCHECK(script->source()->IsUndefined(isolate)); Handle<FixedArray> empty = isolate->factory()->NewFixedArray(0); script->set_line_ends(*empty); DCHECK(script->line_ends()->IsFixedArray()); @@ -13444,42 +12966,94 @@ void Script::InitLineEnds(Handle<Script> script) { DCHECK(script->line_ends()->IsFixedArray()); } - -int Script::GetColumnNumber(Handle<Script> script, int code_pos) { - int line_number = GetLineNumber(script, code_pos); - if (line_number == -1) return -1; +#define SMI_VALUE(x) (Smi::cast(x)->value()) +bool Script::GetPositionInfo(int position, PositionInfo* info, + OffsetFlag offset_flag) { + Handle<Script> script(this); + InitLineEnds(script); DisallowHeapAllocation no_allocation; - FixedArray* line_ends_array = FixedArray::cast(script->line_ends()); - line_number = line_number - script->line_offset(); - if (line_number == 0) return code_pos + script->column_offset(); - int prev_line_end_pos = - Smi::cast(line_ends_array->get(line_number - 1))->value(); - return code_pos - (prev_line_end_pos + 1); -} + DCHECK(script->line_ends()->IsFixedArray()); + FixedArray* ends = FixedArray::cast(script->line_ends()); -int Script::GetLineNumberWithArray(int code_pos) { - DisallowHeapAllocation no_allocation; - DCHECK(line_ends()->IsFixedArray()); - FixedArray* line_ends_array = FixedArray::cast(line_ends()); - int line_ends_len = line_ends_array->length(); - if (line_ends_len == 0) return -1; + const int ends_len = ends->length(); + if (ends_len == 0) return false; - if ((Smi::cast(line_ends_array->get(0)))->value() >= code_pos) { - return line_offset(); + // Return early on invalid positions. Negative positions behave as if 0 was + // passed, and positions beyond the end of the script return as failure. + if (position < 0) { + position = 0; + } else if (position > SMI_VALUE(ends->get(ends_len - 1))) { + return false; } - int left = 0; - int right = line_ends_len; - while (int half = (right - left) / 2) { - if ((Smi::cast(line_ends_array->get(left + half)))->value() > code_pos) { - right -= half; - } else { - left += half; + // Determine line number by doing a binary search on the line ends array. + if (SMI_VALUE(ends->get(0)) >= position) { + info->line = 0; + info->line_start = 0; + info->column = position; + } else { + int left = 0; + int right = ends_len - 1; + + while (right > 0) { + DCHECK_LE(left, right); + const int mid = (left + right) / 2; + if (position > SMI_VALUE(ends->get(mid))) { + left = mid + 1; + } else if (position <= SMI_VALUE(ends->get(mid - 1))) { + right = mid - 1; + } else { + info->line = mid; + break; + } } + DCHECK(SMI_VALUE(ends->get(info->line)) >= position && + SMI_VALUE(ends->get(info->line - 1)) < position); + info->line_start = SMI_VALUE(ends->get(info->line - 1)) + 1; + info->column = position - info->line_start; } - return right + line_offset(); + + // Line end is position of the linebreak character. + info->line_end = SMI_VALUE(ends->get(info->line)); + if (info->line_end > 0) { + DCHECK(script->source()->IsString()); + Handle<String> src(String::cast(script->source())); + if (src->length() >= info->line_end && + src->Get(info->line_end - 1) == '\r') { + info->line_end--; + } + } + + // Add offsets if requested. + if (offset_flag == WITH_OFFSET) { + if (info->line == 0) { + info->column += script->column_offset(); + } + info->line += script->line_offset(); + } + + return true; +} +#undef SMI_VALUE + +int Script::GetColumnNumber(Handle<Script> script, int code_pos) { + PositionInfo info; + if (!script->GetPositionInfo(code_pos, &info, WITH_OFFSET)) { + return -1; + } + + return info.column; +} + +int Script::GetLineNumberWithArray(int code_pos) { + PositionInfo info; + if (!GetPositionInfo(code_pos, &info, WITH_OFFSET)) { + return -1; + } + + return info.line; } @@ -13491,7 +13065,9 @@ int Script::GetLineNumber(Handle<Script> script, int code_pos) { int Script::GetLineNumber(int code_pos) { DisallowHeapAllocation no_allocation; - if (!line_ends()->IsUndefined()) return GetLineNumberWithArray(code_pos); + if (!line_ends()->IsUndefined(GetIsolate())) { + return GetLineNumberWithArray(code_pos); + } // Slow mode: we do not have line_ends. We have to iterate through source. if (!source()->IsString()) return -1; @@ -13509,28 +13085,19 @@ int Script::GetLineNumber(int code_pos) { Handle<Object> Script::GetNameOrSourceURL(Handle<Script> script) { Isolate* isolate = script->GetIsolate(); - Handle<String> name_or_source_url_key = - isolate->factory()->InternalizeOneByteString( - STATIC_CHAR_VECTOR("nameOrSourceURL")); - Handle<JSObject> script_wrapper = Script::GetWrapper(script); - Handle<Object> property = - JSReceiver::GetProperty(script_wrapper, name_or_source_url_key) - .ToHandleChecked(); - DCHECK(property->IsJSFunction()); - Handle<Object> result; - // Do not check against pending exception, since this function may be called - // when an exception has already been pending. - if (!Execution::TryCall(isolate, property, script_wrapper, 0, NULL) - .ToHandle(&result)) { - return isolate->factory()->undefined_value(); + + // Keep in sync with ScriptNameOrSourceURL in messages.js. + + if (!script->source_url()->IsUndefined(isolate)) { + return handle(script->source_url(), isolate); } - return result; + return handle(script->name(), isolate); } Handle<JSObject> Script::GetWrapper(Handle<Script> script) { Isolate* isolate = script->GetIsolate(); - if (!script->wrapper()->IsUndefined()) { + if (!script->wrapper()->IsUndefined(isolate)) { DCHECK(script->wrapper()->IsWeakCell()); Handle<WeakCell> cell(WeakCell::cast(script->wrapper())); if (!cell->cleared()) { @@ -13560,7 +13127,8 @@ MaybeHandle<SharedFunctionInfo> Script::FindSharedFunctionInfo( SharedFunctionInfo* shared; while ((shared = iterator.Next<SharedFunctionInfo>())) { if (fun->function_token_position() == shared->function_token_position() && - fun->start_position() == shared->start_position()) { + fun->start_position() == shared->start_position() && + fun->end_position() == shared->end_position()) { return Handle<SharedFunctionInfo>(shared); } } @@ -13694,8 +13262,9 @@ bool SharedFunctionInfo::PassesFilter(const char* raw_filter) { } bool SharedFunctionInfo::HasSourceCode() const { - return !script()->IsUndefined() && - !reinterpret_cast<Script*>(script())->source()->IsUndefined(); + Isolate* isolate = GetIsolate(); + return !script()->IsUndefined(isolate) && + !reinterpret_cast<Script*>(script())->source()->IsUndefined(isolate); } @@ -13751,9 +13320,8 @@ void JSFunction::CalculateInstanceSizeForDerivedClass( int* instance_size, int* in_object_properties) { Isolate* isolate = GetIsolate(); int expected_nof_properties = 0; - for (PrototypeIterator iter(isolate, this, - PrototypeIterator::START_AT_RECEIVER); - !iter.IsAtEnd(); iter.Advance()) { + for (PrototypeIterator iter(isolate, this, kStartAtReceiver); !iter.IsAtEnd(); + iter.Advance()) { JSReceiver* current = iter.GetCurrent<JSReceiver>(); if (!current->IsJSFunction()) break; JSFunction* func = JSFunction::cast(current); @@ -13858,9 +13426,37 @@ void SharedFunctionInfo::DisableOptimization(BailoutReason reason) { } } +namespace { + +// Sets the expected number of properties based on estimate from parser. +void SetExpectedNofPropertiesFromEstimate(Handle<SharedFunctionInfo> shared, + FunctionLiteral* literal) { + int estimate = literal->expected_property_count(); + + // If no properties are added in the constructor, they are more likely + // to be added later. + if (estimate == 0) estimate = 2; + + // TODO(yangguo): check whether those heuristics are still up-to-date. + // We do not shrink objects that go into a snapshot (yet), so we adjust + // the estimate conservatively. + if (shared->GetIsolate()->serializer_enabled()) { + estimate += 2; + } else { + // Inobject slack tracking will reclaim redundant inobject space later, + // so we can afford to adjust the estimate generously. + estimate += 8; + } + + shared->set_expected_nof_properties(estimate); +} + +} // namespace void SharedFunctionInfo::InitFromFunctionLiteral( Handle<SharedFunctionInfo> shared_info, FunctionLiteral* lit) { + // When adding fields here, make sure Scope::AnalyzePartially is updated + // accordingly. shared_info->set_length(lit->scope()->default_function_length()); shared_info->set_internal_formal_parameter_count(lit->parameter_count()); shared_info->set_function_token_position(lit->function_token_position()); @@ -13876,20 +13472,16 @@ void SharedFunctionInfo::InitFromFunctionLiteral( shared_info->set_language_mode(lit->language_mode()); shared_info->set_uses_arguments(lit->scope()->arguments() != NULL); shared_info->set_has_duplicate_parameters(lit->has_duplicate_parameters()); - shared_info->set_ast_node_count(lit->ast_node_count()); shared_info->set_is_function(lit->is_function()); - if (lit->dont_optimize_reason() != kNoReason) { - shared_info->DisableOptimization(lit->dont_optimize_reason()); - } - shared_info->set_dont_crankshaft(lit->flags() & - AstProperties::kDontCrankshaft); + shared_info->set_never_compiled(true); shared_info->set_kind(lit->kind()); if (!IsConstructable(lit->kind(), lit->language_mode())) { - shared_info->set_construct_stub( + shared_info->SetConstructStub( *shared_info->GetIsolate()->builtins()->ConstructedNonConstructable()); } shared_info->set_needs_home_object(lit->scope()->NeedsHomeObject()); shared_info->set_asm_function(lit->scope()->asm_function()); + SetExpectedNofPropertiesFromEstimate(shared_info, lit); } @@ -13903,6 +13495,10 @@ bool SharedFunctionInfo::VerifyBailoutId(BailoutId id) { return true; // Return true if there was no DCHECK. } +void SharedFunctionInfo::SetConstructStub(Code* code) { + if (code->kind() == Code::BUILTIN) code->set_is_construct_stub(true); + set_construct_stub(code); +} void Map::StartInobjectSlackTracking() { DCHECK(!IsInobjectSlackTrackingInProgress()); @@ -13919,14 +13515,18 @@ void Map::StartInobjectSlackTracking() { void SharedFunctionInfo::ResetForNewContext(int new_ic_age) { code()->ClearInlineCaches(); - // If we clear ICs, we need to clear the type feedback vector too, since - // CallICs are synced with a feedback vector slot. - ClearTypeFeedbackInfo(); set_ic_age(new_ic_age); if (code()->kind() == Code::FUNCTION) { code()->set_profiler_ticks(0); - if (optimization_disabled() && - opt_count() >= FLAG_max_opt_count) { + if (optimization_disabled() && opt_count() >= FLAG_max_opt_count) { + // Re-enable optimizations if they were disabled due to opt_count limit. + set_optimization_disabled(false); + } + set_opt_count(0); + set_deopt_count(0); + } else if (code()->is_interpreter_trampoline_builtin()) { + set_profiler_ticks(0); + if (optimization_disabled() && opt_count() >= FLAG_max_opt_count) { // Re-enable optimizations if they were disabled due to opt_count limit. set_optimization_disabled(false); } @@ -13960,6 +13560,19 @@ int SharedFunctionInfo::SearchOptimizedCodeMapEntry(Context* native_context, return -1; } +void SharedFunctionInfo::ClearCodeFromOptimizedCodeMap() { + if (!OptimizedCodeMapIsCleared()) { + FixedArray* optimized_code_map = this->optimized_code_map(); + int length = optimized_code_map->length(); + WeakCell* empty_weak_cell = GetHeap()->empty_weak_cell(); + for (int i = kEntriesStart; i < length; i += kEntryLength) { + optimized_code_map->set(i + kCachedCodeOffset, empty_weak_cell, + SKIP_WRITE_BARRIER); + } + optimized_code_map->set(kSharedCodeIndex, empty_weak_cell, + SKIP_WRITE_BARRIER); + } +} CodeAndLiterals SharedFunctionInfo::SearchOptimizedCodeMap( Context* native_context, BailoutId osr_ast_id) { @@ -13986,12 +13599,6 @@ CodeAndLiterals SharedFunctionInfo::SearchOptimizedCodeMap( : LiteralsArray::cast(literals_cell->value())}; } } - if (FLAG_trace_opt && !OptimizedCodeMapIsCleared() && - result.code == nullptr) { - PrintF("[didn't find optimized code in optimized code map for "); - ShortPrint(); - PrintF("]\n"); - } return result; } @@ -14014,63 +13621,66 @@ const char* const VisitorSynchronization::kTagNames[ void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) { DCHECK(RelocInfo::IsCodeTarget(rinfo->rmode())); - Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); - Object* old_target = target; - VisitPointer(&target); - CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target. + Object* old_pointer = Code::GetCodeFromTargetAddress(rinfo->target_address()); + Object* new_pointer = old_pointer; + VisitPointer(&new_pointer); + DCHECK_EQ(old_pointer, new_pointer); } void ObjectVisitor::VisitCodeAgeSequence(RelocInfo* rinfo) { DCHECK(RelocInfo::IsCodeAgeSequence(rinfo->rmode())); - Object* stub = rinfo->code_age_stub(); - if (stub) { - VisitPointer(&stub); + Object* old_pointer = rinfo->code_age_stub(); + Object* new_pointer = old_pointer; + if (old_pointer != nullptr) { + VisitPointer(&new_pointer); + DCHECK_EQ(old_pointer, new_pointer); } } void ObjectVisitor::VisitCodeEntry(Address entry_address) { - Object* code = Code::GetObjectFromEntryAddress(entry_address); - Object* old_code = code; - VisitPointer(&code); - if (code != old_code) { - Memory::Address_at(entry_address) = reinterpret_cast<Code*>(code)->entry(); - } + Object* old_pointer = Code::GetObjectFromEntryAddress(entry_address); + Object* new_pointer = old_pointer; + VisitPointer(&new_pointer); + DCHECK_EQ(old_pointer, new_pointer); } void ObjectVisitor::VisitCell(RelocInfo* rinfo) { DCHECK(rinfo->rmode() == RelocInfo::CELL); - Object* cell = rinfo->target_cell(); - Object* old_cell = cell; - VisitPointer(&cell); - if (cell != old_cell) { - rinfo->set_target_cell(reinterpret_cast<Cell*>(cell)); - } + Object* old_pointer = rinfo->target_cell(); + Object* new_pointer = old_pointer; + VisitPointer(&new_pointer); + DCHECK_EQ(old_pointer, new_pointer); } void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) { DCHECK(RelocInfo::IsDebugBreakSlot(rinfo->rmode()) && rinfo->IsPatchedDebugBreakSlotSequence()); - Object* target = Code::GetCodeFromTargetAddress(rinfo->debug_call_address()); - Object* old_target = target; - VisitPointer(&target); - CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target. + Object* old_pointer = + Code::GetCodeFromTargetAddress(rinfo->debug_call_address()); + Object* new_pointer = old_pointer; + VisitPointer(&new_pointer); + DCHECK_EQ(old_pointer, new_pointer); } void ObjectVisitor::VisitEmbeddedPointer(RelocInfo* rinfo) { DCHECK(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT); - Object* p = rinfo->target_object(); - VisitPointer(&p); + Object* old_pointer = rinfo->target_object(); + Object* new_pointer = old_pointer; + VisitPointer(&new_pointer); + DCHECK_EQ(old_pointer, new_pointer); } void ObjectVisitor::VisitExternalReference(RelocInfo* rinfo) { - Address p = rinfo->target_external_reference(); - VisitExternalReference(&p); + Address old_reference = rinfo->target_external_reference(); + Address new_reference = old_reference; + VisitExternalReference(&new_reference); + DCHECK_EQ(old_reference, new_reference); } @@ -14109,6 +13719,14 @@ void Code::CopyFrom(const CodeDesc& desc) { CopyBytes(instruction_start(), desc.buffer, static_cast<size_t>(desc.instr_size)); + // copy unwinding info, if any + if (desc.unwinding_info) { + DCHECK_GT(desc.unwinding_info_size, 0); + set_unwinding_info_size(desc.unwinding_info_size); + CopyBytes(unwinding_info_start(), desc.unwinding_info, + static_cast<size_t>(desc.unwinding_info_size)); + } + // copy reloc info CopyBytes(relocation_start(), desc.buffer + desc.buffer_size - desc.reloc_size, @@ -14156,64 +13774,6 @@ void Code::CopyFrom(const CodeDesc& desc) { Assembler::FlushICache(GetIsolate(), instruction_start(), instruction_size()); } -// Locate the source position which is closest to the code offset. This is -// using the source position information embedded in the relocation info. -// The position returned is relative to the beginning of the script where the -// source for this function is found. -int Code::SourcePosition(int code_offset) { - Address pc = instruction_start() + code_offset; - int distance = kMaxInt; - int position = RelocInfo::kNoPosition; // Initially no position found. - // Run through all the relocation info to find the best matching source - // position. All the code needs to be considered as the sequence of the - // instructions in the code does not necessarily follow the same order as the - // source. - RelocIterator it(this, RelocInfo::kPositionMask); - while (!it.done()) { - // Only look at positions after the current pc. - if (it.rinfo()->pc() < pc) { - // Get position and distance. - - int dist = static_cast<int>(pc - it.rinfo()->pc()); - int pos = static_cast<int>(it.rinfo()->data()); - // If this position is closer than the current candidate or if it has the - // same distance as the current candidate and the position is higher then - // this position is the new candidate. - if ((dist < distance) || - (dist == distance && pos > position)) { - position = pos; - distance = dist; - } - } - it.next(); - } - DCHECK(kind() == FUNCTION || (is_optimized_code() && is_turbofanned()) || - is_wasm_code() || position == RelocInfo::kNoPosition); - return position; -} - - -// Same as Code::SourcePosition above except it only looks for statement -// positions. -int Code::SourceStatementPosition(int code_offset) { - // First find the position as close as possible using all position - // information. - int position = SourcePosition(code_offset); - // Now find the closest statement position before the position. - int statement_position = 0; - RelocIterator it(this, RelocInfo::kPositionMask); - while (!it.done()) { - if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) { - int p = static_cast<int>(it.rinfo()->data()); - if (statement_position < p && p <= position) { - statement_position = p; - } - } - it.next(); - } - return statement_position; -} - SafepointEntry Code::GetSafepointEntry(Address pc) { SafepointTable table(this); @@ -14275,150 +13835,55 @@ void Code::FindAndReplace(const FindAndReplacePattern& pattern) { } -void Code::FindAllMaps(MapHandleList* maps) { - DCHECK(is_inline_cache_stub()); - DisallowHeapAllocation no_allocation; - int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); - for (RelocIterator it(this, mask); !it.done(); it.next()) { - RelocInfo* info = it.rinfo(); - Object* object = info->target_object(); - if (object->IsWeakCell()) object = WeakCell::cast(object)->value(); - if (object->IsMap()) maps->Add(handle(Map::cast(object))); - } -} - - -Code* Code::FindFirstHandler() { - DCHECK(is_inline_cache_stub()); - DisallowHeapAllocation no_allocation; - int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | - RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); - bool skip_next_handler = false; - for (RelocIterator it(this, mask); !it.done(); it.next()) { - RelocInfo* info = it.rinfo(); - if (info->rmode() == RelocInfo::EMBEDDED_OBJECT) { - Object* obj = info->target_object(); - skip_next_handler |= obj->IsWeakCell() && WeakCell::cast(obj)->cleared(); - } else { - Code* code = Code::GetCodeFromTargetAddress(info->target_address()); - if (code->kind() == Code::HANDLER) { - if (!skip_next_handler) return code; - skip_next_handler = false; - } - } - } - return NULL; -} - - -bool Code::FindHandlers(CodeHandleList* code_list, int length) { - DCHECK(is_inline_cache_stub()); - DisallowHeapAllocation no_allocation; - int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | - RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); - bool skip_next_handler = false; - int i = 0; - for (RelocIterator it(this, mask); !it.done(); it.next()) { - if (i == length) return true; - RelocInfo* info = it.rinfo(); - if (info->rmode() == RelocInfo::EMBEDDED_OBJECT) { - Object* obj = info->target_object(); - skip_next_handler |= obj->IsWeakCell() && WeakCell::cast(obj)->cleared(); - } else { - Code* code = Code::GetCodeFromTargetAddress(info->target_address()); - // IC stubs with handlers never contain non-handler code objects before - // handler targets. - if (code->kind() != Code::HANDLER) break; - if (!skip_next_handler) { - code_list->Add(Handle<Code>(code)); - i++; - } - skip_next_handler = false; - } - } - return i == length; -} - - -MaybeHandle<Code> Code::FindHandlerForMap(Map* map) { - DCHECK(is_inline_cache_stub()); - int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | - RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); - bool return_next = false; - for (RelocIterator it(this, mask); !it.done(); it.next()) { - RelocInfo* info = it.rinfo(); - if (info->rmode() == RelocInfo::EMBEDDED_OBJECT) { - Object* object = info->target_object(); - if (object->IsWeakCell()) object = WeakCell::cast(object)->value(); - if (object == map) return_next = true; - } else if (return_next) { - Code* code = Code::GetCodeFromTargetAddress(info->target_address()); - DCHECK(code->kind() == Code::HANDLER); - return handle(code); - } - } - return MaybeHandle<Code>(); -} - - -Name* Code::FindFirstName() { - DCHECK(is_inline_cache_stub()); - DisallowHeapAllocation no_allocation; - int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); - for (RelocIterator it(this, mask); !it.done(); it.next()) { - RelocInfo* info = it.rinfo(); - Object* object = info->target_object(); - if (object->IsName()) return Name::cast(object); - } - return NULL; -} - - void Code::ClearInlineCaches() { - ClearInlineCaches(NULL); -} - - -void Code::ClearInlineCaches(Code::Kind kind) { - ClearInlineCaches(&kind); -} - - -void Code::ClearInlineCaches(Code::Kind* kind) { int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID); for (RelocIterator it(this, mask); !it.done(); it.next()) { RelocInfo* info = it.rinfo(); Code* target(Code::GetCodeFromTargetAddress(info->target_address())); if (target->is_inline_cache_stub()) { - if (kind == NULL || *kind == target->kind()) { - IC::Clear(this->GetIsolate(), info->pc(), - info->host()->constant_pool()); - } + IC::Clear(this->GetIsolate(), info->pc(), info->host()->constant_pool()); } } } int AbstractCode::SourcePosition(int offset) { - return IsBytecodeArray() ? GetBytecodeArray()->SourcePosition(offset) - : GetCode()->SourcePosition(offset); + int position = 0; + // Subtract one because the current PC is one instruction after the call site. + if (IsCode()) offset--; + for (SourcePositionTableIterator iterator(source_position_table()); + !iterator.done() && iterator.code_offset() <= offset; + iterator.Advance()) { + position = iterator.source_position(); + } + return position; } int AbstractCode::SourceStatementPosition(int offset) { - return IsBytecodeArray() ? GetBytecodeArray()->SourceStatementPosition(offset) - : GetCode()->SourceStatementPosition(offset); + // First find the closest position. + int position = SourcePosition(offset); + // Now find the closest statement position before the position. + int statement_position = 0; + for (SourcePositionTableIterator it(source_position_table()); !it.done(); + it.Advance()) { + if (it.is_statement()) { + int p = it.source_position(); + if (statement_position < p && p <= position) { + statement_position = p; + } + } + } + return statement_position; } -void SharedFunctionInfo::ClearTypeFeedbackInfo() { - feedback_vector()->ClearSlots(this); +void JSFunction::ClearTypeFeedbackInfo() { + feedback_vector()->ClearSlots(shared()); } - -void SharedFunctionInfo::ClearTypeFeedbackInfoAtGCTime() { - feedback_vector()->ClearSlotsAtGCTime(this); +void JSFunction::ClearTypeFeedbackInfoAtGCTime() { + feedback_vector()->ClearSlotsAtGCTime(shared()); } - BailoutId Code::TranslatePcOffsetToAstId(uint32_t pc_offset) { DisallowHeapAllocation no_gc; DCHECK(kind() == FUNCTION); @@ -14441,6 +13906,12 @@ uint32_t Code::TranslateAstIdToPcOffset(BailoutId ast_id) { return 0; } +int Code::LookupRangeInHandlerTable(int code_offset, int* data, + HandlerTable::CatchPrediction* prediction) { + DCHECK(!is_optimized_code()); + HandlerTable* table = HandlerTable::cast(handler_table()); + return table->LookupRange(code_offset, data, prediction); +} void Code::MakeCodeAgeSequenceYoung(byte* sequence, Isolate* isolate) { PatchPlatformCodeAge(isolate, sequence, kNoAgeCodeAge, NO_MARKING_PARITY); @@ -14614,14 +14085,14 @@ Code* Code::GetCodeAgeStub(Isolate* isolate, Age age, MarkingParity parity) { void Code::PrintDeoptLocation(FILE* out, Address pc) { Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(this, pc); class SourcePosition pos = info.position; - if (info.deopt_reason != Deoptimizer::kNoReason || !pos.IsUnknown()) { + if (info.deopt_reason != DeoptimizeReason::kNoReason || !pos.IsUnknown()) { if (FLAG_hydrogen_track_positions) { PrintF(out, " ;;; deoptimize at %d_%d: %s\n", pos.inlining_id(), pos.position(), - Deoptimizer::GetDeoptReason(info.deopt_reason)); + DeoptimizeReasonToString(info.deopt_reason)); } else { PrintF(out, " ;;; deoptimize at %d: %s\n", pos.raw(), - Deoptimizer::GetDeoptReason(info.deopt_reason)); + DeoptimizeReasonToString(info.deopt_reason)); } } } @@ -14654,6 +14125,14 @@ const char* Code::Kind2String(Kind kind) { return NULL; } +// Identify kind of code. +const char* AbstractCode::Kind2String(Kind kind) { + if (kind < AbstractCode::INTERPRETED_FUNCTION) + return Code::Kind2String((Code::Kind)kind); + if (kind == AbstractCode::INTERPRETED_FUNCTION) return "INTERPRETED_FUNCTION"; + UNREACHABLE(); + return NULL; +} Handle<WeakCell> Code::WeakCellFor(Handle<Code> code) { DCHECK(code->kind() == OPTIMIZED_FUNCTION); @@ -14677,7 +14156,6 @@ WeakCell* Code::CachedWeakCell() { return NULL; } - #ifdef ENABLE_DISASSEMBLER void DeoptimizationInputData::DeoptimizationInputDataPrint( @@ -14811,9 +14289,20 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint( break; } + case Translation::FLOAT_REGISTER: { + int reg_code = iterator.Next(); + os << "{input=" + << RegisterConfiguration::Crankshaft()->GetFloatRegisterName( + reg_code) + << "}"; + break; + } + case Translation::DOUBLE_REGISTER: { int reg_code = iterator.Next(); - os << "{input=" << DoubleRegister::from_code(reg_code).ToString() + os << "{input=" + << RegisterConfiguration::Crankshaft()->GetDoubleRegisterName( + reg_code) << "}"; break; } @@ -14842,6 +14331,7 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint( break; } + case Translation::FLOAT_STACK_SLOT: case Translation::DOUBLE_STACK_SLOT: { int input_slot_index = iterator.Next(); os << "{input=" << input_slot_index << "}"; @@ -14886,8 +14376,9 @@ void DeoptimizationOutputData::DeoptimizationOutputDataPrint( int pc_and_state = this->PcAndState(i)->value(); os << std::setw(6) << this->AstId(i).ToInt() << " " << std::setw(8) << FullCodeGenerator::PcField::decode(pc_and_state) << " " - << FullCodeGenerator::State2String( - FullCodeGenerator::StateField::decode(pc_and_state)) << "\n"; + << Deoptimizer::BailoutStateToString( + FullCodeGenerator::BailoutStateField::decode(pc_and_state)) + << "\n"; } } @@ -14926,28 +14417,17 @@ const char* Code::ICState2String(InlineCacheState state) { case UNINITIALIZED: return "UNINITIALIZED"; case PREMONOMORPHIC: return "PREMONOMORPHIC"; case MONOMORPHIC: return "MONOMORPHIC"; - case PROTOTYPE_FAILURE: - return "PROTOTYPE_FAILURE"; + case RECOMPUTE_HANDLER: + return "RECOMPUTE_HANDLER"; case POLYMORPHIC: return "POLYMORPHIC"; case MEGAMORPHIC: return "MEGAMORPHIC"; case GENERIC: return "GENERIC"; - case DEBUG_STUB: return "DEBUG_STUB"; } UNREACHABLE(); return NULL; } -const char* Code::StubType2String(StubType type) { - switch (type) { - case NORMAL: return "NORMAL"; - case FAST: return "FAST"; - } - UNREACHABLE(); // keep the compiler happy - return NULL; -} - - void Code::PrintExtraICState(std::ostream& os, // NOLINT Kind kind, ExtraICState extra) { os << "extra_ic_state = "; @@ -14967,11 +14447,11 @@ void Code::Disassemble(const char* name, std::ostream& os) { // NOLINT os << "major_key = " << (n == NULL ? "null" : n) << "\n"; } if (is_inline_cache_stub()) { - os << "ic_state = " << ICState2String(ic_state()) << "\n"; - PrintExtraICState(os, kind(), extra_ic_state()); - if (ic_state() == MONOMORPHIC) { - os << "type = " << StubType2String(type()) << "\n"; + if (!IC::ICUseVector(kind())) { + InlineCacheState ic_state = IC::StateFromCode(this); + os << "ic_state = " << ICState2String(ic_state) << "\n"; } + PrintExtraICState(os, kind(), extra_ic_state()); if (is_compare_ic_stub()) { DCHECK(CodeStub::GetMajorKey(this) == CodeStub::CompareIC); CompareICStub stub(stub_key(), GetIsolate()); @@ -15038,6 +14518,17 @@ void Code::Disassemble(const char* name, std::ostream& os) { // NOLINT } os << "\n"; + SourcePositionTableIterator it(source_position_table()); + if (!it.done()) { + os << "Source positions:\n pc offset position\n"; + for (; !it.done(); it.Advance()) { + os << std::setw(10) << it.code_offset() << std::setw(10) + << it.source_position() << (it.is_statement() ? " statement" : "") + << "\n"; + } + os << "\n"; + } + if (kind() == FUNCTION) { DeoptimizationOutputData* data = DeoptimizationOutputData::cast(this->deoptimization_data()); @@ -15090,7 +14581,7 @@ void Code::Disassemble(const char* name, std::ostream& os) { // NOLINT os << "\n"; } #ifdef OBJECT_PRINT - if (!type_feedback_info()->IsUndefined()) { + if (!type_feedback_info()->IsUndefined(GetIsolate())) { TypeFeedbackInfo::cast(type_feedback_info())->TypeFeedbackInfoPrint(os); os << "\n"; } @@ -15112,52 +14603,29 @@ void Code::Disassemble(const char* name, std::ostream& os) { // NOLINT it.rinfo()->Print(GetIsolate(), os); } os << "\n"; -} -#endif // ENABLE_DISASSEMBLER -int BytecodeArray::SourcePosition(int offset) { - int last_position = 0; - for (interpreter::SourcePositionTableIterator iterator( - source_position_table()); - !iterator.done() && iterator.bytecode_offset() <= offset; - iterator.Advance()) { - last_position = iterator.source_position(); + if (has_unwinding_info()) { + os << "UnwindingInfo (size = " << unwinding_info_size() << ")\n"; + EhFrameDisassembler eh_frame_disassembler(unwinding_info_start(), + unwinding_info_end()); + eh_frame_disassembler.DisassembleToStream(os); + os << "\n"; } - return last_position; } +#endif // ENABLE_DISASSEMBLER -int BytecodeArray::SourceStatementPosition(int offset) { - // First find the position as close as possible using all position - // information. - int position = SourcePosition(offset); - // Now find the closest statement position before the position. - int statement_position = 0; - interpreter::SourcePositionTableIterator iterator(source_position_table()); - while (!iterator.done()) { - if (iterator.is_statement()) { - int p = iterator.source_position(); - if (statement_position < p && p <= position) { - statement_position = p; - } - } - iterator.Advance(); - } - return statement_position; -} void BytecodeArray::Disassemble(std::ostream& os) { os << "Parameter count " << parameter_count() << "\n"; os << "Frame size " << frame_size() << "\n"; - Vector<char> buf = Vector<char>::New(50); const uint8_t* base_address = GetFirstBytecodeAddress(); - interpreter::SourcePositionTableIterator source_positions( - source_position_table()); + SourcePositionTableIterator source_positions(source_position_table()); interpreter::BytecodeArrayIterator iterator(handle(this)); while (!iterator.done()) { if (!source_positions.done() && - iterator.current_offset() == source_positions.bytecode_offset()) { + iterator.current_offset() == source_positions.code_offset()) { os << std::setw(5) << source_positions.source_position(); os << (source_positions.is_statement() ? " S> " : " E> "); source_positions.Advance(); @@ -15165,12 +14633,14 @@ void BytecodeArray::Disassemble(std::ostream& os) { os << " "; } const uint8_t* current_address = base_address + iterator.current_offset(); - SNPrintF(buf, "%p", current_address); - os << buf.start() << " : "; - interpreter::Bytecodes::Decode(os, current_address, parameter_count()); + os << reinterpret_cast<const void*>(current_address) << " @ " + << std::setw(4) << iterator.current_offset() << " : "; + interpreter::BytecodeDecoder::Decode(os, current_address, + parameter_count()); if (interpreter::Bytecodes::IsJump(iterator.current_bytecode())) { - SNPrintF(buf, " (%p)", base_address + iterator.GetJumpTargetOffset()); - os << buf.start(); + const void* jump_target = base_address + iterator.GetJumpTargetOffset(); + os << " (" << jump_target << " @ " << iterator.GetJumpTargetOffset() + << ")"; } os << std::endl; iterator.Advance(); @@ -15196,6 +14666,13 @@ void BytecodeArray::CopyBytecodesTo(BytecodeArray* to) { from->length()); } +int BytecodeArray::LookupRangeInHandlerTable( + int code_offset, int* data, HandlerTable::CatchPrediction* prediction) { + HandlerTable* table = HandlerTable::cast(handler_table()); + code_offset++; // Point after current bytecode. + return table->LookupRange(code_offset, data, prediction); +} + // static void JSArray::Initialize(Handle<JSArray> array, int capacity, int length) { DCHECK(capacity >= 0); @@ -15203,29 +14680,6 @@ void JSArray::Initialize(Handle<JSArray> array, int capacity, int length) { array, length, capacity, INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE); } - -// Returns false if the passed-in index is marked non-configurable, which will -// cause the truncation operation to halt, and thus no further old values need -// be collected. -static bool GetOldValue(Isolate* isolate, - Handle<JSObject> object, - uint32_t index, - List<Handle<Object> >* old_values, - List<uint32_t>* indices) { - LookupIterator it(isolate, object, index, object, LookupIterator::HIDDEN); - CHECK(JSReceiver::GetPropertyAttributes(&it).IsJust()); - DCHECK(it.IsFound()); - if (!it.IsConfigurable()) return false; - Handle<Object> value = - it.state() == LookupIterator::ACCESSOR - ? Handle<Object>::cast(isolate->factory()->the_hole_value()) - : JSReceiver::GetDataProperty(&it); - old_values->Add(value); - indices->Add(index); - return true; -} - - void JSArray::SetLength(Handle<JSArray> array, uint32_t new_length) { // We should never end in here with a pixel or external array. DCHECK(array->AllowsSetLength()); @@ -15236,91 +14690,6 @@ void JSArray::SetLength(Handle<JSArray> array, uint32_t new_length) { } -MaybeHandle<Object> JSArray::ObservableSetLength(Handle<JSArray> array, - uint32_t new_length) { - if (!array->map()->is_observed()) { - SetLength(array, new_length); - return array; - } - - Isolate* isolate = array->GetIsolate(); - List<uint32_t> indices; - List<Handle<Object> > old_values; - Handle<Object> old_length_handle(array->length(), isolate); - uint32_t old_length = 0; - CHECK(old_length_handle->ToArrayLength(&old_length)); - - int num_elements = array->NumberOfOwnElements(ALL_PROPERTIES); - if (num_elements > 0) { - if (old_length == static_cast<uint32_t>(num_elements)) { - // Simple case for arrays without holes. - for (uint32_t i = old_length - 1; i + 1 > new_length; --i) { - if (!GetOldValue(isolate, array, i, &old_values, &indices)) break; - } - } else { - // For sparse arrays, only iterate over existing elements. - // TODO(rafaelw): For fast, sparse arrays, we can avoid iterating over - // the to-be-removed indices twice. - Handle<FixedArray> keys = isolate->factory()->NewFixedArray(num_elements); - array->GetOwnElementKeys(*keys, ALL_PROPERTIES); - while (num_elements-- > 0) { - uint32_t index = NumberToUint32(keys->get(num_elements)); - if (index < new_length) break; - if (!GetOldValue(isolate, array, index, &old_values, &indices)) break; - } - } - } - - SetLength(array, new_length); - - CHECK(array->length()->ToArrayLength(&new_length)); - if (old_length == new_length) return array; - - RETURN_ON_EXCEPTION(isolate, BeginPerformSplice(array), Object); - - for (int i = 0; i < indices.length(); ++i) { - // For deletions where the property was an accessor, old_values[i] - // will be the hole, which instructs EnqueueChangeRecord to elide - // the "oldValue" property. - RETURN_ON_EXCEPTION( - isolate, - JSObject::EnqueueChangeRecord( - array, "delete", isolate->factory()->Uint32ToString(indices[i]), - old_values[i]), - Object); - } - - RETURN_ON_EXCEPTION(isolate, - JSObject::EnqueueChangeRecord( - array, "update", isolate->factory()->length_string(), - old_length_handle), - Object); - - RETURN_ON_EXCEPTION(isolate, EndPerformSplice(array), Object); - - uint32_t index = Min(old_length, new_length); - uint32_t add_count = new_length > old_length ? new_length - old_length : 0; - uint32_t delete_count = new_length < old_length ? old_length - new_length : 0; - Handle<JSArray> deleted = isolate->factory()->NewJSArray(0); - if (delete_count > 0) { - for (int i = indices.length() - 1; i >= 0; i--) { - // Skip deletions where the property was an accessor, leaving holes - // in the array of old values. - if (old_values[i]->IsTheHole()) continue; - JSObject::AddDataElement(deleted, indices[i] - index, old_values[i], NONE) - .Assert(); - } - - JSArray::SetLength(deleted, delete_count); - } - - RETURN_ON_EXCEPTION( - isolate, EnqueueSpliceRecord(array, index, deleted, add_count), Object); - - return array; -} - - // static void Map::AddDependentCode(Handle<Map> map, DependentCode::DependencyGroup group, @@ -15648,10 +15017,10 @@ Maybe<bool> JSProxy::SetPrototype(Handle<JSProxy> proxy, Handle<Object> value, bool from_javascript, ShouldThrow should_throw) { Isolate* isolate = proxy->GetIsolate(); - STACK_CHECK(Nothing<bool>()); + STACK_CHECK(isolate, Nothing<bool>()); Handle<Name> trap_name = isolate->factory()->setPrototypeOf_string(); // 1. Assert: Either Type(V) is Object or Type(V) is Null. - DCHECK(value->IsJSReceiver() || value->IsNull()); + DCHECK(value->IsJSReceiver() || value->IsNull(isolate)); // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O. Handle<Object> handler(proxy->handler(), isolate); // 3. If handler is null, throw a TypeError exception. @@ -15670,7 +15039,7 @@ Maybe<bool> JSProxy::SetPrototype(Handle<JSProxy> proxy, Handle<Object> value, Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name), Nothing<bool>()); // 7. If trap is undefined, then return target.[[SetPrototypeOf]](). - if (trap->IsUndefined()) { + if (trap->IsUndefined(isolate)) { return JSReceiver::SetPrototype(target, value, from_javascript, should_throw); } @@ -15719,46 +15088,10 @@ Maybe<bool> JSObject::SetPrototype(Handle<JSObject> object, ShouldThrow should_throw) { Isolate* isolate = object->GetIsolate(); - const bool observed = from_javascript && object->map()->is_observed(); - Handle<Object> old_value; - if (observed) { - ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, old_value, - JSReceiver::GetPrototype(isolate, object), - Nothing<bool>()); - } - - Maybe<bool> result = - SetPrototypeUnobserved(object, value, from_javascript, should_throw); - MAYBE_RETURN(result, Nothing<bool>()); - - if (result.FromJust() && observed) { - Handle<Object> new_value; - ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, new_value, - JSReceiver::GetPrototype(isolate, object), - Nothing<bool>()); - if (!new_value->SameValue(*old_value)) { - RETURN_ON_EXCEPTION_VALUE( - isolate, JSObject::EnqueueChangeRecord( - object, "setPrototype", - isolate->factory()->proto_string(), old_value), - Nothing<bool>()); - } - } - - return result; -} - - -Maybe<bool> JSObject::SetPrototypeUnobserved(Handle<JSObject> object, - Handle<Object> value, - bool from_javascript, - ShouldThrow should_throw) { #ifdef DEBUG int size = object->Size(); #endif - Isolate* isolate = object->GetIsolate(); - if (from_javascript) { if (object->IsAccessCheckNeeded() && !isolate->MayAccess(handle(isolate->context()), object)) { @@ -15774,7 +15107,7 @@ Maybe<bool> JSObject::SetPrototypeUnobserved(Handle<JSObject> object, Heap* heap = isolate->heap(); // Silently ignore the change if value is not a JSObject or null. // SpiderMonkey behaves this way. - if (!value->IsJSReceiver() && !value->IsNull()) return Just(true); + if (!value->IsJSReceiver() && !value->IsNull(isolate)) return Just(true); bool dictionary_elements_in_chain = object->map()->DictionaryElementsInPrototypeChainOnly(); @@ -15784,8 +15117,7 @@ Maybe<bool> JSObject::SetPrototypeUnobserved(Handle<JSObject> object, if (from_javascript) { // Find the first object in the chain whose prototype object is not // hidden. - PrototypeIterator iter(isolate, real_receiver, - PrototypeIterator::START_AT_PROTOTYPE, + PrototypeIterator iter(isolate, real_receiver, kStartAtPrototype, PrototypeIterator::END_AT_NON_HIDDEN); while (!iter.IsAtEnd()) { // Casting to JSObject is fine because hidden prototypes are never @@ -15800,6 +15132,13 @@ Maybe<bool> JSObject::SetPrototypeUnobserved(Handle<JSObject> object, // Nothing to do if prototype is already set. if (map->prototype() == *value) return Just(true); + bool immutable_proto = object->map()->is_immutable_proto(); + if (immutable_proto) { + RETURN_FAILURE( + isolate, should_throw, + NewTypeError(MessageTemplate::kImmutablePrototypeSet, object)); + } + // From 8.6.2 Object Internal Methods // ... // In addition, if [[Extensible]] is false the value of the [[Class]] and @@ -15818,7 +15157,7 @@ Maybe<bool> JSObject::SetPrototypeUnobserved(Handle<JSObject> object, // new prototype chain. if (value->IsJSReceiver()) { for (PrototypeIterator iter(isolate, JSReceiver::cast(*value), - PrototypeIterator::START_AT_RECEIVER); + kStartAtReceiver); !iter.IsAtEnd(); iter.Advance()) { if (iter.GetCurrent<JSReceiver>() == *object) { // Cycle detected. @@ -15851,6 +15190,17 @@ Maybe<bool> JSObject::SetPrototypeUnobserved(Handle<JSObject> object, return Just(true); } +// static +void JSObject::SetImmutableProto(Handle<JSObject> object) { + DCHECK(!object->IsAccessCheckNeeded()); // Never called from JS + Handle<Map> map(object->map()); + + // Nothing to do if prototype is already set. + if (map->is_immutable_proto()) return; + + Handle<Map> new_map = Map::TransitionToImmutableProto(map); + object->set_map(*new_map); +} void JSObject::EnsureCanContainElements(Handle<JSObject> object, Arguments* args, @@ -15994,12 +15344,8 @@ Maybe<bool> JSObject::AddDataElement(Handle<JSObject> object, uint32_t index, uint32_t old_length = 0; uint32_t new_capacity = 0; - Handle<Object> old_length_handle; if (object->IsJSArray()) { CHECK(JSArray::cast(*object)->length()->ToArrayLength(&old_length)); - if (object->map()->is_observed()) { - old_length_handle = handle(JSArray::cast(*object)->length(), isolate); - } } ElementsKind kind = object->GetElementsKind(); @@ -16043,38 +15389,6 @@ Maybe<bool> JSObject::AddDataElement(Handle<JSObject> object, uint32_t index, JSArray::cast(*object)->set_length(*new_length_handle); } - if (!old_length_handle.is_null() && new_length != old_length) { - // |old_length_handle| is kept null above unless the object is observed. - DCHECK(object->map()->is_observed()); - Handle<JSArray> array = Handle<JSArray>::cast(object); - Handle<String> name = isolate->factory()->Uint32ToString(index); - - RETURN_ON_EXCEPTION_VALUE(isolate, BeginPerformSplice(array), - Nothing<bool>()); - RETURN_ON_EXCEPTION_VALUE( - isolate, EnqueueChangeRecord(array, "add", name, - isolate->factory()->the_hole_value()), - Nothing<bool>()); - RETURN_ON_EXCEPTION_VALUE( - isolate, EnqueueChangeRecord(array, "update", - isolate->factory()->length_string(), - old_length_handle), - Nothing<bool>()); - RETURN_ON_EXCEPTION_VALUE(isolate, EndPerformSplice(array), - Nothing<bool>()); - Handle<JSArray> deleted = isolate->factory()->NewJSArray(0); - RETURN_ON_EXCEPTION_VALUE(isolate, - EnqueueSpliceRecord(array, old_length, deleted, - new_length - old_length), - Nothing<bool>()); - } else if (object->map()->is_observed()) { - Handle<String> name = isolate->factory()->Uint32ToString(index); - RETURN_ON_EXCEPTION_VALUE( - isolate, EnqueueChangeRecord(object, "add", name, - isolate->factory()->the_hole_value()), - Nothing<bool>()); - } - return Just(true); } @@ -16342,10 +15656,11 @@ int JSObject::GetFastElementsUsage() { #ifdef OBJECT_PRINT template <typename Derived, typename Shape, typename Key> void Dictionary<Derived, Shape, Key>::Print(std::ostream& os) { // NOLINT + Isolate* isolate = this->GetIsolate(); int capacity = this->Capacity(); for (int i = 0; i < capacity; i++) { Object* k = this->KeyAt(i); - if (this->IsKey(k)) { + if (this->IsKey(isolate, k)) { os << "\n "; if (k->IsString()) { String::cast(k)->StringPrint(os); @@ -16356,18 +15671,24 @@ void Dictionary<Derived, Shape, Key>::Print(std::ostream& os) { // NOLINT } } } +template <typename Derived, typename Shape, typename Key> +void Dictionary<Derived, Shape, Key>::Print() { + OFStream os(stdout); + Print(os); +} #endif template<typename Derived, typename Shape, typename Key> void Dictionary<Derived, Shape, Key>::CopyValuesTo(FixedArray* elements) { + Isolate* isolate = this->GetIsolate(); int pos = 0; int capacity = this->Capacity(); DisallowHeapAllocation no_gc; WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc); for (int i = 0; i < capacity; i++) { Object* k = this->KeyAt(i); - if (this->IsKey(k)) { + if (this->IsKey(isolate, k)) { elements->set(pos++, this->ValueAt(i), mode); } } @@ -16377,55 +15698,10 @@ void Dictionary<Derived, Shape, Key>::CopyValuesTo(FixedArray* elements) { MaybeHandle<Object> JSObject::GetPropertyWithInterceptor(LookupIterator* it, bool* done) { - *done = false; - Isolate* isolate = it->isolate(); - // Make sure that the top context does not change when doing callbacks or - // interceptor calls. - AssertNoContextChange ncc(isolate); - DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); - Handle<InterceptorInfo> interceptor = it->GetInterceptor(); - if (interceptor->getter()->IsUndefined()) { - return isolate->factory()->undefined_value(); - } - - Handle<JSObject> holder = it->GetHolder<JSObject>(); - Handle<Object> result; - Handle<Object> receiver = it->GetReceiver(); - if (!receiver->IsJSReceiver()) { - ASSIGN_RETURN_ON_EXCEPTION( - isolate, receiver, Object::ConvertReceiver(isolate, receiver), Object); - } - PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, - *holder, Object::DONT_THROW); - - if (it->IsElement()) { - uint32_t index = it->index(); - v8::IndexedPropertyGetterCallback getter = - v8::ToCData<v8::IndexedPropertyGetterCallback>(interceptor->getter()); - result = args.Call(getter, index); - } else { - Handle<Name> name = it->name(); - DCHECK(!name->IsPrivate()); - - if (name->IsSymbol() && !interceptor->can_intercept_symbols()) { - return isolate->factory()->undefined_value(); - } - - v8::GenericNamedPropertyGetterCallback getter = - v8::ToCData<v8::GenericNamedPropertyGetterCallback>( - interceptor->getter()); - result = args.Call(getter, name); - } - - RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); - if (result.is_null()) return isolate->factory()->undefined_value(); - *done = true; - // Rebox handle before return - return handle(*result, isolate); + return GetPropertyWithInterceptorInternal(it, it->GetInterceptor(), done); } - Maybe<bool> JSObject::HasRealNamedProperty(Handle<JSObject> object, Handle<Name> name) { LookupIterator it = LookupIterator::PropertyOrElement( @@ -16452,6 +15728,10 @@ Maybe<bool> JSObject::HasRealNamedCallbackProperty(Handle<JSObject> object, : Nothing<bool>(); } +int FixedArrayBase::GetMaxLengthForNewSpaceAllocation(ElementsKind kind) { + return ((Page::kMaxRegularHeapObjectSize - FixedArrayBase::kHeaderSize) >> + ElementsKindToShiftSize(kind)); +} void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) { Object* temp = get(i); @@ -16565,196 +15845,46 @@ void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) { } } -void JSObject::CollectOwnPropertyNames(KeyAccumulator* keys, - PropertyFilter filter) { - if (HasFastProperties()) { - int real_size = map()->NumberOfOwnDescriptors(); - Handle<DescriptorArray> descs(map()->instance_descriptors()); - for (int i = 0; i < real_size; i++) { - PropertyDetails details = descs->GetDetails(i); - if ((details.attributes() & filter) != 0) continue; - if (filter & ONLY_ALL_CAN_READ) { - if (details.kind() != kAccessor) continue; - Object* accessors = descs->GetValue(i); - if (!accessors->IsAccessorInfo()) continue; - if (!AccessorInfo::cast(accessors)->all_can_read()) continue; - } - Name* key = descs->GetKey(i); - if (key->FilterKey(filter)) continue; - keys->AddKey(key, DO_NOT_CONVERT); - } - } else if (IsJSGlobalObject()) { - GlobalDictionary::CollectKeysTo(handle(global_dictionary()), keys, filter); - } else { - NameDictionary::CollectKeysTo(handle(property_dictionary()), keys, filter); - } -} - - -int JSObject::NumberOfOwnElements(PropertyFilter filter) { - // Fast case for objects with no elements. - if (!IsJSValue() && HasFastElements()) { - uint32_t length = - IsJSArray() - ? static_cast<uint32_t>( - Smi::cast(JSArray::cast(this)->length())->value()) - : static_cast<uint32_t>(FixedArrayBase::cast(elements())->length()); - if (length == 0) return 0; - } - // Compute the number of enumerable elements. - return GetOwnElementKeys(NULL, filter); -} - -void JSObject::CollectOwnElementKeys(Handle<JSObject> object, - KeyAccumulator* keys, - PropertyFilter filter) { - if (filter & SKIP_STRINGS) return; - ElementsAccessor* accessor = object->GetElementsAccessor(); - accessor->CollectElementIndices(object, keys, kMaxUInt32, filter, 0); -} - - -int JSObject::GetOwnElementKeys(FixedArray* storage, PropertyFilter filter) { - int counter = 0; - - // If this is a String wrapper, add the string indices first, - // as they're guaranteed to precede the elements in numerical order - // and ascending order is required by ECMA-262, 6th, 9.1.12. - if (IsJSValue()) { - Object* val = JSValue::cast(this)->value(); - if (val->IsString()) { - String* str = String::cast(val); - if (storage) { - for (int i = 0; i < str->length(); i++) { - storage->set(counter + i, Smi::FromInt(i)); - } - } - counter += str->length(); - } - } - - switch (GetElementsKind()) { - case FAST_SMI_ELEMENTS: - case FAST_ELEMENTS: - case FAST_HOLEY_SMI_ELEMENTS: - case FAST_HOLEY_ELEMENTS: - case FAST_STRING_WRAPPER_ELEMENTS: { - int length = IsJSArray() ? - Smi::cast(JSArray::cast(this)->length())->value() : - FixedArray::cast(elements())->length(); - for (int i = 0; i < length; i++) { - if (!FixedArray::cast(elements())->get(i)->IsTheHole()) { - if (storage != NULL) { - storage->set(counter, Smi::FromInt(i)); - } - counter++; - } - } - DCHECK(!storage || storage->length() >= counter); - break; - } - case FAST_DOUBLE_ELEMENTS: - case FAST_HOLEY_DOUBLE_ELEMENTS: { - int length = IsJSArray() ? - Smi::cast(JSArray::cast(this)->length())->value() : - FixedArrayBase::cast(elements())->length(); - for (int i = 0; i < length; i++) { - if (!FixedDoubleArray::cast(elements())->is_the_hole(i)) { - if (storage != NULL) { - storage->set(counter, Smi::FromInt(i)); - } - counter++; - } - } - DCHECK(!storage || storage->length() >= counter); - break; - } - -#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ - case TYPE##_ELEMENTS: \ - - TYPED_ARRAYS(TYPED_ARRAY_CASE) -#undef TYPED_ARRAY_CASE - { - int length = FixedArrayBase::cast(elements())->length(); - while (counter < length) { - if (storage != NULL) { - storage->set(counter, Smi::FromInt(counter)); - } - counter++; - } - DCHECK(!storage || storage->length() >= counter); - break; - } - - case DICTIONARY_ELEMENTS: - case SLOW_STRING_WRAPPER_ELEMENTS: { - if (storage != NULL) { - element_dictionary()->CopyKeysTo(storage, counter, filter, - SeededNumberDictionary::SORTED); - } - counter += element_dictionary()->NumberOfElementsFilterAttributes(filter); - break; - } - case FAST_SLOPPY_ARGUMENTS_ELEMENTS: - case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: { - FixedArray* parameter_map = FixedArray::cast(elements()); - int mapped_length = parameter_map->length() - 2; - FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); - if (arguments->IsDictionary()) { - // Copy the keys from arguments first, because Dictionary::CopyKeysTo - // will insert in storage starting at index 0. - SeededNumberDictionary* dictionary = - SeededNumberDictionary::cast(arguments); - if (storage != NULL) { - dictionary->CopyKeysTo(storage, counter, filter, - SeededNumberDictionary::UNSORTED); - } - counter += dictionary->NumberOfElementsFilterAttributes(filter); - for (int i = 0; i < mapped_length; ++i) { - if (!parameter_map->get(i + 2)->IsTheHole()) { - if (storage != NULL) storage->set(counter, Smi::FromInt(i)); - ++counter; - } - } - if (storage != NULL) storage->SortPairs(storage, counter); - - } else { - int backing_length = arguments->length(); - int i = 0; - for (; i < mapped_length; ++i) { - if (!parameter_map->get(i + 2)->IsTheHole()) { - if (storage != NULL) storage->set(counter, Smi::FromInt(i)); - ++counter; - } else if (i < backing_length && !arguments->get(i)->IsTheHole()) { - if (storage != NULL) storage->set(counter, Smi::FromInt(i)); - ++counter; - } - } - for (; i < backing_length; ++i) { - if (storage != NULL) storage->set(counter, Smi::FromInt(i)); - ++counter; - } - } - break; +bool JSObject::WasConstructedFromApiFunction() { + auto instance_type = map()->instance_type(); + bool is_api_object = instance_type == JS_API_OBJECT_TYPE || + instance_type == JS_SPECIAL_API_OBJECT_TYPE; +#ifdef ENABLE_SLOW_DCHECKS + if (FLAG_enable_slow_asserts) { + Object* maybe_constructor = map()->GetConstructor(); + if (!maybe_constructor->IsJSFunction()) return false; + JSFunction* constructor = JSFunction::cast(maybe_constructor); + if (constructor->shared()->IsApiFunction()) { + DCHECK(is_api_object); + } else { + DCHECK(!is_api_object); } - case NO_ELEMENTS: - break; } - - DCHECK(!storage || storage->length() == counter); - return counter; +#endif + return is_api_object; } - MaybeHandle<String> Object::ObjectProtoToString(Isolate* isolate, Handle<Object> object) { - if (object->IsUndefined()) return isolate->factory()->undefined_to_string(); - if (object->IsNull()) return isolate->factory()->null_to_string(); + if (*object == isolate->heap()->undefined_value()) { + return isolate->factory()->undefined_to_string(); + } + if (*object == isolate->heap()->null_value()) { + return isolate->factory()->null_to_string(); + } Handle<JSReceiver> receiver = Object::ToObject(isolate, object).ToHandleChecked(); + // For proxies, we must check IsArray() before get(toStringTag) to comply + // with the specification + Maybe<bool> is_array = Nothing<bool>(); + InstanceType instance_type = receiver->map()->instance_type(); + if (instance_type == JS_PROXY_TYPE) { + is_array = Object::IsArray(receiver); + MAYBE_RETURN(is_array, MaybeHandle<String>()); + } + Handle<String> tag; Handle<Object> to_string_tag; ASSIGN_RETURN_ON_EXCEPTION( @@ -16764,11 +15894,55 @@ MaybeHandle<String> Object::ObjectProtoToString(Isolate* isolate, String); if (to_string_tag->IsString()) { tag = Handle<String>::cast(to_string_tag); - } - - if (tag.is_null()) { - ASSIGN_RETURN_ON_EXCEPTION(isolate, tag, - JSReceiver::BuiltinStringTag(receiver), String); + } else { + switch (instance_type) { + case JS_API_OBJECT_TYPE: + case JS_SPECIAL_API_OBJECT_TYPE: + tag = handle(receiver->class_name(), isolate); + break; + case JS_ARGUMENTS_TYPE: + return isolate->factory()->arguments_to_string(); + case JS_ARRAY_TYPE: + return isolate->factory()->array_to_string(); + case JS_BOUND_FUNCTION_TYPE: + case JS_FUNCTION_TYPE: + return isolate->factory()->function_to_string(); + case JS_ERROR_TYPE: + return isolate->factory()->error_to_string(); + case JS_DATE_TYPE: + return isolate->factory()->date_to_string(); + case JS_REGEXP_TYPE: + return isolate->factory()->regexp_to_string(); + case JS_PROXY_TYPE: { + if (is_array.FromJust()) { + return isolate->factory()->array_to_string(); + } + if (receiver->IsCallable()) { + return isolate->factory()->function_to_string(); + } + return isolate->factory()->object_to_string(); + } + case JS_VALUE_TYPE: { + Object* value = JSValue::cast(*receiver)->value(); + if (value->IsString()) { + return isolate->factory()->string_to_string(); + } + if (value->IsNumber()) { + return isolate->factory()->number_to_string(); + } + if (value->IsBoolean()) { + return isolate->factory()->boolean_to_string(); + } + if (value->IsSymbol()) { + return isolate->factory()->object_to_string(); + } + UNREACHABLE(); + tag = handle(receiver->class_name(), isolate); + break; + } + default: + return isolate->factory()->object_to_string(); + } } IncrementalStringBuilder builder(isolate); @@ -16778,7 +15952,6 @@ MaybeHandle<String> Object::ObjectProtoToString(Isolate* isolate, return builder.Finish(); } - const char* Symbol::PrivateSymbolToName() const { Heap* heap = GetIsolate()->heap(); #define SYMBOL_CHECK_AND_PRINT(name) \ @@ -16790,12 +15963,12 @@ const char* Symbol::PrivateSymbolToName() const { void Symbol::SymbolShortPrint(std::ostream& os) { - os << "<Symbol: " << Hash(); - if (!name()->IsUndefined()) { + os << "<Symbol:"; + if (!name()->IsUndefined(GetIsolate())) { os << " "; HeapStringAllocator allocator; StringStream accumulator(&allocator); - String::cast(name())->StringShortPrint(&accumulator); + String::cast(name())->StringShortPrint(&accumulator, false); os << accumulator.ToCString().get(); } else { os << " (" << PrivateSymbolToName() << ")"; @@ -16847,7 +16020,7 @@ class StringSharedKey : public HashTableKey { // collection. Script* script(Script::cast(shared->script())); hash ^= String::cast(script->source())->Hash(); - STATIC_ASSERT(LANGUAGE_END == 3); + STATIC_ASSERT(LANGUAGE_END == 2); if (is_strict(language_mode)) hash ^= 0x8000; hash += scope_position; } @@ -16913,7 +16086,6 @@ JSRegExp::Flags RegExpFlagsFromString(Handle<String> flags, bool* success) { flag = JSRegExp::kMultiline; break; case 'u': - if (!FLAG_harmony_unicode_regexps) return JSRegExp::Flags(0); flag = JSRegExp::kUnicode; break; case 'y': @@ -16945,22 +16117,6 @@ MaybeHandle<JSRegExp> JSRegExp::New(Handle<String> pattern, Flags flags) { // static -MaybeHandle<JSRegExp> JSRegExp::New(Handle<String> pattern, - Handle<String> flags_string) { - Isolate* isolate = pattern->GetIsolate(); - bool success = false; - Flags flags = RegExpFlagsFromString(flags_string, &success); - if (!success) { - THROW_NEW_ERROR( - isolate, - NewSyntaxError(MessageTemplate::kInvalidRegExpFlags, flags_string), - JSRegExp); - } - return New(pattern, flags); -} - - -// static Handle<JSRegExp> JSRegExp::Copy(Handle<JSRegExp> regexp) { Isolate* const isolate = regexp->GetIsolate(); return Handle<JSRegExp>::cast(isolate->factory()->CopyJSObject(regexp)); @@ -16973,7 +16129,13 @@ inline int CountRequiredEscapes(Handle<String> source) { int escapes = 0; Vector<const Char> src = source->GetCharVector<Char>(); for (int i = 0; i < src.length(); i++) { - if (src[i] == '/' && (i == 0 || src[i - 1] != '\\')) escapes++; + if (src[i] == '\\') { + // Escape. Skip next character; + i++; + } else if (src[i] == '/') { + // Not escaped forward-slash needs escape. + escapes++; + } } return escapes; } @@ -16988,7 +16150,14 @@ inline Handle<StringType> WriteEscapedRegExpSource(Handle<String> source, int s = 0; int d = 0; while (s < src.length()) { - if (src[s] == '/' && (s == 0 || src[s - 1] != '\\')) dst[d++] = '\\'; + if (src[s] == '\\') { + // Escape. Copy this and next character. + dst[d++] = src[s++]; + if (s == src.length()) break; + } else if (src[s] == '/') { + // Not escaped forward-slash needs escape. + dst[d++] = '\\'; + } dst[d++] = src[s++]; } DCHECK_EQ(result->length(), d); @@ -17051,6 +16220,9 @@ MaybeHandle<JSRegExp> JSRegExp::Initialize(Handle<JSRegExp> regexp, ASSIGN_RETURN_ON_EXCEPTION(isolate, escaped_source, EscapeRegExpSource(isolate, source), JSRegExp); + RETURN_ON_EXCEPTION(isolate, RegExpImpl::Compile(regexp, source, flags), + JSRegExp); + regexp->set_source(*escaped_source); regexp->set_flags(Smi::FromInt(flags)); @@ -17063,17 +16235,13 @@ MaybeHandle<JSRegExp> JSRegExp::Initialize(Handle<JSRegExp> regexp, Smi::FromInt(0), SKIP_WRITE_BARRIER); } else { // Map has changed, so use generic, but slower, method. - PropertyAttributes writable = - static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE); - JSObject::SetOwnPropertyIgnoreAttributes( - regexp, factory->last_index_string(), - Handle<Smi>(Smi::FromInt(0), isolate), writable) - .Check(); + RETURN_ON_EXCEPTION( + isolate, + JSReceiver::SetProperty(regexp, factory->last_index_string(), + Handle<Smi>(Smi::FromInt(0), isolate), STRICT), + JSRegExp); } - RETURN_ON_EXCEPTION(isolate, RegExpImpl::Compile(regexp, source, flags), - JSRegExp); - return regexp; } @@ -17214,7 +16382,7 @@ Handle<Derived> HashTable<Derived, Shape, Key>::New( Factory* factory = isolate->factory(); int length = EntryToIndex(capacity); Handle<FixedArray> array = factory->NewFixedArray(length, pretenure); - array->set_map_no_write_barrier(*factory->hash_table_map()); + array->set_map_no_write_barrier(Shape::GetMap(isolate)); Handle<Derived> table = Handle<Derived>::cast(array); table->SetNumberOfElements(0); @@ -17244,21 +16412,12 @@ int NameDictionaryBase<Derived, Shape>::FindEntry(Handle<Name> key) { uint32_t capacity = this->Capacity(); uint32_t entry = Derived::FirstProbe(key->Hash(), capacity); uint32_t count = 1; - + Isolate* isolate = this->GetIsolate(); while (true) { - int index = Derived::EntryToIndex(entry); - Object* element = this->get(index); - if (element->IsUndefined()) break; // Empty entry. + Object* element = this->KeyAt(entry); + if (element->IsUndefined(isolate)) break; // Empty entry. if (*key == element) return entry; - if (!element->IsUniqueName() && - !element->IsTheHole() && - Name::cast(element)->Equals(*key)) { - // Replace a key that is a non-internalized string by the equivalent - // internalized string for faster further lookups. - this->set(index, *key); - return entry; - } - DCHECK(element->IsTheHole() || !Name::cast(element)->Equals(*key)); + DCHECK(element->IsTheHole(isolate) || element->IsUniqueName()); entry = Derived::NextProbe(entry, count++, capacity); } return Derived::kNotFound; @@ -17283,10 +16442,13 @@ void HashTable<Derived, Shape, Key>::Rehash( // Rehash the elements. int capacity = this->Capacity(); + Heap* heap = new_table->GetHeap(); + Object* the_hole = heap->the_hole_value(); + Object* undefined = heap->undefined_value(); for (int i = 0; i < capacity; i++) { uint32_t from_index = EntryToIndex(i); Object* k = this->get(from_index); - if (IsKey(k)) { + if (k != the_hole && k != undefined) { uint32_t hash = this->HashForObject(key, k); uint32_t insertion_index = EntryToIndex(new_table->FindInsertionEntry(hash)); @@ -17340,6 +16502,7 @@ template<typename Derived, typename Shape, typename Key> void HashTable<Derived, Shape, Key>::Rehash(Key key) { DisallowHeapAllocation no_gc; WriteBarrierMode mode = GetWriteBarrierMode(no_gc); + Isolate* isolate = GetIsolate(); uint32_t capacity = Capacity(); bool done = false; for (int probe = 1; !done; probe++) { @@ -17347,11 +16510,11 @@ void HashTable<Derived, Shape, Key>::Rehash(Key key) { // are placed correctly. Other elements might need to be moved. done = true; for (uint32_t current = 0; current < capacity; current++) { - Object* current_key = get(EntryToIndex(current)); - if (IsKey(current_key)) { + Object* current_key = KeyAt(current); + if (IsKey(isolate, current_key)) { uint32_t target = EntryForProbe(key, current_key, probe, current); if (current == target) continue; - Object* target_key = get(EntryToIndex(target)); + Object* target_key = KeyAt(target); if (!IsKey(target_key) || EntryForProbe(key, target_key, probe, target) != target) { // Put the current element into the correct position. @@ -17367,12 +16530,11 @@ void HashTable<Derived, Shape, Key>::Rehash(Key key) { } } // Wipe deleted entries. - Heap* heap = GetHeap(); - Object* the_hole = heap->the_hole_value(); - Object* undefined = heap->undefined_value(); + Object* the_hole = isolate->heap()->the_hole_value(); + Object* undefined = isolate->heap()->undefined_value(); for (uint32_t current = 0; current < capacity; current++) { - if (get(EntryToIndex(current)) == the_hole) { - set(EntryToIndex(current), undefined); + if (KeyAt(current) == the_hole) { + set(EntryToIndex(current) + Derived::kEntryKeyIndex, undefined); } } SetNumberOfDeletedElements(0); @@ -17460,9 +16622,10 @@ uint32_t HashTable<Derived, Shape, Key>::FindInsertionEntry(uint32_t hash) { uint32_t entry = FirstProbe(hash, capacity); uint32_t count = 1; // EnsureCapacity will guarantee the hash table is never full. + Isolate* isolate = GetIsolate(); while (true) { Object* element = KeyAt(entry); - if (element->IsUndefined() || element->IsTheHole()) break; + if (!IsKey(isolate, element)) break; entry = NextProbe(entry, count++, capacity); } return entry; @@ -17498,20 +16661,23 @@ template class Dictionary<UnseededNumberDictionary, uint32_t>; template Handle<SeededNumberDictionary> -Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>:: - New(Isolate*, int at_least_space_for, PretenureFlag pretenure); +Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::New( + Isolate*, int at_least_space_for, PretenureFlag pretenure, + MinimumCapacity capacity_option); template Handle<UnseededNumberDictionary> -Dictionary<UnseededNumberDictionary, UnseededNumberDictionaryShape, uint32_t>:: - New(Isolate*, int at_least_space_for, PretenureFlag pretenure); +Dictionary<UnseededNumberDictionary, UnseededNumberDictionaryShape, + uint32_t>::New(Isolate*, int at_least_space_for, + PretenureFlag pretenure, + MinimumCapacity capacity_option); template Handle<NameDictionary> -Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >:: - New(Isolate*, int n, PretenureFlag pretenure); +Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>::New( + Isolate*, int n, PretenureFlag pretenure, MinimumCapacity capacity_option); template Handle<GlobalDictionary> -Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name> >::New( - Isolate*, int n, PretenureFlag pretenure); +Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>::New( + Isolate*, int n, PretenureFlag pretenure, MinimumCapacity capacity_option); template Handle<SeededNumberDictionary> Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>:: @@ -17545,6 +16711,11 @@ template Handle<NameDictionary> HashTable<NameDictionary, NameDictionaryShape, Handle<Name> >:: New(Isolate*, int, MinimumCapacity, PretenureFlag); +template Handle<ObjectHashSet> HashTable<ObjectHashSet, ObjectHashSetShape, + Handle<Object>>::New(Isolate*, int n, + MinimumCapacity, + PretenureFlag); + template Handle<NameDictionary> HashTable<NameDictionary, NameDictionaryShape, Handle<Name> >:: Shrink(Handle<NameDictionary>, Handle<Name>); @@ -17558,13 +16729,14 @@ template Handle<UnseededNumberDictionary> uint32_t>::Shrink(Handle<UnseededNumberDictionary>, uint32_t); template Handle<NameDictionary> -Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >::Add( - Handle<NameDictionary>, Handle<Name>, Handle<Object>, PropertyDetails); +Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>::Add( + Handle<NameDictionary>, Handle<Name>, Handle<Object>, PropertyDetails, + int*); template Handle<GlobalDictionary> - Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name> >::Add( - Handle<GlobalDictionary>, Handle<Name>, Handle<Object>, - PropertyDetails); +Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>::Add( + Handle<GlobalDictionary>, Handle<Name>, Handle<Object>, PropertyDetails, + int*); template Handle<FixedArray> Dictionary< NameDictionary, NameDictionaryShape, @@ -17575,18 +16747,14 @@ template Handle<FixedArray> Dictionary< Handle<Name> >::GenerateNewEnumerationIndices(Handle<NameDictionary>); template Handle<SeededNumberDictionary> -Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>:: - Add(Handle<SeededNumberDictionary>, - uint32_t, - Handle<Object>, - PropertyDetails); +Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::Add( + Handle<SeededNumberDictionary>, uint32_t, Handle<Object>, PropertyDetails, + int*); template Handle<UnseededNumberDictionary> -Dictionary<UnseededNumberDictionary, UnseededNumberDictionaryShape, uint32_t>:: - Add(Handle<UnseededNumberDictionary>, - uint32_t, - Handle<Object>, - PropertyDetails); +Dictionary<UnseededNumberDictionary, UnseededNumberDictionaryShape, + uint32_t>::Add(Handle<UnseededNumberDictionary>, uint32_t, + Handle<Object>, PropertyDetails, int*); template Handle<SeededNumberDictionary> Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>:: @@ -17609,6 +16777,39 @@ template int HashTable<SeededNumberDictionary, SeededNumberDictionaryShape, template int NameDictionaryBase<NameDictionary, NameDictionaryShape>::FindEntry( Handle<Name>); +template int Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>:: + NumberOfElementsFilterAttributes(PropertyFilter filter); + +template int Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>:: + NumberOfElementsFilterAttributes(PropertyFilter filter); + +template void +Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>:: + CopyEnumKeysTo(Handle<Dictionary<GlobalDictionary, GlobalDictionaryShape, + Handle<Name>>> + dictionary, + Handle<FixedArray> storage, KeyCollectionMode mode, + KeyAccumulator* accumulator); + +template void +Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>::CopyEnumKeysTo( + Handle<Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>> + dictionary, + Handle<FixedArray> storage, KeyCollectionMode mode, + KeyAccumulator* accumulator); + +template void +Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>:: + CollectKeysTo(Handle<Dictionary<GlobalDictionary, GlobalDictionaryShape, + Handle<Name>>> + dictionary, + KeyAccumulator* keys); + +template void +Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>::CollectKeysTo( + Handle<Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>> + dictionary, + KeyAccumulator* keys); Handle<Object> JSObject::PrepareSlowElementsForSort( Handle<JSObject> object, uint32_t limit) { @@ -17630,7 +16831,7 @@ Handle<Object> JSObject::PrepareSlowElementsForSort( DisallowHeapAllocation no_gc; for (int i = 0; i < capacity; i++) { Object* k = dict->KeyAt(i); - if (!dict->IsKey(k)) continue; + if (!dict->IsKey(isolate, k)) continue; DCHECK(k->IsNumber()); DCHECK(!k->IsSmi() || Smi::cast(k)->value() >= 0); @@ -17648,7 +16849,7 @@ Handle<Object> JSObject::PrepareSlowElementsForSort( uint32_t key = NumberToUint32(k); if (key < limit) { - if (value->IsUndefined()) { + if (value->IsUndefined(isolate)) { undefs++; } else if (pos > static_cast<uint32_t>(Smi::kMaxValue)) { // Adding an entry with the key beyond smi-range requires @@ -17705,8 +16906,7 @@ Handle<Object> JSObject::PrepareSlowElementsForSort( Handle<Object> JSObject::PrepareElementsForSort(Handle<JSObject> object, uint32_t limit) { Isolate* isolate = object->GetIsolate(); - if (object->HasSloppyArgumentsElements() || - object->map()->is_observed()) { + if (object->HasSloppyArgumentsElements()) { return handle(Smi::FromInt(-1), isolate); } @@ -17799,10 +16999,10 @@ Handle<Object> JSObject::PrepareElementsForSort(Handle<JSObject> object, // number of stores of non-undefined, non-the-hole values. for (unsigned int i = 0; i < undefs; i++) { Object* current = elements->get(i); - if (current->IsTheHole()) { + if (current->IsTheHole(isolate)) { holes--; undefs--; - } else if (current->IsUndefined()) { + } else if (current->IsUndefined(isolate)) { undefs--; } else { continue; @@ -17810,10 +17010,10 @@ Handle<Object> JSObject::PrepareElementsForSort(Handle<JSObject> object, // Position i needs to be filled. while (undefs > i) { current = elements->get(undefs); - if (current->IsTheHole()) { + if (current->IsTheHole(isolate)) { holes--; undefs--; - } else if (current->IsUndefined()) { + } else if (current->IsUndefined(isolate)) { undefs--; } else { elements->set(i, current, write_barrier); @@ -17877,29 +17077,35 @@ void JSGlobalObject::InvalidatePropertyCell(Handle<JSGlobalObject> global, PropertyCell::InvalidateEntry(dictionary, entry); } - -// TODO(ishell): rename to EnsureEmptyPropertyCell or something. -Handle<PropertyCell> JSGlobalObject::EnsurePropertyCell( - Handle<JSGlobalObject> global, Handle<Name> name) { +Handle<PropertyCell> JSGlobalObject::EnsureEmptyPropertyCell( + Handle<JSGlobalObject> global, Handle<Name> name, + PropertyCellType cell_type, int* entry_out) { + Isolate* isolate = global->GetIsolate(); DCHECK(!global->HasFastProperties()); - auto dictionary = handle(global->global_dictionary()); + Handle<GlobalDictionary> dictionary(global->global_dictionary(), isolate); int entry = dictionary->FindEntry(name); Handle<PropertyCell> cell; if (entry != GlobalDictionary::kNotFound) { + if (entry_out) *entry_out = entry; // This call should be idempotent. DCHECK(dictionary->ValueAt(entry)->IsPropertyCell()); cell = handle(PropertyCell::cast(dictionary->ValueAt(entry))); - DCHECK(cell->property_details().cell_type() == - PropertyCellType::kUninitialized || - cell->property_details().cell_type() == - PropertyCellType::kInvalidated); - DCHECK(cell->value()->IsTheHole()); + PropertyCellType original_cell_type = cell->property_details().cell_type(); + DCHECK(original_cell_type == PropertyCellType::kInvalidated || + original_cell_type == PropertyCellType::kUninitialized); + DCHECK(cell->value()->IsTheHole(isolate)); + if (original_cell_type == PropertyCellType::kInvalidated) { + cell = PropertyCell::InvalidateEntry(dictionary, entry); + } + PropertyDetails details(NONE, DATA, 0, cell_type); + cell->set_property_details(details); return cell; } - Isolate* isolate = global->GetIsolate(); cell = isolate->factory()->NewPropertyCell(); - PropertyDetails details(NONE, DATA, 0, PropertyCellType::kUninitialized); - dictionary = GlobalDictionary::Add(dictionary, name, cell, details); + PropertyDetails details(NONE, DATA, 0, cell_type); + dictionary = + GlobalDictionary::Add(dictionary, name, cell, details, entry_out); + // {*entry_out} is initialized inside GlobalDictionary::Add(). global->set_properties(*dictionary); return cell; } @@ -18036,6 +17242,16 @@ Handle<String> StringTable::LookupString(Isolate* isolate, Handle<ConsString> cons = Handle<ConsString>::cast(string); cons->set_first(*result); cons->set_second(isolate->heap()->empty_string()); + } else if (string->IsSlicedString()) { + STATIC_ASSERT(ConsString::kSize == SlicedString::kSize); + DisallowHeapAllocation no_gc; + bool one_byte = result->IsOneByteRepresentation(); + Handle<Map> map = one_byte ? isolate->factory()->cons_one_byte_string_map() + : isolate->factory()->cons_string_map(); + string->set_map(*map); + Handle<ConsString> cons = Handle<ConsString>::cast(string); + cons->set_first(*result); + cons->set_second(isolate->heap()->empty_string()); } return result; } @@ -18096,12 +17312,26 @@ bool StringSet::Has(Handle<String> name) { return FindEntry(*name) != kNotFound; } +Handle<ObjectHashSet> ObjectHashSet::Add(Handle<ObjectHashSet> set, + Handle<Object> key) { + Isolate* isolate = set->GetIsolate(); + int32_t hash = Object::GetOrCreateHash(isolate, key)->value(); + + if (!set->Has(isolate, key, hash)) { + set = EnsureCapacity(set, 1, key); + int entry = set->FindInsertionEntry(hash); + set->set(EntryToIndex(entry), *key); + set->ElementAdded(); + } + return set; +} + Handle<Object> CompilationCacheTable::Lookup(Handle<String> src, Handle<Context> context, LanguageMode language_mode) { Isolate* isolate = GetIsolate(); Handle<SharedFunctionInfo> shared(context->closure()->shared()); - StringSharedKey key(src, shared, language_mode, RelocInfo::kNoPosition); + StringSharedKey key(src, shared, language_mode, kNoSourcePosition); int entry = FindEntry(&key); if (entry == kNotFound) return isolate->factory()->undefined_value(); int index = EntryToIndex(entry); @@ -18141,24 +17371,12 @@ Handle<CompilationCacheTable> CompilationCacheTable::Put( Handle<Context> context, LanguageMode language_mode, Handle<Object> value) { Isolate* isolate = cache->GetIsolate(); Handle<SharedFunctionInfo> shared(context->closure()->shared()); - StringSharedKey key(src, shared, language_mode, RelocInfo::kNoPosition); - { - Handle<Object> k = key.AsHandle(isolate); - DisallowHeapAllocation no_allocation_scope; - int entry = cache->FindEntry(&key); - if (entry != kNotFound) { - cache->set(EntryToIndex(entry), *k); - cache->set(EntryToIndex(entry) + 1, *value); - return cache; - } - } - + StringSharedKey key(src, shared, language_mode, kNoSourcePosition); + Handle<Object> k = key.AsHandle(isolate); cache = EnsureCapacity(cache, 1, &key); int entry = cache->FindInsertionEntry(key.Hash()); - Handle<Object> k = - isolate->factory()->NewNumber(static_cast<double>(key.Hash())); cache->set(EntryToIndex(entry), *k); - cache->set(EntryToIndex(entry) + 1, Smi::FromInt(kHashGenerations)); + cache->set(EntryToIndex(entry) + 1, *value); cache->ElementAdded(); return cache; } @@ -18251,17 +17469,13 @@ void CompilationCacheTable::Remove(Object* value) { return; } - -template<typename Derived, typename Shape, typename Key> +template <typename Derived, typename Shape, typename Key> Handle<Derived> Dictionary<Derived, Shape, Key>::New( - Isolate* isolate, - int at_least_space_for, - PretenureFlag pretenure) { + Isolate* isolate, int at_least_space_for, PretenureFlag pretenure, + MinimumCapacity capacity_option) { DCHECK(0 <= at_least_space_for); - Handle<Derived> dict = DerivedHashTable::New(isolate, - at_least_space_for, - USE_DEFAULT_MINIMUM_CAPACITY, - pretenure); + Handle<Derived> dict = DerivedHashTable::New(isolate, at_least_space_for, + capacity_option, pretenure); // Initialize the next enumeration index. dict->SetNextEnumerationIndex(PropertyDetails::kInitialIndex); @@ -18272,7 +17486,8 @@ Handle<Derived> Dictionary<Derived, Shape, Key>::New( template <typename Derived, typename Shape, typename Key> Handle<FixedArray> Dictionary<Derived, Shape, Key>::BuildIterationIndicesArray( Handle<Derived> dictionary) { - Factory* factory = dictionary->GetIsolate()->factory(); + Isolate* isolate = dictionary->GetIsolate(); + Factory* factory = isolate->factory(); int length = dictionary->NumberOfElements(); Handle<FixedArray> iteration_order = factory->NewFixedArray(length); @@ -18283,7 +17498,7 @@ Handle<FixedArray> Dictionary<Derived, Shape, Key>::BuildIterationIndicesArray( int capacity = dictionary->Capacity(); int pos = 0; for (int i = 0; i < capacity; i++) { - if (dictionary->IsKey(dictionary->KeyAt(i))) { + if (dictionary->IsKey(isolate, dictionary->KeyAt(i))) { int index = dictionary->DetailsAt(i).dictionary_index(); iteration_order->set(pos, Smi::FromInt(i)); enumeration_order->set(pos, Smi::FromInt(index)); @@ -18385,31 +17600,28 @@ Handle<Derived> Dictionary<Derived, Shape, Key>::AtPut( return dictionary; } - -template<typename Derived, typename Shape, typename Key> -Handle<Derived> Dictionary<Derived, Shape, Key>::Add( - Handle<Derived> dictionary, - Key key, - Handle<Object> value, - PropertyDetails details) { +template <typename Derived, typename Shape, typename Key> +Handle<Derived> Dictionary<Derived, Shape, Key>::Add(Handle<Derived> dictionary, + Key key, + Handle<Object> value, + PropertyDetails details, + int* entry_out) { // Valdate key is absent. SLOW_DCHECK((dictionary->FindEntry(key) == Dictionary::kNotFound)); // Check whether the dictionary should be extended. dictionary = EnsureCapacity(dictionary, 1, key); - AddEntry(dictionary, key, value, details, dictionary->Hash(key)); + int entry = AddEntry(dictionary, key, value, details, dictionary->Hash(key)); + if (entry_out) *entry_out = entry; return dictionary; } - -// Add a key, value pair to the dictionary. -template<typename Derived, typename Shape, typename Key> -void Dictionary<Derived, Shape, Key>::AddEntry( - Handle<Derived> dictionary, - Key key, - Handle<Object> value, - PropertyDetails details, - uint32_t hash) { +// Add a key, value pair to the dictionary. Returns entry value. +template <typename Derived, typename Shape, typename Key> +int Dictionary<Derived, Shape, Key>::AddEntry(Handle<Derived> dictionary, + Key key, Handle<Object> value, + PropertyDetails details, + uint32_t hash) { // Compute the key object. Handle<Object> k = Shape::AsHandle(dictionary->GetIsolate(), key); @@ -18426,20 +17638,21 @@ void Dictionary<Derived, Shape, Key>::AddEntry( DCHECK((dictionary->KeyAt(entry)->IsNumber() || dictionary->KeyAt(entry)->IsName())); dictionary->ElementAdded(); + return entry; } bool SeededNumberDictionary::HasComplexElements() { if (!requires_slow_elements()) return false; + Isolate* isolate = this->GetIsolate(); int capacity = this->Capacity(); for (int i = 0; i < capacity; i++) { Object* k = this->KeyAt(i); - if (this->IsKey(k)) { - DCHECK(!IsDeleted(i)); - PropertyDetails details = this->DetailsAt(i); - if (details.type() == ACCESSOR_CONSTANT) return true; - PropertyAttributes attr = details.attributes(); - if (attr & ALL_ATTRIBUTES_MASK) return true; - } + if (!this->IsKey(isolate, k)) continue; + DCHECK(!IsDeleted(i)); + PropertyDetails details = this->DetailsAt(i); + if (details.type() == ACCESSOR_CONSTANT) return true; + PropertyAttributes attr = details.attributes(); + if (attr & ALL_ATTRIBUTES_MASK) return true; } return false; } @@ -18486,6 +17699,17 @@ Handle<UnseededNumberDictionary> UnseededNumberDictionary::AddNumberEntry( return Add(dictionary, key, value, PropertyDetails::Empty()); } +Handle<UnseededNumberDictionary> UnseededNumberDictionary::DeleteKey( + Handle<UnseededNumberDictionary> dictionary, uint32_t key) { + int entry = dictionary->FindEntry(key); + if (entry == kNotFound) return dictionary; + + Factory* factory = dictionary->GetIsolate()->factory(); + dictionary->SetEntry(entry, factory->the_hole_value(), + factory->the_hole_value()); + dictionary->ElementRemoved(); + return dictionary->Shrink(dictionary, key); +} Handle<SeededNumberDictionary> SeededNumberDictionary::AtNumberPut( Handle<SeededNumberDictionary> dictionary, uint32_t key, @@ -18535,11 +17759,12 @@ Handle<UnseededNumberDictionary> UnseededNumberDictionary::Set( template <typename Derived, typename Shape, typename Key> int Dictionary<Derived, Shape, Key>::NumberOfElementsFilterAttributes( PropertyFilter filter) { + Isolate* isolate = this->GetIsolate(); int capacity = this->Capacity(); int result = 0; for (int i = 0; i < capacity; i++) { Object* k = this->KeyAt(i); - if (this->IsKey(k) && !k->FilterKey(filter)) { + if (this->IsKey(isolate, k) && !k->FilterKey(filter)) { if (this->IsDeleted(i)) continue; PropertyDetails details = this->DetailsAt(i); PropertyAttributes attr = details.attributes(); @@ -18561,74 +17786,73 @@ struct EnumIndexComparator { Dictionary* dict; }; - template <typename Derived, typename Shape, typename Key> -void Dictionary<Derived, Shape, Key>::CopyEnumKeysTo(FixedArray* storage) { +void Dictionary<Derived, Shape, Key>::CopyEnumKeysTo( + Handle<Dictionary<Derived, Shape, Key>> dictionary, + Handle<FixedArray> storage, KeyCollectionMode mode, + KeyAccumulator* accumulator) { + Isolate* isolate = dictionary->GetIsolate(); int length = storage->length(); - int capacity = this->Capacity(); + int capacity = dictionary->Capacity(); int properties = 0; for (int i = 0; i < capacity; i++) { - Object* k = this->KeyAt(i); - if (this->IsKey(k) && !k->IsSymbol()) { - PropertyDetails details = this->DetailsAt(i); - if (details.IsDontEnum() || this->IsDeleted(i)) continue; + Object* key = dictionary->KeyAt(i); + bool is_shadowing_key = false; + if (!dictionary->IsKey(isolate, key)) continue; + if (key->IsSymbol()) continue; + PropertyDetails details = dictionary->DetailsAt(i); + if (details.IsDontEnum()) { + if (mode == KeyCollectionMode::kIncludePrototypes) { + is_shadowing_key = true; + } else { + continue; + } + } + if (dictionary->IsDeleted(i)) continue; + if (is_shadowing_key) { + accumulator->AddShadowingKey(key); + continue; + } else { storage->set(properties, Smi::FromInt(i)); - properties++; - if (properties == length) break; } + properties++; + if (properties == length) break; } + CHECK_EQ(length, properties); - EnumIndexComparator<Derived> cmp(static_cast<Derived*>(this)); + DisallowHeapAllocation no_gc; + Dictionary<Derived, Shape, Key>* raw_dictionary = *dictionary; + FixedArray* raw_storage = *storage; + EnumIndexComparator<Derived> cmp(static_cast<Derived*>(*dictionary)); Smi** start = reinterpret_cast<Smi**>(storage->GetFirstElementAddress()); std::sort(start, start + length, cmp); for (int i = 0; i < length; i++) { - int index = Smi::cast(storage->get(i))->value(); - storage->set(i, this->KeyAt(index)); - } -} - - -template <typename Derived, typename Shape, typename Key> -int Dictionary<Derived, Shape, Key>::CopyKeysTo( - FixedArray* storage, int index, PropertyFilter filter, - typename Dictionary<Derived, Shape, Key>::SortMode sort_mode) { - DCHECK(storage->length() >= NumberOfElementsFilterAttributes(filter)); - int start_index = index; - int capacity = this->Capacity(); - for (int i = 0; i < capacity; i++) { - Object* k = this->KeyAt(i); - if (!this->IsKey(k) || k->FilterKey(filter)) continue; - if (this->IsDeleted(i)) continue; - PropertyDetails details = this->DetailsAt(i); - PropertyAttributes attr = details.attributes(); - if ((attr & filter) != 0) continue; - storage->set(index++, k); - } - if (sort_mode == Dictionary::SORTED) { - storage->SortPairs(storage, index); + int index = Smi::cast(raw_storage->get(i))->value(); + raw_storage->set(i, raw_dictionary->KeyAt(index)); } - DCHECK(storage->length() >= index); - return index - start_index; } template <typename Derived, typename Shape, typename Key> void Dictionary<Derived, Shape, Key>::CollectKeysTo( - Handle<Dictionary<Derived, Shape, Key> > dictionary, KeyAccumulator* keys, - PropertyFilter filter) { + Handle<Dictionary<Derived, Shape, Key>> dictionary, KeyAccumulator* keys) { + Isolate* isolate = keys->isolate(); int capacity = dictionary->Capacity(); Handle<FixedArray> array = - keys->isolate()->factory()->NewFixedArray(dictionary->NumberOfElements()); + isolate->factory()->NewFixedArray(dictionary->NumberOfElements()); int array_size = 0; - + PropertyFilter filter = keys->filter(); { DisallowHeapAllocation no_gc; Dictionary<Derived, Shape, Key>* raw_dict = *dictionary; for (int i = 0; i < capacity; i++) { Object* k = raw_dict->KeyAt(i); - if (!raw_dict->IsKey(k) || k->FilterKey(filter)) continue; + if (!raw_dict->IsKey(isolate, k) || k->FilterKey(filter)) continue; if (raw_dict->IsDeleted(i)) continue; PropertyDetails details = raw_dict->DetailsAt(i); - if ((details.attributes() & filter) != 0) continue; + if ((details.attributes() & filter) != 0) { + keys->AddShadowingKey(k); + continue; + } if (filter & ONLY_ALL_CAN_READ) { if (details.kind() != kAccessor) continue; Object* accessors = raw_dict->ValueAt(i); @@ -18646,9 +17870,23 @@ void Dictionary<Derived, Shape, Key>::CollectKeysTo( std::sort(start, start + array_size, cmp); } + bool has_seen_symbol = false; for (int i = 0; i < array_size; i++) { int index = Smi::cast(array->get(i))->value(); - keys->AddKey(dictionary->KeyAt(index), DO_NOT_CONVERT); + Object* key = dictionary->KeyAt(index); + if (key->IsSymbol()) { + has_seen_symbol = true; + continue; + } + keys->AddKey(key, DO_NOT_CONVERT); + } + if (has_seen_symbol) { + for (int i = 0; i < array_size; i++) { + int index = Smi::cast(array->get(i))->value(); + Object* key = dictionary->KeyAt(index); + if (!key->IsSymbol()) continue; + keys->AddKey(key, DO_NOT_CONVERT); + } } } @@ -18656,27 +17894,26 @@ void Dictionary<Derived, Shape, Key>::CollectKeysTo( // Backwards lookup (slow). template<typename Derived, typename Shape, typename Key> Object* Dictionary<Derived, Shape, Key>::SlowReverseLookup(Object* value) { + Isolate* isolate = this->GetIsolate(); int capacity = this->Capacity(); for (int i = 0; i < capacity; i++) { Object* k = this->KeyAt(i); - if (this->IsKey(k)) { - Object* e = this->ValueAt(i); - // TODO(dcarney): this should be templatized. - if (e->IsPropertyCell()) { - e = PropertyCell::cast(e)->value(); - } - if (e == value) return k; + if (!this->IsKey(isolate, k)) continue; + Object* e = this->ValueAt(i); + // TODO(dcarney): this should be templatized. + if (e->IsPropertyCell()) { + e = PropertyCell::cast(e)->value(); } + if (e == value) return k; } - Heap* heap = Dictionary::GetHeap(); - return heap->undefined_value(); + return isolate->heap()->undefined_value(); } Object* ObjectHashTable::Lookup(Isolate* isolate, Handle<Object> key, int32_t hash) { DisallowHeapAllocation no_gc; - DCHECK(IsKey(*key)); + DCHECK(IsKey(isolate, *key)); int entry = FindEntry(isolate, key, hash); if (entry == kNotFound) return isolate->heap()->the_hole_value(); @@ -18686,13 +17923,13 @@ Object* ObjectHashTable::Lookup(Isolate* isolate, Handle<Object> key, Object* ObjectHashTable::Lookup(Handle<Object> key) { DisallowHeapAllocation no_gc; - DCHECK(IsKey(*key)); Isolate* isolate = GetIsolate(); + DCHECK(IsKey(isolate, *key)); // If the object does not have an identity hash, it was never used as a key. Object* hash = key->GetHash(); - if (hash->IsUndefined()) { + if (hash->IsUndefined(isolate)) { return isolate->heap()->the_hole_value(); } return Lookup(isolate, key, Smi::cast(hash)->value()); @@ -18707,10 +17944,10 @@ Object* ObjectHashTable::Lookup(Handle<Object> key, int32_t hash) { Handle<ObjectHashTable> ObjectHashTable::Put(Handle<ObjectHashTable> table, Handle<Object> key, Handle<Object> value) { - DCHECK(table->IsKey(*key)); - DCHECK(!value->IsTheHole()); - Isolate* isolate = table->GetIsolate(); + DCHECK(table->IsKey(isolate, *key)); + DCHECK(!value->IsTheHole(isolate)); + // Make sure the key object has an identity hash code. int32_t hash = Object::GetOrCreateHash(isolate, key)->value(); @@ -18722,10 +17959,9 @@ Handle<ObjectHashTable> ObjectHashTable::Put(Handle<ObjectHashTable> table, Handle<Object> key, Handle<Object> value, int32_t hash) { - DCHECK(table->IsKey(*key)); - DCHECK(!value->IsTheHole()); - Isolate* isolate = table->GetIsolate(); + DCHECK(table->IsKey(isolate, *key)); + DCHECK(!value->IsTheHole(isolate)); int entry = table->FindEntry(isolate, key, hash); @@ -18735,7 +17971,7 @@ Handle<ObjectHashTable> ObjectHashTable::Put(Handle<ObjectHashTable> table, return table; } - // Rehash if more than 25% of the entries are deleted entries. + // Rehash if more than 33% of the entries are deleted entries. // TODO(jochen): Consider to shrink the fixed array in place. if ((table->NumberOfDeletedElements() << 1) > table->NumberOfElements()) { table->Rehash(isolate->factory()->undefined_value()); @@ -18764,10 +18000,10 @@ Handle<ObjectHashTable> ObjectHashTable::Put(Handle<ObjectHashTable> table, Handle<ObjectHashTable> ObjectHashTable::Remove(Handle<ObjectHashTable> table, Handle<Object> key, bool* was_present) { - DCHECK(table->IsKey(*key)); + DCHECK(table->IsKey(table->GetIsolate(), *key)); Object* hash = key->GetHash(); - if (hash->IsUndefined()) { + if (hash->IsUndefined(table->GetIsolate())) { *was_present = false; return table; } @@ -18780,9 +18016,10 @@ Handle<ObjectHashTable> ObjectHashTable::Remove(Handle<ObjectHashTable> table, Handle<Object> key, bool* was_present, int32_t hash) { - DCHECK(table->IsKey(*key)); + Isolate* isolate = table->GetIsolate(); + DCHECK(table->IsKey(isolate, *key)); - int entry = table->FindEntry(table->GetIsolate(), key, hash); + int entry = table->FindEntry(isolate, key, hash); if (entry == kNotFound) { *was_present = false; return table; @@ -18810,9 +18047,10 @@ void ObjectHashTable::RemoveEntry(int entry) { Object* WeakHashTable::Lookup(Handle<HeapObject> key) { DisallowHeapAllocation no_gc; - DCHECK(IsKey(*key)); + Isolate* isolate = GetIsolate(); + DCHECK(IsKey(isolate, *key)); int entry = FindEntry(key); - if (entry == kNotFound) return GetHeap()->the_hole_value(); + if (entry == kNotFound) return isolate->heap()->the_hole_value(); return get(EntryToValueIndex(entry)); } @@ -18820,7 +18058,8 @@ Object* WeakHashTable::Lookup(Handle<HeapObject> key) { Handle<WeakHashTable> WeakHashTable::Put(Handle<WeakHashTable> table, Handle<HeapObject> key, Handle<HeapObject> value) { - DCHECK(table->IsKey(*key)); + Isolate* isolate = key->GetIsolate(); + DCHECK(table->IsKey(isolate, *key)); int entry = table->FindEntry(key); // Key is already in table, just overwrite value. if (entry != kNotFound) { @@ -18828,7 +18067,7 @@ Handle<WeakHashTable> WeakHashTable::Put(Handle<WeakHashTable> table, return table; } - Handle<WeakCell> key_cell = key->GetIsolate()->factory()->NewWeakCell(key); + Handle<WeakCell> key_cell = isolate->factory()->NewWeakCell(key); // Check whether the hash table should be extended. table = EnsureCapacity(table, 1, key, TENURED); @@ -18922,11 +18161,14 @@ Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Clear( template <class Derived, class Iterator, int entrysize> bool OrderedHashTable<Derived, Iterator, entrysize>::HasKey( Handle<Derived> table, Handle<Object> key) { - int entry = table->KeyToFirstEntry(*key); + DisallowHeapAllocation no_gc; + Isolate* isolate = table->GetIsolate(); + Object* raw_key = *key; + int entry = table->KeyToFirstEntry(isolate, raw_key); // Walk the chain in the bucket to find the key. while (entry != kNotFound) { Object* candidate_key = table->KeyAt(entry); - if (candidate_key->SameValueZero(*key)) return true; + if (candidate_key->SameValueZero(raw_key)) return true; entry = table->NextChainEntry(entry); } return false; @@ -18961,25 +18203,46 @@ Handle<OrderedHashSet> OrderedHashSet::Add(Handle<OrderedHashSet> table, return table; } +Handle<FixedArray> OrderedHashSet::ConvertToKeysArray( + Handle<OrderedHashSet> table, GetKeysConversion convert) { + Isolate* isolate = table->GetIsolate(); + int length = table->NumberOfElements(); + int nof_buckets = table->NumberOfBuckets(); + // Convert the dictionary to a linear list. + Handle<FixedArray> result = Handle<FixedArray>::cast(table); + // From this point on table is no longer a valid OrderedHashSet. + result->set_map(isolate->heap()->fixed_array_map()); + for (int i = 0; i < length; i++) { + int index = kHashTableStartIndex + nof_buckets + (i * kEntrySize); + Object* key = table->get(index); + if (convert == GetKeysConversion::kConvertToString && key->IsNumber()) { + key = *isolate->factory()->NumberToString(handle(key, isolate)); + } + result->set(i, key); + } + result->Shrink(length); + return result; +} template<class Derived, class Iterator, int entrysize> Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Rehash( Handle<Derived> table, int new_capacity) { + Isolate* isolate = table->GetIsolate(); DCHECK(!table->IsObsolete()); Handle<Derived> new_table = - Allocate(table->GetIsolate(), - new_capacity, - table->GetHeap()->InNewSpace(*table) ? NOT_TENURED : TENURED); + Allocate(isolate, new_capacity, + isolate->heap()->InNewSpace(*table) ? NOT_TENURED : TENURED); int nof = table->NumberOfElements(); int nod = table->NumberOfDeletedElements(); int new_buckets = new_table->NumberOfBuckets(); int new_entry = 0; int removed_holes_index = 0; + DisallowHeapAllocation no_gc; for (int old_entry = 0; old_entry < (nof + nod); ++old_entry) { Object* key = table->KeyAt(old_entry); - if (key->IsTheHole()) { + if (key->IsTheHole(isolate)) { table->SetRemovedIndexAt(removed_holes_index++, old_entry); continue; } @@ -19083,7 +18346,8 @@ void OrderedHashTableIterator<Derived, TableType>::Transition() { template<class Derived, class TableType> bool OrderedHashTableIterator<Derived, TableType>::HasMore() { DisallowHeapAllocation no_allocation; - if (this->table()->IsUndefined()) return false; + Isolate* isolate = this->GetIsolate(); + if (this->table()->IsUndefined(isolate)) return false; Transition(); @@ -19091,7 +18355,7 @@ bool OrderedHashTableIterator<Derived, TableType>::HasMore() { int index = Smi::cast(this->index())->value(); int used_capacity = table->UsedCapacity(); - while (index < used_capacity && table->KeyAt(index)->IsTheHole()) { + while (index < used_capacity && table->KeyAt(index)->IsTheHole(isolate)) { index++; } @@ -19099,7 +18363,7 @@ bool OrderedHashTableIterator<Derived, TableType>::HasMore() { if (index < used_capacity) return true; - set_table(GetHeap()->undefined_value()); + set_table(isolate->heap()->undefined_value()); return false; } @@ -19218,45 +18482,58 @@ bool JSWeakCollection::Delete(Handle<JSWeakCollection> weak_collection, return was_present; } -// Check if there is a break point at this code offset. -bool DebugInfo::HasBreakPoint(int code_offset) { +// Check if there is a break point at this source position. +bool DebugInfo::HasBreakPoint(int source_position) { // Get the break point info object for this code offset. - Object* break_point_info = GetBreakPointInfo(code_offset); + Object* break_point_info = GetBreakPointInfo(source_position); // If there is no break point info object or no break points in the break // point info object there is no break point at this code offset. - if (break_point_info->IsUndefined()) return false; + if (break_point_info->IsUndefined(GetIsolate())) return false; return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0; } -// Get the break point info object for this code offset. -Object* DebugInfo::GetBreakPointInfo(int code_offset) { - // Find the index of the break point info object for this code offset. - int index = GetBreakPointInfoIndex(code_offset); - - // Return the break point info object if any. - if (index == kNoBreakPointInfo) return GetHeap()->undefined_value(); - return BreakPointInfo::cast(break_points()->get(index)); +// Get the break point info object for this source position. +Object* DebugInfo::GetBreakPointInfo(int source_position) { + Isolate* isolate = GetIsolate(); + if (!break_points()->IsUndefined(isolate)) { + for (int i = 0; i < break_points()->length(); i++) { + if (!break_points()->get(i)->IsUndefined(isolate)) { + BreakPointInfo* break_point_info = + BreakPointInfo::cast(break_points()->get(i)); + if (break_point_info->source_position() == source_position) { + return break_point_info; + } + } + } + } + return isolate->heap()->undefined_value(); } -// Clear a break point at the specified code offset. -void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info, int code_offset, +bool DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info, Handle<Object> break_point_object) { - Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_offset), - debug_info->GetIsolate()); - if (break_point_info->IsUndefined()) return; - BreakPointInfo::ClearBreakPoint( - Handle<BreakPointInfo>::cast(break_point_info), - break_point_object); + Isolate* isolate = debug_info->GetIsolate(); + if (debug_info->break_points()->IsUndefined(isolate)) return false; + + for (int i = 0; i < debug_info->break_points()->length(); i++) { + if (debug_info->break_points()->get(i)->IsUndefined(isolate)) continue; + Handle<BreakPointInfo> break_point_info = Handle<BreakPointInfo>( + BreakPointInfo::cast(debug_info->break_points()->get(i)), isolate); + if (BreakPointInfo::HasBreakPointObject(break_point_info, + break_point_object)) { + BreakPointInfo::ClearBreakPoint(break_point_info, break_point_object); + return true; + } + } + return false; } -void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info, int code_offset, - int source_position, int statement_position, +void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info, int source_position, Handle<Object> break_point_object) { Isolate* isolate = debug_info->GetIsolate(); - Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_offset), - isolate); - if (!break_point_info->IsUndefined()) { + Handle<Object> break_point_info( + debug_info->GetBreakPointInfo(source_position), isolate); + if (!break_point_info->IsUndefined(isolate)) { BreakPointInfo::SetBreakPoint( Handle<BreakPointInfo>::cast(break_point_info), break_point_object); @@ -19265,17 +18542,18 @@ void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info, int code_offset, // Adding a new break point for a code offset which did not have any // break points before. Try to find a free slot. + static const int kNoBreakPointInfo = -1; int index = kNoBreakPointInfo; for (int i = 0; i < debug_info->break_points()->length(); i++) { - if (debug_info->break_points()->get(i)->IsUndefined()) { + if (debug_info->break_points()->get(i)->IsUndefined(isolate)) { index = i; break; } } if (index == kNoBreakPointInfo) { // No free slot - extend break point info array. - Handle<FixedArray> old_break_points = - Handle<FixedArray>(FixedArray::cast(debug_info->break_points())); + Handle<FixedArray> old_break_points = Handle<FixedArray>( + FixedArray::cast(debug_info->break_points()), isolate); Handle<FixedArray> new_break_points = isolate->factory()->NewFixedArray( old_break_points->length() + @@ -19292,33 +18570,32 @@ void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info, int code_offset, // Allocate new BreakPointInfo object and set the break point. Handle<BreakPointInfo> new_break_point_info = Handle<BreakPointInfo>::cast( isolate->factory()->NewStruct(BREAK_POINT_INFO_TYPE)); - new_break_point_info->set_code_offset(code_offset); new_break_point_info->set_source_position(source_position); - new_break_point_info->set_statement_position(statement_position); new_break_point_info->set_break_point_objects( isolate->heap()->undefined_value()); BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object); debug_info->break_points()->set(index, *new_break_point_info); } -// Get the break point objects for a code offset. -Handle<Object> DebugInfo::GetBreakPointObjects(int code_offset) { - Object* break_point_info = GetBreakPointInfo(code_offset); - if (break_point_info->IsUndefined()) { - return GetIsolate()->factory()->undefined_value(); +// Get the break point objects for a source position. +Handle<Object> DebugInfo::GetBreakPointObjects(int source_position) { + Object* break_point_info = GetBreakPointInfo(source_position); + Isolate* isolate = GetIsolate(); + if (break_point_info->IsUndefined(isolate)) { + return isolate->factory()->undefined_value(); } return Handle<Object>( - BreakPointInfo::cast(break_point_info)->break_point_objects(), - GetIsolate()); + BreakPointInfo::cast(break_point_info)->break_point_objects(), isolate); } // Get the total number of break points. int DebugInfo::GetBreakPointCount() { - if (break_points()->IsUndefined()) return 0; + Isolate* isolate = GetIsolate(); + if (break_points()->IsUndefined(isolate)) return 0; int count = 0; for (int i = 0; i < break_points()->length(); i++) { - if (!break_points()->get(i)->IsUndefined()) { + if (!break_points()->get(i)->IsUndefined(isolate)) { BreakPointInfo* break_point_info = BreakPointInfo::cast(break_points()->get(i)); count += break_point_info->GetBreakPointCount(); @@ -19331,9 +18608,9 @@ int DebugInfo::GetBreakPointCount() { Handle<Object> DebugInfo::FindBreakPointInfo( Handle<DebugInfo> debug_info, Handle<Object> break_point_object) { Isolate* isolate = debug_info->GetIsolate(); - if (!debug_info->break_points()->IsUndefined()) { + if (!debug_info->break_points()->IsUndefined(isolate)) { for (int i = 0; i < debug_info->break_points()->length(); i++) { - if (!debug_info->break_points()->get(i)->IsUndefined()) { + if (!debug_info->break_points()->get(i)->IsUndefined(isolate)) { Handle<BreakPointInfo> break_point_info = Handle<BreakPointInfo>( BreakPointInfo::cast(debug_info->break_points()->get(i)), isolate); if (BreakPointInfo::HasBreakPointObject(break_point_info, @@ -19346,30 +18623,12 @@ Handle<Object> DebugInfo::FindBreakPointInfo( return isolate->factory()->undefined_value(); } - -// Find the index of the break point info object for the specified code -// position. -int DebugInfo::GetBreakPointInfoIndex(int code_offset) { - if (break_points()->IsUndefined()) return kNoBreakPointInfo; - for (int i = 0; i < break_points()->length(); i++) { - if (!break_points()->get(i)->IsUndefined()) { - BreakPointInfo* break_point_info = - BreakPointInfo::cast(break_points()->get(i)); - if (break_point_info->code_offset() == code_offset) { - return i; - } - } - } - return kNoBreakPointInfo; -} - - // Remove the specified break point object. void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info, Handle<Object> break_point_object) { Isolate* isolate = break_point_info->GetIsolate(); // If there are no break points just ignore. - if (break_point_info->break_point_objects()->IsUndefined()) return; + if (break_point_info->break_point_objects()->IsUndefined(isolate)) return; // If there is a single break point clear it if it is the same. if (!break_point_info->break_point_objects()->IsFixedArray()) { if (break_point_info->break_point_objects() == *break_point_object) { @@ -19405,7 +18664,7 @@ void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info, Isolate* isolate = break_point_info->GetIsolate(); // If there was no break point objects before just set it. - if (break_point_info->break_point_objects()->IsUndefined()) { + if (break_point_info->break_point_objects()->IsUndefined(isolate)) { break_point_info->set_break_point_objects(*break_point_object); return; } @@ -19440,7 +18699,10 @@ bool BreakPointInfo::HasBreakPointObject( Handle<BreakPointInfo> break_point_info, Handle<Object> break_point_object) { // No break point. - if (break_point_info->break_point_objects()->IsUndefined()) return false; + Isolate* isolate = break_point_info->GetIsolate(); + if (break_point_info->break_point_objects()->IsUndefined(isolate)) { + return false; + } // Single break point. if (!break_point_info->break_point_objects()->IsFixedArray()) { return break_point_info->break_point_objects() == *break_point_object; @@ -19459,7 +18721,7 @@ bool BreakPointInfo::HasBreakPointObject( // Get the number of break points. int BreakPointInfo::GetBreakPointCount() { // No break point. - if (break_point_objects()->IsUndefined()) return 0; + if (break_point_objects()->IsUndefined(GetIsolate())) return 0; // Single break point. if (!break_point_objects()->IsFixedArray()) return 1; // Multiple break points. @@ -19660,6 +18922,62 @@ void JSDate::SetCachedFields(int64_t local_time_ms, DateCache* date_cache) { set_sec(Smi::FromInt(sec), SKIP_WRITE_BARRIER); } +namespace { + +Script* ScriptFromJSValue(Object* in) { + DCHECK(in->IsJSValue()); + JSValue* jsvalue = JSValue::cast(in); + DCHECK(jsvalue->value()->IsScript()); + return Script::cast(jsvalue->value()); +} + +} // namespace + +int JSMessageObject::GetLineNumber() const { + if (start_position() == -1) return Message::kNoLineNumberInfo; + + Handle<Script> the_script = handle(ScriptFromJSValue(script())); + + Script::PositionInfo info; + const Script::OffsetFlag offset_flag = Script::WITH_OFFSET; + if (!the_script->GetPositionInfo(start_position(), &info, offset_flag)) { + return Message::kNoLineNumberInfo; + } + + return info.line + 1; +} + +int JSMessageObject::GetColumnNumber() const { + if (start_position() == -1) return -1; + + Handle<Script> the_script = handle(ScriptFromJSValue(script())); + + Script::PositionInfo info; + const Script::OffsetFlag offset_flag = Script::WITH_OFFSET; + if (!the_script->GetPositionInfo(start_position(), &info, offset_flag)) { + return -1; + } + + return info.column; // Note: No '+1' in contrast to GetLineNumber. +} + +Handle<String> JSMessageObject::GetSourceLine() const { + Handle<Script> the_script = handle(ScriptFromJSValue(script())); + + Isolate* isolate = the_script->GetIsolate(); + if (the_script->type() == Script::TYPE_WASM) { + return isolate->factory()->empty_string(); + } + + Script::PositionInfo info; + const Script::OffsetFlag offset_flag = Script::WITH_OFFSET; + if (!the_script->GetPositionInfo(start_position(), &info, offset_flag)) { + return isolate->factory()->empty_string(); + } + + Handle<String> src = handle(String::cast(the_script->source()), isolate); + return isolate->factory()->NewSubString(src, info.line_start, info.line_end); +} void JSArrayBuffer::Neuter() { CHECK(is_neuterable()); @@ -19776,20 +19094,19 @@ Handle<JSArrayBuffer> JSTypedArray::GetBuffer() { return MaterializeArrayBuffer(self); } - Handle<PropertyCell> PropertyCell::InvalidateEntry( Handle<GlobalDictionary> dictionary, int entry) { Isolate* isolate = dictionary->GetIsolate(); // Swap with a copy. DCHECK(dictionary->ValueAt(entry)->IsPropertyCell()); Handle<PropertyCell> cell(PropertyCell::cast(dictionary->ValueAt(entry))); - auto new_cell = isolate->factory()->NewPropertyCell(); + Handle<PropertyCell> new_cell = isolate->factory()->NewPropertyCell(); new_cell->set_value(cell->value()); dictionary->ValueAtPut(entry, *new_cell); - bool is_the_hole = cell->value()->IsTheHole(); + bool is_the_hole = cell->value()->IsTheHole(isolate); // Cell is officially mutable henceforth. PropertyDetails details = cell->property_details(); - details = details.set_cell_type(is_the_hole ? PropertyCellType::kInvalidated + details = details.set_cell_type(is_the_hole ? PropertyCellType::kUninitialized : PropertyCellType::kMutable); new_cell->set_property_details(details); // Old cell is ready for invalidation. @@ -19830,12 +19147,13 @@ PropertyCellType PropertyCell::UpdatedType(Handle<PropertyCell> cell, Handle<Object> value, PropertyDetails details) { PropertyCellType type = details.cell_type(); - DCHECK(!value->IsTheHole()); - if (cell->value()->IsTheHole()) { + Isolate* isolate = cell->GetIsolate(); + DCHECK(!value->IsTheHole(isolate)); + if (cell->value()->IsTheHole(isolate)) { switch (type) { // Only allow a cell to transition once into constant state. case PropertyCellType::kUninitialized: - if (value->IsUndefined()) return PropertyCellType::kUndefined; + if (value->IsUndefined(isolate)) return PropertyCellType::kUndefined; return PropertyCellType::kConstant; case PropertyCellType::kInvalidated: return PropertyCellType::kMutable; @@ -19862,10 +19180,11 @@ PropertyCellType PropertyCell::UpdatedType(Handle<PropertyCell> cell, return PropertyCellType::kMutable; } - -void PropertyCell::UpdateCell(Handle<GlobalDictionary> dictionary, int entry, - Handle<Object> value, PropertyDetails details) { - DCHECK(!value->IsTheHole()); +Handle<PropertyCell> PropertyCell::PrepareForValue( + Handle<GlobalDictionary> dictionary, int entry, Handle<Object> value, + PropertyDetails details) { + Isolate* isolate = dictionary->GetIsolate(); + DCHECK(!value->IsTheHole(isolate)); DCHECK(dictionary->ValueAt(entry)->IsPropertyCell()); Handle<PropertyCell> cell(PropertyCell::cast(dictionary->ValueAt(entry))); const PropertyDetails original_details = cell->property_details(); @@ -19876,11 +19195,9 @@ void PropertyCell::UpdateCell(Handle<GlobalDictionary> dictionary, int entry, PropertyCellType old_type = original_details.cell_type(); // Preserve the enumeration index unless the property was deleted or never // initialized. - if (cell->value()->IsTheHole()) { + if (cell->value()->IsTheHole(isolate)) { index = dictionary->NextEnumerationIndex(); dictionary->SetNextEnumerationIndex(index + 1); - // Negative lookup cells must be invalidated. - invalidate = true; } DCHECK(index > 0); details = details.set_index(index); @@ -19888,18 +19205,17 @@ void PropertyCell::UpdateCell(Handle<GlobalDictionary> dictionary, int entry, PropertyCellType new_type = UpdatedType(cell, value, original_details); if (invalidate) cell = PropertyCell::InvalidateEntry(dictionary, entry); - // Install new property details and cell value. + // Install new property details. details = details.set_cell_type(new_type); cell->set_property_details(details); - cell->set_value(*value); // Deopt when transitioning from a constant type. if (!invalidate && (old_type != new_type || original_details.IsReadOnly() != details.IsReadOnly())) { - Isolate* isolate = dictionary->GetIsolate(); cell->dependent_code()->DeoptimizeDependentCodeGroup( isolate, DependentCode::kPropertyCellChangedGroup); } + return cell; } @@ -19914,5 +19230,56 @@ void PropertyCell::SetValueWithInvalidation(Handle<PropertyCell> cell, } } +int JSGeneratorObject::source_position() const { + CHECK(is_suspended()); + AbstractCode* code; + int code_offset; + if (function()->shared()->HasBytecodeArray()) { + // New-style generators. + DCHECK(!function()->shared()->HasBaselineCode()); + code_offset = Smi::cast(input_or_debug_pos())->value(); + // The stored bytecode offset is relative to a different base than what + // is used in the source position table, hence the subtraction. + code_offset -= BytecodeArray::kHeaderSize - kHeapObjectTag; + code = AbstractCode::cast(function()->shared()->bytecode_array()); + } else { + // Old-style generators. + DCHECK(function()->shared()->HasBaselineCode()); + code_offset = continuation(); + CHECK(0 <= code_offset); + CHECK(code_offset < function()->code()->instruction_size()); + code = AbstractCode::cast(function()->shared()->code()); + } + return code->SourcePosition(code_offset); +} + +// static +AccessCheckInfo* AccessCheckInfo::Get(Isolate* isolate, + Handle<JSObject> receiver) { + DisallowHeapAllocation no_gc; + DCHECK(receiver->map()->is_access_check_needed()); + Object* maybe_constructor = receiver->map()->GetConstructor(); + // Might happen for a detached context. + if (!maybe_constructor->IsJSFunction()) return nullptr; + JSFunction* constructor = JSFunction::cast(maybe_constructor); + // Might happen for the debug context. + if (!constructor->shared()->IsApiFunction()) return nullptr; + + Object* data_obj = + constructor->shared()->get_api_func_data()->access_check_info(); + if (data_obj->IsUndefined(isolate)) return nullptr; + + return AccessCheckInfo::cast(data_obj); +} + +bool JSReceiver::HasProxyInPrototype(Isolate* isolate) { + for (PrototypeIterator iter(isolate, this, kStartAtReceiver, + PrototypeIterator::END_AT_NULL); + !iter.IsAtEnd(); iter.AdvanceIgnoringProxies()) { + if (iter.GetCurrent<Object>()->IsJSProxy()) return true; + } + return false; +} + } // namespace internal } // namespace v8 |