diff options
Diffstat (limited to 'deps/v8/src/objects.cc')
-rw-r--r-- | deps/v8/src/objects.cc | 4509 |
1 files changed, 3166 insertions, 1343 deletions
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 7f5ce5a091..ef846d6c42 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -1,4 +1,4 @@ -// Copyright 2013 the V8 project authors. All rights reserved. +// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -28,6 +28,7 @@ #include "src/field-index-inl.h" #include "src/full-codegen/full-codegen.h" #include "src/ic/ic.h" +#include "src/identity-map.h" #include "src/interpreter/bytecodes.h" #include "src/isolate-inl.h" #include "src/key-accumulator.h" @@ -37,14 +38,17 @@ #include "src/macro-assembler.h" #include "src/messages.h" #include "src/objects-inl.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/string-builder.h" #include "src/string-search.h" #include "src/string-stream.h" #include "src/utils.h" +#include "src/zone.h" #ifdef ENABLE_DISASSEMBLER #include "src/disasm.h" @@ -54,6 +58,19 @@ namespace v8 { namespace internal { +std::ostream& operator<<(std::ostream& os, InstanceType instance_type) { + switch (instance_type) { +#define WRITE_TYPE(TYPE) \ + case TYPE: \ + return os << #TYPE; + INSTANCE_TYPE_LIST(WRITE_TYPE) +#undef WRITE_TYPE + } + UNREACHABLE(); + return os << "UNKNOWN"; // Keep the compiler happy. +} + + Handle<HeapType> Object::OptimalType(Isolate* isolate, Representation representation) { if (representation.IsNone()) return HeapType::None(isolate); @@ -61,9 +78,7 @@ Handle<HeapType> Object::OptimalType(Isolate* isolate, if (representation.IsHeapObject() && IsHeapObject()) { // We can track only JavaScript objects with stable maps. Handle<Map> map(HeapObject::cast(this)->map(), isolate); - if (map->is_stable() && - map->instance_type() >= FIRST_NONCALLABLE_SPEC_OBJECT_TYPE && - map->instance_type() <= LAST_NONCALLABLE_SPEC_OBJECT_TYPE) { + if (map->is_stable() && map->IsJSReceiverMap()) { return HeapType::Class(map, isolate); } } @@ -608,6 +623,23 @@ MaybeHandle<Object> Object::BitwiseXor(Isolate* isolate, Handle<Object> lhs, } +Maybe<bool> Object::IsArray(Handle<Object> object) { + if (object->IsJSArray()) return Just(true); + if (object->IsJSProxy()) { + Handle<JSProxy> proxy = Handle<JSProxy>::cast(object); + Isolate* isolate = proxy->GetIsolate(); + if (proxy->IsRevoked()) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyRevoked, + isolate->factory()->NewStringFromAsciiChecked("IsArray"))); + return Nothing<bool>(); + } + return Object::IsArray(handle(proxy->target(), isolate)); + } + return Just(false); +} + + bool Object::IsPromise(Handle<Object> object) { if (!object->IsJSObject()) return false; auto js_object = Handle<JSObject>::cast(object); @@ -633,9 +665,8 @@ MaybeHandle<Object> Object::GetMethod(Handle<JSReceiver> receiver, return isolate->factory()->undefined_value(); } if (!func->IsCallable()) { - // TODO(bmeurer): Better error message here? - THROW_NEW_ERROR(isolate, - NewTypeError(MessageTemplate::kCalledNonCallable, func), + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kPropertyNotFunction, + func, name, receiver), Object); } return func; @@ -643,6 +674,72 @@ MaybeHandle<Object> Object::GetMethod(Handle<JSReceiver> receiver, // static +MaybeHandle<FixedArray> Object::CreateListFromArrayLike( + Isolate* isolate, Handle<Object> object, ElementTypes element_types) { + // 1. ReturnIfAbrupt(object). + // 2. (default elementTypes -- not applicable.) + // 3. If Type(obj) is not Object, throw a TypeError exception. + if (!object->IsJSReceiver()) { + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kCalledOnNonObject, + isolate->factory()->NewStringFromAsciiChecked( + "CreateListFromArrayLike")), + FixedArray); + } + // 4. Let len be ? ToLength(? Get(obj, "length")). + Handle<Object> raw_length_obj; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, raw_length_obj, + JSReceiver::GetProperty(object, isolate->factory()->length_string()), + FixedArray); + Handle<Object> raw_length_number; + ASSIGN_RETURN_ON_EXCEPTION(isolate, raw_length_number, + Object::ToLength(isolate, raw_length_obj), + FixedArray); + uint32_t len; + if (!raw_length_number->ToUint32(&len) || + len > static_cast<uint32_t>(FixedArray::kMaxLength)) { + THROW_NEW_ERROR(isolate, + NewRangeError(MessageTemplate::kInvalidArrayLength), + FixedArray); + } + // 5. Let list be an empty List. + Handle<FixedArray> list = isolate->factory()->NewFixedArray(len); + // 6. Let index be 0. + // 7. Repeat while index < len: + for (uint32_t index = 0; index < len; ++index) { + // 7a. Let indexName be ToString(index). + // 7b. Let next be ? Get(obj, indexName). + Handle<Object> next; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, next, Object::GetElement(isolate, object, index), FixedArray); + switch (element_types) { + case ElementTypes::kAll: + // Nothing to do. + break; + case ElementTypes::kStringAndSymbol: { + // 7c. If Type(next) is not an element of elementTypes, throw a + // TypeError exception. + if (!next->IsName()) { + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kNotPropertyName, next), + FixedArray); + } + // 7d. Append next as the last element of list. + // Internalize on the fly so we can use pointer identity later. + next = isolate->factory()->InternalizeName(Handle<Name>::cast(next)); + break; + } + } + list->set(index, *next); + // 7e. Set index to index + 1. (See loop header.) + } + // 8. Return list. + return list; +} + + +// static Maybe<bool> JSReceiver::HasProperty(LookupIterator* it) { for (; it->IsFound(); it->Next()) { switch (it->state()) { @@ -651,8 +748,8 @@ Maybe<bool> JSReceiver::HasProperty(LookupIterator* it) { UNREACHABLE(); case LookupIterator::JSPROXY: // Call the "has" trap on proxies. - return JSProxy::HasPropertyWithHandler(it->GetHolder<JSProxy>(), - it->GetName()); + return JSProxy::HasProperty(it->isolate(), it->GetHolder<JSProxy>(), + it->GetName()); case LookupIterator::INTERCEPTOR: { Maybe<PropertyAttributes> result = JSObject::GetPropertyAttributesWithInterceptor(it); @@ -688,8 +785,9 @@ MaybeHandle<Object> Object::GetProperty(LookupIterator* it, case LookupIterator::TRANSITION: UNREACHABLE(); case LookupIterator::JSPROXY: - return JSProxy::GetPropertyWithHandler( - it->GetHolder<JSProxy>(), it->GetReceiver(), it->GetName()); + return JSProxy::GetProperty(it->isolate(), it->GetHolder<JSProxy>(), + it->GetName(), it->GetReceiver(), + language_mode); case LookupIterator::INTERCEPTOR: { bool done; Handle<Object> result; @@ -714,6 +812,104 @@ 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, + Handle<Name> name, + Handle<Object> receiver, + LanguageMode language_mode) { + if (receiver->IsJSGlobalObject()) { + THROW_NEW_ERROR( + isolate, + NewTypeError(MessageTemplate::kReadGlobalReferenceThroughProxy, name), + Object); + } + + DCHECK(!name->IsPrivate()); + STACK_CHECK(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. + Handle<Object> handler(proxy->handler(), isolate); + // 3. If handler is null, throw a TypeError exception. + // 4. Assert: Type(handler) is Object. + if (proxy->IsRevoked()) { + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kProxyRevoked, trap_name), + Object); + } + // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. + Handle<JSReceiver> target(proxy->target(), isolate); + // 6. Let trap be ? GetMethod(handler, "get"). + Handle<Object> trap; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, trap, + Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name), Object); + // 7. If trap is undefined, then + if (trap->IsUndefined()) { + // 7.a Return target.[[Get]](P, Receiver). + LookupIterator it = + LookupIterator::PropertyOrElement(isolate, receiver, name, target); + return Object::GetProperty(&it, language_mode); + } + // 8. Let trapResult be ? Call(trap, handler, «target, P, Receiver»). + Handle<Object> trap_result; + Handle<Object> args[] = {target, name, receiver}; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, trap_result, + Execution::Call(isolate, trap, handler, arraysize(args), args), Object); + // 9. Let targetDesc be ? target.[[GetOwnProperty]](P). + PropertyDescriptor target_desc; + Maybe<bool> target_found = + JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc); + MAYBE_RETURN_NULL(target_found); + // 10. If targetDesc is not undefined, then + if (target_found.FromJust()) { + // 10.a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is + // false and targetDesc.[[Writable]] is false, then + // 10.a.i. If SameValue(trapResult, targetDesc.[[Value]]) is false, + // throw a TypeError exception. + bool inconsistent = PropertyDescriptor::IsDataDescriptor(&target_desc) && + !target_desc.configurable() && + !target_desc.writable() && + !trap_result->SameValue(*target_desc.value()); + if (inconsistent) { + THROW_NEW_ERROR( + isolate, NewTypeError(MessageTemplate::kProxyGetNonConfigurableData, + name, target_desc.value(), trap_result), + Object); + } + // 10.b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] + // is false and targetDesc.[[Get]] is undefined, then + // 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(); + if (inconsistent) { + THROW_NEW_ERROR( + isolate, + NewTypeError(MessageTemplate::kProxyGetNonConfigurableAccessor, name, + trap_result), + Object); + } + } + // 11. Return trap_result + return trap_result; +} + + Handle<Object> JSReceiver::GetDataProperty(Handle<JSReceiver> object, Handle<Name> name) { LookupIterator it(object, name, @@ -730,7 +926,9 @@ Handle<Object> JSReceiver::GetDataProperty(LookupIterator* it) { case LookupIterator::TRANSITION: UNREACHABLE(); case LookupIterator::ACCESS_CHECK: - if (it->HasAccess()) continue; + // Support calling this method without an active context, but refuse + // access to access-checked objects in that case. + if (it->isolate()->context() != nullptr && it->HasAccess()) continue; // Fall through. case LookupIterator::JSPROXY: it->NotFound(); @@ -831,6 +1029,33 @@ Object* FunctionTemplateInfo::GetCompatibleReceiver(Isolate* isolate, } +// static +MaybeHandle<JSObject> JSObject::New(Handle<JSFunction> constructor, + Handle<JSReceiver> new_target, + Handle<AllocationSite> site) { + // If called through new, new.target can be: + // - a subclass of constructor, + // - a proxy wrapper around constructor, or + // - the constructor itself. + // If called through Reflect.construct, it's guaranteed to be a constructor. + Isolate* const isolate = constructor->GetIsolate(); + DCHECK(constructor->IsConstructor()); + DCHECK(new_target->IsConstructor()); + DCHECK(!constructor->has_initial_map() || + constructor->initial_map()->instance_type() != JS_FUNCTION_TYPE); + + Handle<Map> initial_map; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, initial_map, + JSFunction::GetDerivedMap(isolate, constructor, new_target), JSObject); + Handle<JSObject> result = + isolate->factory()->NewJSObjectFromMap(initial_map, NOT_TENURED, site); + isolate->counters()->constructed_objects()->Increment(); + isolate->counters()->constructed_objects_runtime()->Increment(); + return result; +} + + Handle<FixedArray> JSObject::EnsureWritableFastElements( Handle<JSObject> object) { DCHECK(object->HasFastSmiOrObjectElements()); @@ -845,17 +1070,64 @@ Handle<FixedArray> JSObject::EnsureWritableFastElements( } -MaybeHandle<Object> JSProxy::GetPropertyWithHandler(Handle<JSProxy> proxy, - Handle<Object> receiver, - Handle<Name> name) { +// ES6 9.5.1 +// static +MaybeHandle<Object> JSProxy::GetPrototype(Handle<JSProxy> proxy) { Isolate* isolate = proxy->GetIsolate(); + Handle<String> trap_name = isolate->factory()->getPrototypeOf_string(); - // TODO(rossberg): adjust once there is a story for symbols vs proxies. - if (name->IsSymbol()) return isolate->factory()->undefined_value(); + STACK_CHECK(MaybeHandle<Object>()); - Handle<Object> args[] = { receiver, name }; - return CallTrap( - proxy, "get", isolate->derived_get_trap(), arraysize(args), args); + // 1. Let handler be the value of the [[ProxyHandler]] internal slot. + // 2. If handler is null, throw a TypeError exception. + // 3. Assert: Type(handler) is Object. + // 4. Let target be the value of the [[ProxyTarget]] internal slot. + if (proxy->IsRevoked()) { + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kProxyRevoked, trap_name), + Object); + } + Handle<JSReceiver> target(proxy->target(), isolate); + Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate); + + // 5. Let trap be ? GetMethod(handler, "getPrototypeOf"). + Handle<Object> trap; + ASSIGN_RETURN_ON_EXCEPTION(isolate, trap, GetMethod(handler, trap_name), + Object); + // 6. If trap is undefined, then return target.[[GetPrototypeOf]](). + if (trap->IsUndefined()) { + return Object::GetPrototype(isolate, target); + } + // 7. Let handlerProto be ? Call(trap, handler, «target»). + Handle<Object> argv[] = {target}; + Handle<Object> handler_proto; + ASSIGN_RETURN_ON_EXCEPTION( + 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())) { + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kProxyGetPrototypeOfInvalid), + Object); + } + // 9. Let extensibleTarget be ? IsExtensible(target). + Maybe<bool> is_extensible = JSReceiver::IsExtensible(target); + MAYBE_RETURN_NULL(is_extensible); + // 10. If extensibleTarget is true, return handlerProto. + if (is_extensible.FromJust()) return handler_proto; + // 11. Let targetProto be ? target.[[GetPrototypeOf]](). + Handle<Object> target_proto; + ASSIGN_RETURN_ON_EXCEPTION(isolate, target_proto, + Object::GetPrototype(isolate, target), Object); + // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError. + if (!handler_proto->SameValue(*target_proto)) { + THROW_NEW_ERROR( + isolate, + NewTypeError(MessageTemplate::kProxyGetPrototypeOfNonExtensible), + Object); + } + // 13. Return handlerProto. + return handler_proto; } @@ -992,11 +1264,6 @@ MaybeHandle<Object> Object::GetPropertyWithDefinedGetter( return MaybeHandle<Object>(); } - Debug* debug = isolate->debug(); - // Handle stepping into a getter if step into is active. - // TODO(rossberg): should this apply to getters that are function proxies? - if (debug->is_active()) debug->HandleStepIn(getter, false); - return Execution::Call(isolate, getter, receiver, 0, NULL); } @@ -1007,11 +1274,6 @@ Maybe<bool> Object::SetPropertyWithDefinedSetter(Handle<Object> receiver, ShouldThrow should_throw) { Isolate* isolate = setter->GetIsolate(); - Debug* debug = isolate->debug(); - // Handle stepping into a setter if step into is active. - // TODO(rossberg): should this apply to getters that are function proxies? - if (debug->is_active()) debug->HandleStepIn(setter, false); - Handle<Object> argv[] = { value }; RETURN_ON_EXCEPTION_VALUE(isolate, Execution::Call(isolate, setter, receiver, arraysize(argv), argv), @@ -1021,6 +1283,18 @@ 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. @@ -1034,6 +1308,9 @@ bool JSObject::AllCanRead(LookupIterator* it) { } } else if (it->state() == LookupIterator::INTERCEPTOR) { if (it->GetInterceptor()->all_can_read()) return true; + } else if (it->state() == LookupIterator::JSPROXY) { + // Stop lookupiterating. And no, AllCanNotRead. + return false; } } return false; @@ -1089,7 +1366,7 @@ Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithFailedAccessCheck( // static bool JSObject::AllCanWrite(LookupIterator* it) { - for (; it->IsFound(); it->Next()) { + for (; it->IsFound() && it->state() != LookupIterator::JSPROXY; it->Next()) { if (it->state() == LookupIterator::ACCESSOR) { Handle<Object> accessors = it->GetAccessors(); if (accessors->IsAccessorInfo()) { @@ -1160,12 +1437,13 @@ void JSObject::SetNormalizedProperty(Handle<JSObject> object, } -bool Object::HasInPrototypeChain(Isolate* isolate, Object* target) { - PrototypeIterator iter(isolate, this, PrototypeIterator::START_AT_RECEIVER); +Maybe<bool> Object::HasInPrototypeChain(Isolate* isolate, Handle<Object> object, + Handle<Object> proto) { + PrototypeIterator iter(isolate, object, PrototypeIterator::START_AT_RECEIVER); while (true) { - iter.AdvanceIgnoringProxies(); - if (iter.IsAtEnd()) return false; - if (iter.IsAtEnd(target)) return true; + if (!iter.AdvanceFollowingProxies()) return Nothing<bool>(); + if (iter.IsAtEnd()) return Just(false); + if (iter.IsAtEnd(proto)) return Just(true); } } @@ -1329,6 +1607,56 @@ bool Object::SameValueZero(Object* other) { } +MaybeHandle<Object> Object::ArraySpeciesConstructor( + Isolate* isolate, Handle<Object> original_array) { + Handle<Context> native_context = isolate->native_context(); + if (!FLAG_harmony_species) { + return Handle<Object>(native_context->array_function(), isolate); + } + Handle<Object> constructor = isolate->factory()->undefined_value(); + Maybe<bool> is_array = Object::IsArray(original_array); + MAYBE_RETURN_NULL(is_array); + if (is_array.FromJust()) { + ASSIGN_RETURN_ON_EXCEPTION( + isolate, constructor, + Object::GetProperty(original_array, + isolate->factory()->constructor_string()), + Object); + if (constructor->IsConstructor()) { + Handle<Context> constructor_context; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, constructor_context, + JSReceiver::GetFunctionRealm(Handle<JSReceiver>::cast(constructor)), + Object); + if (*constructor_context != *native_context && + *constructor == constructor_context->array_function()) { + constructor = isolate->factory()->undefined_value(); + } + } + if (constructor->IsJSReceiver()) { + ASSIGN_RETURN_ON_EXCEPTION( + isolate, constructor, + Object::GetProperty(constructor, + isolate->factory()->species_symbol()), + Object); + if (constructor->IsNull()) { + constructor = isolate->factory()->undefined_value(); + } + } + } + if (constructor->IsUndefined()) { + return Handle<Object>(native_context->array_function(), isolate); + } else { + if (!constructor->IsConstructor()) { + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kSpeciesNotConstructor), + Object); + } + return constructor; + } +} + + void Object::ShortPrint(FILE* out) { OFStream os(out); os << Brief(this); @@ -1420,6 +1748,7 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) { // Externalizing twice leaks the external resource, so it's // prohibited by the API. DCHECK(!this->IsExternalString()); + DCHECK(!resource->IsCompressible()); #ifdef ENABLE_SLOW_DCHECKS if (FLAG_enable_slow_asserts) { // Assert that the resource and the string are equivalent. @@ -1482,6 +1811,7 @@ bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) { // Externalizing twice leaks the external resource, so it's // prohibited by the API. DCHECK(!this->IsExternalString()); + DCHECK(!resource->IsCompressible()); #ifdef ENABLE_SLOW_DCHECKS if (FLAG_enable_slow_asserts) { // Assert that the resource and the string are equivalent. @@ -1619,6 +1949,22 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) { 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())); + break; + } case JS_WEAK_MAP_TYPE: { accumulator->Add("<JS WeakMap>"); break; @@ -1745,9 +2091,7 @@ MaybeHandle<JSFunction> Map::GetConstructorFunction( void Map::PrintReconfiguration(FILE* file, int modify_index, PropertyKind kind, PropertyAttributes attributes) { OFStream os(file); - os << "[reconfiguring "; - constructor_name()->PrintOn(file); - os << "] "; + os << "[reconfiguring]"; Name* name = instance_descriptors()->GetKey(modify_index); if (name->IsString()) { String::cast(name)->PrintOn(file); @@ -1772,9 +2116,7 @@ void Map::PrintGeneralization(FILE* file, HeapType* old_field_type, HeapType* new_field_type) { OFStream os(file); - os << "[generalizing "; - constructor_name()->PrintOn(file); - os << "] "; + os << "[generalizing]"; Name* name = instance_descriptors()->GetKey(modify_index); if (name->IsString()) { String::cast(name)->PrintOn(file); @@ -1806,9 +2148,7 @@ void Map::PrintGeneralization(FILE* file, void JSObject::PrintInstanceMigration(FILE* file, Map* original_map, Map* new_map) { - PrintF(file, "[migrating "); - map()->constructor_name()->PrintOn(file); - PrintF(file, "] "); + PrintF(file, "[migrating]"); DescriptorArray* o = original_map->instance_descriptors(); DescriptorArray* n = new_map->instance_descriptors(); for (int i = 0; i < original_map->NumberOfOwnDescriptors(); i++) { @@ -1877,6 +2217,10 @@ void HeapObject::HeapObjectShortPrint(std::ostream& os) { // NOLINT case BYTECODE_ARRAY_TYPE: os << "<BytecodeArray[" << BytecodeArray::cast(this)->length() << "]>"; break; + case TRANSITION_ARRAY_TYPE: + os << "<TransitionArray[" << TransitionArray::cast(this)->length() + << "]>"; + break; case FREE_SPACE_TYPE: os << "<FreeSpace[" << FreeSpace::cast(this)->size() << "]>"; break; @@ -1961,9 +2305,6 @@ void HeapObject::HeapObjectShortPrint(std::ostream& os) { // NOLINT case JS_PROXY_TYPE: os << "<JSProxy>"; break; - case JS_FUNCTION_PROXY_TYPE: - os << "<JSFunctionProxy>"; - break; case FOREIGN_TYPE: os << "<Foreign>"; break; @@ -1999,12 +2340,33 @@ void HeapObject::HeapObjectShortPrint(std::ostream& os) { // NOLINT } -void HeapObject::Iterate(ObjectVisitor* v) { - // Handle header - IteratePointer(v, kMapOffset); - // Handle object body +void HeapObject::Iterate(ObjectVisitor* v) { IterateFast<ObjectVisitor>(v); } + + +void HeapObject::IterateBody(ObjectVisitor* v) { Map* m = map(); - IterateBody(m->instance_type(), SizeFromMap(m), v); + IterateBodyFast<ObjectVisitor>(m->instance_type(), SizeFromMap(m), v); +} + + +void HeapObject::IterateBody(InstanceType type, int object_size, + ObjectVisitor* v) { + IterateBodyFast<ObjectVisitor>(type, object_size, v); +} + + +struct CallIsValidSlot { + template <typename BodyDescriptor> + static bool apply(HeapObject* obj, int offset, int) { + return BodyDescriptor::IsValidSlot(obj, offset); + } +}; + + +bool HeapObject::IsValidSlot(int offset) { + DCHECK_NE(0, offset); + return BodyDescriptorApply<CallIsValidSlot, bool>(map()->instance_type(), + this, offset, 0); } @@ -2126,7 +2488,7 @@ void Simd128Value::CopyBits(void* destination) const { String* JSReceiver::class_name() { - if (IsJSFunction() || IsJSFunctionProxy()) { + if (IsFunction()) { return GetHeap()->Function_string(); } Object* maybe_constructor = map()->GetConstructor(); @@ -2139,31 +2501,89 @@ String* JSReceiver::class_name() { } -String* Map::constructor_name() { - if (is_prototype_map() && prototype_info()->IsPrototypeInfo()) { - PrototypeInfo* proto_info = PrototypeInfo::cast(prototype_info()); - if (proto_info->constructor_name()->IsString()) { - return String::cast(proto_info->constructor_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(); + + // If the object was instantiated simply with base == new.target, the + // constructor on the map provides the most accurate name. + // Don't provide the info for prototypes, since their constructors are + // reclaimed and replaced by Object in OptimizeAsPrototype. + if (!receiver->IsJSProxy() && receiver->map()->new_target_is_base() && + !receiver->map()->is_prototype_map()) { + Object* maybe_constructor = receiver->map()->GetConstructor(); + if (maybe_constructor->IsJSFunction()) { + JSFunction* constructor = JSFunction::cast(maybe_constructor); + String* name = String::cast(constructor->shared()->name()); + if (name->length() == 0) name = constructor->shared()->inferred_name(); + if (name->length() != 0 && + !name->Equals(isolate->heap()->Object_string())) { + return handle(name, isolate); + } } } - Object* maybe_constructor = GetConstructor(); + + if (FLAG_harmony_tostring) { + Handle<Object> maybe_tag = JSReceiver::GetDataProperty( + receiver, isolate->factory()->to_string_tag_symbol()); + if (maybe_tag->IsString()) return Handle<String>::cast(maybe_tag); + } + + PrototypeIterator iter(isolate, receiver); + if (iter.IsAtEnd()) return handle(receiver->class_name()); + Handle<JSReceiver> start = PrototypeIterator::GetCurrent<JSReceiver>(iter); + LookupIterator it(receiver, isolate->factory()->constructor_string(), start, + LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR); + Handle<Object> maybe_constructor = JSReceiver::GetDataProperty(&it); + Handle<String> result = isolate->factory()->Object_string(); if (maybe_constructor->IsJSFunction()) { - JSFunction* constructor = JSFunction::cast(maybe_constructor); + JSFunction* constructor = JSFunction::cast(*maybe_constructor); String* name = String::cast(constructor->shared()->name()); - if (name->length() > 0) return name; - String* inferred_name = constructor->shared()->inferred_name(); - if (inferred_name->length() > 0) return inferred_name; - Object* proto = prototype(); - if (proto->IsJSObject()) return JSObject::cast(proto)->constructor_name(); + if (name->length() == 0) name = constructor->shared()->inferred_name(); + if (name->length() > 0) result = handle(name, isolate); } - // TODO(rossberg): what about proxies? - // If the constructor is not present, return "Object". - return GetHeap()->Object_string(); + + return result.is_identical_to(isolate->factory()->Object_string()) + ? handle(receiver->class_name()) + : result; } -String* JSReceiver::constructor_name() { - return map()->constructor_name(); +Context* JSReceiver::GetCreationContext() { + if (IsJSBoundFunction()) { + return JSBoundFunction::cast(this)->creation_context(); + } + Object* constructor = map()->GetConstructor(); + JSFunction* function; + if (constructor->IsJSFunction()) { + function = JSFunction::cast(constructor); + } else { + // Functions have null as a constructor, + // but any JSFunction knows its context immediately. + CHECK(IsJSFunction()); + function = JSFunction::cast(this); + } + + return function->context()->native_context(); } @@ -2271,21 +2691,6 @@ void JSObject::AddSlowProperty(Handle<JSObject> object, } -Context* JSObject::GetCreationContext() { - Object* constructor = this->map()->GetConstructor(); - JSFunction* function; - if (!constructor->IsJSFunction()) { - // Functions have null as a constructor, - // but any JSFunction knows its context immediately. - function = JSFunction::cast(this); - } else { - function = JSFunction::cast(constructor); - } - - return function->context()->native_context(); -} - - MaybeHandle<Object> JSObject::EnqueueChangeRecord(Handle<JSObject> object, const char* type_str, Handle<Name> name, @@ -2355,9 +2760,10 @@ bool Map::InstancesNeedRewriting(Map* target, int target_number_of_fields, } -static void UpdatePrototypeUserRegistration(Handle<Map> old_map, - Handle<Map> new_map, - Isolate* isolate) { +// static +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()); @@ -2724,25 +3130,13 @@ static inline bool EqualImmutableValues(Object* obj1, Object* obj2) { } -// Invalidates a transition target at |key|, and installs |new_descriptors| over -// the current instance_descriptors to ensure proper sharing of descriptor -// arrays. -// Returns true if the transition target at given key was deprecated. -bool Map::DeprecateTarget(PropertyKind kind, Name* key, - PropertyAttributes attributes, - DescriptorArray* new_descriptors, - LayoutDescriptor* new_layout_descriptor) { - bool transition_target_deprecated = false; - Map* maybe_transition = - TransitionArray::SearchTransition(this, kind, key, attributes); - if (maybe_transition != NULL) { - maybe_transition->DeprecateTransitionTree(); - transition_target_deprecated = true; - } - +// Installs |new_descriptors| over the current instance_descriptors to ensure +// proper sharing of descriptor arrays. +void Map::ReplaceDescriptors(DescriptorArray* new_descriptors, + LayoutDescriptor* new_layout_descriptor) { // Don't overwrite the empty descriptor array or initial map's descriptors. if (NumberOfOwnDescriptors() == 0 || GetBackPointer()->IsUndefined()) { - return transition_target_deprecated; + return; } DescriptorArray* to_replace = instance_descriptors(); @@ -2756,7 +3150,6 @@ bool Map::DeprecateTarget(PropertyKind kind, Name* key, current = Map::cast(next); } set_owns_descriptors(false); - return transition_target_deprecated; } @@ -3422,9 +3815,6 @@ Handle<Map> Map::ReconfigureProperty(Handle<Map> old_map, int modify_index, int split_nof = split_map->NumberOfOwnDescriptors(); DCHECK_NE(old_nof, split_nof); - Handle<LayoutDescriptor> new_layout_descriptor = - LayoutDescriptor::New(split_map, new_descriptors, old_nof); - PropertyKind split_kind; PropertyAttributes split_attributes; if (modify_index == split_nof) { @@ -3435,14 +3825,19 @@ Handle<Map> Map::ReconfigureProperty(Handle<Map> old_map, int modify_index, split_kind = split_prop_details.kind(); split_attributes = split_prop_details.attributes(); } - bool transition_target_deprecated = split_map->DeprecateTarget( - split_kind, old_descriptors->GetKey(split_nof), split_attributes, - *new_descriptors, *new_layout_descriptor); - // If |transition_target_deprecated| is true then the transition array - // already contains entry for given descriptor. This means that the transition + // Invalidate a transition target at |key|. + Map* maybe_transition = TransitionArray::SearchTransition( + *split_map, split_kind, old_descriptors->GetKey(split_nof), + split_attributes); + if (maybe_transition != NULL) { + maybe_transition->DeprecateTransitionTree(); + } + + // If |maybe_transition| is not NULL then the transition array already + // contains entry for given descriptor. This means that the transition // could be inserted regardless of whether transitions array is full or not. - if (!transition_target_deprecated && + if (maybe_transition == NULL && !TransitionArray::CanHaveMoreTransitions(split_map)) { return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode, new_kind, new_attributes, @@ -3473,13 +3868,16 @@ Handle<Map> Map::ReconfigureProperty(Handle<Map> old_map, int modify_index, *old_field_type, *new_field_type); } - // Add missing transitions. - Handle<Map> new_map = split_map; - for (int i = split_nof; i < old_nof; ++i) { - new_map = CopyInstallDescriptors(new_map, i, new_descriptors, - new_layout_descriptor); - } - new_map->set_owns_descriptors(true); + Handle<LayoutDescriptor> new_layout_descriptor = + LayoutDescriptor::New(split_map, new_descriptors, old_nof); + + Handle<Map> new_map = + AddMissingTransitions(split_map, new_descriptors, new_layout_descriptor); + + // Deprecated part of the transition tree is no longer reachable, so replace + // current instance descriptors in the "survived" part of the tree with + // the new descriptors to maintain descriptors sharing invariant. + split_map->ReplaceDescriptors(*new_descriptors, *new_layout_descriptor); return new_map; } @@ -3622,6 +4020,7 @@ Maybe<bool> JSObject::SetPropertyWithInterceptor(LookupIterator* it, result = args.Call(setter, index, v8::Utils::ToLocal(value)); } else { Handle<Name> name = it->name(); + DCHECK(!name->IsPrivate()); if (name->IsSymbol() && !interceptor->can_intercept_symbols()) { return Just(false); @@ -3643,6 +4042,8 @@ Maybe<bool> JSObject::SetPropertyWithInterceptor(LookupIterator* it, result_internal->VerifyApiCallResultType(); #endif return Just(true); + // TODO(neis): In the future, we may want to actually return the interceptor's + // result, which then should be a boolean. } @@ -3684,21 +4085,8 @@ Maybe<bool> Object::SetPropertyInternal(LookupIterator* it, should_throw); case LookupIterator::JSPROXY: - if (it->HolderIsReceiverOrHiddenPrototype()) { - return JSProxy::SetPropertyWithHandler( - it->GetHolder<JSProxy>(), it->GetReceiver(), it->GetName(), value, - should_throw); - } else { - // TODO(verwaest): Use the MaybeHandle to indicate result. - bool has_result = false; - Maybe<bool> maybe_result = - JSProxy::SetPropertyViaPrototypesWithHandler( - it->GetHolder<JSProxy>(), it->GetReceiver(), it->GetName(), - value, should_throw, &has_result); - if (has_result) return maybe_result; - done = true; - } - break; + return JSProxy::SetProperty(it->GetHolder<JSProxy>(), it->GetName(), + value, it->GetReceiver(), language_mode); case LookupIterator::INTERCEPTOR: if (it->HolderIsReceiverOrHiddenPrototype()) { @@ -3767,12 +4155,16 @@ Maybe<bool> Object::SetPropertyInternal(LookupIterator* it, Maybe<bool> Object::SetProperty(LookupIterator* it, Handle<Object> value, LanguageMode language_mode, StoreFromKeyed store_mode) { + ShouldThrow should_throw = + is_sloppy(language_mode) ? DONT_THROW : THROW_ON_ERROR; + if (it->GetReceiver()->IsJSProxy() && it->GetName()->IsPrivate()) { + RETURN_FAILURE(it->isolate(), should_throw, + NewTypeError(MessageTemplate::kProxyPrivate)); + } bool found = false; Maybe<bool> result = SetPropertyInternal(it, value, language_mode, store_mode, &found); if (found) return result; - ShouldThrow should_throw = - is_sloppy(language_mode) ? DONT_THROW : THROW_ON_ERROR; return AddDataProperty(it, value, NONE, should_throw, store_mode); } @@ -3782,6 +4174,11 @@ Maybe<bool> Object::SetSuperProperty(LookupIterator* it, Handle<Object> value, StoreFromKeyed store_mode) { ShouldThrow should_throw = is_sloppy(language_mode) ? DONT_THROW : THROW_ON_ERROR; + Isolate* isolate = it->isolate(); + if (it->GetReceiver()->IsJSProxy() && it->GetName()->IsPrivate()) { + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kProxyPrivate)); + } bool found = false; Maybe<bool> result = @@ -3794,12 +4191,12 @@ Maybe<bool> Object::SetSuperProperty(LookupIterator* it, Handle<Object> value, if (!it->GetReceiver()->IsJSReceiver()) { return WriteToReadOnlyProperty(it, value, should_throw); } + Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver()); LookupIterator::Configuration c = LookupIterator::OWN; LookupIterator own_lookup = - it->IsElement() - ? LookupIterator(it->isolate(), it->GetReceiver(), it->index(), c) - : LookupIterator(it->GetReceiver(), it->name(), c); + it->IsElement() ? LookupIterator(isolate, receiver, it->index(), c) + : LookupIterator(receiver, it->name(), c); for (; own_lookup.IsFound(); own_lookup.Next()) { switch (own_lookup.state()) { @@ -3811,7 +4208,8 @@ Maybe<bool> Object::SetSuperProperty(LookupIterator* it, Handle<Object> value, break; case LookupIterator::INTEGER_INDEXED_EXOTIC: - return RedefineIncompatibleProperty(it->isolate(), it->GetName(), value, + case LookupIterator::ACCESSOR: + return RedefineIncompatibleProperty(isolate, it->GetName(), value, should_throw); case LookupIterator::DATA: { @@ -3822,18 +4220,26 @@ Maybe<bool> Object::SetSuperProperty(LookupIterator* it, Handle<Object> value, return SetDataProperty(&own_lookup, value); } - case LookupIterator::ACCESSOR: { - return RedefineIncompatibleProperty(it->isolate(), it->GetName(), value, - should_throw); - } - case LookupIterator::INTERCEPTOR: case LookupIterator::JSPROXY: { - bool found = false; - Maybe<bool> result = SetPropertyInternal( - &own_lookup, value, language_mode, store_mode, &found); - if (found) return result; - break; + PropertyDescriptor desc; + Maybe<bool> owned = + JSReceiver::GetOwnPropertyDescriptor(&own_lookup, &desc); + MAYBE_RETURN(owned, Nothing<bool>()); + if (!owned.FromJust()) { + return JSReceiver::CreateDataProperty(&own_lookup, value, + should_throw); + } + if (PropertyDescriptor::IsAccessorDescriptor(&desc) || + !desc.writable()) { + return RedefineIncompatibleProperty(isolate, it->GetName(), value, + should_throw); + } + + PropertyDescriptor value_desc; + value_desc.set_value(value); + return JSReceiver::DefineOwnProperty(isolate, receiver, it->GetName(), + &value_desc, should_throw); } case LookupIterator::NOT_FOUND: @@ -3913,8 +4319,8 @@ Maybe<bool> Object::RedefineIncompatibleProperty(Isolate* isolate, Maybe<bool> Object::SetDataProperty(LookupIterator* it, Handle<Object> value) { - // Proxies are handled on the WithHandler path. Other non-JSObjects cannot - // have own properties. + // Proxies are handled elsewhere. Other non-JSObjects cannot have own + // properties. Handle<JSObject> receiver = Handle<JSObject>::cast(it->GetReceiver()); // Store on the holder which may be hidden behind the receiver. @@ -4372,19 +4778,16 @@ Handle<Map> Map::TransitionElementsTo(Handle<Map> map, DCHECK_EQ(FAST_SLOPPY_ARGUMENTS_ELEMENTS, to_kind); return handle(native_context->fast_aliased_arguments_map()); } - } else { - Object* maybe_array_maps = map->is_strong() - ? native_context->js_array_strong_maps() - : native_context->js_array_maps(); + } else if (IsFastElementsKind(from_kind) && IsFastElementsKind(to_kind)) { // Reuse map transitions for JSArrays. - if (maybe_array_maps->IsFixedArray()) { - DisallowHeapAllocation no_gc; - FixedArray* array_maps = FixedArray::cast(maybe_array_maps); - if (array_maps->get(from_kind) == *map) { - Object* maybe_transitioned_map = array_maps->get(to_kind); - if (maybe_transitioned_map->IsMap()) { - return handle(Map::cast(maybe_transitioned_map)); - } + DisallowHeapAllocation no_gc; + Strength strength = map->is_strong() ? Strength::STRONG : Strength::WEAK; + if (native_context->get(Context::ArrayMapIndex(from_kind, strength)) == + *map) { + Object* maybe_transitioned_map = + native_context->get(Context::ArrayMapIndex(to_kind, strength)); + if (maybe_transitioned_map->IsMap()) { + return handle(Map::cast(maybe_transitioned_map), isolate); } } } @@ -4433,281 +4836,291 @@ Handle<Map> JSObject::GetElementsTransitionMap(Handle<JSObject> object, } -Maybe<bool> JSProxy::HasPropertyWithHandler(Handle<JSProxy> proxy, - Handle<Name> name) { +void JSProxy::Revoke(Handle<JSProxy> proxy) { Isolate* isolate = proxy->GetIsolate(); + if (!proxy->IsRevoked()) proxy->set_handler(isolate->heap()->null_value()); + DCHECK(proxy->IsRevoked()); +} - // TODO(rossberg): adjust once there is a story for symbols vs proxies. - if (name->IsSymbol()) return Just(false); - Handle<Object> args[] = { name }; - Handle<Object> result; +Maybe<bool> JSProxy::HasProperty(Isolate* isolate, Handle<JSProxy> proxy, + Handle<Name> name) { + DCHECK(!name->IsPrivate()); + STACK_CHECK(Nothing<bool>()); + // 1. (Assert) + // 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. + // 4. Assert: Type(handler) is Object. + if (proxy->IsRevoked()) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyRevoked, isolate->factory()->has_string())); + return Nothing<bool>(); + } + // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. + Handle<JSReceiver> target(proxy->target(), isolate); + // 6. Let trap be ? GetMethod(handler, "has"). + Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( - isolate, result, CallTrap(proxy, "has", isolate->derived_has_trap(), - arraysize(args), args), + isolate, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler), + isolate->factory()->has_string()), Nothing<bool>()); - - return Just(result->BooleanValue()); + // 7. If trap is undefined, then + if (trap->IsUndefined()) { + // 7a. Return target.[[HasProperty]](P). + return JSReceiver::HasProperty(target, name); + } + // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «target, P»)). + Handle<Object> trap_result_obj; + Handle<Object> args[] = {target, name}; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, trap_result_obj, + Execution::Call(isolate, trap, handler, arraysize(args), args), + Nothing<bool>()); + bool boolean_trap_result = trap_result_obj->BooleanValue(); + // 9. If booleanTrapResult is false, then: + if (!boolean_trap_result) { + // 9a. Let targetDesc be ? target.[[GetOwnProperty]](P). + PropertyDescriptor target_desc; + Maybe<bool> target_found = JSReceiver::GetOwnPropertyDescriptor( + isolate, target, name, &target_desc); + MAYBE_RETURN(target_found, Nothing<bool>()); + // 9b. If targetDesc is not undefined, then: + if (target_found.FromJust()) { + // 9b i. If targetDesc.[[Configurable]] is false, throw a TypeError + // exception. + if (!target_desc.configurable()) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyHasNonConfigurable, name)); + return Nothing<bool>(); + } + // 9b ii. Let extensibleTarget be ? IsExtensible(target). + Maybe<bool> extensible_target = JSReceiver::IsExtensible(target); + MAYBE_RETURN(extensible_target, Nothing<bool>()); + // 9b iii. If extensibleTarget is false, throw a TypeError exception. + if (!extensible_target.FromJust()) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyHasNonExtensible, name)); + return Nothing<bool>(); + } + } + } + // 10. Return booleanTrapResult. + return Just(boolean_trap_result); } -Maybe<bool> JSProxy::SetPropertyWithHandler(Handle<JSProxy> proxy, - Handle<Object> receiver, - Handle<Name> name, - Handle<Object> value, - ShouldThrow should_throw) { +Maybe<bool> JSProxy::SetProperty(Handle<JSProxy> proxy, Handle<Name> name, + Handle<Object> value, Handle<Object> receiver, + LanguageMode language_mode) { + DCHECK(!name->IsPrivate()); Isolate* isolate = proxy->GetIsolate(); + STACK_CHECK(Nothing<bool>()); + Factory* factory = isolate->factory(); + Handle<String> trap_name = factory->set_string(); + ShouldThrow should_throw = + is_sloppy(language_mode) ? DONT_THROW : THROW_ON_ERROR; - // TODO(rossberg): adjust once there is a story for symbols vs proxies. - if (name->IsSymbol()) return Just(true); + if (proxy->IsRevoked()) { + isolate->Throw( + *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name)); + return Nothing<bool>(); + } + Handle<JSReceiver> target(proxy->target(), isolate); + Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate); - Handle<Object> args[] = { receiver, name, value }; - RETURN_ON_EXCEPTION_VALUE(isolate, - CallTrap(proxy, "set", isolate->derived_set_trap(), - arraysize(args), args), - Nothing<bool>()); + Handle<Object> trap; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>()); + if (trap->IsUndefined()) { + LookupIterator it = + LookupIterator::PropertyOrElement(isolate, receiver, name, target); + return Object::SetSuperProperty(&it, value, language_mode, + Object::MAY_BE_STORE_FROM_KEYED); + } + Handle<Object> trap_result; + Handle<Object> args[] = {target, name, value, receiver}; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, trap_result, + Execution::Call(isolate, trap, handler, arraysize(args), args), + Nothing<bool>()); + if (!trap_result->BooleanValue()) { + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor, + trap_name, name)); + } + + // Enforce the invariant. + PropertyDescriptor target_desc; + Maybe<bool> owned = + JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc); + MAYBE_RETURN(owned, Nothing<bool>()); + if (owned.FromJust()) { + bool inconsistent = PropertyDescriptor::IsDataDescriptor(&target_desc) && + !target_desc.configurable() && + !target_desc.writable() && + !value->SameValue(*target_desc.value()); + if (inconsistent) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxySetFrozenData, name)); + return Nothing<bool>(); + } + inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) && + !target_desc.configurable() && + target_desc.set()->IsUndefined(); + if (inconsistent) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxySetFrozenAccessor, name)); + return Nothing<bool>(); + } + } return Just(true); - // TODO(neis): This needs to be made spec-conformant by looking at the - // trap's result. } -Maybe<bool> JSProxy::SetPropertyViaPrototypesWithHandler( - Handle<JSProxy> proxy, Handle<Object> receiver, Handle<Name> name, - Handle<Object> value, ShouldThrow should_throw, bool* done) { +Maybe<bool> JSProxy::DeletePropertyOrElement(Handle<JSProxy> proxy, + Handle<Name> name, + LanguageMode language_mode) { + DCHECK(!name->IsPrivate()); + ShouldThrow should_throw = + is_sloppy(language_mode) ? DONT_THROW : THROW_ON_ERROR; Isolate* isolate = proxy->GetIsolate(); - Handle<Object> handler(proxy->handler(), isolate); // Trap might morph proxy. + STACK_CHECK(Nothing<bool>()); + Factory* factory = isolate->factory(); + Handle<String> trap_name = factory->deleteProperty_string(); - // TODO(rossberg): adjust once there is a story for symbols vs proxies. - if (name->IsSymbol()) { - *done = false; // Return value will be ignored. + if (proxy->IsRevoked()) { + isolate->Throw( + *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name)); return Nothing<bool>(); } + Handle<JSReceiver> target(proxy->target(), isolate); + Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate); - *done = true; // except where redefined... - Handle<Object> args[] = { name }; - Handle<Object> result; + Handle<Object> trap; ASSIGN_RETURN_ON_EXCEPTION_VALUE( - isolate, result, CallTrap(proxy, "getPropertyDescriptor", - Handle<Object>(), arraysize(args), args), - Nothing<bool>()); - - if (result->IsUndefined()) { - *done = false; // Return value will be ignored. - return Nothing<bool>(); + isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>()); + if (trap->IsUndefined()) { + return JSReceiver::DeletePropertyOrElement(target, name, language_mode); } - // Emulate [[GetProperty]] semantics for proxies. - Handle<Object> argv[] = { result }; - Handle<Object> desc; + Handle<Object> trap_result; + Handle<Object> args[] = {target, name}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( - isolate, desc, - Execution::Call(isolate, isolate->to_complete_property_descriptor(), - result, arraysize(argv), argv), + isolate, trap_result, + Execution::Call(isolate, trap, handler, arraysize(args), args), Nothing<bool>()); - - // [[GetProperty]] requires to check that all properties are configurable. - Handle<String> configurable_name = - isolate->factory()->InternalizeOneByteString( - STATIC_CHAR_VECTOR("configurable_")); - Handle<Object> configurable = - Object::GetProperty(desc, configurable_name).ToHandleChecked(); - DCHECK(configurable->IsBoolean()); - if (configurable->IsFalse()) { - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kProxyPropNotConfigurable, handler, name, - isolate->factory()->NewStringFromAsciiChecked( - "getPropertyDescriptor"))); + if (!trap_result->BooleanValue()) { + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor, + trap_name, name)); + } + + // Enforce the invariant. + PropertyDescriptor target_desc; + Maybe<bool> owned = + JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc); + MAYBE_RETURN(owned, Nothing<bool>()); + if (owned.FromJust() && !target_desc.configurable()) { + isolate->Throw(*factory->NewTypeError( + MessageTemplate::kProxyDeletePropertyNonConfigurable, name)); return Nothing<bool>(); } - DCHECK(configurable->IsTrue()); - - // Check for DataDescriptor. - Handle<String> hasWritable_name = - isolate->factory()->InternalizeOneByteString( - STATIC_CHAR_VECTOR("hasWritable_")); - Handle<Object> hasWritable = - Object::GetProperty(desc, hasWritable_name).ToHandleChecked(); - DCHECK(hasWritable->IsBoolean()); - if (hasWritable->IsTrue()) { - Handle<String> writable_name = isolate->factory()->InternalizeOneByteString( - STATIC_CHAR_VECTOR("writable_")); - Handle<Object> writable = - Object::GetProperty(desc, writable_name).ToHandleChecked(); - DCHECK(writable->IsBoolean()); - *done = writable->IsFalse(); - if (!*done) return Nothing<bool>(); // Return value will be ignored. - return WriteToReadOnlyProperty(isolate, receiver, name, value, - should_throw); - } - - // We have an AccessorDescriptor. - Handle<String> set_name = - isolate->factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("set_")); - Handle<Object> setter = Object::GetProperty(desc, set_name).ToHandleChecked(); - if (!setter->IsUndefined()) { - // TODO(rossberg): nicer would be to cast to some JSCallable here... - return SetPropertyWithDefinedSetter( - receiver, Handle<JSReceiver>::cast(setter), value, should_throw); - } - - RETURN_FAILURE( - isolate, should_throw, - NewTypeError(MessageTemplate::kNoSetterInCallback, name, proxy)); + return Just(true); } -MaybeHandle<Object> JSProxy::DeletePropertyWithHandler( - Handle<JSProxy> proxy, Handle<Name> name, LanguageMode language_mode) { - Isolate* isolate = proxy->GetIsolate(); +// static +MaybeHandle<JSProxy> JSProxy::New(Isolate* isolate, Handle<Object> target, + Handle<Object> handler) { + if (!target->IsJSReceiver()) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyNonObject), + JSProxy); + } + if (target->IsJSProxy() && JSProxy::cast(*target)->IsRevoked()) { + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kProxyHandlerOrTargetRevoked), + JSProxy); + } + if (!handler->IsJSReceiver()) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kProxyNonObject), + JSProxy); + } + if (handler->IsJSProxy() && JSProxy::cast(*handler)->IsRevoked()) { + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kProxyHandlerOrTargetRevoked), + JSProxy); + } + return isolate->factory()->NewJSProxy(Handle<JSReceiver>::cast(target), + Handle<JSReceiver>::cast(handler)); +} - // TODO(rossberg): adjust once there is a story for symbols vs proxies. - if (name->IsSymbol()) return isolate->factory()->false_value(); - Handle<Object> args[] = { name }; - Handle<Object> result; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, result, - CallTrap(proxy, - "delete", - Handle<Object>(), - arraysize(args), - args), - Object); - - bool result_bool = result->BooleanValue(); - if (is_strict(language_mode) && !result_bool) { - Handle<Object> handler(proxy->handler(), isolate); - THROW_NEW_ERROR( - isolate, - NewTypeError(MessageTemplate::kProxyHandlerDeleteFailed, handler), - Object); +// static +MaybeHandle<Context> JSProxy::GetFunctionRealm(Handle<JSProxy> proxy) { + DCHECK(proxy->map()->is_constructor()); + if (proxy->IsRevoked()) { + THROW_NEW_ERROR(proxy->GetIsolate(), + NewTypeError(MessageTemplate::kProxyRevoked), Context); } - return isolate->factory()->ToBoolean(result_bool); + Handle<JSReceiver> target(JSReceiver::cast(proxy->target())); + return JSReceiver::GetFunctionRealm(target); } -Maybe<PropertyAttributes> JSProxy::GetPropertyAttributesWithHandler( - Handle<JSProxy> proxy, Handle<Object> receiver, Handle<Name> name) { - Isolate* isolate = proxy->GetIsolate(); - HandleScope scope(isolate); +// static +MaybeHandle<Context> JSBoundFunction::GetFunctionRealm( + Handle<JSBoundFunction> function) { + DCHECK(function->map()->is_constructor()); + return JSReceiver::GetFunctionRealm( + handle(function->bound_target_function())); +} - // TODO(rossberg): adjust once there is a story for symbols vs proxies. - if (name->IsSymbol()) return Just(ABSENT); - Handle<Object> args[] = { name }; - Handle<Object> result; - ASSIGN_RETURN_ON_EXCEPTION_VALUE( - isolate, result, proxy->CallTrap(proxy, "getPropertyDescriptor", - Handle<Object>(), arraysize(args), args), - Nothing<PropertyAttributes>()); +// static +Handle<Context> JSFunction::GetFunctionRealm(Handle<JSFunction> function) { + DCHECK(function->map()->is_constructor()); + return handle(function->context()->native_context()); +} - if (result->IsUndefined()) return Just(ABSENT); - Handle<Object> argv[] = { result }; - Handle<Object> desc; - ASSIGN_RETURN_ON_EXCEPTION_VALUE( - isolate, desc, - Execution::Call(isolate, isolate->to_complete_property_descriptor(), - result, arraysize(argv), argv), - Nothing<PropertyAttributes>()); - - // Convert result to PropertyAttributes. - Handle<String> enum_n = isolate->factory()->InternalizeOneByteString( - STATIC_CHAR_VECTOR("enumerable_")); - Handle<Object> enumerable; - ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, enumerable, - Object::GetProperty(desc, enum_n), - Nothing<PropertyAttributes>()); - Handle<String> conf_n = isolate->factory()->InternalizeOneByteString( - STATIC_CHAR_VECTOR("configurable_")); - Handle<Object> configurable; - ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, configurable, - Object::GetProperty(desc, conf_n), - Nothing<PropertyAttributes>()); - Handle<String> writ_n = isolate->factory()->InternalizeOneByteString( - STATIC_CHAR_VECTOR("writable_")); - Handle<Object> writable; - ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, writable, - Object::GetProperty(desc, writ_n), - Nothing<PropertyAttributes>()); - if (!writable->BooleanValue()) { - Handle<String> set_n = isolate->factory()->InternalizeOneByteString( - STATIC_CHAR_VECTOR("set_")); - Handle<Object> setter; - ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, setter, - Object::GetProperty(desc, set_n), - Nothing<PropertyAttributes>()); - writable = isolate->factory()->ToBoolean(!setter->IsUndefined()); - } - - if (configurable->IsFalse()) { - Handle<Object> handler(proxy->handler(), isolate); - Handle<String> trap = isolate->factory()->InternalizeOneByteString( - STATIC_CHAR_VECTOR("getPropertyDescriptor")); - Handle<Object> error = isolate->factory()->NewTypeError( - MessageTemplate::kProxyPropNotConfigurable, handler, name, trap); - isolate->Throw(*error); - return Nothing<PropertyAttributes>(); - } - - int attributes = NONE; - if (!enumerable->BooleanValue()) attributes |= DONT_ENUM; - if (!configurable->BooleanValue()) attributes |= DONT_DELETE; - if (!writable->BooleanValue()) attributes |= READ_ONLY; - return Just(static_cast<PropertyAttributes>(attributes)); -} - - -void JSProxy::Fix(Handle<JSProxy> proxy) { - Isolate* isolate = proxy->GetIsolate(); +// static +MaybeHandle<Context> JSObject::GetFunctionRealm(Handle<JSObject> object) { + DCHECK(object->map()->is_constructor()); + DCHECK(!object->IsJSFunction()); + return handle(object->GetCreationContext()); +} - // Save identity hash. - Handle<Object> hash(proxy->GetIdentityHash(), isolate); - if (proxy->IsJSFunctionProxy()) { - isolate->factory()->BecomeJSFunction(proxy); - // Code will be set on the JavaScript side. - } else { - isolate->factory()->BecomeJSObject(proxy); +// static +MaybeHandle<Context> JSReceiver::GetFunctionRealm(Handle<JSReceiver> receiver) { + if (receiver->IsJSProxy()) { + return JSProxy::GetFunctionRealm(Handle<JSProxy>::cast(receiver)); } - DCHECK(proxy->IsJSObject()); - // Inherit identity, if it was present. - if (hash->IsSmi()) { - JSObject::SetIdentityHash(Handle<JSObject>::cast(proxy), - Handle<Smi>::cast(hash)); + if (receiver->IsJSFunction()) { + return JSFunction::GetFunctionRealm(Handle<JSFunction>::cast(receiver)); } -} + if (receiver->IsJSBoundFunction()) { + return JSBoundFunction::GetFunctionRealm( + Handle<JSBoundFunction>::cast(receiver)); + } -MaybeHandle<Object> JSProxy::CallTrap(Handle<JSProxy> proxy, - const char* name, - Handle<Object> derived, - int argc, - Handle<Object> argv[]) { - Isolate* isolate = proxy->GetIsolate(); - Handle<Object> handler(proxy->handler(), isolate); + return JSObject::GetFunctionRealm(Handle<JSObject>::cast(receiver)); +} - Handle<String> trap_name = isolate->factory()->InternalizeUtf8String(name); - Handle<Object> trap; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, trap, - Object::GetPropertyOrElement(handler, trap_name), - Object); - if (trap->IsUndefined()) { - if (derived.is_null()) { - THROW_NEW_ERROR(isolate, - NewTypeError(MessageTemplate::kProxyHandlerTrapMissing, - handler, trap_name), - Object); - } - trap = Handle<Object>(derived); - } - - return Execution::Call(isolate, trap, handler, argc, argv); +Maybe<PropertyAttributes> JSProxy::GetPropertyAttributes(LookupIterator* it) { + Isolate* isolate = it->isolate(); + HandleScope scope(isolate); + PropertyDescriptor desc; + Maybe<bool> found = JSProxy::GetOwnPropertyDescriptor( + isolate, it->GetHolder<JSProxy>(), it->GetName(), &desc); + MAYBE_RETURN(found, Nothing<PropertyAttributes>()); + if (!found.FromJust()) return Just(ABSENT); + return Just(desc.ToAttributes()); } @@ -4963,28 +5376,6 @@ MaybeHandle<Object> JSObject::DefinePropertyOrElementIgnoreAttributes( } -Maybe<bool> JSObject::CreateDataProperty(LookupIterator* it, - Handle<Object> value) { - DCHECK(it->GetReceiver()->IsJSObject()); - Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(it); - if (maybe.IsNothing()) return Nothing<bool>(); - - if (it->IsFound()) { - if (!it->IsConfigurable()) return Just(false); - } else { - if (!JSObject::IsExtensible(Handle<JSObject>::cast(it->GetReceiver()))) - return Just(false); - } - - RETURN_ON_EXCEPTION_VALUE( - it->isolate(), - DefineOwnPropertyIgnoreAttributes(it, value, NONE, DONT_FORCE_FIELD), - Nothing<bool>()); - - return Just(true); -} - - Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithInterceptor( LookupIterator* it) { Isolate* isolate = it->isolate(); @@ -5012,6 +5403,7 @@ Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithInterceptor( result = args.Call(query, index); } else { Handle<Name> name = it->name(); + DCHECK(!name->IsPrivate()); v8::GenericNamedPropertyQueryCallback query = v8::ToCData<v8::GenericNamedPropertyQueryCallback>( interceptor->query()); @@ -5037,7 +5429,7 @@ Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithInterceptor( result = args.Call(getter, index); } else { Handle<Name> name = it->name(); - + DCHECK(!name->IsPrivate()); v8::GenericNamedPropertyGetterCallback getter = v8::ToCData<v8::GenericNamedPropertyGetterCallback>( interceptor->getter()); @@ -5061,8 +5453,7 @@ Maybe<PropertyAttributes> JSReceiver::GetPropertyAttributes( case LookupIterator::TRANSITION: UNREACHABLE(); case LookupIterator::JSPROXY: - return JSProxy::GetPropertyAttributesWithHandler( - it->GetHolder<JSProxy>(), it->GetReceiver(), it->GetName()); + return JSProxy::GetPropertyAttributes(it); case LookupIterator::INTERCEPTOR: { Maybe<PropertyAttributes> result = JSObject::GetPropertyAttributesWithInterceptor(it); @@ -5451,7 +5842,7 @@ void JSObject::RequireSlowElements(SeededNumberDictionary* dictionary) { dictionary->set_requires_slow_elements(); // TODO(verwaest): Remove this hack. if (map()->is_prototype_map()) { - GetHeap()->ClearAllKeyedStoreICs(); + TypeFeedbackVector::ClearAllKeyedStoreICs(GetIsolate()); } } @@ -5582,7 +5973,6 @@ Handle<Smi> JSObject::GetOrCreateIdentityHash(Handle<JSObject> object) { if (object->IsJSGlobalProxy()) { return GetOrCreateIdentityHashHelper(Handle<JSGlobalProxy>::cast(object)); } - Isolate* isolate = object->GetIsolate(); Handle<Object> maybe_hash(object->GetIdentityHash(), isolate); @@ -5757,8 +6147,7 @@ Handle<Object> JSObject::SetHiddenPropertiesHashTable(Handle<JSObject> object, } -MaybeHandle<Object> JSObject::DeletePropertyWithInterceptor( - LookupIterator* it) { +Maybe<bool> JSObject::DeletePropertyWithInterceptor(LookupIterator* it) { Isolate* isolate = it->isolate(); // Make sure that the top context does not change when doing callbacks or // interceptor calls. @@ -5766,7 +6155,7 @@ MaybeHandle<Object> JSObject::DeletePropertyWithInterceptor( DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); Handle<InterceptorInfo> interceptor(it->GetInterceptor()); - if (interceptor->deleter()->IsUndefined()) return MaybeHandle<Object>(); + if (interceptor->deleter()->IsUndefined()) return Nothing<bool>(); Handle<JSObject> holder = it->GetHolder<JSObject>(); @@ -5781,9 +6170,10 @@ MaybeHandle<Object> JSObject::DeletePropertyWithInterceptor( ApiIndexedPropertyAccess("interceptor-indexed-delete", *holder, index)); result = args.Call(deleter, index); } else if (it->name()->IsSymbol() && !interceptor->can_intercept_symbols()) { - return MaybeHandle<Object>(); + return Nothing<bool>(); } else { Handle<Name> name = it->name(); + DCHECK(!name->IsPrivate()); v8::GenericNamedPropertyDeleterCallback deleter = v8::ToCData<v8::GenericNamedPropertyDeleterCallback>( interceptor->deleter()); @@ -5792,25 +6182,26 @@ MaybeHandle<Object> JSObject::DeletePropertyWithInterceptor( result = args.Call(deleter, v8::Utils::ToLocal(name)); } - RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); - if (result.IsEmpty()) return MaybeHandle<Object>(); + RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); + if (result.IsEmpty()) return Nothing<bool>(); DCHECK(result->IsBoolean()); Handle<Object> result_internal = v8::Utils::OpenHandle(*result); result_internal->VerifyApiCallResultType(); // Rebox CustomArguments::kReturnValueOffset before returning. - return handle(*result_internal, isolate); + return Just(result_internal->BooleanValue()); } -void JSObject::DeleteNormalizedProperty(Handle<JSObject> object, - Handle<Name> name, int entry) { +void JSReceiver::DeleteNormalizedProperty(Handle<JSReceiver> object, + Handle<Name> name, int entry) { DCHECK(!object->HasFastProperties()); Isolate* isolate = object->GetIsolate(); if (object->IsJSGlobalObject()) { // If we have a global object, invalidate the cell and swap in a new one. - Handle<GlobalDictionary> dictionary(object->global_dictionary()); + Handle<GlobalDictionary> dictionary( + JSObject::cast(*object)->global_dictionary()); DCHECK_NE(GlobalDictionary::kNotFound, entry); auto cell = PropertyCell::InvalidateEntry(dictionary, entry); @@ -5830,15 +6221,23 @@ void JSObject::DeleteNormalizedProperty(Handle<JSObject> object, } -// ECMA-262, 3rd, 8.6.2.5 -MaybeHandle<Object> JSReceiver::DeleteProperty(LookupIterator* it, - LanguageMode language_mode) { +Maybe<bool> JSReceiver::DeleteProperty(LookupIterator* it, + LanguageMode language_mode) { Isolate* isolate = it->isolate(); + if (it->state() == LookupIterator::JSPROXY) { - return JSProxy::DeletePropertyWithHandler(it->GetHolder<JSProxy>(), - it->GetName(), language_mode); + return JSProxy::DeletePropertyOrElement(it->GetHolder<JSProxy>(), + it->GetName(), language_mode); } + if (it->GetReceiver()->IsJSProxy()) { + if (it->state() != LookupIterator::NOT_FOUND) { + DCHECK_EQ(LookupIterator::DATA, it->state()); + DCHECK(it->GetName()->IsPrivate()); + it->Delete(); + } + return Just(true); + } Handle<JSObject> receiver = Handle<JSObject>::cast(it->GetReceiver()); bool is_observed = @@ -5856,19 +6255,20 @@ MaybeHandle<Object> JSReceiver::DeleteProperty(LookupIterator* it, case LookupIterator::ACCESS_CHECK: if (it->HasAccess()) break; isolate->ReportFailedAccessCheck(it->GetHolder<JSObject>()); - RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); - return it->factory()->false_value(); + RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); + return Just(false); case LookupIterator::INTERCEPTOR: { - MaybeHandle<Object> maybe_result = - JSObject::DeletePropertyWithInterceptor(it); - // Delete with interceptor succeeded. Return result. - if (!maybe_result.is_null()) return maybe_result; + Maybe<bool> result = JSObject::DeletePropertyWithInterceptor(it); // An exception was thrown in the interceptor. Propagate. - if (isolate->has_pending_exception()) return maybe_result; + if (isolate->has_pending_exception()) return Nothing<bool>(); + // Delete with interceptor succeeded. Return result. + // TODO(neis): In strict mode, we should probably throw if the + // interceptor returns false. + if (result.IsJust()) return result; break; } case LookupIterator::INTEGER_INDEXED_EXOTIC: - return it->factory()->true_value(); + return Just(true); case LookupIterator::DATA: if (is_observed) { old_value = it->GetDataValue(); @@ -5882,49 +6282,50 @@ MaybeHandle<Object> JSReceiver::DeleteProperty(LookupIterator* it, receiver->map()->is_strong() ? MessageTemplate::kStrongDeleteProperty : MessageTemplate::kStrictDeleteProperty; - THROW_NEW_ERROR( - isolate, NewTypeError(templ, it->GetName(), receiver), Object); + isolate->Throw(*isolate->factory()->NewTypeError( + templ, it->GetName(), receiver)); + return Nothing<bool>(); } - return it->factory()->false_value(); + return Just(false); } it->Delete(); if (is_observed) { - RETURN_ON_EXCEPTION(isolate, - JSObject::EnqueueChangeRecord( - receiver, "delete", it->GetName(), old_value), - Object); + RETURN_ON_EXCEPTION_VALUE( + isolate, JSObject::EnqueueChangeRecord(receiver, "delete", + it->GetName(), old_value), + Nothing<bool>()); } - return it->factory()->true_value(); + return Just(true); } } } - return it->factory()->true_value(); + return Just(true); } -MaybeHandle<Object> JSReceiver::DeleteElement(Handle<JSReceiver> object, - uint32_t index, - LanguageMode language_mode) { +Maybe<bool> JSReceiver::DeleteElement(Handle<JSReceiver> object, uint32_t index, + LanguageMode language_mode) { LookupIterator it(object->GetIsolate(), object, index, LookupIterator::HIDDEN); return DeleteProperty(&it, language_mode); } -MaybeHandle<Object> JSReceiver::DeleteProperty(Handle<JSReceiver> object, - Handle<Name> name, - LanguageMode language_mode) { +Maybe<bool> JSReceiver::DeleteProperty(Handle<JSReceiver> object, + Handle<Name> name, + LanguageMode language_mode) { LookupIterator it(object, name, LookupIterator::HIDDEN); return DeleteProperty(&it, language_mode); } -MaybeHandle<Object> JSReceiver::DeletePropertyOrElement( - Handle<JSReceiver> object, Handle<Name> name, LanguageMode language_mode) { +Maybe<bool> JSReceiver::DeletePropertyOrElement(Handle<JSReceiver> object, + Handle<Name> name, + LanguageMode language_mode) { LookupIterator it = LookupIterator::PropertyOrElement( name->GetIsolate(), object, name, LookupIterator::HIDDEN); return DeleteProperty(&it, language_mode); @@ -5961,8 +6362,7 @@ Object* JSReceiver::DefineProperty(Isolate* isolate, Handle<Object> object, Handle<Object> key, Handle<Object> attributes) { // 1. If Type(O) is not Object, throw a TypeError exception. - // TODO(jkummerow): Implement Proxy support, change to "IsSpecObject". - if (!object->IsJSObject()) { + if (!object->IsJSReceiver()) { Handle<String> fun_name = isolate->factory()->InternalizeUtf8String("Object.defineProperty"); THROW_NEW_ERROR_RETURN_FAILURE( @@ -5978,11 +6378,11 @@ Object* JSReceiver::DefineProperty(Isolate* isolate, Handle<Object> object, return isolate->heap()->exception(); } // 6. Let success be DefinePropertyOrThrow(O,key, desc). - bool success = DefineOwnProperty(isolate, Handle<JSObject>::cast(object), key, - &desc, THROW_ON_ERROR); + Maybe<bool> success = DefineOwnProperty( + isolate, Handle<JSReceiver>::cast(object), key, &desc, THROW_ON_ERROR); // 7. ReturnIfAbrupt(success). - if (isolate->has_pending_exception()) return isolate->heap()->exception(); - CHECK(success == true); + MAYBE_RETURN(success, isolate->heap()->exception()); + CHECK(success.FromJust()); // 8. Return O. return *object; } @@ -5990,32 +6390,35 @@ Object* JSReceiver::DefineProperty(Isolate* isolate, Handle<Object> object, // ES6 19.1.2.3.1 // static -Object* JSReceiver::DefineProperties(Isolate* isolate, Handle<Object> object, - Handle<Object> properties) { +MaybeHandle<Object> JSReceiver::DefineProperties(Isolate* isolate, + Handle<Object> object, + Handle<Object> properties) { // 1. If Type(O) is not Object, throw a TypeError exception. - // TODO(jkummerow): Implement Proxy support, change to "IsSpecObject". - if (!object->IsJSObject()) { + if (!object->IsJSReceiver()) { Handle<String> fun_name = isolate->factory()->InternalizeUtf8String("Object.defineProperties"); - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewTypeError(MessageTemplate::kCalledOnNonObject, fun_name)); + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kCalledOnNonObject, fun_name), + Object); } // 2. Let props be ToObject(Properties). // 3. ReturnIfAbrupt(props). Handle<JSReceiver> props; if (!Object::ToObject(isolate, properties).ToHandle(&props)) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewTypeError(MessageTemplate::kUndefinedOrNullToObject)); + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kUndefinedOrNullToObject), + Object); } // 4. Let keys be props.[[OwnPropertyKeys]](). // 5. ReturnIfAbrupt(keys). Handle<FixedArray> keys; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + ASSIGN_RETURN_ON_EXCEPTION( isolate, keys, - JSReceiver::GetKeys(props, JSReceiver::OWN_ONLY, INCLUDE_SYMBOLS)); + JSReceiver::GetKeys(props, JSReceiver::OWN_ONLY, ALL_PROPERTIES), Object); // 6. Let descriptors be an empty List. int capacity = keys->length(); std::vector<PropertyDescriptor> descriptors(capacity); + size_t descriptors_index = 0; // 7. Repeat for each element nextKey of keys in List order, for (int i = 0; i < keys->length(); ++i) { Handle<Object> next_key(keys->get(i), isolate); @@ -6025,58 +6428,60 @@ Object* JSReceiver::DefineProperties(Isolate* isolate, Handle<Object> object, LookupIterator it = LookupIterator::PropertyOrElement( isolate, props, next_key, &success, LookupIterator::HIDDEN); DCHECK(success); - // TODO(jkummerow): Support JSProxies. Make sure we call the correct - // getOwnPropertyDescriptor trap, and convert the result object to a - // PropertyDescriptor. - Maybe<PropertyAttributes> maybe = JSObject::GetPropertyAttributes(&it); - if (!maybe.IsJust()) return isolate->heap()->exception(); + Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it); + if (!maybe.IsJust()) return MaybeHandle<Object>(); PropertyAttributes attrs = maybe.FromJust(); // 7c. If propDesc is not undefined and propDesc.[[Enumerable]] is true: if (attrs == ABSENT) continue; - // GetKeys() only returns enumerable keys. - DCHECK((attrs & DONT_ENUM) == 0); + if (attrs & DONT_ENUM) continue; // 7c i. Let descObj be Get(props, nextKey). // 7c ii. ReturnIfAbrupt(descObj). Handle<Object> desc_obj; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, desc_obj, - JSObject::GetProperty(&it)); + ASSIGN_RETURN_ON_EXCEPTION(isolate, desc_obj, Object::GetProperty(&it), + Object); // 7c iii. Let desc be ToPropertyDescriptor(descObj). - success = PropertyDescriptor::ToPropertyDescriptor(isolate, desc_obj, - &descriptors[i]); + success = PropertyDescriptor::ToPropertyDescriptor( + isolate, desc_obj, &descriptors[descriptors_index]); // 7c iv. ReturnIfAbrupt(desc). - if (!success) return isolate->heap()->exception(); + if (!success) return MaybeHandle<Object>(); // 7c v. Append the pair (a two element List) consisting of nextKey and // desc to the end of descriptors. - descriptors[i].set_name(next_key); + descriptors[descriptors_index].set_name(next_key); + descriptors_index++; } // 8. For each pair from descriptors in list order, - for (size_t i = 0; i < descriptors.size(); ++i) { + for (size_t i = 0; i < descriptors_index; ++i) { PropertyDescriptor* desc = &descriptors[i]; // 8a. Let P be the first element of pair. // 8b. Let desc be the second element of pair. // 8c. Let status be DefinePropertyOrThrow(O, P, desc). - bool status = DefineOwnProperty(isolate, Handle<JSObject>::cast(object), - desc->name(), desc, THROW_ON_ERROR); + Maybe<bool> status = + DefineOwnProperty(isolate, Handle<JSReceiver>::cast(object), + desc->name(), desc, THROW_ON_ERROR); // 8d. ReturnIfAbrupt(status). - if (isolate->has_pending_exception()) return isolate->heap()->exception(); - CHECK(status == true); + if (!status.IsJust()) return MaybeHandle<Object>(); + CHECK(status.FromJust()); } // 9. Return o. - return *object; + return object; } // static -bool JSReceiver::DefineOwnProperty(Isolate* isolate, Handle<JSReceiver> object, - Handle<Object> key, PropertyDescriptor* desc, - ShouldThrow should_throw) { +Maybe<bool> JSReceiver::DefineOwnProperty(Isolate* isolate, + Handle<JSReceiver> object, + Handle<Object> key, + PropertyDescriptor* desc, + ShouldThrow should_throw) { if (object->IsJSArray()) { return JSArray::DefineOwnProperty(isolate, Handle<JSArray>::cast(object), key, desc, should_throw); } + if (object->IsJSProxy()) { + return JSProxy::DefineOwnProperty(isolate, Handle<JSProxy>::cast(object), + key, desc, should_throw); + } // TODO(jkummerow): Support Modules (ES6 9.4.6.6) - // TODO(jkummerow): Support Proxies (ES6 9.5.6) - if (!object->IsJSObject()) return true; // OrdinaryDefineOwnProperty, by virtue of calling // DefineOwnPropertyIgnoreAttributes, can handle arguments (ES6 9.4.4.2) @@ -6088,11 +6493,11 @@ bool JSReceiver::DefineOwnProperty(Isolate* isolate, Handle<JSReceiver> object, // static -bool JSReceiver::OrdinaryDefineOwnProperty(Isolate* isolate, - Handle<JSObject> object, - Handle<Object> key, - PropertyDescriptor* desc, - ShouldThrow should_throw) { +Maybe<bool> JSReceiver::OrdinaryDefineOwnProperty(Isolate* isolate, + Handle<JSObject> object, + Handle<Object> key, + PropertyDescriptor* desc, + ShouldThrow should_throw) { bool success = false; DCHECK(key->IsName() || key->IsNumber()); // |key| is a PropertyKey... LookupIterator it = LookupIterator::PropertyOrElement( @@ -6103,8 +6508,8 @@ bool JSReceiver::OrdinaryDefineOwnProperty(Isolate* isolate, if (it.state() == LookupIterator::ACCESS_CHECK) { if (!it.HasAccess()) { isolate->ReportFailedAccessCheck(it.GetHolder<JSObject>()); - RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, false); - return false; + RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); + return Just(true); } it.Next(); } @@ -6115,18 +6520,15 @@ bool JSReceiver::OrdinaryDefineOwnProperty(Isolate* isolate, // ES6 9.1.6.1 // static -bool JSReceiver::OrdinaryDefineOwnProperty(LookupIterator* it, - PropertyDescriptor* desc, - ShouldThrow should_throw) { +Maybe<bool> JSReceiver::OrdinaryDefineOwnProperty(LookupIterator* it, + PropertyDescriptor* desc, + ShouldThrow should_throw) { Isolate* isolate = it->isolate(); - // == OrdinaryDefineOwnProperty (O, P, Desc) == // 1. Let current be O.[[GetOwnProperty]](P). // 2. ReturnIfAbrupt(current). PropertyDescriptor current; - if (!GetOwnPropertyDescriptor(it, ¤t) && - isolate->has_pending_exception()) { - return false; - } + MAYBE_RETURN(GetOwnPropertyDescriptor(it, ¤t), Nothing<bool>()); + // TODO(jkummerow/verwaest): It would be nice if we didn't have to reset // the iterator every time. Currently, the reasons why we need it are: // - handle interceptors correctly @@ -6136,22 +6538,47 @@ bool JSReceiver::OrdinaryDefineOwnProperty(LookupIterator* it, Handle<JSObject> object = Handle<JSObject>::cast(it->GetReceiver()); bool extensible = JSObject::IsExtensible(object); + return ValidateAndApplyPropertyDescriptor(isolate, it, extensible, desc, + ¤t, should_throw); +} + + +// ES6 9.1.6.2 +// static +Maybe<bool> JSReceiver::IsCompatiblePropertyDescriptor( + Isolate* isolate, bool extensible, PropertyDescriptor* desc, + PropertyDescriptor* current, Handle<Name> property_name, + ShouldThrow should_throw) { + // 1. Return ValidateAndApplyPropertyDescriptor(undefined, undefined, + // Extensible, Desc, Current). + return ValidateAndApplyPropertyDescriptor( + isolate, NULL, extensible, desc, current, should_throw, property_name); +} + + +// ES6 9.1.6.3 +// static +Maybe<bool> JSReceiver::ValidateAndApplyPropertyDescriptor( + Isolate* isolate, LookupIterator* it, bool extensible, + PropertyDescriptor* desc, PropertyDescriptor* current, + ShouldThrow should_throw, Handle<Name> property_name) { + // We either need a LookupIterator, or a property name. + DCHECK((it == NULL) != property_name.is_null()); + Handle<JSObject> object; + if (it != NULL) object = Handle<JSObject>::cast(it->GetReceiver()); bool desc_is_data_descriptor = PropertyDescriptor::IsDataDescriptor(desc); bool desc_is_accessor_descriptor = PropertyDescriptor::IsAccessorDescriptor(desc); bool desc_is_generic_descriptor = PropertyDescriptor::IsGenericDescriptor(desc); - - // == ValidateAndApplyPropertyDescriptor (O, P, extensible, Desc, current) == + // 1. (Assert) // 2. If current is undefined, then - if (current.is_empty()) { + if (current->is_empty()) { // 2a. If extensible is false, return false. if (!extensible) { - if (should_throw == THROW_ON_ERROR) { - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kDefineDisallowed, it->GetName())); - } - return false; + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kDefineDisallowed, + it != NULL ? it->GetName() : property_name)); } // 2c. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then: // (This is equivalent to !IsAccessorDescriptor(desc).) @@ -6163,7 +6590,7 @@ bool JSReceiver::OrdinaryDefineOwnProperty(LookupIterator* it, // [[Configurable]] attribute values are described by Desc. If the value // of an attribute field of Desc is absent, the attribute of the newly // created property is set to its default value. - if (!object->IsUndefined()) { + if (it != NULL) { if (!desc->has_writable()) desc->set_writable(false); if (!desc->has_enumerable()) desc->set_enumerable(false); if (!desc->has_configurable()) desc->set_configurable(false); @@ -6174,7 +6601,7 @@ bool JSReceiver::OrdinaryDefineOwnProperty(LookupIterator* it, MaybeHandle<Object> result = JSObject::DefineOwnPropertyIgnoreAttributes( it, value, desc->ToAttributes(), JSObject::DONT_FORCE_FIELD); - if (result.is_null()) return false; + if (result.is_null()) return Nothing<bool>(); } } else { // 2d. Else Desc must be an accessor Property Descriptor, @@ -6184,7 +6611,7 @@ bool JSReceiver::OrdinaryDefineOwnProperty(LookupIterator* it, // [[Configurable]] attribute values are described by Desc. If the value // of an attribute field of Desc is absent, the attribute of the newly // created property is set to its default value. - if (!object->IsUndefined()) { + if (it != NULL) { if (!desc->has_enumerable()) desc->set_enumerable(false); if (!desc->has_configurable()) desc->set_configurable(false); Handle<Object> getter( @@ -6197,53 +6624,50 @@ bool JSReceiver::OrdinaryDefineOwnProperty(LookupIterator* it, : Handle<Object>::cast(isolate->factory()->null_value())); MaybeHandle<Object> result = JSObject::DefineAccessor(it, getter, setter, desc->ToAttributes()); - if (result.is_null()) return false; + if (result.is_null()) return Nothing<bool>(); } } // 2e. Return true. - return true; + return Just(true); } // 3. Return true, if every field in Desc is absent. // 4. Return true, if every field in Desc also occurs in current and the // value of every field in Desc is the same value as the corresponding field // in current when compared using the SameValue algorithm. - if ((!desc->has_enumerable() || desc->enumerable() == current.enumerable()) && + if ((!desc->has_enumerable() || + desc->enumerable() == current->enumerable()) && (!desc->has_configurable() || - desc->configurable() == current.configurable()) && + desc->configurable() == current->configurable()) && (!desc->has_value() || - (current.has_value() && current.value()->SameValue(*desc->value()))) && + (current->has_value() && current->value()->SameValue(*desc->value()))) && (!desc->has_writable() || - (current.has_writable() && current.writable() == desc->writable())) && + (current->has_writable() && current->writable() == desc->writable())) && (!desc->has_get() || - (current.has_get() && current.get()->SameValue(*desc->get()))) && + (current->has_get() && current->get()->SameValue(*desc->get()))) && (!desc->has_set() || - (current.has_set() && current.set()->SameValue(*desc->set())))) { - return true; + (current->has_set() && current->set()->SameValue(*desc->set())))) { + return Just(true); } // 5. If the [[Configurable]] field of current is false, then - if (!current.configurable()) { + if (!current->configurable()) { // 5a. Return false, if the [[Configurable]] field of Desc is true. if (desc->has_configurable() && desc->configurable()) { - if (should_throw == THROW_ON_ERROR) { - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kRedefineDisallowed, it->GetName())); - } - return false; + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kRedefineDisallowed, + it != NULL ? it->GetName() : property_name)); } // 5b. Return false, if the [[Enumerable]] field of Desc is present and the // [[Enumerable]] fields of current and Desc are the Boolean negation of // each other. - if (desc->has_enumerable() && desc->enumerable() != current.enumerable()) { - if (should_throw == THROW_ON_ERROR) { - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kRedefineDisallowed, it->GetName())); - } - return false; + if (desc->has_enumerable() && desc->enumerable() != current->enumerable()) { + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kRedefineDisallowed, + it != NULL ? it->GetName() : property_name)); } } bool current_is_data_descriptor = - PropertyDescriptor::IsDataDescriptor(¤t); + PropertyDescriptor::IsDataDescriptor(current); // 6. If IsGenericDescriptor(Desc) is true, no further validation is required. if (desc_is_generic_descriptor) { // Nothing to see here. @@ -6252,12 +6676,10 @@ bool JSReceiver::OrdinaryDefineOwnProperty(LookupIterator* it, // different results, then: } else if (current_is_data_descriptor != desc_is_data_descriptor) { // 7a. Return false, if the [[Configurable]] field of current is false. - if (!current.configurable()) { - if (should_throw == THROW_ON_ERROR) { - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kRedefineDisallowed, it->GetName())); - } - return false; + if (!current->configurable()) { + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kRedefineDisallowed, + it != NULL ? it->GetName() : property_name)); } // 7b. If IsDataDescriptor(current) is true, then: if (current_is_data_descriptor) { @@ -6280,70 +6702,63 @@ bool JSReceiver::OrdinaryDefineOwnProperty(LookupIterator* it, // true, then: } else if (current_is_data_descriptor && desc_is_data_descriptor) { // 8a. If the [[Configurable]] field of current is false, then: - if (!current.configurable()) { + if (!current->configurable()) { // [Strong mode] Disallow changing writable -> readonly for // non-configurable properties. - if (current.writable() && desc->has_writable() && !desc->writable() && - object->map()->is_strong()) { - if (should_throw == THROW_ON_ERROR) { - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kStrongRedefineDisallowed, object, - it->GetName())); - } - return false; + if (it != NULL && current->writable() && desc->has_writable() && + !desc->writable() && object->map()->is_strong()) { + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kStrongRedefineDisallowed, + object, it->GetName())); } // 8a i. Return false, if the [[Writable]] field of current is false and // the [[Writable]] field of Desc is true. - if (!current.writable() && desc->has_writable() && desc->writable()) { - if (should_throw == THROW_ON_ERROR) { - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kRedefineDisallowed, it->GetName())); - } - return false; + if (!current->writable() && desc->has_writable() && desc->writable()) { + RETURN_FAILURE( + isolate, should_throw, + NewTypeError(MessageTemplate::kRedefineDisallowed, + it != NULL ? it->GetName() : property_name)); } // 8a ii. If the [[Writable]] field of current is false, then: - if (!current.writable()) { + if (!current->writable()) { // 8a ii 1. Return false, if the [[Value]] field of Desc is present and // SameValue(Desc.[[Value]], current.[[Value]]) is false. - if (desc->has_value() && !desc->value()->SameValue(*current.value())) { - if (should_throw == THROW_ON_ERROR) { - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kRedefineDisallowed, it->GetName())); - } - return false; + if (desc->has_value() && !desc->value()->SameValue(*current->value())) { + RETURN_FAILURE( + isolate, should_throw, + NewTypeError(MessageTemplate::kRedefineDisallowed, + it != NULL ? it->GetName() : property_name)); } } } } else { // 9. Else IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) // are both true, - DCHECK(PropertyDescriptor::IsAccessorDescriptor(¤t) && + DCHECK(PropertyDescriptor::IsAccessorDescriptor(current) && desc_is_accessor_descriptor); // 9a. If the [[Configurable]] field of current is false, then: - if (!current.configurable()) { + if (!current->configurable()) { // 9a i. Return false, if the [[Set]] field of Desc is present and // SameValue(Desc.[[Set]], current.[[Set]]) is false. - if (desc->has_set() && !desc->set()->SameValue(*current.set())) { - if (should_throw == THROW_ON_ERROR) { - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kRedefineDisallowed, it->GetName())); - } - return false; + if (desc->has_set() && !desc->set()->SameValue(*current->set())) { + RETURN_FAILURE( + isolate, should_throw, + NewTypeError(MessageTemplate::kRedefineDisallowed, + it != NULL ? it->GetName() : property_name)); } // 9a ii. Return false, if the [[Get]] field of Desc is present and // SameValue(Desc.[[Get]], current.[[Get]]) is false. - if (desc->has_get() && !desc->get()->SameValue(*current.get())) { - if (should_throw == THROW_ON_ERROR) { - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kRedefineDisallowed, it->GetName())); - } - return false; + if (desc->has_get() && !desc->get()->SameValue(*current->get())) { + RETURN_FAILURE( + isolate, should_throw, + NewTypeError(MessageTemplate::kRedefineDisallowed, + it != NULL ? it->GetName() : property_name)); } } } // 10. If O is not undefined, then: - if (!object->IsUndefined()) { + if (it != NULL) { // 10a. For each field of Desc that is present, set the corresponding // attribute of the property named P of object O to the value of the field. PropertyAttributes attrs = NONE; @@ -6353,14 +6768,14 @@ bool JSReceiver::OrdinaryDefineOwnProperty(LookupIterator* it, attrs | (desc->enumerable() ? NONE : DONT_ENUM)); } else { attrs = static_cast<PropertyAttributes>( - attrs | (current.enumerable() ? NONE : DONT_ENUM)); + attrs | (current->enumerable() ? NONE : DONT_ENUM)); } if (desc->has_configurable()) { attrs = static_cast<PropertyAttributes>( attrs | (desc->configurable() ? NONE : DONT_DELETE)); } else { attrs = static_cast<PropertyAttributes>( - attrs | (current.configurable() ? NONE : DONT_DELETE)); + attrs | (current->configurable() ? NONE : DONT_DELETE)); } if (desc_is_data_descriptor || (desc_is_generic_descriptor && current_is_data_descriptor)) { @@ -6369,41 +6784,85 @@ bool JSReceiver::OrdinaryDefineOwnProperty(LookupIterator* it, attrs | (desc->writable() ? NONE : READ_ONLY)); } else { attrs = static_cast<PropertyAttributes>( - attrs | (current.writable() ? NONE : READ_ONLY)); + attrs | (current->writable() ? NONE : READ_ONLY)); } Handle<Object> value( desc->has_value() ? desc->value() - : current.has_value() - ? current.value() + : current->has_value() + ? current->value() : Handle<Object>::cast( isolate->factory()->undefined_value())); MaybeHandle<Object> result = JSObject::DefineOwnPropertyIgnoreAttributes( it, value, attrs, JSObject::DONT_FORCE_FIELD); - if (result.is_null()) return false; + if (result.is_null()) return Nothing<bool>(); } else { DCHECK(desc_is_accessor_descriptor || (desc_is_generic_descriptor && - PropertyDescriptor::IsAccessorDescriptor(¤t))); + PropertyDescriptor::IsAccessorDescriptor(current))); Handle<Object> getter( desc->has_get() ? desc->get() - : current.has_get() - ? current.get() + : current->has_get() + ? current->get() : Handle<Object>::cast(isolate->factory()->null_value())); Handle<Object> setter( desc->has_set() ? desc->set() - : current.has_set() - ? current.set() + : current->has_set() + ? current->set() : Handle<Object>::cast(isolate->factory()->null_value())); MaybeHandle<Object> result = JSObject::DefineAccessor(it, getter, setter, attrs); - if (result.is_null()) return false; + if (result.is_null()) return Nothing<bool>(); } } // 11. Return true. - return true; + return Just(true); +} + + +// static +Maybe<bool> JSReceiver::CreateDataProperty(LookupIterator* it, + Handle<Object> value, + ShouldThrow should_throw) { + DCHECK(!it->check_prototype_chain()); + Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver()); + Isolate* isolate = receiver->GetIsolate(); + + if (receiver->IsJSObject()) { + return JSObject::CreateDataProperty(it, value); // Shortcut. + } + + PropertyDescriptor new_desc; + new_desc.set_value(value); + new_desc.set_writable(true); + new_desc.set_enumerable(true); + new_desc.set_configurable(true); + + return JSReceiver::DefineOwnProperty(isolate, receiver, it->GetName(), + &new_desc, should_throw); +} + + +Maybe<bool> JSObject::CreateDataProperty(LookupIterator* it, + Handle<Object> value) { + DCHECK(it->GetReceiver()->IsJSObject()); + MAYBE_RETURN(JSReceiver::GetPropertyAttributes(it), Nothing<bool>()); + + if (it->IsFound()) { + if (!it->IsConfigurable()) return Just(false); + } else { + if (!JSObject::IsExtensible(Handle<JSObject>::cast(it->GetReceiver()))) + return Just(false); + } + + RETURN_ON_EXCEPTION_VALUE( + it->isolate(), + DefineOwnPropertyIgnoreAttributes(it, value, NONE, DONT_FORCE_FIELD), + Nothing<bool>()); + + return Just(true); } @@ -6424,9 +6883,10 @@ bool PropertyKeyToArrayIndex(Handle<Object> index_obj, uint32_t* output) { // ES6 9.4.2.1 // static -bool JSArray::DefineOwnProperty(Isolate* isolate, Handle<JSArray> o, - Handle<Object> name, PropertyDescriptor* desc, - ShouldThrow should_throw) { +Maybe<bool> JSArray::DefineOwnProperty(Isolate* isolate, Handle<JSArray> o, + Handle<Object> name, + PropertyDescriptor* desc, + ShouldThrow should_throw) { // 1. Assert: IsPropertyKey(P) is true. ("P" is |name|.) // 2. If P is "length", then: // TODO(jkummerow): Check if we need slow string comparison. @@ -6439,10 +6899,10 @@ bool JSArray::DefineOwnProperty(Isolate* isolate, Handle<JSArray> o, if (PropertyKeyToArrayIndex(name, &index)) { // 3a. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length"). PropertyDescriptor old_len_desc; - bool success = GetOwnPropertyDescriptor( + Maybe<bool> success = GetOwnPropertyDescriptor( isolate, o, isolate->factory()->length_string(), &old_len_desc); // 3b. (Assert) - DCHECK(success); + DCHECK(success.FromJust()); USE(success); // 3c. Let oldLen be oldLenDesc.[[Value]]. uint32_t old_len = 0; @@ -6454,30 +6914,31 @@ bool JSArray::DefineOwnProperty(Isolate* isolate, Handle<JSArray> o, // return false. if (index >= old_len && old_len_desc.has_writable() && !old_len_desc.writable()) { - if (should_throw == THROW_ON_ERROR) { - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kDefineDisallowed, name)); - } - return false; + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kDefineDisallowed, name)); } // 3g. Let succeeded be OrdinaryDefineOwnProperty(A, P, Desc). - bool succeeded = + Maybe<bool> succeeded = OrdinaryDefineOwnProperty(isolate, o, name, desc, should_throw); - // 3h. (Assert) + // 3h. Assert: succeeded is not an abrupt completion. + // In our case, if should_throw == THROW_ON_ERROR, it can be! // 3i. If succeeded is false, return false. - if (!succeeded) return false; + if (succeeded.IsNothing() || !succeeded.FromJust()) return succeeded; // 3j. If index >= oldLen, then: if (index >= old_len) { // 3j i. Set oldLenDesc.[[Value]] to index + 1. old_len_desc.set_value(isolate->factory()->NewNumberFromUint(index + 1)); // 3j ii. Let succeeded be // OrdinaryDefineOwnProperty(A, "length", oldLenDesc). - OrdinaryDefineOwnProperty(isolate, o, isolate->factory()->length_string(), - &old_len_desc, should_throw); - // 3j iii. (Assert) + succeeded = OrdinaryDefineOwnProperty(isolate, o, + isolate->factory()->length_string(), + &old_len_desc, should_throw); + // 3j iii. Assert: succeeded is true. + DCHECK(succeeded.FromJust()); + USE(succeeded); } // 3k. Return true. - return true; + return Just(true); } // 4. Return OrdinaryDefineOwnProperty(A, P, Desc). @@ -6524,9 +6985,9 @@ bool JSArray::AnythingToArrayLength(Isolate* isolate, // ES6 9.4.2.4 // static -bool JSArray::ArraySetLength(Isolate* isolate, Handle<JSArray> a, - PropertyDescriptor* desc, - ShouldThrow should_throw) { +Maybe<bool> JSArray::ArraySetLength(Isolate* isolate, Handle<JSArray> a, + PropertyDescriptor* desc, + ShouldThrow should_throw) { // 1. If the [[Value]] field of Desc is absent, then if (!desc->has_value()) { // 1a. Return OrdinaryDefineOwnProperty(A, "length", Desc). @@ -6539,20 +7000,17 @@ bool JSArray::ArraySetLength(Isolate* isolate, Handle<JSArray> a, // 3. - 7. Convert Desc.[[Value]] to newLen. uint32_t new_len = 0; if (!AnythingToArrayLength(isolate, desc->value(), &new_len)) { - if (should_throw == THROW_ON_ERROR && !isolate->has_pending_exception()) { - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kCannotConvertToPrimitive)); - } - return false; + DCHECK(isolate->has_pending_exception()); + return Nothing<bool>(); } // 8. Set newLenDesc.[[Value]] to newLen. // (Done below, if needed.) // 9. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length"). PropertyDescriptor old_len_desc; - bool success = GetOwnPropertyDescriptor( + Maybe<bool> success = GetOwnPropertyDescriptor( isolate, a, isolate->factory()->length_string(), &old_len_desc); // 10. (Assert) - DCHECK(success); + DCHECK(success.FromJust()); USE(success); // 11. Let oldLen be oldLenDesc.[[Value]]. uint32_t old_len = 0; @@ -6568,11 +7026,9 @@ bool JSArray::ArraySetLength(Isolate* isolate, Handle<JSArray> a, } // 13. If oldLenDesc.[[Writable]] is false, return false. if (!old_len_desc.writable()) { - if (should_throw == THROW_ON_ERROR) - isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kRedefineDisallowed, - isolate->factory()->length_string())); - return false; + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kRedefineDisallowed, + isolate->factory()->length_string())); } // 14. If newLenDesc.[[Writable]] is absent or has the value true, // let newWritable be true. @@ -6590,33 +7046,186 @@ bool JSArray::ArraySetLength(Isolate* isolate, Handle<JSArray> a, // 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 false; + return Nothing<bool>(); } // Steps 19d-ii, 20. if (!new_writable) { PropertyDescriptor readonly; readonly.set_writable(false); - OrdinaryDefineOwnProperty(isolate, a, isolate->factory()->length_string(), - &readonly, should_throw); + Maybe<bool> success = OrdinaryDefineOwnProperty( + isolate, a, isolate->factory()->length_string(), &readonly, + should_throw); + DCHECK(success.FromJust()); + USE(success); } uint32_t actual_new_len = 0; CHECK(a->length()->ToArrayLength(&actual_new_len)); // Steps 19d-v, 21. Return false if there were non-deletable elements. - success = actual_new_len == new_len; - if (!success && should_throw == THROW_ON_ERROR) { + bool result = actual_new_len == new_len; + if (!result) { + RETURN_FAILURE( + isolate, should_throw, + NewTypeError(MessageTemplate::kStrictDeleteProperty, + isolate->factory()->NewNumberFromUint(actual_new_len - 1), + a)); + } + return Just(result); +} + + +// ES6 9.5.6 +// static +Maybe<bool> JSProxy::DefineOwnProperty(Isolate* isolate, Handle<JSProxy> proxy, + Handle<Object> key, + PropertyDescriptor* desc, + ShouldThrow should_throw) { + STACK_CHECK(Nothing<bool>()); + if (key->IsSymbol() && Handle<Symbol>::cast(key)->IsPrivate()) { + return AddPrivateProperty(isolate, proxy, Handle<Symbol>::cast(key), desc, + should_throw); + } + Handle<String> trap_name = isolate->factory()->defineProperty_string(); + // 1. Assert: IsPropertyKey(P) is true. + DCHECK(key->IsName() || key->IsNumber()); + // 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. + // 4. Assert: Type(handler) is Object. + if (proxy->IsRevoked()) { isolate->Throw(*isolate->factory()->NewTypeError( - MessageTemplate::kStrictDeleteProperty, - isolate->factory()->NewNumberFromUint(actual_new_len - 1), a)); + MessageTemplate::kProxyRevoked, trap_name)); + return Nothing<bool>(); } - return success; + // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. + Handle<JSReceiver> target(proxy->target(), isolate); + // 6. Let trap be ? GetMethod(handler, "defineProperty"). + Handle<Object> trap; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, trap, + Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name), + Nothing<bool>()); + // 7. If trap is undefined, then: + if (trap->IsUndefined()) { + // 7a. Return target.[[DefineOwnProperty]](P, Desc). + return JSReceiver::DefineOwnProperty(isolate, target, key, desc, + should_throw); + } + // 8. Let descObj be FromPropertyDescriptor(Desc). + Handle<Object> desc_obj = desc->ToObject(isolate); + // 9. Let booleanTrapResult be + // ToBoolean(? Call(trap, handler, «target, P, descObj»)). + Handle<Name> property_name = + key->IsName() + ? Handle<Name>::cast(key) + : Handle<Name>::cast(isolate->factory()->NumberToString(key)); + // Do not leak private property names. + DCHECK(!property_name->IsPrivate()); + Handle<Object> trap_result_obj; + Handle<Object> args[] = {target, property_name, desc_obj}; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, trap_result_obj, + Execution::Call(isolate, trap, handler, arraysize(args), args), + Nothing<bool>()); + // 10. If booleanTrapResult is false, return false. + if (!trap_result_obj->BooleanValue()) { + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kProxyTrapReturnedFalsishFor, + trap_name, property_name)); + } + // 11. Let targetDesc be ? target.[[GetOwnProperty]](P). + PropertyDescriptor target_desc; + Maybe<bool> target_found = + JSReceiver::GetOwnPropertyDescriptor(isolate, target, key, &target_desc); + MAYBE_RETURN(target_found, Nothing<bool>()); + // 12. Let extensibleTarget be ? IsExtensible(target). + Maybe<bool> maybe_extensible = JSReceiver::IsExtensible(target); + MAYBE_RETURN(maybe_extensible, Nothing<bool>()); + bool extensible_target = maybe_extensible.FromJust(); + // 13. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] + // is false, then: + // 13a. Let settingConfigFalse be true. + // 14. Else let settingConfigFalse be false. + bool setting_config_false = desc->has_configurable() && !desc->configurable(); + // 15. If targetDesc is undefined, then + if (!target_found.FromJust()) { + // 15a. If extensibleTarget is false, throw a TypeError exception. + if (!extensible_target) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyDefinePropertyNonExtensible, property_name)); + return Nothing<bool>(); + } + // 15b. If settingConfigFalse is true, throw a TypeError exception. + if (setting_config_false) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyDefinePropertyNonConfigurable, property_name)); + return Nothing<bool>(); + } + } else { + // 16. Else targetDesc is not undefined, + // 16a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, + // targetDesc) is false, throw a TypeError exception. + Maybe<bool> valid = + IsCompatiblePropertyDescriptor(isolate, extensible_target, desc, + &target_desc, property_name, DONT_THROW); + MAYBE_RETURN(valid, Nothing<bool>()); + if (!valid.FromJust()) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyDefinePropertyIncompatible, property_name)); + return Nothing<bool>(); + } + // 16b. If settingConfigFalse is true and targetDesc.[[Configurable]] is + // true, throw a TypeError exception. + if (setting_config_false && target_desc.configurable()) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyDefinePropertyNonConfigurable, property_name)); + return Nothing<bool>(); + } + } + // 17. Return true. + return Just(true); } // static -bool JSReceiver::GetOwnPropertyDescriptor(Isolate* isolate, - Handle<JSReceiver> object, - Handle<Object> key, - PropertyDescriptor* desc) { +Maybe<bool> JSProxy::AddPrivateProperty(Isolate* isolate, Handle<JSProxy> proxy, + Handle<Symbol> private_name, + PropertyDescriptor* desc, + ShouldThrow should_throw) { + // Despite the generic name, this can only add private data properties. + if (!PropertyDescriptor::IsDataDescriptor(desc) || + desc->ToAttributes() != DONT_ENUM) { + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kProxyPrivate)); + } + DCHECK(proxy->map()->is_dictionary_map()); + Handle<Object> value = + desc->has_value() + ? desc->value() + : Handle<Object>::cast(isolate->factory()->undefined_value()); + + LookupIterator it(proxy, private_name); + + if (it.IsFound()) { + DCHECK_EQ(LookupIterator::DATA, it.state()); + DCHECK_EQ(DONT_ENUM, it.property_details().attributes()); + it.WriteDataValue(value); + return Just(true); + } + + Handle<NameDictionary> dict(proxy->property_dictionary()); + PropertyDetails details(DONT_ENUM, DATA, 0, PropertyCellType::kNoCell); + Handle<NameDictionary> result = + NameDictionary::Add(dict, private_name, value, details); + if (!dict.is_identical_to(result)) proxy->set_properties(*result); + return Just(true); +} + + +// static +Maybe<bool> JSReceiver::GetOwnPropertyDescriptor(Isolate* isolate, + Handle<JSReceiver> object, + Handle<Object> key, + PropertyDescriptor* desc) { bool success = false; DCHECK(key->IsName() || key->IsNumber()); // |key| is a PropertyKey... LookupIterator it = LookupIterator::PropertyOrElement( @@ -6626,25 +7235,25 @@ bool JSReceiver::GetOwnPropertyDescriptor(Isolate* isolate, } -// TODO(jkummerow): Any chance to unify this with -// "MaybeHandle<Object> GetOwnProperty()" in runtime-object.cc? - -// TODO(jkummerow/verwaest): Proxy support: call getOwnPropertyDescriptor trap -// and convert the result (if it's an object) with ToPropertyDescriptor. - // ES6 9.1.5.1 -// Returns true on success; false if there was an exception or no property. +// Returns true on success, false if the property didn't exist, nothing if +// an exception was thrown. // static -bool JSReceiver::GetOwnPropertyDescriptor(LookupIterator* it, - PropertyDescriptor* desc) { +Maybe<bool> JSReceiver::GetOwnPropertyDescriptor(LookupIterator* it, + PropertyDescriptor* desc) { Isolate* isolate = it->isolate(); + // "Virtual" dispatch. + if (it->IsFound() && it->GetHolder<JSReceiver>()->IsJSProxy()) { + return JSProxy::GetOwnPropertyDescriptor(isolate, it->GetHolder<JSProxy>(), + it->GetName(), desc); + } + // 1. (Assert) // 2. If O does not have an own property with key P, return undefined. Maybe<PropertyAttributes> maybe = JSObject::GetPropertyAttributes(it); - - if (!maybe.IsJust()) return false; + MAYBE_RETURN(maybe, Nothing<bool>()); PropertyAttributes attrs = maybe.FromJust(); - if (attrs == ABSENT) return false; + if (attrs == ABSENT) return Just(false); DCHECK(!isolate->has_pending_exception()); // 3. Let D be a newly created Property Descriptor with no fields. @@ -6658,7 +7267,7 @@ bool JSReceiver::GetOwnPropertyDescriptor(LookupIterator* it, Handle<Object> value; if (!JSObject::GetProperty(it).ToHandle(&value)) { DCHECK(isolate->has_pending_exception()); - return false; + return Nothing<bool>(); } desc->set_value(value); // 5b. Set D.[[Writable]] to the value of X's [[Writable]] attribute @@ -6680,7 +7289,123 @@ bool JSReceiver::GetOwnPropertyDescriptor(LookupIterator* it, // 9. Return D. DCHECK(PropertyDescriptor::IsAccessorDescriptor(desc) != PropertyDescriptor::IsDataDescriptor(desc)); - return true; + return Just(true); +} + + +// ES6 9.5.5 +// static +Maybe<bool> JSProxy::GetOwnPropertyDescriptor(Isolate* isolate, + Handle<JSProxy> proxy, + Handle<Name> name, + PropertyDescriptor* desc) { + DCHECK(!name->IsPrivate()); + STACK_CHECK(Nothing<bool>()); + + Handle<String> trap_name = + isolate->factory()->getOwnPropertyDescriptor_string(); + // 1. (Assert) + // 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. + // 4. Assert: Type(handler) is Object. + if (proxy->IsRevoked()) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyRevoked, trap_name)); + return Nothing<bool>(); + } + // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. + Handle<JSReceiver> target(proxy->target(), isolate); + // 6. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor"). + Handle<Object> trap; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, trap, + Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name), + Nothing<bool>()); + // 7. If trap is undefined, then + if (trap->IsUndefined()) { + // 7a. Return target.[[GetOwnProperty]](P). + return JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, desc); + } + // 8. Let trapResultObj be ? Call(trap, handler, «target, P»). + Handle<Object> trap_result_obj; + Handle<Object> args[] = {target, name}; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, trap_result_obj, + Execution::Call(isolate, trap, handler, arraysize(args), args), + Nothing<bool>()); + // 9. If Type(trapResultObj) is neither Object nor Undefined, throw a + // TypeError exception. + if (!trap_result_obj->IsJSReceiver() && !trap_result_obj->IsUndefined()) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyGetOwnPropertyDescriptorInvalid, name)); + return Nothing<bool>(); + } + // 10. Let targetDesc be ? target.[[GetOwnProperty]](P). + PropertyDescriptor target_desc; + Maybe<bool> found = + JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc); + MAYBE_RETURN(found, Nothing<bool>()); + // 11. If trapResultObj is undefined, then + if (trap_result_obj->IsUndefined()) { + // 11a. If targetDesc is undefined, return undefined. + if (!found.FromJust()) return Just(false); + // 11b. If targetDesc.[[Configurable]] is false, throw a TypeError + // exception. + if (!target_desc.configurable()) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyGetOwnPropertyDescriptorUndefined, name)); + return Nothing<bool>(); + } + // 11c. Let extensibleTarget be ? IsExtensible(target). + Maybe<bool> extensible_target = JSReceiver::IsExtensible(target); + MAYBE_RETURN(extensible_target, Nothing<bool>()); + // 11d. (Assert) + // 11e. If extensibleTarget is false, throw a TypeError exception. + if (!extensible_target.FromJust()) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyGetOwnPropertyDescriptorNonExtensible, name)); + return Nothing<bool>(); + } + // 11f. Return undefined. + return Just(false); + } + // 12. Let extensibleTarget be ? IsExtensible(target). + Maybe<bool> extensible_target = JSReceiver::IsExtensible(target); + MAYBE_RETURN(extensible_target, Nothing<bool>()); + // 13. Let resultDesc be ? ToPropertyDescriptor(trapResultObj). + if (!PropertyDescriptor::ToPropertyDescriptor(isolate, trap_result_obj, + desc)) { + DCHECK(isolate->has_pending_exception()); + return Nothing<bool>(); + } + // 14. Call CompletePropertyDescriptor(resultDesc). + PropertyDescriptor::CompletePropertyDescriptor(isolate, desc); + // 15. Let valid be IsCompatiblePropertyDescriptor (extensibleTarget, + // resultDesc, targetDesc). + Maybe<bool> valid = + IsCompatiblePropertyDescriptor(isolate, extensible_target.FromJust(), + desc, &target_desc, name, DONT_THROW); + MAYBE_RETURN(valid, Nothing<bool>()); + // 16. If valid is false, throw a TypeError exception. + if (!valid.FromJust()) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyGetOwnPropertyDescriptorIncompatible, name)); + return Nothing<bool>(); + } + // 17. If resultDesc.[[Configurable]] is false, then + if (!desc->configurable()) { + // 17a. If targetDesc is undefined or targetDesc.[[Configurable]] is true: + if (target_desc.is_empty() || target_desc.configurable()) { + // 17a i. Throw a TypeError exception. + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyGetOwnPropertyDescriptorNonConfigurable, + name)); + return Nothing<bool>(); + } + } + // 18. Return resultDesc. + return Just(true); } @@ -6819,15 +7544,162 @@ bool JSObject::ReferencesObject(Object* obj) { } +Maybe<bool> JSReceiver::SetIntegrityLevel(Handle<JSReceiver> receiver, + IntegrityLevel level, + ShouldThrow should_throw) { + DCHECK(level == SEALED || level == FROZEN); + + if (receiver->IsJSObject()) { + Handle<JSObject> object = Handle<JSObject>::cast(receiver); + if (!object->HasSloppyArgumentsElements() && + !object->map()->is_observed() && + (!object->map()->is_strong() || level == SEALED)) { // Fast path. + if (level == SEALED) { + return JSObject::PreventExtensionsWithTransition<SEALED>(object, + should_throw); + } else { + return JSObject::PreventExtensionsWithTransition<FROZEN>(object, + should_throw); + } + } + } + + Isolate* isolate = receiver->GetIsolate(); + + MAYBE_RETURN(JSReceiver::PreventExtensions(receiver, should_throw), + Nothing<bool>()); + + Handle<FixedArray> keys; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, keys, JSReceiver::OwnPropertyKeys(receiver), Nothing<bool>()); + + PropertyDescriptor no_conf; + no_conf.set_configurable(false); + + PropertyDescriptor no_conf_no_write; + no_conf_no_write.set_configurable(false); + no_conf_no_write.set_writable(false); + + if (level == SEALED) { + for (int i = 0; i < keys->length(); ++i) { + Handle<Object> key(keys->get(i), isolate); + MAYBE_RETURN( + DefineOwnProperty(isolate, receiver, key, &no_conf, THROW_ON_ERROR), + Nothing<bool>()); + } + return Just(true); + } + + for (int i = 0; i < keys->length(); ++i) { + Handle<Object> key(keys->get(i), isolate); + PropertyDescriptor current_desc; + Maybe<bool> owned = JSReceiver::GetOwnPropertyDescriptor( + isolate, receiver, key, ¤t_desc); + MAYBE_RETURN(owned, Nothing<bool>()); + if (owned.FromJust()) { + PropertyDescriptor desc = + PropertyDescriptor::IsAccessorDescriptor(¤t_desc) + ? no_conf + : no_conf_no_write; + MAYBE_RETURN( + DefineOwnProperty(isolate, receiver, key, &desc, THROW_ON_ERROR), + Nothing<bool>()); + } + } + return Just(true); +} + + +Maybe<bool> JSReceiver::TestIntegrityLevel(Handle<JSReceiver> object, + IntegrityLevel level) { + DCHECK(level == SEALED || level == FROZEN); + Isolate* isolate = object->GetIsolate(); + + Maybe<bool> extensible = JSReceiver::IsExtensible(object); + MAYBE_RETURN(extensible, Nothing<bool>()); + if (extensible.FromJust()) return Just(false); + + Handle<FixedArray> keys; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, keys, JSReceiver::OwnPropertyKeys(object), Nothing<bool>()); + + for (int i = 0; i < keys->length(); ++i) { + Handle<Object> key(keys->get(i), isolate); + PropertyDescriptor current_desc; + Maybe<bool> owned = JSReceiver::GetOwnPropertyDescriptor( + isolate, object, key, ¤t_desc); + MAYBE_RETURN(owned, Nothing<bool>()); + if (owned.FromJust()) { + if (current_desc.configurable()) return Just(false); + if (level == FROZEN && + PropertyDescriptor::IsDataDescriptor(¤t_desc) && + current_desc.writable()) { + return Just(false); + } + } + } + return Just(true); +} + + Maybe<bool> JSReceiver::PreventExtensions(Handle<JSReceiver> object, ShouldThrow should_throw) { - if (!object->IsJSObject()) return Just(false); - // TODO(neis): Deal with proxies. + if (object->IsJSProxy()) { + return JSProxy::PreventExtensions(Handle<JSProxy>::cast(object), + should_throw); + } + DCHECK(object->IsJSObject()); return JSObject::PreventExtensions(Handle<JSObject>::cast(object), should_throw); } +Maybe<bool> JSProxy::PreventExtensions(Handle<JSProxy> proxy, + ShouldThrow should_throw) { + Isolate* isolate = proxy->GetIsolate(); + STACK_CHECK(Nothing<bool>()); + Factory* factory = isolate->factory(); + Handle<String> trap_name = factory->preventExtensions_string(); + + if (proxy->IsRevoked()) { + isolate->Throw( + *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name)); + return Nothing<bool>(); + } + Handle<JSReceiver> target(proxy->target(), isolate); + Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate); + + Handle<Object> trap; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>()); + if (trap->IsUndefined()) { + return JSReceiver::PreventExtensions(target, should_throw); + } + + Handle<Object> trap_result; + Handle<Object> args[] = {target}; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, trap_result, + Execution::Call(isolate, trap, handler, arraysize(args), args), + Nothing<bool>()); + if (!trap_result->BooleanValue()) { + RETURN_FAILURE( + isolate, should_throw, + NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name)); + } + + // Enforce the invariant. + Maybe<bool> target_result = JSReceiver::IsExtensible(target); + MAYBE_RETURN(target_result, Nothing<bool>()); + if (target_result.FromJust()) { + isolate->Throw(*factory->NewTypeError( + MessageTemplate::kProxyPreventExtensionsExtensible)); + return Nothing<bool>(); + } + return Just(true); +} + + Maybe<bool> JSObject::PreventExtensions(Handle<JSObject> object, ShouldThrow should_throw) { Isolate* isolate = object->GetIsolate(); @@ -6840,7 +7712,6 @@ Maybe<bool> JSObject::PreventExtensions(Handle<JSObject> object, !isolate->MayAccess(handle(isolate->context()), object)) { isolate->ReportFailedAccessCheck(object); RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); - UNREACHABLE(); RETURN_FAILURE(isolate, should_throw, NewTypeError(MessageTemplate::kNoAccess)); } @@ -6885,6 +7756,55 @@ Maybe<bool> JSObject::PreventExtensions(Handle<JSObject> object, } +Maybe<bool> JSReceiver::IsExtensible(Handle<JSReceiver> object) { + if (object->IsJSProxy()) { + return JSProxy::IsExtensible(Handle<JSProxy>::cast(object)); + } + return Just(JSObject::IsExtensible(Handle<JSObject>::cast(object))); +} + + +Maybe<bool> JSProxy::IsExtensible(Handle<JSProxy> proxy) { + Isolate* isolate = proxy->GetIsolate(); + STACK_CHECK(Nothing<bool>()); + Factory* factory = isolate->factory(); + Handle<String> trap_name = factory->isExtensible_string(); + + if (proxy->IsRevoked()) { + isolate->Throw( + *factory->NewTypeError(MessageTemplate::kProxyRevoked, trap_name)); + return Nothing<bool>(); + } + Handle<JSReceiver> target(proxy->target(), isolate); + Handle<JSReceiver> handler(JSReceiver::cast(proxy->handler()), isolate); + + Handle<Object> trap; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>()); + if (trap->IsUndefined()) { + return JSReceiver::IsExtensible(target); + } + + Handle<Object> trap_result; + Handle<Object> args[] = {target}; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, trap_result, + Execution::Call(isolate, trap, handler, arraysize(args), args), + Nothing<bool>()); + + // Enforce the invariant. + Maybe<bool> target_result = JSReceiver::IsExtensible(target); + MAYBE_RETURN(target_result, Nothing<bool>()); + if (target_result.FromJust() != trap_result->BooleanValue()) { + isolate->Throw( + *factory->NewTypeError(MessageTemplate::kProxyIsExtensibleInconsistent, + factory->ToBoolean(target_result.FromJust()))); + return Nothing<bool>(); + } + return target_result; +} + + bool JSObject::IsExtensible(Handle<JSObject> object) { Isolate* isolate = object->GetIsolate(); if (object->IsAccessCheckNeeded() && @@ -6939,7 +7859,6 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition( !isolate->MayAccess(handle(isolate->context()), object)) { isolate->ReportFailedAccessCheck(object); RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); - UNREACHABLE(); RETURN_FAILURE(isolate, should_throw, NewTypeError(MessageTemplate::kNoAccess)); } @@ -7046,20 +7965,6 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition( } -MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) { - MAYBE_RETURN_NULL( - PreventExtensionsWithTransition<FROZEN>(object, THROW_ON_ERROR)); - return object; -} - - -MaybeHandle<Object> JSObject::Seal(Handle<JSObject> object) { - MAYBE_RETURN_NULL( - PreventExtensionsWithTransition<SEALED>(object, THROW_ON_ERROR)); - return object; -} - - void JSObject::SetObserved(Handle<JSObject> object) { DCHECK(!object->IsJSGlobalProxy()); DCHECK(!object->IsJSGlobalObject()); @@ -7205,22 +8110,20 @@ MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk( } } } else { - Handle<FixedArray> names = - isolate->factory()->NewFixedArray(copy->NumberOfOwnProperties()); - copy->GetOwnPropertyNames(*names, 0); + // Only deep copy fields from the object literal expression. + // In particular, don't try to copy the length attribute of + // an array. + PropertyFilter filter = static_cast<PropertyFilter>( + ONLY_WRITABLE | ONLY_ENUMERABLE | ONLY_CONFIGURABLE); + KeyAccumulator accumulator(isolate, filter); + accumulator.NextPrototype(); + copy->CollectOwnPropertyNames(&accumulator, filter); + Handle<FixedArray> names = accumulator.GetKeys(); for (int i = 0; i < names->length(); i++) { - DCHECK(names->get(i)->IsString()); - Handle<String> key_string(String::cast(names->get(i))); - Maybe<PropertyAttributes> maybe = - JSReceiver::GetOwnPropertyAttributes(copy, key_string); - DCHECK(maybe.IsJust()); - PropertyAttributes attributes = maybe.FromJust(); - // Only deep copy fields from the object literal expression. - // In particular, don't try to copy the length attribute of - // an array. - if (attributes != NONE) continue; + DCHECK(names->get(i)->IsName()); + Handle<Name> name(Name::cast(names->get(i))); Handle<Object> value = - Object::GetProperty(copy, key_string).ToHandleChecked(); + Object::GetProperty(copy, name).ToHandleChecked(); if (value->IsJSObject()) { Handle<JSObject> result; ASSIGN_RETURN_ON_EXCEPTION( @@ -7229,7 +8132,7 @@ MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk( JSObject); if (copying) { // Creating object copy for literals. No strict mode needed. - JSObject::SetProperty(copy, key_string, result, SLOPPY).Assert(); + JSObject::SetProperty(copy, name, result, SLOPPY).Assert(); } } } @@ -7409,6 +8312,71 @@ MaybeHandle<Object> JSReceiver::OrdinaryToPrimitive( } +// TODO(cbruni/jkummerow): Consider moving this into elements.cc. +bool HasEnumerableElements(JSObject* object) { + if (object->IsJSValue()) { + Object* value = JSValue::cast(object)->value(); + if (value->IsString()) { + if (String::cast(value)->length() > 0) return true; + } + } + switch (object->GetElementsKind()) { + case FAST_SMI_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: { + int length = object->IsJSArray() + ? Smi::cast(JSArray::cast(object)->length())->value() + : object->elements()->length(); + return length > 0; + } + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: { + FixedArray* elements = FixedArray::cast(object->elements()); + int length = object->IsJSArray() + ? Smi::cast(JSArray::cast(object)->length())->value() + : elements->length(); + for (int i = 0; i < length; i++) { + if (!elements->is_the_hole(i)) return true; + } + return false; + } + case FAST_HOLEY_DOUBLE_ELEMENTS: { + int length = object->IsJSArray() + ? Smi::cast(JSArray::cast(object)->length())->value() + : object->elements()->length(); + // Zero-length arrays would use the empty FixedArray... + if (length == 0) return false; + // ...so only cast to FixedDoubleArray otherwise. + FixedDoubleArray* elements = FixedDoubleArray::cast(object->elements()); + for (int i = 0; i < length; i++) { + if (!elements->is_the_hole(i)) return true; + } + return false; + } +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + case TYPE##_ELEMENTS: + + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE + { + int length = object->elements()->length(); + return length > 0; + } + case DICTIONARY_ELEMENTS: { + SeededNumberDictionary* elements = + SeededNumberDictionary::cast(object->elements()); + return elements->NumberOfElementsFilterAttributes(ONLY_ENUMERABLE) > 0; + } + case FAST_SLOPPY_ARGUMENTS_ELEMENTS: + case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: + // We're approximating non-empty arguments objects here. + return true; + } + UNREACHABLE(); + 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). @@ -7425,7 +8393,7 @@ bool JSReceiver::IsSimpleEnum() { if (current->IsAccessCheckNeeded()) return false; DCHECK(!current->HasNamedInterceptor()); DCHECK(!current->HasIndexedInterceptor()); - if (current->NumberOfEnumElements() > 0) return false; + if (HasEnumerableElements(current)) return false; if (current != this && enum_length != 0) return false; } return true; @@ -7433,7 +8401,7 @@ bool JSReceiver::IsSimpleEnum() { int Map::NumberOfDescribedProperties(DescriptorFlag which, - PropertyAttributes filter) { + PropertyFilter filter) { int result = 0; DescriptorArray* descs = instance_descriptors(); int limit = which == ALL_DESCRIPTORS @@ -7498,14 +8466,14 @@ Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate, // 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 not DONT_ENUM or - // SYMBOLIC. + // counting the number of own descriptors that are ENUMERABLE_STRINGS. if (own_property_count == kInvalidEnumCacheSentinel) { own_property_count = - map->NumberOfDescribedProperties(OWN_DESCRIPTORS, DONT_SHOW); + map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS); } else { - DCHECK(own_property_count == - map->NumberOfDescribedProperties(OWN_DESCRIPTORS, DONT_SHOW)); + DCHECK( + own_property_count == + map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS)); } if (descs->HasEnumCache()) { @@ -7592,103 +8560,368 @@ Handle<FixedArray> JSObject::GetEnumPropertyKeys(Handle<JSObject> object, } -MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, - KeyCollectionType type, - KeyFilter filter, - GetKeysConversion getConversion) { - USE(ContainsOnlyValidKeys); - Isolate* isolate = object->GetIsolate(); - KeyAccumulator accumulator(isolate, filter); - Handle<JSFunction> arguments_function( - JSFunction::cast(isolate->sloppy_arguments_map()->GetConstructor())); - PrototypeIterator::WhereToEnd end = type == OWN_ONLY +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); + v8::Local<v8::Object> 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.IsEmpty()) return Just(true); + DCHECK(v8::Utils::OpenHandle(*result)->IsJSArray() || + (v8::Utils::OpenHandle(*result)->IsJSObject() && + Handle<JSObject>::cast(v8::Utils::OpenHandle(*result)) + ->HasSloppyArgumentsElements())); + // The accumulator takes care of string/symbol filtering. + if (type == kIndexed) { + accumulator->AddElementKeysFromInterceptor( + Handle<JSObject>::cast(v8::Utils::OpenHandle(*result))); + } else { + accumulator->AddKeys( + Handle<JSObject>::cast(v8::Utils::OpenHandle(*result))); + } + 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, + JSReceiver::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 == JSReceiver::INCLUDE_PROTOS) { + return Just(false); + } + // ...whereas [[OwnPropertyKeys]] shall return whitelisted properties. + DCHECK(type == JSReceiver::OWN_ONLY); + *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) { + // We can cache the computed property keys if access checks are + // not needed and no interceptors are involved. + // + // We do not use the cache if the object has elements and + // therefore it does not make sense to cache the property names + // for arguments objects. Arguments objects will always have + // elements. + // Wrapped strings have elements, but don't have an elements + // array or dictionary. So the fast inline test for whether to + // use the cache says yes, so we should not create a cache. + Handle<JSFunction> arguments_function( + JSFunction::cast(isolate->sloppy_arguments_map()->GetConstructor())); + bool cache_enum_length = + ((object->map()->GetConstructor() != *arguments_function) && + !object->IsJSValue() && !object->IsAccessCheckNeeded() && + !object->HasNamedInterceptor() && !object->HasIndexedInterceptor()); + // Compute the property keys and cache them if possible. + Handle<FixedArray> enum_keys = + JSObject::GetEnumPropertyKeys(object, cache_enum_length); + accumulator->AddKeys(enum_keys); + } 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, + JSReceiver::KeyCollectionType type, + PropertyFilter filter, + KeyAccumulator* accumulator) { + PrototypeIterator::WhereToEnd end = type == JSReceiver::OWN_ONLY ? PrototypeIterator::END_AT_NON_HIDDEN : PrototypeIterator::END_AT_NULL; - // Only collect keys if access is permitted. for (PrototypeIterator iter(isolate, object, PrototypeIterator::START_AT_RECEIVER); !iter.IsAtEnd(end); iter.Advance()) { - accumulator.NextPrototype(); - if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { - Handle<JSProxy> proxy = PrototypeIterator::GetCurrent<JSProxy>(iter); - Handle<Object> args[] = { proxy }; - Handle<Object> names; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, names, - Execution::Call(isolate, - isolate->proxy_enumerate(), - object, - arraysize(args), - args), - FixedArray); - accumulator.AddKeysFromProxy(Handle<JSObject>::cast(names)); - break; - } - - Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter); - - // Check access rights if required. - if (current->IsAccessCheckNeeded() && - !isolate->MayAccess(handle(isolate->context()), current)) { - if (iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)) { - isolate->ReportFailedAccessCheck(current); - RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, FixedArray); + Handle<JSReceiver> current = + PrototypeIterator::GetCurrent<JSReceiver>(iter); + Maybe<bool> result = Just(false); // Dummy initialization. + if (current->IsJSProxy()) { + if (type == JSReceiver::OWN_ONLY) { + result = JSProxy::OwnPropertyKeys(isolate, receiver, + Handle<JSProxy>::cast(current), + filter, accumulator); + } else { + DCHECK(type == JSReceiver::INCLUDE_PROTOS); + result = JSProxy::Enumerate( + isolate, receiver, Handle<JSProxy>::cast(current), accumulator); } - break; + } 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". + } + return Just(true); +} - JSObject::CollectOwnElementKeys(current, &accumulator, - static_cast<PropertyAttributes>(DONT_ENUM)); - // Add the element keys from the interceptor. - if (current->HasIndexedInterceptor()) { - Handle<JSObject> result; - if (JSObject::GetKeysForIndexedInterceptor(current, object) - .ToHandle(&result)) { - accumulator.AddElementKeysFromInterceptor(result); - } - RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, FixedArray); - } +// ES6 9.5.11 +// Returns false in case of exception. +// static +Maybe<bool> JSProxy::Enumerate(Isolate* isolate, Handle<JSReceiver> receiver, + Handle<JSProxy> proxy, + 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()->enumerate_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, "enumerate"). + Handle<Object> trap; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler), + isolate->factory()->enumerate_string()), + Nothing<bool>()); + // 6. If trap is undefined, then + if (trap->IsUndefined()) { + // 6a. Return target.[[Enumerate]](). + return GetKeys_Internal(isolate, receiver, target, INCLUDE_PROTOS, + ENUMERABLE_STRINGS, accumulator); + } + // The "proxy_enumerate" helper calls the trap (steps 7 - 9), which returns + // a generator; it then iterates over that generator until it's exhausted + // and returns an array containing the generated values. + Handle<Object> trap_result_array; + Handle<Object> args[] = {trap, handler, target}; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, trap_result_array, + Execution::Call(isolate, isolate->proxy_enumerate(), + isolate->factory()->undefined_value(), arraysize(args), + args), + Nothing<bool>()); + accumulator->NextPrototype(); + accumulator->AddKeysFromProxy(Handle<JSObject>::cast(trap_result_array)); + return Just(true); +} - if (filter == SKIP_SYMBOLS) { - // We can cache the computed property keys if access checks are - // not needed and no interceptors are involved. - // - // We do not use the cache if the object has elements and - // therefore it does not make sense to cache the property names - // for arguments objects. Arguments objects will always have - // elements. - // Wrapped strings have elements, but don't have an elements - // array or dictionary. So the fast inline test for whether to - // use the cache says yes, so we should not create a cache. - bool cache_enum_length = - ((current->map()->GetConstructor() != *arguments_function) && - !current->IsJSValue() && !current->IsAccessCheckNeeded() && - !current->HasNamedInterceptor() && - !current->HasIndexedInterceptor()); - // Compute the property keys and cache them if possible. - Handle<FixedArray> enum_keys = - JSObject::GetEnumPropertyKeys(current, cache_enum_length); - accumulator.AddKeys(enum_keys); + +// 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 { - DCHECK(filter == INCLUDE_SYMBOLS); - PropertyAttributes attr_filter = - static_cast<PropertyAttributes>(DONT_ENUM | PRIVATE_SYMBOL); - current->CollectOwnPropertyNames(&accumulator, attr_filter); - } - - // Add the property keys from the interceptor. - if (current->HasNamedInterceptor()) { - Handle<JSObject> result; - if (JSObject::GetKeysForNamedInterceptor(current, object) - .ToHandle(&result)) { - accumulator.AddKeys(result); - } - RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, FixedArray); + // 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; + const int kPresent = 1; + const int kGone = 0; + IdentityMap<int> unchecked_result_keys(isolate->heap(), &set_zone); + int unchecked_result_keys_size = trap_result->length(); + for (int i = 0; i < trap_result->length(); ++i) { + DCHECK(trap_result->get(i)->IsUniqueName()); + unchecked_result_keys.Set(trap_result->get(i), kPresent); + } + // 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); +} + - Handle<FixedArray> keys = accumulator.GetKeys(getConversion); +MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, + KeyCollectionType type, + PropertyFilter filter, + GetKeysConversion keys_conversion) { + USE(ContainsOnlyValidKeys); + Isolate* isolate = object->GetIsolate(); + KeyAccumulator accumulator(isolate, filter); + MAYBE_RETURN( + GetKeys_Internal(isolate, object, object, type, filter, &accumulator), + MaybeHandle<FixedArray>()); + Handle<FixedArray> keys = accumulator.GetKeys(keys_conversion); DCHECK(ContainsOnlyValidKeys(keys)); return keys; } @@ -7942,7 +9175,8 @@ Handle<Map> Map::RawCopy(Handle<Map> map, int instance_size) { if (!map->is_dictionary_map()) { new_bit_field3 = IsUnstable::update(new_bit_field3, false); } - new_bit_field3 = Counter::update(new_bit_field3, kRetainingCounterStart); + new_bit_field3 = + ConstructionCounter::update(new_bit_field3, kNoSlackTracking); result->set_bit_field3(new_bit_field3); return result; } @@ -8042,9 +9276,17 @@ Handle<Map> Map::CopyInitialMap(Handle<Map> map, int instance_size, int in_object_properties, int unused_property_fields) { #ifdef DEBUG + Isolate* isolate = map->GetIsolate(); + // Strict and strong function maps have Function as a constructor but the + // Function's initial map is a sloppy function map. Same holds for + // GeneratorFunction and its initial map. Object* constructor = map->GetConstructor(); DCHECK(constructor->IsJSFunction()); - DCHECK_EQ(*map, JSFunction::cast(constructor)->initial_map()); + DCHECK(*map == JSFunction::cast(constructor)->initial_map() || + *map == *isolate->strict_function_map() || + *map == *isolate->strong_function_map() || + *map == *isolate->strict_generator_function_map() || + *map == *isolate->strong_generator_function_map()); #endif // Initial maps must always own their descriptors and it's descriptor array // does not contain descriptors that do not belong to the map. @@ -8077,9 +9319,10 @@ Handle<Map> Map::CopyDropDescriptors(Handle<Map> map) { Handle<Map> result = RawCopy(map, map->instance_size()); // Please note instance_type and instance_size are set when allocated. - result->SetInObjectProperties(map->GetInObjectProperties()); - result->set_unused_property_fields(map->unused_property_fields()); - + if (map->IsJSObjectMap()) { + result->SetInObjectProperties(map->GetInObjectProperties()); + result->set_unused_property_fields(map->unused_property_fields()); + } result->ClearCodeCache(map->GetHeap()); map->NotifyLeafMapLayoutChange(); return result; @@ -8228,48 +9471,85 @@ Handle<Map> Map::CopyReplaceDescriptors( } -// Since this method is used to rewrite an existing transition tree, it can -// always insert transitions without checking. -Handle<Map> Map::CopyInstallDescriptors( - Handle<Map> map, int new_descriptor, Handle<DescriptorArray> descriptors, +// Creates transition tree starting from |split_map| and adding all descriptors +// starting from descriptor with index |split_map|.NumberOfOwnDescriptors(). +// The way how it is done is tricky because of GC and special descriptors +// marking logic. +Handle<Map> Map::AddMissingTransitions( + Handle<Map> split_map, Handle<DescriptorArray> descriptors, Handle<LayoutDescriptor> full_layout_descriptor) { DCHECK(descriptors->IsSortedNoDuplicates()); + int split_nof = split_map->NumberOfOwnDescriptors(); + int nof_descriptors = descriptors->number_of_descriptors(); + DCHECK_LT(split_nof, nof_descriptors); + + // Start with creating last map which will own full descriptors array. + // This is necessary to guarantee that GC will mark the whole descriptor + // array if any of the allocations happening below fail. + // Number of unused properties is temporarily incorrect and the layout + // descriptor could unnecessarily be in slow mode but we will fix after + // all the other intermediate maps are created. + Handle<Map> last_map = CopyDropDescriptors(split_map); + last_map->InitializeDescriptors(*descriptors, *full_layout_descriptor); + last_map->set_unused_property_fields(0); + + // During creation of intermediate maps we violate descriptors sharing + // invariant since the last map is not yet connected to the transition tree + // we create here. But it is safe because GC never trims map's descriptors + // if there are no dead transitions from that map and this is exactly the + // case for all the intermediate maps we create here. + Handle<Map> map = split_map; + for (int i = split_nof; i < nof_descriptors - 1; ++i) { + Handle<Map> new_map = CopyDropDescriptors(map); + InstallDescriptors(map, new_map, i, descriptors, full_layout_descriptor); + map = new_map; + } + map->NotifyLeafMapLayoutChange(); + InstallDescriptors(map, last_map, nof_descriptors - 1, descriptors, + full_layout_descriptor); + return last_map; +} - Handle<Map> result = CopyDropDescriptors(map); - result->set_instance_descriptors(*descriptors); - result->SetNumberOfOwnDescriptors(new_descriptor + 1); +// Since this method is used to rewrite an existing transition tree, it can +// always insert transitions without checking. +void Map::InstallDescriptors(Handle<Map> parent, Handle<Map> child, + int new_descriptor, + Handle<DescriptorArray> descriptors, + Handle<LayoutDescriptor> full_layout_descriptor) { + DCHECK(descriptors->IsSortedNoDuplicates()); - int unused_property_fields = map->unused_property_fields(); + child->set_instance_descriptors(*descriptors); + child->SetNumberOfOwnDescriptors(new_descriptor + 1); + + int unused_property_fields = parent->unused_property_fields(); PropertyDetails details = descriptors->GetDetails(new_descriptor); if (details.location() == kField) { - unused_property_fields = map->unused_property_fields() - 1; + unused_property_fields = parent->unused_property_fields() - 1; if (unused_property_fields < 0) { unused_property_fields += JSObject::kFieldsAdded; } } - result->set_unused_property_fields(unused_property_fields); + child->set_unused_property_fields(unused_property_fields); if (FLAG_unbox_double_fields) { Handle<LayoutDescriptor> layout_descriptor = - LayoutDescriptor::AppendIfFastOrUseFull(map, details, + LayoutDescriptor::AppendIfFastOrUseFull(parent, details, full_layout_descriptor); - result->set_layout_descriptor(*layout_descriptor); + child->set_layout_descriptor(*layout_descriptor); #ifdef VERIFY_HEAP // TODO(ishell): remove these checks from VERIFY_HEAP mode. if (FLAG_verify_heap) { - CHECK(result->layout_descriptor()->IsConsistentWithMap(*result)); + CHECK(child->layout_descriptor()->IsConsistentWithMap(*child)); } #else - SLOW_DCHECK(result->layout_descriptor()->IsConsistentWithMap(*result)); + SLOW_DCHECK(child->layout_descriptor()->IsConsistentWithMap(*child)); #endif - result->set_visitor_id(Heap::GetStaticVisitorIdForMap(*result)); + child->set_visitor_id(Heap::GetStaticVisitorIdForMap(*child)); } Handle<Name> name = handle(descriptors->GetKey(new_descriptor)); - ConnectTransition(map, result, name, SIMPLE_PROPERTY_TRANSITION); - - return result; + ConnectTransition(parent, child, name, SIMPLE_PROPERTY_TRANSITION); } @@ -8308,6 +9588,58 @@ Handle<Map> Map::CopyAsElementsKind(Handle<Map> map, ElementsKind kind, } +Handle<Map> Map::AsLanguageMode(Handle<Map> initial_map, + LanguageMode language_mode, FunctionKind kind) { + DCHECK_EQ(JS_FUNCTION_TYPE, initial_map->instance_type()); + // Initial map for sloppy mode function is stored in the function + // constructor. Initial maps for strict and strong modes are cached as + // special transitions using |strict_function_transition_symbol| and + // |strong_function_transition_symbol| respectively 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; + case STRONG: + transition_symbol = factory->strong_function_transition_symbol(); + break; + default: + UNREACHABLE(); + break; + } + Map* maybe_transition = + TransitionArray::SearchSpecial(*initial_map, *transition_symbol); + if (maybe_transition != NULL) { + return handle(maybe_transition, isolate); + } + initial_map->NotifyLeafMapLayoutChange(); + + // Create new map taking descriptors from the |function_map| and all + // the other details from the |initial_map|. + Handle<Map> map = + Map::CopyInitialMap(function_map, initial_map->instance_size(), + initial_map->GetInObjectProperties(), + initial_map->unused_property_fields()); + map->SetConstructor(initial_map->GetConstructor()); + map->set_prototype(initial_map->prototype()); + + if (TransitionArray::CanHaveMoreTransitions(initial_map)) { + Map::ConnectTransition(initial_map, map, transition_symbol, + SPECIAL_TRANSITION); + } + return map; +} + + Handle<Map> Map::CopyForObserved(Handle<Map> map) { DCHECK(!map->is_observed()); @@ -8427,25 +9759,6 @@ Handle<Map> Map::CopyForPreventExtensions(Handle<Map> map, } -Handle<Map> Map::FixProxy(Handle<Map> map, InstanceType type, int size) { - DCHECK(type == JS_OBJECT_TYPE || type == JS_FUNCTION_TYPE); - DCHECK(map->IsJSProxyMap()); - - Isolate* isolate = map->GetIsolate(); - - // Allocate fresh map. - // TODO(rossberg): Once we optimize proxies, cache these maps. - Handle<Map> new_map = isolate->factory()->NewMap(type, size); - - Handle<Object> prototype(map->prototype(), isolate); - Map::SetPrototype(new_map, prototype); - - map->NotifyLeafMapLayoutChange(); - - return new_map; -} - - bool DescriptorArray::CanHoldValue(int descriptor, Object* value) { PropertyDetails details = GetDetails(descriptor); switch (details.type()) { @@ -8726,7 +10039,6 @@ Handle<DescriptorArray> DescriptorArray::CopyUpToAddAttributes( Handle<DescriptorArray> descriptors = DescriptorArray::Allocate(desc->GetIsolate(), size, slack); - DescriptorArray::WhitenessWitness witness(*descriptors); if (attributes != NONE) { for (int i = 0; i < size; ++i) { @@ -8734,7 +10046,7 @@ Handle<DescriptorArray> DescriptorArray::CopyUpToAddAttributes( Name* key = desc->GetKey(i); PropertyDetails details = desc->GetDetails(i); // Bulk attribute changes never affect private properties. - if (!key->IsSymbol() || !Symbol::cast(key)->is_private()) { + if (!key->IsPrivate()) { int mask = DONT_DELETE | DONT_ENUM; // READ_ONLY is an invalid attribute for JS setters/getters. if (details.type() != ACCESSOR_CONSTANT || !value->IsAccessorPair()) { @@ -8745,11 +10057,11 @@ Handle<DescriptorArray> DescriptorArray::CopyUpToAddAttributes( } Descriptor inner_desc( handle(key), handle(value, desc->GetIsolate()), details); - descriptors->Set(i, &inner_desc, witness); + descriptors->SetDescriptor(i, &inner_desc); } } else { for (int i = 0; i < size; ++i) { - descriptors->CopyFrom(i, *desc, witness); + descriptors->CopyFrom(i, *desc); } } @@ -8759,6 +10071,22 @@ Handle<DescriptorArray> DescriptorArray::CopyUpToAddAttributes( } +bool DescriptorArray::IsEqualUpTo(DescriptorArray* desc, int nof_descriptors) { + for (int i = 0; i < nof_descriptors; i++) { + if (GetKey(i) != desc->GetKey(i) || GetValue(i) != desc->GetValue(i)) { + return false; + } + PropertyDetails details = GetDetails(i); + PropertyDetails other_details = desc->GetDetails(i); + if (details.type() != other_details.type() || + !details.representation().Equals(other_details.representation())) { + return false; + } + } + return true; +} + + Handle<Map> Map::CopyReplaceDescriptor(Handle<Map> map, Handle<DescriptorArray> descriptors, Descriptor* descriptor, @@ -9440,6 +10768,12 @@ Handle<ArrayList> ArrayList::Add(Handle<ArrayList> array, Handle<Object> obj1, } +bool ArrayList::IsFull() { + int capacity = length(); + return kFirstIndex + Length() == capacity; +} + + Handle<ArrayList> ArrayList::EnsureSpace(Handle<ArrayList> array, int length) { int capacity = array->length(); bool empty = (capacity == 0); @@ -9465,7 +10799,7 @@ Handle<DescriptorArray> DescriptorArray::Allocate(Isolate* isolate, int size = number_of_descriptors + slack; if (size == 0) return factory->empty_descriptor_array(); // Allocate the array of keys. - Handle<FixedArray> result = factory->NewFixedArray(LengthFor(size)); + Handle<FixedArray> result = factory->NewFixedArray(LengthFor(size), TENURED); result->set(kDescriptorLengthIndex, Smi::FromInt(number_of_descriptors)); result->set(kEnumCacheIndex, Smi::FromInt(0)); @@ -9508,21 +10842,16 @@ void DescriptorArray::SetEnumCache(Handle<DescriptorArray> descriptors, } -void DescriptorArray::CopyFrom(int index, DescriptorArray* src, - const WhitenessWitness& witness) { +void DescriptorArray::CopyFrom(int index, DescriptorArray* src) { Object* value = src->GetValue(index); PropertyDetails details = src->GetDetails(index); Descriptor desc(handle(src->GetKey(index)), handle(value, src->GetIsolate()), details); - Set(index, &desc, witness); + SetDescriptor(index, &desc); } -// We need the whiteness witness since sort will reshuffle the entries in the -// descriptor array. If the descriptor array were to be black, the shuffling -// would move a slot that was already recorded as pointing into an evacuation -// candidate. This would result in missing updates upon evacuation. void DescriptorArray::Sort() { // In-place heap sort. int len = number_of_descriptors(); @@ -9628,47 +10957,6 @@ Handle<LiteralsArray> LiteralsArray::New(Isolate* isolate, } -// static -Handle<BindingsArray> BindingsArray::New(Isolate* isolate, - Handle<TypeFeedbackVector> vector, - Handle<JSReceiver> bound_function, - Handle<Object> bound_this, - int number_of_bindings) { - Handle<FixedArray> bindings = isolate->factory()->NewFixedArray( - number_of_bindings + kFirstBindingIndex); - Handle<BindingsArray> casted_bindings = Handle<BindingsArray>::cast(bindings); - casted_bindings->set_feedback_vector(*vector); - casted_bindings->set_bound_function(*bound_function); - casted_bindings->set_bound_this(*bound_this); - return casted_bindings; -} - - -// static -Handle<JSArray> BindingsArray::CreateBoundArguments( - Handle<BindingsArray> bindings) { - int bound_argument_count = bindings->bindings_count(); - Factory* factory = bindings->GetIsolate()->factory(); - Handle<FixedArray> arguments = factory->NewFixedArray(bound_argument_count); - bindings->CopyTo(kFirstBindingIndex, *arguments, 0, bound_argument_count); - return factory->NewJSArrayWithElements(arguments); -} - - -// static -Handle<JSArray> BindingsArray::CreateRuntimeBindings( - Handle<BindingsArray> bindings) { - Factory* factory = bindings->GetIsolate()->factory(); - // A runtime bindings array consists of - // [bound function, bound this, [arg0, arg1, ...]]. - Handle<FixedArray> runtime_bindings = - factory->NewFixedArray(2 + bindings->bindings_count()); - bindings->CopyTo(kBoundFunctionIndex, *runtime_bindings, 0, - 2 + bindings->bindings_count()); - return factory->NewJSArrayWithElements(runtime_bindings); -} - - int HandlerTable::LookupRange(int pc_offset, int* stack_depth_out, CatchPrediction* prediction_out) { int innermost_handler = -1, innermost_start = -1; @@ -10336,8 +11624,10 @@ static void CalculateLineEndsImpl(Isolate* isolate, if (src_len > 0 && cache->IsLineTerminatorSequence(src[src_len - 1], 0)) { line_ends->Add(src_len - 1); - } else if (include_ending_line) { - // Even if the last line misses a line end, it is counted. + } + if (include_ending_line) { + // Include one character beyond the end of script. The rewriter uses that + // position for the implicit return statement. line_ends->Add(src_len); } } @@ -10933,12 +12223,6 @@ void String::PrintOn(FILE* file) { } -inline static uint32_t ObjectAddressForHashing(Object* object) { - uint32_t value = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(object)); - return value & MemoryChunk::kAlignmentMask; -} - - int Map::Hash() { // For performance reasons we only hash the 3 most variable fields of a map: // constructor, prototype and bit_field2. For predictability reasons we @@ -10973,7 +12257,15 @@ bool CheckEquivalent(Map* first, Map* second) { bool Map::EquivalentToForTransition(Map* other) { - return CheckEquivalent(this, other); + if (!CheckEquivalent(this, other)) return false; + if (instance_type() == JS_FUNCTION_TYPE) { + // JSFunctions require more checks to ensure that sloppy function is + // not equvalent to strict function. + int nof = Min(NumberOfOwnDescriptors(), other->NumberOfOwnDescriptors()); + return instance_descriptors()->IsEqualUpTo(other->instance_descriptors(), + nof); + } + return true; } @@ -11054,14 +12346,19 @@ void SharedFunctionInfo::AddSharedCodeToOptimizedCodeMap( Isolate* isolate = shared->GetIsolate(); if (isolate->serializer_enabled()) return; DCHECK(code->kind() == Code::OPTIMIZED_FUNCTION); - Handle<Object> value(shared->optimized_code_map(), isolate); - if (value->IsSmi()) return; // Empty code maps are unsupported. - Handle<FixedArray> code_map = Handle<FixedArray>::cast(value); - code_map->set(kSharedCodeIndex, *code); + // Empty code maps are unsupported. + if (!shared->OptimizedCodeMapIsCleared()) { + Handle<WeakCell> cell = isolate->factory()->NewWeakCell(code); + // A collection may have occured and cleared the optimized code map in the + // allocation above. + if (!shared->OptimizedCodeMapIsCleared()) { + shared->optimized_code_map()->set(kSharedCodeIndex, *cell); + } + } } -void SharedFunctionInfo::AddToOptimizedCodeMap( +void SharedFunctionInfo::AddToOptimizedCodeMapInternal( Handle<SharedFunctionInfo> shared, Handle<Context> native_context, Handle<HeapObject> code, Handle<LiteralsArray> literals, BailoutId osr_ast_id) { @@ -11074,86 +12371,110 @@ void SharedFunctionInfo::AddToOptimizedCodeMap( DCHECK(native_context->IsNativeContext()); STATIC_ASSERT(kEntryLength == 4); Handle<FixedArray> new_code_map; - Handle<Object> value(shared->optimized_code_map(), isolate); int entry; - if (value->IsSmi()) { - // No optimized code map. - DCHECK_EQ(0, Smi::cast(*value)->value()); + + if (shared->OptimizedCodeMapIsCleared()) { new_code_map = isolate->factory()->NewFixedArray(kInitialLength, TENURED); + new_code_map->set(kSharedCodeIndex, *isolate->factory()->empty_weak_cell(), + SKIP_WRITE_BARRIER); entry = kEntriesStart; } else { - Handle<FixedArray> old_code_map = Handle<FixedArray>::cast(value); + Handle<FixedArray> old_code_map(shared->optimized_code_map(), isolate); entry = shared->SearchOptimizedCodeMapEntry(*native_context, osr_ast_id); if (entry > kSharedCodeIndex) { - // Found an existing context-specific entry, it must not contain any code. - DCHECK_EQ(isolate->heap()->undefined_value(), - old_code_map->get(entry + kCachedCodeOffset)); + // Found an existing context-specific entry. If the user provided valid + // code, it must not contain any code. + DCHECK(code->IsUndefined() || + WeakCell::cast(old_code_map->get(entry + kCachedCodeOffset)) + ->cleared()); + // Just set the code and literals to the entry. - old_code_map->set(entry + kCachedCodeOffset, *code); - old_code_map->set(entry + kLiteralsOffset, *literals); + if (!code->IsUndefined()) { + Handle<WeakCell> code_cell = isolate->factory()->NewWeakCell(code); + old_code_map->set(entry + kCachedCodeOffset, *code_cell); + } + Handle<WeakCell> literals_cell = + isolate->factory()->NewWeakCell(literals); + old_code_map->set(entry + kLiteralsOffset, *literals_cell); return; } - // Copy old optimized code map and append one new entry. - new_code_map = isolate->factory()->CopyFixedArrayAndGrow( - old_code_map, kEntryLength, TENURED); - // TODO(mstarzinger): Temporary workaround. The allocation above might have - // flushed the optimized code map and the copy we created is full of holes. - // For now we just give up on adding the entry and pretend it got flushed. - if (shared->optimized_code_map()->IsSmi()) return; - entry = old_code_map->length(); + // Can we reuse an entry? + DCHECK(entry < kEntriesStart); + int length = old_code_map->length(); + for (int i = kEntriesStart; i < length; i += kEntryLength) { + if (WeakCell::cast(old_code_map->get(i + kContextOffset))->cleared()) { + new_code_map = old_code_map; + entry = i; + break; + } + } + + if (entry < kEntriesStart) { + // Copy old optimized code map and append one new entry. + new_code_map = isolate->factory()->CopyFixedArrayAndGrow( + old_code_map, kEntryLength, TENURED); + // TODO(mstarzinger): Temporary workaround. The allocation above might + // have flushed the optimized code map and the copy we created is full of + // holes. For now we just give up on adding the entry and pretend it got + // flushed. + if (shared->OptimizedCodeMapIsCleared()) return; + entry = old_code_map->length(); + } } - new_code_map->set(entry + kContextOffset, *native_context); - new_code_map->set(entry + kCachedCodeOffset, *code); - new_code_map->set(entry + kLiteralsOffset, *literals); + + Handle<WeakCell> code_cell = code->IsUndefined() + ? isolate->factory()->empty_weak_cell() + : isolate->factory()->NewWeakCell(code); + Handle<WeakCell> literals_cell = isolate->factory()->NewWeakCell(literals); + WeakCell* context_cell = native_context->self_weak_cell(); + + new_code_map->set(entry + kContextOffset, context_cell); + new_code_map->set(entry + kCachedCodeOffset, *code_cell); + new_code_map->set(entry + kLiteralsOffset, *literals_cell); new_code_map->set(entry + kOsrAstIdOffset, Smi::FromInt(osr_ast_id.ToInt())); #ifdef DEBUG for (int i = kEntriesStart; i < new_code_map->length(); i += kEntryLength) { - DCHECK(new_code_map->get(i + kContextOffset)->IsNativeContext()); - Object* code = new_code_map->get(i + kCachedCodeOffset); - if (code != isolate->heap()->undefined_value()) { - DCHECK(code->IsCode()); - DCHECK(Code::cast(code)->kind() == Code::OPTIMIZED_FUNCTION); - } - DCHECK(new_code_map->get(i + kLiteralsOffset)->IsFixedArray()); + WeakCell* cell = WeakCell::cast(new_code_map->get(i + kContextOffset)); + DCHECK(cell->cleared() || cell->value()->IsNativeContext()); + cell = WeakCell::cast(new_code_map->get(i + kCachedCodeOffset)); + DCHECK(cell->cleared() || + (cell->value()->IsCode() && + Code::cast(cell->value())->kind() == Code::OPTIMIZED_FUNCTION)); + cell = WeakCell::cast(new_code_map->get(i + kLiteralsOffset)); + DCHECK(cell->cleared() || cell->value()->IsFixedArray()); DCHECK(new_code_map->get(i + kOsrAstIdOffset)->IsSmi()); } #endif - // Zap any old optimized code map. - if (!shared->optimized_code_map()->IsSmi()) { - FixedArray* old_code_map = FixedArray::cast(shared->optimized_code_map()); - old_code_map->FillWithHoles(0, old_code_map->length()); + FixedArray* old_code_map = shared->optimized_code_map(); + if (old_code_map != *new_code_map) { + shared->set_optimized_code_map(*new_code_map); } - - shared->set_optimized_code_map(*new_code_map); } void SharedFunctionInfo::ClearOptimizedCodeMap() { - // Zap any old optimized code map. - if (!optimized_code_map()->IsSmi()) { - FixedArray* old_code_map = FixedArray::cast(optimized_code_map()); - old_code_map->FillWithHoles(0, old_code_map->length()); - } - - set_optimized_code_map(Smi::FromInt(0)); + FixedArray* cleared_map = GetHeap()->cleared_optimized_code_map(); + set_optimized_code_map(cleared_map, SKIP_WRITE_BARRIER); } void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code, const char* reason) { DisallowHeapAllocation no_gc; - if (optimized_code_map()->IsSmi()) return; + if (OptimizedCodeMapIsCleared()) return; Heap* heap = GetHeap(); - FixedArray* code_map = FixedArray::cast(optimized_code_map()); + FixedArray* code_map = optimized_code_map(); int dst = kEntriesStart; int length = code_map->length(); for (int src = kEntriesStart; src < length; src += kEntryLength) { - DCHECK(code_map->get(src)->IsNativeContext()); - if (code_map->get(src + kCachedCodeOffset) == optimized_code) { + DCHECK(WeakCell::cast(code_map->get(src))->cleared() || + WeakCell::cast(code_map->get(src))->value()->IsNativeContext()); + if (WeakCell::cast(code_map->get(src + kCachedCodeOffset))->value() == + optimized_code) { BailoutId osr(Smi::cast(code_map->get(src + kOsrAstIdOffset))->value()); if (FLAG_trace_opt) { PrintF("[evicting entry from optimizing code map (%s) for ", reason); @@ -11170,7 +12491,8 @@ void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code, } // In case of non-OSR entry just clear the code in order to proceed // sharing literals. - code_map->set_undefined(src + kCachedCodeOffset); + code_map->set(src + kCachedCodeOffset, heap->empty_weak_cell(), + SKIP_WRITE_BARRIER); } // Keep the src entry by copying it to the dst entry. @@ -11185,9 +12507,11 @@ void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code, } dst += kEntryLength; } - if (code_map->get(kSharedCodeIndex) == optimized_code) { + if (WeakCell::cast(code_map->get(kSharedCodeIndex))->value() == + optimized_code) { // Evict context-independent code as well. - code_map->set_undefined(kSharedCodeIndex); + code_map->set(kSharedCodeIndex, heap->empty_weak_cell(), + SKIP_WRITE_BARRIER); if (FLAG_trace_opt) { PrintF("[evicting entry from optimizing code map (%s) for ", reason); ShortPrint(); @@ -11199,7 +12523,7 @@ void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code, heap->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>(code_map, length - dst); if (code_map->length() == kEntriesStart && - code_map->get(kSharedCodeIndex)->IsUndefined()) { + WeakCell::cast(code_map->get(kSharedCodeIndex))->cleared()) { ClearOptimizedCodeMap(); } } @@ -11207,14 +12531,14 @@ void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code, void SharedFunctionInfo::TrimOptimizedCodeMap(int shrink_by) { - FixedArray* code_map = FixedArray::cast(optimized_code_map()); + FixedArray* code_map = optimized_code_map(); DCHECK(shrink_by % kEntryLength == 0); DCHECK(shrink_by <= code_map->length() - kEntriesStart); // Always trim even when array is cleared because of heap verifier. GetHeap()->RightTrimFixedArray<Heap::SEQUENTIAL_TO_SWEEPER>(code_map, shrink_by); if (code_map->length() == kEntriesStart && - code_map->get(kSharedCodeIndex)->IsUndefined()) { + WeakCell::cast(code_map->get(kSharedCodeIndex))->cleared()) { ClearOptimizedCodeMap(); } } @@ -11239,18 +12563,11 @@ static void ShrinkInstanceSize(Map* map, void* data) { } -void JSFunction::CompleteInobjectSlackTracking() { - DCHECK(has_initial_map()); - initial_map()->CompleteInobjectSlackTracking(); -} - - void Map::CompleteInobjectSlackTracking() { // Has to be an initial map. DCHECK(GetBackPointer()->IsUndefined()); - DCHECK_GE(counter(), kSlackTrackingCounterEnd - 1); - set_counter(kRetainingCounterStart); + set_construction_counter(kNoSlackTracking); int slack = unused_property_fields(); TransitionArray::TraverseTransitionTree(this, &GetMinInobjectSlack, &slack); @@ -11284,7 +12601,6 @@ static bool PrototypeBenefitsFromNormalization(Handle<JSObject> object) { void JSObject::OptimizeAsPrototype(Handle<JSObject> object, PrototypeOptimizationMode mode) { if (object->IsJSGlobalObject()) return; - if (object->IsJSGlobalProxy()) return; if (mode == FAST_PROTOTYPE && PrototypeBenefitsFromNormalization(object)) { // First normalize to ensure all JSFunctions are DATA_CONSTANT. JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0, @@ -11310,13 +12626,9 @@ void JSObject::OptimizeAsPrototype(Handle<JSObject> object, Isolate* isolate = object->GetIsolate(); if (!constructor->shared()->IsApiFunction() && object->class_name() == isolate->heap()->Object_string()) { - Handle<String> constructor_name(object->constructor_name(), isolate); Context* context = constructor->context()->native_context(); JSFunction* object_function = context->object_function(); object->map()->SetConstructor(object_function); - Handle<PrototypeInfo> proto_info = - Map::GetOrCreatePrototypeInfo(object, isolate); - proto_info->set_constructor_name(*constructor_name); } } } @@ -11346,7 +12658,6 @@ void JSObject::LazyRegisterPrototypeUser(Handle<Map> user, Isolate* isolate) { break; } Handle<Object> maybe_proto = PrototypeIterator::GetCurrent(iter); - if (maybe_proto->IsJSGlobalProxy()) continue; // Proxies on the prototype chain are not supported. They make it // impossible to make any assumptions about the prototype chain anyway. if (maybe_proto->IsJSProxy()) return; @@ -11381,17 +12692,18 @@ bool JSObject::UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate) { DCHECK(user->is_prototype_map()); // If it doesn't have a PrototypeInfo, it was never registered. if (!user->prototype_info()->IsPrototypeInfo()) return false; - // If it doesn't have a prototype, it can't be registered. - if (!user->prototype()->IsJSObject()) return false; + // If it had no prototype before, see if it had users that might expect + // registration. + if (!user->prototype()->IsJSObject()) { + Object* users = + PrototypeInfo::cast(user->prototype_info())->prototype_users(); + return users->IsWeakFixedArray(); + } Handle<JSObject> prototype(JSObject::cast(user->prototype()), isolate); Handle<PrototypeInfo> user_info = Map::GetOrCreatePrototypeInfo(user, isolate); int slot = user_info->registry_slot(); if (slot == PrototypeInfo::UNREGISTERED) return false; - if (prototype->IsJSGlobalProxy()) { - PrototypeIterator iter(isolate, prototype); - prototype = PrototypeIterator::GetCurrent<JSObject>(iter); - } DCHECK(prototype->map()->is_prototype_map()); Object* maybe_proto_info = prototype->map()->prototype_info(); // User knows its registry slot, prototype info and user registry must exist. @@ -11440,10 +12752,6 @@ static void InvalidatePrototypeChainsInternal(Map* map) { void JSObject::InvalidatePrototypeChains(Map* map) { if (!FLAG_eliminate_prototype_chain_checks) return; DisallowHeapAllocation no_gc; - if (map->IsJSGlobalProxyMap()) { - PrototypeIterator iter(map); - map = iter.GetCurrent<JSObject>()->map(); - } InvalidatePrototypeChainsInternal(map); } @@ -11480,10 +12788,6 @@ Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map, Handle<Object> maybe_prototype(map->prototype(), isolate); if (!maybe_prototype->IsJSObject()) return Handle<Cell>::null(); Handle<JSObject> prototype = Handle<JSObject>::cast(maybe_prototype); - if (prototype->IsJSGlobalProxy()) { - PrototypeIterator iter(isolate, prototype); - prototype = PrototypeIterator::GetCurrent<JSObject>(iter); - } // Ensure the prototype is registered with its own prototypes so its cell // will be invalidated when necessary. JSObject::LazyRegisterPrototypeUser(handle(prototype->map(), isolate), @@ -11523,33 +12827,26 @@ Handle<Object> CacheInitialJSArrayMaps( Handle<Context> native_context, Handle<Map> initial_map) { // Replace all of the cached initial array maps in the native context with // the appropriate transitioned elements kind maps. - Factory* factory = native_context->GetIsolate()->factory(); - Handle<FixedArray> maps = factory->NewFixedArrayWithHoles( - kElementsKindCount, TENURED); - + Strength strength = + initial_map->is_strong() ? Strength::STRONG : Strength::WEAK; Handle<Map> current_map = initial_map; ElementsKind kind = current_map->elements_kind(); - DCHECK(kind == GetInitialFastElementsKind()); - maps->set(kind, *current_map); + DCHECK_EQ(GetInitialFastElementsKind(), kind); + native_context->set(Context::ArrayMapIndex(kind, strength), *current_map); for (int i = GetSequenceIndexFromFastElementsKind(kind) + 1; i < kFastElementsKindCount; ++i) { Handle<Map> new_map; ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(i); - Map* maybe_elements_transition = current_map->ElementsTransitionMap(); - if (maybe_elements_transition != NULL) { + if (Map* maybe_elements_transition = current_map->ElementsTransitionMap()) { new_map = handle(maybe_elements_transition); - DCHECK(new_map->elements_kind() == next_kind); } else { new_map = Map::CopyAsElementsKind( current_map, next_kind, INSERT_TRANSITION); } - maps->set(next_kind, *new_map); + DCHECK_EQ(next_kind, new_map->elements_kind()); + native_context->set(Context::ArrayMapIndex(next_kind, strength), *new_map); current_map = new_map; } - if (initial_map->is_strong()) - native_context->set_js_array_strong_maps(*maps); - else - native_context->set_js_array_maps(*maps); return initial_map; } @@ -11567,9 +12864,7 @@ void JSFunction::SetInstancePrototype(Handle<JSFunction> function, // copy containing the new prototype. Also complete any in-object // slack tracking that is in progress at this point because it is // still tracking the old copy. - if (function->IsInobjectSlackTrackingInProgress()) { - function->CompleteInobjectSlackTracking(); - } + function->CompleteInobjectSlackTrackingIfActive(); Handle<Map> initial_map(function->initial_map(), isolate); @@ -11689,10 +12984,85 @@ void JSFunction::SetInitialMap(Handle<JSFunction> function, Handle<Map> map, } +#ifdef DEBUG +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_ARRAY_BUFFER_TYPE: + case JS_TYPED_ARRAY_TYPE: + case JS_DATA_VIEW_TYPE: + case JS_SET_TYPE: + case JS_MAP_TYPE: + case JS_SET_ITERATOR_TYPE: + case JS_MAP_ITERATOR_TYPE: + case JS_ITERATOR_RESULT_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 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 HEAP_NUMBER_TYPE: + case MUTABLE_HEAP_NUMBER_TYPE: + case SIMD128_VALUE_TYPE: + case FILLER_TYPE: + case BYTE_ARRAY_TYPE: + case FREE_SPACE_TYPE: + case SHARED_FUNCTION_INFO_TYPE: + +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + case FIXED_##TYPE##_ARRAY_TYPE: +#undef TYPED_ARRAY_CASE + +#define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE: + STRUCT_LIST(MAKE_STRUCT_CASE) +#undef MAKE_STRUCT_CASE + // We must not end up here for these instance types at all. + UNREACHABLE(); + // Fall through. + default: + return false; + } +} + +} // namespace +#endif + + void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) { + DCHECK(function->IsConstructor() || function->shared()->is_generator()); if (function->has_initial_map()) return; Isolate* isolate = function->GetIsolate(); + // The constructor should be compiled for the optimization hints to be + // available. + Compiler::Compile(function, CLEAR_EXCEPTION); + // First create a new map with the size and number of in-object properties // suggested by the function. InstanceType instance_type; @@ -11725,78 +13095,109 @@ void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) { // Finally link initial map and constructor function. DCHECK(prototype->IsJSReceiver()); JSFunction::SetInitialMap(function, map, prototype); - - if (!function->shared()->is_generator()) { - function->StartInobjectSlackTracking(); - } + map->StartInobjectSlackTracking(); } -Handle<Map> JSFunction::EnsureDerivedHasInitialMap( - Handle<JSFunction> original_constructor, Handle<JSFunction> constructor) { - DCHECK(constructor->has_initial_map()); - Isolate* isolate = constructor->GetIsolate(); - Handle<Map> constructor_initial_map(constructor->initial_map(), isolate); - if (*original_constructor == *constructor) return constructor_initial_map; - if (original_constructor->has_initial_map()) { - // Check that |original_constructor|'s initial map still in sync with - // the |constructor|, otherwise we must create a new initial map for - // |original_constructor|. - if (original_constructor->initial_map()->GetConstructor() == *constructor) { - return handle(original_constructor->initial_map(), isolate); - } - } - - // First create a new map with the size and number of in-object properties - // suggested by the function. - DCHECK(!original_constructor->shared()->is_generator()); - DCHECK(!constructor->shared()->is_generator()); +// static +MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate, + Handle<JSFunction> constructor, + Handle<JSReceiver> new_target) { + EnsureHasInitialMap(constructor); - // Fetch or allocate prototype. + Handle<Map> constructor_initial_map(constructor->initial_map(), isolate); + if (*new_target == *constructor) return constructor_initial_map; + + // Fast case, new.target is a subclass of constructor. The map is cacheable + // (and may already have been cached). new.target.prototype is guaranteed to + // be a JSReceiver. + if (new_target->IsJSFunction()) { + Handle<JSFunction> function = Handle<JSFunction>::cast(new_target); + + // Check that |function|'s initial map still in sync with the |constructor|, + // otherwise we must create a new initial map for |function|. + if (function->has_initial_map() && + function->initial_map()->GetConstructor() == *constructor) { + return handle(function->initial_map(), isolate); + } + + // Create a new map with the size and number of in-object properties + // suggested by |function|. + + // Link initial map and constructor function if the new.target is actually a + // subclass constructor. + if (IsSubclassConstructor(function->shared()->kind())) { + Handle<Object> prototype(function->instance_prototype(), isolate); + InstanceType instance_type = constructor_initial_map->instance_type(); + DCHECK(CanSubclassHaveInobjectProperties(instance_type)); + int internal_fields = + JSObject::GetInternalFieldCount(*constructor_initial_map); + int pre_allocated = constructor_initial_map->GetInObjectProperties() - + constructor_initial_map->unused_property_fields(); + int instance_size; + int in_object_properties; + function->CalculateInstanceSizeForDerivedClass( + instance_type, internal_fields, &instance_size, + &in_object_properties); + + int unused_property_fields = in_object_properties - pre_allocated; + Handle<Map> map = + Map::CopyInitialMap(constructor_initial_map, instance_size, + in_object_properties, unused_property_fields); + map->set_new_target_is_base(false); + + JSFunction::SetInitialMap(function, map, prototype); + map->SetConstructor(*constructor); + map->StartInobjectSlackTracking(); + return map; + } + } + + // Slow path, new.target is either a proxy or can't cache the map. + // new.target.prototype is not guaranteed to be a JSReceiver, and may need to + // fall back to the intrinsicDefaultProto. Handle<Object> prototype; - if (original_constructor->has_instance_prototype()) { - prototype = handle(original_constructor->instance_prototype(), isolate); + if (new_target->IsJSFunction()) { + Handle<JSFunction> function = Handle<JSFunction>::cast(new_target); + // Make sure the new.target.prototype is cached. + EnsureHasInitialMap(function); + prototype = handle(function->prototype(), isolate); } else { - prototype = isolate->factory()->NewFunctionPrototype(original_constructor); - } - - // Finally link initial map and constructor function if the original - // constructor is actually a subclass constructor. - if (IsSubclassConstructor(original_constructor->shared()->kind())) { -// TODO(ishell): v8:4531, allow ES6 built-ins subclasses to have -// in-object properties. -#if 0 - InstanceType instance_type = constructor_initial_map->instance_type(); - int internal_fields = - JSObject::GetInternalFieldCount(*constructor_initial_map); - int pre_allocated = constructor_initial_map->GetInObjectProperties() - - constructor_initial_map->unused_property_fields(); - int instance_size; - int in_object_properties; - original_constructor->CalculateInstanceSizeForDerivedClass( - instance_type, internal_fields, &instance_size, &in_object_properties); - - int unused_property_fields = in_object_properties - pre_allocated; - Handle<Map> map = - Map::CopyInitialMap(constructor_initial_map, instance_size, - in_object_properties, unused_property_fields); -#endif - Handle<Map> map = Map::CopyInitialMap(constructor_initial_map); - - JSFunction::SetInitialMap(original_constructor, map, prototype); - map->SetConstructor(*constructor); - original_constructor->StartInobjectSlackTracking(); - return map; - - } else { - Handle<Map> map = Map::CopyInitialMap(constructor_initial_map); - DCHECK(prototype->IsJSReceiver()); - if (map->prototype() != *prototype) { - Map::SetPrototype(map, prototype, FAST_PROTOTYPE); - } - map->SetConstructor(*constructor); - return map; + Handle<String> prototype_string = isolate->factory()->prototype_string(); + ASSIGN_RETURN_ON_EXCEPTION( + isolate, prototype, + JSReceiver::GetProperty(new_target, prototype_string), Map); + // The above prototype lookup might change the constructor and its + // prototype, hence we have to reload the initial map. + EnsureHasInitialMap(constructor); + constructor_initial_map = handle(constructor->initial_map(), isolate); + } + + // If prototype is not a JSReceiver, fetch the intrinsicDefaultProto from the + // correct realm. Rather than directly fetching the .prototype, we fetch the + // constructor that points to the .prototype. This relies on + // constructor.prototype being FROZEN for those constructors. + if (!prototype->IsJSReceiver()) { + Handle<Context> context; + ASSIGN_RETURN_ON_EXCEPTION(isolate, context, + JSReceiver::GetFunctionRealm(new_target), Map); + DCHECK(context->IsNativeContext()); + Handle<Object> maybe_index = JSReceiver::GetDataProperty( + constructor, isolate->factory()->native_context_index_symbol()); + int index = maybe_index->IsSmi() ? Smi::cast(*maybe_index)->value() + : Context::OBJECT_FUNCTION_INDEX; + Handle<JSFunction> realm_constructor(JSFunction::cast(context->get(index))); + prototype = handle(realm_constructor->prototype(), isolate); + } + + Handle<Map> map = Map::CopyInitialMap(constructor_initial_map); + map->set_new_target_is_base(false); + DCHECK(prototype->IsJSReceiver()); + if (map->prototype() != *prototype) { + Map::SetPrototype(map, prototype, FAST_PROTOTYPE); } + map->SetConstructor(*constructor); + return map; } @@ -11843,7 +13244,7 @@ bool JSFunction::PassesFilter(const char* raw_filter) { } -Handle<String> JSFunction::GetDebugName(Handle<JSFunction> function) { +Handle<String> JSFunction::GetName(Handle<JSFunction> function) { Isolate* isolate = function->GetIsolate(); Handle<Object> name = JSReceiver::GetDataProperty(function, isolate->factory()->name_string()); @@ -11852,6 +13253,94 @@ Handle<String> JSFunction::GetDebugName(Handle<JSFunction> function) { } +Handle<String> JSFunction::GetDebugName(Handle<JSFunction> function) { + Isolate* isolate = function->GetIsolate(); + Handle<Object> name = JSReceiver::GetDataProperty( + function, isolate->factory()->display_name_string()); + if (name->IsString()) return Handle<String>::cast(name); + return JSFunction::GetName(function); +} + + +namespace { + +char const kNativeCodeSource[] = "function () { [native code] }"; + + +Handle<String> NativeCodeFunctionSourceString( + Handle<SharedFunctionInfo> shared_info) { + Isolate* const isolate = shared_info->GetIsolate(); + if (shared_info->name()->IsString()) { + IncrementalStringBuilder builder(isolate); + builder.AppendCString("function "); + builder.AppendString(handle(String::cast(shared_info->name()), isolate)); + builder.AppendCString("() { [native code] }"); + return builder.Finish().ToHandleChecked(); + } + return isolate->factory()->NewStringFromAsciiChecked(kNativeCodeSource); +} + +} // namespace + + +// static +Handle<String> JSBoundFunction::ToString(Handle<JSBoundFunction> function) { + Isolate* const isolate = function->GetIsolate(); + return isolate->factory()->NewStringFromAsciiChecked(kNativeCodeSource); +} + + +// static +Handle<String> JSFunction::ToString(Handle<JSFunction> function) { + Isolate* const isolate = function->GetIsolate(); + Handle<SharedFunctionInfo> shared_info(function->shared(), isolate); + + // Check if {function} should hide its source code. + if (!shared_info->script()->IsScript() || + Script::cast(shared_info->script())->hide_source()) { + return NativeCodeFunctionSourceString(shared_info); + } + + // Check if we should print {function} as a class. + Handle<Object> class_start_position = JSReceiver::GetDataProperty( + function, isolate->factory()->class_start_position_symbol()); + if (class_start_position->IsSmi()) { + Handle<Object> class_end_position = JSReceiver::GetDataProperty( + function, isolate->factory()->class_end_position_symbol()); + Handle<String> script_source( + String::cast(Script::cast(shared_info->script())->source()), isolate); + return isolate->factory()->NewSubString( + script_source, Handle<Smi>::cast(class_start_position)->value(), + Handle<Smi>::cast(class_end_position)->value()); + } + + // Check if we have source code for the {function}. + if (!shared_info->HasSourceCode()) { + return NativeCodeFunctionSourceString(shared_info); + } + + IncrementalStringBuilder builder(isolate); + if (!shared_info->is_arrow()) { + if (shared_info->is_concise_method()) { + if (shared_info->is_generator()) builder.AppendCharacter('*'); + } else { + if (shared_info->is_generator()) { + builder.AppendCString("function* "); + } else { + builder.AppendCString("function "); + } + } + if (shared_info->name_should_print_as_anonymous()) { + builder.AppendCString("anonymous"); + } else { + builder.AppendString(handle(String::cast(shared_info->name()), isolate)); + } + } + builder.AppendString(Handle<String>::cast(shared_info->GetSourceCode())); + return builder.Finish().ToHandleChecked(); +} + + void Oddball::Initialize(Isolate* isolate, Handle<Oddball> oddball, const char* to_string, Handle<Object> to_number, const char* type_of, byte kind) { @@ -12295,6 +13784,10 @@ void SharedFunctionInfo::InitFromFunctionLiteral( shared_info->set_dont_crankshaft(lit->flags() & AstProperties::kDontCrankshaft); shared_info->set_kind(lit->kind()); + if (!IsConstructable(lit->kind(), lit->language_mode())) { + shared_info->set_construct_stub( + *shared_info->GetIsolate()->builtins()->ConstructedNonConstructable()); + } shared_info->set_needs_home_object(lit->scope()->NeedsHomeObject()); shared_info->set_asm_function(lit->scope()->asm_function()); } @@ -12311,18 +13804,16 @@ bool SharedFunctionInfo::VerifyBailoutId(BailoutId id) { } -void JSFunction::StartInobjectSlackTracking() { - DCHECK(has_initial_map() && !IsInobjectSlackTrackingInProgress()); - - Map* map = initial_map(); +void Map::StartInobjectSlackTracking() { + DCHECK(!IsInobjectSlackTrackingInProgress()); // No tracking during the snapshot construction phase. Isolate* isolate = GetIsolate(); if (isolate->serializer_enabled()) return; - if (map->unused_property_fields() == 0) return; + if (unused_property_fields() == 0) return; - map->set_counter(Map::kSlackTrackingCounterStart); + set_construction_counter(Map::kSlackTrackingCounterStart); } @@ -12349,18 +13840,19 @@ int SharedFunctionInfo::SearchOptimizedCodeMapEntry(Context* native_context, BailoutId osr_ast_id) { DisallowHeapAllocation no_gc; DCHECK(native_context->IsNativeContext()); - Object* value = optimized_code_map(); - if (!value->IsSmi()) { - FixedArray* optimized_code_map = FixedArray::cast(value); + if (!OptimizedCodeMapIsCleared()) { + FixedArray* optimized_code_map = this->optimized_code_map(); int length = optimized_code_map->length(); Smi* osr_ast_id_smi = Smi::FromInt(osr_ast_id.ToInt()); for (int i = kEntriesStart; i < length; i += kEntryLength) { - if (optimized_code_map->get(i + kContextOffset) == native_context && + if (WeakCell::cast(optimized_code_map->get(i + kContextOffset)) + ->value() == native_context && optimized_code_map->get(i + kOsrAstIdOffset) == osr_ast_id_smi) { return i; } } - Object* shared_code = optimized_code_map->get(kSharedCodeIndex); + Object* shared_code = + WeakCell::cast(optimized_code_map->get(kSharedCodeIndex))->value(); if (shared_code->IsCode() && osr_ast_id.IsNone()) { return kSharedCodeIndex; } @@ -12374,18 +13866,27 @@ CodeAndLiterals SharedFunctionInfo::SearchOptimizedCodeMap( CodeAndLiterals result = {nullptr, nullptr}; int entry = SearchOptimizedCodeMapEntry(native_context, osr_ast_id); if (entry != kNotFound) { - FixedArray* code_map = FixedArray::cast(optimized_code_map()); + FixedArray* code_map = optimized_code_map(); if (entry == kSharedCodeIndex) { - result = {Code::cast(code_map->get(kSharedCodeIndex)), nullptr}; - + // We know the weak cell isn't cleared because we made sure of it in + // SearchOptimizedCodeMapEntry and performed no allocations since that + // call. + result = { + Code::cast(WeakCell::cast(code_map->get(kSharedCodeIndex))->value()), + nullptr}; } else { DCHECK_LE(entry + kEntryLength, code_map->length()); - Object* code = code_map->get(entry + kCachedCodeOffset); - result = {code->IsUndefined() ? nullptr : Code::cast(code), - LiteralsArray::cast(code_map->get(entry + kLiteralsOffset))}; + WeakCell* cell = WeakCell::cast(code_map->get(entry + kCachedCodeOffset)); + WeakCell* literals_cell = + WeakCell::cast(code_map->get(entry + kLiteralsOffset)); + + result = {cell->cleared() ? nullptr : Code::cast(cell->value()), + literals_cell->cleared() + ? nullptr + : LiteralsArray::cast(literals_cell->value())}; } } - if (FLAG_trace_opt && !optimized_code_map()->IsSmi() && + if (FLAG_trace_opt && !OptimizedCodeMapIsCleared() && result.code == nullptr) { PrintF("[didn't find optimized code in optimized code map for "); ShortPrint(); @@ -12784,7 +14285,6 @@ void Code::ClearInlineCaches(Code::Kind kind) { void Code::ClearInlineCaches(Code::Kind* kind) { int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | - RelocInfo::ModeMask(RelocInfo::CONSTRUCT_CALL) | RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID); for (RelocIterator it(this, mask); !it.done(); it.next()) { RelocInfo* info = it.rinfo(); @@ -13122,6 +14622,17 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint( break; } + case Translation::INTERPRETED_FRAME: { + int bytecode_offset = iterator.Next(); + int shared_info_id = iterator.Next(); + unsigned height = iterator.Next(); + Object* shared_info = LiteralArray()->get(shared_info_id); + os << "{bytecode_offset=" << bytecode_offset << ", function=" + << Brief(SharedFunctionInfo::cast(shared_info)->DebugName()) + << ", height=" << height << "}"; + break; + } + case Translation::JS_FRAME_FUNCTION: { os << "{function}"; break; @@ -13217,8 +14728,10 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint( } case Translation::LITERAL: { - unsigned literal_index = iterator.Next(); - os << "{literal_id=" << literal_index << "}"; + int literal_index = iterator.Next(); + Object* literal_value = LiteralArray()->get(literal_index); + os << "{literal_id=" << literal_index << " (" << Brief(literal_value) + << ")}"; break; } @@ -13494,15 +15007,24 @@ void BytecodeArray::Disassemble(std::ostream& os) { SNPrintF(buf, "%p", bytecode_start); os << buf.start() << " : "; interpreter::Bytecodes::Decode(os, bytecode_start, parameter_count()); - if (interpreter::Bytecodes::IsJump(bytecode)) { - int offset = static_cast<int8_t>(bytecode_start[1]); + + if (interpreter::Bytecodes::IsJumpConstantWide(bytecode)) { + DCHECK_EQ(bytecode_size, 3); + int index = static_cast<int>(ReadUnalignedUInt16(bytecode_start + 1)); + int offset = Smi::cast(constant_pool()->get(index))->value(); SNPrintF(buf, " (%p)", bytecode_start + offset); os << buf.start(); } else if (interpreter::Bytecodes::IsJumpConstant(bytecode)) { + DCHECK_EQ(bytecode_size, 2); int index = static_cast<int>(bytecode_start[1]); int offset = Smi::cast(constant_pool()->get(index))->value(); SNPrintF(buf, " (%p)", bytecode_start + offset); os << buf.start(); + } else if (interpreter::Bytecodes::IsJump(bytecode)) { + DCHECK_EQ(bytecode_size, 2); + int offset = static_cast<int8_t>(bytecode_start[1]); + SNPrintF(buf, " (%p)", bytecode_start + offset); + os << buf.start(); } os << "\n"; } @@ -13566,8 +15088,7 @@ MaybeHandle<Object> JSArray::ObservableSetLength(Handle<JSArray> array, uint32_t old_length = 0; CHECK(old_length_handle->ToArrayLength(&old_length)); - static const PropertyAttributes kNoAttrFilter = NONE; - int num_elements = array->NumberOfOwnElements(kNoAttrFilter); + 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. @@ -13579,7 +15100,7 @@ MaybeHandle<Object> JSArray::ObservableSetLength(Handle<JSArray> array, // 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, kNoAttrFilter); + array->GetOwnElementKeys(*keys, ALL_PROPERTIES); while (num_elements-- > 0) { uint32_t index = NumberToUint32(keys->get(num_elements)); if (index < new_length) break; @@ -13649,20 +15170,6 @@ void Map::AddDependentCode(Handle<Map> map, } -DependentCode::GroupStartIndexes::GroupStartIndexes(DependentCode* entries) { - Recompute(entries); -} - - -void DependentCode::GroupStartIndexes::Recompute(DependentCode* entries) { - start_indexes_[0] = 0; - for (int g = 1; g <= kGroupCount; g++) { - int count = entries->number_of_entries(static_cast<DependencyGroup>(g - 1)); - start_indexes_[g] = start_indexes_[g - 1] + count; - } -} - - Handle<DependentCode> DependentCode::InsertCompilationDependencies( Handle<DependentCode> entries, DependencyGroup group, Handle<Foreign> info) { @@ -13680,44 +15187,54 @@ Handle<DependentCode> DependentCode::InsertWeakCode( Handle<DependentCode> DependentCode::Insert(Handle<DependentCode> entries, DependencyGroup group, Handle<Object> object) { - GroupStartIndexes starts(*entries); - int start = starts.at(group); - int end = starts.at(group + 1); - int number_of_entries = starts.number_of_entries(); + if (entries->length() == 0 || entries->group() > group) { + // There is no such group. + return DependentCode::New(group, object, entries); + } + if (entries->group() < group) { + // The group comes later in the list. + Handle<DependentCode> old_next(entries->next_link()); + Handle<DependentCode> new_next = Insert(old_next, group, object); + if (!old_next.is_identical_to(new_next)) { + entries->set_next_link(*new_next); + } + return entries; + } + DCHECK_EQ(group, entries->group()); + int count = entries->count(); // Check for existing entry to avoid duplicates. - for (int i = start; i < end; i++) { + for (int i = 0; i < count; i++) { if (entries->object_at(i) == *object) return entries; } - if (entries->length() < kCodesStartIndex + number_of_entries + 1) { + if (entries->length() < kCodesStartIndex + count + 1) { entries = EnsureSpace(entries); - // The number of codes can change after Compact and GC. - starts.Recompute(*entries); - start = starts.at(group); - end = starts.at(group + 1); + // Count could have changed, reload it. + count = entries->count(); } - - entries->ExtendGroup(group); - entries->set_object_at(end, *object); - entries->set_number_of_entries(group, end + 1 - start); + entries->set_object_at(count, *object); + entries->set_count(count + 1); return entries; } +Handle<DependentCode> DependentCode::New(DependencyGroup group, + Handle<Object> object, + Handle<DependentCode> next) { + Isolate* isolate = next->GetIsolate(); + Handle<DependentCode> result = Handle<DependentCode>::cast( + isolate->factory()->NewFixedArray(kCodesStartIndex + 1, TENURED)); + result->set_next_link(*next); + result->set_flags(GroupField::encode(group) | CountField::encode(1)); + result->set_object_at(0, *object); + return result; +} + + Handle<DependentCode> DependentCode::EnsureSpace( Handle<DependentCode> entries) { - Isolate* isolate = entries->GetIsolate(); - if (entries->length() == 0) { - entries = Handle<DependentCode>::cast( - isolate->factory()->NewFixedArray(kCodesStartIndex + 1, TENURED)); - for (int g = 0; g < kGroupCount; g++) { - entries->set_number_of_entries(static_cast<DependencyGroup>(g), 0); - } - return entries; - } if (entries->Compact()) return entries; - GroupStartIndexes starts(*entries); - int capacity = - kCodesStartIndex + DependentCode::Grow(starts.number_of_entries()); + Isolate* isolate = entries->GetIsolate(); + int capacity = kCodesStartIndex + DependentCode::Grow(entries->count()); int grow_by = capacity - entries->length(); return Handle<DependentCode>::cast( isolate->factory()->CopyFixedArrayAndGrow(entries, grow_by, TENURED)); @@ -13725,46 +15242,47 @@ Handle<DependentCode> DependentCode::EnsureSpace( bool DependentCode::Compact() { - GroupStartIndexes starts(this); - int n = 0; - for (int g = 0; g < kGroupCount; g++) { - int start = starts.at(g); - int end = starts.at(g + 1); - int count = 0; - DCHECK(start >= n); - for (int i = start; i < end; i++) { - Object* obj = object_at(i); - if (!obj->IsWeakCell() || !WeakCell::cast(obj)->cleared()) { - if (i != n + count) { - copy(i, n + count); - } - count++; + int old_count = count(); + int new_count = 0; + for (int i = 0; i < old_count; i++) { + Object* obj = object_at(i); + if (!obj->IsWeakCell() || !WeakCell::cast(obj)->cleared()) { + if (i != new_count) { + copy(i, new_count); } + new_count++; } - if (count != end - start) { - set_number_of_entries(static_cast<DependencyGroup>(g), count); - } - n += count; } - return n < starts.number_of_entries(); + set_count(new_count); + for (int i = new_count; i < old_count; i++) { + clear_at(i); + } + return new_count < old_count; } void DependentCode::UpdateToFinishedCode(DependencyGroup group, Foreign* info, WeakCell* code_cell) { + if (this->length() == 0 || this->group() > group) { + // There is no such group. + return; + } + if (this->group() < group) { + // The group comes later in the list. + next_link()->UpdateToFinishedCode(group, info, code_cell); + return; + } + DCHECK_EQ(group, this->group()); DisallowHeapAllocation no_gc; - GroupStartIndexes starts(this); - int start = starts.at(group); - int end = starts.at(group + 1); - for (int i = start; i < end; i++) { + int count = this->count(); + for (int i = 0; i < count; i++) { if (object_at(i) == info) { set_object_at(i, code_cell); break; } } - #ifdef DEBUG - for (int i = start; i < end; i++) { + for (int i = 0; i < count; i++) { DCHECK(object_at(i) != info); } #endif @@ -13773,34 +15291,36 @@ void DependentCode::UpdateToFinishedCode(DependencyGroup group, Foreign* info, void DependentCode::RemoveCompilationDependencies( DependentCode::DependencyGroup group, Foreign* info) { + if (this->length() == 0 || this->group() > group) { + // There is no such group. + return; + } + if (this->group() < group) { + // The group comes later in the list. + next_link()->RemoveCompilationDependencies(group, info); + return; + } + DCHECK_EQ(group, this->group()); DisallowHeapAllocation no_allocation; - GroupStartIndexes starts(this); - int start = starts.at(group); - int end = starts.at(group + 1); + int old_count = count(); // Find compilation info wrapper. int info_pos = -1; - for (int i = start; i < end; i++) { + for (int i = 0; i < old_count; i++) { if (object_at(i) == info) { info_pos = i; break; } } if (info_pos == -1) return; // Not found. - int gap = info_pos; - // Use the last of each group to fill the gap in the previous group. - for (int i = group; i < kGroupCount; i++) { - int last_of_group = starts.at(i + 1) - 1; - DCHECK(last_of_group >= gap); - if (last_of_group == gap) continue; - copy(last_of_group, gap); - gap = last_of_group; - } - DCHECK(gap == starts.number_of_entries() - 1); - clear_at(gap); // Clear last gap. - set_number_of_entries(group, end - start - 1); + // Use the last code to fill the gap. + if (info_pos < old_count - 1) { + copy(old_count - 1, info_pos); + } + clear_at(old_count - 1); + set_count(old_count - 1); #ifdef DEBUG - for (int i = start; i < end - 1; i++) { + for (int i = 0; i < old_count - 1; i++) { DCHECK(object_at(i) != info); } #endif @@ -13808,30 +15328,55 @@ void DependentCode::RemoveCompilationDependencies( bool DependentCode::Contains(DependencyGroup group, WeakCell* code_cell) { - GroupStartIndexes starts(this); - int start = starts.at(group); - int end = starts.at(group + 1); - for (int i = start; i < end; i++) { + if (this->length() == 0 || this->group() > group) { + // There is no such group. + return false; + } + if (this->group() < group) { + // The group comes later in the list. + return next_link()->Contains(group, code_cell); + } + DCHECK_EQ(group, this->group()); + int count = this->count(); + for (int i = 0; i < count; i++) { if (object_at(i) == code_cell) return true; } return false; } +bool DependentCode::IsEmpty(DependencyGroup group) { + if (this->length() == 0 || this->group() > group) { + // There is no such group. + return true; + } + if (this->group() < group) { + // The group comes later in the list. + return next_link()->IsEmpty(group); + } + DCHECK_EQ(group, this->group()); + return count() == 0; +} + + bool DependentCode::MarkCodeForDeoptimization( Isolate* isolate, DependentCode::DependencyGroup group) { + if (this->length() == 0 || this->group() > group) { + // There is no such group. + return false; + } + if (this->group() < group) { + // The group comes later in the list. + return next_link()->MarkCodeForDeoptimization(isolate, group); + } + DCHECK_EQ(group, this->group()); DisallowHeapAllocation no_allocation_scope; - DependentCode::GroupStartIndexes starts(this); - int start = starts.at(group); - int end = starts.at(group + 1); - int code_entries = starts.number_of_entries(); - if (start == end) return false; - // Mark all the code that needs to be deoptimized. bool marked = false; bool invalidate_embedded_objects = group == kWeakCodeGroup; - for (int i = start; i < end; i++) { + int count = this->count(); + for (int i = 0; i < count; i++) { Object* obj = object_at(i); if (obj->IsWeakCell()) { WeakCell* cell = WeakCell::cast(obj); @@ -13852,16 +15397,10 @@ bool DependentCode::MarkCodeForDeoptimization( info->Abort(); } } - // Compact the array by moving all subsequent groups to fill in the new holes. - for (int src = end, dst = start; src < code_entries; src++, dst++) { - copy(src, dst); - } - // Now the holes are at the end of the array, zap them for heap-verifier. - int removed = end - start; - for (int i = code_entries - removed; i < code_entries; i++) { + for (int i = 0; i < count; i++) { clear_at(i); } - set_number_of_entries(group, 0); + set_count(0); return marked; } @@ -13932,13 +15471,85 @@ Handle<Map> Map::TransitionToPrototype(Handle<Map> map, Maybe<bool> JSReceiver::SetPrototype(Handle<JSReceiver> object, Handle<Object> value, bool from_javascript, ShouldThrow should_throw) { - if (!object->IsJSObject()) return Just(false); - // TODO(neis): Deal with proxies. + if (object->IsJSProxy()) { + return JSProxy::SetPrototype(Handle<JSProxy>::cast(object), value, + from_javascript, should_throw); + } return JSObject::SetPrototype(Handle<JSObject>::cast(object), value, from_javascript, should_throw); } +// ES6: 9.5.2 [[SetPrototypeOf]] (V) +// static +Maybe<bool> JSProxy::SetPrototype(Handle<JSProxy> proxy, Handle<Object> value, + bool from_javascript, + ShouldThrow should_throw) { + Isolate* isolate = proxy->GetIsolate(); + STACK_CHECK(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()); + // 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. + // 4. Assert: Type(handler) is Object. + if (proxy->IsRevoked()) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxyRevoked, trap_name)); + return Nothing<bool>(); + } + // 5. Let target be the value of the [[ProxyTarget]] internal slot. + Handle<JSReceiver> target(proxy->target(), isolate); + // 6. Let trap be ? GetMethod(handler, "getPrototypeOf"). + Handle<Object> trap; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, trap, + Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name), + Nothing<bool>()); + // 7. If trap is undefined, then return target.[[SetPrototypeOf]](). + if (trap->IsUndefined()) { + return JSReceiver::SetPrototype(target, value, from_javascript, + should_throw); + } + // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «target, V»)). + Handle<Object> argv[] = {target, value}; + Handle<Object> trap_result; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, trap_result, + Execution::Call(isolate, trap, handler, arraysize(argv), argv), + Nothing<bool>()); + bool bool_trap_result = trap_result->BooleanValue(); + // 9. Let extensibleTarget be ? IsExtensible(target). + Maybe<bool> is_extensible = JSReceiver::IsExtensible(target); + if (is_extensible.IsNothing()) return Nothing<bool>(); + // 10. If extensibleTarget is true, return booleanTrapResult. + if (is_extensible.FromJust()) { + if (bool_trap_result) return Just(true); + RETURN_FAILURE( + isolate, should_throw, + NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name)); + } + // 11. Let targetProto be ? target.[[GetPrototypeOf]](). + Handle<Object> target_proto; + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, target_proto, + Object::GetPrototype(isolate, target), + Nothing<bool>()); + // 12. If booleanTrapResult is true and SameValue(V, targetProto) is false, + // throw a TypeError exception. + if (bool_trap_result && !value->SameValue(*target_proto)) { + isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kProxySetPrototypeOfNonExtensible)); + return Nothing<bool>(); + } + // 13. Return booleanTrapResult. + if (bool_trap_result) return Just(true); + RETURN_FAILURE( + isolate, should_throw, + NewTypeError(MessageTemplate::kProxyTrapReturnedFalsish, trap_name)); +} + + Maybe<bool> JSObject::SetPrototype(Handle<JSObject> object, Handle<Object> value, bool from_javascript, ShouldThrow should_throw) { @@ -13947,7 +15558,9 @@ Maybe<bool> JSObject::SetPrototype(Handle<JSObject> object, const bool observed = from_javascript && object->map()->is_observed(); Handle<Object> old_value; if (observed) { - old_value = Object::GetPrototype(isolate, object); + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, old_value, + Object::GetPrototype(isolate, object), + Nothing<bool>()); } Maybe<bool> result = @@ -13955,7 +15568,10 @@ Maybe<bool> JSObject::SetPrototype(Handle<JSObject> object, MAYBE_RETURN(result, Nothing<bool>()); if (result.FromJust() && observed) { - Handle<Object> new_value = Object::GetPrototype(isolate, object); + Handle<Object> new_value; + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, new_value, + Object::GetPrototype(isolate, object), + Nothing<bool>()); if (!new_value->SameValue(*old_value)) { RETURN_ON_EXCEPTION_VALUE( isolate, JSObject::EnqueueChangeRecord( @@ -13984,7 +15600,8 @@ Maybe<bool> JSObject::SetPrototypeUnobserved(Handle<JSObject> object, !isolate->MayAccess(handle(isolate->context()), object)) { isolate->ReportFailedAccessCheck(object); RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); - UNREACHABLE(); + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kNoAccess)); } } else { DCHECK(!object->IsAccessCheckNeeded()); @@ -14064,7 +15681,7 @@ Maybe<bool> JSObject::SetPrototypeUnobserved(Handle<JSObject> object, // If the prototype chain didn't previously have element callbacks, then // KeyedStoreICs need to be cleared to ensure any that involve this // map go generic. - object->GetHeap()->ClearAllKeyedStoreICs(); + TypeFeedbackVector::ClearAllKeyedStoreICs(isolate); } heap->ClearInstanceofCache(); @@ -14513,8 +16130,8 @@ int JSObject::GetFastElementsUsage() { case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - // Only JSArray have packed elements. - return Smi::cast(JSArray::cast(this)->length())->value(); + return IsJSArray() ? Smi::cast(JSArray::cast(this)->length())->value() + : store->length(); case FAST_SLOPPY_ARGUMENTS_ELEMENTS: store = FixedArray::cast(FixedArray::cast(store)->get(1)); // Fall through. @@ -14589,16 +16206,6 @@ InterceptorInfo* JSObject::GetNamedInterceptor() { } -InterceptorInfo* JSObject::GetIndexedInterceptor() { - DCHECK(map()->has_indexed_interceptor()); - JSFunction* constructor = JSFunction::cast(map()->GetConstructor()); - DCHECK(constructor->shared()->IsApiFunction()); - Object* result = - constructor->shared()->get_api_func_data()->indexed_property_handler(); - return InterceptorInfo::cast(result); -} - - MaybeHandle<Object> JSObject::GetPropertyWithInterceptor(LookupIterator* it, bool* done) { *done = false; @@ -14627,6 +16234,7 @@ MaybeHandle<Object> JSObject::GetPropertyWithInterceptor(LookupIterator* it, 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(); @@ -14650,52 +16258,6 @@ MaybeHandle<Object> JSObject::GetPropertyWithInterceptor(LookupIterator* it, } -// Compute the property keys from the interceptor. -MaybeHandle<JSObject> JSObject::GetKeysForNamedInterceptor( - Handle<JSObject> object, Handle<JSReceiver> receiver) { - Isolate* isolate = receiver->GetIsolate(); - Handle<InterceptorInfo> interceptor(object->GetNamedInterceptor()); - PropertyCallbackArguments - args(isolate, interceptor->data(), *receiver, *object); - v8::Local<v8::Object> result; - if (!interceptor->enumerator()->IsUndefined()) { - v8::GenericNamedPropertyEnumeratorCallback enum_fun = - v8::ToCData<v8::GenericNamedPropertyEnumeratorCallback>( - interceptor->enumerator()); - LOG(isolate, ApiObjectAccess("interceptor-named-enum", *object)); - result = args.Call(enum_fun); - } - if (result.IsEmpty()) return MaybeHandle<JSObject>(); - DCHECK(v8::Utils::OpenHandle(*result)->IsJSArray() || - v8::Utils::OpenHandle(*result)->HasSloppyArgumentsElements()); - // Rebox before returning. - return handle(*v8::Utils::OpenHandle(*result), isolate); -} - - -// Compute the element keys from the interceptor. -MaybeHandle<JSObject> JSObject::GetKeysForIndexedInterceptor( - Handle<JSObject> object, Handle<JSReceiver> receiver) { - Isolate* isolate = receiver->GetIsolate(); - Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor()); - PropertyCallbackArguments - args(isolate, interceptor->data(), *receiver, *object); - v8::Local<v8::Object> result; - if (!interceptor->enumerator()->IsUndefined()) { - v8::IndexedPropertyEnumeratorCallback enum_fun = - v8::ToCData<v8::IndexedPropertyEnumeratorCallback>( - interceptor->enumerator()); - LOG(isolate, ApiObjectAccess("interceptor-indexed-enum", *object)); - result = args.Call(enum_fun); - } - if (result.IsEmpty()) return MaybeHandle<JSObject>(); - DCHECK(v8::Utils::OpenHandle(*result)->IsJSArray() || - v8::Utils::OpenHandle(*result)->HasSloppyArgumentsElements()); - // Rebox before returning. - return handle(*v8::Utils::OpenHandle(*result), isolate); -} - - Maybe<bool> JSObject::HasRealNamedProperty(Handle<JSObject> object, Handle<Name> name) { LookupIterator it = LookupIterator::PropertyOrElement( @@ -14723,25 +16285,6 @@ Maybe<bool> JSObject::HasRealNamedCallbackProperty(Handle<JSObject> object, } -int JSObject::NumberOfOwnProperties(PropertyAttributes filter) { - if (HasFastProperties()) { - Map* map = this->map(); - if (filter == NONE) return map->NumberOfOwnDescriptors(); - if (filter == DONT_SHOW) { - // The cached enum length was computed with filter == DONT_SHOW, so - // that's the only filter for which it's valid to retrieve it. - int result = map->EnumLength(); - if (result != kInvalidEnumCacheSentinel) return result; - } - return map->NumberOfDescribedProperties(OWN_DESCRIPTORS, filter); - } else if (IsJSGlobalObject()) { - return global_dictionary()->NumberOfElementsFilterAttributes(filter); - } else { - return property_dictionary()->NumberOfElementsFilterAttributes(filter); - } -} - - void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) { Object* temp = get(i); set(i, get(j)); @@ -14855,55 +16398,33 @@ void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) { } -// Fill in the names of own properties into the supplied storage. The main -// purpose of this function is to provide reflection information for the object -// mirrors. -int JSObject::GetOwnPropertyNames(FixedArray* storage, int index, - PropertyAttributes filter) { - DCHECK(storage->length() >= (NumberOfOwnProperties(filter) - index)); +void JSObject::CollectOwnPropertyNames(KeyAccumulator* keys, + PropertyFilter filter) { if (HasFastProperties()) { - int start_index = index; - int real_size = map()->NumberOfOwnDescriptors(); - DescriptorArray* descs = map()->instance_descriptors(); - for (int i = 0; i < real_size; i++) { - if ((descs->GetDetails(i).attributes() & filter) == 0 && - !descs->GetKey(i)->FilterKey(filter)) { - storage->set(index++, descs->GetKey(i)); - } - } - return index - start_index; - } else if (IsJSGlobalObject()) { - return global_dictionary()->CopyKeysTo(storage, index, filter, - GlobalDictionary::UNSORTED); - } else { - return property_dictionary()->CopyKeysTo(storage, index, filter, - NameDictionary::UNSORTED); - } -} - - -int JSObject::CollectOwnPropertyNames(KeyAccumulator* keys, - PropertyAttributes filter) { - if (HasFastProperties()) { - int nof_keys = keys->length(); int real_size = map()->NumberOfOwnDescriptors(); Handle<DescriptorArray> descs(map()->instance_descriptors()); for (int i = 0; i < real_size; i++) { - if ((descs->GetDetails(i).attributes() & filter) != 0) continue; + 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); } - return nof_keys - keys->length(); } else if (IsJSGlobalObject()) { - return global_dictionary()->CollectKeysTo(keys, filter); + GlobalDictionary::CollectKeysTo(handle(global_dictionary()), keys, filter); } else { - return property_dictionary()->CollectKeysTo(keys, filter); + NameDictionary::CollectKeysTo(handle(property_dictionary()), keys, filter); } } -int JSObject::NumberOfOwnElements(PropertyAttributes filter) { +int JSObject::NumberOfOwnElements(PropertyFilter filter) { // Fast case for objects with no elements. if (!IsJSValue() && HasFastElements()) { uint32_t length = @@ -14918,14 +16439,10 @@ int JSObject::NumberOfOwnElements(PropertyAttributes filter) { } -int JSObject::NumberOfEnumElements() { - return NumberOfOwnElements(static_cast<PropertyAttributes>(DONT_ENUM)); -} - - void JSObject::CollectOwnElementKeys(Handle<JSObject> object, KeyAccumulator* keys, - PropertyAttributes filter) { + PropertyFilter filter) { + if (filter & SKIP_STRINGS) return; uint32_t string_keys = 0; // If this is a String wrapper, add the string indices first, @@ -14933,7 +16450,7 @@ void JSObject::CollectOwnElementKeys(Handle<JSObject> object, // and ascending order is required by ECMA-262, 6th, 9.1.12. if (object->IsJSValue()) { Object* val = JSValue::cast(*object)->value(); - if (val->IsString()) { + if (val->IsString() && (filter & ONLY_ALL_CAN_READ) == 0) { String* str = String::cast(val); string_keys = str->length(); for (uint32_t i = 0; i < string_keys; i++) { @@ -14946,8 +16463,7 @@ void JSObject::CollectOwnElementKeys(Handle<JSObject> object, } -int JSObject::GetOwnElementKeys(FixedArray* storage, - PropertyAttributes filter) { +int JSObject::GetOwnElementKeys(FixedArray* storage, PropertyFilter filter) { int counter = 0; // If this is a String wrapper, add the string indices first, @@ -15076,6 +16592,39 @@ int JSObject::GetOwnElementKeys(FixedArray* storage, } +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(); + + Handle<JSReceiver> receiver; + CHECK(Object::ToObject(isolate, object).ToHandle(&receiver)); + + Handle<String> tag; + if (FLAG_harmony_tostring) { + Handle<Object> to_string_tag; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, to_string_tag, + GetProperty(receiver, isolate->factory()->to_string_tag_symbol()), + 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); + } + + IncrementalStringBuilder builder(isolate); + builder.AppendCString("[object "); + builder.AppendString(tag); + builder.AppendCharacter(']'); + return builder.Finish(); +} + + const char* Symbol::PrivateSymbolToName() const { Heap* heap = GetIsolate()->heap(); #define SYMBOL_CHECK_AND_PRINT(name) \ @@ -15191,12 +16740,197 @@ class StringSharedKey : public HashTableKey { }; +namespace { + +JSRegExp::Flags RegExpFlagsFromString(Handle<String> flags, bool* success) { + JSRegExp::Flags value = JSRegExp::kNone; + int length = flags->length(); + // A longer flags string cannot be valid. + if (length > 5) return JSRegExp::Flags(0); + for (int i = 0; i < length; i++) { + JSRegExp::Flag flag = JSRegExp::kNone; + switch (flags->Get(i)) { + case 'g': + flag = JSRegExp::kGlobal; + break; + case 'i': + flag = JSRegExp::kIgnoreCase; + break; + case 'm': + flag = JSRegExp::kMultiline; + break; + case 'u': + if (!FLAG_harmony_unicode_regexps) return JSRegExp::Flags(0); + flag = JSRegExp::kUnicode; + break; + case 'y': + if (!FLAG_harmony_regexps) return JSRegExp::Flags(0); + flag = JSRegExp::kSticky; + break; + default: + return JSRegExp::Flags(0); + } + // Duplicate flag. + if (value & flag) return JSRegExp::Flags(0); + value |= flag; + } + *success = true; + return value; +} + +} // namespace + + +// static +MaybeHandle<JSRegExp> JSRegExp::New(Handle<String> pattern, Flags flags) { + Isolate* isolate = pattern->GetIsolate(); + Handle<JSFunction> constructor = isolate->regexp_function(); + Handle<JSRegExp> regexp = + Handle<JSRegExp>::cast(isolate->factory()->NewJSObject(constructor)); + + return JSRegExp::Initialize(regexp, pattern, 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)); +} + + +template <typename Char> +inline int CountRequiredEscapes(Handle<String> source) { + DisallowHeapAllocation no_gc; + 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++; + } + return escapes; +} + + +template <typename Char, typename StringType> +inline Handle<StringType> WriteEscapedRegExpSource(Handle<String> source, + Handle<StringType> result) { + DisallowHeapAllocation no_gc; + Vector<const Char> src = source->GetCharVector<Char>(); + Vector<Char> dst(result->GetChars(), result->length()); + int s = 0; + int d = 0; + while (s < src.length()) { + if (src[s] == '/' && (s == 0 || src[s - 1] != '\\')) dst[d++] = '\\'; + dst[d++] = src[s++]; + } + DCHECK_EQ(result->length(), d); + return result; +} + + +MaybeHandle<String> EscapeRegExpSource(Isolate* isolate, + Handle<String> source) { + String::Flatten(source); + if (source->length() == 0) return isolate->factory()->query_colon_string(); + bool one_byte = source->IsOneByteRepresentationUnderneath(); + int escapes = one_byte ? CountRequiredEscapes<uint8_t>(source) + : CountRequiredEscapes<uc16>(source); + if (escapes == 0) return source; + int length = source->length() + escapes; + if (one_byte) { + Handle<SeqOneByteString> result; + ASSIGN_RETURN_ON_EXCEPTION(isolate, result, + isolate->factory()->NewRawOneByteString(length), + String); + return WriteEscapedRegExpSource<uint8_t>(source, result); + } else { + Handle<SeqTwoByteString> result; + ASSIGN_RETURN_ON_EXCEPTION(isolate, result, + isolate->factory()->NewRawTwoByteString(length), + String); + return WriteEscapedRegExpSource<uc16>(source, result); + } +} + + +// static +MaybeHandle<JSRegExp> JSRegExp::Initialize(Handle<JSRegExp> regexp, + Handle<String> source, + Handle<String> flags_string) { + Isolate* isolate = source->GetIsolate(); + bool success = false; + Flags flags = RegExpFlagsFromString(flags_string, &success); + if (!success) { + THROW_NEW_ERROR( + isolate, + NewSyntaxError(MessageTemplate::kInvalidRegExpFlags, flags_string), + JSRegExp); + } + return Initialize(regexp, source, flags); +} + + +// static +MaybeHandle<JSRegExp> JSRegExp::Initialize(Handle<JSRegExp> regexp, + Handle<String> source, Flags flags) { + Isolate* isolate = regexp->GetIsolate(); + Factory* factory = isolate->factory(); + // If source is the empty string we set it to "(?:)" instead as + // suggested by ECMA-262, 5th, section 15.10.4.1. + if (source->length() == 0) source = factory->query_colon_string(); + + Handle<String> escaped_source; + ASSIGN_RETURN_ON_EXCEPTION(isolate, escaped_source, + EscapeRegExpSource(isolate, source), JSRegExp); + + regexp->set_source(*escaped_source); + regexp->set_flags(Smi::FromInt(flags)); + + Map* map = regexp->map(); + Object* constructor = map->GetConstructor(); + if (constructor->IsJSFunction() && + JSFunction::cast(constructor)->initial_map() == map) { + // If we still have the original map, set in-object properties directly. + regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex, + 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, RegExpImpl::Compile(regexp, source, flags), + JSRegExp); + + return regexp; +} + + // RegExpKey carries the source and flags of a regular expression as key. class RegExpKey : public HashTableKey { public: RegExpKey(Handle<String> string, JSRegExp::Flags flags) - : string_(string), - flags_(Smi::FromInt(flags.value())) { } + : string_(string), flags_(Smi::FromInt(flags)) {} // Rather than storing the key in the hash table, a pointer to the // stored value is stored where the key should be. IsMatch then @@ -15298,15 +17032,14 @@ class InternalizedStringKey : public HashTableKey { template<typename Derived, typename Shape, typename Key> void HashTable<Derived, Shape, Key>::IteratePrefix(ObjectVisitor* v) { - IteratePointers(v, 0, kElementsStartOffset); + BodyDescriptorBase::IteratePointers(this, 0, kElementsStartOffset, v); } template<typename Derived, typename Shape, typename Key> void HashTable<Derived, Shape, Key>::IterateElements(ObjectVisitor* v) { - IteratePointers(v, - kElementsStartOffset, - kHeaderSize + length() * kPointerSize); + BodyDescriptorBase::IteratePointers(this, kElementsStartOffset, + kHeaderSize + length() * kPointerSize, v); } @@ -15493,14 +17226,8 @@ Handle<Derived> HashTable<Derived, Shape, Key>::EnsureCapacity( Isolate* isolate = table->GetIsolate(); int capacity = table->Capacity(); int nof = table->NumberOfElements() + n; - int nod = table->NumberOfDeletedElements(); - // Return if: - // 50% is still free after adding n elements and - // at most 50% of the free elements are deleted elements. - if (nod <= (capacity - nof) >> 1) { - int needed_free = nof >> 1; - if (nof + needed_free <= capacity) return table; - } + + if (table->HasSufficientCapacity(n)) return table; const int kMinCapacityForPretenure = 256; bool should_pretenure = pretenure == TENURED || @@ -15517,6 +17244,22 @@ Handle<Derived> HashTable<Derived, Shape, Key>::EnsureCapacity( } +template <typename Derived, typename Shape, typename Key> +bool HashTable<Derived, Shape, Key>::HasSufficientCapacity(int n) { + int capacity = Capacity(); + int nof = NumberOfElements() + n; + int nod = NumberOfDeletedElements(); + // Return true if: + // 50% is still free after adding n elements and + // at most 50% of the free elements are deleted elements. + if (nod <= (capacity - nof) >> 1) { + int needed_free = nof >> 1; + if (nof + needed_free <= capacity) return true; + } + return false; +} + + template<typename Derived, typename Shape, typename Key> Handle<Derived> HashTable<Derived, Shape, Key>::Shrink(Handle<Derived> table, Key key) { @@ -15683,6 +17426,9 @@ template Handle<UnseededNumberDictionary> Dictionary<UnseededNumberDictionary, UnseededNumberDictionaryShape, uint32_t>:: EnsureCapacity(Handle<UnseededNumberDictionary>, int, uint32_t); +template void Dictionary<NameDictionary, NameDictionaryShape, + Handle<Name> >::SetRequiresCopyOnCapacityChange(); + template Handle<NameDictionary> Dictionary<NameDictionary, NameDictionaryShape, Handle<Name> >:: EnsureCapacity(Handle<NameDictionary>, int, Handle<Name>); @@ -16411,7 +18157,17 @@ Dictionary<Derived, Shape, Key>::GenerateNewEnumerationIndices( } -template<typename Derived, typename Shape, typename Key> +template <typename Derived, typename Shape, typename Key> +void Dictionary<Derived, Shape, Key>::SetRequiresCopyOnCapacityChange() { + DCHECK_EQ(0, DerivedHashTable::NumberOfElements()); + DCHECK_EQ(0, DerivedHashTable::NumberOfDeletedElements()); + // Make sure that HashTable::EnsureCapacity will create a copy. + DerivedHashTable::SetNumberOfDeletedElements(DerivedHashTable::Capacity()); + DCHECK(!DerivedHashTable::HasSufficientCapacity(1)); +} + + +template <typename Derived, typename Shape, typename Key> Handle<Derived> Dictionary<Derived, Shape, Key>::EnsureCapacity( Handle<Derived> dictionary, int n, Key key) { // Check whether there are enough enumeration indices to add n elements. @@ -16515,7 +18271,7 @@ void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key, if (key > kRequiresSlowElementsLimit) { if (used_as_prototype) { // TODO(verwaest): Remove this hack. - GetHeap()->ClearAllKeyedStoreICs(); + TypeFeedbackVector::ClearAllKeyedStoreICs(GetIsolate()); } set_requires_slow_elements(); return; @@ -16594,7 +18350,7 @@ Handle<UnseededNumberDictionary> UnseededNumberDictionary::Set( template <typename Derived, typename Shape, typename Key> int Dictionary<Derived, Shape, Key>::NumberOfElementsFilterAttributes( - PropertyAttributes filter) { + PropertyFilter filter) { int capacity = this->Capacity(); int result = 0; for (int i = 0; i < capacity; i++) { @@ -16615,12 +18371,12 @@ bool Dictionary<Derived, Shape, Key>::HasComplexElements() { int capacity = this->Capacity(); for (int i = 0; i < capacity; i++) { Object* k = this->KeyAt(i); - if (this->IsKey(k) && !k->FilterKey(NONE)) { + if (this->IsKey(k) && !k->FilterKey(ALL_PROPERTIES)) { if (this->IsDeleted(i)) continue; PropertyDetails details = this->DetailsAt(i); if (details.type() == ACCESSOR_CONSTANT) return true; PropertyAttributes attr = details.attributes(); - if (attr & (READ_ONLY | DONT_DELETE | DONT_ENUM)) return true; + if (attr & ALL_ATTRIBUTES_MASK) return true; } } return false; @@ -16667,7 +18423,7 @@ void Dictionary<Derived, Shape, Key>::CopyEnumKeysTo(FixedArray* storage) { template <typename Derived, typename Shape, typename Key> int Dictionary<Derived, Shape, Key>::CopyKeysTo( - FixedArray* storage, int index, PropertyAttributes filter, + FixedArray* storage, int index, PropertyFilter filter, typename Dictionary<Derived, Shape, Key>::SortMode sort_mode) { DCHECK(storage->length() >= NumberOfElementsFilterAttributes(filter)); int start_index = index; @@ -16690,20 +18446,44 @@ int Dictionary<Derived, Shape, Key>::CopyKeysTo( template <typename Derived, typename Shape, typename Key> -int Dictionary<Derived, Shape, Key>::CollectKeysTo(KeyAccumulator* keys, - PropertyAttributes filter) { - int capacity = this->Capacity(); - int keyLength = keys->length(); - 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; - keys->AddKey(k); +void Dictionary<Derived, Shape, Key>::CollectKeysTo( + Handle<Dictionary<Derived, Shape, Key> > dictionary, KeyAccumulator* keys, + PropertyFilter filter) { + int capacity = dictionary->Capacity(); + Handle<FixedArray> array = + keys->isolate()->factory()->NewFixedArray(dictionary->NumberOfElements()); + int array_size = 0; + + { + 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->IsDeleted(i)) continue; + PropertyDetails details = raw_dict->DetailsAt(i); + if ((details.attributes() & filter) != 0) continue; + if (filter & ONLY_ALL_CAN_READ) { + if (details.kind() != kAccessor) continue; + Object* accessors = raw_dict->ValueAt(i); + if (accessors->IsPropertyCell()) { + accessors = PropertyCell::cast(accessors)->value(); + } + if (!accessors->IsAccessorInfo()) continue; + if (!AccessorInfo::cast(accessors)->all_can_read()) continue; + } + array->set(array_size++, Smi::FromInt(i)); + } + + EnumIndexComparator<Derived> cmp(static_cast<Derived*>(raw_dict)); + Smi** start = reinterpret_cast<Smi**>(array->GetFirstElementAddress()); + std::sort(start, start + array_size, cmp); + } + + for (int i = 0; i < array_size; i++) { + int index = Smi::cast(array->get(i))->value(); + keys->AddKey(dictionary->KeyAt(index)); } - return keyLength - keys->length(); } @@ -17510,6 +19290,39 @@ int BreakPointInfo::GetBreakPointCount() { } +// static +MaybeHandle<JSDate> JSDate::New(Handle<JSFunction> constructor, + Handle<JSReceiver> new_target, double tv) { + Isolate* const isolate = constructor->GetIsolate(); + Handle<JSObject> result; + ASSIGN_RETURN_ON_EXCEPTION(isolate, result, + JSObject::New(constructor, new_target), JSDate); + if (-DateCache::kMaxTimeInMs <= tv && tv <= DateCache::kMaxTimeInMs) { + tv = DoubleToInteger(tv) + 0.0; + } else { + tv = std::numeric_limits<double>::quiet_NaN(); + } + Handle<Object> value = isolate->factory()->NewNumber(tv); + Handle<JSDate>::cast(result)->SetValue(*value, std::isnan(tv)); + return Handle<JSDate>::cast(result); +} + + +// static +double JSDate::CurrentTimeValue(Isolate* isolate) { + if (FLAG_log_timer_events || FLAG_prof_cpp) LOG(isolate, CurrentTimeEvent()); + + // According to ECMA-262, section 15.9.1, page 117, the precision of + // the number in a Date object representing a particular instant in + // time is milliseconds. Therefore, we floor the result of getting + // the OS time. + return Floor(FLAG_verify_predictable + ? isolate->heap()->MonotonicallyIncreasingTimeInMs() + : base::OS::TimeCurrentMillis()); +} + + +// static Object* JSDate::GetField(Object* object, Smi* index) { return JSDate::cast(object)->DoGetField( static_cast<FieldIndex>(index->value())); @@ -17602,6 +19415,16 @@ Object* JSDate::GetUTCField(FieldIndex index, } +// static +Handle<Object> JSDate::SetValue(Handle<JSDate> date, double v) { + Isolate* const isolate = date->GetIsolate(); + Handle<Object> value = isolate->factory()->NewNumber(v); + bool value_is_nan = std::isnan(v); + date->SetValue(*value, value_is_nan); + return value; +} + + void JSDate::SetValue(Object* value, bool is_value_nan) { set_value(value); if (is_value_nan) { |