diff options
author | Michaël Zasso <targos@protonmail.com> | 2018-12-04 08:20:37 +0100 |
---|---|---|
committer | Michaël Zasso <targos@protonmail.com> | 2018-12-06 15:23:33 +0100 |
commit | 9b4bf7de6c9a7c25f116c7a502384c20b5cfaea3 (patch) | |
tree | 2b0c843168dafb939d8df8a15b2aa72b76dee51d /deps/v8/src/objects | |
parent | b8fbe69db1292307adb2c2b2e0d5ef48c4ab2faf (diff) | |
download | node-new-9b4bf7de6c9a7c25f116c7a502384c20b5cfaea3.tar.gz |
deps: update V8 to 7.1.302.28
PR-URL: https://github.com/nodejs/node/pull/23423
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Myles Borins <myles.borins@gmail.com>
Diffstat (limited to 'deps/v8/src/objects')
90 files changed, 7311 insertions, 2347 deletions
diff --git a/deps/v8/src/objects/allocation-site-inl.h b/deps/v8/src/objects/allocation-site-inl.h new file mode 100644 index 0000000000..2ed280c054 --- /dev/null +++ b/deps/v8/src/objects/allocation-site-inl.h @@ -0,0 +1,197 @@ +// Copyright 2018 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. + +#ifndef V8_OBJECTS_ALLOCATION_SITE_INL_H_ +#define V8_OBJECTS_ALLOCATION_SITE_INL_H_ + +#include "src/objects/allocation-site.h" + +#include "src/heap/heap-inl.h" +#include "src/objects/js-objects-inl.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +CAST_ACCESSOR(AllocationMemento) +CAST_ACCESSOR(AllocationSite) + +ACCESSORS(AllocationSite, transition_info_or_boilerplate, Object, + kTransitionInfoOrBoilerplateOffset) +ACCESSORS(AllocationSite, nested_site, Object, kNestedSiteOffset) +INT32_ACCESSORS(AllocationSite, pretenure_data, kPretenureDataOffset) +INT32_ACCESSORS(AllocationSite, pretenure_create_count, + kPretenureCreateCountOffset) +ACCESSORS(AllocationSite, dependent_code, DependentCode, kDependentCodeOffset) +ACCESSORS_CHECKED(AllocationSite, weak_next, Object, kWeakNextOffset, + HasWeakNext()) +ACCESSORS(AllocationMemento, allocation_site, Object, kAllocationSiteOffset) + +JSObject* AllocationSite::boilerplate() const { + DCHECK(PointsToLiteral()); + return JSObject::cast(transition_info_or_boilerplate()); +} + +void AllocationSite::set_boilerplate(JSObject* object, WriteBarrierMode mode) { + set_transition_info_or_boilerplate(object, mode); +} + +int AllocationSite::transition_info() const { + DCHECK(!PointsToLiteral()); + return Smi::cast(transition_info_or_boilerplate())->value(); +} + +void AllocationSite::set_transition_info(int value) { + DCHECK(!PointsToLiteral()); + set_transition_info_or_boilerplate(Smi::FromInt(value), SKIP_WRITE_BARRIER); +} + +bool AllocationSite::HasWeakNext() const { + return map() == GetReadOnlyRoots().allocation_site_map(); +} + +void AllocationSite::Initialize() { + set_transition_info_or_boilerplate(Smi::kZero); + SetElementsKind(GetInitialFastElementsKind()); + set_nested_site(Smi::kZero); + set_pretenure_data(0); + set_pretenure_create_count(0); + set_dependent_code( + DependentCode::cast(GetReadOnlyRoots().empty_weak_fixed_array()), + SKIP_WRITE_BARRIER); +} + +bool AllocationSite::IsZombie() const { + return pretenure_decision() == kZombie; +} + +bool AllocationSite::IsMaybeTenure() const { + return pretenure_decision() == kMaybeTenure; +} + +bool AllocationSite::PretenuringDecisionMade() const { + return pretenure_decision() != kUndecided; +} + +void AllocationSite::MarkZombie() { + DCHECK(!IsZombie()); + Initialize(); + set_pretenure_decision(kZombie); +} + +ElementsKind AllocationSite::GetElementsKind() const { + return ElementsKindBits::decode(transition_info()); +} + +void AllocationSite::SetElementsKind(ElementsKind kind) { + set_transition_info(ElementsKindBits::update(transition_info(), kind)); +} + +bool AllocationSite::CanInlineCall() const { + return DoNotInlineBit::decode(transition_info()) == 0; +} + +void AllocationSite::SetDoNotInlineCall() { + set_transition_info(DoNotInlineBit::update(transition_info(), true)); +} + +bool AllocationSite::PointsToLiteral() const { + Object* raw_value = transition_info_or_boilerplate(); + DCHECK_EQ(!raw_value->IsSmi(), + raw_value->IsJSArray() || raw_value->IsJSObject()); + return !raw_value->IsSmi(); +} + +// Heuristic: We only need to create allocation site info if the boilerplate +// elements kind is the initial elements kind. +bool AllocationSite::ShouldTrack(ElementsKind boilerplate_elements_kind) { + return IsSmiElementsKind(boilerplate_elements_kind); +} + +inline bool AllocationSite::CanTrack(InstanceType type) { + if (FLAG_allocation_site_pretenuring) { + // TurboFan doesn't care at all about String pretenuring feedback, + // so don't bother even trying to track that. + return type == JS_ARRAY_TYPE || type == JS_OBJECT_TYPE; + } + return type == JS_ARRAY_TYPE; +} + +AllocationSite::PretenureDecision AllocationSite::pretenure_decision() const { + return PretenureDecisionBits::decode(pretenure_data()); +} + +void AllocationSite::set_pretenure_decision(PretenureDecision decision) { + int32_t value = pretenure_data(); + set_pretenure_data(PretenureDecisionBits::update(value, decision)); +} + +bool AllocationSite::deopt_dependent_code() const { + return DeoptDependentCodeBit::decode(pretenure_data()); +} + +void AllocationSite::set_deopt_dependent_code(bool deopt) { + int32_t value = pretenure_data(); + set_pretenure_data(DeoptDependentCodeBit::update(value, deopt)); +} + +int AllocationSite::memento_found_count() const { + return MementoFoundCountBits::decode(pretenure_data()); +} + +inline void AllocationSite::set_memento_found_count(int count) { + int32_t value = pretenure_data(); + // Verify that we can count more mementos than we can possibly find in one + // new space collection. + DCHECK((GetHeap()->MaxSemiSpaceSize() / + (Heap::kMinObjectSizeInWords * kPointerSize + + AllocationMemento::kSize)) < MementoFoundCountBits::kMax); + DCHECK_LT(count, MementoFoundCountBits::kMax); + set_pretenure_data(MementoFoundCountBits::update(value, count)); +} + +int AllocationSite::memento_create_count() const { + return pretenure_create_count(); +} + +void AllocationSite::set_memento_create_count(int count) { + set_pretenure_create_count(count); +} + +bool AllocationSite::IncrementMementoFoundCount(int increment) { + if (IsZombie()) return false; + + int value = memento_found_count(); + set_memento_found_count(value + increment); + return memento_found_count() >= kPretenureMinimumCreated; +} + +inline void AllocationSite::IncrementMementoCreateCount() { + DCHECK(FLAG_allocation_site_pretenuring); + int value = memento_create_count(); + set_memento_create_count(value + 1); +} + +bool AllocationMemento::IsValid() const { + return allocation_site()->IsAllocationSite() && + !AllocationSite::cast(allocation_site())->IsZombie(); +} + +AllocationSite* AllocationMemento::GetAllocationSite() const { + DCHECK(IsValid()); + return AllocationSite::cast(allocation_site()); +} + +Address AllocationMemento::GetAllocationSiteUnchecked() const { + return reinterpret_cast<Address>(allocation_site()); +} + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_ALLOCATION_SITE_INL_H_ diff --git a/deps/v8/src/objects/allocation-site.h b/deps/v8/src/objects/allocation-site.h new file mode 100644 index 0000000000..d923fd8f23 --- /dev/null +++ b/deps/v8/src/objects/allocation-site.h @@ -0,0 +1,186 @@ +// Copyright 2018 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. + +#ifndef V8_OBJECTS_ALLOCATION_SITE_H_ +#define V8_OBJECTS_ALLOCATION_SITE_H_ + +#include "src/objects.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +class AllocationSite : public Struct, public NeverReadOnlySpaceObject { + public: + static const uint32_t kMaximumArrayBytesToPretransition = 8 * 1024; + static const double kPretenureRatio; + static const int kPretenureMinimumCreated = 100; + + // Values for pretenure decision field. + enum PretenureDecision { + kUndecided = 0, + kDontTenure = 1, + kMaybeTenure = 2, + kTenure = 3, + kZombie = 4, + kLastPretenureDecisionValue = kZombie + }; + + const char* PretenureDecisionName(PretenureDecision decision); + + // Contains either a Smi-encoded bitfield or a boilerplate. If it's a Smi the + // AllocationSite is for a constructed Array. + DECL_ACCESSORS(transition_info_or_boilerplate, Object) + DECL_ACCESSORS(boilerplate, JSObject) + DECL_INT_ACCESSORS(transition_info) + + // nested_site threads a list of sites that represent nested literals + // walked in a particular order. So [[1, 2], 1, 2] will have one + // nested_site, but [[1, 2], 3, [4]] will have a list of two. + DECL_ACCESSORS(nested_site, Object) + + // Bitfield containing pretenuring information. + DECL_INT32_ACCESSORS(pretenure_data) + + DECL_INT32_ACCESSORS(pretenure_create_count) + DECL_ACCESSORS(dependent_code, DependentCode) + + // heap->allocation_site_list() points to the last AllocationSite which form + // a linked list through the weak_next property. The GC might remove elements + // from the list by updateing weak_next. + DECL_ACCESSORS(weak_next, Object) + + inline void Initialize(); + + // Checks if the allocation site contain weak_next field; + inline bool HasWeakNext() const; + + // This method is expensive, it should only be called for reporting. + bool IsNested(); + + // transition_info bitfields, for constructed array transition info. + class ElementsKindBits : public BitField<ElementsKind, 0, 5> {}; + class DoNotInlineBit : public BitField<bool, 5, 1> {}; + // Unused bits 6-30. + + // Bitfields for pretenure_data + class MementoFoundCountBits : public BitField<int, 0, 26> {}; + class PretenureDecisionBits : public BitField<PretenureDecision, 26, 3> {}; + class DeoptDependentCodeBit : public BitField<bool, 29, 1> {}; + STATIC_ASSERT(PretenureDecisionBits::kMax >= kLastPretenureDecisionValue); + + // Increments the mementos found counter and returns true when the first + // memento was found for a given allocation site. + inline bool IncrementMementoFoundCount(int increment = 1); + + inline void IncrementMementoCreateCount(); + + PretenureFlag GetPretenureMode() const; + + void ResetPretenureDecision(); + + inline PretenureDecision pretenure_decision() const; + inline void set_pretenure_decision(PretenureDecision decision); + + inline bool deopt_dependent_code() const; + inline void set_deopt_dependent_code(bool deopt); + + inline int memento_found_count() const; + inline void set_memento_found_count(int count); + + inline int memento_create_count() const; + inline void set_memento_create_count(int count); + + // The pretenuring decision is made during gc, and the zombie state allows + // us to recognize when an allocation site is just being kept alive because + // a later traversal of new space may discover AllocationMementos that point + // to this AllocationSite. + inline bool IsZombie() const; + + inline bool IsMaybeTenure() const; + + inline void MarkZombie(); + + inline bool MakePretenureDecision(PretenureDecision current_decision, + double ratio, bool maximum_size_scavenge); + + inline bool DigestPretenuringFeedback(bool maximum_size_scavenge); + + inline ElementsKind GetElementsKind() const; + inline void SetElementsKind(ElementsKind kind); + + inline bool CanInlineCall() const; + inline void SetDoNotInlineCall(); + + inline bool PointsToLiteral() const; + + template <AllocationSiteUpdateMode update_or_check = + AllocationSiteUpdateMode::kUpdate> + static bool DigestTransitionFeedback(Handle<AllocationSite> site, + ElementsKind to_kind); + + DECL_PRINTER(AllocationSite) + DECL_VERIFIER(AllocationSite) + + DECL_CAST(AllocationSite) + static inline bool ShouldTrack(ElementsKind boilerplate_elements_kind); + static bool ShouldTrack(ElementsKind from, ElementsKind to); + static inline bool CanTrack(InstanceType type); + +// Layout description. +// AllocationSite has to start with TransitionInfoOrboilerPlateOffset +// and end with WeakNext field. +#define ALLOCATION_SITE_FIELDS(V) \ + V(kTransitionInfoOrBoilerplateOffset, kPointerSize) \ + V(kNestedSiteOffset, kPointerSize) \ + V(kDependentCodeOffset, kPointerSize) \ + V(kCommonPointerFieldEndOffset, 0) \ + V(kPretenureDataOffset, kInt32Size) \ + V(kPretenureCreateCountOffset, kInt32Size) \ + /* Size of AllocationSite without WeakNext field */ \ + V(kSizeWithoutWeakNext, 0) \ + V(kWeakNextOffset, kPointerSize) \ + /* Size of AllocationSite with WeakNext field */ \ + V(kSizeWithWeakNext, 0) + + DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize, ALLOCATION_SITE_FIELDS) + + static const int kStartOffset = HeapObject::kHeaderSize; + + class BodyDescriptor; + + private: + inline bool PretenuringDecisionMade() const; + + DISALLOW_IMPLICIT_CONSTRUCTORS(AllocationSite); +}; + +class AllocationMemento : public Struct { + public: + static const int kAllocationSiteOffset = HeapObject::kHeaderSize; + static const int kSize = kAllocationSiteOffset + kPointerSize; + + DECL_ACCESSORS(allocation_site, Object) + + inline bool IsValid() const; + inline AllocationSite* GetAllocationSite() const; + inline Address GetAllocationSiteUnchecked() const; + + DECL_PRINTER(AllocationMemento) + DECL_VERIFIER(AllocationMemento) + + DECL_CAST(AllocationMemento) + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(AllocationMemento); +}; + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_ALLOCATION_SITE_H_ diff --git a/deps/v8/src/objects/api-callbacks-inl.h b/deps/v8/src/objects/api-callbacks-inl.h index 4f7680d8ed..b8ca8bef20 100644 --- a/deps/v8/src/objects/api-callbacks-inl.h +++ b/deps/v8/src/objects/api-callbacks-inl.h @@ -59,8 +59,22 @@ BIT_FIELD_ACCESSORS(AccessorInfo, flags, is_special_data_property, BIT_FIELD_ACCESSORS(AccessorInfo, flags, replace_on_access, AccessorInfo::ReplaceOnAccessBit) BIT_FIELD_ACCESSORS(AccessorInfo, flags, is_sloppy, AccessorInfo::IsSloppyBit) -BIT_FIELD_ACCESSORS(AccessorInfo, flags, has_no_side_effect, - AccessorInfo::HasNoSideEffectBit) +BIT_FIELD_ACCESSORS(AccessorInfo, flags, getter_side_effect_type, + AccessorInfo::GetterSideEffectTypeBits) + +SideEffectType AccessorInfo::setter_side_effect_type() const { + return SetterSideEffectTypeBits::decode(flags()); +} + +void AccessorInfo::set_setter_side_effect_type(SideEffectType value) { + // We do not support describing setters as having no side effect, since + // calling set accessors must go through a store bytecode. Store bytecodes + // support checking receivers for temporary objects, but still expect + // the receiver to be written to. + CHECK_NE(value, SideEffectType::kHasNoSideEffect); + set_flags(SetterSideEffectTypeBits::update(flags(), value)); +} + BIT_FIELD_ACCESSORS(AccessorInfo, flags, initial_property_attributes, AccessorInfo::InitialAttributesBits) diff --git a/deps/v8/src/objects/api-callbacks.h b/deps/v8/src/objects/api-callbacks.h index 6f3629a2c3..f7522da8a7 100644 --- a/deps/v8/src/objects/api-callbacks.h +++ b/deps/v8/src/objects/api-callbacks.h @@ -48,7 +48,12 @@ class AccessorInfo : public Struct { DECL_BOOLEAN_ACCESSORS(is_special_data_property) DECL_BOOLEAN_ACCESSORS(replace_on_access) DECL_BOOLEAN_ACCESSORS(is_sloppy) - DECL_BOOLEAN_ACCESSORS(has_no_side_effect) + + inline SideEffectType getter_side_effect_type() const; + inline void set_getter_side_effect_type(SideEffectType type); + + inline SideEffectType setter_side_effect_type() const; + inline void set_setter_side_effect_type(SideEffectType type); // The property attributes used when an API object template is instantiated // for the first time. Changing of this value afterwards does not affect @@ -89,13 +94,15 @@ class AccessorInfo : public Struct { inline bool HasExpectedReceiverType(); // Bit positions in |flags|. -#define ACCESSOR_INFO_FLAGS_BIT_FIELDS(V, _) \ - V(AllCanReadBit, bool, 1, _) \ - V(AllCanWriteBit, bool, 1, _) \ - V(IsSpecialDataPropertyBit, bool, 1, _) \ - V(IsSloppyBit, bool, 1, _) \ - V(ReplaceOnAccessBit, bool, 1, _) \ - V(HasNoSideEffectBit, bool, 1, _) \ +#define ACCESSOR_INFO_FLAGS_BIT_FIELDS(V, _) \ + V(AllCanReadBit, bool, 1, _) \ + V(AllCanWriteBit, bool, 1, _) \ + V(IsSpecialDataPropertyBit, bool, 1, _) \ + V(IsSloppyBit, bool, 1, _) \ + V(ReplaceOnAccessBit, bool, 1, _) \ + V(GetterSideEffectTypeBits, SideEffectType, 2, _) \ + /* We could save a bit from setter side-effect type, if necessary */ \ + V(SetterSideEffectTypeBits, SideEffectType, 2, _) \ V(InitialAttributesBits, PropertyAttributes, 3, _) DEFINE_BIT_FIELDS(ACCESSOR_INFO_FLAGS_BIT_FIELDS) diff --git a/deps/v8/src/objects/arguments-inl.h b/deps/v8/src/objects/arguments-inl.h index 7d92ce0496..222ca7954e 100644 --- a/deps/v8/src/objects/arguments-inl.h +++ b/deps/v8/src/objects/arguments-inl.h @@ -63,7 +63,8 @@ bool JSSloppyArgumentsObject::GetSloppyArgumentsLength(Isolate* isolate, return false; } DCHECK(object->HasFastElements() || object->HasFastArgumentsElements()); - Object* len_obj = object->InObjectPropertyAt(JSArgumentsObject::kLengthIndex); + Object* len_obj = + object->InObjectPropertyAt(JSArgumentsObjectWithLength::kLengthIndex); if (!len_obj->IsSmi()) return false; *out = Max(0, Smi::ToInt(len_obj)); diff --git a/deps/v8/src/objects/arguments.h b/deps/v8/src/objects/arguments.h index 36c6204d1a..15f3d2a2f5 100644 --- a/deps/v8/src/objects/arguments.h +++ b/deps/v8/src/objects/arguments.h @@ -5,8 +5,8 @@ #ifndef V8_OBJECTS_ARGUMENTS_H_ #define V8_OBJECTS_ARGUMENTS_H_ -#include "src/objects.h" #include "src/objects/fixed-array.h" +#include "src/objects/js-objects.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" @@ -14,11 +14,21 @@ namespace v8 { namespace internal { +// Superclass for all objects with instance type {JS_ARGUMENTS_TYPE} +class JSArgumentsObject : public JSObject { + public: + DECL_VERIFIER(JSArgumentsObject) + DECL_CAST(JSArgumentsObject) + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSArgumentsObject); +}; + // Common superclass for JSSloppyArgumentsObject and JSStrictArgumentsObject. // Note that the instance type {JS_ARGUMENTS_TYPE} does _not_ guarantee the // below layout, the in-object properties might have transitioned to dictionary // mode already. Only use the below layout with the specific initial maps. -class JSArgumentsObject : public JSObject { +class JSArgumentsObjectWithLength : public JSArgumentsObject { public: // Offsets of object fields. static const int kLengthOffset = JSObject::kHeaderSize; @@ -26,19 +36,19 @@ class JSArgumentsObject : public JSObject { // Indices of in-object properties. static const int kLengthIndex = 0; - DECL_VERIFIER(JSArgumentsObject) - DECL_CAST(JSArgumentsObject) + DECL_VERIFIER(JSArgumentsObjectWithLength) + DECL_CAST(JSArgumentsObjectWithLength) private: - DISALLOW_IMPLICIT_CONSTRUCTORS(JSArgumentsObject); + DISALLOW_IMPLICIT_CONSTRUCTORS(JSArgumentsObjectWithLength); }; // JSSloppyArgumentsObject is just a JSObject with specific initial map. // This initial map adds in-object properties for "length" and "callee". -class JSSloppyArgumentsObject : public JSArgumentsObject { +class JSSloppyArgumentsObject : public JSArgumentsObjectWithLength { public: // Offsets of object fields. - static const int kCalleeOffset = JSArgumentsObject::kSize; + static const int kCalleeOffset = JSArgumentsObjectWithLength::kSize; static const int kSize = kCalleeOffset + kPointerSize; // Indices of in-object properties. static const int kCalleeIndex = kLengthIndex + 1; @@ -53,10 +63,10 @@ class JSSloppyArgumentsObject : public JSArgumentsObject { // JSStrictArgumentsObject is just a JSObject with specific initial map. // This initial map adds an in-object property for "length". -class JSStrictArgumentsObject : public JSArgumentsObject { +class JSStrictArgumentsObject : public JSArgumentsObjectWithLength { public: // Offsets of object fields. - static const int kSize = JSArgumentsObject::kSize; + static const int kSize = JSArgumentsObjectWithLength::kSize; private: DISALLOW_IMPLICIT_CONSTRUCTORS(JSStrictArgumentsObject); diff --git a/deps/v8/src/objects/bigint.cc b/deps/v8/src/objects/bigint.cc index 458aa7c1eb..dcf99e2f29 100644 --- a/deps/v8/src/objects/bigint.cc +++ b/deps/v8/src/objects/bigint.cc @@ -36,9 +36,6 @@ namespace internal { class MutableBigInt : public FreshlyAllocatedBigInt, public NeverReadOnlySpaceObject { public: - using NeverReadOnlySpaceObject::GetHeap; - using NeverReadOnlySpaceObject::GetIsolate; - // Bottleneck for converting MutableBigInts to BigInts. static MaybeHandle<BigInt> MakeImmutable(MaybeHandle<MutableBigInt> maybe); static Handle<BigInt> MakeImmutable(Handle<MutableBigInt> result); @@ -96,7 +93,8 @@ class MutableBigInt : public FreshlyAllocatedBigInt, static inline Handle<MutableBigInt> AbsoluteBitwiseOp( Isolate* isolate, Handle<BigIntBase> x, Handle<BigIntBase> y, MutableBigInt* result_storage, ExtraDigitsHandling extra_digits, - SymmetricOp symmetric, std::function<digit_t(digit_t, digit_t)> op); + SymmetricOp symmetric, + const std::function<digit_t(digit_t, digit_t)>& op); static Handle<MutableBigInt> AbsoluteAnd( Isolate* isolate, Handle<BigIntBase> x, Handle<BigIntBase> y, MutableBigInt* result_storage = nullptr); @@ -155,9 +153,11 @@ class MutableBigInt : public FreshlyAllocatedBigInt, static MaybeHandle<String> ToStringBasePowerOfTwo(Isolate* isolate, Handle<BigIntBase> x, - int radix); + int radix, + ShouldThrow should_throw); static MaybeHandle<String> ToStringGeneric(Isolate* isolate, - Handle<BigIntBase> x, int radix); + Handle<BigIntBase> x, int radix, + ShouldThrow should_throw); static double ToDouble(Handle<BigIntBase> x); enum Rounding { kRoundDown, kTie, kRoundUp }; @@ -924,14 +924,15 @@ ComparisonResult BigInt::CompareToDouble(Handle<BigInt> x, double y) { } MaybeHandle<String> BigInt::ToString(Isolate* isolate, Handle<BigInt> bigint, - int radix) { + int radix, ShouldThrow should_throw) { if (bigint->is_zero()) { return isolate->factory()->NewStringFromStaticChars("0"); } if (base::bits::IsPowerOfTwo(radix)) { - return MutableBigInt::ToStringBasePowerOfTwo(isolate, bigint, radix); + return MutableBigInt::ToStringBasePowerOfTwo(isolate, bigint, radix, + should_throw); } - return MutableBigInt::ToStringGeneric(isolate, bigint, radix); + return MutableBigInt::ToStringGeneric(isolate, bigint, radix, should_throw); } MaybeHandle<BigInt> BigInt::FromNumber(Isolate* isolate, @@ -1255,7 +1256,7 @@ MaybeHandle<MutableBigInt> MutableBigInt::AbsoluteSubOne(Isolate* isolate, inline Handle<MutableBigInt> MutableBigInt::AbsoluteBitwiseOp( Isolate* isolate, Handle<BigIntBase> x, Handle<BigIntBase> y, MutableBigInt* result_storage, ExtraDigitsHandling extra_digits, - SymmetricOp symmetric, std::function<digit_t(digit_t, digit_t)> op) { + SymmetricOp symmetric, const std::function<digit_t(digit_t, digit_t)>& op) { int x_length = x->length(); int y_length = y->length(); int num_pairs = y_length; @@ -1924,9 +1925,9 @@ MaybeHandle<BigInt> BigInt::FromSerializedDigits( static const char kConversionChars[] = "0123456789abcdefghijklmnopqrstuvwxyz"; -MaybeHandle<String> MutableBigInt::ToStringBasePowerOfTwo(Isolate* isolate, - Handle<BigIntBase> x, - int radix) { +MaybeHandle<String> MutableBigInt::ToStringBasePowerOfTwo( + Isolate* isolate, Handle<BigIntBase> x, int radix, + ShouldThrow should_throw) { STATIC_ASSERT(base::bits::IsPowerOfTwo(kDigitBits)); DCHECK(base::bits::IsPowerOfTwo(radix)); DCHECK(radix >= 2 && radix <= 32); @@ -1945,7 +1946,11 @@ MaybeHandle<String> MutableBigInt::ToStringBasePowerOfTwo(Isolate* isolate, (bit_length + bits_per_char - 1) / bits_per_char + sign; if (chars_required > String::kMaxLength) { - THROW_NEW_ERROR(isolate, NewInvalidStringLengthError(), String); + if (should_throw == kThrowOnError) { + THROW_NEW_ERROR(isolate, NewInvalidStringLengthError(), String); + } else { + return MaybeHandle<String>(); + } } Handle<SeqOneByteString> result = @@ -1988,7 +1993,8 @@ MaybeHandle<String> MutableBigInt::ToStringBasePowerOfTwo(Isolate* isolate, MaybeHandle<String> MutableBigInt::ToStringGeneric(Isolate* isolate, Handle<BigIntBase> x, - int radix) { + int radix, + ShouldThrow should_throw) { DCHECK(radix >= 2 && radix <= 36); DCHECK(!x->is_zero()); Heap* heap = isolate->heap(); @@ -2014,7 +2020,11 @@ MaybeHandle<String> MutableBigInt::ToStringGeneric(Isolate* isolate, chars_required += sign; if (chars_required > String::kMaxLength) { - THROW_NEW_ERROR(isolate, NewInvalidStringLengthError(), String); + if (should_throw == kThrowOnError) { + THROW_NEW_ERROR(isolate, NewInvalidStringLengthError(), String); + } else { + return MaybeHandle<String>(); + } } Handle<SeqOneByteString> result = isolate->factory() diff --git a/deps/v8/src/objects/bigint.h b/deps/v8/src/objects/bigint.h index a30a4779de..6081c5e3f8 100644 --- a/deps/v8/src/objects/bigint.h +++ b/deps/v8/src/objects/bigint.h @@ -173,7 +173,8 @@ class V8_EXPORT_PRIVATE BigInt : public BigIntBase { } static MaybeHandle<String> ToString(Isolate* isolate, Handle<BigInt> bigint, - int radix = 10); + int radix = 10, + ShouldThrow should_throw = kThrowOnError); // "The Number value for x", see: // https://tc39.github.io/ecma262/#sec-ecmascript-language-types-number-type // Returns a Smi or HeapNumber. diff --git a/deps/v8/src/objects/builtin-function-id.h b/deps/v8/src/objects/builtin-function-id.h new file mode 100644 index 0000000000..ed54811a2b --- /dev/null +++ b/deps/v8/src/objects/builtin-function-id.h @@ -0,0 +1,217 @@ +// Copyright 2018 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. + +#ifndef V8_OBJECTS_BUILTIN_FUNCTION_ID_H_ +#define V8_OBJECTS_BUILTIN_FUNCTION_ID_H_ + +#include <stdint.h> + +namespace v8 { +namespace internal { + +// List of builtin functions we want to identify to improve code +// generation. +// +// Each entry has a name of a global object property holding an object +// optionally followed by ".prototype", a name of a builtin function +// on the object (the one the id is set for), and a label. +// +// Installation of ids for the selected builtin functions is handled +// by the bootstrapper. +#define FUNCTIONS_WITH_ID_LIST(V) \ + V(Array, isArray, ArrayIsArray) \ + V(Array.prototype, concat, ArrayConcat) \ + V(Array.prototype, every, ArrayEvery) \ + V(Array.prototype, fill, ArrayFill) \ + V(Array.prototype, filter, ArrayFilter) \ + V(Array.prototype, findIndex, ArrayFindIndex) \ + V(Array.prototype, forEach, ArrayForEach) \ + V(Array.prototype, includes, ArrayIncludes) \ + V(Array.prototype, indexOf, ArrayIndexOf) \ + V(Array.prototype, join, ArrayJoin) \ + V(Array.prototype, lastIndexOf, ArrayLastIndexOf) \ + V(Array.prototype, map, ArrayMap) \ + V(Array.prototype, pop, ArrayPop) \ + V(Array.prototype, push, ArrayPush) \ + V(Array.prototype, reverse, ArrayReverse) \ + V(Array.prototype, shift, ArrayShift) \ + V(Array.prototype, slice, ArraySlice) \ + V(Array.prototype, some, ArraySome) \ + V(Array.prototype, splice, ArraySplice) \ + V(Array.prototype, unshift, ArrayUnshift) \ + V(Date, now, DateNow) \ + V(Date.prototype, getDate, DateGetDate) \ + V(Date.prototype, getDay, DateGetDay) \ + V(Date.prototype, getFullYear, DateGetFullYear) \ + V(Date.prototype, getHours, DateGetHours) \ + V(Date.prototype, getMilliseconds, DateGetMilliseconds) \ + V(Date.prototype, getMinutes, DateGetMinutes) \ + V(Date.prototype, getMonth, DateGetMonth) \ + V(Date.prototype, getSeconds, DateGetSeconds) \ + V(Date.prototype, getTime, DateGetTime) \ + V(Function.prototype, apply, FunctionApply) \ + V(Function.prototype, bind, FunctionBind) \ + V(Function.prototype, call, FunctionCall) \ + V(Object, assign, ObjectAssign) \ + V(Object, create, ObjectCreate) \ + V(Object, is, ObjectIs) \ + V(Object.prototype, hasOwnProperty, ObjectHasOwnProperty) \ + V(Object.prototype, isPrototypeOf, ObjectIsPrototypeOf) \ + V(Object.prototype, toString, ObjectToString) \ + V(RegExp.prototype, compile, RegExpCompile) \ + V(RegExp.prototype, exec, RegExpExec) \ + V(RegExp.prototype, test, RegExpTest) \ + V(RegExp.prototype, toString, RegExpToString) \ + V(String.prototype, charCodeAt, StringCharCodeAt) \ + V(String.prototype, charAt, StringCharAt) \ + V(String.prototype, codePointAt, StringCodePointAt) \ + V(String.prototype, concat, StringConcat) \ + V(String.prototype, endsWith, StringEndsWith) \ + V(String.prototype, includes, StringIncludes) \ + V(String.prototype, indexOf, StringIndexOf) \ + V(String.prototype, lastIndexOf, StringLastIndexOf) \ + V(String.prototype, repeat, StringRepeat) \ + V(String.prototype, slice, StringSlice) \ + V(String.prototype, startsWith, StringStartsWith) \ + V(String.prototype, substr, StringSubstr) \ + V(String.prototype, substring, StringSubstring) \ + V(String.prototype, toLowerCase, StringToLowerCase) \ + V(String.prototype, toString, StringToString) \ + V(String.prototype, toUpperCase, StringToUpperCase) \ + V(String.prototype, trim, StringTrim) \ + V(String.prototype, trimLeft, StringTrimStart) \ + V(String.prototype, trimRight, StringTrimEnd) \ + V(String.prototype, valueOf, StringValueOf) \ + V(String, fromCharCode, StringFromCharCode) \ + V(String, fromCodePoint, StringFromCodePoint) \ + V(String, raw, StringRaw) \ + V(Math, random, MathRandom) \ + V(Math, floor, MathFloor) \ + V(Math, round, MathRound) \ + V(Math, ceil, MathCeil) \ + V(Math, abs, MathAbs) \ + V(Math, log, MathLog) \ + V(Math, log1p, MathLog1p) \ + V(Math, log2, MathLog2) \ + V(Math, log10, MathLog10) \ + V(Math, cbrt, MathCbrt) \ + V(Math, exp, MathExp) \ + V(Math, expm1, MathExpm1) \ + V(Math, sqrt, MathSqrt) \ + V(Math, pow, MathPow) \ + V(Math, max, MathMax) \ + V(Math, min, MathMin) \ + V(Math, cos, MathCos) \ + V(Math, cosh, MathCosh) \ + V(Math, sign, MathSign) \ + V(Math, sin, MathSin) \ + V(Math, sinh, MathSinh) \ + V(Math, tan, MathTan) \ + V(Math, tanh, MathTanh) \ + V(Math, acos, MathAcos) \ + V(Math, acosh, MathAcosh) \ + V(Math, asin, MathAsin) \ + V(Math, asinh, MathAsinh) \ + V(Math, atan, MathAtan) \ + V(Math, atan2, MathAtan2) \ + V(Math, atanh, MathAtanh) \ + V(Math, imul, MathImul) \ + V(Math, clz32, MathClz32) \ + V(Math, fround, MathFround) \ + V(Math, trunc, MathTrunc) \ + V(Number, isFinite, NumberIsFinite) \ + V(Number, isInteger, NumberIsInteger) \ + V(Number, isNaN, NumberIsNaN) \ + V(Number, isSafeInteger, NumberIsSafeInteger) \ + V(Number, parseFloat, NumberParseFloat) \ + V(Number, parseInt, NumberParseInt) \ + V(Number.prototype, toString, NumberToString) \ + V(Map.prototype, clear, MapClear) \ + V(Map.prototype, delete, MapDelete) \ + V(Map.prototype, entries, MapEntries) \ + V(Map.prototype, forEach, MapForEach) \ + V(Map.prototype, has, MapHas) \ + V(Map.prototype, keys, MapKeys) \ + V(Map.prototype, get, MapGet) \ + V(Map.prototype, set, MapSet) \ + V(Map.prototype, values, MapValues) \ + V(Set.prototype, add, SetAdd) \ + V(Set.prototype, clear, SetClear) \ + V(Set.prototype, delete, SetDelete) \ + V(Set.prototype, entries, SetEntries) \ + V(Set.prototype, forEach, SetForEach) \ + V(Set.prototype, has, SetHas) \ + V(Set.prototype, values, SetValues) \ + V(WeakMap.prototype, delete, WeakMapDelete) \ + V(WeakMap.prototype, has, WeakMapHas) \ + V(WeakMap.prototype, set, WeakMapSet) \ + V(WeakSet.prototype, add, WeakSetAdd) \ + V(WeakSet.prototype, delete, WeakSetDelete) \ + V(WeakSet.prototype, has, WeakSetHas) + +#define ATOMIC_FUNCTIONS_WITH_ID_LIST(V) \ + V(Atomics, load, AtomicsLoad) \ + V(Atomics, store, AtomicsStore) \ + V(Atomics, exchange, AtomicsExchange) \ + V(Atomics, compareExchange, AtomicsCompareExchange) \ + V(Atomics, add, AtomicsAdd) \ + V(Atomics, sub, AtomicsSub) \ + V(Atomics, and, AtomicsAnd) \ + V(Atomics, or, AtomicsOr) \ + V(Atomics, xor, AtomicsXor) + +enum class BuiltinFunctionId : uint8_t { + kArrayConstructor, +#define DECL_FUNCTION_ID(ignored1, ignore2, name) k##name, + FUNCTIONS_WITH_ID_LIST(DECL_FUNCTION_ID) + ATOMIC_FUNCTIONS_WITH_ID_LIST(DECL_FUNCTION_ID) +#undef DECL_FUNCTION_ID + // These are manually assigned to special getters during bootstrapping. + kArrayBufferByteLength, + kArrayBufferIsView, + kArrayEntries, + kArrayKeys, + kArrayValues, + kArrayIteratorNext, + kBigIntConstructor, + kMapSize, + kSetSize, + kMapIteratorNext, + kSetIteratorNext, + kDataViewBuffer, + kDataViewByteLength, + kDataViewByteOffset, + kFunctionHasInstance, + kGlobalDecodeURI, + kGlobalDecodeURIComponent, + kGlobalEncodeURI, + kGlobalEncodeURIComponent, + kGlobalEscape, + kGlobalUnescape, + kGlobalIsFinite, + kGlobalIsNaN, + kNumberConstructor, + kSymbolConstructor, + kSymbolPrototypeToString, + kSymbolPrototypeValueOf, + kTypedArrayByteLength, + kTypedArrayByteOffset, + kTypedArrayEntries, + kTypedArrayKeys, + kTypedArrayLength, + kTypedArrayToStringTag, + kTypedArrayValues, + kSharedArrayBufferByteLength, + kStringConstructor, + kStringIterator, + kStringIteratorNext, + kStringToLowerCaseIntl, + kStringToUpperCaseIntl, + kInvalidBuiltinFunctionId = static_cast<uint8_t>(-1), +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_OBJECTS_BUILTIN_FUNCTION_ID_H_ diff --git a/deps/v8/src/objects/code-inl.h b/deps/v8/src/objects/code-inl.h index 308e645c84..34c9f2fc28 100644 --- a/deps/v8/src/objects/code-inl.h +++ b/deps/v8/src/objects/code-inl.h @@ -26,6 +26,12 @@ CAST_ACCESSOR(Code) CAST_ACCESSOR(CodeDataContainer) CAST_ACCESSOR(DependentCode) CAST_ACCESSOR(DeoptimizationData) +CAST_ACCESSOR(SourcePositionTableWithFrameCache) + +ACCESSORS(SourcePositionTableWithFrameCache, source_position_table, ByteArray, + kSourcePositionTableIndex) +ACCESSORS(SourcePositionTableWithFrameCache, stack_frame_cache, + SimpleNumberDictionary, kStackFrameCacheIndex) int AbstractCode::raw_instruction_size() { if (IsCode()) { @@ -133,14 +139,14 @@ BytecodeArray* AbstractCode::GetBytecodeArray() { } DependentCode* DependentCode::next_link() { - return DependentCode::cast(Get(kNextLinkIndex)->ToStrongHeapObject()); + return DependentCode::cast(Get(kNextLinkIndex)->GetHeapObjectAssumeStrong()); } void DependentCode::set_next_link(DependentCode* next) { Set(kNextLinkIndex, HeapObjectReference::Strong(next)); } -int DependentCode::flags() { return Smi::ToInt(Get(kFlagsIndex)->ToSmi()); } +int DependentCode::flags() { return Smi::ToInt(Get(kFlagsIndex)->cast<Smi>()); } void DependentCode::set_flags(int flags) { Set(kFlagsIndex, MaybeObject::FromObject(Smi::FromInt(flags))); diff --git a/deps/v8/src/objects/code.h b/deps/v8/src/objects/code.h index f3c3c0b5b3..1f1d4b71d6 100644 --- a/deps/v8/src/objects/code.h +++ b/deps/v8/src/objects/code.h @@ -27,8 +27,6 @@ class Register; // Code describes objects with on-the-fly generated machine code. class Code : public HeapObject, public NeverReadOnlySpaceObject { public: - using NeverReadOnlySpaceObject::GetHeap; - using NeverReadOnlySpaceObject::GetIsolate; // Opaque data type for encapsulating code flags like kind, inline // cache state, and arguments count. typedef uint32_t Flags; @@ -307,9 +305,6 @@ class Code : public HeapObject, public NeverReadOnlySpaceObject { // object has been moved by delta bytes. void Relocate(intptr_t delta); - // Migrate code described by desc. - void CopyFrom(Heap* heap, const CodeDesc& desc); - // Migrate code from desc without flushing the instruction cache. void CopyFromNoFlush(Heap* heap, const CodeDesc& desc); @@ -459,9 +454,6 @@ class Code : public HeapObject, public NeverReadOnlySpaceObject { // field {Code::code_data_container} itself is immutable. class CodeDataContainer : public HeapObject, public NeverReadOnlySpaceObject { public: - using NeverReadOnlySpaceObject::GetHeap; - using NeverReadOnlySpaceObject::GetIsolate; - DECL_ACCESSORS(next_code_link, Object) DECL_INT_ACCESSORS(kind_specific_flags) @@ -485,15 +477,7 @@ class CodeDataContainer : public HeapObject, public NeverReadOnlySpaceObject { static const int kPointerFieldsStrongEndOffset = kNextCodeLinkOffset; static const int kPointerFieldsWeakEndOffset = kKindSpecificFlagsOffset; - // Ignores weakness. - typedef FixedBodyDescriptor<HeapObject::kHeaderSize, - kPointerFieldsWeakEndOffset, kSize> - BodyDescriptor; - - // Respects weakness. - typedef FixedBodyDescriptor<HeapObject::kHeaderSize, - kPointerFieldsStrongEndOffset, kSize> - BodyDescriptorWeak; + class BodyDescriptor; private: DISALLOW_IMPLICIT_CONSTRUCTORS(CodeDataContainer); @@ -501,9 +485,6 @@ class CodeDataContainer : public HeapObject, public NeverReadOnlySpaceObject { class AbstractCode : public HeapObject, public NeverReadOnlySpaceObject { public: - using NeverReadOnlySpaceObject::GetHeap; - using NeverReadOnlySpaceObject::GetIsolate; - // All code kinds and INTERPRETED_FUNCTION. enum Kind { #define DEFINE_CODE_KIND_ENUM(name) name, @@ -624,13 +605,10 @@ class DependentCode : public WeakFixedArray { }; // Register a code dependency of {cell} on {object}. - static void InstallDependency(Isolate* isolate, MaybeObjectHandle code, + static void InstallDependency(Isolate* isolate, const MaybeObjectHandle& code, Handle<HeapObject> object, DependencyGroup group); - bool Contains(DependencyGroup group, MaybeObject* code); - bool IsEmpty(DependencyGroup group); - void DeoptimizeDependentCodeGroup(Isolate* isolate, DependencyGroup group); bool MarkCodeForDeoptimization(Isolate* isolate, DependencyGroup group); @@ -650,14 +628,14 @@ class DependentCode : public WeakFixedArray { Handle<DependentCode> dep); static Handle<DependentCode> New(Isolate* isolate, DependencyGroup group, - MaybeObjectHandle object, + const MaybeObjectHandle& object, Handle<DependentCode> next); static Handle<DependentCode> EnsureSpace(Isolate* isolate, Handle<DependentCode> entries); static Handle<DependentCode> InsertWeakCode(Isolate* isolate, Handle<DependentCode> entries, DependencyGroup group, - MaybeObjectHandle code); + const MaybeObjectHandle& code); // Compact by removing cleared weak cells and return true if there was // any cleared weak cell. @@ -811,8 +789,6 @@ class BytecodeArray : public FixedArrayBase { static const int kMaxLength = kMaxSize - kHeaderSize; class BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; private: DISALLOW_IMPLICIT_CONSTRUCTORS(BytecodeArray); @@ -903,6 +879,22 @@ class DeoptimizationData : public FixedArray { static int LengthFor(int entry_count) { return IndexForEntry(entry_count); } }; +class SourcePositionTableWithFrameCache : public Tuple2 { + public: + DECL_ACCESSORS(source_position_table, ByteArray) + DECL_ACCESSORS(stack_frame_cache, SimpleNumberDictionary) + + DECL_CAST(SourcePositionTableWithFrameCache) + + static const int kSourcePositionTableIndex = Struct::kHeaderSize; + static const int kStackFrameCacheIndex = + kSourcePositionTableIndex + kPointerSize; + static const int kSize = kStackFrameCacheIndex + kPointerSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(SourcePositionTableWithFrameCache); +}; + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/objects/compilation-cache.h b/deps/v8/src/objects/compilation-cache.h index 76deeb9684..8e118a0e60 100644 --- a/deps/v8/src/objects/compilation-cache.h +++ b/deps/v8/src/objects/compilation-cache.h @@ -69,12 +69,6 @@ class CompilationCacheTable : public HashTable<CompilationCacheTable, CompilationCacheShape>, public NeverReadOnlySpaceObject { public: - using NeverReadOnlySpaceObject::GetHeap; - using NeverReadOnlySpaceObject::GetIsolate; - - // Find cached value for a string key, otherwise return null. - Handle<Object> Lookup(Handle<String> src, Handle<SharedFunctionInfo> shared, - LanguageMode language_mode); MaybeHandle<SharedFunctionInfo> LookupScript(Handle<String> src, Handle<Context> native_context, LanguageMode language_mode); @@ -82,11 +76,6 @@ class CompilationCacheTable Handle<Context> native_context, LanguageMode language_mode, int position); Handle<Object> LookupRegExp(Handle<String> source, JSRegExp::Flags flags); - static Handle<CompilationCacheTable> Put(Handle<CompilationCacheTable> cache, - Handle<String> src, - Handle<SharedFunctionInfo> shared, - LanguageMode language_mode, - Handle<Object> value); static Handle<CompilationCacheTable> PutScript( Handle<CompilationCacheTable> cache, Handle<String> src, Handle<Context> native_context, LanguageMode language_mode, diff --git a/deps/v8/src/objects/debug-objects.cc b/deps/v8/src/objects/debug-objects.cc index b77b6e136e..43fcdb5aee 100644 --- a/deps/v8/src/objects/debug-objects.cc +++ b/deps/v8/src/objects/debug-objects.cc @@ -375,7 +375,7 @@ void CoverageInfo::Print(std::unique_ptr<char[]> function_name) { for (int i = 0; i < SlotCount(); i++) { os << "{" << StartSourcePosition(i) << "," << EndSourcePosition(i) << "}" - << std::endl; + << ": " << BlockCount(i) << std::endl; } } diff --git a/deps/v8/src/objects/debug-objects.h b/deps/v8/src/objects/debug-objects.h index 3b94a4e46e..84f244c758 100644 --- a/deps/v8/src/objects/debug-objects.h +++ b/deps/v8/src/objects/debug-objects.h @@ -21,9 +21,6 @@ class BytecodeArray; // debugged. class DebugInfo : public Struct, public NeverReadOnlySpaceObject { public: - using NeverReadOnlySpaceObject::GetHeap; - using NeverReadOnlySpaceObject::GetIsolate; - enum Flag { kNone = 0, kHasBreakInfo = 1 << 0, diff --git a/deps/v8/src/objects/dictionary.h b/deps/v8/src/objects/dictionary.h index eac358c1cd..6d7ee42eec 100644 --- a/deps/v8/src/objects/dictionary.h +++ b/deps/v8/src/objects/dictionary.h @@ -5,10 +5,10 @@ #ifndef V8_OBJECTS_DICTIONARY_H_ #define V8_OBJECTS_DICTIONARY_H_ -#include "src/objects/hash-table.h" - #include "src/base/export-template.h" #include "src/globals.h" +#include "src/objects/hash-table.h" +#include "src/objects/property-array.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" @@ -113,7 +113,7 @@ class NameDictionaryShape : public BaseDictionaryShape<Handle<Name>> { static inline uint32_t Hash(Isolate* isolate, Handle<Name> key); static inline uint32_t HashForObject(Isolate* isolate, Object* object); static inline Handle<Object> AsHandle(Isolate* isolate, Handle<Name> key); - static inline int GetMapRootIndex(); + static inline RootIndex GetMapRootIndex(); static const int kPrefixSize = 2; static const int kEntrySize = 3; static const int kEntryValueIndex = 1; @@ -216,7 +216,7 @@ class GlobalDictionaryShape : public NameDictionaryShape { static inline Object* Unwrap(Object* key); static inline bool IsKey(ReadOnlyRoots roots, Object* k); static inline bool IsLive(ReadOnlyRoots roots, Object* key); - static inline int GetMapRootIndex(); + static inline RootIndex GetMapRootIndex(); }; class GlobalDictionary @@ -246,7 +246,7 @@ class NumberDictionaryShape : public NumberDictionaryBaseShape { static const int kPrefixSize = 1; static const int kEntrySize = 3; - static inline int GetMapRootIndex(); + static inline RootIndex GetMapRootIndex(); }; class SimpleNumberDictionaryShape : public NumberDictionaryBaseShape { @@ -266,7 +266,7 @@ class SimpleNumberDictionaryShape : public NumberDictionaryBaseShape { UNREACHABLE(); } - static inline int GetMapRootIndex(); + static inline RootIndex GetMapRootIndex(); }; extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) diff --git a/deps/v8/src/objects/fixed-array-inl.h b/deps/v8/src/objects/fixed-array-inl.h index 29c4593cfd..8c24ee80be 100644 --- a/deps/v8/src/objects/fixed-array-inl.h +++ b/deps/v8/src/objects/fixed-array-inl.h @@ -288,8 +288,8 @@ HeapObject* WeakArrayList::Iterator::Next() { if (array_ != nullptr) { while (index_ < array_->length()) { MaybeObject* item = array_->Get(index_++); - DCHECK(item->IsWeakHeapObject() || item->IsClearedWeakHeapObject()); - if (!item->IsClearedWeakHeapObject()) return item->ToWeakHeapObject(); + DCHECK(item->IsWeakOrCleared()); + if (!item->IsCleared()) return item->GetHeapObjectAssumeWeak(); } array_ = nullptr; } diff --git a/deps/v8/src/objects/fixed-array.h b/deps/v8/src/objects/fixed-array.h index 287015ef7c..867d04e638 100644 --- a/deps/v8/src/objects/fixed-array.h +++ b/deps/v8/src/objects/fixed-array.h @@ -188,8 +188,6 @@ class FixedArray : public FixedArrayBase { #endif typedef FlexibleBodyDescriptor<kHeaderSize> BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; protected: // Set operation on FixedArray without using write barriers. Can @@ -256,8 +254,6 @@ class FixedDoubleArray : public FixedArrayBase { DECL_VERIFIER(FixedDoubleArray) class BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; private: DISALLOW_IMPLICIT_CONSTRUCTORS(FixedDoubleArray); @@ -298,7 +294,6 @@ class WeakFixedArray : public HeapObject { DECL_VERIFIER(WeakFixedArray) typedef WeakArrayBodyDescriptor BodyDescriptor; - typedef BodyDescriptor BodyDescriptorWeak; static const int kLengthOffset = HeapObject::kHeaderSize; static const int kHeaderSize = kLengthOffset + kPointerSize; @@ -334,7 +329,7 @@ class WeakArrayList : public HeapObject { static Handle<WeakArrayList> AddToEnd(Isolate* isolate, Handle<WeakArrayList> array, - MaybeObjectHandle value); + const MaybeObjectHandle& value); inline MaybeObject* Get(int index) const; @@ -361,7 +356,6 @@ class WeakArrayList : public HeapObject { inline void synchronized_set_capacity(int value); typedef WeakArrayBodyDescriptor BodyDescriptor; - typedef BodyDescriptor BodyDescriptorWeak; static const int kCapacityOffset = HeapObject::kHeaderSize; static const int kLengthOffset = kCapacityOffset + kPointerSize; @@ -381,7 +375,7 @@ class WeakArrayList : public HeapObject { // around in the array - this method can only be used in cases where the user // doesn't care about the indices! Users should make sure there are no // duplicates. - bool RemoveOne(MaybeObjectHandle value); + bool RemoveOne(const MaybeObjectHandle& value); class Iterator { public: @@ -442,7 +436,6 @@ class ArrayList : public FixedArray { // Return a copy of the list of size Length() without the first entry. The // number returned by Length() is stored in the first entry. static Handle<FixedArray> Elements(Isolate* isolate, Handle<ArrayList> array); - bool IsFull(); DECL_CAST(ArrayList) private: @@ -521,8 +514,6 @@ class ByteArray : public FixedArrayBase { "ByteArray maxLength not a Smi"); class BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; private: DISALLOW_IMPLICIT_CONSTRUCTORS(ByteArray); @@ -587,8 +578,6 @@ class FixedTypedArrayBase : public FixedArrayBase { static const size_t kMaxLength = Smi::kMaxValue; class BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; inline int size() const; diff --git a/deps/v8/src/objects/frame-array.h b/deps/v8/src/objects/frame-array.h index 0ae22f9526..5bccfb5807 100644 --- a/deps/v8/src/objects/frame-array.h +++ b/deps/v8/src/objects/frame-array.h @@ -50,7 +50,8 @@ class FrameArray : public FixedArray { kIsAsmJsWasmFrame = 1 << 2, kIsStrict = 1 << 3, kIsConstructor = 1 << 4, - kAsmJsAtNumberConversion = 1 << 5 + kAsmJsAtNumberConversion = 1 << 5, + kIsAsync = 1 << 6 }; static Handle<FrameArray> AppendJSFrame(Handle<FrameArray> in, diff --git a/deps/v8/src/objects/hash-table-inl.h b/deps/v8/src/objects/hash-table-inl.h index 1f2c09316d..bc391fede6 100644 --- a/deps/v8/src/objects/hash-table-inl.h +++ b/deps/v8/src/objects/hash-table-inl.h @@ -58,12 +58,12 @@ void HashTableBase::SetNumberOfDeletedElements(int nod) { } template <typename Key> -int BaseShape<Key>::GetMapRootIndex() { - return Heap::kHashTableMapRootIndex; +RootIndex BaseShape<Key>::GetMapRootIndex() { + return RootIndex::kHashTableMap; } -int EphemeronHashTableShape::GetMapRootIndex() { - return Heap::kEphemeronHashTableMapRootIndex; +RootIndex EphemeronHashTableShape::GetMapRootIndex() { + return RootIndex::kEphemeronHashTableMap; } template <typename Derived, typename Shape> diff --git a/deps/v8/src/objects/hash-table.h b/deps/v8/src/objects/hash-table.h index aa86865abf..66d3f6dfb2 100644 --- a/deps/v8/src/objects/hash-table.h +++ b/deps/v8/src/objects/hash-table.h @@ -55,7 +55,7 @@ template <typename KeyT> class BaseShape { public: typedef KeyT Key; - static inline int GetMapRootIndex(); + static inline RootIndex GetMapRootIndex(); static const bool kNeedsHoleCheck = true; static Object* Unwrap(Object* key) { return key; } static inline bool IsKey(ReadOnlyRoots roots, Object* key); @@ -244,7 +244,7 @@ class HashTableKey { virtual bool IsMatch(Object* other) = 0; // Returns the hash value for this key. // Required. - virtual ~HashTableKey() {} + virtual ~HashTableKey() = default; uint32_t Hash() const { return hash_; } @@ -321,7 +321,7 @@ class ObjectHashTable class EphemeronHashTableShape : public ObjectHashTableShape { public: - static inline int GetMapRootIndex(); + static inline RootIndex GetMapRootIndex(); }; // EphemeronHashTable is similar to ObjectHashTable but gets special treatment diff --git a/deps/v8/src/objects/intl-objects.cc b/deps/v8/src/objects/intl-objects.cc index b9ceecf9a1..dcacb4dd2f 100644 --- a/deps/v8/src/objects/intl-objects.cc +++ b/deps/v8/src/objects/intl-objects.cc @@ -21,543 +21,44 @@ #include "src/isolate.h" #include "src/objects-inl.h" #include "src/objects/js-collator-inl.h" -#include "src/objects/managed.h" +#include "src/objects/js-date-time-format-inl.h" +#include "src/objects/js-number-format-inl.h" #include "src/objects/string.h" #include "src/property-descriptor.h" #include "unicode/brkiter.h" -#include "unicode/bytestream.h" -#include "unicode/calendar.h" #include "unicode/coll.h" -#include "unicode/curramt.h" -#include "unicode/dcfmtsym.h" #include "unicode/decimfmt.h" -#include "unicode/dtfmtsym.h" -#include "unicode/dtptngen.h" -#include "unicode/gregocal.h" #include "unicode/locid.h" #include "unicode/numfmt.h" #include "unicode/numsys.h" -#include "unicode/plurrule.h" -#include "unicode/rbbi.h" #include "unicode/regex.h" #include "unicode/smpdtfmt.h" #include "unicode/timezone.h" -#include "unicode/uchar.h" #include "unicode/ucol.h" -#include "unicode/ucurr.h" -#include "unicode/unum.h" -#include "unicode/upluralrules.h" #include "unicode/ures.h" #include "unicode/uvernum.h" #include "unicode/uversion.h" -#if U_ICU_VERSION_MAJOR_NUM >= 59 -#include "unicode/char16ptr.h" -#endif - namespace v8 { namespace internal { -namespace { - -bool ExtractStringSetting(Isolate* isolate, Handle<JSObject> options, - const char* key, icu::UnicodeString* setting) { - v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate); - Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key); - Handle<Object> object = - JSReceiver::GetProperty(isolate, options, str).ToHandleChecked(); - if (object->IsString()) { - v8::String::Utf8Value utf8_string( - v8_isolate, v8::Utils::ToLocal(Handle<String>::cast(object))); - *setting = icu::UnicodeString::fromUTF8(*utf8_string); - return true; - } - return false; -} - -bool ExtractIntegerSetting(Isolate* isolate, Handle<JSObject> options, - const char* key, int32_t* value) { - Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key); - Handle<Object> object = - JSReceiver::GetProperty(isolate, options, str).ToHandleChecked(); - if (object->IsNumber()) { - return object->ToInt32(value); - } - return false; -} - -bool ExtractBooleanSetting(Isolate* isolate, Handle<JSObject> options, - const char* key, bool* value) { - Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key); - Handle<Object> object = - JSReceiver::GetProperty(isolate, options, str).ToHandleChecked(); - if (object->IsBoolean()) { - *value = object->BooleanValue(isolate); - return true; - } - return false; -} - -icu::SimpleDateFormat* CreateICUDateFormat(Isolate* isolate, - const icu::Locale& icu_locale, - Handle<JSObject> options) { - // Create time zone as specified by the user. We have to re-create time zone - // since calendar takes ownership. - icu::TimeZone* tz = nullptr; - icu::UnicodeString timezone; - if (ExtractStringSetting(isolate, options, "timeZone", &timezone)) { - tz = icu::TimeZone::createTimeZone(timezone); - } else { - tz = icu::TimeZone::createDefault(); - } - - // Create a calendar using locale, and apply time zone to it. - UErrorCode status = U_ZERO_ERROR; - icu::Calendar* calendar = - icu::Calendar::createInstance(tz, icu_locale, status); - - if (calendar->getDynamicClassID() == - icu::GregorianCalendar::getStaticClassID()) { - icu::GregorianCalendar* gc = (icu::GregorianCalendar*)calendar; - UErrorCode status = U_ZERO_ERROR; - // The beginning of ECMAScript time, namely -(2**53) - const double start_of_time = -9007199254740992; - gc->setGregorianChange(start_of_time, status); - DCHECK(U_SUCCESS(status)); - } - - // Make formatter from skeleton. Calendar and numbering system are added - // to the locale as Unicode extension (if they were specified at all). - icu::SimpleDateFormat* date_format = nullptr; - icu::UnicodeString skeleton; - if (ExtractStringSetting(isolate, options, "skeleton", &skeleton)) { - // See https://github.com/tc39/ecma402/issues/225 . The best pattern - // generation needs to be done in the base locale according to the - // current spec however odd it may be. See also crbug.com/826549 . - // This is a temporary work-around to get v8's external behavior to match - // the current spec, but does not follow the spec provisions mentioned - // in the above Ecma 402 issue. - // TODO(jshin): The spec may need to be revised because using the base - // locale for the pattern match is not quite right. Moreover, what to - // do with 'related year' part when 'chinese/dangi' calendar is specified - // has to be discussed. Revisit once the spec is clarified/revised. - icu::Locale no_extension_locale(icu_locale.getBaseName()); - std::unique_ptr<icu::DateTimePatternGenerator> generator( - icu::DateTimePatternGenerator::createInstance(no_extension_locale, - status)); - icu::UnicodeString pattern; - if (U_SUCCESS(status)) - pattern = generator->getBestPattern(skeleton, status); - - date_format = new icu::SimpleDateFormat(pattern, icu_locale, status); - if (U_SUCCESS(status)) { - date_format->adoptCalendar(calendar); - } - } - - if (U_FAILURE(status)) { - delete calendar; - delete date_format; - date_format = nullptr; - } - - return date_format; -} - -void SetResolvedDateSettings(Isolate* isolate, const icu::Locale& icu_locale, - icu::SimpleDateFormat* date_format, - Handle<JSObject> resolved) { - Factory* factory = isolate->factory(); - UErrorCode status = U_ZERO_ERROR; - icu::UnicodeString pattern; - date_format->toPattern(pattern); - JSObject::SetProperty( - isolate, resolved, factory->intl_pattern_symbol(), - factory - ->NewStringFromTwoByte(Vector<const uint16_t>( - reinterpret_cast<const uint16_t*>(pattern.getBuffer()), - pattern.length())) - .ToHandleChecked(), - LanguageMode::kSloppy) - .Assert(); - - // Set time zone and calendar. - const icu::Calendar* calendar = date_format->getCalendar(); - // getType() returns legacy calendar type name instead of LDML/BCP47 calendar - // key values. intl.js maps them to BCP47 values for key "ca". - // TODO(jshin): Consider doing it here, instead. - const char* calendar_name = calendar->getType(); - JSObject::SetProperty( - isolate, resolved, factory->NewStringFromStaticChars("calendar"), - factory->NewStringFromAsciiChecked(calendar_name), LanguageMode::kSloppy) - .Assert(); - - const icu::TimeZone& tz = calendar->getTimeZone(); - icu::UnicodeString time_zone; - tz.getID(time_zone); - - icu::UnicodeString canonical_time_zone; - icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status); - if (U_SUCCESS(status)) { - // In CLDR (http://unicode.org/cldr/trac/ticket/9943), Etc/UTC is made - // a separate timezone ID from Etc/GMT even though they're still the same - // timezone. We have Etc/UTC because 'UTC', 'Etc/Universal', - // 'Etc/Zulu' and others are turned to 'Etc/UTC' by ICU. Etc/GMT comes - // from Etc/GMT0, Etc/GMT+0, Etc/GMT-0, Etc/Greenwich. - // ecma402##sec-canonicalizetimezonename step 3 - if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/UTC") || - canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) { - JSObject::SetProperty( - isolate, resolved, factory->NewStringFromStaticChars("timeZone"), - factory->NewStringFromStaticChars("UTC"), LanguageMode::kSloppy) - .Assert(); - } else { - JSObject::SetProperty(isolate, resolved, - factory->NewStringFromStaticChars("timeZone"), - factory - ->NewStringFromTwoByte(Vector<const uint16_t>( - reinterpret_cast<const uint16_t*>( - canonical_time_zone.getBuffer()), - canonical_time_zone.length())) - .ToHandleChecked(), - LanguageMode::kSloppy) - .Assert(); - } - } - - // Ugly hack. ICU doesn't expose numbering system in any way, so we have - // to assume that for given locale NumberingSystem constructor produces the - // same digits as NumberFormat/Calendar would. - status = U_ZERO_ERROR; - icu::NumberingSystem* numbering_system = - icu::NumberingSystem::createInstance(icu_locale, status); - if (U_SUCCESS(status)) { - const char* ns = numbering_system->getName(); - JSObject::SetProperty( - isolate, resolved, factory->NewStringFromStaticChars("numberingSystem"), - factory->NewStringFromAsciiChecked(ns), LanguageMode::kSloppy) - .Assert(); - } else { - JSObject::SetProperty(isolate, resolved, - factory->NewStringFromStaticChars("numberingSystem"), - factory->undefined_value(), LanguageMode::kSloppy) - .Assert(); - } - delete numbering_system; - - // Set the locale - char result[ULOC_FULLNAME_CAPACITY]; - status = U_ZERO_ERROR; - uloc_toLanguageTag(icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, - FALSE, &status); - if (U_SUCCESS(status)) { - JSObject::SetProperty( - isolate, resolved, factory->NewStringFromStaticChars("locale"), - factory->NewStringFromAsciiChecked(result), LanguageMode::kSloppy) - .Assert(); - } else { - // This would never happen, since we got the locale from ICU. - JSObject::SetProperty( - isolate, resolved, factory->NewStringFromStaticChars("locale"), - factory->NewStringFromStaticChars("und"), LanguageMode::kSloppy) - .Assert(); - } -} - -void SetNumericSettings(Isolate* isolate, icu::DecimalFormat* number_format, - Handle<JSObject> options) { - int32_t digits; - if (ExtractIntegerSetting(isolate, options, "minimumIntegerDigits", - &digits)) { - number_format->setMinimumIntegerDigits(digits); - } - - if (ExtractIntegerSetting(isolate, options, "minimumFractionDigits", - &digits)) { - number_format->setMinimumFractionDigits(digits); - } - - if (ExtractIntegerSetting(isolate, options, "maximumFractionDigits", - &digits)) { - number_format->setMaximumFractionDigits(digits); - } - - bool significant_digits_used = false; - if (ExtractIntegerSetting(isolate, options, "minimumSignificantDigits", - &digits)) { - number_format->setMinimumSignificantDigits(digits); - significant_digits_used = true; - } - - if (ExtractIntegerSetting(isolate, options, "maximumSignificantDigits", - &digits)) { - number_format->setMaximumSignificantDigits(digits); - significant_digits_used = true; - } - - number_format->setSignificantDigitsUsed(significant_digits_used); - - number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp); -} - -icu::DecimalFormat* CreateICUNumberFormat(Isolate* isolate, - const icu::Locale& icu_locale, - Handle<JSObject> options) { - // Make formatter from options. Numbering system is added - // to the locale as Unicode extension (if it was specified at all). - UErrorCode status = U_ZERO_ERROR; - icu::DecimalFormat* number_format = nullptr; - icu::UnicodeString style; - icu::UnicodeString currency; - if (ExtractStringSetting(isolate, options, "style", &style)) { - if (style == UNICODE_STRING_SIMPLE("currency")) { - icu::UnicodeString display; - ExtractStringSetting(isolate, options, "currency", ¤cy); - ExtractStringSetting(isolate, options, "currencyDisplay", &display); - -#if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6) - icu::NumberFormat::EStyles format_style; - if (display == UNICODE_STRING_SIMPLE("code")) { - format_style = icu::NumberFormat::kIsoCurrencyStyle; - } else if (display == UNICODE_STRING_SIMPLE("name")) { - format_style = icu::NumberFormat::kPluralCurrencyStyle; - } else { - format_style = icu::NumberFormat::kCurrencyStyle; - } -#else // ICU version is 4.8 or above (we ignore versions below 4.0). - UNumberFormatStyle format_style; - if (display == UNICODE_STRING_SIMPLE("code")) { - format_style = UNUM_CURRENCY_ISO; - } else if (display == UNICODE_STRING_SIMPLE("name")) { - format_style = UNUM_CURRENCY_PLURAL; - } else { - format_style = UNUM_CURRENCY; - } -#endif - - number_format = static_cast<icu::DecimalFormat*>( - icu::NumberFormat::createInstance(icu_locale, format_style, status)); - - if (U_FAILURE(status)) { - delete number_format; - return nullptr; - } - } else if (style == UNICODE_STRING_SIMPLE("percent")) { - number_format = static_cast<icu::DecimalFormat*>( - icu::NumberFormat::createPercentInstance(icu_locale, status)); - if (U_FAILURE(status)) { - delete number_format; - return nullptr; - } - // Make sure 1.1% doesn't go into 2%. - number_format->setMinimumFractionDigits(1); - } else { - // Make a decimal instance by default. - number_format = static_cast<icu::DecimalFormat*>( - icu::NumberFormat::createInstance(icu_locale, status)); - } - } - - if (U_FAILURE(status)) { - delete number_format; - return nullptr; - } - - // Set all options. - if (!currency.isEmpty()) { - number_format->setCurrency(currency.getBuffer(), status); - } - - SetNumericSettings(isolate, number_format, options); - - bool grouping; - if (ExtractBooleanSetting(isolate, options, "useGrouping", &grouping)) { - number_format->setGroupingUsed(grouping); - } - - return number_format; -} - -void SetResolvedNumericSettings(Isolate* isolate, const icu::Locale& icu_locale, - icu::DecimalFormat* number_format, - Handle<JSObject> resolved) { - Factory* factory = isolate->factory(); - - JSObject::SetProperty( - isolate, resolved, - factory->NewStringFromStaticChars("minimumIntegerDigits"), - factory->NewNumberFromInt(number_format->getMinimumIntegerDigits()), - LanguageMode::kSloppy) - .Assert(); - - JSObject::SetProperty( - isolate, resolved, - factory->NewStringFromStaticChars("minimumFractionDigits"), - factory->NewNumberFromInt(number_format->getMinimumFractionDigits()), - LanguageMode::kSloppy) - .Assert(); - - JSObject::SetProperty( - isolate, resolved, - factory->NewStringFromStaticChars("maximumFractionDigits"), - factory->NewNumberFromInt(number_format->getMaximumFractionDigits()), - LanguageMode::kSloppy) - .Assert(); - - Handle<String> key = - factory->NewStringFromStaticChars("minimumSignificantDigits"); - Maybe<bool> maybe = JSReceiver::HasOwnProperty(resolved, key); - CHECK(maybe.IsJust()); - if (maybe.FromJust()) { - JSObject::SetProperty( - isolate, resolved, - factory->NewStringFromStaticChars("minimumSignificantDigits"), - factory->NewNumberFromInt(number_format->getMinimumSignificantDigits()), - LanguageMode::kSloppy) - .Assert(); - } - - key = factory->NewStringFromStaticChars("maximumSignificantDigits"); - maybe = JSReceiver::HasOwnProperty(resolved, key); - CHECK(maybe.IsJust()); - if (maybe.FromJust()) { - JSObject::SetProperty( - isolate, resolved, - factory->NewStringFromStaticChars("maximumSignificantDigits"), - factory->NewNumberFromInt(number_format->getMaximumSignificantDigits()), - LanguageMode::kSloppy) - .Assert(); - } - - // Set the locale - char result[ULOC_FULLNAME_CAPACITY]; - UErrorCode status = U_ZERO_ERROR; - uloc_toLanguageTag(icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, - FALSE, &status); - if (U_SUCCESS(status)) { - JSObject::SetProperty( - isolate, resolved, factory->NewStringFromStaticChars("locale"), - factory->NewStringFromAsciiChecked(result), LanguageMode::kSloppy) - .Assert(); - } else { - // This would never happen, since we got the locale from ICU. - JSObject::SetProperty( - isolate, resolved, factory->NewStringFromStaticChars("locale"), - factory->NewStringFromStaticChars("und"), LanguageMode::kSloppy) - .Assert(); - } -} - -void SetResolvedNumberSettings(Isolate* isolate, const icu::Locale& icu_locale, - icu::DecimalFormat* number_format, - Handle<JSObject> resolved) { - Factory* factory = isolate->factory(); - - // Set resolved currency code in options.currency if not empty. - icu::UnicodeString currency(number_format->getCurrency()); - if (!currency.isEmpty()) { - JSObject::SetProperty( - isolate, resolved, factory->NewStringFromStaticChars("currency"), - factory - ->NewStringFromTwoByte(Vector<const uint16_t>( - reinterpret_cast<const uint16_t*>(currency.getBuffer()), - currency.length())) - .ToHandleChecked(), - LanguageMode::kSloppy) - .Assert(); - } - +std::string Intl::GetNumberingSystem(const icu::Locale& icu_locale) { // Ugly hack. ICU doesn't expose numbering system in any way, so we have // to assume that for given locale NumberingSystem constructor produces the // same digits as NumberFormat/Calendar would. UErrorCode status = U_ZERO_ERROR; - icu::NumberingSystem* numbering_system = - icu::NumberingSystem::createInstance(icu_locale, status); - if (U_SUCCESS(status)) { - const char* ns = numbering_system->getName(); - JSObject::SetProperty( - isolate, resolved, factory->NewStringFromStaticChars("numberingSystem"), - factory->NewStringFromAsciiChecked(ns), LanguageMode::kSloppy) - .Assert(); - } else { - JSObject::SetProperty(isolate, resolved, - factory->NewStringFromStaticChars("numberingSystem"), - factory->undefined_value(), LanguageMode::kSloppy) - .Assert(); - } - delete numbering_system; - - JSObject::SetProperty(isolate, resolved, - factory->NewStringFromStaticChars("useGrouping"), - factory->ToBoolean(number_format->isGroupingUsed()), - LanguageMode::kSloppy) - .Assert(); - - SetResolvedNumericSettings(isolate, icu_locale, number_format, resolved); -} - -icu::BreakIterator* CreateICUBreakIterator(Isolate* isolate, - const icu::Locale& icu_locale, - Handle<JSObject> options) { - UErrorCode status = U_ZERO_ERROR; - icu::BreakIterator* break_iterator = nullptr; - icu::UnicodeString type; - if (!ExtractStringSetting(isolate, options, "type", &type)) return nullptr; - - if (type == UNICODE_STRING_SIMPLE("character")) { - break_iterator = - icu::BreakIterator::createCharacterInstance(icu_locale, status); - } else if (type == UNICODE_STRING_SIMPLE("sentence")) { - break_iterator = - icu::BreakIterator::createSentenceInstance(icu_locale, status); - } else if (type == UNICODE_STRING_SIMPLE("line")) { - break_iterator = icu::BreakIterator::createLineInstance(icu_locale, status); - } else { - // Defualt is word iterator. - break_iterator = icu::BreakIterator::createWordInstance(icu_locale, status); - } - - if (U_FAILURE(status)) { - delete break_iterator; - return nullptr; - } - - isolate->CountUsage(v8::Isolate::UseCounterFeature::kBreakIterator); - - return break_iterator; -} - -void SetResolvedBreakIteratorSettings(Isolate* isolate, - const icu::Locale& icu_locale, - icu::BreakIterator* break_iterator, - Handle<JSObject> resolved) { - Factory* factory = isolate->factory(); - UErrorCode status = U_ZERO_ERROR; - - // Set the locale - char result[ULOC_FULLNAME_CAPACITY]; - status = U_ZERO_ERROR; - uloc_toLanguageTag(icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, - FALSE, &status); + std::unique_ptr<icu::NumberingSystem> numbering_system( + icu::NumberingSystem::createInstance(icu_locale, status)); + std::string value; if (U_SUCCESS(status)) { - JSObject::SetProperty( - isolate, resolved, factory->NewStringFromStaticChars("locale"), - factory->NewStringFromAsciiChecked(result), LanguageMode::kSloppy) - .Assert(); - } else { - // This would never happen, since we got the locale from ICU. - JSObject::SetProperty( - isolate, resolved, factory->NewStringFromStaticChars("locale"), - factory->NewStringFromStaticChars("und"), LanguageMode::kSloppy) - .Assert(); + value = numbering_system->getName(); } + return value; } -MaybeHandle<JSObject> CachedOrNewService(Isolate* isolate, - Handle<String> service, - Handle<Object> locales, - Handle<Object> options, - Handle<Object> internal_options) { +MaybeHandle<JSObject> Intl::CachedOrNewService( + Isolate* isolate, Handle<String> service, Handle<Object> locales, + Handle<Object> options, Handle<Object> internal_options) { Handle<Object> result; Handle<Object> undefined_value(ReadOnlyRoots(isolate).undefined_value(), isolate); @@ -569,7 +70,6 @@ MaybeHandle<JSObject> CachedOrNewService(Isolate* isolate, JSArray); return Handle<JSObject>::cast(result); } -} // namespace icu::Locale Intl::CreateICULocale(Isolate* isolate, Handle<String> bcp47_locale_str) { @@ -583,14 +83,18 @@ icu::Locale Intl::CreateICULocale(Isolate* isolate, // Convert BCP47 into ICU locale format. UErrorCode status = U_ZERO_ERROR; char icu_result[ULOC_FULLNAME_CAPACITY]; - int icu_length = 0; + int parsed_length = 0; // bcp47_locale_str should be a canonicalized language tag, which // means this shouldn't fail. uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, - &icu_length, &status); + &parsed_length, &status); CHECK(U_SUCCESS(status)); - CHECK_LT(0, icu_length); + + // bcp47_locale is already checked for its structural validity + // so that it should be parsed completely. + int bcp47length = bcp47_locale.length(); + CHECK_EQ(bcp47length, parsed_length); icu::Locale icu_locale(icu_result); if (icu_locale.isBogus()) { @@ -601,253 +105,6 @@ icu::Locale Intl::CreateICULocale(Isolate* isolate, } // static -icu::SimpleDateFormat* DateFormat::InitializeDateTimeFormat( - Isolate* isolate, Handle<String> locale, Handle<JSObject> options, - Handle<JSObject> resolved) { - icu::Locale icu_locale = Intl::CreateICULocale(isolate, locale); - DCHECK(!icu_locale.isBogus()); - - icu::SimpleDateFormat* date_format = - CreateICUDateFormat(isolate, icu_locale, options); - if (!date_format) { - // Remove extensions and try again. - icu::Locale no_extension_locale(icu_locale.getBaseName()); - date_format = CreateICUDateFormat(isolate, no_extension_locale, options); - - if (!date_format) { - FATAL("Failed to create ICU date format, are ICU data files missing?"); - } - - // Set resolved settings (pattern, numbering system, calendar). - SetResolvedDateSettings(isolate, no_extension_locale, date_format, - resolved); - } else { - SetResolvedDateSettings(isolate, icu_locale, date_format, resolved); - } - - CHECK_NOT_NULL(date_format); - return date_format; -} - -icu::SimpleDateFormat* DateFormat::UnpackDateFormat(Handle<JSObject> obj) { - return reinterpret_cast<icu::SimpleDateFormat*>( - obj->GetEmbedderField(DateFormat::kSimpleDateFormatIndex)); -} - -void DateFormat::DeleteDateFormat(const v8::WeakCallbackInfo<void>& data) { - delete reinterpret_cast<icu::SimpleDateFormat*>(data.GetInternalField(0)); - GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter())); -} - -MaybeHandle<JSObject> DateFormat::Unwrap(Isolate* isolate, - Handle<JSReceiver> receiver, - const char* method_name) { - Handle<Context> native_context = - Handle<Context>(isolate->context()->native_context(), isolate); - Handle<JSFunction> constructor = Handle<JSFunction>( - JSFunction::cast(native_context->intl_date_time_format_function()), - isolate); - Handle<String> method_name_str = - isolate->factory()->NewStringFromAsciiChecked(method_name); - - return Intl::UnwrapReceiver(isolate, receiver, constructor, - Intl::Type::kDateTimeFormat, method_name_str, - true); -} - -// ecma402/#sec-formatdatetime -// FormatDateTime( dateTimeFormat, x ) -MaybeHandle<String> DateFormat::FormatDateTime( - Isolate* isolate, Handle<JSObject> date_time_format_holder, double x) { - double date_value = DateCache::TimeClip(x); - if (std::isnan(date_value)) { - THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue), - String); - } - - CHECK(Intl::IsObjectOfType(isolate, date_time_format_holder, - Intl::Type::kDateTimeFormat)); - icu::SimpleDateFormat* date_format = - DateFormat::UnpackDateFormat(date_time_format_holder); - CHECK_NOT_NULL(date_format); - - icu::UnicodeString result; - date_format->format(date_value, result); - - return isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>( - reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length())); -} - -// ecma402/#sec-datetime-format-functions -// DateTime Format Functions -MaybeHandle<String> DateFormat::DateTimeFormat( - Isolate* isolate, Handle<JSObject> date_time_format_holder, - Handle<Object> date) { - // 2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]] - // internal slot. - DCHECK(Intl::IsObjectOfType(isolate, date_time_format_holder, - Intl::Type::kDateTimeFormat)); - - // 3. If date is not provided or is undefined, then - double x; - if (date->IsUndefined()) { - // 3.a Let x be Call(%Date_now%, undefined). - x = JSDate::CurrentTimeValue(isolate); - } else { - // 4. Else, - // a. Let x be ? ToNumber(date). - ASSIGN_RETURN_ON_EXCEPTION(isolate, date, Object::ToNumber(isolate, date), - String); - CHECK(date->IsNumber()); - x = date->Number(); - } - // 5. Return FormatDateTime(dtf, x). - return DateFormat::FormatDateTime(isolate, date_time_format_holder, x); -} - -MaybeHandle<String> DateFormat::ToLocaleDateTime( - Isolate* isolate, Handle<Object> date, Handle<Object> locales, - Handle<Object> options, const char* required, const char* defaults, - const char* service) { - Factory* factory = isolate->factory(); - // 1. Let x be ? thisTimeValue(this value); - if (!date->IsJSDate()) { - THROW_NEW_ERROR(isolate, - NewTypeError(MessageTemplate::kMethodInvokedOnWrongType, - factory->NewStringFromStaticChars("Date")), - String); - } - - double const x = Handle<JSDate>::cast(date)->value()->Number(); - // 2. If x is NaN, return "Invalid Date" - if (std::isnan(x)) { - return factory->NewStringFromStaticChars("Invalid Date"); - } - - // 3. Let options be ? ToDateTimeOptions(options, required, defaults). - Handle<JSObject> internal_options; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, internal_options, - DateFormat::ToDateTimeOptions(isolate, options, required, defaults), - String); - - // 4. Let dateFormat be ? Construct(%DateTimeFormat%, « locales, options »). - Handle<JSObject> date_format; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, date_format, - CachedOrNewService(isolate, factory->NewStringFromAsciiChecked(service), - locales, options, internal_options), - String); - - // 5. Return FormatDateTime(dateFormat, x). - return DateFormat::FormatDateTime(isolate, date_format, x); -} - -icu::DecimalFormat* NumberFormat::InitializeNumberFormat( - Isolate* isolate, Handle<String> locale, Handle<JSObject> options, - Handle<JSObject> resolved) { - icu::Locale icu_locale = Intl::CreateICULocale(isolate, locale); - DCHECK(!icu_locale.isBogus()); - - icu::DecimalFormat* number_format = - CreateICUNumberFormat(isolate, icu_locale, options); - if (!number_format) { - // Remove extensions and try again. - icu::Locale no_extension_locale(icu_locale.getBaseName()); - number_format = - CreateICUNumberFormat(isolate, no_extension_locale, options); - - if (!number_format) { - FATAL("Failed to create ICU number format, are ICU data files missing?"); - } - - // Set resolved settings (pattern, numbering system). - SetResolvedNumberSettings(isolate, no_extension_locale, number_format, - resolved); - } else { - SetResolvedNumberSettings(isolate, icu_locale, number_format, resolved); - } - - CHECK_NOT_NULL(number_format); - return number_format; -} - -icu::DecimalFormat* NumberFormat::UnpackNumberFormat(Handle<JSObject> obj) { - return reinterpret_cast<icu::DecimalFormat*>( - obj->GetEmbedderField(NumberFormat::kDecimalFormatIndex)); -} - -void NumberFormat::DeleteNumberFormat(const v8::WeakCallbackInfo<void>& data) { - delete reinterpret_cast<icu::DecimalFormat*>(data.GetInternalField(0)); - GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter())); -} - -icu::BreakIterator* V8BreakIterator::InitializeBreakIterator( - Isolate* isolate, Handle<String> locale, Handle<JSObject> options, - Handle<JSObject> resolved) { - icu::Locale icu_locale = Intl::CreateICULocale(isolate, locale); - DCHECK(!icu_locale.isBogus()); - - icu::BreakIterator* break_iterator = - CreateICUBreakIterator(isolate, icu_locale, options); - if (!break_iterator) { - // Remove extensions and try again. - icu::Locale no_extension_locale(icu_locale.getBaseName()); - break_iterator = - CreateICUBreakIterator(isolate, no_extension_locale, options); - - if (!break_iterator) { - FATAL("Failed to create ICU break iterator, are ICU data files missing?"); - } - - // Set resolved settings (locale). - SetResolvedBreakIteratorSettings(isolate, no_extension_locale, - break_iterator, resolved); - } else { - SetResolvedBreakIteratorSettings(isolate, icu_locale, break_iterator, - resolved); - } - - CHECK_NOT_NULL(break_iterator); - return break_iterator; -} - -icu::BreakIterator* V8BreakIterator::UnpackBreakIterator(Handle<JSObject> obj) { - return reinterpret_cast<icu::BreakIterator*>( - obj->GetEmbedderField(V8BreakIterator::kBreakIteratorIndex)); -} - -void V8BreakIterator::DeleteBreakIterator( - const v8::WeakCallbackInfo<void>& data) { - delete reinterpret_cast<icu::BreakIterator*>(data.GetInternalField(0)); - delete reinterpret_cast<icu::UnicodeString*>(data.GetInternalField(1)); - GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter())); -} - -void V8BreakIterator::AdoptText(Isolate* isolate, - Handle<JSObject> break_iterator_holder, - Handle<String> text) { - icu::BreakIterator* break_iterator = - V8BreakIterator::UnpackBreakIterator(break_iterator_holder); - CHECK_NOT_NULL(break_iterator); - - icu::UnicodeString* u_text = reinterpret_cast<icu::UnicodeString*>( - break_iterator_holder->GetEmbedderField( - V8BreakIterator::kUnicodeStringIndex)); - delete u_text; - - int length = text->length(); - text = String::Flatten(isolate, text); - DisallowHeapAllocation no_gc; - String::FlatContent flat = text->GetFlatContent(); - std::unique_ptr<uc16[]> sap; - const UChar* text_value = GetUCharBufferFromFlat(flat, &sap, length); - u_text = new icu::UnicodeString(text_value, length); - break_iterator_holder->SetEmbedderField(V8BreakIterator::kUnicodeStringIndex, - reinterpret_cast<Smi*>(u_text)); - - break_iterator->setText(*u_text); -} MaybeHandle<String> Intl::ToString(Isolate* isolate, const icu::UnicodeString& string) { @@ -902,12 +159,18 @@ void Intl::AddElement(Isolate* isolate, Handle<JSArray> array, int index, JSObject::AddProperty(isolate, element, additional_property_name, additional_property_value, NONE); } + +namespace { + // Build the shortened locale; eg, convert xx_Yyyy_ZZ to xx_ZZ. -bool Intl::RemoveLocaleScriptTag(const std::string& icu_locale, - std::string* locale_less_script) { +// +// If locale has a script tag then return true and the locale without the +// script else return false and an empty string. +bool RemoveLocaleScriptTag(const std::string& icu_locale, + std::string* locale_less_script) { icu::Locale new_locale = icu::Locale::createCanonical(icu_locale.c_str()); const char* icu_script = new_locale.getScript(); - if (icu_script == NULL || strlen(icu_script) == 0) { + if (icu_script == nullptr || strlen(icu_script) == 0) { *locale_less_script = std::string(); return false; } @@ -915,134 +178,33 @@ bool Intl::RemoveLocaleScriptTag(const std::string& icu_locale, const char* icu_language = new_locale.getLanguage(); const char* icu_country = new_locale.getCountry(); icu::Locale short_locale = icu::Locale(icu_language, icu_country); - const char* icu_name = short_locale.getName(); - *locale_less_script = std::string(icu_name); + *locale_less_script = short_locale.getName(); return true; } -namespace { - -Maybe<bool> IsPropertyUndefined(Isolate* isolate, Handle<JSObject> options, - const char* property) { - Factory* factory = isolate->factory(); - // i. Let prop be the property name. - // ii. Let value be ? Get(options, prop). - Handle<Object> value; - ASSIGN_RETURN_ON_EXCEPTION_VALUE( - isolate, value, - Object::GetPropertyOrElement( - isolate, options, factory->NewStringFromAsciiChecked(property)), - Nothing<bool>()); - return Just(value->IsUndefined(isolate)); -} - } // namespace -// ecma-402/#sec-todatetimeoptions -MaybeHandle<JSObject> DateFormat::ToDateTimeOptions( - Isolate* isolate, Handle<Object> input_options, const char* required, - const char* defaults) { - Factory* factory = isolate->factory(); - // 1. If options is undefined, let options be null; otherwise let options be ? - // ToObject(options). - Handle<JSObject> options; - if (input_options->IsUndefined(isolate)) { - options = factory->NewJSObjectWithNullProto(); - } else { - Handle<JSReceiver> options_obj; - ASSIGN_RETURN_ON_EXCEPTION(isolate, options_obj, - Object::ToObject(isolate, input_options), - JSObject); - // 2. Let options be ObjectCreate(options). - ASSIGN_RETURN_ON_EXCEPTION(isolate, options, - JSObject::ObjectCreate(isolate, options_obj), - JSObject); - } - - // 3. Let needDefaults be true. - bool needs_default = true; - - bool required_is_any = strcmp(required, "any") == 0; - // 4. If required is "date" or "any", then - if (required_is_any || (strcmp(required, "date") == 0)) { - // a. For each of the property names "weekday", "year", "month", "day", do - for (auto& prop : {"weekday", "year", "month", "day"}) { - // i. Let prop be the property name. - // ii. Let value be ? Get(options, prop) - Maybe<bool> maybe_undefined = IsPropertyUndefined(isolate, options, prop); - MAYBE_RETURN(maybe_undefined, Handle<JSObject>()); - // iii. If value is not undefined, let needDefaults be false. - if (!maybe_undefined.FromJust()) { - needs_default = false; - } - } - } - - // 5. If required is "time" or "any", then - if (required_is_any || (strcmp(required, "time") == 0)) { - // a. For each of the property names "hour", "minute", "second", do - for (auto& prop : {"hour", "minute", "second"}) { - // i. Let prop be the property name. - // ii. Let value be ? Get(options, prop) - Maybe<bool> maybe_undefined = IsPropertyUndefined(isolate, options, prop); - MAYBE_RETURN(maybe_undefined, Handle<JSObject>()); - // iii. If value is not undefined, let needDefaults be false. - if (!maybe_undefined.FromJust()) { - needs_default = false; - } - } - } - - // 6. If needDefaults is true and defaults is either "date" or "all", then - if (needs_default) { - bool default_is_all = strcmp(defaults, "all") == 0; - if (default_is_all || (strcmp(defaults, "date") == 0)) { - // a. For each of the property names "year", "month", "day", do - // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). - for (auto& prop : {"year", "month", "day"}) { - MAYBE_RETURN( - JSReceiver::CreateDataProperty( - isolate, options, factory->NewStringFromAsciiChecked(prop), - factory->numeric_string(), kThrowOnError), - Handle<JSObject>()); - } - } - // 7. If needDefaults is true and defaults is either "time" or "all", then - if (default_is_all || (strcmp(defaults, "time") == 0)) { - // a. For each of the property names "hour", "minute", "second", do - // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). - for (auto& prop : {"hour", "minute", "second"}) { - MAYBE_RETURN( - JSReceiver::CreateDataProperty( - isolate, options, factory->NewStringFromAsciiChecked(prop), - factory->numeric_string(), kThrowOnError), - Handle<JSObject>()); - } - } - } - // 8. Return options. - return options; -} - -std::set<std::string> Intl::GetAvailableLocales(const IcuService& service) { +std::set<std::string> Intl::GetAvailableLocales(const ICUService service) { const icu::Locale* icu_available_locales = nullptr; int32_t count = 0; std::set<std::string> locales; switch (service) { - case IcuService::kBreakIterator: + case ICUService::kBreakIterator: + case ICUService::kSegmenter: icu_available_locales = icu::BreakIterator::getAvailableLocales(count); break; - case IcuService::kCollator: + case ICUService::kCollator: icu_available_locales = icu::Collator::getAvailableLocales(count); break; - case IcuService::kDateFormat: + case ICUService::kRelativeDateTimeFormatter: + case ICUService::kDateFormat: icu_available_locales = icu::DateFormat::getAvailableLocales(count); break; - case IcuService::kNumberFormat: + case ICUService::kNumberFormat: icu_available_locales = icu::NumberFormat::getAvailableLocales(count); break; - case IcuService::kPluralRules: + case ICUService::kPluralRules: // TODO(littledan): For PluralRules, filter out locales that // don't support PluralRules. // PluralRules is missing an appropriate getAvailableLocales method, @@ -1050,44 +212,7 @@ std::set<std::string> Intl::GetAvailableLocales(const IcuService& service) { // https://ssl.icu-project.org/trac/ticket/12756 icu_available_locales = icu::Locale::getAvailableLocales(count); break; - case IcuService::kResourceBundle: { - UErrorCode status = U_ZERO_ERROR; - UEnumeration* en = ures_openAvailableLocales(nullptr, &status); - int32_t length = 0; - const char* locale_str = uenum_next(en, &length, &status); - while (U_SUCCESS(status) && (locale_str != nullptr)) { - std::string locale(locale_str, length); - std::replace(locale.begin(), locale.end(), '_', '-'); - locales.insert(locale); - std::string shortened_locale; - if (Intl::RemoveLocaleScriptTag(locale_str, &shortened_locale)) { - std::replace(shortened_locale.begin(), shortened_locale.end(), '_', - '-'); - locales.insert(shortened_locale); - } - locale_str = uenum_next(en, &length, &status); - } - uenum_close(en); - return locales; - } - case IcuService::kRelativeDateTimeFormatter: { - // ICU RelativeDateTimeFormatter does not provide a getAvailableLocales() - // interface, because RelativeDateTimeFormatter depends on - // 1. NumberFormat and 2. ResourceBundle, return the - // intersection of these two set. - // ICU FR at https://unicode-org.atlassian.net/browse/ICU-20009 - // TODO(ftang): change to call ICU's getAvailableLocales() after it is - // added. - std::set<std::string> number_format_set( - Intl::GetAvailableLocales(IcuService::kNumberFormat)); - std::set<std::string> resource_bundle_set( - Intl::GetAvailableLocales(IcuService::kResourceBundle)); - set_intersection(resource_bundle_set.begin(), resource_bundle_set.end(), - number_format_set.begin(), number_format_set.end(), - std::inserter(locales, locales.begin())); - return locales; - } - case IcuService::kListFormatter: { + case ICUService::kListFormatter: { // TODO(ftang): for now just use // icu::Locale::getAvailableLocales(count) until we migrate to // Intl::GetAvailableLocales(). @@ -1114,7 +239,7 @@ std::set<std::string> Intl::GetAvailableLocales(const IcuService& service) { locales.insert(locale); std::string shortened_locale; - if (Intl::RemoveLocaleScriptTag(icu_name, &shortened_locale)) { + if (RemoveLocaleScriptTag(icu_name, &shortened_locale)) { std::replace(shortened_locale.begin(), shortened_locale.end(), '_', '-'); locales.insert(shortened_locale); } @@ -1123,30 +248,60 @@ std::set<std::string> Intl::GetAvailableLocales(const IcuService& service) { return locales; } -IcuService Intl::StringToIcuService(Handle<String> service) { - if (service->IsUtf8EqualTo(CStrVector("collator"))) { - return IcuService::kCollator; - } else if (service->IsUtf8EqualTo(CStrVector("numberformat"))) { - return IcuService::kNumberFormat; - } else if (service->IsUtf8EqualTo(CStrVector("dateformat"))) { - return IcuService::kDateFormat; - } else if (service->IsUtf8EqualTo(CStrVector("breakiterator"))) { - return IcuService::kBreakIterator; - } else if (service->IsUtf8EqualTo(CStrVector("pluralrules"))) { - return IcuService::kPluralRules; - } else if (service->IsUtf8EqualTo(CStrVector("relativetimeformat"))) { - return IcuService::kRelativeDateTimeFormatter; - } else if (service->IsUtf8EqualTo(CStrVector("listformat"))) { - return IcuService::kListFormatter; +namespace { + +// TODO(gsathya): Remove this once we port ResolveLocale to C++. +ICUService StringToICUService(Handle<String> service) { + std::unique_ptr<char[]> service_cstr = service->ToCString(); + if (strcmp(service_cstr.get(), "collator") == 0) { + return ICUService::kCollator; + } else if (strcmp(service_cstr.get(), "numberformat") == 0) { + return ICUService::kNumberFormat; + } else if (strcmp(service_cstr.get(), "dateformat") == 0) { + return ICUService::kDateFormat; + } else if (strcmp(service_cstr.get(), "breakiterator") == 0) { + return ICUService::kBreakIterator; + } else if (strcmp(service_cstr.get(), "pluralrules") == 0) { + return ICUService::kPluralRules; + } else if (strcmp(service_cstr.get(), "relativetimeformat") == 0) { + return ICUService::kRelativeDateTimeFormatter; + } else if (strcmp(service_cstr.get(), "listformat") == 0) { + return ICUService::kListFormatter; + } else if (service->IsUtf8EqualTo(CStrVector("segmenter"))) { + return ICUService::kSegmenter; + } + UNREACHABLE(); +} + +const char* ICUServiceToString(ICUService service) { + switch (service) { + case ICUService::kCollator: + return "Intl.Collator"; + case ICUService::kNumberFormat: + return "Intl.NumberFormat"; + case ICUService::kDateFormat: + return "Intl.DateFormat"; + case ICUService::kBreakIterator: + return "Intl.v8BreakIterator"; + case ICUService::kPluralRules: + return "Intl.PluralRules"; + case ICUService::kRelativeDateTimeFormatter: + return "Intl.RelativeTimeFormat"; + case ICUService::kListFormatter: + return "Intl.kListFormat"; + case ICUService::kSegmenter: + return "Intl.kSegmenter"; } UNREACHABLE(); } +} // namespace + V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> Intl::AvailableLocalesOf( Isolate* isolate, Handle<String> service) { Factory* factory = isolate->factory(); std::set<std::string> results = - Intl::GetAvailableLocales(StringToIcuService(service)); + Intl::GetAvailableLocales(StringToICUService(service)); Handle<JSObject> locales = factory->NewJSObjectWithNullProto(); int32_t i = 0; @@ -1196,24 +351,11 @@ bool Intl::IsObjectOfType(Isolate* isolate, Handle<Object> input, return type == expected_type; } -namespace { - -// In ECMA 402 v1, Intl constructors supported a mode of operation -// where calling them with an existing object as a receiver would -// transform the receiver into the relevant Intl instance with all -// internal slots. In ECMA 402 v2, this capability was removed, to -// avoid adding internal slots on existing objects. In ECMA 402 v3, -// the capability was re-added as "normative optional" in a mode -// which chains the underlying Intl instance on any object, when the -// constructor is called -// // See ecma402/#legacy-constructor. -MaybeHandle<Object> LegacyUnwrapReceiver(Isolate* isolate, - Handle<JSReceiver> receiver, - Handle<JSFunction> constructor, - Intl::Type type) { - bool has_initialized_slot = Intl::IsObjectOfType(isolate, receiver, type); - +MaybeHandle<Object> Intl::LegacyUnwrapReceiver(Isolate* isolate, + Handle<JSReceiver> receiver, + Handle<JSFunction> constructor, + bool has_initialized_slot) { Handle<Object> obj_is_instance_of; ASSIGN_RETURN_ON_EXCEPTION(isolate, obj_is_instance_of, Object::InstanceOf(isolate, receiver, constructor), @@ -1236,94 +378,9 @@ MaybeHandle<Object> LegacyUnwrapReceiver(Isolate* isolate, return receiver; } -} // namespace - -MaybeHandle<JSObject> Intl::UnwrapReceiver(Isolate* isolate, - Handle<JSReceiver> receiver, - Handle<JSFunction> constructor, - Intl::Type type, - Handle<String> method_name, - bool check_legacy_constructor) { - DCHECK(type == Intl::Type::kCollator || type == Intl::Type::kNumberFormat || - type == Intl::Type::kDateTimeFormat || - type == Intl::Type::kBreakIterator); - Handle<Object> new_receiver = receiver; - if (check_legacy_constructor) { - ASSIGN_RETURN_ON_EXCEPTION( - isolate, new_receiver, - LegacyUnwrapReceiver(isolate, receiver, constructor, type), JSObject); - } - - // Collator has been ported to use regular instance types. We - // shouldn't be using Intl::IsObjectOfType anymore. - if (type == Intl::Type::kCollator) { - if (!receiver->IsJSCollator()) { - // 3. a. Throw a TypeError exception. - THROW_NEW_ERROR(isolate, - NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, - method_name, receiver), - JSObject); - } - return Handle<JSCollator>::cast(receiver); - } - - DCHECK_NE(type, Intl::Type::kCollator); - // 3. If Type(new_receiver) is not Object or nf does not have an - // [[Initialized...]] internal slot, then - if (!Intl::IsObjectOfType(isolate, new_receiver, type)) { - // 3. a. Throw a TypeError exception. - THROW_NEW_ERROR(isolate, - NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, - method_name, receiver), - JSObject); - } - - // The above IsObjectOfType returns true only for JSObjects, which - // makes this cast safe. - return Handle<JSObject>::cast(new_receiver); -} - -MaybeHandle<JSObject> NumberFormat::Unwrap(Isolate* isolate, - Handle<JSReceiver> receiver, - const char* method_name) { - Handle<Context> native_context = - Handle<Context>(isolate->context()->native_context(), isolate); - Handle<JSFunction> constructor = Handle<JSFunction>( - JSFunction::cast(native_context->intl_number_format_function()), isolate); - Handle<String> method_name_str = - isolate->factory()->NewStringFromAsciiChecked(method_name); - - return Intl::UnwrapReceiver(isolate, receiver, constructor, - Intl::Type::kNumberFormat, method_name_str, true); -} - -MaybeHandle<String> NumberFormat::FormatNumber( - Isolate* isolate, Handle<JSObject> number_format_holder, double value) { - icu::DecimalFormat* number_format = - NumberFormat::UnpackNumberFormat(number_format_holder); - CHECK_NOT_NULL(number_format); - - icu::UnicodeString result; - number_format->format(value, result); - - return isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>( - reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length())); -} - -void Intl::DefineWEProperty(Isolate* isolate, Handle<JSObject> target, - Handle<Name> key, Handle<Object> value) { - PropertyDescriptor desc; - desc.set_writable(true); - desc.set_enumerable(true); - desc.set_value(value); - Maybe<bool> success = - JSReceiver::DefineOwnProperty(isolate, target, key, &desc, kDontThrow); - DCHECK(success.IsJust() && success.FromJust()); - USE(success); -} - namespace { +#if USE_CHROMIUM_ICU == 0 && U_ICU_VERSION_MAJOR_NUM < 63 // Define general regexp macros. // Note "(?:" means the regexp group a non-capture group. #define REGEX_ALPHA "[a-z]" @@ -1435,6 +492,7 @@ icu::RegexMatcher* GetLanguageVariantRegexMatcher(Isolate* isolate) { } return language_variant_regexp_matcher; } +#endif // USE_CHROMIUM_ICU == 0 && U_ICU_VERSION_MAJOR_NUM < 63 } // anonymous namespace @@ -1560,6 +618,7 @@ char AsciiToLower(char c) { return c | (1 << 5); } +#if USE_CHROMIUM_ICU == 0 && U_ICU_VERSION_MAJOR_NUM < 63 /** * Check the structural Validity of the language tag per ECMA 402 6.2.2: * - Well-formed per RFC 5646 2.1 @@ -1571,7 +630,7 @@ char AsciiToLower(char c) { * primary/extended language, script, region, variant are not checked * against the IANA language subtag registry. * - * ICU is too permissible and lets invalid tags, like + * ICU 62 or earlier is too permissible and lets invalid tags, like * hant-cmn-cn, through. * * Returns false if the language tag is invalid. @@ -1612,7 +671,7 @@ bool IsStructurallyValidLanguageTag(Isolate* isolate, // is not valid and would fail LANGUAGE_TAG_RE test. size_t pos = 0; std::vector<std::string> parts; - while ((pos = locale.find("-")) != std::string::npos) { + while ((pos = locale.find('-')) != std::string::npos) { std::string token = locale.substr(0, pos); parts.push_back(token); locale = locale.substr(pos + 1); @@ -1662,6 +721,7 @@ bool IsStructurallyValidLanguageTag(Isolate* isolate, return true; } +#endif // USE_CHROMIUM_ICU == 0 || U_ICU_VERSION_MAJOR_NUM < 63 bool IsLowerAscii(char c) { return c >= 'a' && c < 'z'; } @@ -1713,6 +773,14 @@ Maybe<std::string> Intl::CanonicalizeLanguageTag(Isolate* isolate, } std::string locale(locale_str->ToCString().get()); + if (locale.length() == 0 || + !String::IsAscii(locale.data(), static_cast<int>(locale.length()))) { + THROW_NEW_ERROR_RETURN_VALUE( + isolate, + NewRangeError(MessageTemplate::kInvalidLanguageTag, locale_str), + Nothing<std::string>()); + } + // Optimize for the most common case: a 2-letter language code in the // canonical form/lowercase that is not one of the deprecated codes // (in, iw, ji, jw). Don't check for ~70 of 3-letter deprecated language @@ -1726,12 +794,15 @@ Maybe<std::string> Intl::CanonicalizeLanguageTag(Isolate* isolate, // Because per BCP 47 2.1.1 language tags are case-insensitive, lowercase // the input before any more check. std::transform(locale.begin(), locale.end(), locale.begin(), AsciiToLower); + +#if USE_CHROMIUM_ICU == 0 && U_ICU_VERSION_MAJOR_NUM < 63 if (!IsStructurallyValidLanguageTag(isolate, locale)) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NewRangeError(MessageTemplate::kInvalidLanguageTag, locale_str), Nothing<std::string>()); } +#endif // ICU maps a few grandfathered tags to what looks like a regular language // tag even though IANA language tag registry does not have a preferred @@ -1749,11 +820,18 @@ Maybe<std::string> Intl::CanonicalizeLanguageTag(Isolate* isolate, // https://unicode-org.atlassian.net/browse/ICU-13417 UErrorCode error = U_ZERO_ERROR; char icu_result[ULOC_FULLNAME_CAPACITY]; + // uloc_forLanguageTag checks the structrual validity. If the input BCP47 + // language tag is parsed all the way to the end, it indicates that the input + // is structurally valid. Due to a couple of bugs, we can't use it + // without Chromium patches or ICU 62 or earlier. + int parsed_length; uloc_forLanguageTag(locale.c_str(), icu_result, ULOC_FULLNAME_CAPACITY, - nullptr, &error); - if (U_FAILURE(error) || error == U_STRING_NOT_TERMINATED_WARNING) { - // TODO(jshin): This should not happen because the structural validity - // is already checked. If that's the case, remove this. + &parsed_length, &error); + if (U_FAILURE(error) || +#if USE_CHROMIUM_ICU == 1 || U_ICU_VERSION_MAJOR_NUM >= 63 + static_cast<size_t>(parsed_length) < locale.length() || +#endif + error == U_STRING_NOT_TERMINATED_WARNING) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NewRangeError(MessageTemplate::kInvalidLanguageTag, locale_str), @@ -1849,120 +927,6 @@ Maybe<std::vector<std::string>> Intl::CanonicalizeLocaleList( return Just(seen); } -// ecma-402/#sec-currencydigits -Handle<Smi> Intl::CurrencyDigits(Isolate* isolate, Handle<String> currency) { - v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate); - v8::String::Value currency_string(v8_isolate, v8::Utils::ToLocal(currency)); - CHECK_NOT_NULL(*currency_string); - - DisallowHeapAllocation no_gc; - UErrorCode status = U_ZERO_ERROR; - uint32_t fraction_digits = ucurr_getDefaultFractionDigits( - reinterpret_cast<const UChar*>(*currency_string), &status); - // For missing currency codes, default to the most common, 2 - if (U_FAILURE(status)) fraction_digits = 2; - return Handle<Smi>(Smi::FromInt(fraction_digits), isolate); -} - -MaybeHandle<JSObject> Intl::CreateNumberFormat(Isolate* isolate, - Handle<String> locale, - Handle<JSObject> options, - Handle<JSObject> resolved) { - Handle<JSFunction> constructor( - isolate->native_context()->intl_number_format_function(), isolate); - - Handle<JSObject> local_object; - ASSIGN_RETURN_ON_EXCEPTION(isolate, local_object, - JSObject::New(constructor, constructor), JSObject); - - // Set number formatter as embedder field of the resulting JS object. - icu::DecimalFormat* number_format = - NumberFormat::InitializeNumberFormat(isolate, locale, options, resolved); - - CHECK_NOT_NULL(number_format); - - local_object->SetEmbedderField(NumberFormat::kDecimalFormatIndex, - reinterpret_cast<Smi*>(number_format)); - - Handle<Object> wrapper = isolate->global_handles()->Create(*local_object); - GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(), - NumberFormat::DeleteNumberFormat, - WeakCallbackType::kInternalFields); - return local_object; -} - -/** - * Parses Unicode extension into key - value map. - * Returns empty object if the extension string is invalid. - * We are not concerned with the validity of the values at this point. - * 'attribute' in RFC 6047 is not supported. Keys without explicit - * values are assigned UNDEFINED. - * TODO(jshin): Fix the handling of 'attribute' (in RFC 6047, but none - * has been defined so that it's not used) and boolean keys without - * an explicit value. - */ -void Intl::ParseExtension(Isolate* isolate, const std::string& extension, - std::map<std::string, std::string>& out) { - if (extension.compare(0, 3, "-u-") != 0) return; - - // Key is {2}alphanum, value is {3,8}alphanum. - // Some keys may not have explicit values (booleans). - std::string key; - std::string value; - // Skip the "-u-". - size_t start = 3; - size_t end; - do { - end = extension.find("-", start); - size_t length = - (end == std::string::npos) ? extension.length() - start : end - start; - std::string element = extension.substr(start, length); - // Key is {2}alphanum - if (length == 2) { - if (!key.empty()) { - out.insert(std::pair<std::string, std::string>(key, value)); - value.clear(); - } - key = element; - // value is {3,8}alphanum. - } else if (length >= 3 && length <= 8 && !key.empty()) { - value = value.empty() ? element : (value + "-" + element); - } else { - return; - } - start = end + 1; - } while (end != std::string::npos); - if (!key.empty()) out.insert(std::pair<std::string, std::string>(key, value)); -} - -namespace { - -bool IsAToZ(char ch) { - return IsInRange(AsciiAlphaToLower(ch), 'a', 'z'); -} - -} // namespace - -// Verifies that the input is a well-formed ISO 4217 currency code. -// ecma402/#sec-currency-codes -bool Intl::IsWellFormedCurrencyCode(Isolate* isolate, Handle<String> currency) { - // 2. If the number of elements in normalized is not 3, return false. - if (currency->length() != 3) return false; - - currency = String::Flatten(isolate, currency); - { - DisallowHeapAllocation no_gc; - String::FlatContent flat = currency->GetFlatContent(); - - // 1. Let normalized be the result of mapping currency to upper case as - // described in 6.1. 3. If normalized contains any character that is not in - // the range "A" to "Z" (U+0041 to U+005A), return false. 4. Return true. - // Don't uppercase to test. It could convert invalid code into a valid one. - // For example \u00DFP (Eszett+P) becomes SSP. - return (IsAToZ(flat.Get(0)) && IsAToZ(flat.Get(1)) && IsAToZ(flat.Get(2))); - } -} - // ecma402 #sup-string.prototype.tolocalelowercase // ecma402 #sup-string.prototype.tolocaleuppercase MaybeHandle<String> Intl::StringLocaleConvertCase(Isolate* isolate, @@ -1976,7 +940,7 @@ MaybeHandle<String> Intl::StringLocaleConvertCase(Isolate* isolate, std::string requested_locale = requested_locales.size() == 0 ? Intl::DefaultLocale(isolate) : requested_locales[0]; - size_t dash = requested_locale.find("-"); + size_t dash = requested_locale.find('-'); if (dash != std::string::npos) { requested_locale = requested_locale.substr(0, dash); } @@ -2069,22 +1033,26 @@ MaybeHandle<String> Intl::NumberToLocaleString(Isolate* isolate, factory->NewStringFromStaticChars("numberformat"), locales, options, factory->undefined_value()), String); - DCHECK( - Intl::IsObjectOfType(isolate, number_format_holder, Intl::kNumberFormat)); + DCHECK(number_format_holder->IsJSNumberFormat()); + Handle<JSNumberFormat> number_format = Handle<JSNumberFormat>( + JSNumberFormat::cast(*number_format_holder), isolate); + Handle<Object> number_obj; ASSIGN_RETURN_ON_EXCEPTION(isolate, number_obj, Object::ToNumber(isolate, num), String); // Spec treats -0 and +0 as 0. double number = number_obj->Number() + 0; + // Return FormatNumber(numberFormat, x). - return NumberFormat::FormatNumber(isolate, number_format_holder, number); + return JSNumberFormat::FormatNumber(isolate, number_format, number); } +namespace { + // ecma402/#sec-defaultnumberoption -Maybe<int> Intl::DefaultNumberOption(Isolate* isolate, Handle<Object> value, - int min, int max, int fallback, - Handle<String> property) { +Maybe<int> DefaultNumberOption(Isolate* isolate, Handle<Object> value, int min, + int max, int fallback, Handle<String> property) { // 2. Else, return fallback. if (value->IsUndefined()) return Just(fallback); @@ -2114,9 +1082,9 @@ Maybe<int> Intl::DefaultNumberOption(Isolate* isolate, Handle<Object> value, } // ecma402/#sec-getnumberoption -Maybe<int> Intl::GetNumberOption(Isolate* isolate, Handle<JSReceiver> options, - Handle<String> property, int min, int max, - int fallback) { +Maybe<int> GetNumberOption(Isolate* isolate, Handle<JSReceiver> options, + Handle<String> property, int min, int max, + int fallback) { // 1. Let value be ? Get(options, property). Handle<Object> value; ASSIGN_RETURN_ON_EXCEPTION_VALUE( @@ -2127,14 +1095,16 @@ Maybe<int> Intl::GetNumberOption(Isolate* isolate, Handle<JSReceiver> options, return DefaultNumberOption(isolate, value, min, max, fallback, property); } -Maybe<int> Intl::GetNumberOption(Isolate* isolate, Handle<JSReceiver> options, - const char* property, int min, int max, - int fallback) { +Maybe<int> GetNumberOption(Isolate* isolate, Handle<JSReceiver> options, + const char* property, int min, int max, + int fallback) { Handle<String> property_str = isolate->factory()->NewStringFromAsciiChecked(property); return GetNumberOption(isolate, options, property_str, min, max, fallback); } +} // namespace + Maybe<bool> Intl::SetNumberFormatDigitOptions(Isolate* isolate, icu::DecimalFormat* number_format, Handle<JSReceiver> options, @@ -2174,7 +1144,7 @@ Maybe<bool> Intl::SetNumberFormatDigitOptions(Isolate* isolate, // 9. Let mnsd be ? Get(options, "minimumSignificantDigits"). Handle<Object> mnsd_obj; Handle<String> mnsd_str = - isolate->factory()->NewStringFromStaticChars("minimumSignificantDigits"); + isolate->factory()->minimumSignificantDigits_string(); ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, mnsd_obj, JSReceiver::GetProperty(isolate, options, mnsd_str), Nothing<bool>()); @@ -2182,7 +1152,7 @@ Maybe<bool> Intl::SetNumberFormatDigitOptions(Isolate* isolate, // 10. Let mxsd be ? Get(options, "maximumSignificantDigits"). Handle<Object> mxsd_obj; Handle<String> mxsd_str = - isolate->factory()->NewStringFromStaticChars("maximumSignificantDigits"); + isolate->factory()->maximumSignificantDigits_string(); ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, mxsd_obj, JSReceiver::GetProperty(isolate, options, mxsd_str), Nothing<bool>()); @@ -2228,96 +1198,108 @@ Maybe<bool> Intl::SetNumberFormatDigitOptions(Isolate* isolate, namespace { -// ECMA 402 9.2.2 BestAvailableLocale(availableLocales, locale) -// https://tc39.github.io/ecma402/#sec-bestavailablelocale -std::string BestAvailableLocale(std::set<std::string> available_locales, - std::string locale) { - const char separator = '-'; - +// ecma402/#sec-bestavailablelocale +std::string BestAvailableLocale(const std::set<std::string>& available_locales, + const std::string& locale) { // 1. Let candidate be locale. + std::string candidate = locale; + // 2. Repeat, - do { + while (true) { // 2.a. If availableLocales contains an element equal to candidate, return // candidate. - if (available_locales.find(locale) != available_locales.end()) { - return locale; + if (available_locales.find(candidate) != available_locales.end()) { + return candidate; } + // 2.b. Let pos be the character index of the last occurrence of "-" // (U+002D) within candidate. If that character does not occur, return // undefined. - size_t pos = locale.rfind(separator); + size_t pos = candidate.rfind('-'); if (pos == std::string::npos) { - return ""; + return std::string(); } + // 2.c. If pos ≥ 2 and the character "-" occurs at index pos-2 of candidate, // decrease pos by 2. - if (pos >= 2 && locale[pos - 2] == separator) { + if (pos >= 2 && candidate[pos - 2] == '-') { pos -= 2; } + // 2.d. Let candidate be the substring of candidate from position 0, // inclusive, to position pos, exclusive. - locale = locale.substr(0, pos); - } while (true); + candidate = candidate.substr(0, pos); + } } -#define ANY_EXTENSION_REGEXP "-[a-z0-9]{1}-.*" +// Removes unicode extensions from a given bcp47 language tag. +// For example, converts 'en-US-u-co-emoji' to 'en-US'. +std::string RemoveUnicodeExtensions(const std::string& locale) { + size_t length = locale.length(); -std::unique_ptr<icu::RegexMatcher> GetAnyExtensionRegexpMatcher() { - UErrorCode status = U_ZERO_ERROR; - std::unique_ptr<icu::RegexMatcher> matcher(new icu::RegexMatcher( - icu::UnicodeString(ANY_EXTENSION_REGEXP, -1, US_INV), 0, status)); - DCHECK(U_SUCCESS(status)); - return matcher; -} + // Privateuse or grandfathered locales have no extension sequences. + if ((length > 1) && (locale[1] == '-')) { + // Check to make sure that this really is a grandfathered or + // privateuse extension. ICU can sometimes mess up the + // canonicalization. + CHECK(locale[0] == 'x' || locale[0] == 'i'); + return locale; + } -#undef ANY_EXTENSION_REGEXP + size_t unicode_extension_start = locale.find("-u-"); -// ECMA 402 9.2.7 LookupSupportedLocales(availableLocales, requestedLocales) -// https://tc39.github.io/ecma402/#sec-lookupsupportedlocales -std::vector<std::string> LookupSupportedLocales( - std::set<std::string> available_locales, - std::vector<std::string> requested_locales) { - std::unique_ptr<icu::RegexMatcher> matcher = GetAnyExtensionRegexpMatcher(); + // No unicode extensions found. + if (unicode_extension_start == std::string::npos) return locale; + + size_t private_extension_start = locale.find("-x-"); + + // Unicode extensions found within privateuse subtags don't count. + if (private_extension_start != std::string::npos && + private_extension_start < unicode_extension_start) { + return locale; + } + + const std::string beginning = locale.substr(0, unicode_extension_start); + size_t unicode_extension_end = length; + DCHECK_GT(length, 2); + + // Find the end of the extension production as per the bcp47 grammar + // by looking for '-' followed by 2 chars and then another '-'. + for (size_t i = unicode_extension_start + 1; i < length - 2; i++) { + if (locale[i] != '-') continue; + + if (locale[i + 2] == '-') { + unicode_extension_end = i; + break; + } + + i += 2; + } + const std::string end = locale.substr(unicode_extension_end); + return beginning + end; +} + +// ecma402/#sec-lookupsupportedlocales +std::vector<std::string> LookupSupportedLocales( + const std::set<std::string>& available_locales, + const std::vector<std::string>& requested_locales) { // 1. Let subset be a new empty List. std::vector<std::string> subset; // 2. For each element locale of requestedLocales in List order, do - for (auto locale : requested_locales) { - // 2.a. Let noExtensionsLocale be the String value that is locale with all - // Unicode locale extension sequences removed. - icu::UnicodeString locale_uni(locale.c_str(), -1, US_INV); - // TODO(bstell): look at using uloc_forLanguageTag to convert the language - // tag to locale id - // TODO(bstell): look at using uloc_getBaseName to just get the name without - // all the keywords - matcher->reset(locale_uni); - UErrorCode status = U_ZERO_ERROR; - // TODO(bstell): need to determine if this is the correct behavior. - // This matches the JS implementation but might not match the spec. - // According to - // https://tc39.github.io/ecma402/#sec-unicode-locale-extension-sequences: - // - // This standard uses the term "Unicode locale extension sequence" for - // any substring of a language tag that is not part of a private use - // subtag sequence, starts with a separator "-" and the singleton "u", - // and includes the maximum sequence of following non-singleton subtags - // and their preceding "-" separators. - // - // According to the spec a locale "en-t-aaa-u-bbb-v-ccc-x-u-ddd", should - // remove only the "-u-bbb" part, and keep everything else, whereas this - // regexp matcher would leave only the "en". - icu::UnicodeString no_extensions_locale_uni = - matcher->replaceAll("", status); - DCHECK(U_SUCCESS(status)); - std::string no_extensions_locale; - no_extensions_locale_uni.toUTF8String(no_extensions_locale); - // 2.b. Let availableLocale be BestAvailableLocale(availableLocales, - // noExtensionsLocale). + for (const std::string& locale : requested_locales) { + // 2. a. Let noExtensionsLocale be the String value that is locale + // with all Unicode locale extension sequences removed. + std::string no_extension_locale = RemoveUnicodeExtensions(locale); + + // 2. b. Let availableLocale be + // BestAvailableLocale(availableLocales, noExtensionsLocale). std::string available_locale = - BestAvailableLocale(available_locales, no_extensions_locale); - // 2.c. If availableLocale is not undefined, append locale to the end of - // subset. + BestAvailableLocale(available_locales, no_extension_locale); + + // 2. c. If availableLocale is not undefined, append locale to the + // end of subset. if (!available_locale.empty()) { subset.push_back(locale); } @@ -2330,8 +1312,8 @@ std::vector<std::string> LookupSupportedLocales( // ECMA 402 9.2.8 BestFitSupportedLocales(availableLocales, requestedLocales) // https://tc39.github.io/ecma402/#sec-bestfitsupportedlocales std::vector<std::string> BestFitSupportedLocales( - std::set<std::string> available_locales, - std::vector<std::string> requested_locales) { + const std::set<std::string>& available_locales, + const std::vector<std::string>& requested_locales) { return LookupSupportedLocales(available_locales, requested_locales); } @@ -2378,26 +1360,28 @@ MaybeHandle<JSObject> CreateReadOnlyArray(Isolate* isolate, // ECMA 402 9.2.9 SupportedLocales(availableLocales, requestedLocales, options) // https://tc39.github.io/ecma402/#sec-supportedlocales MaybeHandle<JSObject> SupportedLocales( - Isolate* isolate, std::string service, - std::set<std::string> available_locales, - std::vector<std::string> requested_locales, Handle<Object> options) { + Isolate* isolate, ICUService service, + const std::set<std::string>& available_locales, + const std::vector<std::string>& requested_locales, Handle<Object> options) { std::vector<std::string> supported_locales; - // 1. If options is not undefined, then - // a. Let options be ? ToObject(options). - // b. Let matcher be ? GetOption(options, "localeMatcher", "string", - // « "lookup", "best fit" », "best fit"). // 2. Else, let matcher be "best fit". MatcherOption matcher = kBestFit; + + // 1. If options is not undefined, then if (!options->IsUndefined(isolate)) { + // 1. a. Let options be ? ToObject(options). Handle<JSReceiver> options_obj; ASSIGN_RETURN_ON_EXCEPTION(isolate, options_obj, Object::ToObject(isolate, options), JSObject); + + // 1. b. Let matcher be ? GetOption(options, "localeMatcher", "string", + // « "lookup", "best fit" », "best fit"). std::unique_ptr<char[]> matcher_str = nullptr; std::vector<const char*> matcher_values = {"lookup", "best fit"}; - Maybe<bool> maybe_found_matcher = - Intl::GetStringOption(isolate, options_obj, "localeMatcher", - matcher_values, service.c_str(), &matcher_str); + Maybe<bool> maybe_found_matcher = Intl::GetStringOption( + isolate, options_obj, "localeMatcher", matcher_values, + ICUServiceToString(service), &matcher_str); MAYBE_RETURN(maybe_found_matcher, MaybeHandle<JSObject>()); if (maybe_found_matcher.FromJust()) { DCHECK_NOT_NULL(matcher_str.get()); @@ -2440,28 +1424,69 @@ MaybeHandle<JSObject> SupportedLocales( } } // namespace -// ECMA 402 10.2.2 Intl.Collator.supportedLocalesOf -// https://tc39.github.io/ecma402/#sec-intl.collator.supportedlocalesof -// of Intl::SupportedLocalesOf thru JS +// ECMA 402 Intl.*.supportedLocalesOf MaybeHandle<JSObject> Intl::SupportedLocalesOf(Isolate* isolate, - Handle<String> service, - Handle<Object> locales_in, - Handle<Object> options_in) { + ICUService service, + Handle<Object> locales, + Handle<Object> options) { // Let availableLocales be %Collator%.[[AvailableLocales]]. - IcuService icu_service = Intl::StringToIcuService(service); - std::set<std::string> available_locales = GetAvailableLocales(icu_service); - std::vector<std::string> requested_locales; + std::set<std::string> available_locales = GetAvailableLocales(service); + // Let requestedLocales be ? CanonicalizeLocaleList(locales). - bool got_requested_locales = - CanonicalizeLocaleList(isolate, locales_in, false).To(&requested_locales); - if (!got_requested_locales) { - return MaybeHandle<JSObject>(); - } + Maybe<std::vector<std::string>> requested_locales = + CanonicalizeLocaleList(isolate, locales, false); + MAYBE_RETURN(requested_locales, MaybeHandle<JSObject>()); // Return ? SupportedLocales(availableLocales, requestedLocales, options). - std::string service_str(service->ToCString().get()); - return SupportedLocales(isolate, service_str, available_locales, - requested_locales, options_in); + return SupportedLocales(isolate, service, available_locales, + requested_locales.FromJust(), options); +} + +std::map<std::string, std::string> Intl::LookupUnicodeExtensions( + const icu::Locale& icu_locale, const std::set<std::string>& relevant_keys) { + std::map<std::string, std::string> extensions; + + UErrorCode status = U_ZERO_ERROR; + std::unique_ptr<icu::StringEnumeration> keywords( + icu_locale.createKeywords(status)); + if (U_FAILURE(status)) return extensions; + + if (!keywords) return extensions; + char value[ULOC_FULLNAME_CAPACITY]; + + int32_t length; + status = U_ZERO_ERROR; + for (const char* keyword = keywords->next(&length, status); + keyword != nullptr; keyword = keywords->next(&length, status)) { + // Ignore failures in ICU and skip to the next keyword. + // + // This is fine.™ + if (U_FAILURE(status)) { + status = U_ZERO_ERROR; + continue; + } + + icu_locale.getKeywordValue(keyword, value, ULOC_FULLNAME_CAPACITY, status); + + // Ignore failures in ICU and skip to the next keyword. + // + // This is fine.™ + if (U_FAILURE(status)) { + status = U_ZERO_ERROR; + continue; + } + + const char* bcp47_key = uloc_toUnicodeLocaleKey(keyword); + + // Ignore keywords that we don't recognize - spec allows that. + if (bcp47_key && (relevant_keys.find(bcp47_key) != relevant_keys.end())) { + const char* bcp47_value = uloc_toUnicodeLocaleType(bcp47_key, value); + extensions.insert( + std::pair<std::string, std::string>(bcp47_key, bcp47_value)); + } + } + + return extensions; } } // namespace internal diff --git a/deps/v8/src/objects/intl-objects.h b/deps/v8/src/objects/intl-objects.h index 38d11772a4..fd2842ebbb 100644 --- a/deps/v8/src/objects/intl-objects.h +++ b/deps/v8/src/objects/intl-objects.h @@ -20,10 +20,7 @@ #include "unicode/uversion.h" namespace U_ICU_NAMESPACE { -class BreakIterator; -class Collator; class DecimalFormat; -class PluralRules; class SimpleDateFormat; class UnicodeString; } @@ -33,191 +30,7 @@ namespace internal { template <typename T> class Handle; - -class DateFormat { - public: - // Create a formatter for the specificied locale and options. Returns the - // resolved settings for the locale / options. - static icu::SimpleDateFormat* InitializeDateTimeFormat( - Isolate* isolate, Handle<String> locale, Handle<JSObject> options, - Handle<JSObject> resolved); - - // Unpacks date format object from corresponding JavaScript object. - static icu::SimpleDateFormat* UnpackDateFormat(Handle<JSObject> obj); - - // Release memory we allocated for the DateFormat once the JS object that - // holds the pointer gets garbage collected. - static void DeleteDateFormat(const v8::WeakCallbackInfo<void>& data); - - // ecma402/#sec-formatdatetime - // FormatDateTime( dateTimeFormat, x ) - V8_WARN_UNUSED_RESULT static MaybeHandle<String> FormatDateTime( - Isolate* isolate, Handle<JSObject> date_time_format_holder, double x); - - // ecma402/#sec-datetime-format-functions - // DateTime Format Functions - V8_WARN_UNUSED_RESULT static MaybeHandle<String> DateTimeFormat( - Isolate* isolate, Handle<JSObject> date_time_format_holder, - Handle<Object> date); - - // The UnwrapDateTimeFormat abstract operation gets the underlying - // DateTimeFormat operation for various methods which implement ECMA-402 v1 - // semantics for supporting initializing existing Intl objects. - // - // ecma402/#sec-unwrapdatetimeformat - V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> Unwrap( - Isolate* isolate, Handle<JSReceiver> receiver, const char* method_name); - - // ecma-402/#sec-todatetimeoptions - V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> ToDateTimeOptions( - Isolate* isolate, Handle<Object> input_options, const char* required, - const char* defaults); - - V8_WARN_UNUSED_RESULT static MaybeHandle<String> ToLocaleDateTime( - Isolate* isolate, Handle<Object> date, Handle<Object> locales, - Handle<Object> options, const char* required, const char* defaults, - const char* service); - - // Layout description. -#define DATE_FORMAT_FIELDS(V) \ - V(kSimpleDateFormat, kPointerSize) \ - V(kBoundFormat, kPointerSize) \ - V(kSize, 0) - - DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, DATE_FORMAT_FIELDS) -#undef DATE_FORMAT_FIELDS - - // ContextSlot defines the context structure for the bound - // DateTimeFormat.prototype.format function - enum ContextSlot { - kDateFormat = Context::MIN_CONTEXT_SLOTS, - - kLength - }; - - // TODO(ryzokuken): Remove this and use regular accessors once DateFormat is a - // subclass of JSObject - // - // This needs to be consistent with the above Layout Description - static const int kSimpleDateFormatIndex = 0; - static const int kBoundFormatIndex = 1; - - private: - DateFormat(); -}; - -class NumberFormat { - public: - // Create a formatter for the specificied locale and options. Returns the - // resolved settings for the locale / options. - static icu::DecimalFormat* InitializeNumberFormat(Isolate* isolate, - Handle<String> locale, - Handle<JSObject> options, - Handle<JSObject> resolved); - - // Unpacks number format object from corresponding JavaScript object. - static icu::DecimalFormat* UnpackNumberFormat(Handle<JSObject> obj); - - // Release memory we allocated for the NumberFormat once the JS object that - // holds the pointer gets garbage collected. - static void DeleteNumberFormat(const v8::WeakCallbackInfo<void>& data); - - // The UnwrapNumberFormat abstract operation gets the underlying - // NumberFormat operation for various methods which implement - // ECMA-402 v1 semantics for supporting initializing existing Intl - // objects. - // - // ecma402/#sec-unwrapnumberformat - static MaybeHandle<JSObject> Unwrap(Isolate* isolate, - Handle<JSReceiver> receiver, - const char* method_name); - - // ecm402/#sec-formatnumber - static MaybeHandle<String> FormatNumber(Isolate* isolate, - Handle<JSObject> number_format_holder, - double value); - - // Layout description. -#define NUMBER_FORMAT_FIELDS(V) \ - /* Pointer fields. */ \ - V(kDecimalFormat, kPointerSize) \ - V(kBoundFormat, kPointerSize) \ - V(kSize, 0) - - DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, NUMBER_FORMAT_FIELDS) -#undef NUMBER_FORMAT_FIELDS - - // ContextSlot defines the context structure for the bound - // NumberFormat.prototype.format function. - enum ContextSlot { - // The number format instance that the function holding this - // context is bound to. - kNumberFormat = Context::MIN_CONTEXT_SLOTS, - - kLength - }; - - // TODO(gsathya): Remove this and use regular accessors once - // NumberFormat is a sub class of JSObject. - // - // This needs to be consistent with the above LayoutDescription. - static const int kDecimalFormatIndex = 0; - static const int kBoundFormatIndex = 1; - - private: - NumberFormat(); -}; - -class V8BreakIterator { - public: - // Create a BreakIterator for the specificied locale and options. Returns the - // resolved settings for the locale / options. - static icu::BreakIterator* InitializeBreakIterator(Isolate* isolate, - Handle<String> locale, - Handle<JSObject> options, - Handle<JSObject> resolved); - - // Unpacks break iterator object from corresponding JavaScript object. - static icu::BreakIterator* UnpackBreakIterator(Handle<JSObject> obj); - - // Release memory we allocated for the BreakIterator once the JS object that - // holds the pointer gets garbage collected. - static void DeleteBreakIterator(const v8::WeakCallbackInfo<void>& data); - - static void AdoptText(Isolate* isolate, - Handle<JSObject> break_iterator_holder, - Handle<String> text); - - // Layout description. -#define BREAK_ITERATOR_FIELDS(V) \ - /* Pointer fields. */ \ - V(kBreakIterator, kPointerSize) \ - V(kUnicodeString, kPointerSize) \ - V(kBoundAdoptText, kPointerSize) \ - V(kSize, 0) - - DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, BREAK_ITERATOR_FIELDS) -#undef BREAK_ITERATOR_FIELDS - - // ContextSlot defines the context structure for the bound - // v8BreakIterator.prototype.adoptText function - enum class ContextSlot { - kV8BreakIterator = Context::MIN_CONTEXT_SLOTS, - - kLength - }; - - // TODO(ryzokuken): Remove this and use regular accessors once v8BreakIterator - // is a subclass of JSObject - // - // This needs to be consistent with the above Layour Description - static const int kBreakIteratorIndex = 0; - static const int kUnicodeStringIndex = 1; - static const int kBoundAdoptTextIndex = 2; - - private: - V8BreakIterator(); -}; +class JSCollator; class Intl { public: @@ -232,6 +45,11 @@ class Intl { kTypeCount }; + enum class BoundFunctionContextSlot { + kBoundFunction = Context::MIN_CONTEXT_SLOTS, + kLength + }; + inline static Intl::Type TypeFromInt(int type); inline static Intl::Type TypeFromSmi(Smi* type); @@ -243,41 +61,27 @@ class Intl { static bool IsObjectOfType(Isolate* isolate, Handle<Object> object, Intl::Type expected_type); - static IcuService StringToIcuService(Handle<String> service); - // Gets the ICU locales for a given service. If there is a locale with a // script tag then the locales also include a locale without the script; eg, // pa_Guru_IN (language=Panjabi, script=Gurmukhi, country-India) would include // pa_IN. - static std::set<std::string> GetAvailableLocales(const IcuService& service); + static std::set<std::string> GetAvailableLocales(ICUService service); + + // Get the name of the numbering system from locale. + // ICU doesn't expose numbering system in any way, so we have to assume that + // for given locale NumberingSystem constructor produces the same digits as + // NumberFormat/Calendar would. + static std::string GetNumberingSystem(const icu::Locale& icu_locale); static V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> AvailableLocalesOf( Isolate* isolate, Handle<String> service); - static MaybeHandle<JSObject> SupportedLocalesOf(Isolate* isolate, - Handle<String> service, - Handle<Object> locales_in, - Handle<Object> options_in); + static V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> SupportedLocalesOf( + Isolate* isolate, ICUService service, Handle<Object> locales_in, + Handle<Object> options_in); static std::string DefaultLocale(Isolate* isolate); - static void DefineWEProperty(Isolate* isolate, Handle<JSObject> target, - Handle<Name> key, Handle<Object> value); - - // If locale has a script tag then return true and the locale without the - // script else return false and an empty string - static bool RemoveLocaleScriptTag(const std::string& icu_locale, - std::string* locale_less_script); - - // Returns the underlying Intl receiver for various methods which - // implement ECMA-402 v1 semantics for supporting initializing - // existing Intl objects. - V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> UnwrapReceiver( - Isolate* isolate, Handle<JSReceiver> receiver, - Handle<JSFunction> constructor, Intl::Type type, - Handle<String> method_name /* TODO(gsathya): Make this char const* */, - bool check_legacy_constructor = false); - // The ResolveLocale abstract operation compares a BCP 47 language // priority list requestedLocales against the locales in // availableLocales and determines the best available language to @@ -355,22 +159,10 @@ class Intl { Isolate* isolate, Handle<Object> locales, bool only_return_one_result = false); - // ecma-402/#sec-currencydigits - // The currency is expected to an all upper case string value. - static Handle<Smi> CurrencyDigits(Isolate* isolate, Handle<String> currency); - - // TODO(ftang): Remove this and use ICU to the conversion in the future - static void ParseExtension(Isolate* isolate, const std::string& extension, - std::map<std::string, std::string>& out); - V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> CreateNumberFormat( Isolate* isolate, Handle<String> locale, Handle<JSObject> options, Handle<JSObject> resolved); - // ecma402/#sec-iswellformedcurrencycode - static bool IsWellFormedCurrencyCode(Isolate* isolate, - Handle<String> currency); - // For locale sensitive functions V8_WARN_UNUSED_RESULT static MaybeHandle<String> StringLocaleConvertCase( Isolate* isolate, Handle<String> s, bool is_upper, @@ -389,19 +181,6 @@ class Intl { Isolate* isolate, Handle<Object> num, Handle<Object> locales, Handle<Object> options); - // ecma402/#sec-defaultnumberoption - V8_WARN_UNUSED_RESULT static Maybe<int> DefaultNumberOption( - Isolate* isolate, Handle<Object> value, int min, int max, int fallback, - Handle<String> property); - - // ecma402/#sec-getnumberoption - V8_WARN_UNUSED_RESULT static Maybe<int> GetNumberOption( - Isolate* isolate, Handle<JSReceiver> options, Handle<String> property, - int min, int max, int fallback); - V8_WARN_UNUSED_RESULT static Maybe<int> GetNumberOption( - Isolate* isolate, Handle<JSReceiver> options, const char* property, - int min, int max, int fallback); - // ecma402/#sec-setnfdigitoptions V8_WARN_UNUSED_RESULT static Maybe<bool> SetNumberFormatDigitOptions( Isolate* isolate, icu::DecimalFormat* number_format, @@ -434,6 +213,29 @@ class Intl { Handle<String> field_type_string, Handle<String> value, Handle<String> additional_property_name, Handle<String> additional_property_value); + + // A helper function to help handle Unicode Extensions in locale. + static std::map<std::string, std::string> LookupUnicodeExtensions( + const icu::Locale& icu_locale, const std::set<std::string>& relevant_keys); + + // In ECMA 402 v1, Intl constructors supported a mode of operation + // where calling them with an existing object as a receiver would + // transform the receiver into the relevant Intl instance with all + // internal slots. In ECMA 402 v2, this capability was removed, to + // avoid adding internal slots on existing objects. In ECMA 402 v3, + // the capability was re-added as "normative optional" in a mode + // which chains the underlying Intl instance on any object, when the + // constructor is called + // + // See ecma402/#legacy-constructor. + V8_WARN_UNUSED_RESULT static MaybeHandle<Object> LegacyUnwrapReceiver( + Isolate* isolate, Handle<JSReceiver> receiver, + Handle<JSFunction> constructor, bool has_initialized_slot); + + // A factory method to got cached objects. + V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> CachedOrNewService( + Isolate* isolate, Handle<String> service, Handle<Object> locales, + Handle<Object> options, Handle<Object> internal_options); }; } // namespace internal diff --git a/deps/v8/src/objects/js-array-buffer-inl.h b/deps/v8/src/objects/js-array-buffer-inl.h index 43bc294d04..7bc01a8b8a 100644 --- a/deps/v8/src/objects/js-array-buffer-inl.h +++ b/deps/v8/src/objects/js-array-buffer-inl.h @@ -20,6 +20,14 @@ CAST_ACCESSOR(JSArrayBuffer) CAST_ACCESSOR(JSArrayBufferView) CAST_ACCESSOR(JSTypedArray) +size_t JSArrayBuffer::byte_length() const { + return READ_UINTPTR_FIELD(this, kByteLengthOffset); +} + +void JSArrayBuffer::set_byte_length(size_t value) { + WRITE_UINTPTR_FIELD(this, kByteLengthOffset, value); +} + void* JSArrayBuffer::backing_store() const { intptr_t ptr = READ_INTPTR_FIELD(this, kBackingStoreOffset); return reinterpret_cast<void*>(ptr); @@ -30,8 +38,6 @@ void JSArrayBuffer::set_backing_store(void* value, WriteBarrierMode mode) { WRITE_INTPTR_FIELD(this, kBackingStoreOffset, ptr); } -ACCESSORS(JSArrayBuffer, byte_length, Object, kByteLengthOffset) - size_t JSArrayBuffer::allocation_length() const { if (backing_store() == nullptr) { return 0; @@ -44,7 +50,7 @@ size_t JSArrayBuffer::allocation_length() const { DCHECK_NOT_NULL(data); return data->allocation_length; } - return byte_length()->Number(); + return byte_length(); } void* JSArrayBuffer::allocation_base() const { @@ -63,13 +69,17 @@ void* JSArrayBuffer::allocation_base() const { } bool JSArrayBuffer::is_wasm_memory() const { - bool const is_wasm_memory = IsWasmMemory::decode(bit_field()); + bool const is_wasm_memory = IsWasmMemoryBit::decode(bit_field()); DCHECK_EQ(is_wasm_memory, GetIsolate()->wasm_engine()->memory_tracker()->IsWasmMemory( backing_store())); return is_wasm_memory; } +void JSArrayBuffer::set_is_wasm_memory(bool is_wasm_memory) { + set_bit_field(IsWasmMemoryBit::update(bit_field(), is_wasm_memory)); +} + void JSArrayBuffer::set_bit_field(uint32_t bits) { if (kInt32Size != kPointerSize) { #if V8_TARGET_LITTLE_ENDIAN @@ -85,76 +95,46 @@ uint32_t JSArrayBuffer::bit_field() const { return READ_UINT32_FIELD(this, kBitFieldOffset); } -bool JSArrayBuffer::is_external() { return IsExternal::decode(bit_field()); } - -void JSArrayBuffer::set_is_external(bool value) { - set_bit_field(IsExternal::update(bit_field(), value)); -} - -bool JSArrayBuffer::is_neuterable() { - return IsNeuterable::decode(bit_field()); -} - -void JSArrayBuffer::set_is_neuterable(bool value) { - set_bit_field(IsNeuterable::update(bit_field(), value)); -} - -bool JSArrayBuffer::was_neutered() { return WasNeutered::decode(bit_field()); } - -void JSArrayBuffer::set_was_neutered(bool value) { - set_bit_field(WasNeutered::update(bit_field(), value)); -} - -bool JSArrayBuffer::is_shared() { return IsShared::decode(bit_field()); } - -void JSArrayBuffer::set_is_shared(bool value) { - set_bit_field(IsShared::update(bit_field(), value)); -} - -bool JSArrayBuffer::is_growable() { return IsGrowable::decode(bit_field()); } - -void JSArrayBuffer::set_is_growable(bool value) { - set_bit_field(IsGrowable::update(bit_field(), value)); -} +// |bit_field| fields. +BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_external, + JSArrayBuffer::IsExternalBit) +BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_neuterable, + JSArrayBuffer::IsNeuterableBit) +BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, was_neutered, + JSArrayBuffer::WasNeuteredBit) +BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_shared, + JSArrayBuffer::IsSharedBit) +BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_growable, + JSArrayBuffer::IsGrowableBit) -Object* JSArrayBufferView::byte_offset() const { - if (WasNeutered()) return Smi::kZero; - return Object::cast(READ_FIELD(this, kByteOffsetOffset)); +size_t JSArrayBufferView::byte_offset() const { + return READ_UINTPTR_FIELD(this, kByteOffsetOffset); } -void JSArrayBufferView::set_byte_offset(Object* value, WriteBarrierMode mode) { - WRITE_FIELD(this, kByteOffsetOffset, value); - CONDITIONAL_WRITE_BARRIER(this, kByteOffsetOffset, value, mode); +void JSArrayBufferView::set_byte_offset(size_t value) { + WRITE_UINTPTR_FIELD(this, kByteOffsetOffset, value); } -Object* JSArrayBufferView::byte_length() const { - if (WasNeutered()) return Smi::kZero; - return Object::cast(READ_FIELD(this, kByteLengthOffset)); +size_t JSArrayBufferView::byte_length() const { + return READ_UINTPTR_FIELD(this, kByteLengthOffset); } -void JSArrayBufferView::set_byte_length(Object* value, WriteBarrierMode mode) { - WRITE_FIELD(this, kByteLengthOffset, value); - CONDITIONAL_WRITE_BARRIER(this, kByteLengthOffset, value, mode); +void JSArrayBufferView::set_byte_length(size_t value) { + WRITE_UINTPTR_FIELD(this, kByteLengthOffset, value); } ACCESSORS(JSArrayBufferView, buffer, Object, kBufferOffset) -#ifdef VERIFY_HEAP -ACCESSORS(JSArrayBufferView, raw_byte_offset, Object, kByteOffsetOffset) -ACCESSORS(JSArrayBufferView, raw_byte_length, Object, kByteLengthOffset) -#endif bool JSArrayBufferView::WasNeutered() const { return JSArrayBuffer::cast(buffer())->was_neutered(); } Object* JSTypedArray::length() const { - if (WasNeutered()) return Smi::kZero; return Object::cast(READ_FIELD(this, kLengthOffset)); } size_t JSTypedArray::length_value() const { - if (WasNeutered()) return 0; - double val = Object::cast(READ_FIELD(this, kLengthOffset))->Number(); + double val = length()->Number(); DCHECK_LE(val, kMaxSafeInteger); // 2^53-1 DCHECK_GE(val, -kMaxSafeInteger); // -2^53+1 DCHECK_LE(val, std::numeric_limits<size_t>::max()); diff --git a/deps/v8/src/objects/js-array-buffer.cc b/deps/v8/src/objects/js-array-buffer.cc index 5ff7828ead..36950f9de6 100644 --- a/deps/v8/src/objects/js-array-buffer.cc +++ b/deps/v8/src/objects/js-array-buffer.cc @@ -41,7 +41,7 @@ void JSArrayBuffer::Neuter() { CHECK(!was_neutered()); CHECK(is_external()); set_backing_store(nullptr); - set_byte_length(Smi::kZero); + set_byte_length(0); set_was_neutered(true); set_is_neuterable(false); // Invalidate the neutering protector. @@ -51,13 +51,6 @@ void JSArrayBuffer::Neuter() { } } -void JSArrayBuffer::StopTrackingWasmMemory(Isolate* isolate) { - DCHECK(is_wasm_memory()); - isolate->wasm_engine()->memory_tracker()->ReleaseAllocation(isolate, - backing_store()); - set_is_wasm_memory(false); -} - void JSArrayBuffer::FreeBackingStoreFromMainThread() { if (allocation_base() == nullptr) { return; @@ -76,7 +69,8 @@ void JSArrayBuffer::FreeBackingStore(Isolate* isolate, Allocation allocation) { isolate->wasm_engine()->memory_tracker(); if (!memory_tracker->FreeMemoryIfIsWasmMemory(isolate, allocation.backing_store)) { - CHECK(FreePages(allocation.allocation_base, allocation.length)); + CHECK(FreePages(GetPlatformPageAllocator(), allocation.allocation_base, + allocation.length)); } } else { isolate->array_buffer_allocator()->Free(allocation.allocation_base, @@ -84,28 +78,21 @@ void JSArrayBuffer::FreeBackingStore(Isolate* isolate, Allocation allocation) { } } -void JSArrayBuffer::set_is_wasm_memory(bool is_wasm_memory) { - set_bit_field(IsWasmMemory::update(bit_field(), is_wasm_memory)); -} - void JSArrayBuffer::Setup(Handle<JSArrayBuffer> array_buffer, Isolate* isolate, bool is_external, void* data, size_t byte_length, - SharedFlag shared, bool is_wasm_memory) { + SharedFlag shared_flag, bool is_wasm_memory) { DCHECK_EQ(array_buffer->GetEmbedderFieldCount(), v8::ArrayBuffer::kEmbedderFieldCount); + DCHECK_LE(byte_length, JSArrayBuffer::kMaxByteLength); for (int i = 0; i < v8::ArrayBuffer::kEmbedderFieldCount; i++) { array_buffer->SetEmbedderField(i, Smi::kZero); } + array_buffer->set_byte_length(byte_length); array_buffer->set_bit_field(0); array_buffer->set_is_external(is_external); - array_buffer->set_is_neuterable(shared == SharedFlag::kNotShared); - array_buffer->set_is_shared(shared == SharedFlag::kShared); + array_buffer->set_is_neuterable(shared_flag == SharedFlag::kNotShared); + array_buffer->set_is_shared(shared_flag == SharedFlag::kShared); array_buffer->set_is_wasm_memory(is_wasm_memory); - - Handle<Object> heap_byte_length = - isolate->factory()->NewNumberFromSize(byte_length); - CHECK(heap_byte_length->IsSmi() || heap_byte_length->IsHeapNumber()); - array_buffer->set_byte_length(*heap_byte_length); // Initialize backing store at last to avoid handling of |JSArrayBuffers| that // are currently being constructed in the |ArrayBufferTracker|. The // registration method below handles the case of registering a buffer that has @@ -120,14 +107,15 @@ void JSArrayBuffer::Setup(Handle<JSArrayBuffer> array_buffer, Isolate* isolate, bool JSArrayBuffer::SetupAllocatingData(Handle<JSArrayBuffer> array_buffer, Isolate* isolate, size_t allocated_length, - bool initialize, SharedFlag shared) { + bool initialize, + SharedFlag shared_flag) { void* data; CHECK_NOT_NULL(isolate->array_buffer_allocator()); if (allocated_length != 0) { if (allocated_length >= MB) isolate->counters()->array_buffer_big_allocations()->AddSample( ConvertToMb(allocated_length)); - if (shared == SharedFlag::kShared) + if (shared_flag == SharedFlag::kShared) isolate->counters()->shared_array_allocations()->AddSample( ConvertToMb(allocated_length)); if (initialize) { @@ -147,7 +135,7 @@ bool JSArrayBuffer::SetupAllocatingData(Handle<JSArrayBuffer> array_buffer, const bool is_external = false; JSArrayBuffer::Setup(array_buffer, isolate, is_external, data, - allocated_length, shared); + allocated_length, shared_flag); return true; } @@ -175,9 +163,8 @@ Handle<JSArrayBuffer> JSTypedArray::MaterializeArrayBuffer( "JSTypedArray::MaterializeArrayBuffer"); } buffer->set_is_external(false); - DCHECK(buffer->byte_length()->IsSmi() || - buffer->byte_length()->IsHeapNumber()); - DCHECK(NumberToInt32(buffer->byte_length()) == fixed_typed_array->DataSize()); + DCHECK_EQ(buffer->byte_length(), + static_cast<uintptr_t>(fixed_typed_array->DataSize())); // Initialize backing store at last to avoid handling of |JSArrayBuffers| that // are currently being constructed in the |ArrayBufferTracker|. The // registration method below handles the case of registering a buffer that has @@ -234,9 +221,9 @@ Maybe<bool> JSTypedArray::DefineOwnProperty(Isolate* isolate, NewTypeError(MessageTemplate::kInvalidTypedArrayIndex)); } // 3b iv. Let length be O.[[ArrayLength]]. - uint32_t length = o->length()->Number(); + size_t length = o->length_value(); // 3b v. If numericIndex ≥ length, return false. - if (index >= length) { + if (o->WasNeutered() || index >= length) { RETURN_FAILURE(isolate, should_throw, NewTypeError(MessageTemplate::kInvalidTypedArrayIndex)); } diff --git a/deps/v8/src/objects/js-array-buffer.h b/deps/v8/src/objects/js-array-buffer.h index 109aacbc47..3f0dd064fa 100644 --- a/deps/v8/src/objects/js-array-buffer.h +++ b/deps/v8/src/objects/js-array-buffer.h @@ -5,7 +5,7 @@ #ifndef V8_OBJECTS_JS_ARRAY_BUFFER_H_ #define V8_OBJECTS_JS_ARRAY_BUFFER_H_ -#include "src/objects.h" +#include "src/objects/js-objects.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" @@ -14,12 +14,22 @@ namespace v8 { namespace internal { // Whether a JSArrayBuffer is a SharedArrayBuffer or not. -enum class SharedFlag { kNotShared, kShared }; +enum class SharedFlag : uint32_t { kNotShared, kShared }; class JSArrayBuffer : public JSObject { public: +// The maximum length for JSArrayBuffer's supported by V8. +// On 32-bit architectures we limit this to 2GiB, so that +// we can continue to use CheckBounds with the Unsigned31 +// restriction for the length. +#if V8_HOST_ARCH_32_BIT + static constexpr size_t kMaxByteLength = kMaxInt; +#else + static constexpr size_t kMaxByteLength = kMaxSafeInteger; +#endif + // [byte_length]: length in bytes - DECL_ACCESSORS(byte_length, Object) + DECL_PRIMITIVE_ACCESSORS(byte_length, size_t) // [backing_store]: backing memory for this array DECL_ACCESSORS(backing_store, void) @@ -29,26 +39,39 @@ class JSArrayBuffer : public JSObject { inline size_t allocation_length() const; inline void* allocation_base() const; - inline uint32_t bit_field() const; - inline void set_bit_field(uint32_t bits); + // [bit_field]: boolean flags + DECL_PRIMITIVE_ACCESSORS(bit_field, uint32_t) + +// Bit positions for [bit_field]. +#define JS_ARRAY_BUFFER_BIT_FIELD_FIELDS(V, _) \ + V(IsExternalBit, bool, 1, _) \ + V(IsNeuterableBit, bool, 1, _) \ + V(WasNeuteredBit, bool, 1, _) \ + V(IsSharedBit, bool, 1, _) \ + V(IsGrowableBit, bool, 1, _) \ + V(IsWasmMemoryBit, bool, 1, _) + DEFINE_BIT_FIELDS(JS_ARRAY_BUFFER_BIT_FIELD_FIELDS) +#undef JS_ARRAY_BUFFER_BIT_FIELD_FIELDS // [is_external]: true indicates that the embedder is in charge of freeing the // backing_store, while is_external == false means that v8 will free the // memory block once all ArrayBuffers referencing it are collected by the GC. - inline bool is_external(); - inline void set_is_external(bool value); + DECL_BOOLEAN_ACCESSORS(is_external) + + // [is_neuterable]: false indicates that this buffer cannot be detached. + DECL_BOOLEAN_ACCESSORS(is_neuterable) - inline bool is_neuterable(); - inline void set_is_neuterable(bool value); + // [was_neutered]: true if the buffer was previously detached. + DECL_BOOLEAN_ACCESSORS(was_neutered) - inline bool was_neutered(); - inline void set_was_neutered(bool value); + // [is_shared]: tells whether this is an ArrayBuffer or a SharedArrayBuffer. + DECL_BOOLEAN_ACCESSORS(is_shared) - inline bool is_shared(); - inline void set_is_shared(bool value); + // [is_growable]: indicates whether it's possible to grow this buffer. + DECL_BOOLEAN_ACCESSORS(is_growable) - inline bool is_growable(); - inline void set_is_growable(bool value); + // [is_wasm_memory]: whether the buffer is tracked by the WasmMemoryTracker. + DECL_BOOLEAN_ACCESSORS(is_wasm_memory) DECL_CAST(JSArrayBuffer) @@ -68,39 +91,30 @@ class JSArrayBuffer : public JSObject { bool is_wasm_memory; }; - // Returns whether the buffer is tracked by the WasmMemoryTracker. - inline bool is_wasm_memory() const; - - // Sets whether the buffer is tracked by the WasmMemoryTracker. - void set_is_wasm_memory(bool is_wasm_memory); - - // Removes the backing store from the WasmMemoryTracker and sets - // |is_wasm_memory| to false. - void StopTrackingWasmMemory(Isolate* isolate); - void FreeBackingStoreFromMainThread(); static void FreeBackingStore(Isolate* isolate, Allocation allocation); V8_EXPORT_PRIVATE static void Setup( Handle<JSArrayBuffer> array_buffer, Isolate* isolate, bool is_external, void* data, size_t allocated_length, - SharedFlag shared = SharedFlag::kNotShared, bool is_wasm_memory = false); + SharedFlag shared_flag = SharedFlag::kNotShared, + bool is_wasm_memory = false); // Returns false if array buffer contents could not be allocated. // In this case, |array_buffer| will not be set up. static bool SetupAllocatingData( Handle<JSArrayBuffer> array_buffer, Isolate* isolate, size_t allocated_length, bool initialize = true, - SharedFlag shared = SharedFlag::kNotShared) V8_WARN_UNUSED_RESULT; + SharedFlag shared_flag = SharedFlag::kNotShared) V8_WARN_UNUSED_RESULT; // Dispatched behavior. DECL_PRINTER(JSArrayBuffer) DECL_VERIFIER(JSArrayBuffer) - static const int kByteLengthOffset = JSObject::kHeaderSize; - // The rest of the fields are not JSObjects, so they are not iterated over in + // The fields are not pointers into our heap, so they are not iterated over in // objects-body-descriptors-inl.h. - static const int kBackingStoreOffset = kByteLengthOffset + kPointerSize; + static const int kByteLengthOffset = JSObject::kHeaderSize; + static const int kBackingStoreOffset = kByteLengthOffset + kUIntptrSize; static const int kBitFieldSlot = kBackingStoreOffset + kPointerSize; #if V8_TARGET_LITTLE_ENDIAN || !V8_HOST_ARCH_64_BIT static const int kBitFieldOffset = kBitFieldSlot; @@ -115,15 +129,6 @@ class JSArrayBuffer : public JSObject { // Iterates all fields in the object including internal ones except // kBackingStoreOffset and kBitFieldSlot. class BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; - - class IsExternal : public BitField<bool, 1, 1> {}; - class IsNeuterable : public BitField<bool, 2, 1> {}; - class WasNeutered : public BitField<bool, 3, 1> {}; - class IsShared : public BitField<bool, 4, 1> {}; - class IsGrowable : public BitField<bool, 5, 1> {}; - class IsWasmMemory : public BitField<bool, 6, 1> {}; private: DISALLOW_IMPLICIT_CONSTRUCTORS(JSArrayBuffer); @@ -135,10 +140,10 @@ class JSArrayBufferView : public JSObject { DECL_ACCESSORS(buffer, Object) // [byte_offset]: offset of typed array in bytes. - DECL_ACCESSORS(byte_offset, Object) + DECL_PRIMITIVE_ACCESSORS(byte_offset, size_t) // [byte_length]: length of typed array in bytes. - DECL_ACCESSORS(byte_length, Object) + DECL_PRIMITIVE_ACCESSORS(byte_length, size_t) DECL_CAST(JSArrayBufferView) @@ -148,15 +153,14 @@ class JSArrayBufferView : public JSObject { static const int kBufferOffset = JSObject::kHeaderSize; static const int kByteOffsetOffset = kBufferOffset + kPointerSize; - static const int kByteLengthOffset = kByteOffsetOffset + kPointerSize; - static const int kViewSize = kByteLengthOffset + kPointerSize; + static const int kByteLengthOffset = kByteOffsetOffset + kUIntptrSize; + static const int kHeaderSize = kByteLengthOffset + kUIntptrSize; - private: -#ifdef VERIFY_HEAP - DECL_ACCESSORS(raw_byte_offset, Object) - DECL_ACCESSORS(raw_byte_length, Object) -#endif + // Iterates all fields in the object including internal ones except + // kByteOffset and kByteLengthOffset. + class BodyDescriptor; + private: DISALLOW_IMPLICIT_CONSTRUCTORS(JSArrayBufferView); }; @@ -189,9 +193,8 @@ class JSTypedArray : public JSArrayBufferView { DECL_PRINTER(JSTypedArray) DECL_VERIFIER(JSTypedArray) - static const int kLengthOffset = kViewSize; + static const int kLengthOffset = JSArrayBufferView::kHeaderSize; static const int kSize = kLengthOffset + kPointerSize; - static const int kSizeWithEmbedderFields = kSize + v8::ArrayBufferView::kEmbedderFieldCount * kPointerSize; @@ -213,8 +216,7 @@ class JSDataView : public JSArrayBufferView { DECL_PRINTER(JSDataView) DECL_VERIFIER(JSDataView) - static const int kSize = kViewSize; - + static const int kSize = JSArrayBufferView::kHeaderSize; static const int kSizeWithEmbedderFields = kSize + v8::ArrayBufferView::kEmbedderFieldCount * kPointerSize; diff --git a/deps/v8/src/objects/js-array.h b/deps/v8/src/objects/js-array.h index b212848ce7..3a9fe48d24 100644 --- a/deps/v8/src/objects/js-array.h +++ b/deps/v8/src/objects/js-array.h @@ -5,8 +5,9 @@ #ifndef V8_OBJECTS_JS_ARRAY_H_ #define V8_OBJECTS_JS_ARRAY_H_ -#include "src/objects.h" +#include "src/objects/allocation-site.h" #include "src/objects/fixed-array.h" +#include "src/objects/js-objects.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" diff --git a/deps/v8/src/objects/js-break-iterator-inl.h b/deps/v8/src/objects/js-break-iterator-inl.h new file mode 100644 index 0000000000..16f8111953 --- /dev/null +++ b/deps/v8/src/objects/js-break-iterator-inl.h @@ -0,0 +1,49 @@ +// Copyright 2018 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. + +#ifndef V8_INTL_SUPPORT +#error Internationalization is expected to be enabled. +#endif // V8_INTL_SUPPORT + +#ifndef V8_OBJECTS_JS_BREAK_ITERATOR_INL_H_ +#define V8_OBJECTS_JS_BREAK_ITERATOR_INL_H_ + +#include "src/objects-inl.h" +#include "src/objects/js-break-iterator.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +inline void JSV8BreakIterator::set_type(Type type) { + DCHECK_GT(JSV8BreakIterator::Type::COUNT, type); + WRITE_FIELD(this, kTypeOffset, Smi::FromInt(static_cast<int>(type))); +} + +inline JSV8BreakIterator::Type JSV8BreakIterator::type() const { + Object* value = READ_FIELD(this, kTypeOffset); + return static_cast<JSV8BreakIterator::Type>(Smi::ToInt(value)); +} + +ACCESSORS(JSV8BreakIterator, locale, String, kLocaleOffset) +ACCESSORS(JSV8BreakIterator, break_iterator, Managed<icu::BreakIterator>, + kBreakIteratorOffset) +ACCESSORS(JSV8BreakIterator, unicode_string, Managed<icu::UnicodeString>, + kUnicodeStringOffset) +ACCESSORS(JSV8BreakIterator, bound_adopt_text, Object, kBoundAdoptTextOffset) +ACCESSORS(JSV8BreakIterator, bound_first, Object, kBoundFirstOffset) +ACCESSORS(JSV8BreakIterator, bound_next, Object, kBoundNextOffset) +ACCESSORS(JSV8BreakIterator, bound_current, Object, kBoundCurrentOffset) +ACCESSORS(JSV8BreakIterator, bound_break_type, Object, kBoundBreakTypeOffset) + +CAST_ACCESSOR(JSV8BreakIterator) + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_JS_BREAK_ITERATOR_INL_H_ diff --git a/deps/v8/src/objects/js-break-iterator.cc b/deps/v8/src/objects/js-break-iterator.cc new file mode 100644 index 0000000000..2031c2cc5b --- /dev/null +++ b/deps/v8/src/objects/js-break-iterator.cc @@ -0,0 +1,170 @@ +// Copyright 2018 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. + +#ifndef V8_INTL_SUPPORT +#error Internationalization is expected to be enabled. +#endif // V8_INTL_SUPPORT + +#include "src/objects/js-break-iterator.h" + +#include "src/objects/intl-objects-inl.h" +#include "src/objects/intl-objects.h" +#include "src/objects/js-break-iterator-inl.h" +#include "unicode/brkiter.h" + +namespace v8 { +namespace internal { + +JSV8BreakIterator::Type JSV8BreakIterator::getType(const char* str) { + if (strcmp(str, "character") == 0) return Type::CHARACTER; + if (strcmp(str, "word") == 0) return Type::WORD; + if (strcmp(str, "sentence") == 0) return Type::SENTENCE; + if (strcmp(str, "line") == 0) return Type::LINE; + UNREACHABLE(); +} + +MaybeHandle<JSV8BreakIterator> JSV8BreakIterator::Initialize( + Isolate* isolate, Handle<JSV8BreakIterator> break_iterator_holder, + Handle<Object> locales, Handle<Object> options_obj) { + Factory* factory = isolate->factory(); + + Handle<JSReceiver> options; + if (options_obj->IsUndefined(isolate)) { + options = factory->NewJSObjectWithNullProto(); + } else { + ASSIGN_RETURN_ON_EXCEPTION( + isolate, options, + Object::ToObject(isolate, options_obj, "Intl.JSV8BreakIterator"), + JSV8BreakIterator); + } + + // Extract locale string + Handle<JSObject> r; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, r, + Intl::ResolveLocale(isolate, "breakiterator", locales, options), + JSV8BreakIterator); + Handle<Object> locale_obj = + JSObject::GetDataProperty(r, factory->locale_string()); + CHECK(locale_obj->IsString()); + Handle<String> locale = Handle<String>::cast(locale_obj); + + // Extract type from options + std::unique_ptr<char[]> type_str = nullptr; + std::vector<const char*> type_values = {"character", "word", "sentence", + "line"}; + Maybe<bool> maybe_found_type = Intl::GetStringOption( + isolate, options, "type", type_values, "Intl.v8BreakIterator", &type_str); + Type type_enum = Type::WORD; + MAYBE_RETURN(maybe_found_type, MaybeHandle<JSV8BreakIterator>()); + if (maybe_found_type.FromJust()) { + DCHECK_NOT_NULL(type_str.get()); + type_enum = getType(type_str.get()); + } + + // Construct icu_locale using the locale string + icu::Locale icu_locale = Intl::CreateICULocale(isolate, locale); + DCHECK(!icu_locale.isBogus()); + + // Construct break_iterator using icu_locale and type + UErrorCode status = U_ZERO_ERROR; + std::unique_ptr<icu::BreakIterator> break_iterator = nullptr; + switch (type_enum) { + case Type::CHARACTER: + break_iterator.reset( + icu::BreakIterator::createCharacterInstance(icu_locale, status)); + break; + case Type::SENTENCE: + break_iterator.reset( + icu::BreakIterator::createSentenceInstance(icu_locale, status)); + break; + case Type::LINE: + break_iterator.reset( + icu::BreakIterator::createLineInstance(icu_locale, status)); + break; + default: + break_iterator.reset( + icu::BreakIterator::createWordInstance(icu_locale, status)); + break; + } + + // Error handling for break_iterator + if (U_FAILURE(status)) { + FATAL("Failed to create ICU break iterator, are ICU data files missing?"); + } + CHECK_NOT_NULL(break_iterator.get()); + isolate->CountUsage(v8::Isolate::UseCounterFeature::kBreakIterator); + + // Construct managed objects from pointers + Handle<Managed<icu::BreakIterator>> managed_break_iterator = + Managed<icu::BreakIterator>::FromUniquePtr(isolate, 0, + std::move(break_iterator)); + Handle<Managed<icu::UnicodeString>> managed_unicode_string = + Managed<icu::UnicodeString>::FromRawPtr(isolate, 0, nullptr); + + // Setting fields + break_iterator_holder->set_locale(*locale); + break_iterator_holder->set_type(type_enum); + break_iterator_holder->set_break_iterator(*managed_break_iterator); + break_iterator_holder->set_unicode_string(*managed_unicode_string); + + // Return break_iterator_holder + return break_iterator_holder; +} + +Handle<JSObject> JSV8BreakIterator::ResolvedOptions( + Isolate* isolate, Handle<JSV8BreakIterator> break_iterator) { + Factory* factory = isolate->factory(); + + Handle<JSObject> result = factory->NewJSObject(isolate->object_function()); + Handle<String> locale(break_iterator->locale(), isolate); + + JSObject::AddProperty(isolate, result, factory->locale_string(), locale, + NONE); + JSObject::AddProperty(isolate, result, factory->type_string(), + break_iterator->TypeAsString(), NONE); + return result; +} + +void JSV8BreakIterator::AdoptText( + Isolate* isolate, Handle<JSV8BreakIterator> break_iterator_holder, + Handle<String> text) { + icu::UnicodeString* u_text; + int length = text->length(); + text = String::Flatten(isolate, text); + { + DisallowHeapAllocation no_gc; + String::FlatContent flat = text->GetFlatContent(); + std::unique_ptr<uc16[]> sap; + const UChar* text_value = GetUCharBufferFromFlat(flat, &sap, length); + u_text = new icu::UnicodeString(text_value, length); + } + + Handle<Managed<icu::UnicodeString>> new_u_text = + Managed<icu::UnicodeString>::FromRawPtr(isolate, 0, u_text); + break_iterator_holder->set_unicode_string(*new_u_text); + + icu::BreakIterator* break_iterator = + break_iterator_holder->break_iterator()->raw(); + CHECK_NOT_NULL(break_iterator); + break_iterator->setText(*u_text); +} + +Handle<String> JSV8BreakIterator::TypeAsString() const { + switch (type()) { + case Type::CHARACTER: + return GetReadOnlyRoots().character_string_handle(); + case Type::WORD: + return GetReadOnlyRoots().word_string_handle(); + case Type::SENTENCE: + return GetReadOnlyRoots().sentence_string_handle(); + case Type::LINE: + return GetReadOnlyRoots().line_string_handle(); + case Type::COUNT: + UNREACHABLE(); + } +} + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/objects/js-break-iterator.h b/deps/v8/src/objects/js-break-iterator.h new file mode 100644 index 0000000000..d5847bdaf6 --- /dev/null +++ b/deps/v8/src/objects/js-break-iterator.h @@ -0,0 +1,87 @@ +// Copyright 2018 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. + +#ifndef V8_INTL_SUPPORT +#error Internationalization is expected to be enabled. +#endif // V8_INTL_SUPPORT + +#ifndef V8_OBJECTS_JS_BREAK_ITERATOR_H_ +#define V8_OBJECTS_JS_BREAK_ITERATOR_H_ + +#include "src/objects.h" +#include "src/objects/intl-objects.h" +#include "src/objects/managed.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace U_ICU_NAMESPACE { +class BreakIterator; +} // namespace U_ICU_NAMESPACE + +namespace v8 { +namespace internal { + +class JSV8BreakIterator : public JSObject { + public: + V8_WARN_UNUSED_RESULT static MaybeHandle<JSV8BreakIterator> Initialize( + Isolate* isolate, Handle<JSV8BreakIterator> break_iterator_holder, + Handle<Object> input_locales, Handle<Object> input_options); + + static Handle<JSObject> ResolvedOptions( + Isolate* isolate, Handle<JSV8BreakIterator> break_iterator); + + static void AdoptText(Isolate* isolate, + Handle<JSV8BreakIterator> break_iterator_holder, + Handle<String> text); + + enum class Type { CHARACTER, WORD, SENTENCE, LINE, COUNT }; + inline void set_type(Type type); + inline Type type() const; + + Handle<String> TypeAsString() const; + + DECL_CAST(JSV8BreakIterator) + DECL_PRINTER(JSV8BreakIterator) + DECL_VERIFIER(JSV8BreakIterator) + + DECL_ACCESSORS(locale, String) + DECL_ACCESSORS(break_iterator, Managed<icu::BreakIterator>) + DECL_ACCESSORS(unicode_string, Managed<icu::UnicodeString>) + DECL_ACCESSORS(bound_adopt_text, Object) + DECL_ACCESSORS(bound_first, Object) + DECL_ACCESSORS(bound_next, Object) + DECL_ACCESSORS(bound_current, Object) + DECL_ACCESSORS(bound_break_type, Object) + +// Layout description. +#define BREAK_ITERATOR_FIELDS(V) \ + /* Pointer fields. */ \ + V(kLocaleOffset, kPointerSize) \ + V(kTypeOffset, kPointerSize) \ + V(kBreakIteratorOffset, kPointerSize) \ + V(kUnicodeStringOffset, kPointerSize) \ + V(kBoundAdoptTextOffset, kPointerSize) \ + V(kBoundFirstOffset, kPointerSize) \ + V(kBoundNextOffset, kPointerSize) \ + V(kBoundCurrentOffset, kPointerSize) \ + V(kBoundBreakTypeOffset, kPointerSize) \ + /* Total Size */ \ + V(kSize, 0) + + DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, BREAK_ITERATOR_FIELDS) +#undef BREAK_ITERATOR_FIELDS + + private: + static Type getType(const char* str); + + DISALLOW_IMPLICIT_CONSTRUCTORS(JSV8BreakIterator) +}; + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_JS_BREAK_ITERATOR_H_ diff --git a/deps/v8/src/objects/js-collator-inl.h b/deps/v8/src/objects/js-collator-inl.h index 279a8bfd49..1a94ac805c 100644 --- a/deps/v8/src/objects/js-collator-inl.h +++ b/deps/v8/src/objects/js-collator-inl.h @@ -20,18 +20,6 @@ namespace internal { ACCESSORS(JSCollator, icu_collator, Managed<icu::Collator>, kICUCollatorOffset) ACCESSORS(JSCollator, bound_compare, Object, kBoundCompareOffset); -SMI_ACCESSORS(JSCollator, flags, kFlagsOffset) - -inline void JSCollator::set_usage(Usage usage) { - DCHECK_LT(usage, Usage::COUNT); - int hints = flags(); - hints = UsageBits::update(hints, usage); - set_flags(hints); -} - -inline JSCollator::Usage JSCollator::usage() const { - return UsageBits::decode(flags()); -} CAST_ACCESSOR(JSCollator); diff --git a/deps/v8/src/objects/js-collator.cc b/deps/v8/src/objects/js-collator.cc index c6cbecfb01..f62177b875 100644 --- a/deps/v8/src/objects/js-collator.cc +++ b/deps/v8/src/objects/js-collator.cc @@ -22,6 +22,11 @@ namespace internal { namespace { +enum class Usage { + SORT, + SEARCH, +}; + // TODO(gsathya): Consider internalizing the value strings. void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options, Handle<String> key, const char* value) { @@ -47,6 +52,13 @@ void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options, .FromJust()); } +void toLanguageTag(const icu::Locale& locale, char* tag) { + UErrorCode status = U_ZERO_ERROR; + uloc_toLanguageTag(locale.getName(), tag, ULOC_FULLNAME_CAPACITY, FALSE, + &status); + CHECK(U_SUCCESS(status)); +} + } // anonymous namespace // static @@ -55,11 +67,6 @@ Handle<JSObject> JSCollator::ResolvedOptions(Isolate* isolate, Handle<JSObject> options = isolate->factory()->NewJSObject(isolate->object_function()); - JSCollator::Usage usage = collator->usage(); - CreateDataPropertyForOptions(isolate, options, - isolate->factory()->usage_string(), - JSCollator::UsageToString(usage)); - icu::Collator* icu_collator = collator->icu_collator()->raw(); CHECK_NOT_NULL(icu_collator); @@ -128,97 +135,71 @@ Handle<JSObject> JSCollator::ResolvedOptions(Isolate* isolate, ignore_punctuation); status = U_ZERO_ERROR; - const char* collation; - std::unique_ptr<icu::StringEnumeration> collation_values( - icu_collator->getKeywordValues("co", status)); - // Collation wasn't provided as a keyword to icu, use default. - if (status == U_ILLEGAL_ARGUMENT_ERROR) { - CreateDataPropertyForOptions( - isolate, options, isolate->factory()->collation_string(), "default"); - } else { - CHECK(U_SUCCESS(status)); - CHECK_NOT_NULL(collation_values.get()); - - int32_t length; - status = U_ZERO_ERROR; - collation = collation_values->next(&length, status); - CHECK(U_SUCCESS(status)); - - // There has to be at least one value. - CHECK_NOT_NULL(collation); - CreateDataPropertyForOptions( - isolate, options, isolate->factory()->collation_string(), collation); - - status = U_ZERO_ERROR; - collation_values->reset(status); - CHECK(U_SUCCESS(status)); - } - - status = U_ZERO_ERROR; - icu::Locale icu_locale = icu_collator->getLocale(ULOC_VALID_LOCALE, status); - CHECK(U_SUCCESS(status)); - char result[ULOC_FULLNAME_CAPACITY]; - status = U_ZERO_ERROR; - uloc_toLanguageTag(icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, - FALSE, &status); + icu::Locale icu_locale(icu_collator->getLocale(ULOC_VALID_LOCALE, status)); CHECK(U_SUCCESS(status)); - CreateDataPropertyForOptions(isolate, options, - isolate->factory()->locale_string(), result); - - return options; -} - -namespace { - -std::map<std::string, std::string> LookupUnicodeExtensions( - const icu::Locale& icu_locale, const std::set<std::string>& relevant_keys) { - std::map<std::string, std::string> extensions; - - UErrorCode status = U_ZERO_ERROR; - std::unique_ptr<icu::StringEnumeration> keywords( - icu_locale.createKeywords(status)); - if (U_FAILURE(status)) return extensions; + const char* collation = "default"; + const char* usage = "sort"; + const char* collation_key = "co"; + const char* legacy_collation_key = uloc_toLegacyKey(collation_key); + DCHECK_NOT_NULL(legacy_collation_key); - if (!keywords) return extensions; - char value[ULOC_FULLNAME_CAPACITY]; - - int32_t length; + char bcp47_locale_tag[ULOC_FULLNAME_CAPACITY]; + char legacy_collation_value[ULOC_FULLNAME_CAPACITY]; status = U_ZERO_ERROR; - for (const char* keyword = keywords->next(&length, status); - keyword != nullptr; keyword = keywords->next(&length, status)) { - // Ignore failures in ICU and skip to the next keyword. - // - // This is fine.™ - if (U_FAILURE(status)) { + int32_t length = + icu_locale.getKeywordValue(legacy_collation_key, legacy_collation_value, + ULOC_FULLNAME_CAPACITY, status); + + if (length > 0 && U_SUCCESS(status)) { + const char* collation_value = + uloc_toUnicodeLocaleType(collation_key, legacy_collation_value); + CHECK_NOT_NULL(collation_value); + + if (strcmp(collation_value, "search") == 0) { + usage = "search"; + + // Search is disallowed as a collation value per spec. Let's + // use `default`, instead. + // + // https://tc39.github.io/ecma402/#sec-properties-of-intl-collator-instances + collation = "default"; + + // We clone the icu::Locale because we don't want the + // icu_collator to be affected when we remove the collation key + // below. + icu::Locale new_icu_locale = icu_locale; + + // The spec forbids the search as a collation value in the + // locale tag, so let's filter it out. status = U_ZERO_ERROR; - continue; - } - - icu_locale.getKeywordValue(keyword, value, ULOC_FULLNAME_CAPACITY, status); + new_icu_locale.setKeywordValue(legacy_collation_key, nullptr, status); + CHECK(U_SUCCESS(status)); - // Ignore failures in ICU and skip to the next keyword. - // - // This is fine.™ - if (U_FAILURE(status)) { - status = U_ZERO_ERROR; - continue; + toLanguageTag(new_icu_locale, bcp47_locale_tag); + } else { + collation = collation_value; + toLanguageTag(icu_locale, bcp47_locale_tag); } + } else { + toLanguageTag(icu_locale, bcp47_locale_tag); + } - const char* bcp47_key = uloc_toUnicodeLocaleKey(keyword); + CreateDataPropertyForOptions( + isolate, options, isolate->factory()->collation_string(), collation); - // Ignore keywords that we don't recognize - spec allows that. - if (bcp47_key && (relevant_keys.find(bcp47_key) != relevant_keys.end())) { - const char* bcp47_value = uloc_toUnicodeLocaleType(bcp47_key, value); - extensions.insert( - std::pair<std::string, std::string>(bcp47_key, bcp47_value)); - } - } + CreateDataPropertyForOptions(isolate, options, + isolate->factory()->usage_string(), usage); + + CreateDataPropertyForOptions( + isolate, options, isolate->factory()->locale_string(), bcp47_locale_tag); - return extensions; + return options; } +namespace { + void SetCaseFirstOption(icu::Collator* icu_collator, const char* value) { CHECK_NOT_NULL(icu_collator); CHECK_NOT_NULL(value); @@ -236,9 +217,10 @@ void SetCaseFirstOption(icu::Collator* icu_collator, const char* value) { } // anonymous namespace // static -MaybeHandle<JSCollator> JSCollator::InitializeCollator( - Isolate* isolate, Handle<JSCollator> collator, Handle<Object> locales, - Handle<Object> options_obj) { +MaybeHandle<JSCollator> JSCollator::Initialize(Isolate* isolate, + Handle<JSCollator> collator, + Handle<Object> locales, + Handle<Object> options_obj) { // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales). Handle<JSObject> requested_locales; ASSIGN_RETURN_ON_EXCEPTION(isolate, requested_locales, @@ -264,7 +246,7 @@ MaybeHandle<JSCollator> JSCollator::InitializeCollator( // "search" », "sort"). std::vector<const char*> values = {"sort", "search"}; std::unique_ptr<char[]> usage_str = nullptr; - JSCollator::Usage usage = JSCollator::Usage::SORT; + Usage usage = Usage::SORT; Maybe<bool> found_usage = Intl::GetStringOption( isolate, options, "usage", values, "Intl.Collator", &usage_str); MAYBE_RETURN(found_usage, MaybeHandle<JSCollator>()); @@ -272,21 +254,10 @@ MaybeHandle<JSCollator> JSCollator::InitializeCollator( if (found_usage.FromJust()) { DCHECK_NOT_NULL(usage_str.get()); if (strcmp(usage_str.get(), "search") == 0) { - usage = JSCollator::Usage::SEARCH; + usage = Usage::SEARCH; } } - // 5. Set collator.[[Usage]] to usage. - collator->set_usage(usage); - - // 6. If usage is "sort", then - // a. Let localeData be %Collator%.[[SortLocaleData]]. - // 7. Else, - // a. Let localeData be %Collator%.[[SearchLocaleData]]. - // - // The above two spec operations aren't required, the Intl spec is - // crazy. See https://github.com/tc39/ecma402/issues/256 - // TODO(gsathya): This is currently done as part of the // Intl::ResolveLocale call below. Fix this once resolveLocale is // changed to not do the lookup. @@ -368,7 +339,7 @@ MaybeHandle<JSCollator> JSCollator::InitializeCollator( DCHECK(!icu_locale.isBogus()); std::map<std::string, std::string> extensions = - LookupUnicodeExtensions(icu_locale, relevant_extension_keys); + Intl::LookupUnicodeExtensions(icu_locale, relevant_extension_keys); // 19. Let collation be r.[[co]]. // @@ -386,11 +357,38 @@ MaybeHandle<JSCollator> JSCollator::InitializeCollator( const std::string& value = co_extension_it->second; if ((value == "search") || (value == "standard")) { UErrorCode status = U_ZERO_ERROR; - icu_locale.setKeywordValue("co", NULL, status); + const char* key = uloc_toLegacyKey("co"); + icu_locale.setKeywordValue(key, nullptr, status); CHECK(U_SUCCESS(status)); } } + // 5. Set collator.[[Usage]] to usage. + // + // 6. If usage is "sort", then + // a. Let localeData be %Collator%.[[SortLocaleData]]. + // 7. Else, + // a. Let localeData be %Collator%.[[SearchLocaleData]]. + // + // The Intl spec doesn't allow us to use "search" as an extension + // value for collation as per: + // https://tc39.github.io/ecma402/#sec-intl-collator-internal-slots + // + // But the only way to pass the value "search" for collation from + // the options object to ICU is to use the 'co' extension keyword. + // + // This will need to be filtered out when creating the + // resolvedOptions object. + if (usage == Usage::SEARCH) { + const char* key = uloc_toLegacyKey("co"); + CHECK_NOT_NULL(key); + const char* value = uloc_toLegacyType(key, "search"); + CHECK_NOT_NULL(value); + UErrorCode status = U_ZERO_ERROR; + icu_locale.setKeywordValue(key, value, status); + CHECK(U_SUCCESS(status)); + } + // 20. If collation is null, let collation be "default". // 21. Set collator.[[Collation]] to collation. // @@ -525,17 +523,5 @@ MaybeHandle<JSCollator> JSCollator::InitializeCollator( return collator; } -// static -const char* JSCollator::UsageToString(Usage usage) { - switch (usage) { - case Usage::SORT: - return "sort"; - case Usage::SEARCH: - return "search"; - case Usage::COUNT: - UNREACHABLE(); - } -} - } // namespace internal } // namespace v8 diff --git a/deps/v8/src/objects/js-collator.h b/deps/v8/src/objects/js-collator.h index b2751a446e..f857df95b1 100644 --- a/deps/v8/src/objects/js-collator.h +++ b/deps/v8/src/objects/js-collator.h @@ -18,13 +18,17 @@ // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" +namespace U_ICU_NAMESPACE { +class Collator; +} // namespace U_ICU_NAMESPACE + namespace v8 { namespace internal { class JSCollator : public JSObject { public: // ecma402/#sec-initializecollator - V8_WARN_UNUSED_RESULT static MaybeHandle<JSCollator> InitializeCollator( + V8_WARN_UNUSED_RESULT static MaybeHandle<JSCollator> Initialize( Isolate* isolate, Handle<JSCollator> collator, Handle<Object> locales, Handle<Object> options); @@ -36,22 +40,9 @@ class JSCollator : public JSObject { DECL_PRINTER(JSCollator) DECL_VERIFIER(JSCollator) - // [[Usage]] is one of the values "sort" or "search", identifying - // the collator usage. - enum class Usage { - SORT, - SEARCH, - - COUNT - }; - inline void set_usage(Usage usage); - inline Usage usage() const; - static const char* UsageToString(Usage usage); - // Layout description. #define JS_COLLATOR_FIELDS(V) \ V(kICUCollatorOffset, kPointerSize) \ - V(kFlagsOffset, kPointerSize) \ V(kBoundCompareOffset, kPointerSize) \ /* Total size. */ \ V(kSize, 0) @@ -59,26 +50,8 @@ class JSCollator : public JSObject { DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, JS_COLLATOR_FIELDS) #undef JS_COLLATOR_FIELDS - // ContextSlot defines the context structure for the bound - // Collator.prototype.compare function. - enum ContextSlot { - // The collator instance that the function holding this context is bound to. - kCollator = Context::MIN_CONTEXT_SLOTS, - kLength - }; - -// Bit positions in |flags|. -#define FLAGS_BIT_FIELDS(V, _) V(UsageBits, Usage, 1, _) - - DEFINE_BIT_FIELDS(FLAGS_BIT_FIELDS) -#undef FLAGS_BIT_FIELDS - - STATIC_ASSERT(Usage::SORT <= UsageBits::kMax); - STATIC_ASSERT(Usage::SEARCH <= UsageBits::kMax); - DECL_ACCESSORS(icu_collator, Managed<icu::Collator>) DECL_ACCESSORS(bound_compare, Object); - DECL_INT_ACCESSORS(flags) private: DISALLOW_IMPLICIT_CONSTRUCTORS(JSCollator); diff --git a/deps/v8/src/objects/js-collection.h b/deps/v8/src/objects/js-collection.h index 47bb7a9c2a..7b5e38e7d8 100644 --- a/deps/v8/src/objects/js-collection.h +++ b/deps/v8/src/objects/js-collection.h @@ -113,9 +113,6 @@ class JSWeakCollection : public JSObject { // Visit the whole object. typedef BodyDescriptorImpl BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; - private: DISALLOW_IMPLICIT_CONSTRUCTORS(JSWeakCollection); }; diff --git a/deps/v8/src/objects/js-date-time-format-inl.h b/deps/v8/src/objects/js-date-time-format-inl.h new file mode 100644 index 0000000000..0ad7f363c5 --- /dev/null +++ b/deps/v8/src/objects/js-date-time-format-inl.h @@ -0,0 +1,33 @@ +// Copyright 2018 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. + +#ifndef V8_INTL_SUPPORT +#error Internationalization is expected to be enabled. +#endif // V8_INTL_SUPPORT + +#ifndef V8_OBJECTS_JS_DATE_TIME_FORMAT_INL_H_ +#define V8_OBJECTS_JS_DATE_TIME_FORMAT_INL_H_ + +#include "src/objects-inl.h" +#include "src/objects/js-date-time-format.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +ACCESSORS(JSDateTimeFormat, icu_locale, Managed<icu::Locale>, kICULocaleOffset); +ACCESSORS(JSDateTimeFormat, icu_simple_date_format, + Managed<icu::SimpleDateFormat>, kICUSimpleDateFormatOffset) +ACCESSORS(JSDateTimeFormat, bound_format, Object, kBoundFormatOffset); + +CAST_ACCESSOR(JSDateTimeFormat); + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_JS_DATE_TIME_FORMAT_INL_H_ diff --git a/deps/v8/src/objects/js-date-time-format.cc b/deps/v8/src/objects/js-date-time-format.cc new file mode 100644 index 0000000000..6285b74b04 --- /dev/null +++ b/deps/v8/src/objects/js-date-time-format.cc @@ -0,0 +1,980 @@ +// Copyright 2018 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. + +#ifndef V8_INTL_SUPPORT +#error Internationalization is expected to be enabled. +#endif // V8_INTL_SUPPORT + +#include "src/objects/js-date-time-format.h" + +#include <memory> +#include <string> +#include <vector> + +#include "src/heap/factory.h" +#include "src/isolate.h" +#include "src/objects/intl-objects.h" +#include "src/objects/js-date-time-format-inl.h" + +#include "unicode/calendar.h" +#include "unicode/dtptngen.h" +#include "unicode/gregocal.h" +#include "unicode/numsys.h" +#include "unicode/smpdtfmt.h" +#include "unicode/unistr.h" + +namespace v8 { +namespace internal { + +namespace { + +class PatternMap { + public: + PatternMap(std::string pattern, std::string value) + : pattern(std::move(pattern)), value(std::move(value)) {} + virtual ~PatternMap() = default; + std::string pattern; + std::string value; +}; + +class PatternItem { + public: + PatternItem(const std::string property, std::vector<PatternMap> pairs, + std::vector<const char*> allowed_values) + : property(std::move(property)), + pairs(std::move(pairs)), + allowed_values(allowed_values) {} + virtual ~PatternItem() = default; + + const std::string property; + // It is important for the pattern in the pairs from longer one to shorter one + // if the longer one contains substring of an shorter one. + std::vector<PatternMap> pairs; + std::vector<const char*> allowed_values; +}; + +const std::vector<PatternItem> GetPatternItems() { + const std::vector<const char*> kLongShort = {"long", "short"}; + const std::vector<const char*> kNarrowLongShort = {"narrow", "long", "short"}; + const std::vector<const char*> k2DigitNumeric = {"2-digit", "numeric"}; + const std::vector<const char*> kNarrowLongShort2DigitNumeric = { + "narrow", "long", "short", "2-digit", "numeric"}; + const std::vector<PatternItem> kPatternItems = { + PatternItem("weekday", + {{"EEEEE", "narrow"}, {"EEEE", "long"}, {"EEE", "short"}}, + kNarrowLongShort), + PatternItem("era", + {{"GGGGG", "narrow"}, {"GGGG", "long"}, {"GGG", "short"}}, + kNarrowLongShort), + PatternItem("year", {{"yy", "2-digit"}, {"y", "numeric"}}, + k2DigitNumeric), + // Sometimes we get L instead of M for month - standalone name. + PatternItem("month", + {{"MMMMM", "narrow"}, + {"MMMM", "long"}, + {"MMM", "short"}, + {"MM", "2-digit"}, + {"M", "numeric"}, + {"LLLLL", "narrow"}, + {"LLLL", "long"}, + {"LLL", "short"}, + {"LL", "2-digit"}, + {"L", "numeric"}}, + kNarrowLongShort2DigitNumeric), + PatternItem("day", {{"dd", "2-digit"}, {"d", "numeric"}}, k2DigitNumeric), + PatternItem("hour", + {{"HH", "2-digit"}, + {"H", "numeric"}, + {"hh", "2-digit"}, + {"h", "numeric"}}, + k2DigitNumeric), + PatternItem("minute", {{"mm", "2-digit"}, {"m", "numeric"}}, + k2DigitNumeric), + PatternItem("second", {{"ss", "2-digit"}, {"s", "numeric"}}, + k2DigitNumeric), + PatternItem("timeZoneName", {{"zzzz", "long"}, {"z", "short"}}, + kLongShort)}; + return kPatternItems; +} + +class PatternData { + public: + PatternData(const std::string property, std::vector<PatternMap> pairs, + std::vector<const char*> allowed_values) + : property(std::move(property)), allowed_values(allowed_values) { + for (const auto& pair : pairs) { + map.insert(std::make_pair(pair.value, pair.pattern)); + } + } + virtual ~PatternData() = default; + + const std::string property; + std::map<const std::string, const std::string> map; + std::vector<const char*> allowed_values; +}; + +enum HourOption { + H_UNKNOWN, + H_12, + H_24, +}; + +const std::vector<PatternData> CreateCommonData(const PatternData& hour_data) { + std::vector<PatternData> build; + for (const PatternItem& item : GetPatternItems()) { + if (item.property == "hour") { + build.push_back(hour_data); + } else { + build.push_back( + PatternData(item.property, item.pairs, item.allowed_values)); + } + } + return build; +} + +const std::vector<PatternData> CreateData(const char* digit2, + const char* numeric) { + static std::vector<const char*> k2DigitNumeric = {"2-digit", "numeric"}; + return CreateCommonData(PatternData( + "hour", {{digit2, "2-digit"}, {numeric, "numeric"}}, k2DigitNumeric)); +} + +const std::vector<PatternData> GetPatternData(HourOption option) { + const std::vector<PatternData> data = CreateData("jj", "j"); + const std::vector<PatternData> data_h12 = CreateData("hh", "h"); + const std::vector<PatternData> data_h24 = CreateData("HH", "H"); + switch (option) { + case HourOption::H_12: + return data_h12; + case HourOption::H_24: + return data_h24; + case HourOption::H_UNKNOWN: + return data; + } +} + +void SetPropertyFromPattern(Isolate* isolate, const std::string& pattern, + Handle<JSObject> options) { + Factory* factory = isolate->factory(); + const std::vector<PatternItem> items = GetPatternItems(); + for (const auto& item : items) { + for (const auto& pair : item.pairs) { + if (pattern.find(pair.pattern) != std::string::npos) { + // After we find the first pair in the item which matching the pattern, + // we set the property and look for the next item in kPatternItems. + CHECK(JSReceiver::CreateDataProperty( + isolate, options, + factory->NewStringFromAsciiChecked(item.property.c_str()), + factory->NewStringFromAsciiChecked(pair.value.c_str()), + kDontThrow) + .FromJust()); + break; + } + } + } + // hour12 + // b. If p is "hour12", then + // i. Let hc be dtf.[[HourCycle]]. + // ii. If hc is "h11" or "h12", let v be true. + // iii. Else if, hc is "h23" or "h24", let v be false. + // iv. Else, let v be undefined. + if (pattern.find('h') != std::string::npos) { + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->NewStringFromStaticChars("hour12"), + factory->true_value(), kDontThrow) + .FromJust()); + } else if (pattern.find('H') != std::string::npos) { + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->NewStringFromStaticChars("hour12"), + factory->false_value(), kDontThrow) + .FromJust()); + } +} + +std::string GetGMTTzID(Isolate* isolate, const std::string& input) { + std::string ret = "Etc/GMT"; + switch (input.length()) { + case 8: + if (input[7] == '0') return ret + '0'; + break; + case 9: + if ((input[7] == '+' || input[7] == '-') && + IsInRange(input[8], '0', '9')) { + return ret + input[7] + input[8]; + } + break; + case 10: + if ((input[7] == '+' || input[7] == '-') && (input[8] == '1') && + IsInRange(input[9], '0', '4')) { + return ret + input[7] + input[8] + input[9]; + } + break; + } + return ""; +} + +// Locale independenty version of isalpha for ascii range. This will return +// false if the ch is alpha but not in ascii range. +bool IsAsciiAlpha(char ch) { + return IsInRange(ch, 'A', 'Z') || IsInRange(ch, 'a', 'z'); +} + +// Locale independent toupper for ascii range. This will not return İ (dotted I) +// for i under Turkish locale while std::toupper may. +char LocaleIndependentAsciiToUpper(char ch) { + return (IsInRange(ch, 'a', 'z')) ? (ch - 'a' + 'A') : ch; +} + +// Locale independent tolower for ascii range. +char LocaleIndependentAsciiToLower(char ch) { + return (IsInRange(ch, 'A', 'Z')) ? (ch - 'A' + 'a') : ch; +} + +// Returns titlecased location, bueNos_airES -> Buenos_Aires +// or ho_cHi_minH -> Ho_Chi_Minh. It is locale-agnostic and only +// deals with ASCII only characters. +// 'of', 'au' and 'es' are special-cased and lowercased. +// ICU's timezone parsing is case sensitive, but ECMAScript is case insensitive +std::string ToTitleCaseTimezoneLocation(Isolate* isolate, + const std::string& input) { + std::string title_cased; + int word_length = 0; + for (char ch : input) { + // Convert first char to upper case, the rest to lower case + if (IsAsciiAlpha(ch)) { + title_cased += word_length == 0 ? LocaleIndependentAsciiToUpper(ch) + : LocaleIndependentAsciiToLower(ch); + word_length++; + } else if (ch == '_' || ch == '-' || ch == '/') { + // Special case Au/Es/Of to be lower case. + if (word_length == 2) { + size_t pos = title_cased.length() - 2; + std::string substr = title_cased.substr(pos, 2); + if (substr == "Of" || substr == "Es" || substr == "Au") { + title_cased[pos] = LocaleIndependentAsciiToLower(title_cased[pos]); + } + } + title_cased += ch; + word_length = 0; + } else { + // Invalid input + return std::string(); + } + } + return title_cased; +} + +} // namespace + +std::string JSDateTimeFormat::CanonicalizeTimeZoneID(Isolate* isolate, + const std::string& input) { + std::string upper = input; + transform(upper.begin(), upper.end(), upper.begin(), + LocaleIndependentAsciiToUpper); + if (upper == "UTC" || upper == "GMT" || upper == "ETC/UTC" || + upper == "ETC/GMT") { + return "UTC"; + } + // We expect only _, '-' and / beside ASCII letters. + // All inputs should conform to Area/Location(/Location)*, or Etc/GMT* . + // TODO(jshin): 1. Support 'GB-Eire", 'EST5EDT", "ROK', 'US/*', 'NZ' and many + // other aliases/linked names when moving timezone validation code to C++. + // See crbug.com/364374 and crbug.com/v8/8007 . + // 2. Resolve the difference betwee CLDR/ICU and IANA time zone db. + // See http://unicode.org/cldr/trac/ticket/9892 and crbug.com/645807 . + if (strncmp(upper.c_str(), "ETC/GMT", 7) == 0) { + return GetGMTTzID(isolate, input); + } + return ToTitleCaseTimezoneLocation(isolate, input); +} + +MaybeHandle<JSObject> JSDateTimeFormat::ResolvedOptions( + Isolate* isolate, Handle<JSDateTimeFormat> date_time_format) { + Factory* factory = isolate->factory(); + // 4. Let options be ! ObjectCreate(%ObjectPrototype%). + Handle<JSObject> options = factory->NewJSObject(isolate->object_function()); + + // 5. For each row of Table 6, except the header row, in any order, do + // a. Let p be the Property value of the current row. + Handle<Object> resolved_obj; + + // locale + UErrorCode status = U_ZERO_ERROR; + char language[ULOC_FULLNAME_CAPACITY]; + uloc_toLanguageTag(date_time_format->icu_locale()->raw()->getName(), language, + ULOC_FULLNAME_CAPACITY, FALSE, &status); + CHECK(U_SUCCESS(status)); + Handle<String> locale = factory->NewStringFromAsciiChecked(language); + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->locale_string(), locale, kDontThrow) + .FromJust()); + + icu::SimpleDateFormat* icu_simple_date_format = + date_time_format->icu_simple_date_format()->raw(); + // calendar + const icu::Calendar* calendar = icu_simple_date_format->getCalendar(); + // getType() returns legacy calendar type name instead of LDML/BCP47 calendar + // key values. intl.js maps them to BCP47 values for key "ca". + // TODO(jshin): Consider doing it here, instead. + std::string calendar_str = calendar->getType(); + + // Maps ICU calendar names to LDML/BCP47 types for key 'ca'. + // See typeMap section in third_party/icu/source/data/misc/keyTypeData.txt + // and + // http://www.unicode.org/repos/cldr/tags/latest/common/bcp47/calendar.xml + if (calendar_str == "gregorian") { + calendar_str = "gregory"; + } else if (calendar_str == "ethiopic-amete-alem") { + calendar_str = "ethioaa"; + } + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->NewStringFromStaticChars("calendar"), + factory->NewStringFromAsciiChecked(calendar_str.c_str()), + kDontThrow) + .FromJust()); + + // Ugly hack. ICU doesn't expose numbering system in any way, so we have + // to assume that for given locale NumberingSystem constructor produces the + // same digits as NumberFormat/Calendar would. + // Tracked by https://unicode-org.atlassian.net/browse/ICU-13431 + std::unique_ptr<icu::NumberingSystem> numbering_system( + icu::NumberingSystem::createInstance( + *(date_time_format->icu_locale()->raw()), status)); + if (U_SUCCESS(status)) { + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->numberingSystem_string(), + factory->NewStringFromAsciiChecked(numbering_system->getName()), + kDontThrow) + .FromJust()); + } + + // timezone + const icu::TimeZone& tz = calendar->getTimeZone(); + icu::UnicodeString time_zone; + tz.getID(time_zone); + status = U_ZERO_ERROR; + icu::UnicodeString canonical_time_zone; + icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status); + if (U_SUCCESS(status)) { + Handle<String> timezone_value; + // In CLDR (http://unicode.org/cldr/trac/ticket/9943), Etc/UTC is made + // a separate timezone ID from Etc/GMT even though they're still the same + // timezone. We have Etc/UTC because 'UTC', 'Etc/Universal', + // 'Etc/Zulu' and others are turned to 'Etc/UTC' by ICU. Etc/GMT comes + // from Etc/GMT0, Etc/GMT+0, Etc/GMT-0, Etc/Greenwich. + // ecma402#sec-canonicalizetimezonename step 3 + if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/UTC") || + canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) { + timezone_value = factory->NewStringFromStaticChars("UTC"); + } else { + ASSIGN_RETURN_ON_EXCEPTION(isolate, timezone_value, + Intl::ToString(isolate, canonical_time_zone), + JSObject); + } + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->NewStringFromStaticChars("timeZone"), + timezone_value, kDontThrow) + .FromJust()); + } else { + // Somehow on Windows we will reach here. + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->NewStringFromStaticChars("timeZone"), + factory->undefined_value(), kDontThrow) + .FromJust()); + } + + icu::UnicodeString pattern_unicode; + icu_simple_date_format->toPattern(pattern_unicode); + std::string pattern; + pattern_unicode.toUTF8String(pattern); + SetPropertyFromPattern(isolate, pattern, options); + return options; +} + +namespace { + +// ecma402/#sec-formatdatetime +// FormatDateTime( dateTimeFormat, x ) +MaybeHandle<String> FormatDateTime(Isolate* isolate, + Handle<JSDateTimeFormat> date_time_format, + double x) { + double date_value = DateCache::TimeClip(x); + if (std::isnan(date_value)) { + THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue), + String); + } + + icu::SimpleDateFormat* date_format = + date_time_format->icu_simple_date_format()->raw(); + CHECK_NOT_NULL(date_format); + + icu::UnicodeString result; + date_format->format(date_value, result); + + return Intl::ToString(isolate, result); +} + +} // namespace + +// ecma402/#sec-datetime-format-functions +// DateTime Format Functions +MaybeHandle<String> JSDateTimeFormat::DateTimeFormat( + Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, + Handle<Object> date) { + // 2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]] + // internal slot. + + // 3. If date is not provided or is undefined, then + double x; + if (date->IsUndefined()) { + // 3.a Let x be Call(%Date_now%, undefined). + x = JSDate::CurrentTimeValue(isolate); + } else { + // 4. Else, + // a. Let x be ? ToNumber(date). + ASSIGN_RETURN_ON_EXCEPTION(isolate, date, Object::ToNumber(isolate, date), + String); + CHECK(date->IsNumber()); + x = date->Number(); + } + // 5. Return FormatDateTime(dtf, x). + return FormatDateTime(isolate, date_time_format, x); +} + +MaybeHandle<String> JSDateTimeFormat::ToLocaleDateTime( + Isolate* isolate, Handle<Object> date, Handle<Object> locales, + Handle<Object> options, RequiredOption required, DefaultsOption defaults, + const char* service) { + Factory* factory = isolate->factory(); + // 1. Let x be ? thisTimeValue(this value); + if (!date->IsJSDate()) { + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kMethodInvokedOnWrongType, + factory->NewStringFromStaticChars("Date")), + String); + } + + double const x = Handle<JSDate>::cast(date)->value()->Number(); + // 2. If x is NaN, return "Invalid Date" + if (std::isnan(x)) { + return factory->NewStringFromStaticChars("Invalid Date"); + } + + // 3. Let options be ? ToDateTimeOptions(options, required, defaults). + Handle<JSObject> internal_options; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, internal_options, + ToDateTimeOptions(isolate, options, required, defaults), String); + + // 4. Let dateFormat be ? Construct(%DateTimeFormat%, « locales, options »). + Handle<JSObject> object; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, object, + Intl::CachedOrNewService(isolate, + factory->NewStringFromAsciiChecked(service), + locales, options, internal_options), + String); + + CHECK(object->IsJSDateTimeFormat()); + Handle<JSDateTimeFormat> date_time_format = + Handle<JSDateTimeFormat>::cast(object); + // 5. Return FormatDateTime(dateFormat, x). + return FormatDateTime(isolate, date_time_format, x); +} + +namespace { + +Maybe<bool> IsPropertyUndefined(Isolate* isolate, Handle<JSObject> options, + const char* property) { + Factory* factory = isolate->factory(); + // i. Let prop be the property name. + // ii. Let value be ? Get(options, prop). + Handle<Object> value; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, value, + Object::GetPropertyOrElement( + isolate, options, factory->NewStringFromAsciiChecked(property)), + Nothing<bool>()); + return Just(value->IsUndefined(isolate)); +} + +Maybe<bool> NeedsDefault(Isolate* isolate, Handle<JSObject> options, + const std::vector<std::string>& props) { + bool needs_default = true; + for (const auto& prop : props) { + // i. Let prop be the property name. + // ii. Let value be ? Get(options, prop) + Maybe<bool> maybe_undefined = + IsPropertyUndefined(isolate, options, prop.c_str()); + MAYBE_RETURN(maybe_undefined, Nothing<bool>()); + // iii. If value is not undefined, let needDefaults be false. + if (!maybe_undefined.FromJust()) { + needs_default = false; + } + } + return Just(needs_default); +} + +Maybe<bool> CreateDefault(Isolate* isolate, Handle<JSObject> options, + const std::vector<std::string>& props) { + Factory* factory = isolate->factory(); + // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). + for (const auto& prop : props) { + MAYBE_RETURN( + JSReceiver::CreateDataProperty( + isolate, options, factory->NewStringFromAsciiChecked(prop.c_str()), + factory->numeric_string(), kThrowOnError), + Nothing<bool>()); + } + return Just(true); +} + +} // namespace + +// ecma-402/#sec-todatetimeoptions +MaybeHandle<JSObject> JSDateTimeFormat::ToDateTimeOptions( + Isolate* isolate, Handle<Object> input_options, RequiredOption required, + DefaultsOption defaults) { + Factory* factory = isolate->factory(); + // 1. If options is undefined, let options be null; otherwise let options be ? + // ToObject(options). + Handle<JSObject> options; + if (input_options->IsUndefined(isolate)) { + options = factory->NewJSObjectWithNullProto(); + } else { + Handle<JSReceiver> options_obj; + ASSIGN_RETURN_ON_EXCEPTION(isolate, options_obj, + Object::ToObject(isolate, input_options), + JSObject); + // 2. Let options be ObjectCreate(options). + ASSIGN_RETURN_ON_EXCEPTION(isolate, options, + JSObject::ObjectCreate(isolate, options_obj), + JSObject); + } + + // 3. Let needDefaults be true. + bool needs_default = true; + + // 4. If required is "date" or "any", then + if (required == RequiredOption::kAny || required == RequiredOption::kDate) { + // a. For each of the property names "weekday", "year", "month", "day", do + const std::vector<std::string> list({"weekday", "year", "month", "day"}); + Maybe<bool> maybe_needs_default = NeedsDefault(isolate, options, list); + MAYBE_RETURN(maybe_needs_default, Handle<JSObject>()); + needs_default = maybe_needs_default.FromJust(); + } + + // 5. If required is "time" or "any", then + if (required == RequiredOption::kAny || required == RequiredOption::kTime) { + // a. For each of the property names "hour", "minute", "second", do + const std::vector<std::string> list({"hour", "minute", "second"}); + Maybe<bool> maybe_needs_default = NeedsDefault(isolate, options, list); + MAYBE_RETURN(maybe_needs_default, Handle<JSObject>()); + needs_default &= maybe_needs_default.FromJust(); + } + + // 6. If needDefaults is true and defaults is either "date" or "all", then + if (needs_default) { + if (defaults == DefaultsOption::kAll || defaults == DefaultsOption::kDate) { + // a. For each of the property names "year", "month", "day", do) + const std::vector<std::string> list({"year", "month", "day"}); + MAYBE_RETURN(CreateDefault(isolate, options, list), Handle<JSObject>()); + } + // 7. If needDefaults is true and defaults is either "time" or "all", then + if (defaults == DefaultsOption::kAll || defaults == DefaultsOption::kTime) { + // a. For each of the property names "hour", "minute", "second", do + const std::vector<std::string> list({"hour", "minute", "second"}); + MAYBE_RETURN(CreateDefault(isolate, options, list), Handle<JSObject>()); + } + } + // 8. Return options. + return options; +} + +MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::UnwrapDateTimeFormat( + Isolate* isolate, Handle<JSReceiver> format_holder) { + Handle<Context> native_context = + Handle<Context>(isolate->context()->native_context(), isolate); + Handle<JSFunction> constructor = Handle<JSFunction>( + JSFunction::cast(native_context->intl_date_time_format_function()), + isolate); + Handle<Object> dtf; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, dtf, + Intl::LegacyUnwrapReceiver(isolate, format_holder, constructor, + format_holder->IsJSDateTimeFormat()), + JSDateTimeFormat); + // 2. If Type(dtf) is not Object or dtf does not have an + // [[InitializedDateTimeFormat]] internal slot, then + if (!dtf->IsJSDateTimeFormat()) { + // a. Throw a TypeError exception. + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, + isolate->factory()->NewStringFromAsciiChecked( + "UnwrapDateTimeFormat"), + format_holder), + JSDateTimeFormat); + } + // 3. Return dtf. + return Handle<JSDateTimeFormat>::cast(dtf); +} + +namespace { + +// ecma-402/#sec-isvalidtimezonename +bool IsValidTimeZoneName(const icu::TimeZone& tz) { + UErrorCode status = U_ZERO_ERROR; + icu::UnicodeString id; + tz.getID(id); + icu::UnicodeString canonical; + icu::TimeZone::getCanonicalID(id, canonical, status); + return U_SUCCESS(status) && + canonical != icu::UnicodeString("Etc/Unknown", -1, US_INV); +} + +std::unique_ptr<icu::TimeZone> CreateTimeZone(Isolate* isolate, + const char* timezone) { + // Create time zone as specified by the user. We have to re-create time zone + // since calendar takes ownership. + if (timezone == nullptr) { + // 19.a. Else / Let timeZone be DefaultTimeZone(). + return std::unique_ptr<icu::TimeZone>(icu::TimeZone::createDefault()); + } + std::string canonicalized = + JSDateTimeFormat::CanonicalizeTimeZoneID(isolate, timezone); + if (canonicalized.empty()) return std::unique_ptr<icu::TimeZone>(); + std::unique_ptr<icu::TimeZone> tz( + icu::TimeZone::createTimeZone(canonicalized.c_str())); + // 18.b If the result of IsValidTimeZoneName(timeZone) is false, then + // i. Throw a RangeError exception. + if (!IsValidTimeZoneName(*tz)) return std::unique_ptr<icu::TimeZone>(); + return tz; +} + +std::unique_ptr<icu::Calendar> CreateCalendar(Isolate* isolate, + const icu::Locale& icu_locale, + const char* timezone) { + std::unique_ptr<icu::TimeZone> tz = CreateTimeZone(isolate, timezone); + if (tz.get() == nullptr) return std::unique_ptr<icu::Calendar>(); + + // Create a calendar using locale, and apply time zone to it. + UErrorCode status = U_ZERO_ERROR; + std::unique_ptr<icu::Calendar> calendar( + icu::Calendar::createInstance(tz.release(), icu_locale, status)); + CHECK(U_SUCCESS(status)); + CHECK_NOT_NULL(calendar.get()); + + if (calendar->getDynamicClassID() == + icu::GregorianCalendar::getStaticClassID()) { + icu::GregorianCalendar* gc = + static_cast<icu::GregorianCalendar*>(calendar.get()); + UErrorCode status = U_ZERO_ERROR; + // The beginning of ECMAScript time, namely -(2**53) + const double start_of_time = -9007199254740992; + gc->setGregorianChange(start_of_time, status); + DCHECK(U_SUCCESS(status)); + } + return calendar; +} + +std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormat( + Isolate* isolate, const icu::Locale& icu_locale, + const std::string& skeleton) { + // See https://github.com/tc39/ecma402/issues/225 . The best pattern + // generation needs to be done in the base locale according to the + // current spec however odd it may be. See also crbug.com/826549 . + // This is a temporary work-around to get v8's external behavior to match + // the current spec, but does not follow the spec provisions mentioned + // in the above Ecma 402 issue. + // TODO(jshin): The spec may need to be revised because using the base + // locale for the pattern match is not quite right. Moreover, what to + // do with 'related year' part when 'chinese/dangi' calendar is specified + // has to be discussed. Revisit once the spec is clarified/revised. + icu::Locale no_extension_locale(icu_locale.getBaseName()); + UErrorCode status = U_ZERO_ERROR; + std::unique_ptr<icu::DateTimePatternGenerator> generator( + icu::DateTimePatternGenerator::createInstance(no_extension_locale, + status)); + icu::UnicodeString pattern; + if (U_SUCCESS(status)) { + pattern = + generator->getBestPattern(icu::UnicodeString(skeleton.c_str()), status); + } + + // Make formatter from skeleton. Calendar and numbering system are added + // to the locale as Unicode extension (if they were specified at all). + status = U_ZERO_ERROR; + std::unique_ptr<icu::SimpleDateFormat> date_format( + new icu::SimpleDateFormat(pattern, icu_locale, status)); + if (U_FAILURE(status)) return std::unique_ptr<icu::SimpleDateFormat>(); + + CHECK_NOT_NULL(date_format.get()); + return date_format; +} + +} // namespace + +enum FormatMatcherOption { kBestFit, kBasic }; + +// ecma402/#sec-initializedatetimeformat +MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize( + Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, + Handle<Object> requested_locales, Handle<Object> input_options) { + // 2. Let options be ? ToDateTimeOptions(options, "any", "date"). + Handle<JSObject> options; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, options, + JSDateTimeFormat::ToDateTimeOptions( + isolate, input_options, RequiredOption::kAny, DefaultsOption::kDate), + JSDateTimeFormat); + + // ResolveLocale currently get option of localeMatcher so we have to call + // ResolveLocale before "hour12" and "hourCycle". + // TODO(ftang): fix this once ResolveLocale is ported to C++ + // 11. Let r be ResolveLocale( %DateTimeFormat%.[[AvailableLocales]], + // requestedLocales, opt, %DateTimeFormat%.[[RelevantExtensionKeys]], + // localeData). + Handle<JSObject> r; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, r, + Intl::ResolveLocale(isolate, "dateformat", requested_locales, options), + JSDateTimeFormat); + + // 6. Let hour12 be ? GetOption(options, "hour12", "boolean", undefined, + // undefined). + bool hour12; + Maybe<bool> maybe_get_hour12 = Intl::GetBoolOption( + isolate, options, "hour12", "Intl.DateTimeFormat", &hour12); + MAYBE_RETURN(maybe_get_hour12, Handle<JSDateTimeFormat>()); + HourOption hour_option = HourOption::H_UNKNOWN; + if (maybe_get_hour12.FromJust()) { + hour_option = hour12 ? HourOption::H_12 : HourOption::H_24; + } + + // 7. Let hourCycle be ? GetOption(options, "hourCycle", "string", « "h11", + // "h12", "h23", "h24" », undefined). + static std::vector<const char*> hour_cycle_values = {"h11", "h12", "h23", + "h24"}; + std::unique_ptr<char[]> hour_cycle = nullptr; + Maybe<bool> maybe_hour_cycle = + Intl::GetStringOption(isolate, options, "hourCycle", hour_cycle_values, + "Intl.DateTimeFormat", &hour_cycle); + MAYBE_RETURN(maybe_hour_cycle, Handle<JSDateTimeFormat>()); + // 8. If hour12 is not undefined, then + if (maybe_get_hour12.FromJust()) { + // a. Let hourCycle be null. + hour_cycle = nullptr; + } + // 9. Set opt.[[hc]] to hourCycle. + // TODO(ftang): change behavior based on hour_cycle. + + Handle<String> locale_with_extension_str = + isolate->factory()->NewStringFromStaticChars("localeWithExtension"); + Handle<Object> locale_with_extension_obj = + JSObject::GetDataProperty(r, locale_with_extension_str); + + // The locale_with_extension has to be a string. Either a user + // provided canonicalized string or the default locale. + CHECK(locale_with_extension_obj->IsString()); + Handle<String> locale_with_extension = + Handle<String>::cast(locale_with_extension_obj); + + icu::Locale icu_locale = + Intl::CreateICULocale(isolate, locale_with_extension); + DCHECK(!icu_locale.isBogus()); + + // 17. Let timeZone be ? Get(options, "timeZone"). + static std::vector<const char*> empty_values = {}; + std::unique_ptr<char[]> timezone = nullptr; + Maybe<bool> maybe_timezone = + Intl::GetStringOption(isolate, options, "timeZone", empty_values, + "Intl.DateTimeFormat", &timezone); + MAYBE_RETURN(maybe_timezone, Handle<JSDateTimeFormat>()); + + // 22. For each row of Table 5, except the header row, do + std::string skeleton; + for (const auto& item : GetPatternData(hour_option)) { + std::unique_ptr<char[]> input; + // a. Let prop be the name given in the Property column of the row. + // b. Let value be ? GetOption(options, prop, "string", « the strings given + // in the Values column of the row », undefined). + Maybe<bool> maybe_get_option = Intl::GetStringOption( + isolate, options, item.property.c_str(), item.allowed_values, + "Intl.DateTimeFormat", &input); + MAYBE_RETURN(maybe_get_option, Handle<JSDateTimeFormat>()); + if (maybe_get_option.FromJust()) { + DCHECK_NOT_NULL(input.get()); + // c. Set opt.[[<prop>]] to value. + skeleton += item.map.find(input.get())->second; + } + } + + // We implement only best fit algorithm, but still need to check + // if the formatMatcher values are in range. + // 25. Let matcher be ? GetOption(options, "formatMatcher", "string", + // « "basic", "best fit" », "best fit"). + Handle<JSReceiver> options_obj; + ASSIGN_RETURN_ON_EXCEPTION(isolate, options_obj, + Object::ToObject(isolate, options), + JSDateTimeFormat); + std::unique_ptr<char[]> matcher_str = nullptr; + std::vector<const char*> matcher_values = {"basic", "best fit"}; + Maybe<bool> maybe_found_matcher = Intl::GetStringOption( + isolate, options_obj, "formatMatcher", matcher_values, + "Intl.DateTimeFormat", &matcher_str); + MAYBE_RETURN(maybe_found_matcher, Handle<JSDateTimeFormat>()); + + std::unique_ptr<icu::SimpleDateFormat> date_format( + CreateICUDateFormat(isolate, icu_locale, skeleton)); + if (date_format.get() == nullptr) { + // Remove extensions and try again. + icu_locale = icu::Locale(icu_locale.getBaseName()); + date_format = CreateICUDateFormat(isolate, icu_locale, skeleton); + if (date_format.get() == nullptr) { + FATAL("Failed to create ICU date format, are ICU data files missing?"); + } + } + + // Set the locale + // 12. Set dateTimeFormat.[[Locale]] to r.[[locale]]. + icu::Locale* cloned_locale = icu_locale.clone(); + CHECK_NOT_NULL(cloned_locale); + Handle<Managed<icu::Locale>> managed_locale = + Managed<icu::Locale>::FromRawPtr(isolate, 0, cloned_locale); + date_time_format->set_icu_locale(*managed_locale); + + // 13. Set dateTimeFormat.[[Calendar]] to r.[[ca]]. + std::unique_ptr<icu::Calendar> calendar( + CreateCalendar(isolate, icu_locale, timezone.get())); + + // 18.b If the result of IsValidTimeZoneName(timeZone) is false, then + // i. Throw a RangeError exception. + if (calendar.get() == nullptr) { + THROW_NEW_ERROR(isolate, + NewRangeError(MessageTemplate::kInvalidTimeZone, + isolate->factory()->NewStringFromAsciiChecked( + timezone.get())), + JSDateTimeFormat); + } + date_format->adoptCalendar(calendar.release()); + + Handle<Managed<icu::SimpleDateFormat>> managed_format = + Managed<icu::SimpleDateFormat>::FromUniquePtr(isolate, 0, + std::move(date_format)); + date_time_format->set_icu_simple_date_format(*managed_format); + return date_time_format; +} + +namespace { + +// The list comes from third_party/icu/source/i18n/unicode/udat.h. +// They're mapped to DateTimeFormat components listed at +// https://tc39.github.io/ecma402/#sec-datetimeformat-abstracts . +Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) { + switch (field_id) { + case -1: + return isolate->factory()->literal_string(); + case UDAT_YEAR_FIELD: + case UDAT_EXTENDED_YEAR_FIELD: + case UDAT_YEAR_NAME_FIELD: + return isolate->factory()->year_string(); + case UDAT_MONTH_FIELD: + case UDAT_STANDALONE_MONTH_FIELD: + return isolate->factory()->month_string(); + case UDAT_DATE_FIELD: + return isolate->factory()->day_string(); + case UDAT_HOUR_OF_DAY1_FIELD: + case UDAT_HOUR_OF_DAY0_FIELD: + case UDAT_HOUR1_FIELD: + case UDAT_HOUR0_FIELD: + return isolate->factory()->hour_string(); + case UDAT_MINUTE_FIELD: + return isolate->factory()->minute_string(); + case UDAT_SECOND_FIELD: + return isolate->factory()->second_string(); + case UDAT_DAY_OF_WEEK_FIELD: + case UDAT_DOW_LOCAL_FIELD: + case UDAT_STANDALONE_DAY_FIELD: + return isolate->factory()->weekday_string(); + case UDAT_AM_PM_FIELD: + return isolate->factory()->dayPeriod_string(); + case UDAT_TIMEZONE_FIELD: + case UDAT_TIMEZONE_RFC_FIELD: + case UDAT_TIMEZONE_GENERIC_FIELD: + case UDAT_TIMEZONE_SPECIAL_FIELD: + case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: + case UDAT_TIMEZONE_ISO_FIELD: + case UDAT_TIMEZONE_ISO_LOCAL_FIELD: + return isolate->factory()->timeZoneName_string(); + case UDAT_ERA_FIELD: + return isolate->factory()->era_string(); + default: + // Other UDAT_*_FIELD's cannot show up because there is no way to specify + // them via options of Intl.DateTimeFormat. + UNREACHABLE(); + // To prevent MSVC from issuing C4715 warning. + return Handle<String>(); + } +} + +} // namespace + +MaybeHandle<Object> JSDateTimeFormat::FormatToParts( + Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, + double date_value) { + Factory* factory = isolate->factory(); + icu::SimpleDateFormat* format = + date_time_format->icu_simple_date_format()->raw(); + CHECK_NOT_NULL(format); + + icu::UnicodeString formatted; + icu::FieldPositionIterator fp_iter; + icu::FieldPosition fp; + UErrorCode status = U_ZERO_ERROR; + format->format(date_value, formatted, &fp_iter, status); + if (U_FAILURE(status)) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object); + } + + Handle<JSArray> result = factory->NewJSArray(0); + int32_t length = formatted.length(); + if (length == 0) return result; + + int index = 0; + int32_t previous_end_pos = 0; + Handle<String> substring; + while (fp_iter.next(fp)) { + int32_t begin_pos = fp.getBeginIndex(); + int32_t end_pos = fp.getEndIndex(); + + if (previous_end_pos < begin_pos) { + ASSIGN_RETURN_ON_EXCEPTION( + isolate, substring, + Intl::ToString(isolate, formatted, previous_end_pos, begin_pos), + Object); + Intl::AddElement(isolate, result, index, + IcuDateFieldIdToDateType(-1, isolate), substring); + ++index; + } + ASSIGN_RETURN_ON_EXCEPTION( + isolate, substring, + Intl::ToString(isolate, formatted, begin_pos, end_pos), Object); + Intl::AddElement(isolate, result, index, + IcuDateFieldIdToDateType(fp.getField(), isolate), + substring); + previous_end_pos = end_pos; + ++index; + } + if (previous_end_pos < length) { + ASSIGN_RETURN_ON_EXCEPTION( + isolate, substring, + Intl::ToString(isolate, formatted, previous_end_pos, length), Object); + Intl::AddElement(isolate, result, index, + IcuDateFieldIdToDateType(-1, isolate), substring); + } + JSObject::ValidateElements(*result); + return result; +} +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/objects/js-date-time-format.h b/deps/v8/src/objects/js-date-time-format.h new file mode 100644 index 0000000000..ae2aa36a97 --- /dev/null +++ b/deps/v8/src/objects/js-date-time-format.h @@ -0,0 +1,100 @@ +// Copyright 2018 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. + +#ifndef V8_INTL_SUPPORT +#error Internationalization is expected to be enabled. +#endif // V8_INTL_SUPPORT + +#ifndef V8_OBJECTS_JS_DATE_TIME_FORMAT_H_ +#define V8_OBJECTS_JS_DATE_TIME_FORMAT_H_ + +#include "src/isolate.h" +#include "src/objects/managed.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace U_ICU_NAMESPACE { +class Locale; +class SimpleDateFormat; +} + +namespace v8 { +namespace internal { + +class JSDateTimeFormat : public JSObject { + public: + V8_WARN_UNUSED_RESULT static MaybeHandle<JSDateTimeFormat> Initialize( + Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, + Handle<Object> locales, Handle<Object> options); + + V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> ResolvedOptions( + Isolate* isolate, Handle<JSDateTimeFormat> date_time_format); + + // ecma402/#sec-unwrapdatetimeformat + V8_WARN_UNUSED_RESULT static MaybeHandle<JSDateTimeFormat> + UnwrapDateTimeFormat(Isolate* isolate, Handle<JSReceiver> format_holder); + + // Convert the options to ICU DateTimePatternGenerator skeleton. + static Maybe<std::string> OptionsToSkeleton(Isolate* isolate, + Handle<JSReceiver> options); + + // Return the time zone id which match ICU's expectation of title casing + // return empty string when error. + static std::string CanonicalizeTimeZoneID(Isolate* isolate, + const std::string& input); + + // ecma402/#sec-datetime-format-functions + // DateTime Format Functions + V8_WARN_UNUSED_RESULT static MaybeHandle<String> DateTimeFormat( + Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, + Handle<Object> date); + + V8_WARN_UNUSED_RESULT static MaybeHandle<Object> FormatToParts( + Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, + double date_value); + + // ecma-402/#sec-todatetimeoptions + enum class RequiredOption { kDate, kTime, kAny }; + enum class DefaultsOption { kDate, kTime, kAll }; + V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> ToDateTimeOptions( + Isolate* isolate, Handle<Object> input_options, RequiredOption required, + DefaultsOption defaults); + + V8_WARN_UNUSED_RESULT static MaybeHandle<String> ToLocaleDateTime( + Isolate* isolate, Handle<Object> date, Handle<Object> locales, + Handle<Object> options, RequiredOption required, DefaultsOption defaults, + const char* service); + + DECL_CAST(JSDateTimeFormat) + +// Layout description. +#define JS_DATE_TIME_FORMAT_FIELDS(V) \ + V(kICULocaleOffset, kPointerSize) \ + V(kICUSimpleDateFormatOffset, kPointerSize) \ + V(kBoundFormatOffset, kPointerSize) \ + /* Total size. */ \ + V(kSize, 0) + + DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, + JS_DATE_TIME_FORMAT_FIELDS) +#undef JS_DATE_TIME_FORMAT_FIELDS + + DECL_ACCESSORS(icu_locale, Managed<icu::Locale>) + DECL_ACCESSORS(icu_simple_date_format, Managed<icu::SimpleDateFormat>) + DECL_ACCESSORS(bound_format, Object) + + DECL_PRINTER(JSDateTimeFormat) + DECL_VERIFIER(JSDateTimeFormat) + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSDateTimeFormat); +}; + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_JS_DATE_TIME_FORMAT_H_ diff --git a/deps/v8/src/objects/js-generator.h b/deps/v8/src/objects/js-generator.h index 4d63d524ea..043b457cf0 100644 --- a/deps/v8/src/objects/js-generator.h +++ b/deps/v8/src/objects/js-generator.h @@ -5,7 +5,7 @@ #ifndef V8_OBJECTS_JS_GENERATOR_H_ #define V8_OBJECTS_JS_GENERATOR_H_ -#include "src/objects.h" +#include "src/objects/js-objects.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" diff --git a/deps/v8/src/objects/js-list-format-inl.h b/deps/v8/src/objects/js-list-format-inl.h index 554b3488b6..0f1395719e 100644 --- a/deps/v8/src/objects/js-list-format-inl.h +++ b/deps/v8/src/objects/js-list-format-inl.h @@ -20,7 +20,8 @@ namespace internal { // Base list format accessors. ACCESSORS(JSListFormat, locale, String, kLocaleOffset) -ACCESSORS(JSListFormat, formatter, Foreign, kFormatterOffset) +ACCESSORS(JSListFormat, icu_formatter, Managed<icu::ListFormatter>, + kICUFormatterOffset) SMI_ACCESSORS(JSListFormat, flags, kFlagsOffset) inline void JSListFormat::set_style(Style style) { diff --git a/deps/v8/src/objects/js-list-format.cc b/deps/v8/src/objects/js-list-format.cc index 66dbe0bfd9..d2713d489f 100644 --- a/deps/v8/src/objects/js-list-format.cc +++ b/deps/v8/src/objects/js-list-format.cc @@ -119,7 +119,7 @@ JSListFormat::Type get_type(const char* str) { UNREACHABLE(); } -MaybeHandle<JSListFormat> JSListFormat::InitializeListFormat( +MaybeHandle<JSListFormat> JSListFormat::Initialize( Isolate* isolate, Handle<JSListFormat> list_format_holder, Handle<Object> input_locales, Handle<Object> input_options) { Factory* factory = isolate->factory(); @@ -199,7 +199,7 @@ MaybeHandle<JSListFormat> JSListFormat::InitializeListFormat( Handle<Managed<icu::ListFormatter>> managed_formatter = Managed<icu::ListFormatter>::FromRawPtr(isolate, 0, formatter); - list_format_holder->set_formatter(*managed_formatter); + list_format_holder->set_icu_formatter(*managed_formatter); return list_format_holder; } @@ -217,11 +217,6 @@ Handle<JSObject> JSListFormat::ResolvedOptions( return result; } -icu::ListFormatter* JSListFormat::UnpackFormatter(Isolate* isolate, - Handle<JSListFormat> holder) { - return Managed<icu::ListFormatter>::cast(holder->formatter())->raw(); -} - Handle<String> JSListFormat::StyleAsString() const { switch (style()) { case Style::LONG: @@ -352,8 +347,7 @@ Maybe<bool> FormatListCommon(Isolate* isolate, std::unique_ptr<icu::UnicodeString[]>& array) { DCHECK(!list->IsUndefined()); - icu::ListFormatter* formatter = - JSListFormat::UnpackFormatter(isolate, format_holder); + icu::ListFormatter* formatter = format_holder->icu_formatter()->raw(); CHECK_NOT_NULL(formatter); *length = list->GetElementsAccessor()->NumberOfElements(*list); diff --git a/deps/v8/src/objects/js-list-format.h b/deps/v8/src/objects/js-list-format.h index 22f8d20005..e9bfec7cc8 100644 --- a/deps/v8/src/objects/js-list-format.h +++ b/deps/v8/src/objects/js-list-format.h @@ -12,6 +12,7 @@ #include "src/heap/factory.h" #include "src/isolate.h" #include "src/objects.h" +#include "src/objects/managed.h" #include "unicode/uversion.h" // Has to be the last include (doesn't have include guards): @@ -28,17 +29,13 @@ class JSListFormat : public JSObject { public: // Initializes relative time format object with properties derived from input // locales and options. - static MaybeHandle<JSListFormat> InitializeListFormat( + static MaybeHandle<JSListFormat> Initialize( Isolate* isolate, Handle<JSListFormat> list_format_holder, Handle<Object> locales, Handle<Object> options); static Handle<JSObject> ResolvedOptions(Isolate* isolate, Handle<JSListFormat> format_holder); - // Unpacks formatter object from corresponding JavaScript object. - static icu::ListFormatter* UnpackFormatter( - Isolate* isolate, Handle<JSListFormat> list_format_holder); - // ecma402 #sec-formatlist V8_WARN_UNUSED_RESULT static MaybeHandle<String> FormatList( Isolate* isolate, Handle<JSListFormat> format_holder, @@ -56,7 +53,7 @@ class JSListFormat : public JSObject { // ListFormat accessors. DECL_ACCESSORS(locale, String) - DECL_ACCESSORS(formatter, Foreign) + DECL_ACCESSORS(icu_formatter, Managed<icu::ListFormatter>) // Style: identifying the relative time format style used. // @@ -105,8 +102,8 @@ class JSListFormat : public JSObject { // Layout description. static const int kJSListFormatOffset = JSObject::kHeaderSize; static const int kLocaleOffset = kJSListFormatOffset + kPointerSize; - static const int kFormatterOffset = kLocaleOffset + kPointerSize; - static const int kFlagsOffset = kFormatterOffset + kPointerSize; + static const int kICUFormatterOffset = kLocaleOffset + kPointerSize; + static const int kFlagsOffset = kICUFormatterOffset + kPointerSize; static const int kSize = kFlagsOffset + kPointerSize; private: diff --git a/deps/v8/src/objects/js-locale-inl.h b/deps/v8/src/objects/js-locale-inl.h index a70bef998e..ac0a7a914f 100644 --- a/deps/v8/src/objects/js-locale-inl.h +++ b/deps/v8/src/objects/js-locale-inl.h @@ -28,14 +28,45 @@ ACCESSORS(JSLocale, locale, String, kLocaleOffset); // Unicode extension accessors. ACCESSORS(JSLocale, calendar, Object, kCalendarOffset); -ACCESSORS(JSLocale, case_first, Object, kCaseFirstOffset); ACCESSORS(JSLocale, collation, Object, kCollationOffset); -ACCESSORS(JSLocale, hour_cycle, Object, kHourCycleOffset); -ACCESSORS(JSLocale, numeric, Object, kNumericOffset); ACCESSORS(JSLocale, numbering_system, Object, kNumberingSystemOffset); +SMI_ACCESSORS(JSLocale, flags, kFlagsOffset) CAST_ACCESSOR(JSLocale); +inline void JSLocale::set_case_first(CaseFirst case_first) { + DCHECK_GT(CaseFirst::COUNT, case_first); + int hints = flags(); + hints = CaseFirstBits::update(hints, case_first); + set_flags(hints); +} + +inline JSLocale::CaseFirst JSLocale::case_first() const { + return CaseFirstBits::decode(flags()); +} + +inline void JSLocale::set_hour_cycle(HourCycle hour_cycle) { + DCHECK_GT(HourCycle::COUNT, hour_cycle); + int hints = flags(); + hints = HourCycleBits::update(hints, hour_cycle); + set_flags(hints); +} + +inline JSLocale::HourCycle JSLocale::hour_cycle() const { + return HourCycleBits::decode(flags()); +} + +inline void JSLocale::set_numeric(Numeric numeric) { + DCHECK_GT(Numeric::COUNT, numeric); + int hints = flags(); + hints = NumericBits::update(hints, numeric); + set_flags(hints); +} + +inline JSLocale::Numeric JSLocale::numeric() const { + return NumericBits::decode(flags()); +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/objects/js-locale.cc b/deps/v8/src/objects/js-locale.cc index 8968aa58c9..78fb30fa41 100644 --- a/deps/v8/src/objects/js-locale.cc +++ b/deps/v8/src/objects/js-locale.cc @@ -35,6 +35,26 @@ namespace internal { namespace { +JSLocale::CaseFirst GetCaseFirst(const char* str) { + if (strcmp(str, "upper") == 0) return JSLocale::CaseFirst::UPPER; + if (strcmp(str, "lower") == 0) return JSLocale::CaseFirst::LOWER; + if (strcmp(str, "false") == 0) return JSLocale::CaseFirst::FALSE_VALUE; + UNREACHABLE(); +} + +JSLocale::HourCycle GetHourCycle(const char* str) { + if (strcmp(str, "h11") == 0) return JSLocale::HourCycle::H11; + if (strcmp(str, "h12") == 0) return JSLocale::HourCycle::H12; + if (strcmp(str, "h23") == 0) return JSLocale::HourCycle::H23; + if (strcmp(str, "h24") == 0) return JSLocale::HourCycle::H24; + UNREACHABLE(); +} + +JSLocale::Numeric GetNumeric(const char* str) { + return strcmp(str, "true") == 0 ? JSLocale::Numeric::TRUE_VALUE + : JSLocale::Numeric::FALSE_VALUE; +} + struct OptionData { const char* name; const char* key; @@ -49,12 +69,12 @@ Maybe<bool> InsertOptionsIntoLocale(Isolate* isolate, CHECK(isolate); CHECK(icu_locale); - static std::vector<const char*> hour_cycle_values = {"h11", "h12", "h23", - "h24"}; - static std::vector<const char*> case_first_values = {"upper", "lower", - "false"}; - static std::vector<const char*> empty_values = {}; - static const std::array<OptionData, 6> kOptionToUnicodeTagMap = { + const std::vector<const char*> hour_cycle_values = {"h11", "h12", "h23", + "h24"}; + const std::vector<const char*> case_first_values = {"upper", "lower", + "false"}; + const std::vector<const char*> empty_values = {}; + const std::array<OptionData, 6> kOptionToUnicodeTagMap = { {{"calendar", "ca", &empty_values, false}, {"collation", "co", &empty_values, false}, {"hourCycle", "hc", &hour_cycle_values, false}, @@ -75,7 +95,7 @@ Maybe<bool> InsertOptionsIntoLocale(Isolate* isolate, : Intl::GetStringOption(isolate, options, option_to_bcp47.name, *(option_to_bcp47.possible_values), "locale", &value_str); - if (maybe_found.IsNothing()) return maybe_found; + MAYBE_RETURN(maybe_found, Nothing<bool>()); // TODO(cira): Use fallback value if value is not found to make // this spec compliant. @@ -138,19 +158,23 @@ bool PopulateLocaleWithUnicodeTags(Isolate* isolate, const char* icu_locale, if (bcp47_key) { const char* bcp47_value = uloc_toUnicodeLocaleType(bcp47_key, value); if (bcp47_value) { - Handle<String> bcp47_handle = - factory->NewStringFromAsciiChecked(bcp47_value); if (strcmp(bcp47_key, "kn") == 0) { - locale_holder->set_numeric(*bcp47_handle); + locale_holder->set_numeric(GetNumeric(bcp47_value)); } else if (strcmp(bcp47_key, "ca") == 0) { + Handle<String> bcp47_handle = + factory->NewStringFromAsciiChecked(bcp47_value); locale_holder->set_calendar(*bcp47_handle); } else if (strcmp(bcp47_key, "kf") == 0) { - locale_holder->set_case_first(*bcp47_handle); + locale_holder->set_case_first(GetCaseFirst(bcp47_value)); } else if (strcmp(bcp47_key, "co") == 0) { + Handle<String> bcp47_handle = + factory->NewStringFromAsciiChecked(bcp47_value); locale_holder->set_collation(*bcp47_handle); } else if (strcmp(bcp47_key, "hc") == 0) { - locale_holder->set_hour_cycle(*bcp47_handle); + locale_holder->set_hour_cycle(GetHourCycle(bcp47_value)); } else if (strcmp(bcp47_key, "nu") == 0) { + Handle<String> bcp47_handle = + factory->NewStringFromAsciiChecked(bcp47_value); locale_holder->set_numbering_system(*bcp47_handle); } } @@ -163,17 +187,17 @@ bool PopulateLocaleWithUnicodeTags(Isolate* isolate, const char* icu_locale, } } // namespace -MaybeHandle<JSLocale> JSLocale::InitializeLocale(Isolate* isolate, - Handle<JSLocale> locale_holder, - Handle<String> locale, - Handle<JSReceiver> options) { +MaybeHandle<JSLocale> JSLocale::Initialize(Isolate* isolate, + Handle<JSLocale> locale_holder, + Handle<String> locale, + Handle<JSReceiver> options) { + locale_holder->set_flags(0); static const char* const kMethod = "Intl.Locale"; v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate); UErrorCode status = U_ZERO_ERROR; // Get ICU locale format, and canonicalize it. char icu_result[ULOC_FULLNAME_CAPACITY]; - char icu_canonical[ULOC_FULLNAME_CAPACITY]; if (locale->length() == 0) { THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kLocaleNotEmpty), @@ -184,18 +208,20 @@ MaybeHandle<JSLocale> JSLocale::InitializeLocale(Isolate* isolate, CHECK_LT(0, bcp47_locale.length()); CHECK_NOT_NULL(*bcp47_locale); - int icu_length = uloc_forLanguageTag( - *bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, nullptr, &status); + int parsed_length = 0; + int icu_length = + uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, + &parsed_length, &status); - if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING || - icu_length == 0) { + if (U_FAILURE(status) || + parsed_length < static_cast<int>(bcp47_locale.length()) || + status == U_STRING_NOT_TERMINATED_WARNING || icu_length == 0) { THROW_NEW_ERROR( isolate, NewRangeError(MessageTemplate::kLocaleBadParameters, isolate->factory()->NewStringFromAsciiChecked(kMethod), locale_holder), JSLocale); - return MaybeHandle<JSLocale>(); } Maybe<bool> error = InsertOptionsIntoLocale(isolate, options, icu_result); @@ -207,40 +233,26 @@ MaybeHandle<JSLocale> JSLocale::InitializeLocale(Isolate* isolate, isolate->factory()->NewStringFromAsciiChecked(kMethod), locale_holder), JSLocale); - return MaybeHandle<JSLocale>(); - } - DCHECK(error.FromJust()); - - uloc_canonicalize(icu_result, icu_canonical, ULOC_FULLNAME_CAPACITY, &status); - if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { - THROW_NEW_ERROR( - isolate, - NewRangeError(MessageTemplate::kLocaleBadParameters, - isolate->factory()->NewStringFromAsciiChecked(kMethod), - locale_holder), - JSLocale); - return MaybeHandle<JSLocale>(); } - if (!PopulateLocaleWithUnicodeTags(isolate, icu_canonical, locale_holder)) { + if (!PopulateLocaleWithUnicodeTags(isolate, icu_result, locale_holder)) { THROW_NEW_ERROR( isolate, NewRangeError(MessageTemplate::kLocaleBadParameters, isolate->factory()->NewStringFromAsciiChecked(kMethod), locale_holder), JSLocale); - return MaybeHandle<JSLocale>(); } // Extract language, script and region parts. char icu_language[ULOC_LANG_CAPACITY]; - uloc_getLanguage(icu_canonical, icu_language, ULOC_LANG_CAPACITY, &status); + uloc_getLanguage(icu_result, icu_language, ULOC_LANG_CAPACITY, &status); char icu_script[ULOC_SCRIPT_CAPACITY]; - uloc_getScript(icu_canonical, icu_script, ULOC_SCRIPT_CAPACITY, &status); + uloc_getScript(icu_result, icu_script, ULOC_SCRIPT_CAPACITY, &status); char icu_region[ULOC_COUNTRY_CAPACITY]; - uloc_getCountry(icu_canonical, icu_region, ULOC_COUNTRY_CAPACITY, &status); + uloc_getCountry(icu_result, icu_region, ULOC_COUNTRY_CAPACITY, &status); if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { THROW_NEW_ERROR( @@ -249,7 +261,6 @@ MaybeHandle<JSLocale> JSLocale::InitializeLocale(Isolate* isolate, isolate->factory()->NewStringFromAsciiChecked(kMethod), locale_holder), JSLocale); - return MaybeHandle<JSLocale>(); } Factory* factory = isolate->factory(); @@ -271,8 +282,7 @@ MaybeHandle<JSLocale> JSLocale::InitializeLocale(Isolate* isolate, } char icu_base_name[ULOC_FULLNAME_CAPACITY]; - uloc_getBaseName(icu_canonical, icu_base_name, ULOC_FULLNAME_CAPACITY, - &status); + uloc_getBaseName(icu_result, icu_base_name, ULOC_FULLNAME_CAPACITY, &status); // We need to convert it back to BCP47. char bcp47_result[ULOC_FULLNAME_CAPACITY]; uloc_toLanguageTag(icu_base_name, bcp47_result, ULOC_FULLNAME_CAPACITY, true, @@ -284,13 +294,12 @@ MaybeHandle<JSLocale> JSLocale::InitializeLocale(Isolate* isolate, isolate->factory()->NewStringFromAsciiChecked(kMethod), locale_holder), JSLocale); - return MaybeHandle<JSLocale>(); } Handle<String> base_name = factory->NewStringFromAsciiChecked(bcp47_result); locale_holder->set_base_name(*base_name); // Produce final representation of the locale string, for toString(). - uloc_toLanguageTag(icu_canonical, bcp47_result, ULOC_FULLNAME_CAPACITY, true, + uloc_toLanguageTag(icu_result, bcp47_result, ULOC_FULLNAME_CAPACITY, true, &status); if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { THROW_NEW_ERROR( @@ -299,7 +308,6 @@ MaybeHandle<JSLocale> JSLocale::InitializeLocale(Isolate* isolate, isolate->factory()->NewStringFromAsciiChecked(kMethod), locale_holder), JSLocale); - return MaybeHandle<JSLocale>(); } Handle<String> locale_handle = factory->NewStringFromAsciiChecked(bcp47_result); @@ -310,20 +318,37 @@ MaybeHandle<JSLocale> JSLocale::InitializeLocale(Isolate* isolate, namespace { -Handle<String> MorphLocale(Isolate* isolate, String* input, +Handle<String> MorphLocale(Isolate* isolate, String* language_tag, int32_t (*morph_func)(const char*, char*, int32_t, UErrorCode*)) { Factory* factory = isolate->factory(); char localeBuffer[ULOC_FULLNAME_CAPACITY]; + char morphBuffer[ULOC_FULLNAME_CAPACITY]; + UErrorCode status = U_ZERO_ERROR; + // Convert from language id to locale. + int32_t parsed_length; + int32_t length = + uloc_forLanguageTag(language_tag->ToCString().get(), localeBuffer, + ULOC_FULLNAME_CAPACITY, &parsed_length, &status); + CHECK(parsed_length == language_tag->length()); + DCHECK(U_SUCCESS(status)); + DCHECK_GT(length, 0); DCHECK_NOT_NULL(morph_func); - int32_t length = (*morph_func)(input->ToCString().get(), localeBuffer, - ULOC_FULLNAME_CAPACITY, &status); + // Add the likely subtags or Minimize the subtags on the locale id + length = + (*morph_func)(localeBuffer, morphBuffer, ULOC_FULLNAME_CAPACITY, &status); + DCHECK(U_SUCCESS(status)); + DCHECK_GT(length, 0); + // Returns a well-formed language tag + length = uloc_toLanguageTag(morphBuffer, localeBuffer, ULOC_FULLNAME_CAPACITY, + false, &status); DCHECK(U_SUCCESS(status)); DCHECK_GT(length, 0); - std::string locale(localeBuffer, length); - std::replace(locale.begin(), locale.end(), '_', '-'); - return factory->NewStringFromAsciiChecked(locale.c_str()); + std::string lang(localeBuffer, length); + std::replace(lang.begin(), lang.end(), '_', '-'); + + return factory->NewStringFromAsciiChecked(lang.c_str()); } } // namespace @@ -336,5 +361,46 @@ Handle<String> JSLocale::Minimize(Isolate* isolate, String* locale) { return MorphLocale(isolate, locale, uloc_minimizeSubtags); } +Handle<String> JSLocale::CaseFirstAsString() const { + switch (case_first()) { + case CaseFirst::UPPER: + return GetReadOnlyRoots().upper_string_handle(); + case CaseFirst::LOWER: + return GetReadOnlyRoots().lower_string_handle(); + case CaseFirst::FALSE_VALUE: + return GetReadOnlyRoots().false_string_handle(); + case CaseFirst::COUNT: + UNREACHABLE(); + } +} + +Handle<String> JSLocale::HourCycleAsString() const { + switch (hour_cycle()) { + case HourCycle::H11: + return GetReadOnlyRoots().h11_string_handle(); + case HourCycle::H12: + return GetReadOnlyRoots().h12_string_handle(); + case HourCycle::H23: + return GetReadOnlyRoots().h23_string_handle(); + case HourCycle::H24: + return GetReadOnlyRoots().h24_string_handle(); + case HourCycle::COUNT: + UNREACHABLE(); + } +} + +Handle<String> JSLocale::NumericAsString() const { + switch (numeric()) { + case Numeric::NOTSET: + return GetReadOnlyRoots().undefined_string_handle(); + case Numeric::TRUE_VALUE: + return GetReadOnlyRoots().true_string_handle(); + case Numeric::FALSE_VALUE: + return GetReadOnlyRoots().false_string_handle(); + case Numeric::COUNT: + UNREACHABLE(); + } +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/objects/js-locale.h b/deps/v8/src/objects/js-locale.h index d111885d52..f42a4cdaee 100644 --- a/deps/v8/src/objects/js-locale.h +++ b/deps/v8/src/objects/js-locale.h @@ -25,13 +25,17 @@ class JSLocale : public JSObject { public: // Initializes locale object with properties derived from input locale string // and options. - static MaybeHandle<JSLocale> InitializeLocale(Isolate* isolate, - Handle<JSLocale> locale_holder, - Handle<String> locale, - Handle<JSReceiver> options); + static MaybeHandle<JSLocale> Initialize(Isolate* isolate, + Handle<JSLocale> locale_holder, + Handle<String> locale, + Handle<JSReceiver> options); static Handle<String> Maximize(Isolate* isolate, String* locale); static Handle<String> Minimize(Isolate* isolate, String* locale); + Handle<String> CaseFirstAsString() const; + Handle<String> NumericAsString() const; + Handle<String> HourCycleAsString() const; + DECL_CAST(JSLocale) // Locale accessors. @@ -43,12 +47,64 @@ class JSLocale : public JSObject { // Unicode extension accessors. DECL_ACCESSORS(calendar, Object) - DECL_ACCESSORS(case_first, Object) DECL_ACCESSORS(collation, Object) - DECL_ACCESSORS(hour_cycle, Object) - DECL_ACCESSORS(numeric, Object) DECL_ACCESSORS(numbering_system, Object) + // CaseFirst: "kf" + // + // ecma402 #sec-Intl.Locale.prototype.caseFirst + enum class CaseFirst { + UPPER, // upper case sorts before lower case + LOWER, // lower case sorts before upper case + // (compiler does not like FALSE so we have to name it FALSE_VALUE) + FALSE_VALUE, // Turn the feature off + COUNT + }; + inline void set_case_first(CaseFirst case_first); + inline CaseFirst case_first() const; + + // Numeric: 'kn" + // + // ecma402 #sec-Intl.Locale.prototype.numeric + enum class Numeric { NOTSET, TRUE_VALUE, FALSE_VALUE, COUNT }; + inline void set_numeric(Numeric numeric); + inline Numeric numeric() const; + + // CaseFirst: "hc" + // + // ecma402 #sec-Intl.Locale.prototype.hourCycle + enum class HourCycle { + H11, // 12-hour format start with hour 0 and go up to 11. + H12, // 12-hour format start with hour 1 and go up to 12. + H23, // 24-hour format start with hour 0 and go up to 23. + H24, // 24-hour format start with hour 1 and go up to 24. + COUNT + }; + inline void set_hour_cycle(HourCycle hour_cycle); + inline HourCycle hour_cycle() const; + +// Bit positions in |flags|. +#define FLAGS_BIT_FIELDS(V, _) \ + V(CaseFirstBits, CaseFirst, 2, _) \ + V(NumericBits, Numeric, 2, _) \ + V(HourCycleBits, HourCycle, 2, _) + DEFINE_BIT_FIELDS(FLAGS_BIT_FIELDS) +#undef FLAGS_BIT_FIELDS + + STATIC_ASSERT(CaseFirst::UPPER <= CaseFirstBits::kMax); + STATIC_ASSERT(CaseFirst::LOWER <= CaseFirstBits::kMax); + STATIC_ASSERT(CaseFirst::FALSE_VALUE <= CaseFirstBits::kMax); + STATIC_ASSERT(Numeric::NOTSET <= NumericBits::kMax); + STATIC_ASSERT(Numeric::FALSE_VALUE <= NumericBits::kMax); + STATIC_ASSERT(Numeric::TRUE_VALUE <= NumericBits::kMax); + STATIC_ASSERT(HourCycle::H11 <= HourCycleBits::kMax); + STATIC_ASSERT(HourCycle::H12 <= HourCycleBits::kMax); + STATIC_ASSERT(HourCycle::H23 <= HourCycleBits::kMax); + STATIC_ASSERT(HourCycle::H24 <= HourCycleBits::kMax); + + // [flags] Bit field containing various flags about the function. + DECL_INT_ACCESSORS(flags) + DECL_PRINTER(JSLocale) DECL_VERIFIER(JSLocale) @@ -61,12 +117,10 @@ class JSLocale : public JSObject { static const int kBaseNameOffset = kRegionOffset + kPointerSize; static const int kLocaleOffset = kBaseNameOffset + kPointerSize; // Unicode extension fields. - static const int kCalendarOffset = kLocaleOffset + kPointerSize; - static const int kCaseFirstOffset = kCalendarOffset + kPointerSize; - static const int kCollationOffset = kCaseFirstOffset + kPointerSize; - static const int kHourCycleOffset = kCollationOffset + kPointerSize; - static const int kNumericOffset = kHourCycleOffset + kPointerSize; - static const int kNumberingSystemOffset = kNumericOffset + kPointerSize; + static const int kFlagsOffset = kLocaleOffset + kPointerSize; + static const int kCalendarOffset = kFlagsOffset + kPointerSize; + static const int kCollationOffset = kCalendarOffset + kPointerSize; + static const int kNumberingSystemOffset = kCollationOffset + kPointerSize; // Final size. static const int kSize = kNumberingSystemOffset + kPointerSize; diff --git a/deps/v8/src/objects/js-number-format-inl.h b/deps/v8/src/objects/js-number-format-inl.h new file mode 100644 index 0000000000..880ef9344f --- /dev/null +++ b/deps/v8/src/objects/js-number-format-inl.h @@ -0,0 +1,58 @@ +// Copyright 2018 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. + +#ifndef V8_INTL_SUPPORT +#error Internationalization is expected to be enabled. +#endif // V8_INTL_SUPPORT + +#ifndef V8_OBJECTS_JS_NUMBER_FORMAT_INL_H_ +#define V8_OBJECTS_JS_NUMBER_FORMAT_INL_H_ + +#include "src/objects-inl.h" +#include "src/objects/js-number-format.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +ACCESSORS(JSNumberFormat, locale, String, kLocaleOffset) +ACCESSORS(JSNumberFormat, icu_number_format, Managed<icu::NumberFormat>, + kICUNumberFormatOffset) +ACCESSORS(JSNumberFormat, bound_format, Object, kBoundFormatOffset) +SMI_ACCESSORS(JSNumberFormat, flags, kFlagsOffset) + +inline void JSNumberFormat::set_style(Style style) { + DCHECK_LT(style, Style::COUNT); + int hints = flags(); + hints = StyleBits::update(hints, style); + set_flags(hints); +} + +inline JSNumberFormat::Style JSNumberFormat::style() const { + return StyleBits::decode(flags()); +} + +inline void JSNumberFormat::set_currency_display( + CurrencyDisplay currency_display) { + DCHECK_LT(currency_display, CurrencyDisplay::COUNT); + int hints = flags(); + hints = CurrencyDisplayBits::update(hints, currency_display); + set_flags(hints); +} + +inline JSNumberFormat::CurrencyDisplay JSNumberFormat::currency_display() + const { + return CurrencyDisplayBits::decode(flags()); +} + +CAST_ACCESSOR(JSNumberFormat); + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_JS_NUMBER_FORMAT_INL_H_ diff --git a/deps/v8/src/objects/js-number-format.cc b/deps/v8/src/objects/js-number-format.cc new file mode 100644 index 0000000000..9fe7c30a9d --- /dev/null +++ b/deps/v8/src/objects/js-number-format.cc @@ -0,0 +1,709 @@ +// Copyright 2018 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. + +#ifndef V8_INTL_SUPPORT +#error Internationalization is expected to be enabled. +#endif // V8_INTL_SUPPORT + +#include "src/objects/js-number-format.h" + +#include <set> +#include <string> + +#include "src/isolate.h" +#include "src/objects-inl.h" +#include "src/objects/intl-objects.h" +#include "src/objects/js-number-format-inl.h" +#include "unicode/decimfmt.h" +#include "unicode/locid.h" +#include "unicode/numfmt.h" +#include "unicode/strenum.h" +#include "unicode/ucurr.h" +#include "unicode/uloc.h" + +namespace v8 { +namespace internal { + +namespace { + +// ecma-402/#sec-currencydigits +// The currency is expected to an all upper case string value. +int CurrencyDigits(const icu::UnicodeString& currency) { + UErrorCode status = U_ZERO_ERROR; + uint32_t fraction_digits = ucurr_getDefaultFractionDigits( + reinterpret_cast<const UChar*>(currency.getBuffer()), &status); + // For missing currency codes, default to the most common, 2 + return U_SUCCESS(status) ? fraction_digits : 2; +} + +bool IsAToZ(char ch) { return IsInRange(AsciiAlphaToLower(ch), 'a', 'z'); } + +// ecma402/#sec-iswellformedcurrencycode +bool IsWellFormedCurrencyCode(const std::string& currency) { + // Verifies that the input is a well-formed ISO 4217 currency code. + // ecma402/#sec-currency-codes + // 2. If the number of elements in normalized is not 3, return false. + if (currency.length() != 3) return false; + // 1. Let normalized be the result of mapping currency to upper case as + // described in 6.1. + // + // 3. If normalized contains any character that is not in + // the range "A" to "Z" (U+0041 to U+005A), return false. + // + // 4. Return true. + // Don't uppercase to test. It could convert invalid code into a valid one. + // For example \u00DFP (Eszett+P) becomes SSP. + return (IsAToZ(currency[0]) && IsAToZ(currency[1]) && IsAToZ(currency[2])); +} + +} // anonymous namespace + +// static +Handle<JSObject> JSNumberFormat::ResolvedOptions( + Isolate* isolate, Handle<JSNumberFormat> number_format_holder) { + Factory* factory = isolate->factory(); + Handle<JSObject> options = factory->NewJSObject(isolate->object_function()); + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->style_string(), + number_format_holder->StyleAsString(), kDontThrow) + .FromJust()); + + icu::NumberFormat* number_format = + number_format_holder->icu_number_format()->raw(); + CHECK_NOT_NULL(number_format); + icu::DecimalFormat* decimal_format = + static_cast<icu::DecimalFormat*>(number_format); + CHECK_NOT_NULL(decimal_format); + + Handle<String> locale = + Handle<String>(number_format_holder->locale(), isolate); + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->locale_string(), locale, kDontThrow) + .FromJust()); + UErrorCode error = U_ZERO_ERROR; + icu::Locale icu_locale = number_format->getLocale(ULOC_VALID_LOCALE, error); + DCHECK(U_SUCCESS(error)); + + std::string numbering_system = Intl::GetNumberingSystem(icu_locale); + if (!numbering_system.empty()) { + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->numberingSystem_string(), + factory->NewStringFromAsciiChecked(numbering_system.c_str()), + kDontThrow) + .FromJust()); + } + + if (number_format_holder->style() == Style::CURRENCY) { + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->currencyDisplay_string(), + number_format_holder->CurrencyDisplayAsString(), kDontThrow) + .FromJust()); + icu::UnicodeString currency(number_format->getCurrency()); + DCHECK(!currency.isEmpty()); + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->currency_string(), + factory + ->NewStringFromTwoByte(Vector<const uint16_t>( + reinterpret_cast<const uint16_t*>(currency.getBuffer()), + currency.length())) + .ToHandleChecked(), + kDontThrow) + .FromJust()); + } + + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->minimumIntegerDigits_string(), + factory->NewNumberFromInt(number_format->getMinimumIntegerDigits()), + kDontThrow) + .FromJust()); + CHECK( + JSReceiver::CreateDataProperty( + isolate, options, factory->minimumFractionDigits_string(), + factory->NewNumberFromInt(number_format->getMinimumFractionDigits()), + kDontThrow) + .FromJust()); + CHECK( + JSReceiver::CreateDataProperty( + isolate, options, factory->maximumFractionDigits_string(), + factory->NewNumberFromInt(number_format->getMaximumFractionDigits()), + kDontThrow) + .FromJust()); + if (decimal_format->areSignificantDigitsUsed()) { + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->minimumSignificantDigits_string(), + factory->NewNumberFromInt( + decimal_format->getMinimumSignificantDigits()), + kDontThrow) + .FromJust()); + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->maximumSignificantDigits_string(), + factory->NewNumberFromInt( + decimal_format->getMaximumSignificantDigits()), + kDontThrow) + .FromJust()); + } + CHECK(JSReceiver::CreateDataProperty( + isolate, options, factory->useGrouping_string(), + factory->ToBoolean((number_format->isGroupingUsed() == TRUE)), + kDontThrow) + .FromJust()); + + return options; +} + +// ecma402/#sec-unwrapnumberformat +MaybeHandle<JSNumberFormat> JSNumberFormat::UnwrapNumberFormat( + Isolate* isolate, Handle<JSReceiver> format_holder) { + // old code copy from NumberFormat::Unwrap that has no spec comment and + // compiled but fail unit tests. + Handle<Context> native_context = + Handle<Context>(isolate->context()->native_context(), isolate); + Handle<JSFunction> constructor = Handle<JSFunction>( + JSFunction::cast(native_context->intl_number_format_function()), isolate); + Handle<Object> object; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, object, + Intl::LegacyUnwrapReceiver(isolate, format_holder, constructor, + format_holder->IsJSNumberFormat()), + JSNumberFormat); + // 4. If ... or nf does not have an [[InitializedNumberFormat]] internal slot, + // then + if (!object->IsJSNumberFormat()) { + // a. Throw a TypeError exception. + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, + isolate->factory()->NewStringFromAsciiChecked( + "UnwrapNumberFormat")), + JSNumberFormat); + } + // 5. Return nf. + return Handle<JSNumberFormat>::cast(object); +} + +// static +MaybeHandle<JSNumberFormat> JSNumberFormat::Initialize( + Isolate* isolate, Handle<JSNumberFormat> number_format, + Handle<Object> locales, Handle<Object> options_obj) { + // set the flags to 0 ASAP. + number_format->set_flags(0); + Factory* factory = isolate->factory(); + // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales). + Handle<JSObject> requested_locales; + ASSIGN_RETURN_ON_EXCEPTION(isolate, requested_locales, + Intl::CanonicalizeLocaleListJS(isolate, locales), + JSNumberFormat); + + // 2. If options is undefined, then + if (options_obj->IsUndefined(isolate)) { + // 2. a. Let options be ObjectCreate(null). + options_obj = isolate->factory()->NewJSObjectWithNullProto(); + } else { + // 3. Else + // 3. a. Let options be ? ToObject(options). + ASSIGN_RETURN_ON_EXCEPTION( + isolate, options_obj, + Object::ToObject(isolate, options_obj, "Intl.NumberFormat"), + JSNumberFormat); + } + + // At this point, options_obj can either be a JSObject or a JSProxy only. + Handle<JSReceiver> options = Handle<JSReceiver>::cast(options_obj); + + // 4. Let opt be a new Record. + // + // 5. Let matcher be ? GetOption(options, "localeMatcher", "string", « + // "lookup", "best fit" », "best fit"). + // + // 6. Set opt.[[localeMatcher]] to matcher. + // + // 7. Let localeData be %NumberFormat%.[[LocaleData]]. + // + // 8. Let r be ResolveLocale(%NumberFormat%.[[AvailableLocales]], + // requestedLocales, opt, %NumberFormat%.[[RelevantExtensionKeys]], + // localeData). + // + // 9. Set numberFormat.[[Locale]] to r.[[locale]]. + + Handle<JSObject> r; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, r, + Intl::ResolveLocale(isolate, "numberformat", requested_locales, options), + JSNumberFormat); + + Handle<String> locale_with_extension_str = + isolate->factory()->NewStringFromStaticChars("localeWithExtension"); + Handle<Object> locale_with_extension_obj = + JSObject::GetDataProperty(r, locale_with_extension_str); + + // The locale_with_extension has to be a string. Either a user + // provided canonicalized string or the default locale. + CHECK(locale_with_extension_obj->IsString()); + Handle<String> locale_with_extension = + Handle<String>::cast(locale_with_extension_obj); + + icu::Locale icu_locale = + Intl::CreateICULocale(isolate, locale_with_extension); + number_format->set_locale(*locale_with_extension); + DCHECK(!icu_locale.isBogus()); + + std::set<std::string> relevant_extension_keys{"nu"}; + std::map<std::string, std::string> extensions = + Intl::LookupUnicodeExtensions(icu_locale, relevant_extension_keys); + + // The list that is the value of the "nu" field of any locale field of + // [[LocaleData]] must not include the values "native", "traditio", or + // "finance". + // + // See https://tc39.github.io/ecma402/#sec-intl.numberformat-internal-slots + if (extensions.find("nu") != extensions.end()) { + const std::string value = extensions.at("nu"); + if (value == "native" || value == "traditio" || value == "finance") { + // 10. Set numberFormat.[[NumberingSystem]] to r.[[nu]]. + UErrorCode status = U_ZERO_ERROR; + icu_locale.setKeywordValue("nu", nullptr, status); + CHECK(U_SUCCESS(status)); + } + } + + // 11. Let dataLocale be r.[[dataLocale]]. + // + // 12. Let style be ? GetOption(options, "style", "string", « "decimal", + // "percent", "currency" », "decimal"). + const char* service = "Intl.NumberFormat"; + std::unique_ptr<char[]> style_cstr; + const std::vector<const char*> style_values = {"decimal", "percent", + "currency"}; + Maybe<bool> found_style = Intl::GetStringOption( + isolate, options, "style", style_values, service, &style_cstr); + MAYBE_RETURN(found_style, MaybeHandle<JSNumberFormat>()); + Style style = Style::DECIMAL; + if (found_style.FromJust()) { + DCHECK_NOT_NULL(style_cstr.get()); + if (strcmp(style_cstr.get(), "percent") == 0) { + style = Style::PERCENT; + } else if (strcmp(style_cstr.get(), "currency") == 0) { + style = Style::CURRENCY; + } + } + + // 13. Set numberFormat.[[Style]] to style. + number_format->set_style(style); + + // 14. Let currency be ? GetOption(options, "currency", "string", undefined, + // undefined). + std::unique_ptr<char[]> currency_cstr; + const std::vector<const char*> empty_values = {}; + Maybe<bool> found_currency = Intl::GetStringOption( + isolate, options, "currency", empty_values, service, ¤cy_cstr); + MAYBE_RETURN(found_currency, MaybeHandle<JSNumberFormat>()); + + std::string currency; + // 15. If currency is not undefined, then + if (found_currency.FromJust()) { + DCHECK_NOT_NULL(currency_cstr.get()); + currency = currency_cstr.get(); + // 15. a. If the result of IsWellFormedCurrencyCode(currency) is false, + // throw a RangeError exception. + if (!IsWellFormedCurrencyCode(currency)) { + THROW_NEW_ERROR( + isolate, + NewRangeError(MessageTemplate::kInvalidCurrencyCode, + factory->NewStringFromAsciiChecked(currency.c_str())), + JSNumberFormat); + } + } + + // 16. If style is "currency" and currency is undefined, throw a TypeError + // exception. + if (style == Style::CURRENCY && !found_currency.FromJust()) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kCurrencyCode), + JSNumberFormat); + } + // 17. If style is "currency", then + int c_digits = 0; + icu::UnicodeString currency_ustr; + if (style == Style::CURRENCY) { + // a. Let currency be the result of converting currency to upper case as + // specified in 6.1 + std::transform(currency.begin(), currency.end(), currency.begin(), toupper); + // c. Let cDigits be CurrencyDigits(currency). + currency_ustr = currency.c_str(); + c_digits = CurrencyDigits(currency_ustr); + } + + // 18. Let currencyDisplay be ? GetOption(options, "currencyDisplay", + // "string", « "code", "symbol", "name" », "symbol"). + std::unique_ptr<char[]> currency_display_cstr; + const std::vector<const char*> currency_display_values = {"code", "name", + "symbol"}; + Maybe<bool> found_currency_display = Intl::GetStringOption( + isolate, options, "currencyDisplay", currency_display_values, service, + ¤cy_display_cstr); + MAYBE_RETURN(found_currency_display, MaybeHandle<JSNumberFormat>()); + CurrencyDisplay currency_display = CurrencyDisplay::SYMBOL; + UNumberFormatStyle format_style = UNUM_CURRENCY; + + if (found_currency_display.FromJust()) { + DCHECK_NOT_NULL(currency_display_cstr.get()); + if (strcmp(currency_display_cstr.get(), "code") == 0) { + currency_display = CurrencyDisplay::CODE; + format_style = UNUM_CURRENCY_ISO; + } else if (strcmp(currency_display_cstr.get(), "name") == 0) { + currency_display = CurrencyDisplay::NAME; + format_style = UNUM_CURRENCY_PLURAL; + } + } + + UErrorCode status = U_ZERO_ERROR; + std::unique_ptr<icu::NumberFormat> icu_number_format; + if (style == Style::DECIMAL) { + icu_number_format.reset( + icu::NumberFormat::createInstance(icu_locale, status)); + } else if (style == Style::PERCENT) { + icu_number_format.reset( + icu::NumberFormat::createPercentInstance(icu_locale, status)); + } else { + DCHECK_EQ(style, Style::CURRENCY); + icu_number_format.reset( + icu::NumberFormat::createInstance(icu_locale, format_style, status)); + } + + if (U_FAILURE(status) || icu_number_format.get() == nullptr) { + status = U_ZERO_ERROR; + // Remove extensions and try again. + icu::Locale no_extension_locale(icu_locale.getBaseName()); + icu_number_format.reset( + icu::NumberFormat::createInstance(no_extension_locale, status)); + + if (U_FAILURE(status) || icu_number_format.get() == nullptr) { + FATAL("Failed to create ICU number_format, are ICU data files missing?"); + } + } + DCHECK(U_SUCCESS(status)); + CHECK_NOT_NULL(icu_number_format.get()); + if (style == Style::CURRENCY) { + // 19. If style is "currency", set numberFormat.[[CurrencyDisplay]] to + // currencyDisplay. + number_format->set_currency_display(currency_display); + + // 17.b. Set numberFormat.[[Currency]] to currency. + if (!currency_ustr.isEmpty()) { + status = U_ZERO_ERROR; + icu_number_format->setCurrency(currency_ustr.getBuffer(), status); + CHECK(U_SUCCESS(status)); + } + } + + // 20. If style is "currency", then + int mnfd_default, mxfd_default; + if (style == Style::CURRENCY) { + // a. Let mnfdDefault be cDigits. + // b. Let mxfdDefault be cDigits. + mnfd_default = c_digits; + mxfd_default = c_digits; + } else { + // 21. Else, + // a. Let mnfdDefault be 0. + mnfd_default = 0; + // b. If style is "percent", then + if (style == Style::PERCENT) { + // i. Let mxfdDefault be 0. + mxfd_default = 0; + } else { + // c. Else, + // i. Let mxfdDefault be 3. + mxfd_default = 3; + } + } + // 22. Perform ? SetNumberFormatDigitOptions(numberFormat, options, + // mnfdDefault, mxfdDefault). + icu::DecimalFormat* icu_decimal_format = + static_cast<icu::DecimalFormat*>(icu_number_format.get()); + Maybe<bool> maybe_set_number_for_digit_options = + Intl::SetNumberFormatDigitOptions(isolate, icu_decimal_format, options, + mnfd_default, mxfd_default); + MAYBE_RETURN(maybe_set_number_for_digit_options, Handle<JSNumberFormat>()); + + // 23. Let useGrouping be ? GetOption(options, "useGrouping", "boolean", + // undefined, true). + bool use_grouping = true; + Maybe<bool> found_use_grouping = Intl::GetBoolOption( + isolate, options, "useGrouping", service, &use_grouping); + MAYBE_RETURN(found_use_grouping, MaybeHandle<JSNumberFormat>()); + // 24. Set numberFormat.[[UseGrouping]] to useGrouping. + icu_number_format->setGroupingUsed(use_grouping ? TRUE : FALSE); + + // 25. Let dataLocaleData be localeData.[[<dataLocale>]]. + // + // 26. Let patterns be dataLocaleData.[[patterns]]. + // + // 27. Assert: patterns is a record (see 11.3.3). + // + // 28. Let stylePatterns be patterns.[[<style>]]. + // + // 29. Set numberFormat.[[PositivePattern]] to + // stylePatterns.[[positivePattern]]. + // + // 30. Set numberFormat.[[NegativePattern]] to + // stylePatterns.[[negativePattern]]. + + Handle<Managed<icu::NumberFormat>> managed_number_format = + Managed<icu::NumberFormat>::FromUniquePtr(isolate, 0, + std::move(icu_number_format)); + number_format->set_icu_number_format(*managed_number_format); + number_format->set_bound_format(*factory->undefined_value()); + + // 31. Return numberFormat. + return number_format; +} + +Handle<String> JSNumberFormat::StyleAsString() const { + switch (style()) { + case Style::DECIMAL: + return GetReadOnlyRoots().decimal_string_handle(); + case Style::PERCENT: + return GetReadOnlyRoots().percent_string_handle(); + case Style::CURRENCY: + return GetReadOnlyRoots().currency_string_handle(); + case Style::COUNT: + UNREACHABLE(); + } +} + +Handle<String> JSNumberFormat::CurrencyDisplayAsString() const { + switch (currency_display()) { + case CurrencyDisplay::CODE: + return GetReadOnlyRoots().code_string_handle(); + case CurrencyDisplay::SYMBOL: + return GetReadOnlyRoots().symbol_string_handle(); + case CurrencyDisplay::NAME: + return GetReadOnlyRoots().name_string_handle(); + case CurrencyDisplay::COUNT: + UNREACHABLE(); + } +} + +MaybeHandle<String> JSNumberFormat::FormatNumber( + Isolate* isolate, Handle<JSNumberFormat> number_format_holder, + double number) { + icu::NumberFormat* number_format = + number_format_holder->icu_number_format()->raw(); + CHECK_NOT_NULL(number_format); + + icu::UnicodeString result; + number_format->format(number, result); + + return isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>( + reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length())); +} + +namespace { + +bool cmp_NumberFormatSpan(const NumberFormatSpan& a, + const NumberFormatSpan& b) { + // Regions that start earlier should be encountered earlier. + if (a.begin_pos < b.begin_pos) return true; + if (a.begin_pos > b.begin_pos) return false; + // For regions that start in the same place, regions that last longer should + // be encountered earlier. + if (a.end_pos < b.end_pos) return false; + if (a.end_pos > b.end_pos) return true; + // For regions that are exactly the same, one of them must be the "literal" + // backdrop we added, which has a field_id of -1, so consider higher field_ids + // to be later. + return a.field_id < b.field_id; +} + +// The list comes from third_party/icu/source/i18n/unicode/unum.h. +// They're mapped to NumberFormat part types mentioned throughout +// https://tc39.github.io/ecma402/#sec-partitionnumberpattern . +Handle<String> IcuNumberFieldIdToNumberType(int32_t field_id, double number, + Isolate* isolate) { + switch (static_cast<UNumberFormatFields>(field_id)) { + case UNUM_INTEGER_FIELD: + if (std::isfinite(number)) return isolate->factory()->integer_string(); + if (std::isnan(number)) return isolate->factory()->nan_string(); + return isolate->factory()->infinity_string(); + case UNUM_FRACTION_FIELD: + return isolate->factory()->fraction_string(); + case UNUM_DECIMAL_SEPARATOR_FIELD: + return isolate->factory()->decimal_string(); + case UNUM_GROUPING_SEPARATOR_FIELD: + return isolate->factory()->group_string(); + case UNUM_CURRENCY_FIELD: + return isolate->factory()->currency_string(); + case UNUM_PERCENT_FIELD: + return isolate->factory()->percentSign_string(); + case UNUM_SIGN_FIELD: + return number < 0 ? isolate->factory()->minusSign_string() + : isolate->factory()->plusSign_string(); + + case UNUM_EXPONENT_SYMBOL_FIELD: + case UNUM_EXPONENT_SIGN_FIELD: + case UNUM_EXPONENT_FIELD: + // We should never get these because we're not using any scientific + // formatter. + UNREACHABLE(); + return Handle<String>(); + + case UNUM_PERMILL_FIELD: + // We're not creating any permill formatter, and it's not even clear how + // that would be possible with the ICU API. + UNREACHABLE(); + return Handle<String>(); + + default: + UNREACHABLE(); + return Handle<String>(); + } +} +} // namespace + +// Flattens a list of possibly-overlapping "regions" to a list of +// non-overlapping "parts". At least one of the input regions must span the +// entire space of possible indexes. The regions parameter will sorted in-place +// according to some criteria; this is done for performance to avoid copying the +// input. +std::vector<NumberFormatSpan> FlattenRegionsToParts( + std::vector<NumberFormatSpan>* regions) { + // The intention of this algorithm is that it's used to translate ICU "fields" + // to JavaScript "parts" of a formatted string. Each ICU field and JavaScript + // part has an integer field_id, which corresponds to something like "grouping + // separator", "fraction", or "percent sign", and has a begin and end + // position. Here's a diagram of: + + // var nf = new Intl.NumberFormat(['de'], {style:'currency',currency:'EUR'}); + // nf.formatToParts(123456.78); + + // : 6 + // input regions: 0000000211 7 + // ('-' means -1): ------------ + // formatted string: "123.456,78 €" + // output parts: 0006000211-7 + + // To illustrate the requirements of this algorithm, here's a contrived and + // convoluted example of inputs and expected outputs: + + // : 4 + // : 22 33 3 + // : 11111 22 + // input regions: 0000000 111 + // : ------------ + // formatted string: "abcdefghijkl" + // output parts: 0221340--231 + // (The characters in the formatted string are irrelevant to this function.) + + // We arrange the overlapping input regions like a mountain range where + // smaller regions are "on top" of larger regions, and we output a birds-eye + // view of the mountains, so that smaller regions take priority over larger + // regions. + std::sort(regions->begin(), regions->end(), cmp_NumberFormatSpan); + std::vector<size_t> overlapping_region_index_stack; + // At least one item in regions must be a region spanning the entire string. + // Due to the sorting above, the first item in the vector will be one of them. + overlapping_region_index_stack.push_back(0); + NumberFormatSpan top_region = regions->at(0); + size_t region_iterator = 1; + int32_t entire_size = top_region.end_pos; + + std::vector<NumberFormatSpan> out_parts; + + // The "climber" is a cursor that advances from left to right climbing "up" + // and "down" the mountains. Whenever the climber moves to the right, that + // represents an item of output. + int32_t climber = 0; + while (climber < entire_size) { + int32_t next_region_begin_pos; + if (region_iterator < regions->size()) { + next_region_begin_pos = regions->at(region_iterator).begin_pos; + } else { + // finish off the rest of the input by proceeding to the end. + next_region_begin_pos = entire_size; + } + + if (climber < next_region_begin_pos) { + while (top_region.end_pos < next_region_begin_pos) { + if (climber < top_region.end_pos) { + // step down + out_parts.push_back(NumberFormatSpan(top_region.field_id, climber, + top_region.end_pos)); + climber = top_region.end_pos; + } else { + // drop down + } + overlapping_region_index_stack.pop_back(); + top_region = regions->at(overlapping_region_index_stack.back()); + } + if (climber < next_region_begin_pos) { + // cross a plateau/mesa/valley + out_parts.push_back(NumberFormatSpan(top_region.field_id, climber, + next_region_begin_pos)); + climber = next_region_begin_pos; + } + } + if (region_iterator < regions->size()) { + overlapping_region_index_stack.push_back(region_iterator++); + top_region = regions->at(overlapping_region_index_stack.back()); + } + } + return out_parts; +} + +MaybeHandle<JSArray> JSNumberFormat::FormatToParts( + Isolate* isolate, Handle<JSNumberFormat> number_format, double number) { + Factory* factory = isolate->factory(); + icu::NumberFormat* fmt = number_format->icu_number_format()->raw(); + CHECK_NOT_NULL(fmt); + + icu::UnicodeString formatted; + icu::FieldPositionIterator fp_iter; + UErrorCode status = U_ZERO_ERROR; + fmt->format(number, formatted, &fp_iter, status); + if (U_FAILURE(status)) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), JSArray); + } + + Handle<JSArray> result = factory->NewJSArray(0); + int32_t length = formatted.length(); + if (length == 0) return result; + + std::vector<NumberFormatSpan> regions; + // Add a "literal" backdrop for the entire string. This will be used if no + // other region covers some part of the formatted string. It's possible + // there's another field with exactly the same begin and end as this backdrop, + // in which case the backdrop's field_id of -1 will give it lower priority. + regions.push_back(NumberFormatSpan(-1, 0, formatted.length())); + + { + icu::FieldPosition fp; + while (fp_iter.next(fp)) { + regions.push_back(NumberFormatSpan(fp.getField(), fp.getBeginIndex(), + fp.getEndIndex())); + } + } + + std::vector<NumberFormatSpan> parts = FlattenRegionsToParts(®ions); + + int index = 0; + for (auto it = parts.begin(); it < parts.end(); it++) { + NumberFormatSpan part = *it; + Handle<String> field_type_string = + part.field_id == -1 + ? isolate->factory()->literal_string() + : IcuNumberFieldIdToNumberType(part.field_id, number, isolate); + Handle<String> substring; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, substring, + Intl::ToString(isolate, formatted, part.begin_pos, part.end_pos), + JSArray); + Intl::AddElement(isolate, result, index, field_type_string, substring); + ++index; + } + JSObject::ValidateElements(*result); + + return result; +} + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/objects/js-number-format.h b/deps/v8/src/objects/js-number-format.h new file mode 100644 index 0000000000..52443dc3d3 --- /dev/null +++ b/deps/v8/src/objects/js-number-format.h @@ -0,0 +1,135 @@ +// Copyright 2018 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. + +#ifndef V8_INTL_SUPPORT +#error Internationalization is expected to be enabled. +#endif // V8_INTL_SUPPORT + +#ifndef V8_OBJECTS_JS_NUMBER_FORMAT_H_ +#define V8_OBJECTS_JS_NUMBER_FORMAT_H_ + +#include "src/heap/factory.h" +#include "src/isolate.h" +#include "src/objects.h" +#include "src/objects/intl-objects.h" +#include "src/objects/managed.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace U_ICU_NAMESPACE { +class NumberFormat; +} // namespace U_ICU_NAMESPACE + +namespace v8 { +namespace internal { + +class JSNumberFormat : public JSObject { + public: + // ecma402/#sec-initializenumberformat + V8_WARN_UNUSED_RESULT static MaybeHandle<JSNumberFormat> Initialize( + Isolate* isolate, Handle<JSNumberFormat> number_format, + Handle<Object> locales, Handle<Object> options); + + // ecma402/#sec-unwrapnumberformat + V8_WARN_UNUSED_RESULT static MaybeHandle<JSNumberFormat> UnwrapNumberFormat( + Isolate* isolate, Handle<JSReceiver> format_holder); + + // ecma402/#sec-intl.numberformat.prototype.resolvedoptions + static Handle<JSObject> ResolvedOptions(Isolate* isolate, + Handle<JSNumberFormat> number_format); + + V8_WARN_UNUSED_RESULT static MaybeHandle<JSArray> FormatToParts( + Isolate* isolate, Handle<JSNumberFormat> number_format, double number); + + V8_WARN_UNUSED_RESULT static MaybeHandle<String> FormatNumber( + Isolate* isolate, Handle<JSNumberFormat> number_format, double number); + + Handle<String> StyleAsString() const; + Handle<String> CurrencyDisplayAsString() const; + + DECL_CAST(JSNumberFormat) + DECL_PRINTER(JSNumberFormat) + DECL_VERIFIER(JSNumberFormat) + + // [[Style]] is one of the values "decimal", "percent" or "currency", + // identifying the style of the number format. + enum class Style { + DECIMAL, + PERCENT, + CURRENCY, + + COUNT + }; + inline void set_style(Style style); + inline Style style() const; + + // [[CurrencyDisplay]] is one of the values "code", "symbol" or "name", + // identifying the display of the currency number format. + enum class CurrencyDisplay { + CODE, + SYMBOL, + NAME, + + COUNT + }; + inline void set_currency_display(CurrencyDisplay currency_display); + inline CurrencyDisplay currency_display() const; + +// Layout description. +#define JS_NUMBER_FORMAT_FIELDS(V) \ + V(kLocaleOffset, kPointerSize) \ + V(kICUNumberFormatOffset, kPointerSize) \ + V(kBoundFormatOffset, kPointerSize) \ + V(kFlagsOffset, kPointerSize) \ + /* Total size. */ \ + V(kSize, 0) + + DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, JS_NUMBER_FORMAT_FIELDS) +#undef JS_NUMBER_FORMAT_FIELDS + +// Bit positions in |flags|. +#define FLAGS_BIT_FIELDS(V, _) \ + V(StyleBits, Style, 2, _) \ + V(CurrencyDisplayBits, CurrencyDisplay, 2, _) + + DEFINE_BIT_FIELDS(FLAGS_BIT_FIELDS) +#undef FLAGS_BIT_FIELDS + + STATIC_ASSERT(Style::DECIMAL <= StyleBits::kMax); + STATIC_ASSERT(Style::PERCENT <= StyleBits::kMax); + STATIC_ASSERT(Style::CURRENCY <= StyleBits::kMax); + + STATIC_ASSERT(CurrencyDisplay::CODE <= CurrencyDisplayBits::kMax); + STATIC_ASSERT(CurrencyDisplay::SYMBOL <= CurrencyDisplayBits::kMax); + STATIC_ASSERT(CurrencyDisplay::NAME <= CurrencyDisplayBits::kMax); + + DECL_ACCESSORS(locale, String) + DECL_ACCESSORS(icu_number_format, Managed<icu::NumberFormat>) + DECL_ACCESSORS(bound_format, Object) + DECL_INT_ACCESSORS(flags) + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSNumberFormat); +}; + +struct NumberFormatSpan { + int32_t field_id; + int32_t begin_pos; + int32_t end_pos; + + NumberFormatSpan() = default; + NumberFormatSpan(int32_t field_id, int32_t begin_pos, int32_t end_pos) + : field_id(field_id), begin_pos(begin_pos), end_pos(end_pos) {} +}; + +std::vector<NumberFormatSpan> FlattenRegionsToParts( + std::vector<NumberFormatSpan>* regions); + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_JS_NUMBER_FORMAT_H_ diff --git a/deps/v8/src/objects/js-objects-inl.h b/deps/v8/src/objects/js-objects-inl.h new file mode 100644 index 0000000000..53483136d8 --- /dev/null +++ b/deps/v8/src/objects/js-objects-inl.h @@ -0,0 +1,904 @@ +// Copyright 2018 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. + +#ifndef V8_OBJECTS_JS_OBJECTS_INL_H_ +#define V8_OBJECTS_JS_OBJECTS_INL_H_ + +#include "src/objects/js-objects.h" + +#include "src/feedback-vector.h" +#include "src/heap/heap-write-barrier.h" +#include "src/keys.h" +#include "src/lookup-inl.h" +#include "src/objects/property-array-inl.h" +#include "src/objects/shared-function-info.h" +#include "src/prototype.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +CAST_ACCESSOR(JSAsyncFromSyncIterator) +CAST_ACCESSOR(JSBoundFunction) +CAST_ACCESSOR(JSDataView) +CAST_ACCESSOR(JSDate) +CAST_ACCESSOR(JSFunction) +CAST_ACCESSOR(JSGlobalObject) +CAST_ACCESSOR(JSGlobalProxy) +CAST_ACCESSOR(JSMessageObject) +CAST_ACCESSOR(JSObject) +CAST_ACCESSOR(JSReceiver) +CAST_ACCESSOR(JSStringIterator) +CAST_ACCESSOR(JSValue) + +MaybeHandle<Object> JSReceiver::GetProperty(Isolate* isolate, + Handle<JSReceiver> receiver, + Handle<Name> name) { + LookupIterator it(isolate, receiver, name, receiver); + if (!it.IsFound()) return it.factory()->undefined_value(); + return Object::GetProperty(&it); +} + +MaybeHandle<Object> JSReceiver::GetElement(Isolate* isolate, + Handle<JSReceiver> receiver, + uint32_t index) { + LookupIterator it(isolate, receiver, index, receiver); + if (!it.IsFound()) return it.factory()->undefined_value(); + return Object::GetProperty(&it); +} + +Handle<Object> JSReceiver::GetDataProperty(Handle<JSReceiver> object, + Handle<Name> name) { + LookupIterator it(object, name, object, + LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR); + if (!it.IsFound()) return it.factory()->undefined_value(); + return GetDataProperty(&it); +} + +MaybeHandle<Object> JSReceiver::GetPrototype(Isolate* isolate, + Handle<JSReceiver> receiver) { + // We don't expect access checks to be needed on JSProxy objects. + DCHECK(!receiver->IsAccessCheckNeeded() || receiver->IsJSObject()); + PrototypeIterator iter(isolate, receiver, kStartAtReceiver, + PrototypeIterator::END_AT_NON_HIDDEN); + do { + if (!iter.AdvanceFollowingProxies()) return MaybeHandle<Object>(); + } while (!iter.IsAtEnd()); + return PrototypeIterator::GetCurrent(iter); +} + +MaybeHandle<Object> JSReceiver::GetProperty(Isolate* isolate, + Handle<JSReceiver> receiver, + const char* name) { + Handle<String> str = isolate->factory()->InternalizeUtf8String(name); + return GetProperty(isolate, receiver, str); +} + +// static +V8_WARN_UNUSED_RESULT MaybeHandle<FixedArray> JSReceiver::OwnPropertyKeys( + Handle<JSReceiver> object) { + return KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, + ALL_PROPERTIES, + GetKeysConversion::kConvertToString); +} + +bool JSObject::PrototypeHasNoElements(Isolate* isolate, JSObject* object) { + DisallowHeapAllocation no_gc; + HeapObject* prototype = HeapObject::cast(object->map()->prototype()); + ReadOnlyRoots roots(isolate); + HeapObject* null = roots.null_value(); + HeapObject* empty_fixed_array = roots.empty_fixed_array(); + HeapObject* empty_slow_element_dictionary = + roots.empty_slow_element_dictionary(); + while (prototype != null) { + Map* map = prototype->map(); + if (map->IsCustomElementsReceiverMap()) return false; + HeapObject* elements = JSObject::cast(prototype)->elements(); + if (elements != empty_fixed_array && + elements != empty_slow_element_dictionary) { + return false; + } + prototype = HeapObject::cast(map->prototype()); + } + return true; +} + +ACCESSORS(JSReceiver, raw_properties_or_hash, Object, kPropertiesOrHashOffset) + +FixedArrayBase* JSObject::elements() const { + Object* array = READ_FIELD(this, kElementsOffset); + return static_cast<FixedArrayBase*>(array); +} + +void JSObject::EnsureCanContainHeapObjectElements(Handle<JSObject> object) { + JSObject::ValidateElements(*object); + ElementsKind elements_kind = object->map()->elements_kind(); + if (!IsObjectElementsKind(elements_kind)) { + if (IsHoleyElementsKind(elements_kind)) { + TransitionElementsKind(object, HOLEY_ELEMENTS); + } else { + TransitionElementsKind(object, PACKED_ELEMENTS); + } + } +} + +void JSObject::EnsureCanContainElements(Handle<JSObject> object, + Object** objects, uint32_t count, + EnsureElementsMode mode) { + ElementsKind current_kind = object->GetElementsKind(); + ElementsKind target_kind = current_kind; + { + DisallowHeapAllocation no_allocation; + DCHECK(mode != ALLOW_COPIED_DOUBLE_ELEMENTS); + bool is_holey = IsHoleyElementsKind(current_kind); + if (current_kind == HOLEY_ELEMENTS) return; + Object* the_hole = object->GetReadOnlyRoots().the_hole_value(); + for (uint32_t i = 0; i < count; ++i) { + Object* current = *objects++; + if (current == the_hole) { + is_holey = true; + target_kind = GetHoleyElementsKind(target_kind); + } else if (!current->IsSmi()) { + if (mode == ALLOW_CONVERTED_DOUBLE_ELEMENTS && current->IsNumber()) { + if (IsSmiElementsKind(target_kind)) { + if (is_holey) { + target_kind = HOLEY_DOUBLE_ELEMENTS; + } else { + target_kind = PACKED_DOUBLE_ELEMENTS; + } + } + } else if (is_holey) { + target_kind = HOLEY_ELEMENTS; + break; + } else { + target_kind = PACKED_ELEMENTS; + } + } + } + } + if (target_kind != current_kind) { + TransitionElementsKind(object, target_kind); + } +} + +void JSObject::EnsureCanContainElements(Handle<JSObject> object, + Handle<FixedArrayBase> elements, + uint32_t length, + EnsureElementsMode mode) { + ReadOnlyRoots roots = object->GetReadOnlyRoots(); + if (elements->map() != roots.fixed_double_array_map()) { + DCHECK(elements->map() == roots.fixed_array_map() || + elements->map() == roots.fixed_cow_array_map()); + if (mode == ALLOW_COPIED_DOUBLE_ELEMENTS) { + mode = DONT_ALLOW_DOUBLE_ELEMENTS; + } + Object** objects = + Handle<FixedArray>::cast(elements)->GetFirstElementAddress(); + EnsureCanContainElements(object, objects, length, mode); + return; + } + + DCHECK(mode == ALLOW_COPIED_DOUBLE_ELEMENTS); + if (object->GetElementsKind() == HOLEY_SMI_ELEMENTS) { + TransitionElementsKind(object, HOLEY_DOUBLE_ELEMENTS); + } else if (object->GetElementsKind() == PACKED_SMI_ELEMENTS) { + Handle<FixedDoubleArray> double_array = + Handle<FixedDoubleArray>::cast(elements); + for (uint32_t i = 0; i < length; ++i) { + if (double_array->is_the_hole(i)) { + TransitionElementsKind(object, HOLEY_DOUBLE_ELEMENTS); + return; + } + } + TransitionElementsKind(object, PACKED_DOUBLE_ELEMENTS); + } +} + +void JSObject::SetMapAndElements(Handle<JSObject> object, Handle<Map> new_map, + Handle<FixedArrayBase> value) { + JSObject::MigrateToMap(object, new_map); + DCHECK((object->map()->has_fast_smi_or_object_elements() || + (*value == object->GetReadOnlyRoots().empty_fixed_array()) || + object->map()->has_fast_string_wrapper_elements()) == + (value->map() == object->GetReadOnlyRoots().fixed_array_map() || + value->map() == object->GetReadOnlyRoots().fixed_cow_array_map())); + DCHECK((*value == object->GetReadOnlyRoots().empty_fixed_array()) || + (object->map()->has_fast_double_elements() == + value->IsFixedDoubleArray())); + object->set_elements(*value); +} + +void JSObject::set_elements(FixedArrayBase* value, WriteBarrierMode mode) { + WRITE_FIELD(this, kElementsOffset, value); + CONDITIONAL_WRITE_BARRIER(this, kElementsOffset, value, mode); +} + +void JSObject::initialize_elements() { + FixedArrayBase* elements = map()->GetInitialElements(); + WRITE_FIELD(this, kElementsOffset, elements); +} + +InterceptorInfo* JSObject::GetIndexedInterceptor() { + return map()->GetIndexedInterceptor(); +} + +InterceptorInfo* JSObject::GetNamedInterceptor() { + return map()->GetNamedInterceptor(); +} + +int JSObject::GetHeaderSize() const { return GetHeaderSize(map()); } + +int JSObject::GetHeaderSize(const Map* map) { + // Check for the most common kind of JavaScript object before + // falling into the generic switch. This speeds up the internal + // field operations considerably on average. + InstanceType instance_type = map->instance_type(); + return instance_type == JS_OBJECT_TYPE + ? JSObject::kHeaderSize + : GetHeaderSize(instance_type, map->has_prototype_slot()); +} + +// static +int JSObject::GetEmbedderFieldCount(const Map* map) { + int instance_size = map->instance_size(); + if (instance_size == kVariableSizeSentinel) return 0; + return ((instance_size - GetHeaderSize(map)) >> kPointerSizeLog2) - + map->GetInObjectProperties(); +} + +int JSObject::GetEmbedderFieldCount() const { + return GetEmbedderFieldCount(map()); +} + +int JSObject::GetEmbedderFieldOffset(int index) { + DCHECK(index < GetEmbedderFieldCount() && index >= 0); + return GetHeaderSize() + (kPointerSize * index); +} + +Object* JSObject::GetEmbedderField(int index) { + DCHECK(index < GetEmbedderFieldCount() && index >= 0); + // Internal objects do follow immediately after the header, whereas in-object + // properties are at the end of the object. Therefore there is no need + // to adjust the index here. + return READ_FIELD(this, GetHeaderSize() + (kPointerSize * index)); +} + +void JSObject::SetEmbedderField(int index, Object* value) { + DCHECK(index < GetEmbedderFieldCount() && index >= 0); + // Internal objects do follow immediately after the header, whereas in-object + // properties are at the end of the object. Therefore there is no need + // to adjust the index here. + int offset = GetHeaderSize() + (kPointerSize * index); + WRITE_FIELD(this, offset, value); + WRITE_BARRIER(this, offset, value); +} + +void JSObject::SetEmbedderField(int index, Smi* value) { + DCHECK(index < GetEmbedderFieldCount() && index >= 0); + // Internal objects do follow immediately after the header, whereas in-object + // properties are at the end of the object. Therefore there is no need + // to adjust the index here. + int offset = GetHeaderSize() + (kPointerSize * index); + WRITE_FIELD(this, offset, value); +} + +bool JSObject::IsUnboxedDoubleField(FieldIndex index) { + if (!FLAG_unbox_double_fields) return false; + return map()->IsUnboxedDoubleField(index); +} + +// Access fast-case object properties at index. The use of these routines +// is needed to correctly distinguish between properties stored in-object and +// properties stored in the properties array. +Object* JSObject::RawFastPropertyAt(FieldIndex index) { + DCHECK(!IsUnboxedDoubleField(index)); + if (index.is_inobject()) { + return READ_FIELD(this, index.offset()); + } else { + return property_array()->get(index.outobject_array_index()); + } +} + +double JSObject::RawFastDoublePropertyAt(FieldIndex index) { + DCHECK(IsUnboxedDoubleField(index)); + return READ_DOUBLE_FIELD(this, index.offset()); +} + +uint64_t JSObject::RawFastDoublePropertyAsBitsAt(FieldIndex index) { + DCHECK(IsUnboxedDoubleField(index)); + return READ_UINT64_FIELD(this, index.offset()); +} + +void JSObject::RawFastPropertyAtPut(FieldIndex index, Object* value) { + if (index.is_inobject()) { + int offset = index.offset(); + WRITE_FIELD(this, offset, value); + WRITE_BARRIER(this, offset, value); + } else { + property_array()->set(index.outobject_array_index(), value); + } +} + +void JSObject::RawFastDoublePropertyAsBitsAtPut(FieldIndex index, + uint64_t bits) { + // Double unboxing is enabled only on 64-bit platforms. + DCHECK_EQ(kDoubleSize, kPointerSize); + Address field_addr = FIELD_ADDR(this, index.offset()); + base::Relaxed_Store(reinterpret_cast<base::AtomicWord*>(field_addr), + static_cast<base::AtomicWord>(bits)); +} + +void JSObject::FastPropertyAtPut(FieldIndex index, Object* value) { + if (IsUnboxedDoubleField(index)) { + DCHECK(value->IsMutableHeapNumber()); + // Ensure that all bits of the double value are preserved. + RawFastDoublePropertyAsBitsAtPut( + index, MutableHeapNumber::cast(value)->value_as_bits()); + } else { + RawFastPropertyAtPut(index, value); + } +} + +void JSObject::WriteToField(int descriptor, PropertyDetails details, + Object* value) { + DCHECK_EQ(kField, details.location()); + DCHECK_EQ(kData, details.kind()); + DisallowHeapAllocation no_gc; + FieldIndex index = FieldIndex::ForDescriptor(map(), descriptor); + if (details.representation().IsDouble()) { + // Nothing more to be done. + if (value->IsUninitialized()) { + return; + } + // Manipulating the signaling NaN used for the hole and uninitialized + // double field sentinel in C++, e.g. with bit_cast or value()/set_value(), + // will change its value on ia32 (the x87 stack is used to return values + // and stores to the stack silently clear the signalling bit). + uint64_t bits; + if (value->IsSmi()) { + bits = bit_cast<uint64_t>(static_cast<double>(Smi::ToInt(value))); + } else { + DCHECK(value->IsHeapNumber()); + bits = HeapNumber::cast(value)->value_as_bits(); + } + if (IsUnboxedDoubleField(index)) { + RawFastDoublePropertyAsBitsAtPut(index, bits); + } else { + auto box = MutableHeapNumber::cast(RawFastPropertyAt(index)); + box->set_value_as_bits(bits); + } + } else { + RawFastPropertyAtPut(index, value); + } +} + +int JSObject::GetInObjectPropertyOffset(int index) { + return map()->GetInObjectPropertyOffset(index); +} + +Object* JSObject::InObjectPropertyAt(int index) { + int offset = GetInObjectPropertyOffset(index); + return READ_FIELD(this, offset); +} + +Object* JSObject::InObjectPropertyAtPut(int index, Object* value, + WriteBarrierMode mode) { + // Adjust for the number of properties stored in the object. + int offset = GetInObjectPropertyOffset(index); + WRITE_FIELD(this, offset, value); + CONDITIONAL_WRITE_BARRIER(this, offset, value, mode); + return value; +} + +void JSObject::InitializeBody(Map* map, int start_offset, + Object* pre_allocated_value, + Object* filler_value) { + DCHECK(!filler_value->IsHeapObject() || !Heap::InNewSpace(filler_value)); + DCHECK(!pre_allocated_value->IsHeapObject() || + !Heap::InNewSpace(pre_allocated_value)); + int size = map->instance_size(); + int offset = start_offset; + if (filler_value != pre_allocated_value) { + int end_of_pre_allocated_offset = + size - (map->UnusedPropertyFields() * kPointerSize); + DCHECK_LE(kHeaderSize, end_of_pre_allocated_offset); + while (offset < end_of_pre_allocated_offset) { + WRITE_FIELD(this, offset, pre_allocated_value); + offset += kPointerSize; + } + } + while (offset < size) { + WRITE_FIELD(this, offset, filler_value); + offset += kPointerSize; + } +} + +Object* JSBoundFunction::raw_bound_target_function() const { + return READ_FIELD(this, kBoundTargetFunctionOffset); +} + +ACCESSORS(JSBoundFunction, bound_target_function, JSReceiver, + kBoundTargetFunctionOffset) +ACCESSORS(JSBoundFunction, bound_this, Object, kBoundThisOffset) +ACCESSORS(JSBoundFunction, bound_arguments, FixedArray, kBoundArgumentsOffset) + +ACCESSORS(JSFunction, shared, SharedFunctionInfo, kSharedFunctionInfoOffset) +ACCESSORS(JSFunction, feedback_cell, FeedbackCell, kFeedbackCellOffset) + +ACCESSORS(JSGlobalObject, native_context, Context, kNativeContextOffset) +ACCESSORS(JSGlobalObject, global_proxy, JSObject, kGlobalProxyOffset) + +ACCESSORS(JSGlobalProxy, native_context, Object, kNativeContextOffset) + +FeedbackVector* JSFunction::feedback_vector() const { + DCHECK(has_feedback_vector()); + return FeedbackVector::cast(feedback_cell()->value()); +} + +// Code objects that are marked for deoptimization are not considered to be +// optimized. This is because the JSFunction might have been already +// deoptimized but its code() still needs to be unlinked, which will happen on +// its next activation. +// TODO(jupvfranco): rename this function. Maybe RunOptimizedCode, +// or IsValidOptimizedCode. +bool JSFunction::IsOptimized() { + return code()->kind() == Code::OPTIMIZED_FUNCTION && + !code()->marked_for_deoptimization(); +} + +bool JSFunction::HasOptimizedCode() { + return IsOptimized() || + (has_feedback_vector() && feedback_vector()->has_optimized_code() && + !feedback_vector()->optimized_code()->marked_for_deoptimization()); +} + +bool JSFunction::HasOptimizationMarker() { + return has_feedback_vector() && feedback_vector()->has_optimization_marker(); +} + +void JSFunction::ClearOptimizationMarker() { + DCHECK(has_feedback_vector()); + feedback_vector()->ClearOptimizationMarker(); +} + +// Optimized code marked for deoptimization will tier back down to running +// interpreted on its next activation, and already doesn't count as IsOptimized. +bool JSFunction::IsInterpreted() { + return code()->is_interpreter_trampoline_builtin() || + (code()->kind() == Code::OPTIMIZED_FUNCTION && + code()->marked_for_deoptimization()); +} + +bool JSFunction::ChecksOptimizationMarker() { + return code()->checks_optimization_marker(); +} + +bool JSFunction::IsMarkedForOptimization() { + return has_feedback_vector() && feedback_vector()->optimization_marker() == + OptimizationMarker::kCompileOptimized; +} + +bool JSFunction::IsMarkedForConcurrentOptimization() { + return has_feedback_vector() && + feedback_vector()->optimization_marker() == + OptimizationMarker::kCompileOptimizedConcurrent; +} + +bool JSFunction::IsInOptimizationQueue() { + return has_feedback_vector() && feedback_vector()->optimization_marker() == + OptimizationMarker::kInOptimizationQueue; +} + +void JSFunction::CompleteInobjectSlackTrackingIfActive() { + if (!has_prototype_slot()) return; + if (has_initial_map() && initial_map()->IsInobjectSlackTrackingInProgress()) { + initial_map()->CompleteInobjectSlackTracking(GetIsolate()); + } +} + +AbstractCode* JSFunction::abstract_code() { + if (IsInterpreted()) { + return AbstractCode::cast(shared()->GetBytecodeArray()); + } else { + return AbstractCode::cast(code()); + } +} + +Code* JSFunction::code() { return Code::cast(READ_FIELD(this, kCodeOffset)); } + +void JSFunction::set_code(Code* value) { + DCHECK(!Heap::InNewSpace(value)); + WRITE_FIELD(this, kCodeOffset, value); + MarkingBarrier(this, HeapObject::RawField(this, kCodeOffset), value); +} + +void JSFunction::set_code_no_write_barrier(Code* value) { + DCHECK(!Heap::InNewSpace(value)); + WRITE_FIELD(this, kCodeOffset, value); +} + +void JSFunction::ClearOptimizedCodeSlot(const char* reason) { + if (has_feedback_vector() && feedback_vector()->has_optimized_code()) { + if (FLAG_trace_opt) { + PrintF("[evicting entry from optimizing code feedback slot (%s) for ", + reason); + ShortPrint(); + PrintF("]\n"); + } + feedback_vector()->ClearOptimizedCode(); + } +} + +void JSFunction::SetOptimizationMarker(OptimizationMarker marker) { + DCHECK(has_feedback_vector()); + DCHECK(ChecksOptimizationMarker()); + DCHECK(!HasOptimizedCode()); + + feedback_vector()->SetOptimizationMarker(marker); +} + +bool JSFunction::has_feedback_vector() const { + return !feedback_cell()->value()->IsUndefined(); +} + +Context* JSFunction::context() { + return Context::cast(READ_FIELD(this, kContextOffset)); +} + +bool JSFunction::has_context() const { + return READ_FIELD(this, kContextOffset)->IsContext(); +} + +JSGlobalProxy* JSFunction::global_proxy() { return context()->global_proxy(); } + +Context* JSFunction::native_context() { return context()->native_context(); } + +void JSFunction::set_context(Object* value) { + DCHECK(value->IsUndefined() || value->IsContext()); + WRITE_FIELD(this, kContextOffset, value); + WRITE_BARRIER(this, kContextOffset, value); +} + +ACCESSORS_CHECKED(JSFunction, prototype_or_initial_map, Object, + kPrototypeOrInitialMapOffset, map()->has_prototype_slot()) + +bool JSFunction::has_prototype_slot() const { + return map()->has_prototype_slot(); +} + +Map* JSFunction::initial_map() { return Map::cast(prototype_or_initial_map()); } + +bool JSFunction::has_initial_map() { + DCHECK(has_prototype_slot()); + return prototype_or_initial_map()->IsMap(); +} + +bool JSFunction::has_instance_prototype() { + DCHECK(has_prototype_slot()); + return has_initial_map() || !prototype_or_initial_map()->IsTheHole(); +} + +bool JSFunction::has_prototype() { + DCHECK(has_prototype_slot()); + return map()->has_non_instance_prototype() || has_instance_prototype(); +} + +bool JSFunction::has_prototype_property() { + return (has_prototype_slot() && IsConstructor()) || + IsGeneratorFunction(shared()->kind()); +} + +bool JSFunction::PrototypeRequiresRuntimeLookup() { + return !has_prototype_property() || map()->has_non_instance_prototype(); +} + +Object* JSFunction::instance_prototype() { + DCHECK(has_instance_prototype()); + if (has_initial_map()) return initial_map()->prototype(); + // When there is no initial map and the prototype is a JSReceiver, the + // initial map field is used for the prototype field. + return prototype_or_initial_map(); +} + +Object* JSFunction::prototype() { + DCHECK(has_prototype()); + // If the function's prototype property has been set to a non-JSReceiver + // value, that value is stored in the constructor field of the map. + if (map()->has_non_instance_prototype()) { + Object* prototype = map()->GetConstructor(); + // The map must have a prototype in that field, not a back pointer. + DCHECK(!prototype->IsMap()); + DCHECK(!prototype->IsFunctionTemplateInfo()); + return prototype; + } + return instance_prototype(); +} + +bool JSFunction::is_compiled() { + return code()->builtin_index() != Builtins::kCompileLazy; +} + +ACCESSORS(JSValue, value, Object, kValueOffset) + +ACCESSORS(JSDate, value, Object, kValueOffset) +ACCESSORS(JSDate, cache_stamp, Object, kCacheStampOffset) +ACCESSORS(JSDate, year, Object, kYearOffset) +ACCESSORS(JSDate, month, Object, kMonthOffset) +ACCESSORS(JSDate, day, Object, kDayOffset) +ACCESSORS(JSDate, weekday, Object, kWeekdayOffset) +ACCESSORS(JSDate, hour, Object, kHourOffset) +ACCESSORS(JSDate, min, Object, kMinOffset) +ACCESSORS(JSDate, sec, Object, kSecOffset) + +SMI_ACCESSORS(JSMessageObject, type, kTypeOffset) +ACCESSORS(JSMessageObject, argument, Object, kArgumentsOffset) +ACCESSORS(JSMessageObject, script, Script, kScriptOffset) +ACCESSORS(JSMessageObject, stack_frames, Object, kStackFramesOffset) +SMI_ACCESSORS(JSMessageObject, start_position, kStartPositionOffset) +SMI_ACCESSORS(JSMessageObject, end_position, kEndPositionOffset) +SMI_ACCESSORS(JSMessageObject, error_level, kErrorLevelOffset) + +ElementsKind JSObject::GetElementsKind() const { + ElementsKind kind = map()->elements_kind(); +#if VERIFY_HEAP && DEBUG + FixedArrayBase* fixed_array = + reinterpret_cast<FixedArrayBase*>(READ_FIELD(this, kElementsOffset)); + + // If a GC was caused while constructing this object, the elements + // pointer may point to a one pointer filler map. + if (ElementsAreSafeToExamine()) { + Map* map = fixed_array->map(); + if (IsSmiOrObjectElementsKind(kind)) { + DCHECK(map == GetReadOnlyRoots().fixed_array_map() || + map == GetReadOnlyRoots().fixed_cow_array_map()); + } else if (IsDoubleElementsKind(kind)) { + DCHECK(fixed_array->IsFixedDoubleArray() || + fixed_array == GetReadOnlyRoots().empty_fixed_array()); + } else if (kind == DICTIONARY_ELEMENTS) { + DCHECK(fixed_array->IsFixedArray()); + DCHECK(fixed_array->IsDictionary()); + } else { + DCHECK(kind > DICTIONARY_ELEMENTS); + } + DCHECK(!IsSloppyArgumentsElementsKind(kind) || + (elements()->IsFixedArray() && elements()->length() >= 2)); + } +#endif + return kind; +} + +bool JSObject::HasObjectElements() { + return IsObjectElementsKind(GetElementsKind()); +} + +bool JSObject::HasSmiElements() { return IsSmiElementsKind(GetElementsKind()); } + +bool JSObject::HasSmiOrObjectElements() { + return IsSmiOrObjectElementsKind(GetElementsKind()); +} + +bool JSObject::HasDoubleElements() { + return IsDoubleElementsKind(GetElementsKind()); +} + +bool JSObject::HasHoleyElements() { + return IsHoleyElementsKind(GetElementsKind()); +} + +bool JSObject::HasFastElements() { + return IsFastElementsKind(GetElementsKind()); +} + +bool JSObject::HasFastPackedElements() { + return IsFastPackedElementsKind(GetElementsKind()); +} + +bool JSObject::HasDictionaryElements() { + return GetElementsKind() == DICTIONARY_ELEMENTS; +} + +bool JSObject::HasFastArgumentsElements() { + return GetElementsKind() == FAST_SLOPPY_ARGUMENTS_ELEMENTS; +} + +bool JSObject::HasSlowArgumentsElements() { + return GetElementsKind() == SLOW_SLOPPY_ARGUMENTS_ELEMENTS; +} + +bool JSObject::HasSloppyArgumentsElements() { + return IsSloppyArgumentsElementsKind(GetElementsKind()); +} + +bool JSObject::HasStringWrapperElements() { + return IsStringWrapperElementsKind(GetElementsKind()); +} + +bool JSObject::HasFastStringWrapperElements() { + return GetElementsKind() == FAST_STRING_WRAPPER_ELEMENTS; +} + +bool JSObject::HasSlowStringWrapperElements() { + return GetElementsKind() == SLOW_STRING_WRAPPER_ELEMENTS; +} + +bool JSObject::HasFixedTypedArrayElements() { + DCHECK_NOT_NULL(elements()); + return map()->has_fixed_typed_array_elements(); +} + +#define FIXED_TYPED_ELEMENTS_CHECK(Type, type, TYPE, ctype) \ + bool JSObject::HasFixed##Type##Elements() { \ + HeapObject* array = elements(); \ + DCHECK_NOT_NULL(array); \ + if (!array->IsHeapObject()) return false; \ + return array->map()->instance_type() == FIXED_##TYPE##_ARRAY_TYPE; \ + } + +TYPED_ARRAYS(FIXED_TYPED_ELEMENTS_CHECK) + +#undef FIXED_TYPED_ELEMENTS_CHECK + +bool JSObject::HasNamedInterceptor() { return map()->has_named_interceptor(); } + +bool JSObject::HasIndexedInterceptor() { + return map()->has_indexed_interceptor(); +} + +void JSGlobalObject::set_global_dictionary(GlobalDictionary* dictionary) { + DCHECK(IsJSGlobalObject()); + set_raw_properties_or_hash(dictionary); +} + +GlobalDictionary* JSGlobalObject::global_dictionary() { + DCHECK(!HasFastProperties()); + DCHECK(IsJSGlobalObject()); + return GlobalDictionary::cast(raw_properties_or_hash()); +} + +NumberDictionary* JSObject::element_dictionary() { + DCHECK(HasDictionaryElements() || HasSlowStringWrapperElements()); + return NumberDictionary::cast(elements()); +} + +void JSReceiver::initialize_properties() { + Heap* heap = GetHeap(); + ReadOnlyRoots roots(heap); + DCHECK(!Heap::InNewSpace(roots.empty_fixed_array())); + DCHECK(!Heap::InNewSpace(heap->empty_property_dictionary())); + if (map()->is_dictionary_map()) { + WRITE_FIELD(this, kPropertiesOrHashOffset, + heap->empty_property_dictionary()); + } else { + WRITE_FIELD(this, kPropertiesOrHashOffset, roots.empty_fixed_array()); + } +} + +bool JSReceiver::HasFastProperties() const { + DCHECK( + raw_properties_or_hash()->IsSmi() || + (raw_properties_or_hash()->IsDictionary() == map()->is_dictionary_map())); + return !map()->is_dictionary_map(); +} + +NameDictionary* JSReceiver::property_dictionary() const { + DCHECK(!IsJSGlobalObject()); + DCHECK(!HasFastProperties()); + + Object* prop = raw_properties_or_hash(); + if (prop->IsSmi()) { + return GetHeap()->empty_property_dictionary(); + } + + return NameDictionary::cast(prop); +} + +// TODO(gsathya): Pass isolate directly to this function and access +// the heap from this. +PropertyArray* JSReceiver::property_array() const { + DCHECK(HasFastProperties()); + + Object* prop = raw_properties_or_hash(); + if (prop->IsSmi() || prop == GetReadOnlyRoots().empty_fixed_array()) { + return GetReadOnlyRoots().empty_property_array(); + } + + return PropertyArray::cast(prop); +} + +Maybe<bool> JSReceiver::HasProperty(Handle<JSReceiver> object, + Handle<Name> name) { + LookupIterator it = LookupIterator::PropertyOrElement(object->GetIsolate(), + object, name, object); + return HasProperty(&it); +} + +Maybe<bool> JSReceiver::HasOwnProperty(Handle<JSReceiver> object, + uint32_t index) { + if (object->IsJSModuleNamespace()) return Just(false); + + if (object->IsJSObject()) { // Shortcut. + LookupIterator it(object->GetIsolate(), object, index, object, + LookupIterator::OWN); + return HasProperty(&it); + } + + Maybe<PropertyAttributes> attributes = + JSReceiver::GetOwnPropertyAttributes(object, index); + MAYBE_RETURN(attributes, Nothing<bool>()); + return Just(attributes.FromJust() != ABSENT); +} + +Maybe<PropertyAttributes> JSReceiver::GetPropertyAttributes( + Handle<JSReceiver> object, Handle<Name> name) { + LookupIterator it = LookupIterator::PropertyOrElement(object->GetIsolate(), + object, name, object); + return GetPropertyAttributes(&it); +} + +Maybe<PropertyAttributes> JSReceiver::GetOwnPropertyAttributes( + Handle<JSReceiver> object, Handle<Name> name) { + LookupIterator it = LookupIterator::PropertyOrElement( + object->GetIsolate(), object, name, object, LookupIterator::OWN); + return GetPropertyAttributes(&it); +} + +Maybe<PropertyAttributes> JSReceiver::GetOwnPropertyAttributes( + Handle<JSReceiver> object, uint32_t index) { + LookupIterator it(object->GetIsolate(), object, index, object, + LookupIterator::OWN); + return GetPropertyAttributes(&it); +} + +Maybe<bool> JSReceiver::HasElement(Handle<JSReceiver> object, uint32_t index) { + LookupIterator it(object->GetIsolate(), object, index, object); + return HasProperty(&it); +} + +Maybe<PropertyAttributes> JSReceiver::GetElementAttributes( + Handle<JSReceiver> object, uint32_t index) { + Isolate* isolate = object->GetIsolate(); + LookupIterator it(isolate, object, index, object); + return GetPropertyAttributes(&it); +} + +Maybe<PropertyAttributes> JSReceiver::GetOwnElementAttributes( + Handle<JSReceiver> object, uint32_t index) { + Isolate* isolate = object->GetIsolate(); + LookupIterator it(isolate, object, index, object, LookupIterator::OWN); + return GetPropertyAttributes(&it); +} + +bool JSGlobalObject::IsDetached() { + return JSGlobalProxy::cast(global_proxy())->IsDetachedFrom(this); +} + +bool JSGlobalProxy::IsDetachedFrom(JSGlobalObject* global) const { + const PrototypeIterator iter(this->GetIsolate(), + const_cast<JSGlobalProxy*>(this)); + return iter.GetCurrent() != global; +} + +inline int JSGlobalProxy::SizeWithEmbedderFields(int embedder_field_count) { + DCHECK_GE(embedder_field_count, 0); + return kSize + embedder_field_count * kPointerSize; +} + +ACCESSORS(JSIteratorResult, value, Object, kValueOffset) +ACCESSORS(JSIteratorResult, done, Object, kDoneOffset) + +ACCESSORS(JSAsyncFromSyncIterator, sync_iterator, JSReceiver, + kSyncIteratorOffset) +ACCESSORS(JSAsyncFromSyncIterator, next, Object, kNextOffset) + +ACCESSORS(JSStringIterator, string, String, kStringOffset) +SMI_ACCESSORS(JSStringIterator, index, kNextIndexOffset) + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_JS_OBJECTS_INL_H_ diff --git a/deps/v8/src/objects/js-objects.h b/deps/v8/src/objects/js-objects.h new file mode 100644 index 0000000000..586fe757db --- /dev/null +++ b/deps/v8/src/objects/js-objects.h @@ -0,0 +1,1408 @@ +// Copyright 2018 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. + +#ifndef V8_OBJECTS_JS_OBJECTS_H_ +#define V8_OBJECTS_JS_OBJECTS_H_ + +#include "src/objects.h" +#include "src/objects/property-array.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +class JSGlobalObject; +class JSGlobalProxy; + +// JSReceiver includes types on which properties can be defined, i.e., +// JSObject and JSProxy. +class JSReceiver : public HeapObject, public NeverReadOnlySpaceObject { + public: + // Returns true if there is no slow (ie, dictionary) backing store. + inline bool HasFastProperties() const; + + // Returns the properties array backing store if it + // exists. Otherwise, returns an empty_property_array when there's a + // Smi (hash code) or an empty_fixed_array for a fast properties + // map. + inline PropertyArray* property_array() const; + + // Gets slow properties for non-global objects. + inline NameDictionary* property_dictionary() const; + + // Sets the properties backing store and makes sure any existing hash is moved + // to the new properties store. To clear out the properties store, pass in the + // empty_fixed_array(), the hash will be maintained in this case as well. + void SetProperties(HeapObject* properties); + + // There are five possible values for the properties offset. + // 1) EmptyFixedArray/EmptyPropertyDictionary - This is the standard + // placeholder. + // + // 2) Smi - This is the hash code of the object. + // + // 3) PropertyArray - This is similar to a FixedArray but stores + // the hash code of the object in its length field. This is a fast + // backing store. + // + // 4) NameDictionary - This is the dictionary-mode backing store. + // + // 4) GlobalDictionary - This is the backing store for the + // GlobalObject. + // + // This is used only in the deoptimizer and heap. Please use the + // above typed getters and setters to access the properties. + DECL_ACCESSORS(raw_properties_or_hash, Object) + + inline void initialize_properties(); + + // Deletes an existing named property in a normalized object. + static void DeleteNormalizedProperty(Handle<JSReceiver> object, int entry); + + DECL_CAST(JSReceiver) + + // ES6 section 7.1.1 ToPrimitive + V8_WARN_UNUSED_RESULT static MaybeHandle<Object> ToPrimitive( + Handle<JSReceiver> receiver, + ToPrimitiveHint hint = ToPrimitiveHint::kDefault); + + // ES6 section 7.1.1.1 OrdinaryToPrimitive + V8_WARN_UNUSED_RESULT static MaybeHandle<Object> OrdinaryToPrimitive( + Handle<JSReceiver> receiver, OrdinaryToPrimitiveHint hint); + + static MaybeHandle<Context> GetFunctionRealm(Handle<JSReceiver> receiver); + + // Get the first non-hidden prototype. + static inline MaybeHandle<Object> GetPrototype(Isolate* isolate, + Handle<JSReceiver> receiver); + + V8_WARN_UNUSED_RESULT static Maybe<bool> HasInPrototypeChain( + Isolate* isolate, Handle<JSReceiver> object, Handle<Object> proto); + + // Reads all enumerable own properties of source and adds them to + // target, using either Set or CreateDataProperty depending on the + // use_set argument. This only copies values not present in the + // maybe_excluded_properties list. + V8_WARN_UNUSED_RESULT static Maybe<bool> SetOrCopyDataProperties( + Isolate* isolate, Handle<JSReceiver> target, Handle<Object> source, + const ScopedVector<Handle<Object>>* excluded_properties = nullptr, + bool use_set = true); + + // Implementation of [[HasProperty]], ECMA-262 5th edition, section 8.12.6. + V8_WARN_UNUSED_RESULT static Maybe<bool> HasProperty(LookupIterator* it); + V8_WARN_UNUSED_RESULT static inline Maybe<bool> HasProperty( + Handle<JSReceiver> object, Handle<Name> name); + V8_WARN_UNUSED_RESULT static inline Maybe<bool> HasElement( + Handle<JSReceiver> object, uint32_t index); + + V8_WARN_UNUSED_RESULT static Maybe<bool> HasOwnProperty( + Handle<JSReceiver> object, Handle<Name> name); + V8_WARN_UNUSED_RESULT static inline Maybe<bool> HasOwnProperty( + Handle<JSReceiver> object, uint32_t index); + + V8_WARN_UNUSED_RESULT static inline MaybeHandle<Object> GetProperty( + Isolate* isolate, Handle<JSReceiver> receiver, const char* key); + V8_WARN_UNUSED_RESULT static inline MaybeHandle<Object> GetProperty( + Isolate* isolate, Handle<JSReceiver> receiver, Handle<Name> name); + V8_WARN_UNUSED_RESULT static inline MaybeHandle<Object> GetElement( + Isolate* isolate, Handle<JSReceiver> receiver, uint32_t index); + + // Implementation of ES6 [[Delete]] + V8_WARN_UNUSED_RESULT static Maybe<bool> DeletePropertyOrElement( + Handle<JSReceiver> object, Handle<Name> name, + LanguageMode language_mode = LanguageMode::kSloppy); + V8_WARN_UNUSED_RESULT static Maybe<bool> DeleteProperty( + Handle<JSReceiver> object, Handle<Name> name, + LanguageMode language_mode = LanguageMode::kSloppy); + V8_WARN_UNUSED_RESULT static Maybe<bool> DeleteProperty( + LookupIterator* it, LanguageMode language_mode); + V8_WARN_UNUSED_RESULT static Maybe<bool> DeleteElement( + Handle<JSReceiver> object, uint32_t index, + LanguageMode language_mode = LanguageMode::kSloppy); + + V8_WARN_UNUSED_RESULT static Object* DefineProperty( + Isolate* isolate, Handle<Object> object, Handle<Object> name, + Handle<Object> attributes); + V8_WARN_UNUSED_RESULT static MaybeHandle<Object> DefineProperties( + Isolate* isolate, Handle<Object> object, Handle<Object> properties); + + // "virtual" dispatcher to the correct [[DefineOwnProperty]] implementation. + V8_WARN_UNUSED_RESULT static Maybe<bool> DefineOwnProperty( + Isolate* isolate, Handle<JSReceiver> object, Handle<Object> key, + PropertyDescriptor* desc, ShouldThrow should_throw); + + // ES6 7.3.4 (when passed kDontThrow) + V8_WARN_UNUSED_RESULT static Maybe<bool> CreateDataProperty( + Isolate* isolate, Handle<JSReceiver> object, Handle<Name> key, + Handle<Object> value, ShouldThrow should_throw); + V8_WARN_UNUSED_RESULT static Maybe<bool> CreateDataProperty( + LookupIterator* it, Handle<Object> value, ShouldThrow should_throw); + + // ES6 9.1.6.1 + V8_WARN_UNUSED_RESULT static Maybe<bool> OrdinaryDefineOwnProperty( + Isolate* isolate, Handle<JSObject> object, Handle<Object> key, + PropertyDescriptor* desc, ShouldThrow should_throw); + V8_WARN_UNUSED_RESULT static Maybe<bool> OrdinaryDefineOwnProperty( + LookupIterator* it, PropertyDescriptor* desc, ShouldThrow should_throw); + // ES6 9.1.6.2 + V8_WARN_UNUSED_RESULT static Maybe<bool> IsCompatiblePropertyDescriptor( + Isolate* isolate, bool extensible, PropertyDescriptor* desc, + PropertyDescriptor* current, Handle<Name> property_name, + ShouldThrow should_throw); + // ES6 9.1.6.3 + // |it| can be NULL in cases where the ES spec passes |undefined| as the + // receiver. Exactly one of |it| and |property_name| must be provided. + V8_WARN_UNUSED_RESULT static Maybe<bool> ValidateAndApplyPropertyDescriptor( + Isolate* isolate, LookupIterator* it, bool extensible, + PropertyDescriptor* desc, PropertyDescriptor* current, + ShouldThrow should_throw, Handle<Name> property_name); + + V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT static Maybe<bool> + GetOwnPropertyDescriptor(Isolate* isolate, Handle<JSReceiver> object, + Handle<Object> key, PropertyDescriptor* desc); + V8_WARN_UNUSED_RESULT static Maybe<bool> GetOwnPropertyDescriptor( + LookupIterator* it, PropertyDescriptor* desc); + + typedef PropertyAttributes IntegrityLevel; + + // ES6 7.3.14 (when passed kDontThrow) + // 'level' must be SEALED or FROZEN. + V8_WARN_UNUSED_RESULT static Maybe<bool> SetIntegrityLevel( + Handle<JSReceiver> object, IntegrityLevel lvl, ShouldThrow should_throw); + + // ES6 7.3.15 + // 'level' must be SEALED or FROZEN. + V8_WARN_UNUSED_RESULT static Maybe<bool> TestIntegrityLevel( + Handle<JSReceiver> object, IntegrityLevel lvl); + + // ES6 [[PreventExtensions]] (when passed kDontThrow) + V8_WARN_UNUSED_RESULT static Maybe<bool> PreventExtensions( + Handle<JSReceiver> object, ShouldThrow should_throw); + + V8_WARN_UNUSED_RESULT static Maybe<bool> IsExtensible( + Handle<JSReceiver> object); + + // Returns the class name ([[Class]] property in the specification). + V8_EXPORT_PRIVATE String* class_name(); + + // Returns the constructor (the function that was used to instantiate the + // object). + static MaybeHandle<JSFunction> GetConstructor(Handle<JSReceiver> receiver); + + // Returns the constructor name (the name (possibly, inferred name) of the + // function that was used to instantiate the object). + static Handle<String> GetConstructorName(Handle<JSReceiver> receiver); + + Handle<Context> GetCreationContext(); + + V8_WARN_UNUSED_RESULT static inline Maybe<PropertyAttributes> + GetPropertyAttributes(Handle<JSReceiver> object, Handle<Name> name); + V8_WARN_UNUSED_RESULT static inline Maybe<PropertyAttributes> + GetOwnPropertyAttributes(Handle<JSReceiver> object, Handle<Name> name); + V8_WARN_UNUSED_RESULT static inline Maybe<PropertyAttributes> + GetOwnPropertyAttributes(Handle<JSReceiver> object, uint32_t index); + + V8_WARN_UNUSED_RESULT static inline Maybe<PropertyAttributes> + GetElementAttributes(Handle<JSReceiver> object, uint32_t index); + V8_WARN_UNUSED_RESULT static inline Maybe<PropertyAttributes> + GetOwnElementAttributes(Handle<JSReceiver> object, uint32_t index); + + V8_WARN_UNUSED_RESULT static Maybe<PropertyAttributes> GetPropertyAttributes( + LookupIterator* it); + + // Set the object's prototype (only JSReceiver and null are allowed values). + V8_WARN_UNUSED_RESULT static Maybe<bool> SetPrototype( + Handle<JSReceiver> object, Handle<Object> value, bool from_javascript, + ShouldThrow should_throw); + + inline static Handle<Object> GetDataProperty(Handle<JSReceiver> object, + Handle<Name> name); + static Handle<Object> GetDataProperty(LookupIterator* it); + + // Retrieves a permanent object identity hash code. The undefined value might + // be returned in case no hash was created yet. + Object* GetIdentityHash(Isolate* isolate); + + // Retrieves a permanent object identity hash code. May create and store a + // hash code if needed and none exists. + static Smi* CreateIdentityHash(Isolate* isolate, JSReceiver* key); + Smi* GetOrCreateIdentityHash(Isolate* isolate); + + // Stores the hash code. The hash passed in must be masked with + // JSReceiver::kHashMask. + void SetIdentityHash(int masked_hash); + + // ES6 [[OwnPropertyKeys]] (modulo return type) + V8_WARN_UNUSED_RESULT static inline MaybeHandle<FixedArray> OwnPropertyKeys( + Handle<JSReceiver> object); + + V8_WARN_UNUSED_RESULT static MaybeHandle<FixedArray> GetOwnValues( + Handle<JSReceiver> object, PropertyFilter filter, + bool try_fast_path = true); + + V8_WARN_UNUSED_RESULT static MaybeHandle<FixedArray> GetOwnEntries( + Handle<JSReceiver> object, PropertyFilter filter, + bool try_fast_path = true); + + V8_WARN_UNUSED_RESULT static Handle<FixedArray> GetOwnElementIndices( + Isolate* isolate, Handle<JSReceiver> receiver, Handle<JSObject> object); + + static const int kHashMask = PropertyArray::HashField::kMask; + + // Layout description. + static const int kPropertiesOrHashOffset = HeapObject::kHeaderSize; + static const int kHeaderSize = HeapObject::kHeaderSize + kPointerSize; + + bool HasProxyInPrototype(Isolate* isolate); + + bool HasComplexElements(); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSReceiver); +}; + +// The JSObject describes real heap allocated JavaScript objects with +// properties. +// Note that the map of JSObject changes during execution to enable inline +// caching. +class JSObject : public JSReceiver { + public: + static bool IsUnmodifiedApiObject(Object** o); + + static V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> New( + Handle<JSFunction> constructor, Handle<JSReceiver> new_target, + Handle<AllocationSite> site); + + static MaybeHandle<Context> GetFunctionRealm(Handle<JSObject> object); + + // 9.1.12 ObjectCreate ( proto [ , internalSlotsList ] ) + // Notice: This is NOT 19.1.2.2 Object.create ( O, Properties ) + static V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> ObjectCreate( + Isolate* isolate, Handle<Object> prototype); + + // [elements]: The elements (properties with names that are integers). + // + // Elements can be in two general modes: fast and slow. Each mode + // corresponds to a set of object representations of elements that + // have something in common. + // + // In the fast mode elements is a FixedArray and so each element can + // be quickly accessed. This fact is used in the generated code. The + // elements array can have one of three maps in this mode: + // fixed_array_map, sloppy_arguments_elements_map or + // fixed_cow_array_map (for copy-on-write arrays). In the latter case + // the elements array may be shared by a few objects and so before + // writing to any element the array must be copied. Use + // EnsureWritableFastElements in this case. + // + // In the slow mode the elements is either a NumberDictionary, a + // FixedArray parameter map for a (sloppy) arguments object. + DECL_ACCESSORS(elements, FixedArrayBase) + inline void initialize_elements(); + static inline void SetMapAndElements(Handle<JSObject> object, Handle<Map> map, + Handle<FixedArrayBase> elements); + inline ElementsKind GetElementsKind() const; + ElementsAccessor* GetElementsAccessor(); + // Returns true if an object has elements of PACKED_SMI_ELEMENTS or + // HOLEY_SMI_ELEMENTS ElementsKind. + inline bool HasSmiElements(); + // Returns true if an object has elements of PACKED_ELEMENTS or + // HOLEY_ELEMENTS ElementsKind. + inline bool HasObjectElements(); + // Returns true if an object has elements of PACKED_SMI_ELEMENTS, + // HOLEY_SMI_ELEMENTS, PACKED_ELEMENTS, or HOLEY_ELEMENTS. + inline bool HasSmiOrObjectElements(); + // Returns true if an object has any of the "fast" elements kinds. + inline bool HasFastElements(); + // Returns true if an object has any of the PACKED elements kinds. + inline bool HasFastPackedElements(); + // Returns true if an object has elements of PACKED_DOUBLE_ELEMENTS or + // HOLEY_DOUBLE_ELEMENTS ElementsKind. + inline bool HasDoubleElements(); + // Returns true if an object has elements of HOLEY_SMI_ELEMENTS, + // HOLEY_DOUBLE_ELEMENTS, or HOLEY_ELEMENTS ElementsKind. + inline bool HasHoleyElements(); + inline bool HasSloppyArgumentsElements(); + inline bool HasStringWrapperElements(); + inline bool HasDictionaryElements(); + + inline bool HasFixedTypedArrayElements(); + + inline bool HasFixedUint8ClampedElements(); + inline bool HasFixedArrayElements(); + inline bool HasFixedInt8Elements(); + inline bool HasFixedUint8Elements(); + inline bool HasFixedInt16Elements(); + inline bool HasFixedUint16Elements(); + inline bool HasFixedInt32Elements(); + inline bool HasFixedUint32Elements(); + inline bool HasFixedFloat32Elements(); + inline bool HasFixedFloat64Elements(); + inline bool HasFixedBigInt64Elements(); + inline bool HasFixedBigUint64Elements(); + + inline bool HasFastArgumentsElements(); + inline bool HasSlowArgumentsElements(); + inline bool HasFastStringWrapperElements(); + inline bool HasSlowStringWrapperElements(); + bool HasEnumerableElements(); + + inline NumberDictionary* element_dictionary(); // Gets slow elements. + + // Requires: HasFastElements(). + static void EnsureWritableFastElements(Handle<JSObject> object); + + V8_WARN_UNUSED_RESULT static Maybe<bool> SetPropertyWithInterceptor( + LookupIterator* it, ShouldThrow should_throw, Handle<Object> value); + + // The API currently still wants DefineOwnPropertyIgnoreAttributes to convert + // AccessorInfo objects to data fields. We allow FORCE_FIELD as an exception + // to the default behavior that calls the setter. + enum AccessorInfoHandling { FORCE_FIELD, DONT_FORCE_FIELD }; + + V8_WARN_UNUSED_RESULT static MaybeHandle<Object> + DefineOwnPropertyIgnoreAttributes( + LookupIterator* it, Handle<Object> value, PropertyAttributes attributes, + AccessorInfoHandling handling = DONT_FORCE_FIELD); + + V8_WARN_UNUSED_RESULT static Maybe<bool> DefineOwnPropertyIgnoreAttributes( + LookupIterator* it, Handle<Object> value, PropertyAttributes attributes, + ShouldThrow should_throw, + AccessorInfoHandling handling = DONT_FORCE_FIELD); + + V8_WARN_UNUSED_RESULT static MaybeHandle<Object> + SetOwnPropertyIgnoreAttributes(Handle<JSObject> object, Handle<Name> name, + Handle<Object> value, + PropertyAttributes attributes); + + V8_WARN_UNUSED_RESULT static MaybeHandle<Object> + SetOwnElementIgnoreAttributes(Handle<JSObject> object, uint32_t index, + Handle<Object> value, + PropertyAttributes attributes); + + // Equivalent to one of the above depending on whether |name| can be converted + // to an array index. + V8_WARN_UNUSED_RESULT static MaybeHandle<Object> + DefinePropertyOrElementIgnoreAttributes(Handle<JSObject> object, + Handle<Name> name, + Handle<Object> value, + PropertyAttributes attributes = NONE); + + // Adds or reconfigures a property to attributes NONE. It will fail when it + // cannot. + V8_WARN_UNUSED_RESULT static Maybe<bool> CreateDataProperty( + LookupIterator* it, Handle<Object> value, + ShouldThrow should_throw = kDontThrow); + + static void AddProperty(Isolate* isolate, Handle<JSObject> object, + Handle<Name> name, Handle<Object> value, + PropertyAttributes attributes); + + static void AddDataElement(Handle<JSObject> receiver, uint32_t index, + Handle<Object> value, + PropertyAttributes attributes); + + // Extend the receiver with a single fast property appeared first in the + // passed map. This also extends the property backing store if necessary. + static void AllocateStorageForMap(Handle<JSObject> object, Handle<Map> map); + + // Migrates the given object to a map whose field representations are the + // lowest upper bound of all known representations for that field. + static void MigrateInstance(Handle<JSObject> instance); + + // Migrates the given object only if the target map is already available, + // or returns false if such a map is not yet available. + static bool TryMigrateInstance(Handle<JSObject> instance); + + // Sets the property value in a normalized object given (key, value, details). + // Handles the special representation of JS global objects. + static void SetNormalizedProperty(Handle<JSObject> object, Handle<Name> name, + Handle<Object> value, + PropertyDetails details); + static void SetDictionaryElement(Handle<JSObject> object, uint32_t index, + Handle<Object> value, + PropertyAttributes attributes); + static void SetDictionaryArgumentsElement(Handle<JSObject> object, + uint32_t index, + Handle<Object> value, + PropertyAttributes attributes); + + static void OptimizeAsPrototype(Handle<JSObject> object, + bool enable_setup_mode = true); + static void ReoptimizeIfPrototype(Handle<JSObject> object); + static void MakePrototypesFast(Handle<Object> receiver, + WhereToStart where_to_start, Isolate* isolate); + static void LazyRegisterPrototypeUser(Handle<Map> user, Isolate* isolate); + static void UpdatePrototypeUserRegistration(Handle<Map> old_map, + Handle<Map> new_map, + Isolate* isolate); + static bool UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate); + static Map* InvalidatePrototypeChains(Map* map); + static void InvalidatePrototypeValidityCell(JSGlobalObject* global); + + // Updates prototype chain tracking information when an object changes its + // map from |old_map| to |new_map|. + static void NotifyMapChange(Handle<Map> old_map, Handle<Map> new_map, + Isolate* isolate); + + // Utility used by many Array builtins and runtime functions + static inline bool PrototypeHasNoElements(Isolate* isolate, JSObject* object); + + // To be passed to PrototypeUsers::Compact. + static void PrototypeRegistryCompactionCallback(HeapObject* value, + int old_index, int new_index); + + // Retrieve interceptors. + inline InterceptorInfo* GetNamedInterceptor(); + inline InterceptorInfo* GetIndexedInterceptor(); + + // Used from JSReceiver. + V8_WARN_UNUSED_RESULT static Maybe<PropertyAttributes> + GetPropertyAttributesWithInterceptor(LookupIterator* it); + V8_WARN_UNUSED_RESULT static Maybe<PropertyAttributes> + GetPropertyAttributesWithFailedAccessCheck(LookupIterator* it); + + // Defines an AccessorPair property on the given object. + // TODO(mstarzinger): Rename to SetAccessor(). + static MaybeHandle<Object> DefineAccessor(Handle<JSObject> object, + Handle<Name> name, + Handle<Object> getter, + Handle<Object> setter, + PropertyAttributes attributes); + static MaybeHandle<Object> DefineAccessor(LookupIterator* it, + Handle<Object> getter, + Handle<Object> setter, + PropertyAttributes attributes); + + // Defines an AccessorInfo property on the given object. + V8_WARN_UNUSED_RESULT static MaybeHandle<Object> SetAccessor( + Handle<JSObject> object, Handle<Name> name, Handle<AccessorInfo> info, + PropertyAttributes attributes); + + // The result must be checked first for exceptions. If there's no exception, + // the output parameter |done| indicates whether the interceptor has a result + // or not. + V8_WARN_UNUSED_RESULT static MaybeHandle<Object> GetPropertyWithInterceptor( + LookupIterator* it, bool* done); + + static void ValidateElements(JSObject* object); + + // Makes sure that this object can contain HeapObject as elements. + static inline void EnsureCanContainHeapObjectElements(Handle<JSObject> obj); + + // Makes sure that this object can contain the specified elements. + static inline void EnsureCanContainElements(Handle<JSObject> object, + Object** elements, uint32_t count, + EnsureElementsMode mode); + static inline void EnsureCanContainElements(Handle<JSObject> object, + Handle<FixedArrayBase> elements, + uint32_t length, + EnsureElementsMode mode); + static void EnsureCanContainElements(Handle<JSObject> object, + Arguments* arguments, uint32_t first_arg, + uint32_t arg_count, + EnsureElementsMode mode); + + // Would we convert a fast elements array to dictionary mode given + // an access at key? + bool WouldConvertToSlowElements(uint32_t index); + + static const uint32_t kMinAddedElementsCapacity = 16; + + // Computes the new capacity when expanding the elements of a JSObject. + static uint32_t NewElementsCapacity(uint32_t old_capacity) { + // (old_capacity + 50%) + kMinAddedElementsCapacity + return old_capacity + (old_capacity >> 1) + kMinAddedElementsCapacity; + } + + // These methods do not perform access checks! + template <AllocationSiteUpdateMode update_or_check = + AllocationSiteUpdateMode::kUpdate> + static bool UpdateAllocationSite(Handle<JSObject> object, + ElementsKind to_kind); + + // Lookup interceptors are used for handling properties controlled by host + // objects. + inline bool HasNamedInterceptor(); + inline bool HasIndexedInterceptor(); + + // Support functions for v8 api (needed for correct interceptor behavior). + V8_WARN_UNUSED_RESULT static Maybe<bool> HasRealNamedProperty( + Handle<JSObject> object, Handle<Name> name); + V8_WARN_UNUSED_RESULT static Maybe<bool> HasRealElementProperty( + Handle<JSObject> object, uint32_t index); + V8_WARN_UNUSED_RESULT static Maybe<bool> HasRealNamedCallbackProperty( + Handle<JSObject> object, Handle<Name> name); + + // Get the header size for a JSObject. Used to compute the index of + // embedder fields as well as the number of embedder fields. + // The |function_has_prototype_slot| parameter is needed only for + // JSFunction objects. + static int GetHeaderSize(InstanceType instance_type, + bool function_has_prototype_slot = false); + static inline int GetHeaderSize(const Map* map); + inline int GetHeaderSize() const; + + static inline int GetEmbedderFieldCount(const Map* map); + inline int GetEmbedderFieldCount() const; + inline int GetEmbedderFieldOffset(int index); + inline Object* GetEmbedderField(int index); + inline void SetEmbedderField(int index, Object* value); + inline void SetEmbedderField(int index, Smi* value); + + // Returns true when the object is potentially a wrapper that gets special + // garbage collection treatment. + // TODO(mlippautz): Make check exact and replace the pattern match in + // Heap::TracePossibleWrapper. + bool IsApiWrapper(); + + // Same as IsApiWrapper() but also allow dropping the wrapper on minor GCs. + bool IsDroppableApiWrapper(); + + // Returns a new map with all transitions dropped from the object's current + // map and the ElementsKind set. + static Handle<Map> GetElementsTransitionMap(Handle<JSObject> object, + ElementsKind to_kind); + static void TransitionElementsKind(Handle<JSObject> object, + ElementsKind to_kind); + + // Always use this to migrate an object to a new map. + // |expected_additional_properties| is only used for fast-to-slow transitions + // and ignored otherwise. + static void MigrateToMap(Handle<JSObject> object, Handle<Map> new_map, + int expected_additional_properties = 0); + + // Forces a prototype without any of the checks that the regular SetPrototype + // would do. + static void ForceSetPrototype(Handle<JSObject> object, Handle<Object> proto); + + // Convert the object to use the canonical dictionary + // representation. If the object is expected to have additional properties + // added this number can be indicated to have the backing store allocated to + // an initial capacity for holding these properties. + static void NormalizeProperties(Handle<JSObject> object, + PropertyNormalizationMode mode, + int expected_additional_properties, + const char* reason); + + // Convert and update the elements backing store to be a + // NumberDictionary dictionary. Returns the backing after conversion. + static Handle<NumberDictionary> NormalizeElements(Handle<JSObject> object); + + void RequireSlowElements(NumberDictionary* dictionary); + + // Transform slow named properties to fast variants. + static void MigrateSlowToFast(Handle<JSObject> object, + int unused_property_fields, const char* reason); + + inline bool IsUnboxedDoubleField(FieldIndex index); + + // Access fast-case object properties at index. + static Handle<Object> FastPropertyAt(Handle<JSObject> object, + Representation representation, + FieldIndex index); + inline Object* RawFastPropertyAt(FieldIndex index); + inline double RawFastDoublePropertyAt(FieldIndex index); + inline uint64_t RawFastDoublePropertyAsBitsAt(FieldIndex index); + + inline void FastPropertyAtPut(FieldIndex index, Object* value); + inline void RawFastPropertyAtPut(FieldIndex index, Object* value); + inline void RawFastDoublePropertyAsBitsAtPut(FieldIndex index, uint64_t bits); + inline void WriteToField(int descriptor, PropertyDetails details, + Object* value); + + // Access to in object properties. + inline int GetInObjectPropertyOffset(int index); + inline Object* InObjectPropertyAt(int index); + inline Object* InObjectPropertyAtPut( + int index, Object* value, WriteBarrierMode mode = UPDATE_WRITE_BARRIER); + + // Set the object's prototype (only JSReceiver and null are allowed values). + V8_WARN_UNUSED_RESULT static Maybe<bool> SetPrototype( + Handle<JSObject> object, Handle<Object> value, bool from_javascript, + ShouldThrow should_throw); + + // Makes the object prototype immutable + // Never called from JavaScript + static void SetImmutableProto(Handle<JSObject> object); + + // Initializes the body starting at |start_offset|. It is responsibility of + // the caller to initialize object header. Fill the pre-allocated fields with + // pre_allocated_value and the rest with filler_value. + // Note: this call does not update write barrier, the caller is responsible + // to ensure that |filler_value| can be collected without WB here. + inline void InitializeBody(Map* map, int start_offset, + Object* pre_allocated_value, Object* filler_value); + + // Check whether this object references another object + bool ReferencesObject(Object* obj); + + V8_WARN_UNUSED_RESULT static Maybe<bool> TestIntegrityLevel( + Handle<JSObject> object, IntegrityLevel lvl); + + V8_WARN_UNUSED_RESULT static Maybe<bool> PreventExtensions( + Handle<JSObject> object, ShouldThrow should_throw); + + static bool IsExtensible(Handle<JSObject> object); + + DECL_CAST(JSObject) + + // Dispatched behavior. + void JSObjectShortPrint(StringStream* accumulator); + DECL_PRINTER(JSObject) + DECL_VERIFIER(JSObject) +#ifdef OBJECT_PRINT + bool PrintProperties(std::ostream& os); // NOLINT + void PrintElements(std::ostream& os); // NOLINT +#endif +#if defined(DEBUG) || defined(OBJECT_PRINT) + void PrintTransitions(std::ostream& os); // NOLINT +#endif + + static void PrintElementsTransition(FILE* file, Handle<JSObject> object, + ElementsKind from_kind, + Handle<FixedArrayBase> from_elements, + ElementsKind to_kind, + Handle<FixedArrayBase> to_elements); + + void PrintInstanceMigration(FILE* file, Map* original_map, Map* new_map); + +#ifdef DEBUG + // Structure for collecting spill information about JSObjects. + class SpillInformation { + public: + void Clear(); + void Print(); + int number_of_objects_; + int number_of_objects_with_fast_properties_; + int number_of_objects_with_fast_elements_; + int number_of_fast_used_fields_; + int number_of_fast_unused_fields_; + int number_of_slow_used_properties_; + int number_of_slow_unused_properties_; + int number_of_fast_used_elements_; + int number_of_fast_unused_elements_; + int number_of_slow_used_elements_; + int number_of_slow_unused_elements_; + }; + + void IncrementSpillStatistics(Isolate* isolate, SpillInformation* info); +#endif + +#ifdef VERIFY_HEAP + // If a GC was caused while constructing this object, the elements pointer + // may point to a one pointer filler map. The object won't be rooted, but + // our heap verification code could stumble across it. + bool ElementsAreSafeToExamine() const; +#endif + + Object* SlowReverseLookup(Object* value); + + // Maximal number of elements (numbered 0 .. kMaxElementCount - 1). + // Also maximal value of JSArray's length property. + static const uint32_t kMaxElementCount = 0xffffffffu; + + // Constants for heuristics controlling conversion of fast elements + // to slow elements. + + // Maximal gap that can be introduced by adding an element beyond + // the current elements length. + static const uint32_t kMaxGap = 1024; + + // Maximal length of fast elements array that won't be checked for + // being dense enough on expansion. + static const int kMaxUncheckedFastElementsLength = 5000; + + // Same as above but for old arrays. This limit is more strict. We + // don't want to be wasteful with long lived objects. + static const int kMaxUncheckedOldFastElementsLength = 500; + + // This constant applies only to the initial map of "global.Object" and + // not to arbitrary other JSObject maps. + static const int kInitialGlobalObjectUnusedPropertiesCount = 4; + + static const int kMaxInstanceSize = 255 * kPointerSize; + + // When extending the backing storage for property values, we increase + // its size by more than the 1 entry necessary, so sequentially adding fields + // to the same object requires fewer allocations and copies. + static const int kFieldsAdded = 3; + STATIC_ASSERT(kMaxNumberOfDescriptors + kFieldsAdded <= + PropertyArray::kMaxLength); + + // Layout description. + static const int kElementsOffset = JSReceiver::kHeaderSize; + static const int kHeaderSize = kElementsOffset + kPointerSize; + + STATIC_ASSERT(kHeaderSize == Internals::kJSObjectHeaderSize); + static const int kMaxInObjectProperties = + (kMaxInstanceSize - kHeaderSize) >> kPointerSizeLog2; + STATIC_ASSERT(kMaxInObjectProperties <= kMaxNumberOfDescriptors); + // TODO(cbruni): Revisit calculation of the max supported embedder fields. + static const int kMaxEmbedderFields = + ((1 << kFirstInobjectPropertyOffsetBitCount) - 1 - kHeaderSize) >> + kPointerSizeLog2; + STATIC_ASSERT(kMaxEmbedderFields <= kMaxInObjectProperties); + + class BodyDescriptor; + + class FastBodyDescriptor; + + // Gets the number of currently used elements. + int GetFastElementsUsage(); + + static bool AllCanRead(LookupIterator* it); + static bool AllCanWrite(LookupIterator* it); + + private: + friend class JSReceiver; + friend class Object; + + // Used from Object::GetProperty(). + V8_WARN_UNUSED_RESULT static MaybeHandle<Object> + GetPropertyWithFailedAccessCheck(LookupIterator* it); + + V8_WARN_UNUSED_RESULT static Maybe<bool> SetPropertyWithFailedAccessCheck( + LookupIterator* it, Handle<Object> value, ShouldThrow should_throw); + + V8_WARN_UNUSED_RESULT static Maybe<bool> DeletePropertyWithInterceptor( + LookupIterator* it, ShouldThrow should_throw); + + bool ReferencesObjectFromElements(FixedArray* elements, ElementsKind kind, + Object* object); + + // Helper for fast versions of preventExtensions, seal, and freeze. + // attrs is one of NONE, SEALED, or FROZEN (depending on the operation). + template <PropertyAttributes attrs> + V8_WARN_UNUSED_RESULT static Maybe<bool> PreventExtensionsWithTransition( + Handle<JSObject> object, ShouldThrow should_throw); + + DISALLOW_IMPLICIT_CONSTRUCTORS(JSObject); +}; + +// JSAccessorPropertyDescriptor is just a JSObject with a specific initial +// map. This initial map adds in-object properties for "get", "set", +// "enumerable" and "configurable" properties, as assigned by the +// FromPropertyDescriptor function for regular accessor properties. +class JSAccessorPropertyDescriptor : public JSObject { + public: + // Offsets of object fields. + static const int kGetOffset = JSObject::kHeaderSize; + static const int kSetOffset = kGetOffset + kPointerSize; + static const int kEnumerableOffset = kSetOffset + kPointerSize; + static const int kConfigurableOffset = kEnumerableOffset + kPointerSize; + static const int kSize = kConfigurableOffset + kPointerSize; + // Indices of in-object properties. + static const int kGetIndex = 0; + static const int kSetIndex = 1; + static const int kEnumerableIndex = 2; + static const int kConfigurableIndex = 3; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSAccessorPropertyDescriptor); +}; + +// JSDataPropertyDescriptor is just a JSObject with a specific initial map. +// This initial map adds in-object properties for "value", "writable", +// "enumerable" and "configurable" properties, as assigned by the +// FromPropertyDescriptor function for regular data properties. +class JSDataPropertyDescriptor : public JSObject { + public: + // Offsets of object fields. + static const int kValueOffset = JSObject::kHeaderSize; + static const int kWritableOffset = kValueOffset + kPointerSize; + static const int kEnumerableOffset = kWritableOffset + kPointerSize; + static const int kConfigurableOffset = kEnumerableOffset + kPointerSize; + static const int kSize = kConfigurableOffset + kPointerSize; + // Indices of in-object properties. + static const int kValueIndex = 0; + static const int kWritableIndex = 1; + static const int kEnumerableIndex = 2; + static const int kConfigurableIndex = 3; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSDataPropertyDescriptor); +}; + +// JSIteratorResult is just a JSObject with a specific initial map. +// This initial map adds in-object properties for "done" and "value", +// as specified by ES6 section 25.1.1.3 The IteratorResult Interface +class JSIteratorResult : public JSObject { + public: + DECL_ACCESSORS(value, Object) + + DECL_ACCESSORS(done, Object) + + // Offsets of object fields. + static const int kValueOffset = JSObject::kHeaderSize; + static const int kDoneOffset = kValueOffset + kPointerSize; + static const int kSize = kDoneOffset + kPointerSize; + // Indices of in-object properties. + static const int kValueIndex = 0; + static const int kDoneIndex = 1; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSIteratorResult); +}; + +// JSBoundFunction describes a bound function exotic object. +class JSBoundFunction : public JSObject { + public: + // [bound_target_function]: The wrapped function object. + inline Object* raw_bound_target_function() const; + DECL_ACCESSORS(bound_target_function, JSReceiver) + + // [bound_this]: The value that is always passed as the this value when + // calling the wrapped function. + DECL_ACCESSORS(bound_this, Object) + + // [bound_arguments]: A list of values whose elements are used as the first + // arguments to any call to the wrapped function. + DECL_ACCESSORS(bound_arguments, FixedArray) + + static MaybeHandle<String> GetName(Isolate* isolate, + Handle<JSBoundFunction> function); + static Maybe<int> GetLength(Isolate* isolate, + Handle<JSBoundFunction> function); + static MaybeHandle<Context> GetFunctionRealm( + Handle<JSBoundFunction> function); + + DECL_CAST(JSBoundFunction) + + // Dispatched behavior. + DECL_PRINTER(JSBoundFunction) + DECL_VERIFIER(JSBoundFunction) + + // The bound function's string representation implemented according + // to ES6 section 19.2.3.5 Function.prototype.toString ( ). + static Handle<String> ToString(Handle<JSBoundFunction> function); + + // Layout description. + static const int kBoundTargetFunctionOffset = JSObject::kHeaderSize; + static const int kBoundThisOffset = kBoundTargetFunctionOffset + kPointerSize; + static const int kBoundArgumentsOffset = kBoundThisOffset + kPointerSize; + static const int kSize = kBoundArgumentsOffset + kPointerSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSBoundFunction); +}; + +// JSFunction describes JavaScript functions. +class JSFunction : public JSObject { + public: + // [prototype_or_initial_map]: + DECL_ACCESSORS(prototype_or_initial_map, Object) + + // [shared]: The information about the function that + // can be shared by instances. + DECL_ACCESSORS(shared, SharedFunctionInfo) + + static const int kLengthDescriptorIndex = 0; + static const int kNameDescriptorIndex = 1; + // Home object descriptor index when function has a [[HomeObject]] slot. + static const int kMaybeHomeObjectDescriptorIndex = 2; + + // [context]: The context for this function. + inline Context* context(); + inline bool has_context() const; + inline void set_context(Object* context); + inline JSGlobalProxy* global_proxy(); + inline Context* native_context(); + + static Handle<Object> GetName(Isolate* isolate, Handle<JSFunction> function); + static Maybe<int> GetLength(Isolate* isolate, Handle<JSFunction> function); + static Handle<Context> GetFunctionRealm(Handle<JSFunction> function); + + // [code]: The generated code object for this function. Executed + // when the function is invoked, e.g. foo() or new foo(). See + // [[Call]] and [[Construct]] description in ECMA-262, section + // 8.6.2, page 27. + inline Code* code(); + inline void set_code(Code* code); + inline void set_code_no_write_barrier(Code* code); + + // Get the abstract code associated with the function, which will either be + // a Code object or a BytecodeArray. + inline AbstractCode* abstract_code(); + + // Tells whether or not this function is interpreted. + // + // Note: function->IsInterpreted() does not necessarily return the same value + // as function->shared()->IsInterpreted() because the closure might have been + // optimized. + inline bool IsInterpreted(); + + // Tells whether or not this function checks its optimization marker in its + // feedback vector. + inline bool ChecksOptimizationMarker(); + + // Tells whether or not this function holds optimized code. + // + // Note: Returning false does not necessarily mean that this function hasn't + // been optimized, as it may have optimized code on its feedback vector. + inline bool IsOptimized(); + + // Tells whether or not this function has optimized code available to it, + // either because it is optimized or because it has optimized code in its + // feedback vector. + inline bool HasOptimizedCode(); + + // Tells whether or not this function has a (non-zero) optimization marker. + inline bool HasOptimizationMarker(); + + // Mark this function for lazy recompilation. The function will be recompiled + // the next time it is executed. + void MarkForOptimization(ConcurrencyMode mode); + + // Tells whether or not the function is already marked for lazy recompilation. + inline bool IsMarkedForOptimization(); + inline bool IsMarkedForConcurrentOptimization(); + + // Tells whether or not the function is on the concurrent recompilation queue. + inline bool IsInOptimizationQueue(); + + // Clears the optimized code slot in the function's feedback vector. + inline void ClearOptimizedCodeSlot(const char* reason); + + // Sets the optimization marker in the function's feedback vector. + inline void SetOptimizationMarker(OptimizationMarker marker); + + // Clears the optimization marker in the function's feedback vector. + inline void ClearOptimizationMarker(); + + // If slack tracking is active, it computes instance size of the initial map + // with minimum permissible object slack. If it is not active, it simply + // returns the initial map's instance size. + int ComputeInstanceSizeWithMinSlack(Isolate* isolate); + + // Completes inobject slack tracking on initial map if it is active. + inline void CompleteInobjectSlackTrackingIfActive(); + + // [feedback_cell]: The FeedbackCell used to hold the FeedbackVector + // eventually. + DECL_ACCESSORS(feedback_cell, FeedbackCell) + + // feedback_vector() can be used once the function is compiled. + inline FeedbackVector* feedback_vector() const; + inline bool has_feedback_vector() const; + static void EnsureFeedbackVector(Handle<JSFunction> function); + + // Unconditionally clear the type feedback vector. + void ClearTypeFeedbackInfo(); + + inline bool has_prototype_slot() const; + + // The initial map for an object created by this constructor. + inline Map* initial_map(); + static void SetInitialMap(Handle<JSFunction> function, Handle<Map> map, + Handle<Object> prototype); + inline bool has_initial_map(); + static void EnsureHasInitialMap(Handle<JSFunction> function); + + // Creates a map that matches the constructor's initial map, but with + // [[prototype]] being new.target.prototype. Because new.target can be a + // JSProxy, this can call back into JavaScript. + static V8_WARN_UNUSED_RESULT MaybeHandle<Map> GetDerivedMap( + Isolate* isolate, Handle<JSFunction> constructor, + Handle<JSReceiver> new_target); + + // Get and set the prototype property on a JSFunction. If the + // function has an initial map the prototype is set on the initial + // map. Otherwise, the prototype is put in the initial map field + // until an initial map is needed. + inline bool has_prototype(); + inline bool has_instance_prototype(); + inline Object* prototype(); + inline Object* instance_prototype(); + inline bool has_prototype_property(); + inline bool PrototypeRequiresRuntimeLookup(); + static void SetPrototype(Handle<JSFunction> function, Handle<Object> value); + + // Returns if this function has been compiled to native code yet. + inline bool is_compiled(); + + static int GetHeaderSize(bool function_has_prototype_slot) { + return function_has_prototype_slot ? JSFunction::kSizeWithPrototype + : JSFunction::kSizeWithoutPrototype; + } + + // Prints the name of the function using PrintF. + void PrintName(FILE* out = stdout); + + DECL_CAST(JSFunction) + + // Calculate the instance size and in-object properties count. + static bool CalculateInstanceSizeForDerivedClass( + Handle<JSFunction> function, InstanceType instance_type, + int requested_embedder_fields, int* instance_size, + int* in_object_properties); + static void CalculateInstanceSizeHelper(InstanceType instance_type, + bool has_prototype_slot, + int requested_embedder_fields, + int requested_in_object_properties, + int* instance_size, + int* in_object_properties); + + class BodyDescriptor; + + // Dispatched behavior. + DECL_PRINTER(JSFunction) + DECL_VERIFIER(JSFunction) + + // The function's name if it is configured, otherwise shared function info + // debug name. + static Handle<String> GetName(Handle<JSFunction> function); + + // ES6 section 9.2.11 SetFunctionName + // Because of the way this abstract operation is used in the spec, + // it should never fail, but in practice it will fail if the generated + // function name's length exceeds String::kMaxLength. + static V8_WARN_UNUSED_RESULT bool SetName(Handle<JSFunction> function, + Handle<Name> name, + Handle<String> prefix); + + // The function's displayName if it is set, otherwise name if it is + // configured, otherwise shared function info + // debug name. + static Handle<String> GetDebugName(Handle<JSFunction> function); + + // The function's string representation implemented according to + // ES6 section 19.2.3.5 Function.prototype.toString ( ). + static Handle<String> ToString(Handle<JSFunction> function); + +// Layout description. +#define JS_FUNCTION_FIELDS(V) \ + /* Pointer fields. */ \ + V(kSharedFunctionInfoOffset, kPointerSize) \ + V(kContextOffset, kPointerSize) \ + V(kFeedbackCellOffset, kPointerSize) \ + V(kEndOfStrongFieldsOffset, 0) \ + V(kCodeOffset, kPointerSize) \ + /* Size of JSFunction object without prototype field. */ \ + V(kSizeWithoutPrototype, 0) \ + V(kPrototypeOrInitialMapOffset, kPointerSize) \ + /* Size of JSFunction object with prototype field. */ \ + V(kSizeWithPrototype, 0) + + DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, JS_FUNCTION_FIELDS) +#undef JS_FUNCTION_FIELDS + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSFunction); +}; + +// JSGlobalProxy's prototype must be a JSGlobalObject or null, +// and the prototype is hidden. JSGlobalProxy always delegates +// property accesses to its prototype if the prototype is not null. +// +// A JSGlobalProxy can be reinitialized which will preserve its identity. +// +// Accessing a JSGlobalProxy requires security check. + +class JSGlobalProxy : public JSObject { + public: + // [native_context]: the owner native context of this global proxy object. + // It is null value if this object is not used by any context. + DECL_ACCESSORS(native_context, Object) + + DECL_CAST(JSGlobalProxy) + + inline bool IsDetachedFrom(JSGlobalObject* global) const; + + static int SizeWithEmbedderFields(int embedder_field_count); + + // Dispatched behavior. + DECL_PRINTER(JSGlobalProxy) + DECL_VERIFIER(JSGlobalProxy) + + // Layout description. + static const int kNativeContextOffset = JSObject::kHeaderSize; + static const int kSize = kNativeContextOffset + kPointerSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSGlobalProxy); +}; + +// JavaScript global object. +class JSGlobalObject : public JSObject { + public: + // [native context]: the natives corresponding to this global object. + DECL_ACCESSORS(native_context, Context) + + // [global proxy]: the global proxy object of the context + DECL_ACCESSORS(global_proxy, JSObject) + + // Gets global object properties. + inline GlobalDictionary* global_dictionary(); + inline void set_global_dictionary(GlobalDictionary* dictionary); + + static void InvalidatePropertyCell(Handle<JSGlobalObject> object, + Handle<Name> name); + // Ensure that the global object has a cell for the given property name. + static Handle<PropertyCell> EnsureEmptyPropertyCell( + Handle<JSGlobalObject> global, Handle<Name> name, + PropertyCellType cell_type, int* entry_out = nullptr); + + DECL_CAST(JSGlobalObject) + + inline bool IsDetached(); + + // Dispatched behavior. + DECL_PRINTER(JSGlobalObject) + DECL_VERIFIER(JSGlobalObject) + + // Layout description. + static const int kNativeContextOffset = JSObject::kHeaderSize; + static const int kGlobalProxyOffset = kNativeContextOffset + kPointerSize; + static const int kHeaderSize = kGlobalProxyOffset + kPointerSize; + static const int kSize = kHeaderSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSGlobalObject); +}; + +// Representation for JS Wrapper objects, String, Number, Boolean, etc. +class JSValue : public JSObject { + public: + // [value]: the object being wrapped. + DECL_ACCESSORS(value, Object) + + DECL_CAST(JSValue) + + // Dispatched behavior. + DECL_PRINTER(JSValue) + DECL_VERIFIER(JSValue) + + // Layout description. + static const int kValueOffset = JSObject::kHeaderSize; + static const int kSize = kValueOffset + kPointerSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSValue); +}; + +class DateCache; + +// Representation for JS date objects. +class JSDate : public JSObject { + public: + static V8_WARN_UNUSED_RESULT MaybeHandle<JSDate> New( + Handle<JSFunction> constructor, Handle<JSReceiver> new_target, double tv); + + // If one component is NaN, all of them are, indicating a NaN time value. + // [value]: the time value. + DECL_ACCESSORS(value, Object) + // [year]: caches year. Either undefined, smi, or NaN. + DECL_ACCESSORS(year, Object) + // [month]: caches month. Either undefined, smi, or NaN. + DECL_ACCESSORS(month, Object) + // [day]: caches day. Either undefined, smi, or NaN. + DECL_ACCESSORS(day, Object) + // [weekday]: caches day of week. Either undefined, smi, or NaN. + DECL_ACCESSORS(weekday, Object) + // [hour]: caches hours. Either undefined, smi, or NaN. + DECL_ACCESSORS(hour, Object) + // [min]: caches minutes. Either undefined, smi, or NaN. + DECL_ACCESSORS(min, Object) + // [sec]: caches seconds. Either undefined, smi, or NaN. + DECL_ACCESSORS(sec, Object) + // [cache stamp]: sample of the date cache stamp at the + // moment when chached fields were cached. + DECL_ACCESSORS(cache_stamp, Object) + + DECL_CAST(JSDate) + + // Returns the time value (UTC) identifying the current time. + static double CurrentTimeValue(Isolate* isolate); + + // Returns the date field with the specified index. + // See FieldIndex for the list of date fields. + static Object* GetField(Object* date, Smi* index); + + static Handle<Object> SetValue(Handle<JSDate> date, double v); + + void SetValue(Object* value, bool is_value_nan); + + // Dispatched behavior. + DECL_PRINTER(JSDate) + DECL_VERIFIER(JSDate) + + // The order is important. It must be kept in sync with date macros + // in macros.py. + enum FieldIndex { + kDateValue, + kYear, + kMonth, + kDay, + kWeekday, + kHour, + kMinute, + kSecond, + kFirstUncachedField, + kMillisecond = kFirstUncachedField, + kDays, + kTimeInDay, + kFirstUTCField, + kYearUTC = kFirstUTCField, + kMonthUTC, + kDayUTC, + kWeekdayUTC, + kHourUTC, + kMinuteUTC, + kSecondUTC, + kMillisecondUTC, + kDaysUTC, + kTimeInDayUTC, + kTimezoneOffset + }; + + // Layout description. + static const int kValueOffset = JSObject::kHeaderSize; + static const int kYearOffset = kValueOffset + kPointerSize; + static const int kMonthOffset = kYearOffset + kPointerSize; + static const int kDayOffset = kMonthOffset + kPointerSize; + static const int kWeekdayOffset = kDayOffset + kPointerSize; + static const int kHourOffset = kWeekdayOffset + kPointerSize; + static const int kMinOffset = kHourOffset + kPointerSize; + static const int kSecOffset = kMinOffset + kPointerSize; + static const int kCacheStampOffset = kSecOffset + kPointerSize; + static const int kSize = kCacheStampOffset + kPointerSize; + + private: + inline Object* DoGetField(FieldIndex index); + + Object* GetUTCField(FieldIndex index, double value, DateCache* date_cache); + + // Computes and caches the cacheable fields of the date. + inline void SetCachedFields(int64_t local_time_ms, DateCache* date_cache); + + DISALLOW_IMPLICIT_CONSTRUCTORS(JSDate); +}; + +// Representation of message objects used for error reporting through +// the API. The messages are formatted in JavaScript so this object is +// a real JavaScript object. The information used for formatting the +// error messages are not directly accessible from JavaScript to +// prevent leaking information to user code called during error +// formatting. +class JSMessageObject : public JSObject { + public: + // [type]: the type of error message. + inline int type() const; + inline void set_type(int value); + + // [arguments]: the arguments for formatting the error message. + DECL_ACCESSORS(argument, Object) + + // [script]: the script from which the error message originated. + DECL_ACCESSORS(script, Script) + + // [stack_frames]: an array of stack frames for this error object. + DECL_ACCESSORS(stack_frames, Object) + + // [start_position]: the start position in the script for the error message. + inline int start_position() const; + inline void set_start_position(int value); + + // [end_position]: the end position in the script for the error message. + inline int end_position() const; + inline void set_end_position(int value); + + // Returns the line number for the error message (1-based), or + // Message::kNoLineNumberInfo if the line cannot be determined. + int GetLineNumber() const; + + // Returns the offset of the given position within the containing line. + int GetColumnNumber() const; + + // Returns the source code line containing the given source + // position, or the empty string if the position is invalid. + Handle<String> GetSourceLine() const; + + inline int error_level() const; + inline void set_error_level(int level); + + DECL_CAST(JSMessageObject) + + // Dispatched behavior. + DECL_PRINTER(JSMessageObject) + DECL_VERIFIER(JSMessageObject) + + // Layout description. + static const int kTypeOffset = JSObject::kHeaderSize; + static const int kArgumentsOffset = kTypeOffset + kPointerSize; + static const int kScriptOffset = kArgumentsOffset + kPointerSize; + static const int kStackFramesOffset = kScriptOffset + kPointerSize; + static const int kStartPositionOffset = kStackFramesOffset + kPointerSize; + static const int kEndPositionOffset = kStartPositionOffset + kPointerSize; + static const int kErrorLevelOffset = kEndPositionOffset + kPointerSize; + static const int kSize = kErrorLevelOffset + kPointerSize; + + typedef FixedBodyDescriptor<HeapObject::kMapOffset, + kStackFramesOffset + kPointerSize, kSize> + BodyDescriptor; +}; + +// The [Async-from-Sync Iterator] object +// (proposal-async-iteration/#sec-async-from-sync-iterator-objects) +// An object which wraps an ordinary Iterator and converts it to behave +// according to the Async Iterator protocol. +// (See https://tc39.github.io/proposal-async-iteration/#sec-iteration) +class JSAsyncFromSyncIterator : public JSObject { + public: + DECL_CAST(JSAsyncFromSyncIterator) + DECL_PRINTER(JSAsyncFromSyncIterator) + DECL_VERIFIER(JSAsyncFromSyncIterator) + + // Async-from-Sync Iterator instances are ordinary objects that inherit + // properties from the %AsyncFromSyncIteratorPrototype% intrinsic object. + // Async-from-Sync Iterator instances are initially created with the internal + // slots listed in Table 4. + // (proposal-async-iteration/#table-async-from-sync-iterator-internal-slots) + DECL_ACCESSORS(sync_iterator, JSReceiver) + + // The "next" method is loaded during GetIterator, and is not reloaded for + // subsequent "next" invocations. + DECL_ACCESSORS(next, Object) + + // Offsets of object fields. + static const int kSyncIteratorOffset = JSObject::kHeaderSize; + static const int kNextOffset = kSyncIteratorOffset + kPointerSize; + static const int kSize = kNextOffset + kPointerSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSAsyncFromSyncIterator); +}; + +class JSStringIterator : public JSObject { + public: + // Dispatched behavior. + DECL_PRINTER(JSStringIterator) + DECL_VERIFIER(JSStringIterator) + + DECL_CAST(JSStringIterator) + + // [string]: the [[IteratedString]] inobject property. + DECL_ACCESSORS(string, String) + + // [index]: The [[StringIteratorNextIndex]] inobject property. + inline int index() const; + inline void set_index(int value); + + static const int kStringOffset = JSObject::kHeaderSize; + static const int kNextIndexOffset = kStringOffset + kPointerSize; + static const int kSize = kNextIndexOffset + kPointerSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSStringIterator); +}; + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_JS_OBJECTS_H_ diff --git a/deps/v8/src/objects/js-plural-rules.cc b/deps/v8/src/objects/js-plural-rules.cc index 07cc62a41e..f76692c501 100644 --- a/deps/v8/src/objects/js-plural-rules.cc +++ b/deps/v8/src/objects/js-plural-rules.cc @@ -85,7 +85,7 @@ void InitializeICUPluralRules( } // namespace // static -MaybeHandle<JSPluralRules> JSPluralRules::InitializePluralRules( +MaybeHandle<JSPluralRules> JSPluralRules::Initialize( Isolate* isolate, Handle<JSPluralRules> plural_rules, Handle<Object> locales, Handle<Object> options_obj) { // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales). @@ -190,8 +190,7 @@ MaybeHandle<JSPluralRules> JSPluralRules::InitializePluralRules( } MaybeHandle<String> JSPluralRules::ResolvePlural( - Isolate* isolate, Handle<JSPluralRules> plural_rules, - Handle<Object> number) { + Isolate* isolate, Handle<JSPluralRules> plural_rules, double number) { icu::PluralRules* icu_plural_rules = plural_rules->icu_plural_rules()->raw(); CHECK_NOT_NULL(icu_plural_rules); @@ -207,7 +206,7 @@ MaybeHandle<String> JSPluralRules::ResolvePlural( // this step, then switch to that API. Bug thread: // http://bugs.icu-project.org/trac/ticket/12763 icu::UnicodeString rounded_string; - icu_decimal_format->format(number->Number(), rounded_string); + icu_decimal_format->format(number, rounded_string); icu::Formattable formattable; UErrorCode status = U_ZERO_ERROR; diff --git a/deps/v8/src/objects/js-plural-rules.h b/deps/v8/src/objects/js-plural-rules.h index 9d5da795ab..f262457acb 100644 --- a/deps/v8/src/objects/js-plural-rules.h +++ b/deps/v8/src/objects/js-plural-rules.h @@ -18,12 +18,16 @@ // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" +namespace U_ICU_NAMESPACE { +class PluralRules; +} // namespace U_ICU_NAMESPACE + namespace v8 { namespace internal { class JSPluralRules : public JSObject { public: - V8_WARN_UNUSED_RESULT static MaybeHandle<JSPluralRules> InitializePluralRules( + V8_WARN_UNUSED_RESULT static MaybeHandle<JSPluralRules> Initialize( Isolate* isolate, Handle<JSPluralRules> plural_rules, Handle<Object> locales, Handle<Object> options); @@ -31,8 +35,7 @@ class JSPluralRules : public JSObject { Handle<JSPluralRules> plural_rules); V8_WARN_UNUSED_RESULT static MaybeHandle<String> ResolvePlural( - Isolate* isolate, Handle<JSPluralRules> plural_rules, - Handle<Object> number); + Isolate* isolate, Handle<JSPluralRules> plural_rules, double number); DECL_CAST(JSPluralRules) DECL_PRINTER(JSPluralRules) diff --git a/deps/v8/src/objects/js-promise.h b/deps/v8/src/objects/js-promise.h index c52e19ce49..b395ac9b6d 100644 --- a/deps/v8/src/objects/js-promise.h +++ b/deps/v8/src/objects/js-promise.h @@ -5,7 +5,7 @@ #ifndef V8_OBJECTS_JS_PROMISE_H_ #define V8_OBJECTS_JS_PROMISE_H_ -#include "src/objects.h" +#include "src/objects/js-objects.h" #include "src/objects/promise.h" // Has to be the last include (doesn't have include guards): diff --git a/deps/v8/src/objects/js-proxy.h b/deps/v8/src/objects/js-proxy.h index 45e27473fe..2a7c518be4 100644 --- a/deps/v8/src/objects/js-proxy.h +++ b/deps/v8/src/objects/js-proxy.h @@ -5,7 +5,7 @@ #ifndef V8_OBJECTS_JS_PROXY_H_ #define V8_OBJECTS_JS_PROXY_H_ -#include "src/objects.h" +#include "src/objects/js-objects.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" @@ -118,8 +118,6 @@ class JSProxy : public JSReceiver { typedef FixedBodyDescriptor<JSReceiver::kPropertiesOrHashOffset, kSize, kSize> BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; static Maybe<bool> SetPrivateSymbol(Isolate* isolate, Handle<JSProxy> proxy, Handle<Symbol> private_name, diff --git a/deps/v8/src/objects/js-regexp-string-iterator.h b/deps/v8/src/objects/js-regexp-string-iterator.h index 9821e33efb..9ad2851c7a 100644 --- a/deps/v8/src/objects/js-regexp-string-iterator.h +++ b/deps/v8/src/objects/js-regexp-string-iterator.h @@ -5,7 +5,7 @@ #ifndef V8_OBJECTS_JS_REGEXP_STRING_ITERATOR_H_ #define V8_OBJECTS_JS_REGEXP_STRING_ITERATOR_H_ -#include "src/objects.h" +#include "src/objects/js-objects.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" diff --git a/deps/v8/src/objects/js-relative-time-format-inl.h b/deps/v8/src/objects/js-relative-time-format-inl.h index 6dc984e252..a4ee3ee7f3 100644 --- a/deps/v8/src/objects/js-relative-time-format-inl.h +++ b/deps/v8/src/objects/js-relative-time-format-inl.h @@ -20,7 +20,8 @@ namespace internal { // Base relative time format accessors. ACCESSORS(JSRelativeTimeFormat, locale, String, kLocaleOffset) -ACCESSORS(JSRelativeTimeFormat, formatter, Foreign, kFormatterOffset) +ACCESSORS(JSRelativeTimeFormat, icu_formatter, + Managed<icu::RelativeDateTimeFormatter>, kICUFormatterOffset) SMI_ACCESSORS(JSRelativeTimeFormat, flags, kFlagsOffset) // TODO(ftang): Use bit field accessor for style and numeric later. diff --git a/deps/v8/src/objects/js-relative-time-format.cc b/deps/v8/src/objects/js-relative-time-format.cc index 56130f7311..b3aa996d64 100644 --- a/deps/v8/src/objects/js-relative-time-format.cc +++ b/deps/v8/src/objects/js-relative-time-format.cc @@ -17,9 +17,9 @@ #include "src/objects-inl.h" #include "src/objects/intl-objects.h" #include "src/objects/js-relative-time-format-inl.h" -#include "src/objects/managed.h" #include "unicode/numfmt.h" #include "unicode/reldatefmt.h" +#include "unicode/uvernum.h" // for U_ICU_VERSION_MAJOR_NUM namespace v8 { namespace internal { @@ -54,8 +54,7 @@ JSRelativeTimeFormat::Numeric JSRelativeTimeFormat::getNumeric( UNREACHABLE(); } -MaybeHandle<JSRelativeTimeFormat> -JSRelativeTimeFormat::InitializeRelativeTimeFormat( +MaybeHandle<JSRelativeTimeFormat> JSRelativeTimeFormat::Initialize( Isolate* isolate, Handle<JSRelativeTimeFormat> relative_time_format_holder, Handle<Object> input_locales, Handle<Object> input_options) { Factory* factory = isolate->factory(); @@ -161,7 +160,7 @@ JSRelativeTimeFormat::InitializeRelativeTimeFormat( icu_formatter); // 30. Set relativeTimeFormat.[[InitializedRelativeTimeFormat]] to true. - relative_time_format_holder->set_formatter(*managed_formatter); + relative_time_format_holder->set_icu_formatter(*managed_formatter); // 31. Return relativeTimeFormat. return relative_time_format_holder; } @@ -180,12 +179,6 @@ Handle<JSObject> JSRelativeTimeFormat::ResolvedOptions( return result; } -icu::RelativeDateTimeFormatter* JSRelativeTimeFormat::UnpackFormatter( - Handle<JSRelativeTimeFormat> holder) { - return Managed<icu::RelativeDateTimeFormatter>::cast(holder->formatter()) - ->raw(); -} - Handle<String> JSRelativeTimeFormat::StyleAsString() const { switch (style()) { case Style::LONG: @@ -210,5 +203,212 @@ Handle<String> JSRelativeTimeFormat::NumericAsString() const { } } +namespace { + +Handle<String> UnitAsString(Isolate* isolate, URelativeDateTimeUnit unit_enum) { + Factory* factory = isolate->factory(); + switch (unit_enum) { + case UDAT_REL_UNIT_SECOND: + return factory->second_string(); + case UDAT_REL_UNIT_MINUTE: + return factory->minute_string(); + case UDAT_REL_UNIT_HOUR: + return factory->hour_string(); + case UDAT_REL_UNIT_DAY: + return factory->day_string(); + case UDAT_REL_UNIT_WEEK: + return factory->week_string(); + case UDAT_REL_UNIT_MONTH: + return factory->month_string(); + case UDAT_REL_UNIT_QUARTER: + return factory->quarter_string(); + case UDAT_REL_UNIT_YEAR: + return factory->year_string(); + default: + UNREACHABLE(); + } +} + +MaybeHandle<JSArray> GenerateRelativeTimeFormatParts( + Isolate* isolate, const icu::UnicodeString& formatted, + const icu::UnicodeString& integer_part, URelativeDateTimeUnit unit_enum) { + Factory* factory = isolate->factory(); + Handle<JSArray> array = factory->NewJSArray(0); + int32_t found = formatted.indexOf(integer_part); + + Handle<String> substring; + if (found < 0) { + // Cannot find the integer_part in the formatted. + // Return [{'type': 'literal', 'value': formatted}] + ASSIGN_RETURN_ON_EXCEPTION(isolate, substring, + Intl::ToString(isolate, formatted), JSArray); + Intl::AddElement(isolate, array, + 0, // index + factory->literal_string(), // field_type_string + substring); + } else { + // Found the formatted integer in the result. + int index = 0; + + // array.push({ + // 'type': 'literal', + // 'value': formatted.substring(0, found)}) + if (found > 0) { + ASSIGN_RETURN_ON_EXCEPTION(isolate, substring, + Intl::ToString(isolate, formatted, 0, found), + JSArray); + Intl::AddElement(isolate, array, index++, + factory->literal_string(), // field_type_string + substring); + } + + // array.push({ + // 'type': 'integer', + // 'value': formatted.substring(found, found + integer_part.length), + // 'unit': unit}) + ASSIGN_RETURN_ON_EXCEPTION(isolate, substring, + Intl::ToString(isolate, formatted, found, + found + integer_part.length()), + JSArray); + Handle<String> unit = UnitAsString(isolate, unit_enum); + Intl::AddElement(isolate, array, index++, + factory->integer_string(), // field_type_string + substring, factory->unit_string(), unit); + + // array.push({ + // 'type': 'literal', + // 'value': formatted.substring( + // found + integer_part.length, formatted.length)}) + if (found + integer_part.length() < formatted.length()) { + ASSIGN_RETURN_ON_EXCEPTION( + isolate, substring, + Intl::ToString(isolate, formatted, found + integer_part.length(), + formatted.length()), + JSArray); + Intl::AddElement(isolate, array, index, + factory->literal_string(), // field_type_string + substring); + } + } + return array; +} + +bool GetURelativeDateTimeUnit(Handle<String> unit, + URelativeDateTimeUnit* unit_enum) { + std::unique_ptr<char[]> unit_str = unit->ToCString(); + if ((strcmp("second", unit_str.get()) == 0) || + (strcmp("seconds", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_SECOND; + } else if ((strcmp("minute", unit_str.get()) == 0) || + (strcmp("minutes", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_MINUTE; + } else if ((strcmp("hour", unit_str.get()) == 0) || + (strcmp("hours", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_HOUR; + } else if ((strcmp("day", unit_str.get()) == 0) || + (strcmp("days", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_DAY; + } else if ((strcmp("week", unit_str.get()) == 0) || + (strcmp("weeks", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_WEEK; + } else if ((strcmp("month", unit_str.get()) == 0) || + (strcmp("months", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_MONTH; + } else if ((strcmp("quarter", unit_str.get()) == 0) || + (strcmp("quarters", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_QUARTER; + } else if ((strcmp("year", unit_str.get()) == 0) || + (strcmp("years", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_YEAR; + } else { + return false; + } + return true; +} + +} // namespace + +MaybeHandle<Object> JSRelativeTimeFormat::Format( + Isolate* isolate, Handle<Object> value_obj, Handle<Object> unit_obj, + Handle<JSRelativeTimeFormat> format_holder, const char* func_name, + bool to_parts) { + Factory* factory = isolate->factory(); + + // 3. Let value be ? ToNumber(value). + Handle<Object> value; + ASSIGN_RETURN_ON_EXCEPTION(isolate, value, + Object::ToNumber(isolate, value_obj), Object); + double number = value->Number(); + // 4. Let unit be ? ToString(unit). + Handle<String> unit; + ASSIGN_RETURN_ON_EXCEPTION(isolate, unit, Object::ToString(isolate, unit_obj), + Object); + + // 4. If isFinite(value) is false, then throw a RangeError exception. + if (!std::isfinite(number)) { + THROW_NEW_ERROR( + isolate, + NewRangeError(MessageTemplate::kNotFiniteNumber, + isolate->factory()->NewStringFromAsciiChecked(func_name)), + Object); + } + + icu::RelativeDateTimeFormatter* formatter = + format_holder->icu_formatter()->raw(); + CHECK_NOT_NULL(formatter); + + URelativeDateTimeUnit unit_enum; + if (!GetURelativeDateTimeUnit(unit, &unit_enum)) { + THROW_NEW_ERROR( + isolate, + NewRangeError(MessageTemplate::kInvalidUnit, + isolate->factory()->NewStringFromAsciiChecked(func_name), + unit), + Object); + } + + UErrorCode status = U_ZERO_ERROR; + icu::UnicodeString formatted; + +#if USE_CHROMIUM_ICU != 1 && U_ICU_VERSION_MAJOR_NUM < 63 + if (unit_enum != UDAT_REL_UNIT_QUARTER) { // ICU did not implement + // UDAT_REL_UNIT_QUARTER < 63 +#endif // USE_CHROMIUM_ICU != 1 && U_ICU_VERSION_MAJOR_NUM < 63 + if (format_holder->numeric() == JSRelativeTimeFormat::Numeric::ALWAYS) { + formatter->formatNumeric(number, unit_enum, formatted, status); + } else { + DCHECK_EQ(JSRelativeTimeFormat::Numeric::AUTO, format_holder->numeric()); + formatter->format(number, unit_enum, formatted, status); + } +#if USE_CHROMIUM_ICU != 1 && U_ICU_VERSION_MAJOR_NUM < 63 + } +#endif // USE_CHROMIUM_ICU != 1 && U_ICU_VERSION_MAJOR_NUM < 63 + + if (U_FAILURE(status)) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object); + } + + if (to_parts) { + icu::UnicodeString integer; + icu::FieldPosition pos; + formatter->getNumberFormat().format(std::abs(number), integer, pos, status); + if (U_FAILURE(status)) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), + Object); + } + + Handle<JSArray> elements; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, elements, + GenerateRelativeTimeFormatParts(isolate, formatted, integer, unit_enum), + Object); + return elements; + } + + return factory->NewStringFromTwoByte(Vector<const uint16_t>( + reinterpret_cast<const uint16_t*>(formatted.getBuffer()), + formatted.length())); +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/objects/js-relative-time-format.h b/deps/v8/src/objects/js-relative-time-format.h index 397c6fe287..eaaeb0e05f 100644 --- a/deps/v8/src/objects/js-relative-time-format.h +++ b/deps/v8/src/objects/js-relative-time-format.h @@ -12,6 +12,7 @@ #include "src/heap/factory.h" #include "src/isolate.h" #include "src/objects.h" +#include "src/objects/managed.h" #include "unicode/uversion.h" // Has to be the last include (doesn't have include guards): @@ -28,26 +29,30 @@ class JSRelativeTimeFormat : public JSObject { public: // Initializes relative time format object with properties derived from input // locales and options. - static MaybeHandle<JSRelativeTimeFormat> InitializeRelativeTimeFormat( + V8_WARN_UNUSED_RESULT static MaybeHandle<JSRelativeTimeFormat> Initialize( Isolate* isolate, Handle<JSRelativeTimeFormat> relative_time_format_holder, Handle<Object> locales, Handle<Object> options); - static Handle<JSObject> ResolvedOptions( + V8_WARN_UNUSED_RESULT static Handle<JSObject> ResolvedOptions( Isolate* isolate, Handle<JSRelativeTimeFormat> format_holder); - // Unpacks formatter object from corresponding JavaScript object. - static icu::RelativeDateTimeFormatter* UnpackFormatter( - Handle<JSRelativeTimeFormat> relative_time_format_holder); Handle<String> StyleAsString() const; Handle<String> NumericAsString() const; + // ecma402/#sec-Intl.RelativeTimeFormat.prototype.format + // ecma402/#sec-Intl.RelativeTimeFormat.prototype.formatToParts + V8_WARN_UNUSED_RESULT static MaybeHandle<Object> Format( + Isolate* isolate, Handle<Object> value_obj, Handle<Object> unit_obj, + Handle<JSRelativeTimeFormat> format_holder, const char* func_name, + bool to_parts); + DECL_CAST(JSRelativeTimeFormat) // RelativeTimeFormat accessors. DECL_ACCESSORS(locale, String) - DECL_ACCESSORS(formatter, Foreign) + DECL_ACCESSORS(icu_formatter, Managed<icu::RelativeDateTimeFormatter>) // Style: identifying the relative time format style used. // @@ -98,8 +103,8 @@ class JSRelativeTimeFormat : public JSObject { // Layout description. static const int kJSRelativeTimeFormatOffset = JSObject::kHeaderSize; static const int kLocaleOffset = kJSRelativeTimeFormatOffset + kPointerSize; - static const int kFormatterOffset = kLocaleOffset + kPointerSize; - static const int kFlagsOffset = kFormatterOffset + kPointerSize; + static const int kICUFormatterOffset = kLocaleOffset + kPointerSize; + static const int kFlagsOffset = kICUFormatterOffset + kPointerSize; static const int kSize = kFlagsOffset + kPointerSize; private: diff --git a/deps/v8/src/objects/js-segmenter-inl.h b/deps/v8/src/objects/js-segmenter-inl.h new file mode 100644 index 0000000000..1aac2b1d63 --- /dev/null +++ b/deps/v8/src/objects/js-segmenter-inl.h @@ -0,0 +1,56 @@ +// Copyright 2018 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. + +#ifndef V8_INTL_SUPPORT +#error Internationalization is expected to be enabled. +#endif // V8_INTL_SUPPORT + +#ifndef V8_OBJECTS_JS_SEGMENTER_INL_H_ +#define V8_OBJECTS_JS_SEGMENTER_INL_H_ + +#include "src/objects-inl.h" +#include "src/objects/js-segmenter.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +// Base segmenter accessors. +ACCESSORS(JSSegmenter, locale, String, kLocaleOffset) +ACCESSORS(JSSegmenter, icu_break_iterator, Managed<icu::BreakIterator>, + kICUBreakIteratorOffset) +SMI_ACCESSORS(JSSegmenter, flags, kFlagsOffset) + +inline void JSSegmenter::set_line_break_style(LineBreakStyle line_break_style) { + DCHECK_GT(LineBreakStyle::COUNT, line_break_style); + int hints = flags(); + hints = LineBreakStyleBits::update(hints, line_break_style); + set_flags(hints); +} + +inline JSSegmenter::LineBreakStyle JSSegmenter::line_break_style() const { + return LineBreakStyleBits::decode(flags()); +} + +inline void JSSegmenter::set_granularity(Granularity granularity) { + DCHECK_GT(Granularity::COUNT, granularity); + int hints = flags(); + hints = GranularityBits::update(hints, granularity); + set_flags(hints); +} + +inline JSSegmenter::Granularity JSSegmenter::granularity() const { + return GranularityBits::decode(flags()); +} + +CAST_ACCESSOR(JSSegmenter); + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_JS_SEGMENTER_INL_H_ diff --git a/deps/v8/src/objects/js-segmenter.cc b/deps/v8/src/objects/js-segmenter.cc new file mode 100644 index 0000000000..62d9bd508a --- /dev/null +++ b/deps/v8/src/objects/js-segmenter.cc @@ -0,0 +1,214 @@ +// Copyright 2018 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. + +#ifndef V8_INTL_SUPPORT +#error Internationalization is expected to be enabled. +#endif // V8_INTL_SUPPORT + +#include "src/objects/js-segmenter.h" + +#include <map> +#include <memory> +#include <string> + +#include "src/heap/factory.h" +#include "src/isolate.h" +#include "src/objects-inl.h" +#include "src/objects/intl-objects.h" +#include "src/objects/js-segmenter-inl.h" +#include "src/objects/managed.h" +#include "unicode/brkiter.h" + +namespace v8 { +namespace internal { + +JSSegmenter::LineBreakStyle JSSegmenter::GetLineBreakStyle(const char* str) { + if (strcmp(str, "strict") == 0) return JSSegmenter::LineBreakStyle::STRICT; + if (strcmp(str, "normal") == 0) return JSSegmenter::LineBreakStyle::NORMAL; + if (strcmp(str, "loose") == 0) return JSSegmenter::LineBreakStyle::LOOSE; + UNREACHABLE(); +} + +JSSegmenter::Granularity JSSegmenter::GetGranularity(const char* str) { + if (strcmp(str, "grapheme") == 0) return JSSegmenter::Granularity::GRAPHEME; + if (strcmp(str, "word") == 0) return JSSegmenter::Granularity::WORD; + if (strcmp(str, "sentence") == 0) return JSSegmenter::Granularity::SENTENCE; + if (strcmp(str, "line") == 0) return JSSegmenter::Granularity::LINE; + UNREACHABLE(); +} + +MaybeHandle<JSSegmenter> JSSegmenter::Initialize( + Isolate* isolate, Handle<JSSegmenter> segmenter_holder, + Handle<Object> input_locales, Handle<Object> input_options) { + Factory* factory = isolate->factory(); + segmenter_holder->set_flags(0); + // 3. Let requestedLocales be ? CanonicalizeLocaleList(locales). + Handle<JSObject> requested_locales; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, requested_locales, + Intl::CanonicalizeLocaleListJS(isolate, input_locales), JSSegmenter); + + // 11. If options is undefined, then + Handle<JSReceiver> options; + if (input_options->IsUndefined(isolate)) { + // a. Let options be ObjectCreate(null). + options = isolate->factory()->NewJSObjectWithNullProto(); + // 12. Else + } else { + // a. Let options be ? ToObject(options). + ASSIGN_RETURN_ON_EXCEPTION(isolate, options, + Object::ToObject(isolate, input_options), + JSSegmenter); + } + + // 8. Set opt.[[lb]] to lineBreakStyle. + + // Because currently we access localeMatcher inside ResolveLocale, we have to + // move ResolveLocale before get lineBreakStyle + // 9. Let r be ResolveLocale(%Segmenter%.[[AvailableLocales]], + // requestedLocales, opt, %Segmenter%.[[RelevantExtensionKeys]]). + Handle<JSObject> r; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, r, + Intl::ResolveLocale(isolate, "segmenter", requested_locales, options), + JSSegmenter); + Handle<Object> locale_obj = + JSObject::GetDataProperty(r, factory->locale_string()); + Handle<String> locale; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, locale, Object::ToString(isolate, locale_obj), JSSegmenter); + + // 7. Let lineBreakStyle be ? GetOption(options, "lineBreakStyle", "string", « + // "strict", "normal", "loose" », "normal"). + std::unique_ptr<char[]> line_break_style_str = nullptr; + const std::vector<const char*> line_break_style_values = {"strict", "normal", + "loose"}; + Maybe<bool> maybe_found_line_break_style = Intl::GetStringOption( + isolate, options, "lineBreakStyle", line_break_style_values, + "Intl.Segmenter", &line_break_style_str); + LineBreakStyle line_break_style_enum = LineBreakStyle::NORMAL; + MAYBE_RETURN(maybe_found_line_break_style, MaybeHandle<JSSegmenter>()); + if (maybe_found_line_break_style.FromJust()) { + DCHECK_NOT_NULL(line_break_style_str.get()); + line_break_style_enum = GetLineBreakStyle(line_break_style_str.get()); + } + + // 10. Set segmenter.[[Locale]] to the value of r.[[Locale]]. + segmenter_holder->set_locale(*locale); + + // 13. Let granularity be ? GetOption(options, "granularity", "string", « + // "grapheme", "word", "sentence", "line" », "grapheme"). + + std::unique_ptr<char[]> granularity_str = nullptr; + const std::vector<const char*> granularity_values = {"grapheme", "word", + "sentence", "line"}; + Maybe<bool> maybe_found_granularity = + Intl::GetStringOption(isolate, options, "granularity", granularity_values, + "Intl.Segmenter", &granularity_str); + Granularity granularity_enum = Granularity::GRAPHEME; + MAYBE_RETURN(maybe_found_granularity, MaybeHandle<JSSegmenter>()); + if (maybe_found_granularity.FromJust()) { + DCHECK_NOT_NULL(granularity_str.get()); + granularity_enum = GetGranularity(granularity_str.get()); + } + + // 14. Set segmenter.[[SegmenterGranularity]] to granularity. + segmenter_holder->set_granularity(granularity_enum); + + // 15. If granularity is "line", + if (granularity_enum == Granularity::LINE) { + // a. Set segmenter.[[SegmenterLineBreakStyle]] to r.[[lb]]. + segmenter_holder->set_line_break_style(line_break_style_enum); + } else { + segmenter_holder->set_line_break_style(LineBreakStyle::NOTSET); + } + + icu::Locale icu_locale = Intl::CreateICULocale(isolate, locale); + DCHECK(!icu_locale.isBogus()); + + UErrorCode status = U_ZERO_ERROR; + std::unique_ptr<icu::BreakIterator> icu_break_iterator; + + switch (granularity_enum) { + case Granularity::GRAPHEME: + icu_break_iterator.reset( + icu::BreakIterator::createCharacterInstance(icu_locale, status)); + break; + case Granularity::WORD: + icu_break_iterator.reset( + icu::BreakIterator::createWordInstance(icu_locale, status)); + break; + case Granularity::SENTENCE: + icu_break_iterator.reset( + icu::BreakIterator::createSentenceInstance(icu_locale, status)); + break; + case Granularity::LINE: + icu_break_iterator.reset( + icu::BreakIterator::createLineInstance(icu_locale, status)); + // 15. If granularity is "line", + // a. Set segmenter.[[SegmenterLineBreakStyle]] to r.[[lb]]. + // TBW + break; + case Granularity::COUNT: + UNREACHABLE(); + } + + CHECK(U_SUCCESS(status)); + CHECK_NOT_NULL(icu_break_iterator.get()); + + Handle<Managed<icu::BreakIterator>> managed_break_iterator = + Managed<icu::BreakIterator>::FromUniquePtr(isolate, 0, + std::move(icu_break_iterator)); + + segmenter_holder->set_icu_break_iterator(*managed_break_iterator); + return segmenter_holder; +} + +Handle<JSObject> JSSegmenter::ResolvedOptions( + Isolate* isolate, Handle<JSSegmenter> segmenter_holder) { + Factory* factory = isolate->factory(); + Handle<JSObject> result = factory->NewJSObject(isolate->object_function()); + Handle<String> locale(segmenter_holder->locale(), isolate); + JSObject::AddProperty(isolate, result, factory->locale_string(), locale, + NONE); + if (segmenter_holder->line_break_style() != LineBreakStyle::NOTSET) { + JSObject::AddProperty(isolate, result, factory->lineBreakStyle_string(), + segmenter_holder->LineBreakStyleAsString(), NONE); + } + JSObject::AddProperty(isolate, result, factory->granularity_string(), + segmenter_holder->GranularityAsString(), NONE); + return result; +} + +Handle<String> JSSegmenter::LineBreakStyleAsString() const { + switch (line_break_style()) { + case LineBreakStyle::STRICT: + return GetReadOnlyRoots().strict_string_handle(); + case LineBreakStyle::NORMAL: + return GetReadOnlyRoots().normal_string_handle(); + case LineBreakStyle::LOOSE: + return GetReadOnlyRoots().loose_string_handle(); + case LineBreakStyle::COUNT: + case LineBreakStyle::NOTSET: + UNREACHABLE(); + } +} + +Handle<String> JSSegmenter::GranularityAsString() const { + switch (granularity()) { + case Granularity::GRAPHEME: + return GetReadOnlyRoots().grapheme_string_handle(); + case Granularity::WORD: + return GetReadOnlyRoots().word_string_handle(); + case Granularity::SENTENCE: + return GetReadOnlyRoots().sentence_string_handle(); + case Granularity::LINE: + return GetReadOnlyRoots().line_string_handle(); + case Granularity::COUNT: + UNREACHABLE(); + } +} + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/objects/js-segmenter.h b/deps/v8/src/objects/js-segmenter.h new file mode 100644 index 0000000000..167d70c210 --- /dev/null +++ b/deps/v8/src/objects/js-segmenter.h @@ -0,0 +1,118 @@ +// Copyright 2018 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. + +#ifndef V8_INTL_SUPPORT +#error Internationalization is expected to be enabled. +#endif // V8_INTL_SUPPORT + +#ifndef V8_OBJECTS_JS_SEGMENTER_H_ +#define V8_OBJECTS_JS_SEGMENTER_H_ + +#include "src/heap/factory.h" +#include "src/isolate.h" +#include "src/objects.h" +#include "src/objects/managed.h" +#include "unicode/uversion.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace U_ICU_NAMESPACE { +class BreakIterator; +} + +namespace v8 { +namespace internal { + +class JSSegmenter : public JSObject { + public: + // Initializes segmenter object with properties derived from input + // locales and options. + V8_WARN_UNUSED_RESULT static MaybeHandle<JSSegmenter> Initialize( + Isolate* isolate, Handle<JSSegmenter> segmenter_holder, + Handle<Object> locales, Handle<Object> options); + + V8_WARN_UNUSED_RESULT static Handle<JSObject> ResolvedOptions( + Isolate* isolate, Handle<JSSegmenter> segmenter_holder); + + Handle<String> LineBreakStyleAsString() const; + Handle<String> GranularityAsString() const; + + DECL_CAST(JSSegmenter) + + // Segmenter accessors. + DECL_ACCESSORS(locale, String) + + DECL_ACCESSORS(icu_break_iterator, Managed<icu::BreakIterator>) + + // LineBreakStyle: identifying the style used for line break. + // + // ecma402 #sec-segmenter-internal-slots + + enum class LineBreakStyle { + NOTSET, // While the granularity is not LINE + STRICT, // CSS level 3 line-break=strict, e.g. treat CJ as NS + NORMAL, // CSS level 3 line-break=normal, e.g. treat CJ as ID, break before + // hyphens for ja,zh + LOOSE, // CSS level 3 line-break=loose + COUNT + }; + inline void set_line_break_style(LineBreakStyle line_break_style); + inline LineBreakStyle line_break_style() const; + + // Granularity: identifying the segmenter used. + // + // ecma402 #sec-segmenter-internal-slots + enum class Granularity { + GRAPHEME, // for character-breaks + WORD, // for word-breaks + SENTENCE, // for sentence-breaks + LINE, // for line-breaks + COUNT + }; + inline void set_granularity(Granularity granularity); + inline Granularity granularity() const; + +// Bit positions in |flags|. +#define FLAGS_BIT_FIELDS(V, _) \ + V(LineBreakStyleBits, LineBreakStyle, 3, _) \ + V(GranularityBits, Granularity, 3, _) + DEFINE_BIT_FIELDS(FLAGS_BIT_FIELDS) +#undef FLAGS_BIT_FIELDS + + STATIC_ASSERT(LineBreakStyle::NOTSET <= LineBreakStyleBits::kMax); + STATIC_ASSERT(LineBreakStyle::STRICT <= LineBreakStyleBits::kMax); + STATIC_ASSERT(LineBreakStyle::NORMAL <= LineBreakStyleBits::kMax); + STATIC_ASSERT(LineBreakStyle::LOOSE <= LineBreakStyleBits::kMax); + STATIC_ASSERT(Granularity::GRAPHEME <= GranularityBits::kMax); + STATIC_ASSERT(Granularity::WORD <= GranularityBits::kMax); + STATIC_ASSERT(Granularity::SENTENCE <= GranularityBits::kMax); + STATIC_ASSERT(Granularity::LINE <= GranularityBits::kMax); + + // [flags] Bit field containing various flags about the function. + DECL_INT_ACCESSORS(flags) + + DECL_PRINTER(JSSegmenter) + DECL_VERIFIER(JSSegmenter) + + // Layout description. + static const int kJSSegmenterOffset = JSObject::kHeaderSize; + static const int kLocaleOffset = kJSSegmenterOffset + kPointerSize; + static const int kICUBreakIteratorOffset = kLocaleOffset + kPointerSize; + static const int kFlagsOffset = kICUBreakIteratorOffset + kPointerSize; + static const int kSize = kFlagsOffset + kPointerSize; + + private: + static LineBreakStyle GetLineBreakStyle(const char* str); + static Granularity GetGranularity(const char* str); + + DISALLOW_IMPLICIT_CONSTRUCTORS(JSSegmenter); +}; + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_JS_SEGMENTER_H_ diff --git a/deps/v8/src/objects/map-inl.h b/deps/v8/src/objects/map-inl.h index 59f061dc05..0ec4113d4d 100644 --- a/deps/v8/src/objects/map-inl.h +++ b/deps/v8/src/objects/map-inl.h @@ -136,10 +136,10 @@ bool Map::IsUnboxedDoubleField(FieldIndex index) const { return !layout_descriptor()->IsTagged(index.property_index()); } -bool Map::TooManyFastProperties(StoreFromKeyed store_mode) const { +bool Map::TooManyFastProperties(StoreOrigin store_origin) const { if (UnusedPropertyFields() != 0) return false; if (is_prototype_map()) return false; - int minimum = store_mode == CERTAINLY_NOT_STORE_FROM_KEYED ? 128 : 12; + int minimum = store_origin == StoreOrigin::kNamed ? 128 : 12; int limit = Max(minimum, GetInObjectProperties()); int external = NumberOfFields() - GetInObjectProperties(); return external > limit; @@ -511,57 +511,30 @@ void Map::NotifyLeafMapLayoutChange(Isolate* isolate) { } } -bool Map::IsJSObject(InstanceType type) { - STATIC_ASSERT(LAST_TYPE == LAST_JS_OBJECT_TYPE); - return type >= FIRST_JS_OBJECT_TYPE; -} - bool Map::CanTransition() const { // Only JSObject and subtypes have map transitions and back pointers. - return IsJSObject(instance_type()); + return InstanceTypeChecker::IsJSObject(instance_type()); } +#define DEF_TESTER(Type, ...) \ + bool Map::Is##Type##Map() const { \ + return InstanceTypeChecker::Is##Type(instance_type()); \ + } +INSTANCE_TYPE_CHECKERS(DEF_TESTER) +#undef DEF_TESTER + bool Map::IsBooleanMap() const { return this == GetReadOnlyRoots().boolean_map(); } -bool Map::IsNullMap() const { return this == GetReadOnlyRoots().null_map(); } - -bool Map::IsUndefinedMap() const { - return this == GetReadOnlyRoots().undefined_map(); -} - bool Map::IsNullOrUndefinedMap() const { - return IsNullMap() || IsUndefinedMap(); + return this == GetReadOnlyRoots().null_map() || + this == GetReadOnlyRoots().undefined_map(); } bool Map::IsPrimitiveMap() const { return instance_type() <= LAST_PRIMITIVE_TYPE; } -bool Map::IsJSReceiverMap() const { - STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); - return instance_type() >= FIRST_JS_RECEIVER_TYPE; -} -bool Map::IsJSObjectMap() const { return IsJSObject(instance_type()); } -bool Map::IsJSPromiseMap() const { return instance_type() == JS_PROMISE_TYPE; } -bool Map::IsJSArrayMap() const { return instance_type() == JS_ARRAY_TYPE; } -bool Map::IsJSFunctionMap() const { - return instance_type() == JS_FUNCTION_TYPE; -} -bool Map::IsStringMap() const { return instance_type() < FIRST_NONSTRING_TYPE; } -bool Map::IsJSProxyMap() const { return instance_type() == JS_PROXY_TYPE; } -bool Map::IsJSGlobalProxyMap() const { - return instance_type() == JS_GLOBAL_PROXY_TYPE; -} -bool Map::IsJSGlobalObjectMap() const { - return instance_type() == JS_GLOBAL_OBJECT_TYPE; -} -bool Map::IsJSTypedArrayMap() const { - return instance_type() == JS_TYPED_ARRAY_TYPE; -} -bool Map::IsJSDataViewMap() const { - return instance_type() == JS_DATA_VIEW_TYPE; -} Object* Map::prototype() const { return READ_FIELD(this, kPrototypeOffset); } diff --git a/deps/v8/src/objects/map.h b/deps/v8/src/objects/map.h index 397f874610..5f6b173cd3 100644 --- a/deps/v8/src/objects/map.h +++ b/deps/v8/src/objects/map.h @@ -37,9 +37,10 @@ namespace internal { V(FreeSpace) \ V(JSApiObject) \ V(JSArrayBuffer) \ - V(JSFunction) \ + V(JSDataView) \ V(JSObject) \ V(JSObjectFast) \ + V(JSTypedArray) \ V(JSWeakCollection) \ V(Map) \ V(NativeContext) \ @@ -403,9 +404,6 @@ class Map : public HeapObject { inline bool has_fixed_typed_array_elements() const; inline bool has_dictionary_elements() const; - static bool IsValidElementsTransition(ElementsKind from_kind, - ElementsKind to_kind); - // Returns true if the current map doesn't have DICTIONARY_ELEMENTS but if a // map with DICTIONARY_ELEMENTS was found in the prototype chain. bool DictionaryElementsInPrototypeChainOnly(Isolate* isolate); @@ -471,8 +469,6 @@ class Map : public HeapObject { bool InstancesNeedRewriting(Map* target, int target_number_of_fields, int target_inobject, int target_unused, int* old_number_of_fields) const; - // TODO(ishell): moveit! - static Handle<Map> GeneralizeAllFields(Isolate* isolate, Handle<Map> map); V8_WARN_UNUSED_RESULT static Handle<FieldType> GeneralizeFieldType( Representation rep1, Handle<FieldType> type1, Representation rep2, Handle<FieldType> type2, Isolate* isolate); @@ -693,13 +689,13 @@ class Map : public HeapObject { // Maximal number of fast properties. Used to restrict the number of map // transitions to avoid an explosion in the number of maps for objects used as // dictionaries. - inline bool TooManyFastProperties(StoreFromKeyed store_mode) const; + inline bool TooManyFastProperties(StoreOrigin store_origin) const; static Handle<Map> TransitionToDataProperty(Isolate* isolate, Handle<Map> map, Handle<Name> name, Handle<Object> value, PropertyAttributes attributes, PropertyConstness constness, - StoreFromKeyed store_mode); + StoreOrigin store_origin); static Handle<Map> TransitionToAccessorProperty( Isolate* isolate, Handle<Map> map, Handle<Name> name, int descriptor, Handle<Object> getter, Handle<Object> setter, @@ -756,27 +752,14 @@ class Map : public HeapObject { Map* FindElementsKindTransitionedMap(Isolate* isolate, MapHandles const& candidates); - inline static bool IsJSObject(InstanceType type); - inline bool CanTransition() const; +#define DECL_TESTER(Type, ...) inline bool Is##Type##Map() const; + INSTANCE_TYPE_CHECKERS(DECL_TESTER) +#undef DECL_TESTER inline bool IsBooleanMap() const; - inline bool IsNullMap() const; - inline bool IsUndefinedMap() const; inline bool IsNullOrUndefinedMap() const; inline bool IsPrimitiveMap() const; - inline bool IsJSReceiverMap() const; - inline bool IsJSObjectMap() const; - inline bool IsJSPromiseMap() const; - inline bool IsJSArrayMap() const; - inline bool IsJSFunctionMap() const; - inline bool IsStringMap() const; - inline bool IsJSProxyMap() const; - inline bool IsModuleMap() const; - inline bool IsJSGlobalProxyMap() const; - inline bool IsJSGlobalObjectMap() const; - inline bool IsJSTypedArrayMap() const; - inline bool IsJSDataViewMap() const; inline bool IsSpecialReceiverMap() const; inline bool IsCustomElementsReceiverMap() const; @@ -945,7 +928,7 @@ class Map : public HeapObject { void UpdateFieldType(Isolate* isolate, int descriptor_number, Handle<Name> name, PropertyConstness new_constness, Representation new_representation, - MaybeObjectHandle new_wrapped_type); + const MaybeObjectHandle& new_wrapped_type); // TODO(ishell): Move to MapUpdater. void PrintReconfiguration(Isolate* isolate, FILE* file, int modify_index, @@ -971,9 +954,6 @@ class Map : public HeapObject { class NormalizedMapCache : public WeakFixedArray, public NeverReadOnlySpaceObject { public: - using NeverReadOnlySpaceObject::GetHeap; - using NeverReadOnlySpaceObject::GetIsolate; - static Handle<NormalizedMapCache> New(Isolate* isolate); V8_WARN_UNUSED_RESULT MaybeHandle<Map> Get(Handle<Map> fast_map, diff --git a/deps/v8/src/objects/maybe-object-inl.h b/deps/v8/src/objects/maybe-object-inl.h index fa3cd8c14f..6d2bc6a9ab 100644 --- a/deps/v8/src/objects/maybe-object-inl.h +++ b/deps/v8/src/objects/maybe-object-inl.h @@ -20,29 +20,24 @@ bool MaybeObject::ToSmi(Smi** value) { return false; } -Smi* MaybeObject::ToSmi() { - DCHECK(HAS_SMI_TAG(this)); - return Smi::cast(reinterpret_cast<Object*>(this)); -} - -bool MaybeObject::IsStrongOrWeakHeapObject() const { - if (IsSmi() || IsClearedWeakHeapObject()) { +bool MaybeObject::IsStrongOrWeak() const { + if (IsSmi() || IsCleared()) { return false; } return true; } -bool MaybeObject::ToStrongOrWeakHeapObject(HeapObject** result) { - if (IsSmi() || IsClearedWeakHeapObject()) { +bool MaybeObject::GetHeapObject(HeapObject** result) { + if (IsSmi() || IsCleared()) { return false; } *result = GetHeapObject(); return true; } -bool MaybeObject::ToStrongOrWeakHeapObject( - HeapObject** result, HeapObjectReferenceType* reference_type) { - if (IsSmi() || IsClearedWeakHeapObject()) { +bool MaybeObject::GetHeapObject(HeapObject** result, + HeapObjectReferenceType* reference_type) { + if (IsSmi() || IsCleared()) { return false; } *reference_type = HasWeakHeapObjectTag(this) @@ -52,11 +47,11 @@ bool MaybeObject::ToStrongOrWeakHeapObject( return true; } -bool MaybeObject::IsStrongHeapObject() const { +bool MaybeObject::IsStrong() const { return !HasWeakHeapObjectTag(this) && !IsSmi(); } -bool MaybeObject::ToStrongHeapObject(HeapObject** result) { +bool MaybeObject::GetHeapObjectIfStrong(HeapObject** result) { if (!HasWeakHeapObjectTag(this) && !IsSmi()) { *result = reinterpret_cast<HeapObject*>(this); return true; @@ -64,35 +59,33 @@ bool MaybeObject::ToStrongHeapObject(HeapObject** result) { return false; } -HeapObject* MaybeObject::ToStrongHeapObject() { - DCHECK(IsStrongHeapObject()); +HeapObject* MaybeObject::GetHeapObjectAssumeStrong() { + DCHECK(IsStrong()); return reinterpret_cast<HeapObject*>(this); } -bool MaybeObject::IsWeakHeapObject() const { - return HasWeakHeapObjectTag(this) && !IsClearedWeakHeapObject(); +bool MaybeObject::IsWeak() const { + return HasWeakHeapObjectTag(this) && !IsCleared(); } -bool MaybeObject::IsWeakOrClearedHeapObject() const { - return HasWeakHeapObjectTag(this); -} +bool MaybeObject::IsWeakOrCleared() const { return HasWeakHeapObjectTag(this); } -bool MaybeObject::ToWeakHeapObject(HeapObject** result) { - if (HasWeakHeapObjectTag(this) && !IsClearedWeakHeapObject()) { +bool MaybeObject::GetHeapObjectIfWeak(HeapObject** result) { + if (IsWeak()) { *result = GetHeapObject(); return true; } return false; } -HeapObject* MaybeObject::ToWeakHeapObject() { - DCHECK(IsWeakHeapObject()); +HeapObject* MaybeObject::GetHeapObjectAssumeWeak() { + DCHECK(IsWeak()); return GetHeapObject(); } HeapObject* MaybeObject::GetHeapObject() { DCHECK(!IsSmi()); - DCHECK(!IsClearedWeakHeapObject()); + DCHECK(!IsCleared()); return RemoveWeakHeapObjectMask(reinterpret_cast<HeapObjectReference*>(this)); } @@ -103,15 +96,10 @@ Object* MaybeObject::GetHeapObjectOrSmi() { return GetHeapObject(); } -bool MaybeObject::IsObject() const { return IsSmi() || IsStrongHeapObject(); } - -Object* MaybeObject::ToObject() { - DCHECK(!HasWeakHeapObjectTag(this)); - return reinterpret_cast<Object*>(this); -} +bool MaybeObject::IsObject() const { return IsSmi() || IsStrong(); } MaybeObject* MaybeObject::MakeWeak(MaybeObject* object) { - DCHECK(object->IsStrongOrWeakHeapObject()); + DCHECK(object->IsStrongOrWeak()); return AddWeakHeapObjectMask(object); } diff --git a/deps/v8/src/objects/maybe-object.h b/deps/v8/src/objects/maybe-object.h index 84c8538224..0d55ff859c 100644 --- a/deps/v8/src/objects/maybe-object.h +++ b/deps/v8/src/objects/maybe-object.h @@ -5,6 +5,7 @@ #ifndef V8_OBJECTS_MAYBE_OBJECT_H_ #define V8_OBJECTS_MAYBE_OBJECT_H_ +#include "include/v8-internal.h" #include "include/v8.h" #include "src/globals.h" #include "src/objects.h" @@ -23,30 +24,53 @@ class MaybeObject { public: bool IsSmi() const { return HAS_SMI_TAG(this); } inline bool ToSmi(Smi** value); - inline Smi* ToSmi(); - bool IsClearedWeakHeapObject() const { + bool IsCleared() const { return ::v8::internal::IsClearedWeakHeapObject(this); } - inline bool IsStrongOrWeakHeapObject() const; - inline bool ToStrongOrWeakHeapObject(HeapObject** result); - inline bool ToStrongOrWeakHeapObject(HeapObject** result, - HeapObjectReferenceType* reference_type); - inline bool IsStrongHeapObject() const; - inline bool ToStrongHeapObject(HeapObject** result); - inline HeapObject* ToStrongHeapObject(); - inline bool IsWeakHeapObject() const; - inline bool IsWeakOrClearedHeapObject() const; - inline bool ToWeakHeapObject(HeapObject** result); - inline HeapObject* ToWeakHeapObject(); - - // Returns the HeapObject pointed to (either strongly or weakly). + inline bool IsStrongOrWeak() const; + inline bool IsStrong() const; + + // If this MaybeObject is a strong pointer to a HeapObject, returns true and + // sets *result. Otherwise returns false. + inline bool GetHeapObjectIfStrong(HeapObject** result); + + // DCHECKs that this MaybeObject is a strong pointer to a HeapObject and + // returns the HeapObject. + inline HeapObject* GetHeapObjectAssumeStrong(); + + inline bool IsWeak() const; + inline bool IsWeakOrCleared() const; + + // If this MaybeObject is a weak pointer to a HeapObject, returns true and + // sets *result. Otherwise returns false. + inline bool GetHeapObjectIfWeak(HeapObject** result); + + // DCHECKs that this MaybeObject is a weak pointer to a HeapObject and + // returns the HeapObject. + inline HeapObject* GetHeapObjectAssumeWeak(); + + // If this MaybeObject is a strong or weak pointer to a HeapObject, returns + // true and sets *result. Otherwise returns false. + inline bool GetHeapObject(HeapObject** result); + inline bool GetHeapObject(HeapObject** result, + HeapObjectReferenceType* reference_type); + + // DCHECKs that this MaybeObject is a strong or a weak pointer to a HeapObject + // and returns the HeapObject. inline HeapObject* GetHeapObject(); + + // DCHECKs that this MaybeObject is a strong or a weak pointer to a HeapObject + // or a SMI and returns the HeapObject or SMI. inline Object* GetHeapObjectOrSmi(); inline bool IsObject() const; - inline Object* ToObject(); + template <typename T> + T* cast() { + DCHECK(!HasWeakHeapObjectTag(this)); + return T::cast(reinterpret_cast<Object*>(this)); + } static MaybeObject* FromSmi(Smi* smi) { DCHECK(HAS_SMI_TAG(smi)); diff --git a/deps/v8/src/objects/microtask-queue-inl.h b/deps/v8/src/objects/microtask-queue-inl.h new file mode 100644 index 0000000000..8d93ee5226 --- /dev/null +++ b/deps/v8/src/objects/microtask-queue-inl.h @@ -0,0 +1,28 @@ +// Copyright 2018 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. + +#ifndef V8_OBJECTS_MICROTASK_QUEUE_INL_H_ +#define V8_OBJECTS_MICROTASK_QUEUE_INL_H_ + +#include "src/objects/microtask-queue.h" + +#include "src/objects-inl.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +CAST_ACCESSOR(MicrotaskQueue) +ACCESSORS(MicrotaskQueue, queue, FixedArray, kQueueOffset) +SMI_ACCESSORS(MicrotaskQueue, pending_microtask_count, + kPendingMicrotaskCountOffset) + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_MICROTASK_QUEUE_INL_H_ diff --git a/deps/v8/src/objects/microtask-queue.cc b/deps/v8/src/objects/microtask-queue.cc new file mode 100644 index 0000000000..a8905acd36 --- /dev/null +++ b/deps/v8/src/objects/microtask-queue.cc @@ -0,0 +1,40 @@ +// Copyright 2018 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. + +#include "src/objects/microtask-queue.h" + +#include "src/objects/microtask-queue-inl.h" + +namespace v8 { +namespace internal { + +// DCHECK requires this for taking the reference of it. +constexpr int MicrotaskQueue::kMinimumQueueCapacity; + +// static +void MicrotaskQueue::EnqueueMicrotask(Isolate* isolate, + Handle<MicrotaskQueue> microtask_queue, + Handle<Microtask> microtask) { + Handle<FixedArray> queue(microtask_queue->queue(), isolate); + int num_tasks = microtask_queue->pending_microtask_count(); + DCHECK_LE(num_tasks, queue->length()); + if (num_tasks == queue->length()) { + queue = isolate->factory()->CopyFixedArrayAndGrow( + queue, std::max(num_tasks, kMinimumQueueCapacity)); + microtask_queue->set_queue(*queue); + } + DCHECK_LE(kMinimumQueueCapacity, queue->length()); + DCHECK_LT(num_tasks, queue->length()); + DCHECK(queue->get(num_tasks)->IsUndefined(isolate)); + queue->set(num_tasks, *microtask); + microtask_queue->set_pending_microtask_count(num_tasks + 1); +} + +// static +void MicrotaskQueue::RunMicrotasks(Handle<MicrotaskQueue> microtask_queue) { + UNIMPLEMENTED(); +} + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/objects/microtask-queue.h b/deps/v8/src/objects/microtask-queue.h new file mode 100644 index 0000000000..bb14cfb498 --- /dev/null +++ b/deps/v8/src/objects/microtask-queue.h @@ -0,0 +1,55 @@ +// Copyright 2018 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. + +#ifndef V8_OBJECTS_MICROTASK_QUEUE_H_ +#define V8_OBJECTS_MICROTASK_QUEUE_H_ + +#include "src/objects.h" +#include "src/objects/microtask.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +class V8_EXPORT_PRIVATE MicrotaskQueue : public Struct { + public: + DECL_CAST(MicrotaskQueue) + DECL_VERIFIER(MicrotaskQueue) + DECL_PRINTER(MicrotaskQueue) + + // A FixedArray that the queued microtasks are stored. + // The first |pending_microtask_count| slots contains Microtask instance + // for each, and followings are undefined_value if any. + DECL_ACCESSORS(queue, FixedArray) + + // The number of microtasks queued in |queue|. This must be less or equal to + // the length of |queue|. + DECL_INT_ACCESSORS(pending_microtask_count) + + // Enqueues |microtask| to |microtask_queue|. + static void EnqueueMicrotask(Isolate* isolate, + Handle<MicrotaskQueue> microtask_queue, + Handle<Microtask> microtask); + + // Runs all enqueued microtasks. + static void RunMicrotasks(Handle<MicrotaskQueue> microtask_queue); + + static constexpr int kMinimumQueueCapacity = 8; + + static const int kQueueOffset = HeapObject::kHeaderSize; + static const int kPendingMicrotaskCountOffset = kQueueOffset + kPointerSize; + static const int kSize = kPendingMicrotaskCountOffset + kPointerSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(MicrotaskQueue); +}; + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_MICROTASK_QUEUE_H_ diff --git a/deps/v8/src/objects/module.cc b/deps/v8/src/objects/module.cc index 02a94c446b..c4d2626e60 100644 --- a/deps/v8/src/objects/module.cc +++ b/deps/v8/src/objects/module.cc @@ -18,8 +18,6 @@ namespace v8 { namespace internal { -namespace { - struct ModuleHandleHash { V8_INLINE size_t operator()(Handle<Module> module) const { return module->hash(); @@ -82,8 +80,6 @@ class UnorderedStringMap zone)) {} }; -} // anonymous namespace - class Module::ResolveSet : public std::unordered_map< Handle<Module>, UnorderedStringSet*, ModuleHandleHash, @@ -106,22 +102,18 @@ class Module::ResolveSet Zone* zone_; }; -namespace { - -int ExportIndex(int cell_index) { +int Module::ExportIndex(int cell_index) { DCHECK_EQ(ModuleDescriptor::GetCellIndexKind(cell_index), ModuleDescriptor::kExport); return cell_index - 1; } -int ImportIndex(int cell_index) { +int Module::ImportIndex(int cell_index) { DCHECK_EQ(ModuleDescriptor::GetCellIndexKind(cell_index), ModuleDescriptor::kImport); return -cell_index - 1; } -} // anonymous namespace - void Module::CreateIndirectExport(Isolate* isolate, Handle<Module> module, Handle<String> name, Handle<ModuleInfoEntry> entry) { diff --git a/deps/v8/src/objects/module.h b/deps/v8/src/objects/module.h index 4612d73c89..fd9f9ace80 100644 --- a/deps/v8/src/objects/module.h +++ b/deps/v8/src/objects/module.h @@ -7,6 +7,7 @@ #include "src/objects.h" #include "src/objects/fixed-array.h" +#include "src/objects/js-objects.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" @@ -27,9 +28,6 @@ class Zone; // The runtime representation of an ECMAScript module. class Module : public Struct, public NeverReadOnlySpaceObject { public: - using NeverReadOnlySpaceObject::GetHeap; - using NeverReadOnlySpaceObject::GetIsolate; - DECL_CAST(Module) DECL_VERIFIER(Module) DECL_PRINTER(Module) @@ -111,6 +109,9 @@ class Module : public Struct, public NeverReadOnlySpaceObject { static void StoreVariable(Handle<Module> module, int cell_index, Handle<Object> value); + static int ImportIndex(int cell_index); + static int ExportIndex(int cell_index); + // Get the namespace object for [module_request] of [module]. If it doesn't // exist yet, it is created. static Handle<JSModuleNamespace> GetModuleNamespace(Isolate* isolate, diff --git a/deps/v8/src/objects/name-inl.h b/deps/v8/src/objects/name-inl.h index e768a40ec2..512e47875c 100644 --- a/deps/v8/src/objects/name-inl.h +++ b/deps/v8/src/objects/name-inl.h @@ -19,24 +19,25 @@ CAST_ACCESSOR(Name) CAST_ACCESSOR(Symbol) ACCESSORS(Symbol, name, Object, kNameOffset) -SMI_ACCESSORS(Symbol, flags, kFlagsOffset) -BOOL_ACCESSORS(Symbol, flags, is_private, kPrivateBit) -BOOL_ACCESSORS(Symbol, flags, is_well_known_symbol, kWellKnownSymbolBit) -BOOL_ACCESSORS(Symbol, flags, is_public, kPublicBit) -BOOL_ACCESSORS(Symbol, flags, is_interesting_symbol, kInterestingSymbolBit) +INT_ACCESSORS(Symbol, flags, kFlagsOffset) +BIT_FIELD_ACCESSORS(Symbol, flags, is_private, Symbol::IsPrivateBit) +BIT_FIELD_ACCESSORS(Symbol, flags, is_well_known_symbol, + Symbol::IsWellKnownSymbolBit) +BIT_FIELD_ACCESSORS(Symbol, flags, is_public, Symbol::IsPublicBit) +BIT_FIELD_ACCESSORS(Symbol, flags, is_interesting_symbol, + Symbol::IsInterestingSymbolBit) bool Symbol::is_private_field() const { - bool value = BooleanBit::get(flags(), kPrivateFieldBit); + bool value = Symbol::IsPrivateFieldBit::decode(flags()); DCHECK_IMPLIES(value, is_private()); return value; } void Symbol::set_is_private_field() { - int old_value = flags(); // TODO(gsathya): Re-order the bits to have these next to each other // and just do the bit shifts once. - set_flags(BooleanBit::set(old_value, kPrivateBit, true) | - BooleanBit::set(old_value, kPrivateFieldBit, true)); + set_flags(Symbol::IsPrivateBit::update(flags(), true)); + set_flags(Symbol::IsPrivateFieldBit::update(flags(), true)); } bool Name::IsUniqueName() const { @@ -51,13 +52,6 @@ uint32_t Name::hash_field() { void Name::set_hash_field(uint32_t value) { WRITE_UINT32_FIELD(this, kHashFieldOffset, value); -#if V8_HOST_ARCH_64_BIT -#if V8_TARGET_LITTLE_ENDIAN - WRITE_UINT32_FIELD(this, kHashFieldSlot + kInt32Size, 0); -#else - WRITE_UINT32_FIELD(this, kHashFieldSlot, 0); -#endif -#endif } bool Name::Equals(Name* other) { diff --git a/deps/v8/src/objects/name.h b/deps/v8/src/objects/name.h index 06e08deb82..bcc1f2c27d 100644 --- a/deps/v8/src/objects/name.h +++ b/deps/v8/src/objects/name.h @@ -67,13 +67,8 @@ class Name : public HeapObject { int NameShortPrint(Vector<char> str); // Layout description. - static const int kHashFieldSlot = HeapObject::kHeaderSize; -#if V8_TARGET_LITTLE_ENDIAN || !V8_HOST_ARCH_64_BIT - static const int kHashFieldOffset = kHashFieldSlot; -#else - static const int kHashFieldOffset = kHashFieldSlot + kInt32Size; -#endif - static const int kSize = kHashFieldSlot + kPointerSize; + static const int kHashFieldOffset = HeapObject::kHeaderSize; + static const int kHeaderSize = kHashFieldOffset + kInt32Size; // Mask constant for checking if a name has a computed hash code // and if it is a string that is an array index. The least significant bit @@ -181,20 +176,22 @@ class Symbol : public Name { DECL_VERIFIER(Symbol) // Layout description. - static const int kNameOffset = Name::kSize; - static const int kFlagsOffset = kNameOffset + kPointerSize; - static const int kSize = kFlagsOffset + kPointerSize; - - // Flags layout. - static const int kPrivateBit = 0; - static const int kWellKnownSymbolBit = 1; - static const int kPublicBit = 2; - static const int kInterestingSymbolBit = 3; - static const int kPrivateFieldBit = 4; - - typedef FixedBodyDescriptor<kNameOffset, kFlagsOffset, kSize> BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; + static const int kFlagsOffset = Name::kHeaderSize; + static const int kNameOffset = kFlagsOffset + kInt32Size; + static const int kSize = kNameOffset + kPointerSize; + +// Flags layout. +#define FLAGS_BIT_FIELDS(V, _) \ + V(IsPrivateBit, bool, 1, _) \ + V(IsWellKnownSymbolBit, bool, 1, _) \ + V(IsPublicBit, bool, 1, _) \ + V(IsInterestingSymbolBit, bool, 1, _) \ + V(IsPrivateFieldBit, bool, 1, _) + + DEFINE_BIT_FIELDS(FLAGS_BIT_FIELDS) +#undef FLAGS_BIT_FIELDS + + typedef FixedBodyDescriptor<kNameOffset, kSize, kSize> BodyDescriptor; void SymbolShortPrint(std::ostream& os); diff --git a/deps/v8/src/objects/object-macros-undef.h b/deps/v8/src/objects/object-macros-undef.h index 8176bb0324..a0c19cab5c 100644 --- a/deps/v8/src/objects/object-macros-undef.h +++ b/deps/v8/src/objects/object-macros-undef.h @@ -46,6 +46,8 @@ #undef WRITE_INTPTR_FIELD #undef RELAXED_READ_INTPTR_FIELD #undef RELAXED_WRITE_INTPTR_FIELD +#undef READ_UINTPTR_FIELD +#undef WRITE_UINTPTR_FIELD #undef READ_UINT8_FIELD #undef WRITE_UINT8_FIELD #undef READ_INT8_FIELD diff --git a/deps/v8/src/objects/object-macros.h b/deps/v8/src/objects/object-macros.h index 9ec24a62f7..c97f59f9c0 100644 --- a/deps/v8/src/objects/object-macros.h +++ b/deps/v8/src/objects/object-macros.h @@ -290,6 +290,12 @@ #define WRITE_INTPTR_FIELD(p, offset, value) \ (*reinterpret_cast<intptr_t*>(FIELD_ADDR(p, offset)) = value) +#define READ_UINTPTR_FIELD(p, offset) \ + (*reinterpret_cast<const uintptr_t*>(FIELD_ADDR(p, offset))) + +#define WRITE_UINTPTR_FIELD(p, offset, value) \ + (*reinterpret_cast<uintptr_t*>(FIELD_ADDR(p, offset)) = value) + #define READ_UINT8_FIELD(p, offset) \ (*reinterpret_cast<const uint8_t*>(FIELD_ADDR(p, offset))) diff --git a/deps/v8/src/objects/ordered-hash-table-inl.h b/deps/v8/src/objects/ordered-hash-table-inl.h index 76b0692c46..76343c21ed 100644 --- a/deps/v8/src/objects/ordered-hash-table-inl.h +++ b/deps/v8/src/objects/ordered-hash-table-inl.h @@ -13,20 +13,20 @@ namespace v8 { namespace internal { -int OrderedHashSet::GetMapRootIndex() { - return Heap::kOrderedHashSetMapRootIndex; +RootIndex OrderedHashSet::GetMapRootIndex() { + return RootIndex::kOrderedHashSetMap; } -int OrderedHashMap::GetMapRootIndex() { - return Heap::kOrderedHashMapMapRootIndex; +RootIndex OrderedHashMap::GetMapRootIndex() { + return RootIndex::kOrderedHashMapMap; } -int SmallOrderedHashMap::GetMapRootIndex() { - return Heap::kSmallOrderedHashMapMapRootIndex; +RootIndex SmallOrderedHashMap::GetMapRootIndex() { + return RootIndex::kSmallOrderedHashMapMap; } -int SmallOrderedHashSet::GetMapRootIndex() { - return Heap::kSmallOrderedHashSetMapRootIndex; +RootIndex SmallOrderedHashSet::GetMapRootIndex() { + return RootIndex::kSmallOrderedHashSetMap; } inline Object* OrderedHashMap::ValueAt(int entry) { diff --git a/deps/v8/src/objects/ordered-hash-table.cc b/deps/v8/src/objects/ordered-hash-table.cc index fdafce56ae..171e4dfae3 100644 --- a/deps/v8/src/objects/ordered-hash-table.cc +++ b/deps/v8/src/objects/ordered-hash-table.cc @@ -26,7 +26,7 @@ Handle<Derived> OrderedHashTable<Derived, entrysize>::Allocate( } int num_buckets = capacity / kLoadFactor; Handle<FixedArray> backing_store = isolate->factory()->NewFixedArrayWithMap( - static_cast<Heap::RootListIndex>(Derived::GetMapRootIndex()), + Derived::GetMapRootIndex(), kHashTableStartIndex + num_buckets + (capacity * kEntrySize), pretenure); Handle<Derived> table = Handle<Derived>::cast(backing_store); for (int i = 0; i < num_buckets; ++i) { diff --git a/deps/v8/src/objects/ordered-hash-table.h b/deps/v8/src/objects/ordered-hash-table.h index 0ee0f71c5c..6c606efc75 100644 --- a/deps/v8/src/objects/ordered-hash-table.h +++ b/deps/v8/src/objects/ordered-hash-table.h @@ -7,6 +7,7 @@ #include "src/globals.h" #include "src/objects/fixed-array.h" +#include "src/objects/js-objects.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" @@ -231,7 +232,7 @@ class OrderedHashSet : public OrderedHashTable<OrderedHashSet, 1> { Handle<OrderedHashSet> table, GetKeysConversion convert); static HeapObject* GetEmpty(ReadOnlyRoots ro_roots); - static inline int GetMapRootIndex(); + static inline RootIndex GetMapRootIndex(); static inline bool Is(Handle<HeapObject> table); }; @@ -249,7 +250,7 @@ class OrderedHashMap : public OrderedHashTable<OrderedHashMap, 2> { static Object* GetHash(Isolate* isolate, Object* key); static HeapObject* GetEmpty(ReadOnlyRoots ro_roots); - static inline int GetMapRootIndex(); + static inline RootIndex GetMapRootIndex(); static inline bool Is(Handle<HeapObject> table); static const int kValueOffset = 1; @@ -326,9 +327,6 @@ class SmallOrderedHashTable : public HeapObject { // Iterates only fields in the DataTable. class BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; - // Returns total size in bytes required for a table of given // capacity. static int SizeFor(int capacity) { @@ -554,7 +552,7 @@ class SmallOrderedHashSet : public SmallOrderedHashTable<SmallOrderedHashSet> { Handle<SmallOrderedHashSet> table, Handle<Object> key); static inline bool Is(Handle<HeapObject> table); - static inline int GetMapRootIndex(); + static inline RootIndex GetMapRootIndex(); }; class SmallOrderedHashMap : public SmallOrderedHashTable<SmallOrderedHashMap> { @@ -575,7 +573,7 @@ class SmallOrderedHashMap : public SmallOrderedHashTable<SmallOrderedHashMap> { Handle<Object> key, Handle<Object> value); static inline bool Is(Handle<HeapObject> table); - static inline int GetMapRootIndex(); + static inline RootIndex GetMapRootIndex(); }; // TODO(gsathya): Rename this to OrderedHashTable, after we rename diff --git a/deps/v8/src/objects/promise.h b/deps/v8/src/objects/promise.h index 5ff5dac6f3..0f7b4f23ce 100644 --- a/deps/v8/src/objects/promise.h +++ b/deps/v8/src/objects/promise.h @@ -13,6 +13,8 @@ namespace v8 { namespace internal { +class JSPromise; + // Struct to hold state required for PromiseReactionJob. See the comment on the // PromiseReaction below for details on how this is being managed to reduce the // memory and allocation overhead. This is the base class for the concrete diff --git a/deps/v8/src/objects/property-array-inl.h b/deps/v8/src/objects/property-array-inl.h new file mode 100644 index 0000000000..cb157db5d6 --- /dev/null +++ b/deps/v8/src/objects/property-array-inl.h @@ -0,0 +1,83 @@ +// Copyright 2018 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. + +#ifndef V8_OBJECTS_PROPERTY_ARRAY_INL_H_ +#define V8_OBJECTS_PROPERTY_ARRAY_INL_H_ + +#include "src/objects/property-array.h" + +#include "src/heap/heap-write-barrier-inl.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +CAST_ACCESSOR(PropertyArray) + +Object* PropertyArray::get(int index) const { + DCHECK_GE(index, 0); + DCHECK_LE(index, this->length()); + return RELAXED_READ_FIELD(this, kHeaderSize + index * kPointerSize); +} + +void PropertyArray::set(int index, Object* value) { + DCHECK(IsPropertyArray()); + DCHECK_GE(index, 0); + DCHECK_LT(index, this->length()); + int offset = kHeaderSize + index * kPointerSize; + RELAXED_WRITE_FIELD(this, offset, value); + WRITE_BARRIER(this, offset, value); +} + +void PropertyArray::set(int index, Object* value, WriteBarrierMode mode) { + DCHECK_GE(index, 0); + DCHECK_LT(index, this->length()); + int offset = kHeaderSize + index * kPointerSize; + RELAXED_WRITE_FIELD(this, offset, value); + CONDITIONAL_WRITE_BARRIER(this, offset, value, mode); +} + +Object** PropertyArray::data_start() { + return HeapObject::RawField(this, kHeaderSize); +} + +int PropertyArray::length() const { + Object* value_obj = READ_FIELD(this, kLengthAndHashOffset); + int value = Smi::ToInt(value_obj); + return LengthField::decode(value); +} + +void PropertyArray::initialize_length(int len) { + SLOW_DCHECK(len >= 0); + SLOW_DCHECK(len < LengthField::kMax); + WRITE_FIELD(this, kLengthAndHashOffset, Smi::FromInt(len)); +} + +int PropertyArray::synchronized_length() const { + Object* value_obj = ACQUIRE_READ_FIELD(this, kLengthAndHashOffset); + int value = Smi::ToInt(value_obj); + return LengthField::decode(value); +} + +int PropertyArray::Hash() const { + Object* value_obj = READ_FIELD(this, kLengthAndHashOffset); + int value = Smi::ToInt(value_obj); + return HashField::decode(value); +} + +void PropertyArray::SetHash(int hash) { + Object* value_obj = READ_FIELD(this, kLengthAndHashOffset); + int value = Smi::ToInt(value_obj); + value = HashField::update(value, hash); + WRITE_FIELD(this, kLengthAndHashOffset, Smi::FromInt(value)); +} + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_PROPERTY_ARRAY_INL_H_ diff --git a/deps/v8/src/objects/property-array.h b/deps/v8/src/objects/property-array.h new file mode 100644 index 0000000000..70f535a8f0 --- /dev/null +++ b/deps/v8/src/objects/property-array.h @@ -0,0 +1,73 @@ +// Copyright 2018 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. + +#ifndef V8_OBJECTS_PROPERTY_ARRAY_H_ +#define V8_OBJECTS_PROPERTY_ARRAY_H_ + +#include "src/objects.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +class PropertyArray : public HeapObject { + public: + // [length]: length of the array. + inline int length() const; + + // Get the length using acquire loads. + inline int synchronized_length() const; + + // This is only used on a newly allocated PropertyArray which + // doesn't have an existing hash. + inline void initialize_length(int length); + + inline void SetHash(int hash); + inline int Hash() const; + + inline Object* get(int index) const; + + inline void set(int index, Object* value); + // Setter with explicit barrier mode. + inline void set(int index, Object* value, WriteBarrierMode mode); + + // Gives access to raw memory which stores the array's data. + inline Object** data_start(); + + // Garbage collection support. + static constexpr int SizeFor(int length) { + return kHeaderSize + length * kPointerSize; + } + + DECL_CAST(PropertyArray) + DECL_PRINTER(PropertyArray) + DECL_VERIFIER(PropertyArray) + + // Layout description. + static const int kLengthAndHashOffset = HeapObject::kHeaderSize; + static const int kHeaderSize = kLengthAndHashOffset + kPointerSize; + + // Garbage collection support. + typedef FlexibleBodyDescriptor<kHeaderSize> BodyDescriptor; + + static const int kLengthFieldSize = 10; + class LengthField : public BitField<int, 0, kLengthFieldSize> {}; + static const int kMaxLength = LengthField::kMax; + class HashField : public BitField<int, kLengthFieldSize, + kSmiValueSize - kLengthFieldSize - 1> {}; + + static const int kNoHashSentinel = 0; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(PropertyArray); +}; + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_PROPERTY_ARRAY_H_ diff --git a/deps/v8/src/objects/prototype-info-inl.h b/deps/v8/src/objects/prototype-info-inl.h index 298674eb10..24e219d46c 100644 --- a/deps/v8/src/objects/prototype-info-inl.h +++ b/deps/v8/src/objects/prototype-info-inl.h @@ -20,7 +20,7 @@ namespace internal { CAST_ACCESSOR(PrototypeInfo) Map* PrototypeInfo::ObjectCreateMap() { - return Map::cast(object_create_map()->ToWeakHeapObject()); + return Map::cast(object_create_map()->GetHeapObjectAssumeWeak()); } // static @@ -31,7 +31,7 @@ void PrototypeInfo::SetObjectCreateMap(Handle<PrototypeInfo> info, bool PrototypeInfo::HasObjectCreateMap() { MaybeObject* cache = object_create_map(); - return cache->IsWeakHeapObject(); + return cache->IsWeak(); } ACCESSORS(PrototypeInfo, module_namespace, Object, kJSModuleNamespaceOffset) @@ -51,7 +51,7 @@ void PrototypeUsers::MarkSlotEmpty(WeakArrayList* array, int index) { } Smi* PrototypeUsers::empty_slot_index(WeakArrayList* array) { - return array->Get(kEmptySlotIndex)->ToSmi(); + return array->Get(kEmptySlotIndex)->cast<Smi>(); } void PrototypeUsers::set_empty_slot_index(WeakArrayList* array, int index) { diff --git a/deps/v8/src/objects/scope-info.cc b/deps/v8/src/objects/scope-info.cc index 9ec87dcb92..0fa5557e8c 100644 --- a/deps/v8/src/objects/scope-info.cc +++ b/deps/v8/src/objects/scope-info.cc @@ -369,7 +369,6 @@ Handle<ScopeInfo> ScopeInfo::CreateForEmptyFunction(Isolate* isolate) { // static Handle<ScopeInfo> ScopeInfo::CreateForBootstrapping(Isolate* isolate, ScopeType type) { - DCHECK(isolate->bootstrapper()->IsActive()); DCHECK(type == SCRIPT_SCOPE || type == FUNCTION_SCOPE); const int parameter_count = 0; diff --git a/deps/v8/src/objects/scope-info.h b/deps/v8/src/objects/scope-info.h index ac0664f7fb..622c51210b 100644 --- a/deps/v8/src/objects/scope-info.h +++ b/deps/v8/src/objects/scope-info.h @@ -177,7 +177,8 @@ class ScopeInfo : public FixedArray { MaybeHandle<ScopeInfo> outer_scope); static Handle<ScopeInfo> CreateForWithScope( Isolate* isolate, MaybeHandle<ScopeInfo> outer_scope); - static Handle<ScopeInfo> CreateForEmptyFunction(Isolate* isolate); + V8_EXPORT_PRIVATE static Handle<ScopeInfo> CreateForEmptyFunction( + Isolate* isolate); static Handle<ScopeInfo> CreateGlobalThisBinding(Isolate* isolate); // Serializes empty scope info. diff --git a/deps/v8/src/objects/script.h b/deps/v8/src/objects/script.h index 3420b71754..bd789ba2ff 100644 --- a/deps/v8/src/objects/script.h +++ b/deps/v8/src/objects/script.h @@ -17,9 +17,6 @@ namespace internal { // Script describes a script which has been added to the VM. class Script : public Struct, public NeverReadOnlySpaceObject { public: - using NeverReadOnlySpaceObject::GetHeap; - using NeverReadOnlySpaceObject::GetIsolate; - // Script types. enum Type { TYPE_NATIVE = 0, diff --git a/deps/v8/src/objects/shared-function-info-inl.h b/deps/v8/src/objects/shared-function-info-inl.h index 0b4a7effb9..cf057e9ca0 100644 --- a/deps/v8/src/objects/shared-function-info-inl.h +++ b/deps/v8/src/objects/shared-function-info-inl.h @@ -282,60 +282,6 @@ void SharedFunctionInfo::DontAdaptArguments() { set_internal_formal_parameter_count(kDontAdaptArgumentsSentinel); } -int SharedFunctionInfo::StartPosition() const { - Object* maybe_scope_info = name_or_scope_info(); - if (maybe_scope_info->IsScopeInfo()) { - ScopeInfo* info = ScopeInfo::cast(maybe_scope_info); - if (info->HasPositionInfo()) { - return info->StartPosition(); - } - } else if (HasUncompiledData()) { - // Works with or without scope. - return uncompiled_data()->start_position(); - } else if (IsApiFunction() || HasBuiltinId()) { - DCHECK_IMPLIES(HasBuiltinId(), builtin_id() != Builtins::kCompileLazy); - return 0; - } - return kNoSourcePosition; -} - -int SharedFunctionInfo::EndPosition() const { - Object* maybe_scope_info = name_or_scope_info(); - if (maybe_scope_info->IsScopeInfo()) { - ScopeInfo* info = ScopeInfo::cast(maybe_scope_info); - if (info->HasPositionInfo()) { - return info->EndPosition(); - } - } else if (HasUncompiledData()) { - // Works with or without scope. - return uncompiled_data()->end_position(); - } else if (IsApiFunction() || HasBuiltinId()) { - DCHECK_IMPLIES(HasBuiltinId(), builtin_id() != Builtins::kCompileLazy); - return 0; - } - return kNoSourcePosition; -} - -void SharedFunctionInfo::SetPosition(int start_position, int end_position) { - Object* maybe_scope_info = name_or_scope_info(); - if (maybe_scope_info->IsScopeInfo()) { - ScopeInfo* info = ScopeInfo::cast(maybe_scope_info); - if (info->HasPositionInfo()) { - info->SetPositionInfo(start_position, end_position); - } - } else if (HasUncompiledData()) { - if (HasUncompiledDataWithPreParsedScope()) { - // Clear out preparsed scope data, since the position setter invalidates - // any scope data. - ClearPreParsedScopeData(); - } - uncompiled_data()->set_start_position(start_position); - uncompiled_data()->set_end_position(end_position); - } else { - UNREACHABLE(); - } -} - bool SharedFunctionInfo::IsInterpreted() const { return HasBytecodeArray(); } ScopeInfo* SharedFunctionInfo::scope_info() const { @@ -613,21 +559,6 @@ bool SharedFunctionInfo::HasWasmExportedFunctionData() const { return function_data()->IsWasmExportedFunctionData(); } -int SharedFunctionInfo::FunctionLiteralId(Isolate* isolate) const { - // Fast path for the common case when the SFI is uncompiled and so the - // function literal id is already in the uncompiled data. - if (HasUncompiledData()) { - int id = uncompiled_data()->function_literal_id(); - // Make sure the id is what we should have found with the slow path. - DCHECK_EQ(id, FindIndexInScript(isolate)); - return id; - } - - // Otherwise, search for the function in the SFI's script's function list, - // and return its index in that list.e - return FindIndexInScript(isolate); -} - Object* SharedFunctionInfo::script() const { Object* maybe_script = script_or_debug_info(); if (maybe_script->IsDebugInfo()) { diff --git a/deps/v8/src/objects/shared-function-info.h b/deps/v8/src/objects/shared-function-info.h index d5f65a91d1..f43fa61b2f 100644 --- a/deps/v8/src/objects/shared-function-info.h +++ b/deps/v8/src/objects/shared-function-info.h @@ -7,6 +7,7 @@ #include "src/bailout-reason.h" #include "src/objects.h" +#include "src/objects/builtin-function-id.h" #include "src/objects/script.h" // Has to be the last include (doesn't have include guards): @@ -53,8 +54,6 @@ class PreParsedScopeData : public HeapObject { POINTER_SIZE_ALIGN(kUnalignedChildDataStartOffset); class BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; static constexpr int SizeFor(int length) { return kChildDataStartOffset + length * kPointerSize; @@ -114,8 +113,6 @@ class UncompiledDataWithoutPreParsedScope : public UncompiledData { // No extra fields compared to UncompiledData. typedef UncompiledData::BodyDescriptor BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; private: DISALLOW_IMPLICIT_CONSTRUCTORS(UncompiledDataWithoutPreParsedScope); @@ -150,8 +147,6 @@ class UncompiledDataWithPreParsedScope : public UncompiledData { FixedBodyDescriptor<kStartOfPointerFieldsOffset, kEndOfPointerFieldsOffset, kSize>> BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; private: DISALLOW_IMPLICIT_CONSTRUCTORS(UncompiledDataWithPreParsedScope); @@ -179,9 +174,6 @@ class InterpreterData : public Struct { // shared by multiple instances of the function. class SharedFunctionInfo : public HeapObject, public NeverReadOnlySpaceObject { public: - using NeverReadOnlySpaceObject::GetHeap; - using NeverReadOnlySpaceObject::GetIsolate; - static constexpr Object* const kNoSharedNameSentinel = Smi::kZero; // [name]: Returns shared name if it exists or an empty string otherwise. @@ -230,14 +222,14 @@ class SharedFunctionInfo : public HeapObject, public NeverReadOnlySpaceObject { DECL_ACCESSORS(scope_info, ScopeInfo) // End position of this function in the script source. - inline int EndPosition() const; + V8_EXPORT_PRIVATE int EndPosition() const; // Start position of this function in the script source. - inline int StartPosition() const; + V8_EXPORT_PRIVATE int StartPosition() const; // Set the start and end position of this function in the script source. // Updates the scope info if available. - inline void SetPosition(int start_position, int end_position); + V8_EXPORT_PRIVATE void SetPosition(int start_position, int end_position); // [outer scope info | feedback metadata] Shared storage for outer scope info // (on uncompiled functions) and feedback metadata (on compiled functions). @@ -358,7 +350,7 @@ class SharedFunctionInfo : public HeapObject, public NeverReadOnlySpaceObject { inline String* inferred_name(); // Get the function literal id associated with this function, for parsing. - inline int FunctionLiteralId(Isolate* isolate) const; + V8_EXPORT_PRIVATE int FunctionLiteralId(Isolate* isolate) const; // Break infos are contained in DebugInfo, this is a convenience method // to simplify access. @@ -625,8 +617,6 @@ class SharedFunctionInfo : public HeapObject, public NeverReadOnlySpaceObject { typedef FixedBodyDescriptor<kStartOfPointerFieldsOffset, kEndOfPointerFieldsOffset, kAlignedSize> BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; // Bit positions in |flags|. #define FLAGS_BIT_FIELDS(V, _) \ diff --git a/deps/v8/src/objects/stack-frame-info-inl.h b/deps/v8/src/objects/stack-frame-info-inl.h new file mode 100644 index 0000000000..8398c7cb5b --- /dev/null +++ b/deps/v8/src/objects/stack-frame-info-inl.h @@ -0,0 +1,38 @@ +// Copyright 2018 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. + +#ifndef V8_OBJECTS_STACK_FRAME_INFO_INL_H_ +#define V8_OBJECTS_STACK_FRAME_INFO_INL_H_ + +#include "src/objects/stack-frame-info.h" + +#include "src/heap/heap-write-barrier-inl.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +CAST_ACCESSOR(StackFrameInfo) + +SMI_ACCESSORS(StackFrameInfo, line_number, kLineNumberIndex) +SMI_ACCESSORS(StackFrameInfo, column_number, kColumnNumberIndex) +SMI_ACCESSORS(StackFrameInfo, script_id, kScriptIdIndex) +ACCESSORS(StackFrameInfo, script_name, Object, kScriptNameIndex) +ACCESSORS(StackFrameInfo, script_name_or_source_url, Object, + kScriptNameOrSourceUrlIndex) +ACCESSORS(StackFrameInfo, function_name, Object, kFunctionNameIndex) +SMI_ACCESSORS(StackFrameInfo, flag, kFlagIndex) +BOOL_ACCESSORS(StackFrameInfo, flag, is_eval, kIsEvalBit) +BOOL_ACCESSORS(StackFrameInfo, flag, is_constructor, kIsConstructorBit) +BOOL_ACCESSORS(StackFrameInfo, flag, is_wasm, kIsWasmBit) +SMI_ACCESSORS(StackFrameInfo, id, kIdIndex) + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_STACK_FRAME_INFO_INL_H_ diff --git a/deps/v8/src/objects/stack-frame-info.h b/deps/v8/src/objects/stack-frame-info.h new file mode 100644 index 0000000000..4adc37109e --- /dev/null +++ b/deps/v8/src/objects/stack-frame-info.h @@ -0,0 +1,62 @@ +// Copyright 2018 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. + +#ifndef V8_OBJECTS_STACK_FRAME_INFO_H_ +#define V8_OBJECTS_STACK_FRAME_INFO_H_ + +#include "src/objects.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +class StackFrameInfo : public Struct, public NeverReadOnlySpaceObject { + public: + DECL_INT_ACCESSORS(line_number) + DECL_INT_ACCESSORS(column_number) + DECL_INT_ACCESSORS(script_id) + DECL_ACCESSORS(script_name, Object) + DECL_ACCESSORS(script_name_or_source_url, Object) + DECL_ACCESSORS(function_name, Object) + DECL_BOOLEAN_ACCESSORS(is_eval) + DECL_BOOLEAN_ACCESSORS(is_constructor) + DECL_BOOLEAN_ACCESSORS(is_wasm) + DECL_INT_ACCESSORS(flag) + DECL_INT_ACCESSORS(id) + + DECL_CAST(StackFrameInfo) + + // Dispatched behavior. + DECL_PRINTER(StackFrameInfo) + DECL_VERIFIER(StackFrameInfo) + + static const int kLineNumberIndex = Struct::kHeaderSize; + static const int kColumnNumberIndex = kLineNumberIndex + kPointerSize; + static const int kScriptIdIndex = kColumnNumberIndex + kPointerSize; + static const int kScriptNameIndex = kScriptIdIndex + kPointerSize; + static const int kScriptNameOrSourceUrlIndex = + kScriptNameIndex + kPointerSize; + static const int kFunctionNameIndex = + kScriptNameOrSourceUrlIndex + kPointerSize; + static const int kFlagIndex = kFunctionNameIndex + kPointerSize; + static const int kIdIndex = kFlagIndex + kPointerSize; + static const int kSize = kIdIndex + kPointerSize; + + private: + // Bit position in the flag, from least significant bit position. + static const int kIsEvalBit = 0; + static const int kIsConstructorBit = 1; + static const int kIsWasmBit = 2; + + DISALLOW_IMPLICIT_CONSTRUCTORS(StackFrameInfo); +}; + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_STACK_FRAME_INFO_H_ diff --git a/deps/v8/src/objects/string-inl.h b/deps/v8/src/objects/string-inl.h index 39f642063e..349fa31f9d 100644 --- a/deps/v8/src/objects/string-inl.h +++ b/deps/v8/src/objects/string-inl.h @@ -19,8 +19,17 @@ namespace v8 { namespace internal { -SMI_ACCESSORS(String, length, kLengthOffset) -SYNCHRONIZED_SMI_ACCESSORS(String, length, kLengthOffset) +INT32_ACCESSORS(String, length, kLengthOffset) + +int String::synchronized_length() const { + return base::AsAtomic32::Acquire_Load( + reinterpret_cast<const int32_t*>(FIELD_ADDR(this, kLengthOffset))); +} + +void String::synchronized_set_length(int value) { + base::AsAtomic32::Release_Store( + reinterpret_cast<int32_t*>(FIELD_ADDR(this, kLengthOffset)), value); +} CAST_ACCESSOR(ConsString) CAST_ACCESSOR(ExternalOneByteString) @@ -536,9 +545,9 @@ HeapObject* ThinString::unchecked_actual() const { return reinterpret_cast<HeapObject*>(READ_FIELD(this, kActualOffset)); } -bool ExternalString::is_short() const { +bool ExternalString::is_uncached() const { InstanceType type = map()->instance_type(); - return (type & kShortExternalStringMask) == kShortExternalStringTag; + return (type & kUncachedExternalStringMask) == kUncachedExternalStringTag; } Address ExternalString::resource_as_address() { @@ -562,7 +571,7 @@ uint32_t ExternalString::resource_as_uint32() { void ExternalString::set_uint32_as_resource(uint32_t value) { *reinterpret_cast<uintptr_t*>(FIELD_ADDR(this, kResourceOffset)) = value; - if (is_short()) return; + if (is_uncached()) return; const char** data_field = reinterpret_cast<const char**>(FIELD_ADDR(this, kResourceDataOffset)); *data_field = nullptr; @@ -573,7 +582,7 @@ const ExternalOneByteString::Resource* ExternalOneByteString::resource() { } void ExternalOneByteString::update_data_cache() { - if (is_short()) return; + if (is_uncached()) return; const char** data_field = reinterpret_cast<const char**>(FIELD_ADDR(this, kResourceDataOffset)); *data_field = resource()->data(); @@ -609,7 +618,7 @@ const ExternalTwoByteString::Resource* ExternalTwoByteString::resource() { } void ExternalTwoByteString::update_data_cache() { - if (is_short()) return; + if (is_uncached()) return; const uint16_t** data_field = reinterpret_cast<const uint16_t**>(FIELD_ADDR(this, kResourceDataOffset)); *data_field = resource()->data(); @@ -733,8 +742,7 @@ class String::SubStringRange::iterator final { typedef uc16* pointer; typedef uc16& reference; - iterator(const iterator& other) - : content_(other.content_), offset_(other.offset_) {} + iterator(const iterator& other) = default; uc16 operator*() { return content_.Get(offset_); } bool operator==(const iterator& other) const { diff --git a/deps/v8/src/objects/string-table.h b/deps/v8/src/objects/string-table.h index 8003bf1aac..b26e86a381 100644 --- a/deps/v8/src/objects/string-table.h +++ b/deps/v8/src/objects/string-table.h @@ -42,7 +42,7 @@ class StringTableShape : public BaseShape<StringTableKey*> { static inline Handle<Object> AsHandle(Isolate* isolate, Key key); - static inline int GetMapRootIndex(); + static inline RootIndex GetMapRootIndex(); static const int kPrefixSize = 0; static const int kEntrySize = 1; diff --git a/deps/v8/src/objects/string.h b/deps/v8/src/objects/string.h index 4058c7cec3..206bed641c 100644 --- a/deps/v8/src/objects/string.h +++ b/deps/v8/src/objects/string.h @@ -31,7 +31,7 @@ enum RobustnessFlag { ROBUST_STRING_TRAVERSAL, FAST_STRING_TRAVERSAL }; // shortcutting. Keeping these restrictions in mind has proven to be error- // prone and so we no longer put StringShapes in variables unless there is a // concrete performance benefit at that particular point in the code. -class StringShape BASE_EMBEDDED { +class StringShape { public: inline explicit StringShape(const String* s); inline explicit StringShape(Map* s); @@ -264,7 +264,7 @@ class String : public Name { virtual MaybeHandle<String> GetNamedCapture(Handle<String> name, CaptureState* state) = 0; - virtual ~Match() {} + virtual ~Match() = default; }; // ES#sec-getsubstitution @@ -300,11 +300,11 @@ class String : public Name { // do any heap allocations. This is useful when printing stack traces. std::unique_ptr<char[]> ToCString(AllowNullsFlag allow_nulls, RobustnessFlag robustness_flag, int offset, - int length, int* length_output = 0); + int length, int* length_output = nullptr); std::unique_ptr<char[]> ToCString( AllowNullsFlag allow_nulls = DISALLOW_NULLS, RobustnessFlag robustness_flag = FAST_STRING_TRAVERSAL, - int* length_output = 0); + int* length_output = nullptr); bool ComputeArrayIndex(uint32_t* index); @@ -341,8 +341,8 @@ class String : public Name { inline bool IsFlat(); // Layout description. - static const int kLengthOffset = Name::kSize; - static const int kSize = kLengthOffset + kPointerSize; + static const int kLengthOffset = Name::kHeaderSize; + static const int kHeaderSize = kLengthOffset + kInt32Size; // Max char codes. static const int32_t kMaxOneByteCharCode = unibrow::Latin1::kMaxChar; @@ -360,7 +360,7 @@ class String : public Name { // See include/v8.h for the definition. static const int kMaxLength = v8::String::kMaxLength; - static_assert(kMaxLength <= (Smi::kMaxValue / 2 - kSize), + static_assert(kMaxLength <= (Smi::kMaxValue / 2 - kHeaderSize), "Unexpected max String length"); // Max length for computing hash. For strings longer than this limit the @@ -370,9 +370,6 @@ class String : public Name { // Limit for truncation in short printing. static const int kMaxShortPrintLength = 1024; - // Support for regular expressions. - const uc16* GetTwoByteData(unsigned start); - // Helper function for flattening strings. template <typename sinkchar> static void WriteToFlat(String* source, sinkchar* sink, int from, int to); @@ -474,9 +471,6 @@ class SeqString : public String { public: DECL_CAST(SeqString) - // Layout description. - static const int kHeaderSize = String::kSize; - // Truncate the string in-place if possible and return the result. // In case of new_length == 0, the empty string is returned without // truncating the original string. @@ -533,8 +527,6 @@ class SeqOneByteString : public SeqString { STATIC_ASSERT((kMaxSize - kHeaderSize) >= String::kMaxLength); class BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; private: DISALLOW_IMPLICIT_CONSTRUCTORS(SeqOneByteString); @@ -559,9 +551,6 @@ class SeqTwoByteString : public SeqString { // is deterministic. void clear_padding(); - // For regexp code. - const uint16_t* SeqTwoByteStringGetData(unsigned start); - DECL_CAST(SeqTwoByteString) // Garbage collection support. This method is called by the @@ -581,8 +570,6 @@ class SeqTwoByteString : public SeqString { String::kMaxLength); class BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; private: DISALLOW_IMPLICIT_CONSTRUCTORS(SeqTwoByteString); @@ -620,7 +607,7 @@ class ConsString : public String { DECL_CAST(ConsString) // Layout description. - static const int kFirstOffset = POINTER_SIZE_ALIGN(String::kSize); + static const int kFirstOffset = String::kHeaderSize; static const int kSecondOffset = kFirstOffset + kPointerSize; static const int kSize = kSecondOffset + kPointerSize; @@ -629,8 +616,6 @@ class ConsString : public String { typedef FixedBodyDescriptor<kFirstOffset, kSecondOffset + kPointerSize, kSize> BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; DECL_VERIFIER(ConsString) @@ -659,12 +644,10 @@ class ThinString : public String { DECL_VERIFIER(ThinString) // Layout description. - static const int kActualOffset = String::kSize; + static const int kActualOffset = String::kHeaderSize; static const int kSize = kActualOffset + kPointerSize; typedef FixedBodyDescriptor<kActualOffset, kSize, kSize> BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; private: DISALLOW_COPY_AND_ASSIGN(ThinString); @@ -696,7 +679,7 @@ class SlicedString : public String { DECL_CAST(SlicedString) // Layout description. - static const int kParentOffset = POINTER_SIZE_ALIGN(String::kSize); + static const int kParentOffset = String::kHeaderSize; static const int kOffsetOffset = kParentOffset + kPointerSize; static const int kSize = kOffsetOffset + kPointerSize; @@ -706,8 +689,6 @@ class SlicedString : public String { typedef FixedBodyDescriptor<kParentOffset, kOffsetOffset + kPointerSize, kSize> BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; DECL_VERIFIER(SlicedString) @@ -729,13 +710,13 @@ class ExternalString : public String { DECL_CAST(ExternalString) // Layout description. - static const int kResourceOffset = POINTER_SIZE_ALIGN(String::kSize); - static const int kShortSize = kResourceOffset + kPointerSize; + static const int kResourceOffset = String::kHeaderSize; + static const int kUncachedSize = kResourceOffset + kPointerSize; static const int kResourceDataOffset = kResourceOffset + kPointerSize; static const int kSize = kResourceDataOffset + kPointerSize; - // Return whether external string is short (data pointer is not cached). - inline bool is_short() const; + // Return whether the external string data pointer is not cached. + inline bool is_uncached() const; // Size in bytes of the external payload. int ExternalPayloadSize() const; @@ -782,8 +763,6 @@ class ExternalOneByteString : public ExternalString { DECL_CAST(ExternalOneByteString) class BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; private: DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalOneByteString); @@ -823,8 +802,6 @@ class ExternalTwoByteString : public ExternalString { DECL_CAST(ExternalTwoByteString) class BodyDescriptor; - // No weak fields. - typedef BodyDescriptor BodyDescriptorWeak; private: DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalTwoByteString); @@ -837,7 +814,7 @@ class FlatStringReader : public Relocatable { public: FlatStringReader(Isolate* isolate, Handle<String> str); FlatStringReader(Isolate* isolate, Vector<const char> input); - void PostGarbageCollection(); + void PostGarbageCollection() override; inline uc32 Get(int index); template <typename Char> inline Char Get(int index); @@ -855,7 +832,7 @@ class FlatStringReader : public Relocatable { // traversal of the entire string class ConsStringIterator { public: - inline ConsStringIterator() {} + inline ConsStringIterator() = default; inline explicit ConsStringIterator(ConsString* cons_string, int offset = 0) { Reset(cons_string, offset); } diff --git a/deps/v8/src/objects/templates.h b/deps/v8/src/objects/templates.h index 6a229d847b..24cbd18bd2 100644 --- a/deps/v8/src/objects/templates.h +++ b/deps/v8/src/objects/templates.h @@ -15,9 +15,6 @@ namespace internal { class TemplateInfo : public Struct, public NeverReadOnlySpaceObject { public: - using NeverReadOnlySpaceObject::GetHeap; - using NeverReadOnlySpaceObject::GetIsolate; - DECL_ACCESSORS(tag, Object) DECL_ACCESSORS(serial_number, Object) DECL_INT_ACCESSORS(number_of_properties) |