diff --git a/include/v8.h b/include/v8.h index 7a38c1e1ab..4bb570576d 100644 --- a/include/v8.h +++ b/include/v8.h @@ -5283,7 +5283,7 @@ class Internals { static const int kNullValueRootIndex = 7; static const int kTrueValueRootIndex = 8; static const int kFalseValueRootIndex = 9; - static const int kEmptyStringRootIndex = 129; + static const int kEmptyStringRootIndex = 130; static const int kNodeClassIdOffset = 1 * kApiPointerSize; static const int kNodeFlagsOffset = 1 * kApiPointerSize + 3; diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 5ef8d309c3..72d144ff40 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1086,11 +1086,13 @@ bool Genesis::InitializeGlobal(Handle inner_global, CHECK_NOT_EMPTY_HANDLE(isolate, JSObject::SetLocalPropertyIgnoreAttributes( result, factory->length_string(), - factory->undefined_value(), DONT_ENUM)); + factory->undefined_value(), DONT_ENUM, + Object::FORCE_TAGGED)); CHECK_NOT_EMPTY_HANDLE(isolate, JSObject::SetLocalPropertyIgnoreAttributes( result, factory->callee_string(), - factory->undefined_value(), DONT_ENUM)); + factory->undefined_value(), DONT_ENUM, + Object::FORCE_TAGGED)); #ifdef DEBUG LookupResult lookup(isolate); diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 0a6e576e30..5a2ff8aba6 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -199,8 +199,10 @@ DEFINE_bool(pretenuring_call_new, false, "pretenure call new") DEFINE_bool(track_fields, true, "track fields with only smi values") DEFINE_bool(track_double_fields, true, "track fields with double values") DEFINE_bool(track_heap_object_fields, true, "track fields with heap values") +DEFINE_bool(track_computed_fields, true, "track computed boilerplate fields") DEFINE_implication(track_double_fields, track_fields) DEFINE_implication(track_heap_object_fields, track_fields) +DEFINE_implication(track_computed_fields, track_fields) // Flags for data representation optimizations DEFINE_bool(unbox_double_arrays, true, "automatically unbox arrays of doubles") diff --git a/src/heap.cc b/src/heap.cc index 6a89efda5b..15b1479cb7 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -2845,6 +2845,13 @@ bool Heap::CreateInitialObjects() { } set_the_hole_value(Oddball::cast(obj)); + { MaybeObject* maybe_obj = CreateOddball("uninitialized", + Smi::FromInt(-1), + Oddball::kUninitialized); + if (!maybe_obj->ToObject(&obj)) return false; + } + set_uninitialized_value(Oddball::cast(obj)); + { MaybeObject* maybe_obj = CreateOddball("arguments_marker", Smi::FromInt(-4), Oddball::kArgumentMarker); diff --git a/src/heap.h b/src/heap.h index 6d04454516..7d204af2b4 100644 --- a/src/heap.h +++ b/src/heap.h @@ -59,6 +59,7 @@ namespace internal { V(Oddball, null_value, NullValue) \ V(Oddball, true_value, TrueValue) \ V(Oddball, false_value, FalseValue) \ + V(Oddball, uninitialized_value, UninitializedValue) \ V(Map, global_property_cell_map, GlobalPropertyCellMap) \ V(Map, shared_function_info_map, SharedFunctionInfoMap) \ V(Map, meta_map, MetaMap) \ diff --git a/src/objects-debug.cc b/src/objects-debug.cc index 891f0d2302..1d55e26572 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -312,8 +312,9 @@ void JSObject::JSObjectVerify() { Representation r = descriptors->GetDetails(i).representation(); int field = descriptors->GetFieldIndex(i); Object* value = RawFastPropertyAt(field); - if (r.IsSmi()) ASSERT(value->IsSmi()); if (r.IsDouble()) ASSERT(value->IsHeapNumber()); + if (value->IsUninitialized()) continue; + if (r.IsSmi()) ASSERT(value->IsSmi()); if (r.IsHeapObject()) ASSERT(value->IsHeapObject()); } } diff --git a/src/objects-inl.h b/src/objects-inl.h index 956d0886a9..799c960ac0 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -292,6 +292,9 @@ MaybeObject* Object::AllocateNewStorageFor(Heap* heap, PretenureFlag tenure) { if (!FLAG_track_double_fields) return this; if (!representation.IsDouble()) return this; + if (IsUninitialized()) { + return heap->AllocateHeapNumber(0, tenure); + } return heap->AllocateHeapNumber(Number(), tenure); } @@ -530,6 +533,11 @@ bool MaybeObject::IsTheHole() { } +bool MaybeObject::IsUninitialized() { + return !IsFailure() && ToObjectUnchecked()->IsUninitialized(); +} + + Failure* Failure::cast(MaybeObject* obj) { ASSERT(HAS_FAILURE_TAG(obj)); return reinterpret_cast(obj); @@ -845,6 +853,11 @@ bool Object::IsTheHole() { } +bool Object::IsUninitialized() { + return IsOddball() && Oddball::cast(this)->kind() == Oddball::kUninitialized; +} + + bool Object::IsTrue() { return IsOddball() && Oddball::cast(this)->kind() == Oddball::kTrue; } @@ -1541,7 +1554,7 @@ MaybeObject* JSObject::MigrateInstance() { // Converting any field to the most specific type will cause the // GeneralizeFieldRepresentation algorithm to create the most general existing // transition that matches the object. This achieves what is needed. - return GeneralizeFieldRepresentation(0, Representation::Smi()); + return GeneralizeFieldRepresentation(0, Representation::None()); } @@ -2366,7 +2379,6 @@ void DescriptorArray::Set(int descriptor_number, // Range check. ASSERT(descriptor_number < number_of_descriptors()); - ASSERT(!desc->GetDetails().representation().IsNone()); NoIncrementalWriteBarrierSet(this, ToKeyIndex(descriptor_number), desc->GetKey()); @@ -2382,7 +2394,6 @@ void DescriptorArray::Set(int descriptor_number, void DescriptorArray::Set(int descriptor_number, Descriptor* desc) { // Range check. ASSERT(descriptor_number < number_of_descriptors()); - ASSERT(!desc->GetDetails().representation().IsNone()); set(ToKeyIndex(descriptor_number), desc->GetKey()); set(ToValueIndex(descriptor_number), desc->GetValue()); @@ -3617,6 +3628,9 @@ bool Map::CanBeDeprecated() { int descriptor = LastAdded(); for (int i = 0; i <= descriptor; i++) { PropertyDetails details = instance_descriptors()->GetDetails(i); + if (FLAG_track_fields && details.representation().IsNone()) { + return true; + } if (FLAG_track_fields && details.representation().IsSmi()) { return true; } diff --git a/src/objects.cc b/src/objects.cc index 24c60ece90..6900cdc4da 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -1817,7 +1817,8 @@ static bool IsIdentifier(UnicodeCache* cache, Name* name) { MaybeObject* JSObject::AddFastProperty(Name* name, Object* value, PropertyAttributes attributes, - StoreFromKeyed store_mode) { + StoreFromKeyed store_mode, + ValueType value_type) { ASSERT(!IsJSGlobalProxy()); ASSERT(DescriptorArray::kNotFound == map()->instance_descriptors()->Search( @@ -1843,8 +1844,8 @@ MaybeObject* JSObject::AddFastProperty(Name* name, int index = map()->NextFreePropertyIndex(); // Allocate new instance descriptors with (name, index) added - Representation representation = IsJSContextExtensionObject() - ? Representation::Tagged() : value->OptimalRepresentation(); + if (IsJSContextExtensionObject()) value_type = FORCE_TAGGED; + Representation representation = value->OptimalRepresentation(value_type); FieldDescriptor new_field(name, index, attributes, representation); @@ -1961,7 +1962,8 @@ MaybeObject* JSObject::AddProperty(Name* name, PropertyAttributes attributes, StrictModeFlag strict_mode, JSReceiver::StoreFromKeyed store_mode, - ExtensibilityCheck extensibility_check) { + ExtensibilityCheck extensibility_check, + ValueType value_type) { ASSERT(!IsJSGlobalProxy()); Map* map_of_this = map(); Heap* heap = GetHeap(); @@ -1988,7 +1990,8 @@ MaybeObject* JSObject::AddProperty(Name* name, JSFunction::cast(value), attributes); } else { - result = AddFastProperty(name, value, attributes, store_mode); + result = AddFastProperty( + name, value, attributes, store_mode, value_type); } } else { // Normalize the object to prevent very large instance descriptors. @@ -2272,7 +2275,7 @@ bool Map::InstancesNeedRewriting(Map* target, int limit = NumberOfOwnDescriptors(); for (int i = 0; i < limit; i++) { if (new_desc->GetDetails(i).representation().IsDouble() && - old_desc->GetDetails(i).representation().IsSmi()) { + !old_desc->GetDetails(i).representation().IsDouble()) { return true; } } @@ -2343,8 +2346,9 @@ MaybeObject* JSObject::MigrateToMap(Map* new_map) { ? old_descriptors->GetValue(i) : RawFastPropertyAt(old_descriptors->GetFieldIndex(i)); if (FLAG_track_double_fields && - old_details.representation().IsSmi() && + !old_details.representation().IsDouble() && details.representation().IsDouble()) { + if (old_details.representation().IsNone()) value = Smi::FromInt(0); // Objects must be allocated in the old object space, since the // overall number of HeapNumbers needed for the conversion might // exceed the capacity of new space, and we would fail repeatedly @@ -2397,7 +2401,7 @@ MaybeObject* JSObject::GeneralizeFieldRepresentation( MaybeObject* maybe_new_map = map()->GeneralizeRepresentation(modify_index, new_representation); if (!maybe_new_map->To(&new_map)) return maybe_new_map; - ASSERT(map() != new_map || new_map->FindRootMap()->is_deprecated()); + if (map() == new_map) return this; return MigrateToMap(new_map); } @@ -2574,10 +2578,21 @@ MaybeObject* Map::GeneralizeRepresentation(int modify_index, Representation old_representation = old_descriptors->GetDetails(modify_index).representation(); - if (old_representation.IsNone()) { - UNREACHABLE(); + // It's fine to transition from None to anything but double without any + // modification to the object, because the default uninitialized value for + // representation None can be overwritten by both smi and tagged values. + // Doubles, however, would require a box allocation. + if (old_representation.IsNone() && + !new_representation.IsNone() && + !new_representation.IsDouble()) { + if (FLAG_trace_generalization) { + PrintF("initializing representation %i: %p -> %s\n", + modify_index, + static_cast(this), + new_representation.Mnemonic()); + } old_descriptors->SetRepresentation(modify_index, new_representation); - return this; + return old_map; } int descriptors = old_map->NumberOfOwnDescriptors(); @@ -2603,7 +2618,7 @@ MaybeObject* Map::GeneralizeRepresentation(int modify_index, updated_descriptors->GetDetails(modify_index).representation(); if (new_representation.fits_into(updated_representation)) { if (FLAG_trace_generalization && - !(modify_index == 0 && new_representation.IsSmi())) { + !(modify_index == 0 && new_representation.IsNone())) { PropertyDetails old_details = old_descriptors->GetDetails(modify_index); PrintF("migrating to existing map %p(%s) -> %p(%s)\n", static_cast(this), @@ -2641,7 +2656,7 @@ MaybeObject* Map::GeneralizeRepresentation(int modify_index, old_descriptors->GetKey(descriptor), new_descriptors); if (FLAG_trace_generalization && - !(modify_index == 0 && new_representation.IsSmi())) { + !(modify_index == 0 && new_representation.IsNone())) { PrintF("migrating to new map %i: %p(%s) -> %p(%s) (%i steps)\n", modify_index, static_cast(this), @@ -3933,10 +3948,12 @@ Handle JSObject::SetLocalPropertyIgnoreAttributes( Handle object, Handle key, Handle value, - PropertyAttributes attributes) { + PropertyAttributes attributes, + ValueType value_type) { CALL_HEAP_FUNCTION( object->GetIsolate(), - object->SetLocalPropertyIgnoreAttributes(*key, *value, attributes), + object->SetLocalPropertyIgnoreAttributes( + *key, *value, attributes, value_type), Object); } @@ -3944,7 +3961,8 @@ Handle JSObject::SetLocalPropertyIgnoreAttributes( MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( Name* name_raw, Object* value_raw, - PropertyAttributes attributes) { + PropertyAttributes attributes, + ValueType value_type) { // Make sure that the top context does not change when doing callbacks or // interceptor calls. AssertNoContextChange ncc; @@ -3970,13 +3988,16 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( return JSObject::cast(proto)->SetLocalPropertyIgnoreAttributes( name_raw, value_raw, - attributes); + attributes, + value_type); } // Check for accessor in prototype chain removed here in clone. if (!lookup.IsFound()) { // Neither properties nor transitions found. - return AddProperty(name_raw, value_raw, attributes, kNonStrictMode); + return AddProperty( + name_raw, value_raw, attributes, kNonStrictMode, + MAY_BE_STORE_FROM_KEYED, PERFORM_EXTENSIBILITY_CHECK, value_type); } // From this point on everything needs to be handlified. @@ -4003,9 +4024,11 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( } case FIELD: { Representation representation = lookup.representation(); - if (!value->FitsRepresentation(representation)) { + Representation value_representation = + value->OptimalRepresentation(value_type); + if (!value_representation.fits_into(representation)) { MaybeObject* maybe_failure = self->GeneralizeFieldRepresentation( - lookup.GetDescriptorIndex(), value->OptimalRepresentation()); + lookup.GetDescriptorIndex(), value_representation); if (maybe_failure->IsFailure()) return maybe_failure; DescriptorArray* desc = self->map()->instance_descriptors(); int descriptor = lookup.GetDescriptorIndex(); @@ -4046,9 +4069,11 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( if (details.type() == FIELD) { if (attributes == details.attributes()) { Representation representation = details.representation(); - if (!value->FitsRepresentation(representation)) { + Representation value_representation = + value->OptimalRepresentation(value_type); + if (!value_representation.fits_into(representation)) { MaybeObject* maybe_map = transition_map->GeneralizeRepresentation( - descriptor, value->OptimalRepresentation()); + descriptor, value_representation); if (!maybe_map->To(&transition_map)) return maybe_map; Object* back = transition_map->GetBackPointer(); if (back->IsMap()) { diff --git a/src/objects.h b/src/objects.h index b7b32f8ddc..8038709019 100644 --- a/src/objects.h +++ b/src/objects.h @@ -869,6 +869,7 @@ class MaybeObject BASE_EMBEDDED { inline bool IsOutOfMemory(); inline bool IsException(); INLINE(bool IsTheHole()); + INLINE(bool IsUninitialized()); inline bool ToObject(Object** obj) { if (IsFailure()) return false; *obj = reinterpret_cast(this); @@ -1046,6 +1047,7 @@ class Object : public MaybeObject { INLINE(bool IsUndefined()); INLINE(bool IsNull()); INLINE(bool IsTheHole()); // Shadows MaybeObject's implementation. + INLINE(bool IsUninitialized()); INLINE(bool IsTrue()); INLINE(bool IsFalse()); inline bool IsArgumentsMarker(); @@ -1060,16 +1062,24 @@ class Object : public MaybeObject { bool ToInt32(int32_t* value); bool ToUint32(uint32_t* value); - inline Representation OptimalRepresentation() { - if (FLAG_track_fields && IsSmi()) { + // Indicates whether OptimalRepresentation can do its work, or whether it + // always has to return Representation::Tagged(). + enum ValueType { + OPTIMAL_REPRESENTATION, + FORCE_TAGGED + }; + + inline Representation OptimalRepresentation( + ValueType type = OPTIMAL_REPRESENTATION) { + if (!FLAG_track_fields) return Representation::Tagged(); + if (type == FORCE_TAGGED) return Representation::Tagged(); + if (IsSmi()) { return Representation::Smi(); } else if (FLAG_track_double_fields && IsHeapNumber()) { return Representation::Double(); - } else if (FLAG_track_heap_object_fields && !IsUndefined()) { - // Don't track undefined as heapobject because it's also used as temporary - // value for computed fields that may turn out to be Smi. That combination - // will go tagged, so go tagged immediately. - // TODO(verwaest): Change once we track computed boilerplate fields. + } else if (FLAG_track_computed_fields && IsUninitialized()) { + return Representation::None(); + } else if (FLAG_track_heap_object_fields) { ASSERT(IsHeapObject()); return Representation::HeapObject(); } else { @@ -1078,7 +1088,9 @@ class Object : public MaybeObject { } inline bool FitsRepresentation(Representation representation) { - if (FLAG_track_fields && representation.IsSmi()) { + if (FLAG_track_fields && representation.IsNone()) { + return false; + } else if (FLAG_track_fields && representation.IsSmi()) { return IsSmi(); } else if (FLAG_track_double_fields && representation.IsDouble()) { return IsNumber(); @@ -1827,7 +1839,8 @@ class JSObject: public JSReceiver { Handle object, Handle key, Handle value, - PropertyAttributes attributes); + PropertyAttributes attributes, + ValueType value_type = OPTIMAL_REPRESENTATION); static inline Handle ExpectedTransitionKey(Handle map); static inline Handle ExpectedTransitionTarget(Handle map); @@ -1854,7 +1867,8 @@ class JSObject: public JSReceiver { MUST_USE_RESULT MaybeObject* SetLocalPropertyIgnoreAttributes( Name* key, Object* value, - PropertyAttributes attributes); + PropertyAttributes attributes, + ValueType value_type = OPTIMAL_REPRESENTATION); // Retrieve a value in a normalized object given a lookup result. // Handles the special representation of JS global objects. @@ -2216,7 +2230,8 @@ class JSObject: public JSReceiver { Name* name, Object* value, PropertyAttributes attributes, - StoreFromKeyed store_mode = MAY_BE_STORE_FROM_KEYED); + StoreFromKeyed store_mode = MAY_BE_STORE_FROM_KEYED, + ValueType value_type = OPTIMAL_REPRESENTATION); // Add a property to a slow-case object. MUST_USE_RESULT MaybeObject* AddSlowProperty(Name* name, @@ -2230,7 +2245,8 @@ class JSObject: public JSReceiver { PropertyAttributes attributes, StrictModeFlag strict_mode, StoreFromKeyed store_mode = MAY_BE_STORE_FROM_KEYED, - ExtensibilityCheck extensibility_check = PERFORM_EXTENSIBILITY_CHECK); + ExtensibilityCheck extensibility_check = PERFORM_EXTENSIBILITY_CHECK, + ValueType value_type = OPTIMAL_REPRESENTATION); // Convert the object to use the canonical dictionary // representation. If the object is expected to have additional properties @@ -8483,7 +8499,8 @@ class Oddball: public HeapObject { static const byte kNull = 3; static const byte kArgumentMarker = 4; static const byte kUndefined = 5; - static const byte kOther = 6; + static const byte kUninitialized = 6; + static const byte kOther = 7; typedef FixedBodyDescriptor boilerplate_value = GetBoilerplateValue(values->at(i)); if (boilerplate_value->IsTheHole()) { is_holey = true; - } else if (boilerplate_value->IsUndefined()) { + } else if (boilerplate_value->IsUninitialized()) { is_simple = false; JSObject::SetOwnElement( array, i, handle(Smi::FromInt(0), isolate()), kNonStrictMode); @@ -3693,7 +3693,7 @@ Handle Parser::GetBoilerplateValue(Expression* expression) { if (CompileTimeValue::IsCompileTimeValue(expression)) { return CompileTimeValue::GetValue(expression); } - return isolate()->factory()->undefined_value(); + return isolate()->factory()->uninitialized_value(); } // Validation per 11.1.5 Object Initialiser @@ -3804,13 +3804,17 @@ void Parser::BuildObjectLiteralConstantProperties( Handle key = property->key()->handle(); Handle value = GetBoilerplateValue(property->value()); - // Ensure objects with doubles are always treated as nested objects. + // Ensure objects that may, at any point in time, contain fields with double + // representation are always treated as nested objects. This is true for + // computed fields (value is undefined), and smi and double literals + // (value->IsNumber()). // TODO(verwaest): Remove once we can store them inline. - if (FLAG_track_double_fields && value->IsNumber()) { + if (FLAG_track_double_fields && + (value->IsNumber() || value->IsUninitialized())) { *may_store_doubles = true; } - is_simple_acc = is_simple_acc && !value->IsUndefined(); + is_simple_acc = is_simple_acc && !value->IsUninitialized(); // Keep track of the number of elements in the object literal and // the largest element index. If the largest element index is diff --git a/src/property-details.h b/src/property-details.h index 6d0f1479d1..669b05dca0 100644 --- a/src/property-details.h +++ b/src/property-details.h @@ -113,7 +113,7 @@ class Representation { bool is_more_general_than(const Representation& other) const { ASSERT(kind_ != kExternal); ASSERT(other.kind_ != kExternal); - if (IsHeapObject()) return other.IsDouble(); + if (IsHeapObject()) return other.IsDouble() || other.IsNone(); return kind_ > other.kind_; } @@ -213,6 +213,7 @@ class PropertyDetails BASE_EMBEDDED { } Representation representation() { + ASSERT(type() != NORMAL); return DecodeRepresentation(RepresentationField::decode(value_)); } diff --git a/src/property.h b/src/property.h index 124775faec..f853fc8ba0 100644 --- a/src/property.h +++ b/src/property.h @@ -203,6 +203,8 @@ class LookupResult BASE_EMBEDDED { } bool CanHoldValue(Handle value) { + if (IsNormal()) return true; + ASSERT(!IsTransition()); return value->FitsRepresentation(details_.representation()); } diff --git a/test/mjsunit/track-fields.js b/test/mjsunit/track-fields.js index ced006c4fb..8b0ec29623 100644 --- a/test/mjsunit/track-fields.js +++ b/test/mjsunit/track-fields.js @@ -325,3 +325,83 @@ df3.first_double = 1.7; df3.second_function = some_function2; df1.first_double = 10; read_first_double(df1); + +// Test boilerplates with computed values. +function none_boilerplate(a) { + return {"a_none":a}; +} +%OptimizeFunctionOnNextCall(none_boilerplate); +var none_double1 = none_boilerplate(1.7); +var none_double2 = none_boilerplate(1.9); +assertTrue(%HaveSameMap(none_double1, none_double2)); +assertEquals(1.7, none_double1.a_none); +assertEquals(1.9, none_double2.a_none); +none_double2.a_none = 3.5; +var none_double1 = none_boilerplate(1.7); +var none_double2 = none_boilerplate(3.5); + +function none_to_smi(a) { + return {"a_smi":a}; +} + +var none_smi1 = none_to_smi(1); +var none_smi2 = none_to_smi(2); +%OptimizeFunctionOnNextCall(none_to_smi); +var none_smi3 = none_to_smi(3); +assertTrue(%HaveSameMap(none_smi1, none_smi2)); +assertTrue(%HaveSameMap(none_smi1, none_smi3)); +assertEquals(1, none_smi1.a_smi); +assertEquals(2, none_smi2.a_smi); +assertEquals(3, none_smi3.a_smi); + +function none_to_double(a) { + return {"a_double":a}; +} + +var none_to_double1 = none_to_double(1.5); +var none_to_double2 = none_to_double(2.8); +%OptimizeFunctionOnNextCall(none_to_double); +var none_to_double3 = none_to_double(3.7); +assertTrue(%HaveSameMap(none_to_double1, none_to_double2)); +assertTrue(%HaveSameMap(none_to_double1, none_to_double3)); +assertEquals(1.5, none_to_double1.a_double); +assertEquals(2.8, none_to_double2.a_double); +assertEquals(3.7, none_to_double3.a_double); + +function none_to_object(a) { + return {"an_object":a}; +} + +var none_to_object1 = none_to_object(true); +var none_to_object2 = none_to_object(false); +%OptimizeFunctionOnNextCall(none_to_object); +var none_to_object3 = none_to_object(3.7); +assertTrue(%HaveSameMap(none_to_object1, none_to_object2)); +assertTrue(%HaveSameMap(none_to_object1, none_to_object3)); +assertEquals(true, none_to_object1.an_object); +assertEquals(false, none_to_object2.an_object); +assertEquals(3.7, none_to_object3.an_object); + +function double_to_object(a) { + var o = {"d_to_h":1.8}; + o.d_to_h = a; + return o; +} + +var dh1 = double_to_object(true); +var dh2 = double_to_object(false); +assertTrue(%HaveSameMap(dh1, dh2)); +assertEquals(true, dh1.d_to_h); +assertEquals(false, dh2.d_to_h); + +function smi_to_object(a) { + var o = {"s_to_t":18}; + o.s_to_t = a; + return o; +} + +var st1 = smi_to_object(true); +var st2 = smi_to_object(false); +assertTrue(%HaveSameMap(st1, st2)); +assertEquals(true, st1.s_to_t); +assertEquals(false, st2.s_to_t);